diff --git a/.settings/settings.json b/.settings/settings.json new file mode 100644 index 000000000..20af2f68a --- /dev/null +++ b/.settings/settings.json @@ -0,0 +1,3 @@ +// Place your settings in this file to overwrite default and user settings. +{ +} \ No newline at end of file diff --git a/.settings/tasks.json b/.settings/tasks.json new file mode 100644 index 000000000..95c321163 --- /dev/null +++ b/.settings/tasks.json @@ -0,0 +1,220 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process + +// A task runner that calls the Typescript compiler (tsc) and +// Compiles a HelloWorld.ts program +{ + "version": "0.1.0", + "command" : "babel", + "isShellCommand": true, + "tasks": [ + { + "taskName": "watch", + "suppressTaskName": true, + "isBuildCommand": true, + "isWatching": true, + "args": [ + "src", "--out-dir", "lib", "-w" + ] + } + ] +} + +// A task runner that calls the Typescript compiler (tsc) and +// compiles based on a tsconfig.json file that is present in +// the root of the folder open in VSCode +/* +{ + "version": "0.1.0", + + // The command is tsc. Assumes that tsc has been installed using npm install -g typescript + "command": "tsc", + + // The command is a shell script + "isShellCommand": true, + + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // Tell the tsc compiler to use the tsconfig.json from the open folder. + "args": ["-p", "."], + + // use the standard tsc problem matcher to find compile problems + // in the output. + "problemMatcher": "$tsc" +} +*/ + +// A task runner configuration for gulp. Gulp provides a less task +// which compiles less to css. +/* +{ + "version": "0.1.0", + "command": "gulp", + "isShellCommand": true, + "tasks": [ + { + "taskName": "less", + // Make this the default build command. + "isBuildCommand": true, + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the standard less compilation problem matcher. + "problemMatcher": "$lessCompile" + } + ] +} +*/ + +// Uncomment the following section to use gulp in a watching mode that compiles a +// less file. The gulp task prints "[hh:mm:ss] Starting 'clean-styles'" to the console +// when existing css files get deleted and "[hh:mm:ss] Finished 'styles'" when the +// overall less compilation has finished. When the clean pattern is detect internal less +// problems are cleaned. When the finshed pattern is detected in the output less +// problems are published. +/* +{ + "version": "0.1.0", + "command": "gulp", + "isShellCommand": true, + "tasks": [ + { + "taskName": "watch-less", + // Make this the default build command. + "isBuildCommand": true, + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Task is running in watching mode. + "isWatching": true, + "problemMatcher": { + // Use the standard less compilation problem matcher as the base. + "base": "$lessCompile", + // A regular expression signalling that a watched task begins executing (usually triggered through file watching). + "watchedTaskBeginsRegExp": "^\\[\\d+:\\d+:\\d+\\] Starting 'clean-styles'\\.\\.\\.$", + // A regular expression signalling that a watched tasks ends executing. + "watchedTaskEndsRegExp": "^\\[\\d+:\\d+:\\d+\\] Finished 'styles' after \\d+" + } + } + ] +} +*/ + +// Uncomment the following section to use jake to build a workspace +// cloned from https://github.com/Microsoft/TypeScript.git +/* +{ + "version": "0.1.0", + // Task runner is jake + "command": "jake", + // Need to be executed in shell / cmd + "isShellCommand": true, + "showOutput": "silent", + "tasks": [ + { + // TS build command is local. + "taskName": "local", + // Make this the default build command. + "isBuildCommand": true, + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the redefined Typescript output problem matcher. + "problemMatcher": [ + "$tsc" + ] + } + ] +} +*/ + +// Uncomment the section below to use msbuild and generate problems +// for csc, cpp, tsc and vb. The configuration assumes that msbuild +// is available on the path and a solution file exists in the +// workspace folder root. +/* +{ + "version": "0.1.0", + "command": "msbuild", + "args": [ + // Ask msbuild to generate full paths for file names. + "/property:GenerateFullPaths=true" + ], + "taskSelector": "/t:", + "showOutput": "silent", + "tasks": [ + { + "taskName": "build", + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the standard MS compiler pattern to detect errors, warnings + // and infos in the output. + "problemMatcher": "$msCompile" + } + ] +} +*/ + +// Uncomment the following section to use msbuild which compiles Typescript +// and less files. +/* +{ + "version": "0.1.0", + "command": "msbuild", + "args": [ + // Ask msbuild to generate full paths for file names. + "/property:GenerateFullPaths=true" + ], + "taskSelector": "/t:", + "showOutput": "silent", + "tasks": [ + { + "taskName": "build", + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the standard MS compiler pattern to detect errors, warnings + // and infos in the output. + "problemMatcher": [ + "$msCompile", + "$lessCompile" + ] + } + ] +} +*/ +// A task runner example that defines a problemMatcher inline instead of using +// a predfined one. +/* +{ + "version": "0.1.0", + "command": "tsc", + "isShellCommand": true, + "args": ["HelloWorld.ts"], + "showOutput": "silent", + "problemMatcher": { + // The problem is owned by the typescript language service. Ensure that the problems + // are merged with problems produced by Visual Studio's language service. + "owner": "typescript", + // The file name for reported problems is relative to the current working directory. + "fileLocation": ["relative", "${cwd}"], + // The actual pattern to match problems in the output. + "pattern": { + // The regular expression. Matches HelloWorld.ts(2,10): error TS2339: Property 'logg' does not exist on type 'Console'. + "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", + // The match group that denotes the file containing the problem. + "file": 1, + // The match group that denotes the problem location. + "location": 2, + // The match group that denotes the problem's severity. Can be omitted. + "severity": 3, + // The match group that denotes the problem code. Can be omitted. + "code": 4, + // The match group that denotes the problem's message. + "message": 5 + } + } +} +*/ \ No newline at end of file diff --git a/examples/avatar.js b/examples/avatar.js deleted file mode 100644 index 32ad48a8f..000000000 --- a/examples/avatar.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * A bot that shows how to mention users in messages and how to - * access user avatars. - */ - -var Discord = require( "../" ); -var myBot = new Discord.Client(); - -myBot.login( "hello@example.com", "password1" ); - -// The "ready" event is triggered after the bot successfully connected to -// Discord and is ready to send messages. -myBot.on( "ready", function() { - console.log( "Bot connected successfully." ); -} ); - -myBot.on( "message", function( message ) { - // React to all messages with the content "$avatar" - if ( message.content === "$avatar" ) { - // Obtain the user who requested the avatar. - var user = message.author; - - // Check whether the user actually has an avatar. - if ( user.avatar ) { - // Construct the avatar URL from the user ID and the avatar ID. - var url = "https://discordapp.com/api/users/" + user.id + "/avatars/" + user.avatar + ".jpg"; - - // A user can be mentioned in a message by inserting the string obtained - // by user.mention() into the message. - // Note that simply writing "@user" will NOT work. - this.sendMessage( message.channel, message.author.mention() + ", here's your avatar: " + url ); - } else { - // Nothing should be done if the user has not set an avatar. - this.sendMessage( message.channel, message.author.mention() + ", you don't have an avatar!" ); - } - } -} ); diff --git a/examples/formatting.js b/examples/formatting.js deleted file mode 100644 index 02756eaaa..000000000 --- a/examples/formatting.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Discord uses a subset of Markdown for formatting, so adding formatting to - * messages is as simple as inserting the formatting codes into the message. - */ - -var Discord = require( "../" ); -var myBot = new Discord.Client(); - -myBot.login( "hello@example.com", "password1" ); - -// The "ready" event is triggered after the bot successfully connected to -// Discord and is ready to send messages. -myBot.on( "ready", function() { - console.log( "Bot connected successfully." ); -} ); - -myBot.on( "message", function( message ) { - // React to all messages with the content "$formatting". - if ( message.content === "$formatting" ) { - // Show off formatting by sending a simple message with formatting codes. - this.sendMessage( message.channel, "**bold** ****semibold**** *italic* " + - "_**bold and italic**_ __underline__ ~~strikethrough~~" ); - } -} ); diff --git a/examples/pingpong.js b/examples/pingpong.js deleted file mode 100644 index 51a2664e3..000000000 --- a/examples/pingpong.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * A basic bot that shows how to connect to a Discord account, - * how to listen to messages and how to send messages. - * - * This bot responds to every "ping" message with a "pong". - */ - -var Discord = require( "../" ); - -// Create the bot -var myBot = new Discord.Client(); - -// Login with an example email and password -myBot.login( "hello@example.com", "password1" ); - -// The "ready" event is triggered after the bot successfully connected to -// Discord and is ready to send messages. -myBot.on( "ready", function() { - console.log( "Bot connected successfully." ); -} ); - -// Add a listener to the "message" event, which triggers upon receiving -// any message -myBot.on( "message", function( message ) { - // message.content accesses the content of the message as a string. - // If it is equal to "ping", then the bot should respond with "pong". - if ( message.content === "ping" ) { - // Send a message ("pong") to the channel the message was sent in, - // which is accessed by message.channel. - this.sendMessage( message, "pong" ); - } -} ); diff --git a/examples/presence.js b/examples/presence.js deleted file mode 100644 index 9014feea2..000000000 --- a/examples/presence.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * A bot that shows how to listen to presence update events, such as a user - * joining or leaving. - */ - -var Discord = require( "../" ); -var myBot = new Discord.Client(); - -myBot.login( "hello@example.com", "password1" ); - -// The "ready" event is triggered after the bot successfully connected to -// Discord and is ready to send messages. -myBot.on( "ready", function() { - console.log( "Bot connected successfully." ); -} ); - -// The "presence" event is triggered when a user joins a server, leaves it or -// goes away. -// The status parameter can be "online", "offline" or "idle", respectively. -myBot.on( "presence", function( user, status, server ) { - // Send a message on the default channel of the server, as presence updates - // are not restricted to one channel. - var message = user.mention() + " is " + status + " in " + server.name + "!"; - console.log(message); - this.sendMessage( server.getDefaultChannel(), message ); -} ); diff --git a/examples/query.js b/examples/query.js deleted file mode 100644 index 9341e7e98..000000000 --- a/examples/query.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * A bot that shows how to access and search the logs of a specific channel. - * Specifically, it returns the last message from a given user in the last - * 100 messages. - */ - -var Discord = require( "discord.js" ); -var myBot = new Discord.Client(); - -myBot.login( "hello@example.com", "password1" ); - -myBot.on( "message", function( message ) { - // React to all messages starting with "$query". - if ( message.content.startsWith( "$query" ) ) { - // Obtain the channel for which logs should be accessed. - var channel = message.channel; - - // Find all the arguments to the command. - var arguments = message.content.split( " " ); - - // Get the first argument specifically, as it contains the username - // to be queried for. - var username = arguments.slice( 1 ).join( " " ); - - // Exit the event handler unless the user exists. - if ( !username ) { - myBot.sendMessage( channel, "That user doesn't exist!" ); - return; - } - - // The getChannelLogs() function takes the channel that should be accessed, - // the amount of messages to query and a callback as its arguments. - myBot.getChannelLogs( channel, 100, function( error, messageList ) { - // filter() takes three arguments, the key to be filtered for (in this - // case the username, so "username"), the value to look for, and whether - // only the first finding should be returned (true) or a list of all - // findings (false). - - // Check to see if there is an error, if there isn't this will be null, - // which equates to false in a conditional. - if ( error ) { - // There was an error, so stop proceeding as if there is an error - // retrieving logs, no logs are returned. We should tell the - // users that there was an error retrieving logs and return. - myBot.sendMessage( channel, "There was an error retrieving logs!" ); - return; - } - - var message = messageList.filter( "username", username, true ); - - // Only continue if the message has been found - if ( message ) { - myBot.sendMessage( channel, "The last message from user " + username + - " is: \"" + message.content + "\"." ). - } else { - myBot.sendMessage( "That user has not sent a message " + - "for the last 100 messages!" ) - } - } ); - } -} ); diff --git a/examples/status.js b/examples/status.js deleted file mode 100644 index 9ee2b9a6a..000000000 --- a/examples/status.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * A bot that doesn't interact with Discord, but instead shows how to listen - * to the "ready" and "disconnected" events, that are triggered when the bot - * starts up or shuts down, respectively. - */ - -var Discord = require( "../" ); -var myBot = new Discord.Client(); - -myBot.login( "hello@example.com", "password1" ); - -// The "ready" event is triggered after the bot successfully connected to -// Discord and is ready to send messages. -myBot.on( "ready", function() { - console.log( "Bot connected successfully." ); -} ); - -// The "disconnected" event is triggered after the connection to Discord -// ended. -// It is also triggered when the connection attempt fails, for example due -// to a wrong password. -myBot.on( "disconnected", function(e) { - console.log( "Bot disconnected from Discord -", e.reason ); -} ); diff --git a/hydrabot/.gitignore b/hydrabot/.gitignore deleted file mode 100644 index 070bc70aa..000000000 --- a/hydrabot/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -config.json -authority.json diff --git a/hydrabot/README.md b/hydrabot/README.md deleted file mode 100644 index 90aeffc4b..000000000 --- a/hydrabot/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# hydrabot -Hydrabot is an open-source bot made with the intents of demonstrating the capabilities of [discord.js](https://github.com/hydrabolt/discord.js/). - -### Set up -The easiest setup would be to clone the discord.js repo, and then open a terminal/cmd in this directory and run `node hydrabot.js`. - -If you don't want to clone the repo but instead just use this folder, you need to edit `hydrabot.js` to use `require("discord.js")` as opposed to `require("../")`. Cloned directories will always be using the latest **discord.js**. - -### Setting up credentials - -Create `config.json` to use your Discord email and password, and then run `node hydrabot.js`. - -What config.json should look like: -```js -{ - "email" : "your email", - "password" : "your password" -} -``` diff --git a/hydrabot/authority.js b/hydrabot/authority.js deleted file mode 100644 index 1f7c1c4af..000000000 --- a/hydrabot/authority.js +++ /dev/null @@ -1,37 +0,0 @@ -var fs = require( "fs" ); - -var authCache = {}; - -exports.init = function() { - try { - var fd = fs.openSync( "./authority.json", "wx" ); - exports.writeCache(); - } catch ( e ) { - if ( e.errno !== -4075 ){ - throw e; - }else{ - authCache = JSON.parse(fs.readFileSync("./authority.json", "utf8")); - } - } -} - -exports.getLevel = function(user){ - - if(authCache[user.id]) - return authCache[user.id]; - else - return 0; - -} - -exports.setLevel = function(user, level){ - authCache[user.id] = level; - exports.writeCache(); -} - -exports.writeCache = function() { - fs.writeFile( './authority.json', JSON.stringify(authCache), function( err ) { - if ( err ) - console.log("Error saving Authority Caches - " + err.code); - } ); -} diff --git a/hydrabot/commands.js b/hydrabot/commands.js deleted file mode 100644 index b499d9dce..000000000 --- a/hydrabot/commands.js +++ /dev/null @@ -1,516 +0,0 @@ -var Authority = require( "./authority.js" ); -var BotClass = require( "./hydrabot.js" ); -var Discord = BotClass.Discord; - -Commands = []; - -Commands[ "info" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var verbose = hasFlag( params, "verbose" ) || hasFlag( params, "v" ); - var user = getUser( message, params ); - - bot.reply( message, [ - "here's some info on " + user.mention() + ":", - "In channel **#" + message.channel.name + "**" + ( verbose ? " - ID *" + message.channel.id + "*" : "" ), ( message.isPM() ? - "You're in a private conversation with me!" + ( verbose ? " The ID is " + message.channel.id : "" ) : "In the server **" + message.channel.server.name + "**" + ( verbose ? " - ID *" + message.channel.server.id + "*" : "" ) - ), - "User ID is *" + user.id + "*", - "Authority/OP Level to me is **" + Authority.getLevel( user ) + "**" - ], function( err ) { - if ( err ) - console.log( err ); - } ); - - } -} - -Commands[ "loading" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var progress = 0; - var currentMessage; - var bars = 20; - - function getM() { - var before = progress; - var after = bars - progress; - var ret = ""; - for ( x = 0; x < before; x++ ) { - ret += "-"; - } - ret += "**#**"; - for ( y = 0; y < after; y++ ) { - ret += "-"; - } - return ret; - } - - function doProg() { - if ( progress === ( bars + 1 ) ) { - progress = 0; - } - - if ( currentMessage ) { - bot.updateMessage( currentMessage, getM(), function( err, msg ) { - if ( !err ) - currentMessage = msg; - } ); - progress++; - } - - } - - bot.sendMessage( message.channel, getM(), function( err, message ) { - currentMessage = message; - setInterval( doProg, 200 ); - } ); - - } -} - -Commands[ "flashy" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var phase = 0; - var msg; - - var textToSay = getKey( params, "m", "FLASH" ); - var speed = parseInt( getKey( params, "s", "500" ) ); - - function change() { - if ( msg ) { - - var highlighting = ( ( phase % 2 ) === 0 ? "**" : "" ); - phase++; - bot.updateMessage( msg, highlighting + textToSay + highlighting, function( err, message ) { - if ( !err ) { - msg = message; - } - } ); - } - } - - bot.sendMessage( message.channel, textToSay, function( err, message ) { - msg = message; - setInterval( change, speed ); - } ); - - } -} - -Commands[ "echo" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - bot.sendMessage( message, params.join( " " ), function( err, msg ) { - if ( err ) { - bot.sendMessage( message, "Unable to echo!" ); - console.log( err ); - } - } ); - - } -} - -Commands[ "auth" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var level = getKey( params, "level", "0" ); - var method = hasFlag( params, "set" ) ? "set" : "get"; - var user = getUser( message, params ); - - if ( method === "set" ) { - if ( authLevel( message.author ) <= level ) { - bot.reply( message, "that authority level is too high for you to set!" ); - } else if ( user.equals( message.author ) ) { - bot.reply( message, "you can't alter your own authority level!" ); - } else if ( authLevel( user ) >= authLevel( message.author ) ) { - bot.reply( message, "that user has a higher or equal OP level to you!" ); - } else if ( level < 0 ) { - bot.reply( message, "that level's a bit too low :P" ); - } else { - setAuthLevel( user, level ); - bot.reply( message, "I set the authority of " + user.mention() + " to **" + level + "**" ); - } - } else { - bot.reply( message, user.equals( message.author ) ? "Your authority level is **" + authLevel( user ) + "**" : "The authority level of " + user.mention() + " is **" + authLevel( user ) + "**" ); - } - - } -} - -Commands[ "clear" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - if ( !message.isPM() ) { - if ( authLevel( message.author ) < 1 ) { - bot.reply( message, BotClass.AUTH_ERROR ); - return; - } - } - - var initMessage = false, - cleared = false; - - bot.getChannelLogs( message.channel, 250, function( err, logs ) { - - if ( err ) { - bot.sendMessage( "Couldn't grab logs to delete messages." ); - } else { - - var deletedCount = 0, - failedCount = 0, - todo = logs.length(); - for ( msg of logs.contents ) { - if ( msg.author.equals( bot.user ) ) { - bot.deleteMessage( msg, function( err ) { - todo--; - if ( err ) - failedCount++; - else - deletedCount++; - - if ( todo === 0 ) { - bot.reply( - message, - "Done! " + deletedCount + " message(s) were deleted, with " + failedCount + " error(s).", - false, { - selfDestruct: 5000 - } - ); - cleared = true; - deleteInitMessage(); - } - } ); - } else { - todo--; - } - } - - } - - } ); - - bot.reply( message, "clearing up my messages...", function( err, msg ) { - if ( !err ) { - initMessage = msg; - if ( cleared ) - deleteInitMessage(); - } - } ); - - function deleteInitMessage() { - if ( initMessage ) { - bot.deleteMessage( initMessage ); - } - } - - } -} - -Commands[ "leave" ] = { - oplevel: 3, - fn: function( bot, params, message ) { - - var silent = hasFlag( params, "s" ) || hasFlag( params, "silent" ); - - if ( message.isPM() ) { - bot.reply( message, "Umm... I can't leave PMs... How awkward..." ); - } else { - - if ( !silent ) - bot.reply( message, "Ok ;( I'm leaving!" ); - - bot.leaveServer( message.channel.server, function( err ) { - if ( err ) { - bot.reply( message, "There was an error leaving... how awkward." ); - } - } ); - } - } -} - -Commands[ "avatar" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var user = getUser( message, params, bot ); - - if ( !user.avatar ) { - bot.sendMessage( message.channel, user.mention() + " does not have an avatar!" ); - } else { - bot.reply( message, user.getAvatarURL() ); - } - } -} - -Commands[ "setusername" ] = { - oplevel: 3, - fn: function( bot, params, message ) { - - var name = getKey( params, "name", "Boris Johnson" ); - - bot.setUsername( name, function( err ) { - if ( err ) - bot.reply( message, err ); - } ) - - } -} - -Commands[ "cat" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var http = require( "http" ); - var request = require( 'request' ); - - bot.sendFile( message, request("http://thecatapi.com/api/images/get?type=jpg"), "cat.jpg", function( err ) { - if(err) - bot.reply( message, err ); - } ); - - } -} - -Commands[ "icon" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - if ( message.isPM() ) { - bot.reply( message, "PMs don't have avatars!" ); - return; - } - - if ( !message.channel.server.icon ) { - bot.reply( message, "this server does not have an icon!" ); - return; - } - - bot.reply( message, message.channel.server.getIconURL() ); - - } -} - -Commands[ "avataritup" ] = { - oplevel: 2, - fn: function( bot, params, message ) { - - console.log( message.channel ); - bot.sendMessage( message, message.channel.server.members.getValues( "avatar" ).join( "\n" ) ); - - } -} - -Commands[ "feedback" ] = { - - oplevel: 0, - fn: function( bot, params, message ) { - - var amount = getKey( params, "amount" ) || getKey( params, "n" ) || 1000; - - bot.getChannelLogs( message.channel, amount, function( err, logs ) { - - console.log( logs ); - - if ( err ) { - bot.reply( message, "an error occurred when grabbing the logs.", false, { - selfDestruct: 3000 - } ); - } else { - - var found = []; - for ( msg of logs.contents ) { - - if ( ~msg.content.indexOf( "[request" ) || ~msg.content.indexOf( "[feature" || ~msg.content.indexOf( "[suggestion" ) ) ) { - if ( msg.content.length > 10 ) { - found.push( msg ); - } - } - - } - - bot.sendMessage( message.author, "Ok, here's a rundown of all feature requests so far:", function( err, ms ) { - - if ( !err ) - gothroughit(); - - } ); - - bot.reply( message, "I found " + found.length + " result(s) that matched this. I'll send it to you in a PM.", false, { - selfDestruct: 3000 - } ); - - function gothroughit() { - for ( msg of found ) { - - bot.sendMessage( message.author, "**" + msg.author.username + "** said:\n " + msg.content ); - - } - } - } - } ); - - } - -} - -Commands[ "acceptinvite" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var inv = getKey( params, "i" ); - - bot.joinServer( inv, function( err, server ) { - if ( err ) { - bot.reply( message, "I couldn't join that server :(" ); - } else { - bot.reply( message, "I joined **" + server.name + "**, a server with " + server.channels.length() + " channels and " + server.members.length() + " members." ); - } - } ); - - } -} - -Commands[ "filtertest" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - console.log( message.channel.server.members.filter( "username", "HYDRABOLT" ) ); - console.log( message.channel.server.members.filter( "username", "HYDRABOLT", false, true ) ); - } -} - -Commands[ "test" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - console.log( message.channel.server.channels.filter( "name", "a", true ) ); - - } -} - -Commands[ "remind" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var time = parseInt( getKey( params, "t" ) || getKey( params, "time" ) ) * 1000 || 21000; - var msg = getKey( params, "m" ) || getKey( params, "msg" ) || getKey( params, "message" ); - - bot.reply( message, "I'll remind you to *" + msg + "* in *" + time / 1000 + "* seconds.", false, true, { - selfDestruct: time - } ); - - setTimeout( send, time ); - - function send() { - bot.sendMessage( message.author, time + " seconds are up! **" + msg + "**." ); - } - - } -} - -Commands[ "annoy" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var user = getUser( message, params ); - - bot.sendMessage( user, "Ha I'm annoying you on " + message.author.mention() + "'s request!" ); - - } -} - -Commands[ "activity" ] = { - oplevel: 0, - fn: function( bot, params, message ) { - - var amount = getKey( params, "amount" ) || getKey( params, "n" ) || 250; - var limit = getKey( params, "limit" ) || getKey( params, "l" ) || 10; - - bot.getChannelLogs( message.channel, amount, function( err, logs ) { - - if ( err ) { - bot.reply( message, "error gettings logs." ); - } else { - - var activity = {}, - count = 0; - for ( msg of logs.contents ) { - - count = logs.length(); - - if ( !activity[ msg.author.id ] ) - activity[ msg.author.id ] = 0; - activity[ msg.author.id ]++; - } - - var report = "here's a list of activity over the last " + count + " messages :\n\n"; - - var usernames = {}; - for ( id in activity ) { - usernames[ id ] = bot.getUser( id ).username; - } - - for ( id in activity ) { - report += usernames[ id ] + " | " + activity[ id ] + " | **" + Math.round( ( activity[ id ] / count ) * 100 ) + "%**.\n"; - } - - bot.reply( message, report, false, false ); - } - - } ); - - } -} - -exports.Commands = Commands; - -function hasFlag( array, flag ) { - return ~array.indexOf( flag ); -} - -function getKey( array, key, def ) { - - for ( element of array ) { - var chunks = element.split( "=" ); - if ( chunks.length > 1 ) { - if ( chunks[ 0 ] == key ) { - return chunks[ 1 ]; - } - } - } - - return def; - -} - -function authLevel( user ) { - return Authority.getLevel( user ); -} - -function setAuthLevel( user, level ) { - Authority.setLevel( user, level ); -} - -function getUser( message, params, bot ) { - var usr = false; - if ( !message.isPM() ) { - var wantedUser = getKey( params, "user", false ) || getKey( params, "u", false ); - if ( wantedUser ) { - if ( bot ) { - console.log( bot.getUsers().length() ); - return bot.getUsers().filter( "username", wantedUser, true ); - } - usr = message.channel.server.members.filter( Discord.isUserID( wantedUser ) ? "id" : "username", wantedUser, true ); - } - } - if ( !usr ) - usr = message.author; - return usr; -} diff --git a/hydrabot/hydrabot.js b/hydrabot/hydrabot.js deleted file mode 100644 index 4f010266d..000000000 --- a/hydrabot/hydrabot.js +++ /dev/null @@ -1,132 +0,0 @@ -// If you did not clone discord.js, change the require parameter to `discord.js` -// and then run `npm install --save discord.js` in the same directory as this -// file. The bot should then run. -var Discord = require( "../" ); -exports.Discord = Discord; - -// Load the config file. If you have not already, make one that follows the -// structure : { "email" : "discordEmail", "password" : "discordPassword" } -var BotConfig = require( "./config.json" ); - -// Load the commands file -var Commands = require( "./commands.js" ).Commands; - -// Load the Authority handler -var Authority = require( "./authority.js" ); - -// Initialise it -Authority.init(); - -// Create a new Discord Client -var hydrabot = new Discord.Client(); - -// An array of single character prefixes the bot will respond to -var commandPrefixes = [ "$", "£", "`" ]; - -// Log the client in using the auth details in config.json - -hydrabot.on("debug", function(m){ - console.log("debug", m); -}) - -console.time("hydrabotbenchmark"); -hydrabot.login( BotConfig.email, BotConfig.password ); -var time = Date.now(); - -// When the bot is ready to go, output to the console -hydrabot.on( "ready", function() { - console.timeEnd("hydrabotbenchmark"); -} ); - -hydrabot.on("userupdate", function(ol, ne){ - - var serversInvolved = hydrabot.getServers().deepFilter(["members", "id"], ol.id); - - for(server of serversInvolved.contents){ - hydrabot.sendMessage(server.getDefaultChannel(), "Just sayin', "+ol.username+" changed their name to "+ne.username+". I know. Disgraceful.", function(err){ - console.log(err); - }, { - selfDestruct: 5000 - }); - } - -}); - -// When the bot gets disconnected, exit. -hydrabot.on( "disconnected", function( obj ) { - // Say we couldn't connect and then exit - console.log( "Disconnected - " + obj.reason ); - process.exit( 0 ); -} ); - -hydrabot.on("messageDelete", function(message){ - console.log(message); -}) - -hydrabot.on("messageUpdate", function(former, edit){ - /* - if(former){ - - if(former.author.equals(this.user) || former.content === edit.content){ - return; - } - - var seconds = Math.round((Date.now() - former.time) / 1000); - - var channel = former.channel; - hydrabot.sendMessage(channel, "**"+former.author.username + "** (edit from message "+seconds+" seconds ago):\n " + former.content); - - } - */ -}) - -hydrabot.on( "message", function( message ) { - - // if the message doesn't begin with a valid command prefix exit - - if ( commandPrefixes.indexOf( message.content.charAt( 0 ) ) == -1 ) - return; - - var command = "", - params = []; //set the message details - - // remove the prefix from the start of the message - message.content = message.content.substr( 1 ); - - // split the message by slashes. This will yield something - // like: ["command", "a", "b", "c"]. - var chunks = message.content.split( "/" ); - - for ( key in chunks ) { //loop through the chunks and trim them - chunks[ key ] = chunks[ key ].trim(); - } - - command = chunks[ 0 ]; //the first param will be the command - params = chunks.slice( 1 ); - - // it's less messy if we outsource to another function - handleMessage( command, params, message ); - -} ); - -function handleMessage( command, params, message ) { - - if ( Commands[ command ] ) { - - if ( Authority.getLevel( message.author ) >= Commands[ command ].oplevel ) { - //user has authority to do this - Commands[ command ].fn( hydrabot, params, message ); - - } else { - //user doesn't have authority - hydrabot.reply( message, exports.AUTH_ERROR ); - } - - } else { - hydrabot.reply( message, exports.NOT_FOUND ); - } - -} - -exports.AUTH_ERROR = "you don't have authority to do this!"; -exports.NOT_FOUND = "that command was not found!"; diff --git a/index.js b/index.js deleted file mode 100644 index d8b9a1560..000000000 --- a/index.js +++ /dev/null @@ -1,1087 +0,0 @@ -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 serverCreateRequests = [], - globalLoginTime = Date.now(); - -function tp(time) { - return Date.now() - time; -} - -/** - * The wrapper module for the Discord Client, also provides some helpful objects. - * - * @module Discord - */ -exports; - -exports.Endpoints = Endpoints; -exports.Server = Server; -exports.Message = Message; -exports.User = User; -exports.Channel = Channel; -exports.List = List; -exports.Invite = Invite; -exports.PMChannel = PMChannel; - -/** - * The Discord Client used to interface with the Discord API. Instantiate this to start writing code - * with discord.js - * @class Client - * @constructor - * @param {Object} [options] An object containing configurable options. - * @param {Number} [options.maxmessage=5000] The maximum amount of messages to be stored per channel. - */ - -exports.Client = function (shouldUseTokenManager) { - - /** - * Contains the options of the client - * @attribute options - * @type {Object} - */ - if (shouldUseTokenManager) - this.tokenManager = new TokenManager("./", "tokencache.json"); - /** - * Contains the token used to authorise HTTP requests and WebSocket connection. Received when login was successful. - * @attribute token - * @readonly - * @type {String} - */ - this.token = ""; - /** - * Indicates whether the client is logged in or not. Does not indicate whether the client is ready. - * @attribute loggedIn - * @readonly - * @type {Boolean} - */ - this.loggedIn = false; - /** - * The WebSocket used when receiving message and other event updates. - * @type {WebSocket} - * @attribute websocket - * @readonly - */ - this.websocket = null; - /** - * An Object containing the functions tied to events. These should be set using Client.on(); - * @type {Object} - * @attribute events - */ - this.events = {}; - /** - * The User of the Client class, set when the initial startup is complete. - * @attribute user - * @type {User} - * @readonly - */ - this.user = null; - /** - * Indicates whether the Client is ready and has cached all servers it as aware of. - * @type {Boolean} - * @attribute ready - * @readonly - */ - this.ready = false; - /** - * A List containing all the Servers the Client has access to. - * @attribute serverList - * @type {List} - * @readonly - */ - this.serverList = new List("id"); - /** - * A List containing all the PMChannels the Client has access to. - * @attribute PMList - * @type {List} - * @readonly - */ - this.PMList = new List("id"); - -} - -/** - * Returns a list of all servers that the Discord Client has access to. - * - * @method getServers - * @return {List} ServerList - */ -exports.Client.prototype.getServers = function () { - return this.serverList; -} - -/** - * Returns a list of all servers that the Discord Client has access to. - * @method getChannels - * @return {List} Channelist - */ -exports.Client.prototype.getChannels = function () { - return this.serverList.concatSublists("channels", "id"); -} - -/** - * Returns a Server matching the given id, or false if not found. Will return false if the server is not cached or not available. - * @method getServer - * @param {String/Number} id The ID of the Server - * @return {Server} The Server matching the ID - */ -exports.Client.prototype.getServer = function (id) { - return this.getServers().filter("id", id, true); -} - -/** - * Returns a Channel matching the given id, or false if not found. Will return false if the Channel is not cached or not available. - * @method getChannel - * @param {String/Number} id The ID of the Channel - * @return {Server} The Channel matching the ID - */ -exports.Client.prototype.getChannel = function (id) { - return this.getChannels().filter("id", id, true); -} - -/** - * Returns a Channel matching the given name, or false if not found. Will return false if the Channel is not cached or not available. - * @method getChannelByName - * @param {String/Number} name The Name of the Channel - * @return {Server} The Channel matching the Name - */ -exports.Client.prototype.getChannelByName = function (name) { - return this.getChannels().filter("name", name, true); -} - -/** - * Triggers an .on() event. - * @param {String} event The event to be triggered - * @param {Array} args The arguments to be passed onto the method - * @return {Boolean} whether the event was triggered successfully. - * @method triggerEvent - * @private - */ -exports.Client.prototype.triggerEvent = function (event, args) { - - if (!this.ready && event !== "raw" && event !== "disconnected" && event !== "debug") { //if we're not even loaded yet, don't try doing anything because it always ends badly! - return false; - } - - if (this.events[event]) { - this.events[event].apply(this, args); - return true; - } else { - return false; - } - -} - -/** - * Binds a function to an event - * @param {String} name The event name which the function should be bound to. - * @param {Function} fn The function that should be bound to the event. - * @method on - */ -exports.Client.prototype.on = function (name, fn) { - this.events[name] = fn; -} - -/** - * Unbinds a function from an event - * @param {String} name The event name which should be cleared - * @method off - */ -exports.Client.prototype.off = function (name) { - this.events[name] = function () { }; -} - -exports.Client.prototype.cacheServer = function (id, cb, members) { - var self = this; - var serverInput = {}; - - if (typeof id === 'string' || id instanceof String) { - //actually an ID - - if (this.serverList.filter("id", id).length > 0) { - return; - } - - Internal.XHR.getServer(self.token, id, function (err, data) { - if (!err) { - makeServer(data); - } - }) - - } else { - // got objects because SPEEEDDDD - - if (this.serverList.filter("id", id.id).length > 0) { - return; - } - serverInput = id; - id = id.id; - makeServer(serverInput); - - } - - function channelsFromHTTP() { - Internal.XHR.getChannel(self.token, id, function (err) { - if (!err) - cacheChannels(res.body); - }) - } - - var server; - - function makeServer(dat) { - server = new Server(dat.region, dat.owner_id, dat.name, id, serverInput.members || dat.members, dat.icon, dat.afk_timeout, dat.afk_channel_id); - if (dat.channels) - cacheChannels(dat.channels); - else - channelsFromHTTP(); - } - - function cacheChannels(dat) { - - var channelList = dat; - for (var channel of channelList) { - server.channels.add(new Channel(channel, server)); - } - self.serverList.add(server); - - cb(server); - } - -} - -/** - * Logs the Client in with the specified credentials and begins initialising it. - * @async - * @method login - * @param {String} email The Discord email. - * @param {String} password The Discord password. - * @param {Function} [callback] Called when received reply from authentication server. - * @param {Object} callback.error Set to null if there was no error logging in, otherwise is an Object that - * can be evaluated as true. - * @param {String} callback.error.reason The reason why there was an error - * @param {Object} callback.error.error The raw XHR error. - * @param {String} callback.token The token received when logging in - */ -exports.Client.prototype.login = function (email, password, callback, noCache) { - - globalLoginTime = Date.now(); - - this.debug("login called at " + globalLoginTime); - - var self = this; - callback = callback || function () { }; - - if (noCache == undefined || noCache == null) { - noCache = false; - } - - self.connectWebsocket(); - - if (this.tokenManager) { - if (this.tokenManager.exists(email) && !noCache) { - - var token = this.tokenManager.getToken(email, password); - if (!token.match(/[^\w.-]+/g)) { - done(this.tokenManager.getToken(email, password)); - self.debug("loaded token from caches in " + tp(globalLoginTime)); - return; - } else { - self.debug("error getting token from caches, using default auth"); - } - } - } - var time = Date.now(); - Internal.XHR.login(email, password, function (err, token) { - if (err) { - self.triggerEvent("disconnected", [{ - reason: "failed to log in", - error: err - }]); - self.websocket.close(); - self.debug("failed to login in " + tp(globalLoginTime)); - } else { - if (!noCache) { - self.tokenManager.addToken(email, token, password); - } - self.debug("loaded token from auth servers in " + tp(globalLoginTime)); - done(token); - } - - }); - - function done(token) { - self.email = email; - self.password = password; - self.debug("using token " + token); - self.token = token; - self.websocket.sendData(); - self.loggedIn = true; - callback(null, token); - } -} - -/** - * Replies to a message with a given message - * @param {Message/User/Channel/Server/String} destination Where the message should be sent. Channel IDs can also be used here. - * @param {String/Message/Array} toSend If a message, the message's content will be sent. If an array, a message will be sent of - * the array seperated by a newline. If a String, the string will be sent. - * @param {Function} callback Called when a response from the API has been received, the message has either been sent or not. - * @param {Object} callback.error If there was an error, this would be an XHR Error object. Otherwise, it will be null. - * @param {Message} callback.message If there were no errors, this will be the sent message in Message form. - * @param {Object} options see sendMessage(options) - * @method reply - */ -exports.Client.prototype.reply = function (destination, toSend, callback, options) { - - if (toSend instanceof Array) { - toSend = toSend.join("\n"); - } - - toSend = destination.author.mention() + ", " + toSend; - - this.sendMessage(destination, toSend, callback, options); - -} - -exports.Client.prototype.connectWebsocket = function (cb) { - - var self = this; - - var sentInitData = false; - - this.websocket = new WebSocket(Endpoints.WEBSOCKET_HUB); - this.websocket.onclose = function (e) { - self.triggerEvent("disconnected", [{ - reason: "websocket disconnected", - error: e - }]); - }; - this.websocket.onmessage = function (e) { - - self.triggerEvent("raw", [e]); - - var dat = JSON.parse(e.data); - var webself = this; - - switch (dat.op) { - - case 0: - if (dat.t === "READY") { - - self.debug("got ready packet"); - - var data = dat.d; - - self.user = new User(data.user); - - var _servers = data.guilds, - servers = []; - - var cached = 0, - toCache = _servers.length; - - for (x in data.private_channels) { - self.PMList.add(new PMChannel(data.private_channels[x].recipient, data.private_channels[x].id)); - } - - for (x in _servers) { - _server = _servers[x]; - - self.cacheServer(_server, function (server) { - cached++; - if (cached === toCache) { - self.ready = true; - self.triggerEvent("ready"); - self.debug("ready triggered"); - } - }); - } - - setInterval(function () { - webself.keepAlive.apply(webself); - }, data.heartbeat_interval); - - } else if (dat.t === "MESSAGE_CREATE") { - var data = dat.d; - - var channel = self.getChannel(data.channel_id); - var message = new Message(data, channel); - self.triggerEvent("message", [message]); - if (channel.messages) - channel.messages.add(message); - - } else if (dat.t === "MESSAGE_DELETE") { - var data = dat.d; - - var channel = self.getChannel(data.channel_id); - - if (!channel.messages) - return; - - var _msg = channel.messages.filter("id", data.id, true); - - if (_msg) { - self.triggerEvent("messageDelete", [_msg]); - channel.messages.removeElement(_msg); - } - - } else if (dat.t === "MESSAGE_UPDATE") { - - var data = dat.d; - if (!self.ready) - return; - - var formerMessage = false; - - var channel = self.getChannel(data.channel_id); - - if (channel) { - - formerMessage = channel.messages.filter("id", data.id, true); - var newMessage; - - data.author = data.author || formerMessage.author; - data.timestamp = data.time || formerMessage.time; - data.content = data.content || formerMessage.content; - data.channel = data.channel || formerMessage.channel; - data.id = data.id || formerMessage.id; - data.mentions = data.mentions || formerMessage.mentions; - data.mention_everyone = data.mention_everyone || formerMessage.everyoneMentioned; - data.embeds = data.embeds || formerMessage.embeds; - - try { - newMessage = new Message(data, channel); - } catch (e) { - self.debug("dropped a message update packet"); - return; - } - - self.triggerEvent("messageUpdate", [formerMessage, newMessage]); - - if (formerMessage) - channel.messages.updateElement(formerMessage, newMessage); - else - channel.messages.add(newMessage); - - } - - } else if (dat.t === "PRESENCE_UPDATE") { - - var data = dat.d; - var getUser = self.getUser(data.user.id); - if (getUser) { - //user already exists - var usr = new User(data.user); - if (usr.equalsStrict(getUser)) { - //no changes, actually a presence. - } else { - if (self.updateUserReferences(data.user.id, getUser, new User(data.user))) { - self.triggerEvent("userupdate", [getUser, usr]); - } - } - } - self.triggerEvent("presence", [new User(data.user), data.status, self.serverList.filter("id", data.guild_id, true)]); - - } else if (dat.t === "GUILD_DELETE") { - - var deletedServer = self.serverList.filter("id", dat.d.id, true); - - if (deletedServer) { - self.triggerEvent("serverDelete", [deletedServer]); - } - - } else if (dat.t === "CHANNEL_DELETE") { - - var delServer = self.serverList.filter("id", dat.d.guild_id, true); - - if (delServer) { - var channel = delServer.channels.filter("id", dat.d.id, true); - - if (channel) { - self.triggerEvent("channelDelete", [channel]); - } - } - - } else if (dat.t === "GUILD_CREATE") { - - if (!self.serverList.filter("id", dat.d.id, true)) { - self.cacheServer(dat.d, function (server) { - if (serverCreateRequests[server.id]) { - serverCreateRequests[server.id](null, server); - serverCreateRequests[server.id] = null; - } else { - self.triggerEvent("serverJoin", [server]); - } - }); - } - - } else if (dat.t === "CHANNEL_CREATE") { - - var srv = self.serverList.filter("id", dat.d.guild_id, true); - - if (srv) { - - if (!srv.channels.filter("id", dat.d.d, true)) { - - var chann = new Channel(dat.d, srv); - - srv.channels.add(new Channel(dat.d, srv)); - self.triggerEvent("channelCreate", [chann]); - - } - - } - - } else if (dat.t === "USER_UPDATE") { - - if (dat.d.id === self.user.id) { - var newUsr = new User(dat.d); - self.triggerEvent("userupdate", [self.user, newUsr]); - self.user = newUsr; - } - - } else if (dat.t === "GUILD_MEMBER_ADD") { - - var srv = self.getServer(dat.d.guild_id); - if (srv) { - var usr = new User(dat.d.user); - srv.members.add(usr); - self.triggerEvent("serverMemberAdd", [usr]); - } - - } else if (dat.t === "GUILD_MEMBER_REMOVE") { - - var srv = self.getServer(dat.d.guild_id); - if (srv) { - var usr = new User(dat.d.user); - srv.members.removeElement(usr); - self.triggerEvent("serverMemberRemove", [usr]); - } - - } - break; - - } - - }; - this.websocket.sendPacket = function (p) { - this.send(JSON.stringify(p)); - } - this.websocket.keepAlive = function () { - - if (this.readyState !== 1) - return false; - - this.sendPacket({ - op: 1, - d: Date.now() - }); - - } - this.websocket.onopen = function () { - - self.debug("websocket conn open"); - this.sendData("onopen"); - - } - this.websocket.sendData = function (why) { - if (this.readyState == 1 && !sentInitData && self.token) { - sentInitData = true; - var connDat = { - op: 2, - d: { - token: self.token, - v: 2 - } - }; - - connDat.d.properties = Internal.WebSocket.properties; - this.sendPacket(connDat); - } - } -} - -exports.Client.prototype.debug = function (msg) { - this.triggerEvent("debug", ["[SL " + tp(globalLoginTime) + "] " + msg]); -} - -/** - * Logs the current Client out of Discord and closes any connections. - * @param {Function} callback Called after a response is obtained. - * @param {Object} callback.error Null unless there was an error, in which case is an XHR error. - * @method logout - */ -exports.Client.prototype.logout = function (callback) { - - callback = callback || function () { }; - - var self = this; - - Internal.XHR.logout(self.token, function (err) { - if (err) { - callback(err); - } - self.loggedIn = false; - self.websocket.close(); - }); - -} - -/** - * Creates a server with the specified name and region and returns it - * @param {String} name The name of the server - * @param {String} region The region of the server - * @param {Function} callback Called when the request is made. - * @param {Object} callback.error An XHR error or null if there were no errors. - * @param {Server} callback.server A Server object representing the created server. - * @method createServer - * @async - */ -exports.Client.prototype.createServer = function (name, region, cb) { - - var self = this; - - Internal.XHR.createServer(self.token, name, region, function (err, data) { - - if (err) { - cb(err); - } else { - - self.cacheServer(data, function (server) { - cb(null, server); - }); - - } - - }); - -} - -/** - * Makes the Client leave the Server - * @param {Server} server A server object. The server you want to leave. - * @param {Function} callback Called when the leave request is made. - * @param {Object} callback.error An XHR error or null if there were no errors. - * @param {Server} callback.server A Server object representing the deleted server. - * @method leaveServer - * @async - */ -exports.Client.prototype.leaveServer = function (server, callback) { - - var self = this; - - // callback is not necessary for this function - callback = callback || function () { }; - - Internal.XHR.leaveServer(self.token, server.id, function (err) { - - if (err) { - callback(err); - } else { - self.serverList.removeElement(server); - callback(null, server); - } - - }); - -} - -/** - * Creates an Invite to the specified channel/server with the specified options - * @param {Channel/Server} channel The channel/server the invite is to be made to. - * @param {Object} [options] The options for the invite - * @param {Number} [options.max_age=0] When the invite will expire in seconds - * @param {Number} [options.max_uses=0] How many uses the invite has - * @param {Boolean} [options.temporary=false] Whether the invite is temporary - * @param {Boolean} [options.xkcdpass=false] Whether the invite's code should be composed of words. - * @param {Function} callback Called when the invite request has been made - * @param {Object} callback.error An XHR Error or null if there were no errors. - * @param {Invite} callback.invite An invite object representing the created invite. - * @method createInvite - */ -exports.Client.prototype.createInvite = function (channel, options, callback) { - - var self = this; - var options = options || {}; - - // callback is not necessary for this function - callback = callback || function () { }; - - if (channel instanceof Server) { - channel = channel.getDefaultChannel(); - } - - options.max_age = options.max_age || 0; - options.max_uses = options.max_uses || 0; - options.temporary = options.temporary || false; - options.xkcdpass = options.xkcd || false; - - Internal.XHR.createInvite(self.token, channel.id, options, function (err, data) { - - if (err) { - callback(err); - } else { - callback(null, new Invite(data)); - } - - }); - -} - -exports.Client.prototype.startPM = function (user, callback) { - - var self = this; - - callback = callback || function () { }; - - Internal.XHR.startPM(self.token, self.user.id, user.id, function (err, data) { - - if (err) { - callback(err); - } else { - var channel = new PMChannel(data.recipient, data.id); - self.PMList.add(channel); - callback(null, channel); - } - - }); - -} - -/** - * Sends a message to the specified destination. - * @param {Server/Channel/PMChannel/Message/User/String} destination Where the message should be sent. If this is a String, the String should be a channel ID. - * @param {String/Array/Message} toSend The message to send. If an array, the array will be seperated into new lines and then sent. - * @param {Function} callback Called when the message has been sent. - * @param {Object} error An XHR Error or null if there were no errors. - * @param {Message} message A message object representing the sent object. - * @param {Object} [options] An object containing options for the message. - * @param {Array/Boolean/String} [options.mentions=true] If an Array, it should be an array of User IDs. If a boolean, false will - * notify no-one, and true will figure out who should be mentioned based on the message. If a String, should be a User - * ID. - * @param {Number} [options.selfDestruct=false] If specified, should be the amount of milliseconds at which the message should - * delete itself after being sent. - * @method sendMessage - */ - -exports.Client.prototype.sendFile = function (destination, toSend, fileName, callback, options) { - - this.sendMessage(destination, toSend, callback, options, fileName); - -} - -exports.Client.prototype.sendMessage = function (destination, toSend, callback, options, fileName) { - - options = options || {}; - callback = callback || function () { }; - - var channel_id, message, mentions, self = this; - - channel_id = resolveChannel(destination, self); - if (!fileName) { - message = resolveMessage(toSend); - mentions = resolveMentions(message, options.mention); - } - - if (channel_id) { - send(); - } else { - //a channel is being sorted - } - - function send() { - - if (fileName) { - Internal.XHR.sendFile(self.token, channel_id, toSend, fileName, function (err) { - - callback(err); - - }); - return; - } - - Internal.XHR.sendMessage(self.token, channel_id, { - content: message, - mentions: mentions - }, function (err, data) { - if (err) { - callback(err); - } else { - var msg = new Message(data, self.getChannel(data.channel_id)); - if (options.selfDestruct) { - setTimeout(function () { - self.deleteMessage(msg); - }, options.selfDestruct); - } - callback(null, msg); - } - }); - } - - function setChannId(id) { - channel_id = id; - } - - function resolveChannel(destination, self) { - var channel_id = false; - if (destination instanceof Server) { - channel_id = destination.getDefaultChannel().id; - } else if (destination instanceof Channel) { - channel_id = destination.id; - } else if (destination instanceof PMChannel) { - channel_id = destination.id; - } else if (destination instanceof Message) { - channel_id = destination.channel.id; - } else if (destination instanceof User) { - var destId = self.PMList.deepFilter(["user", "id"], destination.id, true); - - if (destId) { - channel_id = destId.id; - } else { - //start a PM and then get that use that - self.startPM(destination, function (err, channel) { - if (err) { - callback(err); - } else { - self.PMList.add(new PMChannel(channel.user, channel.id)); - setChannId(channel.id); - send(); - } - }); - return false; - } - } else { - channel_id = destination; - } - return channel_id; - } - - function resolveMessage(toSend) { - var message; - if (typeof toSend === "string" || toSend instanceof String) - message = toSend; - else if (toSend instanceof Array) - message = toSend.join("\n"); - else if (toSend instanceof Message) - message = toSend.content; - else - message = toSend; - return message.substring(0, 2000); - } - - function resolveMentions(message, mentionsOpt) { - var mentions = []; - if (mentionsOpt === false) { } else if (mentionsOpt || mentionsOpt === "auto" || mentionsOpt == null || mentionsOpt == undefined) { - for (mention of (message.match(/<@[^>]*>/g) || [])) { - mentions.push(mention.substring(2, mention.length - 1)); - } - } else if (mentionsOpt instanceof Array) { - for (mention of mentionsOpt) { - if (mention instanceof User) { - mentions.push(mention.id); - } else { - mentions.push(mention); - } - } - } - return mentions; - } -} - -/** - * Deletes the specified message if the bot has authority - * @param {Message} message The message to delete - * @param {Function} callback Called after the message deletion request is sent. - * @param {Object} callback.error If there was an error, this would be an XHR Error object. Otherwise, it will be null. - * @param {Message} callback.message A message object representing the deleted object. - * @method deleteMessage - */ -exports.Client.prototype.deleteMessage = function (message, callback) { - callback = callback || function () { }; - - var self = this; - - Internal.XHR.deleteMessage(self.token, message.channel.id, message.id, callback); -} - -exports.Client.prototype.updateMessage = function (oldMessage, newContent, callback, options) { - - var self = this; - var channel = oldMessage.channel; - options = options || {}; - - Internal.XHR.updateMessage(self.token, channel.id, oldMessage.id, { - content: newContent, - mentions: [] - }, function (err, data) { - if (err) { - callback(err); - return; - } - var msg = new Message(data, self.getChannel(data.channel_id)); - if (options.selfDestruct) { - setTimeout(function () { - self.deleteMessage(msg); - }, options.selfDestruct); - } - callback(null, msg); - }); - -} - -exports.Client.prototype.setUsername = function (username, callback) { - - var self = this; - - Internal.XHR.setUsername(self.token, self.user.avatar, self.email, null, self.password, username, function (err) { - - callback(err); - - }); - -} - -exports.Client.prototype.getChannelLogs = function (channel, amount, callback) { - var self = this; - - Internal.XHR.getChannelLogs(self.token, channel.id, (amount || 50), function (err, data) { - - if (err) { - callback(err); - return; - } - - var logs = new List("id"); - for (message of data) { - logs.add(new Message(message, channel)); - } - callback(null, logs); - - }); -} - -exports.Client.prototype.createChannel = function (server, name, type, callback) { - - var self = this; - - Internal.XHR.createChannel(self.token, server.id, name, type, function (err, data) { - - if (err) { - callback(err); - } else { - var chann = new Channel(data, server); - server.channels.add(chann); - callback(null, chann); - } - - }); -} - -exports.Client.prototype.deleteChannel = function (channel, callback) { - var self = this; - - Internal.XHR.deleteChannel(self.token, channel.id, function (err) { - - channel.server.channels.removeElement(channel); - self.triggerEvent("channelDelete", [channel]); - callback(null); - - }); -} - -exports.Client.prototype.deleteServer = function (server, callback) { - - var self = this; - - Internal.XHR.deleteServer(self.token, server.id, function (err) { - - self.serverList.removeElement(server); - self.triggerEvent("serverDelete", [server]); - callback(null); - - }); - -} - -exports.Client.prototype.joinServer = function (invite, callback) { - - var self = this; - - var code = (invite instanceof Invite ? invite.code : invite); - - Internal.XHR.acceptInvite(self.token, code, function (err, inviteData) { - - if (err) { - callback(err); - } else { - serverCreateRequests[inviteData.guild.id] = callback; - } - - }); - -} - -exports.Client.prototype.getServers = function () { - return this.serverList; -} - -exports.Client.prototype.getChannels = function () { - return this.serverList.concatSublists("channels", "id"); -} - -exports.Client.prototype.getUsers = function () { - return this.getServers().concatSublists("members", "id"); -} - -exports.Client.prototype.getServer = function (id) { - return this.getServers().filter("id", id, true); -} - -exports.Client.prototype.getChannel = function (id) { - var normalChan = this.getChannels().filter("id", id, true); - return normalChan || this.PMList.filter("id", id, true); -} - -exports.Client.prototype.getChannelByName = function (name) { - return this.getChannels().filter("name", name, true); -} - -exports.Client.prototype.getUser = function (id) { - return this.getUsers().filter("id", id, true); -} - -exports.isUserID = function (id) { - return ((id + "").length === 17 && !isNaN(id)); -} - -exports.Client.prototype.updateUserReferences = function (id, oldUser, user) { - - if (oldUser.equalsStrict(user)) { - return false; - } - - for (server of this.serverList.contents) { - - server.members.updateElement(oldUser, user); - - } - - this.debug("Updated references to " + oldUser.username + " to " + this.getUser(id).username); - return true; - -} - -exports.Client.prototype.addPM = function (pm) { - this.PMList.add(pm); -} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..0438b79f6 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "commonjs" + } +} \ No newline at end of file diff --git a/lib/Client.js b/lib/Client.js new file mode 100644 index 000000000..23b4fd713 --- /dev/null +++ b/lib/Client.js @@ -0,0 +1,1365 @@ +//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"); +var Server = require("./Server.js"); +var Channel = require("./Channel.js"); +var Message = require("./Message.js"); +var Invite = require("./Invite.js"); +var PMChannel = require("./PMChannel.js"); + +//node modules +var request = require("superagent"); +var WebSocket = require("ws"); + +var defaultOptions = { + cache_tokens: false +}; + +var Client = (function () { + function Client() { + var options = arguments.length <= 0 || arguments[0] === undefined ? defaultOptions : arguments[0]; + var token = arguments.length <= 1 || arguments[1] === undefined ? undefined : arguments[1]; + + _classCallCheck(this, Client); + + /* + When created, if a token is specified the Client will + try connecting with it. If the token is incorrect, no + further efforts will be made to connect. + */ + this.options = options; + this.token = token; + this.state = 0; + this.websocket = null; + this.events = new Map(); + this.user = null; + this.alreadySentData = false; + this.serverCreateListener = new Map(); + + this.email = "abc"; + this.password = "abc"; + + /* + State values: + 0 - idle + 1 - logging in + 2 - logged in + 3 - ready + 4 - disconnected + */ + + this.userCache = []; + this.channelCache = []; + this.serverCache = []; + this.pmChannelCache = []; + this.readyTime = null; + } + + _createClass(Client, [{ + key: "sendPacket", + value: function sendPacket(JSONObject) { + if (this.websocket.readyState === 1) { + this.websocket.send(JSON.stringify(JSONObject)); + } + } + + //def debug + }, { + key: "debug", + value: function debug(message) { + console.log(message); + } + }, { + key: "on", + value: function on(event, fn) { + this.events.set(event, fn); + } + }, { + key: "off", + value: function off(event, fn) { + this.events["delete"](event); + } + }, { + key: "keepAlive", + value: function keepAlive() { + this.debug("keep alive triggered"); + this.sendPacket({ + op: 1, + d: Date.now() + }); + } + + //def trigger + }, { + key: "trigger", + value: function trigger(event) { + var args = []; + for (var arg in arguments) { + args.push(arguments[arg]); + } + var evt = this.events.get(event); + if (evt) { + evt.apply(this, args.slice(1)); + } + } + + //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 ? "pass1234" : arguments[1]; + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, token) {} : arguments[2]; + + var self = this; + + this.createws(); + return new Promise(function (resolve, reject) { + if (self.state === 0 || self.state === 4) { + + self.state = 1; //set the state to logging in + + self.email = email; + self.password = password; + + 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); + reject(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); + resolve(self.token); + } + }); + } else { + reject(new Error("Client already logging in or ready")); + } + }); + } + }, { + key: "logout", + value: function logout() { + var callback = arguments.length <= 0 || arguments[0] === undefined ? function (err) {} : arguments[0]; + + var self = this; + + return new Promise(function (resolve, reject) { + + request.post(Endpoints.LOGOUT).set("authorization", self.token).end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + self.state = 4; + callback(); + resolve(); + } + }); + }); + } + }, { + key: "createServer", + value: function createServer(name, region) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, server) {} : arguments[2]; + + var self = this; + return new Promise(function (resolve, reject) { + + request.post(Endpoints.SERVERS).set("authorization", self.token).send({ + name: name, + region: region + }).end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + // potentially redundant in future + // creating here does NOT give us the channels of the server + // so we must wait for the guild_create event. + self.serverCreateListener.set(res.body.id, [resolve, callback]); + /*var srv = self.addServer(res.body); + callback(null, srv); + resolve(srv);*/ + } + }); + }); + } + }, { + key: "createChannel", + value: function createChannel(server, channelName, channelType) { + var callback = arguments.length <= 3 || arguments[3] === undefined ? function (err, chann) {} : arguments[3]; + + var self = this; + + return new Promise(function (resolve, reject) { + + request.post(Endpoints.SERVERS + "/" + self.resolveServerID(server) + "/channels").set("authorization", self.token).send({ + name: channelName, + type: channelType + }).end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + var server = self.getServer("id", res.body.guild_id); + var chann = self.addChannel(res.body, res.body.guild_id); + server.addChannel(chann); + callback(null, chann); + resolve(chann); + } + }); + }); + } + }, { + key: "leaveServer", + value: function leaveServer(server) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, server) {} : arguments[1]; + + var self = this; + + return new Promise(function (resolve, reject) { + + request.del(Endpoints.SERVERS + "/" + self.resolveServerID(server)).set("authorization", self.token).end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + self.serverCache.splice(self.serverCache.indexOf(server), 1); + callback(null); + resolve(); + } + }); + }); + } + }, { + key: "createInvite", + value: function createInvite(serverOrChannel, options) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, invite) {} : arguments[2]; + + var self = this; + + return new Promise(function (resolve, reject) { + + var destination; + + if (serverOrChannel instanceof Server) { + destination = serverOrChannel.id; + } else if (serverOrChannel instanceof Channel) { + destination = serverOrChannel.id; + } else { + destination = serverOrChannel; + } + + options = options || {}; + options.max_age = options.maxAge || 0; + options.max_uses = options.maxUses || 0; + options.temporary = options.temporary || false; + options.xkcdpass = options.xkcd || false; + + request.post(Endpoints.CHANNELS + "/" + destination + "/invites").set("authorization", self.token).send(options).end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + var inv = new Invite(res.body, self); + callback(null, inv); + resolve(inv); + } + }); + }); + } + }, { + key: "startPM", + value: function startPM(user) { + + var self = this; + + return new Promise(function (resolve, reject) { + var userId = user; + if (user instanceof User) { + userId = user.id; + } + request.post(Endpoints.USERS + "/" + self.user.id + "/channels").set("authorization", self.token).send({ + recipient_id: userId + }).end(function (err, res) { + if (err) { + reject(err); + } else { + resolve(self.addPMChannel(res.body)); + } + }); + }); + } + }, { + key: "reply", + value: function reply(destination, message) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, msg) {} : arguments[2]; + + var self = this; + + return new Promise(function (response, reject) { + + var user = destination.sender; + self.sendMessage(destination, message, callback, user + ", ").then(response)["catch"](reject); + }); + } + }, { + key: "deleteMessage", + value: function deleteMessage(message, timeout) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, msg) {} : arguments[2]; + + var self = this; + + return new Promise(function (resolve, reject) { + if (timeout) { + setTimeout(remove, timeout); + } else { + remove(); + } + + function remove() { + request.del(Endpoints.CHANNELS + "/" + message.channel.id + "/messages/" + message.id).set("authorization", self.token).end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + callback(null); + resolve(); + } + }); + } + }); + } + }, { + key: "updateMessage", + value: function updateMessage(message, content) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, msg) {} : arguments[2]; + + var self = this; + + return new Promise(function (resolve, reject) { + + content = content instanceof Array ? content.join("\n") : content; + + request.patch(Endpoints.CHANNELS + "/" + message.channel.id + "/messages/" + message.id).set("authorization", self.token).send({ + content: content, + mentions: [] + }).end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + var msg = new Message(res.body, message.channel, message.mentions, message.sender); + callback(null, msg); + resolve(msg); + + message.channel.messages[message.channel.messages.indexOf(message)] = msg; + } + }); + }); + } + }, { + key: "setUsername", + value: function setUsername(newName) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err) {} : arguments[1]; + + var self = this; + + return new Promise(function (resolve, reject) { + request.patch(Endpoints.API + "/users/@me").set("authorization", self.token).send({ + avatar: self.user.avatar, + email: self.email, + new_password: null, + password: self.password, + username: newName + }).end(function (err) { + callback(err); + if (err) reject(err);else resolve(); + }); + }); + } + }, { + key: "getChannelLogs", + value: function getChannelLogs(channel) { + var amount = arguments.length <= 1 || arguments[1] === undefined ? 500 : arguments[1]; + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, logs) {} : arguments[2]; + + var self = this; + + return new Promise(function (resolve, reject) { + + var channelID = channel; + if (channel instanceof Channel) { + channelID = channel.id; + } + + request.get(Endpoints.CHANNELS + "/" + channelID + "/messages?limit=" + amount).set("authorization", self.token).end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + var logs = []; + + var channel = self.getChannel("id", channelID); + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = res.body[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var message = _step.value; + + var mentions = []; + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = message.mentions[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var mention = _step2.value; + + mentions.push(self.addUser(mention)); + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2["return"]) { + _iterator2["return"](); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } + + var author = self.addUser(message.author); + + logs.push(new Message(message, channel, mentions, author)); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + callback(null, logs); + resolve(logs); + } + }); + }); + } + }, { + key: "deleteChannel", + value: function deleteChannel(channel) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err) {} : arguments[1]; + + var self = this; + + return new Promise(function (resolve, reject) { + + var channelID = channel; + if (channel instanceof Channel) { + channelID = channel.id; + } + + request.del(Endpoints.CHANNELS + "/" + channelID).set("authorization", self.token).end(function (err) { + if (err) { + callback(err); + reject(err); + } else { + callback(null); + resolve(); + } + }); + }); + } + }, { + key: "joinServer", + value: function joinServer(invite) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, server) {} : arguments[1]; + + var self = this; + + return new Promise(function (resolve, reject) { + + var id = invite instanceof Invite ? invite.code : invite; + + request.post(Endpoints.API + "/invite/" + id).set("authorization", self.token).end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + self.serverCreateListener.set(res.body.guild.id, [resolve, callback]); + } + }); + }); + } + }, { + key: "sendMessage", + value: function sendMessage(destination, message) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function (err, msg) {} : arguments[2]; + var premessage = arguments.length <= 3 || arguments[3] === undefined ? "" : arguments[3]; + + var self = this; + + return new Promise(function (resolve, reject) { + + message = premessage + resolveMessage(message); + var mentions = resolveMentions(); + destination = resolveDestination(destination); + + if (destination) send(); + + function send() { + + request.post(Endpoints.CHANNELS + "/" + destination + "/messages").set("authorization", self.token).send({ + content: message, + mentions: mentions + }).end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + var data = res.body; + + var mentions = []; + + data.mentions = data.mentions || []; //for some reason this was not defined at some point? + + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = data.mentions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var mention = _step3.value; + + mentions.push(self.addUser(mention)); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3["return"]) { + _iterator3["return"](); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } + + var channel = self.getChannel("id", data.channel_id); + if (channel) { + var msg = channel.addMessage(new Message(data, channel, mentions, self.addUser(data.author))); + callback(null, msg); + resolve(msg); + } + } + }); + } + + function resolveDestination() { + var channId = false; + + if (destination instanceof Server) { + channId = destination.id; //general is the same as server id + } else if (destination instanceof Channel) { + channId = destination.id; + } else if (destination instanceof Message) { + channId = destination.channel.id; + } else if (destination instanceof User) { + + //check if we have a PM + var _iteratorNormalCompletion4 = true; + var _didIteratorError4 = false; + var _iteratorError4 = undefined; + + try { + for (var _iterator4 = self.pmChannelCache[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { + var pmc = _step4.value; + + if (pmc.user.equals(destination)) { + return pmc.id; + } + } + + //we don't, at this point we're late + } catch (err) { + _didIteratorError4 = true; + _iteratorError4 = err; + } finally { + try { + if (!_iteratorNormalCompletion4 && _iterator4["return"]) { + _iterator4["return"](); + } + } finally { + if (_didIteratorError4) { + throw _iteratorError4; + } + } + } + + self.startPM(destination).then(function (pmc) { + destination = pmc.id; + send(); + }); + } else { + channId = destination; + } + + return channId; + } + + function resolveMessage() { + var msg = message; + if (message instanceof Array) { + msg = message.join("\n"); + } + return msg; + } + + function resolveMentions() { + var _mentions = []; + var _iteratorNormalCompletion5 = true; + var _didIteratorError5 = false; + var _iteratorError5 = undefined; + + try { + for (var _iterator5 = (message.match(/<@[^>]*>/g) || [])[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { + var mention = _step5.value; + + _mentions.push(mention.substring(2, mention.length - 1)); + } + } catch (err) { + _didIteratorError5 = true; + _iteratorError5 = err; + } finally { + try { + if (!_iteratorNormalCompletion5 && _iterator5["return"]) { + _iterator5["return"](); + } + } finally { + if (_didIteratorError5) { + throw _iteratorError5; + } + } + } + + return _mentions; + } + }); + } + + //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 = {}; + + 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); + + var _iteratorNormalCompletion6 = true; + var _didIteratorError6 = false; + var _iteratorError6 = undefined; + + try { + for (var _iterator6 = data.guilds[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { + var _server = _step6.value; + + var server = self.addServer(_server); + } + } catch (err) { + _didIteratorError6 = true; + _iteratorError6 = err; + } finally { + try { + if (!_iteratorNormalCompletion6 && _iterator6["return"]) { + _iterator6["return"](); + } + } finally { + if (_didIteratorError6) { + throw _iteratorError6; + } + } + } + + var _iteratorNormalCompletion7 = true; + var _didIteratorError7 = false; + var _iteratorError7 = undefined; + + try { + for (var _iterator7 = data.private_channels[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { + var _pmc = _step7.value; + + var pmc = self.addPMChannel(_pmc); + } + } catch (err) { + _didIteratorError7 = true; + _iteratorError7 = err; + } finally { + try { + if (!_iteratorNormalCompletion7 && _iterator7["return"]) { + _iterator7["return"](); + } + } finally { + if (_didIteratorError7) { + throw _iteratorError7; + } + } + } + + self.trigger("ready"); + self.readyTime = Date.now(); + self.debug("cached " + self.serverCache.length + " servers, " + self.channelCache.length + " channels, " + self.pmChannelCache.length + " PMs and " + self.userCache.length + " users."); + self.state = 3; + setInterval(function () { + self.keepAlive.apply(self); + }, data.heartbeat_interval); + + break; + case "MESSAGE_CREATE": + self.debug("received message"); + + var mentions = []; + data.mentions = data.mentions || []; //for some reason this was not defined at some point? + var _iteratorNormalCompletion8 = true; + var _didIteratorError8 = false; + var _iteratorError8 = undefined; + + try { + for (var _iterator8 = data.mentions[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { + var mention = _step8.value; + + mentions.push(self.addUser(mention)); + } + } catch (err) { + _didIteratorError8 = true; + _iteratorError8 = err; + } finally { + try { + if (!_iteratorNormalCompletion8 && _iterator8["return"]) { + _iterator8["return"](); + } + } finally { + if (_didIteratorError8) { + throw _iteratorError8; + } + } + } + + var channel = self.getChannel("id", data.channel_id); + if (channel) { + var msg = channel.addMessage(new Message(data, channel, mentions, self.addUser(data.author))); + self.trigger("message", msg); + } + + break; + case "MESSAGE_DELETE": + self.debug("message deleted"); + + var channel = self.getChannel("id", data.channel_id); + var message = channel.getMessage("id", data.id); + if (message) { + self.trigger("messageDelete", channel, message); + channel.messages.splice(channel.messages.indexOf(message), 1); + } else { + //don't have the cache of that message ;( + self.trigger("messageDelete", channel); + } + break; + case "MESSAGE_UPDATE": + self.debug("message updated"); + + var channel = self.getChannel("id", data.channel_id); + var formerMessage = channel.getMessage("id", data.id); + + if (formerMessage) { + + //new message might be partial, so we need to fill it with whatever the old message was. + var info = {}; + + for (var key in formerMessage) { + info[key] = formerMessage[key]; + } + + for (var key in data) { + info[key] = data[key]; + } + + var mentions = []; + var _iteratorNormalCompletion9 = true; + var _didIteratorError9 = false; + var _iteratorError9 = undefined; + + try { + for (var _iterator9 = info.mentions[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { + var mention = _step9.value; + + mentions.push(self.addUser(mention)); + } + } catch (err) { + _didIteratorError9 = true; + _iteratorError9 = err; + } finally { + try { + if (!_iteratorNormalCompletion9 && _iterator9["return"]) { + _iterator9["return"](); + } + } finally { + if (_didIteratorError9) { + throw _iteratorError9; + } + } + } + + var newMessage = new Message(info, channel, mentions, formerMessage.author); + + self.trigger("messageUpdate", newMessage, formerMessage); + + channel.messages[channel.messages.indexOf(formerMessage)] = newMessage; + } + + // message isn't in cache, and if it's a partial it could cause + // all hell to break loose... best to just act as if nothing happened + + break; + + case "GUILD_DELETE": + + var server = self.getServer("id", data.id); + + if (server) { + self.serverCache.splice(self.serverCache.indexOf(server), 1); + self.trigger("serverDelete", server); + } + + break; + + case "CHANNEL_DELETE": + + var channel = self.getChannel("id", data.id); + + if (channel) { + + var server = channel.server; + + if (server) { + + server.channels.splice(server.channels.indexOf(channel), 1); + } + + self.trigger("channelDelete", channel); + + self.serverCache.splice(self.serverCache.indexOf(channel), 1); + } + + break; + + case "GUILD_CREATE": + + var server = self.getServer("id", data.id); + + if (!server) { + //if server doesn't already exist because duh + server = self.addServer(data); + } /*else if(server.channels.length === 0){ + + var srv = new Server(data, self); + for(channel of data.channels){ + srv.channels.push(new Channel(channel, data.id)); + } + self.serverCache[self.serverCache.indexOf(server)] = srv; + + }*/ + + if (self.serverCreateListener.get(data.id)) { + var cbs = self.serverCreateListener.get(data.id); + cbs[0](server); //promise then callback + cbs[1](null, server); //legacy callback + self.serverCreateListener["delete"](data.id); + } + + self.trigger("serverCreate", server); + + break; + + case "CHANNEL_CREATE": + + var channel = self.getChannel("id", data.id); + + if (!channel) { + + var chann = self.addChannel(data, data.guild_id); + var srv = self.getServer("id", data.guild_id); + if (srv) { + srv.addChannel(chann); + } + self.trigger("channelCreate", chann); + } + + break; + + case "GUILD_MEMBER_ADD": + + var server = self.getServer("id", data.guild_id); + + if (server) { + + var user = self.addUser(data.user); //if for whatever reason it doesn't exist.. + + if (! ~server.members.indexOf(user)) { + server.members.push(user); + } + + self.trigger("serverNewMember", user); + } + + break; + + case "GUILD_MEMBER_REMOVE": + + var server = self.getServer("id", data.guild_id); + + if (server) { + + var user = self.addUser(data.user); //if for whatever reason it doesn't exist.. + + if (~server.members.indexOf(user)) { + server.members.splice(server.members.indexOf(user), 1); + } + + self.trigger("serverRemoveMember", user); + } + + break; + + case "USER_UPDATE": + + if (self.user && data.id === self.user.id) { + + var newUser = new User(data); //not actually adding to the cache + + self.trigger("userUpdate", newUser, self.user); + + if (~self.userCache.indexOf(self.user)) { + self.userCache[self.userCache.indexOf(self.user)] = newUser; + } + + self.user = newUser; + } + + break; + + case "PRESENCE_UPDATE": + + var userInCache = self.getUser("id", data.user.id); + + if (userInCache) { + //user exists + var presenceUser = new User(data.user); + if (presenceUser.equalsStrict(userInCache)) { + //they're exactly the same, an actual presence update + self.trigger("presence", { + user: userInCache, + status: data.status, + server: self.getServer("id", data.guild_id), + gameId: data.game_id + }); + } else { + //one of their details changed. + self.trigger("userUpdate", userInCache, presenceUser); + self.userCache[self.userCache.indexOf(userInCache)] = presenceUser; + } + } + + break; + + default: + self.debug("received unknown packet"); + self.trigger("unknown", dat); + break; + + } + }; + } + + //def addUser + }, { + key: "addUser", + value: function addUser(data) { + if (!this.getUser("id", data.id)) { + this.userCache.push(new User(data)); + } + return this.getUser("id", data.id); + } + + //def addChannel + }, { + key: "addChannel", + value: function addChannel(data, serverId) { + if (!this.getChannel("id", data.id)) { + this.channelCache.push(new Channel(data, this.getServer("id", serverId))); + } + return this.getChannel("id", data.id); + } + }, { + key: "addPMChannel", + value: function addPMChannel(data) { + if (!this.getPMChannel("id", data.id)) { + this.pmChannelCache.push(new PMChannel(data, this)); + } + return this.getPMChannel("id", data.id); + } + + //def addServer + }, { + key: "addServer", + value: function addServer(data) { + + var server = this.getServer("id", data.id); + + if (!server) { + server = new Server(data, this); + this.serverCache.push(server); + if (data.channels) { + var _iteratorNormalCompletion10 = true; + var _didIteratorError10 = false; + var _iteratorError10 = undefined; + + try { + for (var _iterator10 = data.channels[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { + var channel = _step10.value; + + server.channels.push(this.addChannel(channel, server.id)); + } + } catch (err) { + _didIteratorError10 = true; + _iteratorError10 = err; + } finally { + try { + if (!_iteratorNormalCompletion10 && _iterator10["return"]) { + _iterator10["return"](); + } + } finally { + if (_didIteratorError10) { + throw _iteratorError10; + } + } + } + } + } + + return server; + } + + //def getUser + }, { + key: "getUser", + value: function getUser(key, value) { + var _iteratorNormalCompletion11 = true; + var _didIteratorError11 = false; + var _iteratorError11 = undefined; + + try { + for (var _iterator11 = this.userCache[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { + var user = _step11.value; + + if (user[key] === value) { + return user; + } + } + } catch (err) { + _didIteratorError11 = true; + _iteratorError11 = err; + } finally { + try { + if (!_iteratorNormalCompletion11 && _iterator11["return"]) { + _iterator11["return"](); + } + } finally { + if (_didIteratorError11) { + throw _iteratorError11; + } + } + } + + return null; + } + + //def getChannel + }, { + key: "getChannel", + value: function getChannel(key, value) { + var _iteratorNormalCompletion12 = true; + var _didIteratorError12 = false; + var _iteratorError12 = undefined; + + try { + for (var _iterator12 = this.channelCache[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) { + var channel = _step12.value; + + if (channel[key] === value) { + return channel; + } + } + } catch (err) { + _didIteratorError12 = true; + _iteratorError12 = err; + } finally { + try { + if (!_iteratorNormalCompletion12 && _iterator12["return"]) { + _iterator12["return"](); + } + } finally { + if (_didIteratorError12) { + throw _iteratorError12; + } + } + } + + return this.getPMChannel(key, value); //might be a PM + } + }, { + key: "getPMChannel", + value: function getPMChannel(key, value) { + var _iteratorNormalCompletion13 = true; + var _didIteratorError13 = false; + var _iteratorError13 = undefined; + + try { + for (var _iterator13 = this.pmChannelCache[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) { + var channel = _step13.value; + + if (channel[key] === value) { + return channel; + } + } + } catch (err) { + _didIteratorError13 = true; + _iteratorError13 = err; + } finally { + try { + if (!_iteratorNormalCompletion13 && _iterator13["return"]) { + _iterator13["return"](); + } + } finally { + if (_didIteratorError13) { + throw _iteratorError13; + } + } + } + + return null; + } + + //def getServer + }, { + key: "getServer", + value: function getServer(key, value) { + var _iteratorNormalCompletion14 = true; + var _didIteratorError14 = false; + var _iteratorError14 = undefined; + + try { + for (var _iterator14 = this.serverCache[Symbol.iterator](), _step14; !(_iteratorNormalCompletion14 = (_step14 = _iterator14.next()).done); _iteratorNormalCompletion14 = true) { + var server = _step14.value; + + if (server[key] === value) { + return server; + } + } + } catch (err) { + _didIteratorError14 = true; + _iteratorError14 = err; + } finally { + try { + if (!_iteratorNormalCompletion14 && _iterator14["return"]) { + _iterator14["return"](); + } + } finally { + if (_didIteratorError14) { + throw _iteratorError14; + } + } + } + + return null; + } + + //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)); + } + } + }, { + key: "resolveServerID", + value: function resolveServerID(resource) { + + if (resource instanceof Server) { + return resource.id; + } else if (!isNaN(resource) && resource.length && resource.length === 17) { + return resource; + } + } + }, { + key: "uptime", + get: function get() { + + return this.readyTime ? Date.now() - this.readyTime : null; + } + }, { + key: "ready", + get: function get() { + return this.state === 3; + } + }, { + key: "servers", + get: function get() { + return this.serverCache; + } + }, { + key: "channels", + get: function get() { + return this.channelCache; + } + }, { + key: "users", + get: function get() { + return this.userCache; + } + }, { + key: "PMChannels", + get: function get() { + return this.pmChannelCache; + } + }, { + key: "messages", + get: function get() { + + var msgs = []; + var _iteratorNormalCompletion15 = true; + var _didIteratorError15 = false; + var _iteratorError15 = undefined; + + try { + for (var _iterator15 = this.channelCache[Symbol.iterator](), _step15; !(_iteratorNormalCompletion15 = (_step15 = _iterator15.next()).done); _iteratorNormalCompletion15 = true) { + var channel = _step15.value; + + msgs = msgs.concat(channel.messages); + } + } catch (err) { + _didIteratorError15 = true; + _iteratorError15 = err; + } finally { + try { + if (!_iteratorNormalCompletion15 && _iterator15["return"]) { + _iterator15["return"](); + } + } finally { + if (_didIteratorError15) { + throw _iteratorError15; + } + } + } + + return msgs; + } + }]); + + return Client; +})(); + +module.exports = Client; \ No newline at end of file diff --git a/lib/PMChannel.js b/lib/PMChannel.js index 1bfe3c155..ae44d3d60 100644 --- a/lib/PMChannel.js +++ b/lib/PMChannel.js @@ -1,6 +1,61 @@ -var User = require("./user.js").User; +"use strict"; -exports.PMChannel = function(user, id){ - this.user = new User(user); - this.id = id; -} +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 PMChannel = (function () { + function PMChannel(data, client) { + _classCallCheck(this, PMChannel); + + this.user = client.getUser("id", data.recipient.id); + this.id = data.id; + this.messages = []; + } + + _createClass(PMChannel, [{ + key: "addMessage", + value: function addMessage(data) { + if (!this.getMessage("id", data.id)) { + this.messages.push(data); + } + return this.getMessage("id", data.id); + } + }, { + key: "getMessage", + value: function getMessage(key, value) { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = this.messages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var message = _step.value; + + if (message[key] === value) { + return message; + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return null; + } + }]); + + return PMChannel; +})(); + +module.exports = PMChannel; \ No newline at end of file diff --git a/lib/TokenManager.js b/lib/TokenManager.js index ceeed9ded..22732a462 100644 --- a/lib/TokenManager.js +++ b/lib/TokenManager.js @@ -1,68 +1,67 @@ -var fs = require( "fs" ); -var crypto = require( "crypto" ); -var md5 = require( "md5" ); +"use strict"; + +var fs = require("fs"); +var crypto = require("crypto"); +var md5 = require("md5"); var tokens = {}; -exports.TokenManager = function( folder, file ) { +exports.TokenManager = function (folder, file) { this.path = folder + file; var self = this; try { - var fd = fs.openSync( self.path, "wx" ); + var fd = fs.openSync(self.path, "wx"); self.writeTokens(); - } catch ( e ) { + } catch (e) { self.readTokens(); } +}; -} - -exports.TokenManager.prototype.addToken = function( id, token, pass ) { - tokens[ md5( id ) ] = encrypt( token, pass ); +exports.TokenManager.prototype.addToken = function (id, token, pass) { + tokens[md5(id)] = encrypt(token, pass); this.writeTokens(); -} +}; -exports.TokenManager.prototype.readTokens = function() { - tokens = JSON.parse( fs.readFileSync( this.path, "utf8" ) ); - for ( tkn in tokens ) { - tokens[ tkn ] = decrypt( tokens[ tkn ], tkn ); +exports.TokenManager.prototype.readTokens = function () { + tokens = JSON.parse(fs.readFileSync(this.path, "utf8")); + for (tkn in tokens) { + tokens[tkn] = decrypt(tokens[tkn], tkn); } -} +}; -exports.TokenManager.prototype.writeTokens = function() { +exports.TokenManager.prototype.writeTokens = function () { var tkn = {}; - for ( token in tokens ) { - tkn[ token ] = encrypt( tokens[ token ], token ); + for (token in tokens) { + tkn[token] = encrypt(tokens[token], token); } - fs.writeFile( this.path, JSON.stringify( tkn ), function( err ) { + fs.writeFile(this.path, JSON.stringify(tkn), function (err) {}); +}; - } ); -} +exports.TokenManager.prototype.exists = function (id) { + return tokens[md5(id)]; +}; -exports.TokenManager.prototype.exists = function( id ) { - return tokens[ md5( id ) ]; -} +exports.TokenManager.prototype.getToken = function (id, pass) { + try { + return decrypt(tokens[md5(id)], pass); + } catch (e) { + return false; + } +}; -exports.TokenManager.prototype.getToken = function( id, pass ) { - try{ - return decrypt( tokens[ md5( id ) ], pass ); - }catch(e){ - return false; - } -} - -function encrypt( string, password ) { - var cipher = crypto.createCipher( "aes-256-ctr", password ) - var crypted = cipher.update( string, 'utf8', 'hex' ) - crypted += cipher.final( 'hex' ); +function encrypt(string, password) { + var cipher = crypto.createCipher("aes-256-ctr", password); + var crypted = cipher.update(string, 'utf8', 'hex'); + crypted += cipher.final('hex'); return crypted; } -function decrypt( string, password ) { - var decipher = crypto.createDecipher( "aes-256-ctr", password ) - var dec = decipher.update( string, 'hex', 'utf8' ) - dec += decipher.final( 'utf8' ); +function decrypt(string, password) { + var decipher = crypto.createDecipher("aes-256-ctr", password); + var dec = decipher.update(string, 'hex', 'utf8'); + dec += decipher.final('utf8'); return dec; -} +} \ No newline at end of file diff --git a/lib/asd.js b/lib/asd.js new file mode 100644 index 000000000..c74b5e209 --- /dev/null +++ b/lib/asd.js @@ -0,0 +1,63 @@ +"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 request = require("superagent"); + +var defaultOptions = { + cache_tokens: false +}; + +var Client = (function () { + function Client() { + var options = arguments.length <= 0 || arguments[0] === undefined ? defaultOptions : arguments[0]; + var token = arguments.length <= 1 || arguments[1] === undefined ? undefined : arguments[1]; + + _classCallCheck(this, Client); + + /* + When created, if a token is specified the Client will + try connecting with it. If the token is incorrect, no + further efforts will be made to connect. + */ + this.options = options; + this.token = token; + this.state = 0; + this.websocket = null; + this.events = new Map(); + this.user = null; + /* + State values: + 0 - idle + 1 - logging in + 2 - logged in + 3 - ready + 4 - disconnected + */ + } + + _createClass(Client, [{ + key: "login", + + //def 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]; + + if (this.state === 0 || this.state === 4) { + + this.state = 1; + request.post(); + } + } + }, { + key: "ready", + get: function get() { + return this.state === 3; + } + }]); + + return Client; +})(); \ No newline at end of file diff --git a/lib/channel.js b/lib/channel.js index 16a8e4a51..d3c3d2dc7 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -1,28 +1,74 @@ -var List = require("./list.js").List; +"use strict"; -exports.Channel = function(name, server, type, id, isPrivate){ +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(!type){ //there's no second argument - var channel = name; - name = channel.name; - server = server; - type = channel.type; - id = channel.id; - isPrivate = channel.is_private; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Channel = (function () { + function Channel(data, server) { + _classCallCheck(this, Channel); + + this.server = server; + this.name = data.name; + this.type = data.type; + this.id = data.id; + this.messages = []; + //this.isPrivate = isPrivate; //not sure about the implementation of this... } - this.name = name; - this.server = server; - this.type = type; - this.id = id; - this.isPrivate = isPrivate; - this.messages = new List("id", 5000); -} + _createClass(Channel, [{ + key: "equals", + value: function equals(object) { + return object.id === this.id; + } + }, { + key: "addMessage", + value: function addMessage(data) { + if (!this.getMessage("id", data.id)) { + this.messages.push(data); + } + return this.getMessage("id", data.id); + } + }, { + key: "getMessage", + value: function getMessage(key, value) { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; -exports.Channel.equals = function(otherChannel){ - if(otherChannel.id === this.id){ - return true; - } else { - return false; - } -} + try { + for (var _iterator = this.messages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var message = _step.value; + + if (message[key] === value) { + return message; + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return null; + } + }, { + key: "client", + get: function get() { + return this.server.client; + } + }]); + + return Channel; +})(); + +module.exports = Channel; \ No newline at end of file diff --git a/lib/endpoints.js b/lib/endpoints.js index 46ca08090..271b465eb 100644 --- a/lib/endpoints.js +++ b/lib/endpoints.js @@ -1,15 +1,13 @@ -var base = "https://discordapp.com/"; -var apibase = base + "api"; +"use strict"; -exports.API = apibase; +exports.BASE_DOMAIN = "discordapp.com"; +exports.BASE = "https://" + exports.BASE_DOMAIN; +exports.WEBSOCKET_HUB = "wss://" + exports.BASE_DOMAIN + "/hub"; -exports.WEBSOCKET_HUB = "wss://discordapp.com/hub" - -exports.USERS = apibase + "/users"; - -exports.LOGIN = apibase + "/auth/login"; -exports.LOGOUT = apibase + "/auth/logout"; - -exports.SERVERS = apibase + "/guilds"; - -exports.CHANNELS = apibase + "/channels"; +exports.API = exports.BASE + "/api"; +exports.AUTH = exports.API + "/auth"; +exports.LOGIN = exports.AUTH + "/login"; +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 new file mode 100644 index 000000000..fa2343ae4 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,8 @@ +"use strict"; + +var request = require("superagent"); +var Endpoints = require("./Endpoints.js"); +var Client = require("./Client.js"); + +exports.Endpoints = Endpoints; +exports.Client = Client; \ No newline at end of file diff --git a/lib/internal.js b/lib/internal.js index 42d0dc689..3acf5940b 100644 --- a/lib/internal.js +++ b/lib/internal.js @@ -1,5 +1,7 @@ -var request = require( "superagent" ); -var Endpoints = require( "./endpoints.js" ); +"use strict"; + +var request = require("superagent"); +var Endpoints = require("./endpoints.js"); var Internal = {}; @@ -14,264 +16,188 @@ Internal.WebSocket.properties = { "$referring_domain": "" }; -Internal.XHR.login = function( email, password, callback ) { +Internal.XHR.login = function (email, password, callback) { - request - .post( Endpoints.LOGIN ) - .send( { - email: email, - password: password - } ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body.token ); - } - } ); + request.post(Endpoints.LOGIN).send({ + email: email, + password: password + }).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body.token); + } + }); +}; -} +Internal.XHR.logout = function (token, callback) { -Internal.XHR.logout = function( token, callback ) { + request.post(Endpoints.LOGOUT).end(function (err, res) { - request - .post( Endpoints.LOGOUT ) - .end( function( err, res ) { + err ? callback(err) : callback(null); + }); +}; - err ? callback( err ) : callback( null ); +Internal.XHR.createServer = function (token, name, region, callback) { - } ); + request.post(Endpoints.SERVERS).set("authorization", token).send({ + name: name, + region: region + }).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; -} +Internal.XHR.leaveServer = function (token, serverId, callback) { -Internal.XHR.createServer = function( token, name, region, callback ) { + request.del(Endpoints.SERVERS + "/" + serverId).set("authorization", token).end(function (err, res) { - request - .post( Endpoints.SERVERS ) - .set( "authorization", token ) - .send( { - name: name, - region: region - } ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } - } ); -} + err ? callback(err) : callback(null); + }); +}; -Internal.XHR.leaveServer = function( token, serverId, callback ) { +Internal.XHR.createInvite = function (token, channelId, options, callback) { + request.post(Endpoints.CHANNELS + "/" + channelId + "/invites").set("authorization", token).send(options).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; - request - .del( Endpoints.SERVERS + "/" + serverId ) - .set( "authorization", token ) - .end( function( err, res ) { +Internal.XHR.startPM = function (token, selfID, userID, callback) { - err ? callback( err ) : callback( null ); + request.post(Endpoints.USERS + "/" + selfID + "/channels").set("authorization", token).send({ + recipient_id: userID + }).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; - } ); +Internal.XHR.sendMessage = function (token, channelID, messageParameters, callback) { + request.post(Endpoints.CHANNELS + "/" + channelID + "/messages").set("authorization", token).send(messageParameters).end(function (err, res) { -} + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; -Internal.XHR.createInvite = function( token, channelId, options, callback ) { - request - .post( Endpoints.CHANNELS + "/" + channelId + "/invites" ) - .set( "authorization", token ) - .send( options ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } - } ) -} +Internal.XHR.sendFile = function (token, channelID, file, fileName, callback) { + request.post(Endpoints.CHANNELS + "/" + channelID + "/messages").set("authorization", token).attach("file", file, fileName).end(function (err, res) { -Internal.XHR.startPM = function( token, selfID, userID, callback ) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; - request - .post( Endpoints.USERS + "/" + selfID + "/channels" ) - .set( "authorization", token ) - .send( { - recipient_id: userID - } ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } - } ); +Internal.XHR.deleteMessage = function (token, channelID, messageID, callback) { + request.del(Endpoints.CHANNELS + "/" + channelID + "/messages/" + messageID).set("authorization", token).end(function (err) { + err ? callback(err) : callback(null); + }); +}; -} +Internal.XHR.updateMessage = function (token, channelID, messageID, messageParameters, callback) { -Internal.XHR.sendMessage = function( token, channelID, messageParameters, callback ) { - request - .post( Endpoints.CHANNELS + "/" + channelID + "/messages" ) - .set( "authorization", token ) - .send( messageParameters ) - .end( function( err, res ) { + request.patch(Endpoints.CHANNELS + "/" + channelID + "/messages/" + messageID).set("authorization", token).send(messageParameters).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } +Internal.XHR.getChannelLogs = function (token, channelID, amount, callback) { + request.get(Endpoints.CHANNELS + "/" + channelID + "/messages?limit=" + amount).set("authorization", token).end(function (err, res) { - } ); + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; -} +Internal.XHR.createChannel = function (token, serverID, name, type, callback) { + request.post(Endpoints.SERVERS + "/" + serverID + "/channels").set("authorization", token).send({ + name: name, + type: type + }).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; -Internal.XHR.sendFile = function( token, channelID, file, fileName, callback ) { - request - .post( Endpoints.CHANNELS + "/" + channelID + "/messages" ) - .set( "authorization", token ) - .attach("file", file, fileName) - .end( function( err, res ) { +Internal.XHR.deleteChannel = function (token, channelID, callback) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } + request.del(Endpoints.CHANNELS + "/" + channelID).set("authorization", token).end(function (err) { + err ? callback(err) : callback(null); + }); +}; +Internal.XHR.deleteServer = function (token, serverID, callback) { + request.del(Endpoints.SERVERS + "/" + serverID).set("authorization", token).end(function (err) { + err ? callback(err) : callback(null); + }); +}; - } ); -} +Internal.XHR.getChannels = function (token, serverID, callback) { + request.get(Endpoints.SERVERS + "/" + serverID + "/channels").set("authorization", token).end(function (err) { + err ? callback(err) : callback(null); + }); +}; -Internal.XHR.deleteMessage = function( token, channelID, messageID, callback ) { - request - .del( Endpoints.CHANNELS + "/" + channelID + "/messages/" + messageID ) - .set( "authorization", token ) - .end( function( err ) { - err ? callback( err ) : callback( null ); - } ); -} +Internal.XHR.getServer = function (token, serverID, callback) { -Internal.XHR.updateMessage = function( token, channelID, messageID, messageParameters, callback ) { + request.get(Endpoints.SERVERS + "/" + serverID).set("authorization", token).end(function (err, res) { - request - .patch( Endpoints.CHANNELS + "/" + channelID + "/messages/" + messageID ) - .set( "authorization", token ) - .send( messageParameters ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } - } ); -} + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; -Internal.XHR.getChannelLogs = function( token, channelID, amount, callback ) { - request - .get( Endpoints.CHANNELS + "/" + channelID + "/messages?limit=" + amount ) - .set( "authorization", token ) - .end( function( err, res ) { +Internal.XHR.acceptInvite = function (token, inviteID, callback) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } + request.post(Endpoints.API + "/invite/" + inviteID).set("authorization", token).end(function (err, res) { + if (err) { + callback(err); + } else { + callback(null, res.body); + } + }); +}; - } ); -} +Internal.XHR.setUsername = function (token, avatar, email, newPassword, password, username, callback) { -Internal.XHR.createChannel = function( token, serverID, name, type, callback ) { - request - .post( Endpoints.SERVERS + "/" + serverID + "/channels" ) - .set( "authorization", token ) - .send( { - name: name, - type: type - } ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } - } ); -} + request.patch(Endpoints.API + "/users/@me").set("authorization", token).send({ + avatar: avatar, + email: email, + new_password: newPassword, + password: password, + username: username + }).end(function (err) { + callback(err); + }); +}; -Internal.XHR.deleteChannel = function( token, channelID, callback ) { - - request - .del( Endpoints.CHANNELS + "/" + channelID ) - .set( "authorization", token ) - .end( function( err ) { - err ? callback( err ) : callback( null ); - } ); - -} -Internal.XHR.deleteServer = function( token, serverID, callback ) { - request - .del( Endpoints.SERVERS + "/" + serverID ) - .set( "authorization", token ) - .end( function( err ) { - err ? callback( err ) : callback( null ); - } ); -} - -Internal.XHR.getChannels = function( token, serverID, callback ) { - request - .get( Endpoints.SERVERS + "/" + serverID + "/channels" ) - .set( "authorization", token ) - .end( function( err ) { - err ? callback( err ) : callback( null ); - } ); -} - -Internal.XHR.getServer = function( token, serverID, callback ) { - - request - .get( Endpoints.SERVERS + "/" + serverID ) - .set( "authorization", token ) - .end( function( err, res ) { - - if ( err ) { - callback( err ); - } else { - callback( null, res.body ); - } - - } ); - -} - -Internal.XHR.acceptInvite = function( token, inviteID, callback ) { - - request - .post( Endpoints.API + "/invite/" + inviteID ) - .set( "authorization", token ) - .end( function( err, res ) { - if ( err ) { - callback( err ); - } else { - callback( null, res.body ) - } - } ); - -} - -Internal.XHR.setUsername = function( token, avatar, email, newPassword, password, username, callback ) { - - request - .patch( Endpoints.API + "/users/@me" ) - .set( "authorization", token ) - .send( { - avatar: avatar, - email: email, - new_password: newPassword, - password: password, - username: username - } ) - .end( function( err ) { - callback( err ); - } ); - -} - -exports.Internal = Internal; +exports.Internal = Internal; \ No newline at end of file diff --git a/lib/invite.js b/lib/invite.js index f2e10077a..6ce06bd04 100644 --- a/lib/invite.js +++ b/lib/invite.js @@ -1,21 +1,37 @@ +"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 User = require("./user.js").User; -exports.Invite = function(json){ +var Invite = (function () { + function Invite(data, client) { + _classCallCheck(this, Invite); - this.max_age = json.max_age; - this.code = json.code; - this.server = json.guild; - this.revoked = json.revoked; - this.created_at = Date.parse(json.created_at); - this.temporary = json.temporary; - this.uses = json.uses; - this.max_uses = json.uses; - this.inviter = new User(json.inviter); - this.xkcdpass = json.xkcdpass; - this.channel = json.channel; -} + this.max_age = data.max_age; + this.code = data.code; + this.server = client.getServer("id", data.guild.id); + this.revoked = data.revoked; + this.created_at = Date.parse(data.created_at); + this.temporary = data.temporary; + this.uses = data.uses; + this.max_uses = data.uses; + this.inviter = client.addUser(data.inviter); + this.xkcd = data.xkcdpass; + this.channel = client.getChannel("id", data.channel.id); + } -exports.Invite.prototype.generateInviteURL = function(xkcd){ - var code = (xkcd ? this.xkcdpass : this.code); - return "https://discord.gg/"+code; -} + _createClass(Invite, [{ + key: "URL", + get: function get() { + var code = this.xkcd ? this.xkcdpass : this.code; + return "https://discord.gg/" + code; + } + }]); + + return Invite; +})(); + +module.exports = Invite; \ No newline at end of file diff --git a/lib/list.js b/lib/list.js index 5ad268523..df07cad86 100644 --- a/lib/list.js +++ b/lib/list.js @@ -4,27 +4,29 @@ * when created. Generally "ID" * @class List */ -exports.List = function( discriminator, cap ) { +"use strict"; + +exports.List = function (discriminator, cap) { /** - * What to use to distringuish duplicates - * @attribute discriminator - * @type {String} - */ + * What to use to distringuish duplicates + * @attribute discriminator + * @type {String} + */ this.discriminator = discriminator; /** - * The maximum amount of elements allowed in the list. - * @default Infinity - * @attribute cap - * @type {Number} - */ + * The maximum amount of elements allowed in the list. + * @default Infinity + * @attribute cap + * @type {Number} + */ this.cap = cap || Number.MAX_SAFE_INTEGER; /** - * The Array version of the List. - * @type {Array} - * @attribute contents - */ + * The Array version of the List. + * @type {Array} + * @attribute contents + */ this.contents = []; -} +}; /** * Adds an element to the list if it isn't already there. @@ -34,40 +36,59 @@ exports.List = function( discriminator, cap ) { * List.add( obj ); * List.add( [ obj, obj, obj ] ); */ -exports.List.prototype.add = function( child ) { +exports.List.prototype.add = function (child) { var self = this; - if ( child.constructor === Array ) { + if (child.constructor === Array) { children = child; - for ( child of children ) { - addChild( child ); - } + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + try { + for (var _iterator = children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + child = _step.value; + + addChild(child); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } } else { - addChild( child ); + addChild(child); } - function addChild( child ) { + function addChild(child) { - if ( self.length() > self.cap ) { - self.splice( 0, 1 ); + if (self.length() > self.cap) { + self.splice(0, 1); } - if ( self.filter( self.discriminator, child[ self.discriminator ] ).length() === 0 ) - self.contents.push( child ); + if (self.filter(self.discriminator, child[self.discriminator]).length() === 0) self.contents.push(child); } -} +}; /** * Returns the length of the List * @method length * @return {Number} */ -exports.List.prototype.length = function() { +exports.List.prototype.length = function () { return this.contents.length; -} +}; /** * Gets the index of an element in the List or defaults to false @@ -75,30 +96,28 @@ exports.List.prototype.length = function() { * @return {Number/Boolean} The index if the object is in the list, or false. * @method getIndex */ -exports.List.prototype.getIndex = function( object ) { +exports.List.prototype.getIndex = function (object) { var index = false; - for ( elementIndex in this.contents ) { - var element = this.contents[ elementIndex ]; - if ( element[ this.discriminator ] == object[ this.discriminator ] ) { + for (elementIndex in this.contents) { + var element = this.contents[elementIndex]; + if (element[this.discriminator] == object[this.discriminator]) { return elementIndex; } - } return index; - -} +}; /** * Removes an element at the specified index * @param {Number} index * @method removeIndex */ -exports.List.prototype.removeIndex = function( index ) { - this.contents.splice( index, 1 ); -} +exports.List.prototype.removeIndex = function (index) { + this.contents.splice(index, 1); +}; /** * Removes an element from the list @@ -106,18 +125,18 @@ exports.List.prototype.removeIndex = function( index ) { * @method removeElement * @return {Boolean} whether the operation was successful or not. */ -exports.List.prototype.removeElement = function( child ) { +exports.List.prototype.removeElement = function (child) { - for ( _element in this.contents ) { - var element = this.contents[ _element ]; - if ( child[ this.discriminator ] == element[ this.discriminator ] ) { - this.removeIndex( _element, 1 ); + for (_element in this.contents) { + var element = this.contents[_element]; + if (child[this.discriminator] == element[this.discriminator]) { + this.removeIndex(_element, 1); return true; } } return false; -} +}; /** * Replaces an element in the list with a specified element @@ -126,123 +145,206 @@ exports.List.prototype.removeElement = function( child ) { * @param {Object} newElement New Element * @return {Boolean} whether the operation was successful or not. */ -exports.List.prototype.updateElement = function( child, newChild ) { +exports.List.prototype.updateElement = function (child, newChild) { - for ( _element in this.contents ) { - var element = this.contents[ _element ]; - if ( child[ this.discriminator ] == element[ this.discriminator ] ) { - this.contents[ _element ] = newChild; + for (_element in this.contents) { + var element = this.contents[_element]; + if (child[this.discriminator] == element[this.discriminator]) { + this.contents[_element] = newChild; return true; } } return false; +}; -} - -exports.List.prototype.concatSublists = function( whereList, discriminator ) { +exports.List.prototype.concatSublists = function (whereList, discriminator) { //this is meant to look at the contents, and assuming the contents are all lists, concatenate their values. - var concatList = new exports.List( discriminator ); + var concatList = new exports.List(discriminator); - for ( item of this.contents ) { - var itemList = item[ whereList ]; - concatList.add( itemList.contents ); + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; + + try { + for (var _iterator2 = this.contents[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + item = _step2.value; + + var itemList = item[whereList]; + concatList.add(itemList.contents); + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2["return"]) { + _iterator2["return"](); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } } return concatList; -} +}; -exports.List.prototype.filter = function( key, value, onlyOne, caseInsen ) { +exports.List.prototype.filter = function (key, value, onlyOne, caseInsen) { var results = []; - value = change( value ); + value = change(value); - for ( index in this.contents ) { - var child = this.contents[ index ]; - if ( change( child[ key ] ) == value ) { - if ( onlyOne ) { + for (index in this.contents) { + var child = this.contents[index]; + if (change(child[key]) == value) { + if (onlyOne) { return child; } else { - results.push( child ); + results.push(child); } } } - function change( val ) { - if ( caseInsen ) { + function change(val) { + if (caseInsen) { val = val.toUpperCase(); } return val; } - if ( onlyOne ) { + if (onlyOne) { return false; } - var retList = new exports.List( this.discriminator ); + var retList = new exports.List(this.discriminator); retList.contents = results; return retList; -} +}; -exports.List.prototype.getValues = function( key ){ +exports.List.prototype.getValues = function (key) { var valList = []; - for( child of this.contents){ - valList.push( child[key] ); + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; + + try { + for (var _iterator3 = this.contents[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + child = _step3.value; + + valList.push(child[key]); + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3["return"]) { + _iterator3["return"](); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } } + return valList; +}; -} - -exports.List.prototype.deepFilter = function( keys, value, onlyOne, caseInsen ) { +exports.List.prototype.deepFilter = function (keys, value, onlyOne, caseInsen) { var results = []; - value = change( value ); + value = change(value); - for ( index in this.contents ) { - var child = this.contents[ index ]; + for (index in this.contents) { + var child = this.contents[index]; var buffer = child; - for ( key of keys ) { - if(buffer instanceof exports.List){ - buffer = buffer.contents; - } - if(buffer instanceof Array){ - for(elem of buffer){ - if( change(elem[key]) == value ){ - buffer = elem; + var _iteratorNormalCompletion4 = true; + var _didIteratorError4 = false; + var _iteratorError4 = undefined; + + try { + for (var _iterator4 = keys[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { + key = _step4.value; + + if (buffer instanceof exports.List) { + buffer = buffer.contents; + } + if (buffer instanceof Array) { + var _iteratorNormalCompletion5 = true; + var _didIteratorError5 = false; + var _iteratorError5 = undefined; + + try { + for (var _iterator5 = buffer[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { + elem = _step5.value; + + if (change(elem[key]) == value) { + buffer = elem; + } + } + } catch (err) { + _didIteratorError5 = true; + _iteratorError5 = err; + } finally { + try { + if (!_iteratorNormalCompletion5 && _iterator5["return"]) { + _iterator5["return"](); + } + } finally { + if (_didIteratorError5) { + throw _iteratorError5; + } + } } } + buffer = buffer[key]; + } + } catch (err) { + _didIteratorError4 = true; + _iteratorError4 = err; + } finally { + try { + if (!_iteratorNormalCompletion4 && _iterator4["return"]) { + _iterator4["return"](); + } + } finally { + if (_didIteratorError4) { + throw _iteratorError4; + } } - buffer = buffer[ key ]; } - if ( change( buffer ) == value ) { - if ( onlyOne ) { + if (change(buffer) == value) { + if (onlyOne) { return child; } else { - results.push( child ); + results.push(child); } } } - function change( val ) { - if ( caseInsen ) { + function change(val) { + if (caseInsen) { val = val.toUpperCase(); } return val; } - if ( onlyOne ) { + if (onlyOne) { return false; } - var retList = new exports.List( this.discriminator ); + var retList = new exports.List(this.discriminator); retList.contents = results; return retList; -} +}; \ No newline at end of file diff --git a/lib/message.js b/lib/message.js index 121bbf5dd..f4b937f02 100644 --- a/lib/message.js +++ b/lib/message.js @@ -1,39 +1,76 @@ -var User = require( "./user.js" ).User; -var List = require( "./list.js" ).List; -var PMChannel = require( "./PMChannel.js" ).PMChannel; +"use strict"; -exports.Message = function( time, author, content, channel, id, mentions, everyoneMentioned, embeds ) { +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 ( !content ) { - message = time; - channel = author; - time = message.timestamp; - author = message.author; - content = message.content; - id = message.id; - mentions = message.mentions; - everyoneMentioned = message.mention_everyone; - embeds = message.embeds; +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var User = require("./user.js").User; +var List = require("./list.js").List; +var PMChannel = require("./PMChannel.js").PMChannel; + +var Message = (function () { + function Message(data, channel, mentions, author) { + _classCallCheck(this, Message); + + this.tts = data.tts; + this.timestamp = Date.parse(data.timestamp); + this.nonce = data.nonce; + this.mentions = mentions; + this.everyoneMentioned = data.mention_everyone; + this.id = data.id; + this.embeds = data.embeds; + this.editedTimestamp = data.edited_timestamp; + this.content = data.content.trim(); + this.channel = channel; + this.author = author; + this.attachments = data.attachments; } - this.time = Date.parse( time ); - this.author = new User( author ); - this.content = content.replace( /\s+/g, ' ' ).trim(); //content.replace(/<[^>]*>/g, "").replace(/\s+/g, ' ').trim(); - this.channel = channel; - this.id = id; - this.mentions = new List( "id" ); - this.everyoneMentioned = everyoneMentioned; - this.embeds = embeds; - for ( x in mentions ) { - var _mention = mentions[ x ]; - this.mentions.add( new User( _mention ) ); - } -} + /*exports.Message.prototype.isPM = function() { + return ( this.channel instanceof PMChannel ); + }*/ -exports.Message.prototype.isPM = function() { - return ( this.channel instanceof PMChannel ); -} + _createClass(Message, [{ + key: "isMentioned", + value: function isMentioned(user) { + var id = user.id ? user.id : user; + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; -exports.Message.prototype.isMentioned = function( user ) { - return ( this.mentions.filter( "id", user.id ).length > 0 ); -} + try { + for (var _iterator = this.mentions[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var mention = _step.value; + + if (mention.id === id) { + return true; + } + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + return false; + } + }, { + key: "sender", + get: function get() { + return this.author; + } + }]); + + return Message; +})(); + +module.exports = Message; \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index a5cfaab6a..9c9903843 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,106 +1,168 @@ -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 ) { +"use strict"; - /** - * 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; +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; }; })(); - for ( x in members ) { - var member = members[ x ].user; - this.members.add( new User( member ) ); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Server = (function () { + function Server(data, client) { + _classCallCheck(this, Server); + + this.client = client; + this.region = data.region; + this.ownerID = data.owner_id; + this.name = data.name; + this.id = data.id; + this.members = []; + this.channels = []; + this.icon = data.icon; + this.afkTimeout = data.afk_timeout; + this.afkChannelId = data.afk_channel_id; + + if (!data.members) { + data.members = [client.user]; + return; + } + + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = data.members[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var member = _step.value; + + // first we cache the user in our Discord Client, + // then we add it to our list. This way when we + // get a user from this server's member list, + // it will be identical (unless an async change occurred) + // to the client's cache. + if (member.user) this.members.push(client.addUser(member.user)); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } } -} -/** - * 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"; -} + _createClass(Server, [{ + key: "getChannel", -/** - * 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(){ + // get/set + value: function getChannel(key, value) { + var _iteratorNormalCompletion2 = true; + var _didIteratorError2 = false; + var _iteratorError2 = undefined; - if(!this.afkChannelId) - return false; + try { + for (var _iterator2 = this.channels[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { + var channel = _step2.value; - return this.channels.filter("id", this.afkChannelId, true); + if (channel[key] === value) { + return channel; + } + } + } catch (err) { + _didIteratorError2 = true; + _iteratorError2 = err; + } finally { + try { + if (!_iteratorNormalCompletion2 && _iterator2["return"]) { + _iterator2["return"](); + } + } finally { + if (_didIteratorError2) { + throw _iteratorError2; + } + } + } -} + return null; + } + }, { + key: "getMember", + value: function getMember(key, value) { + var _iteratorNormalCompletion3 = true; + var _didIteratorError3 = false; + var _iteratorError3 = undefined; -/** - * Returns the #general channel of the server. - * @method getDefaultChannel - * @return {Channel} Returns the #general channel of the Server. - */ -exports.Server.prototype.getDefaultChannel = function() { + try { + for (var _iterator3 = this.members[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { + var member = _step3.value; - return this.channels.filter( "name", "general", true ); + if (member[key] === value) { + return member; + } + } + } catch (err) { + _didIteratorError3 = true; + _iteratorError3 = err; + } finally { + try { + if (!_iteratorNormalCompletion3 && _iterator3["return"]) { + _iterator3["return"](); + } + } finally { + if (_didIteratorError3) { + throw _iteratorError3; + } + } + } -} + return null; + } + }, { + key: "addChannel", + value: function addChannel(chann) { + if (!this.getChannel("id", chann.id)) { + this.channels.push(chann); + } + return chann; + } + }, { + key: "addMember", + value: function addMember(member) { + if (!this.getMember("id", member.id)) { + this.members.push(member); + } + return member; + } + }, { + key: "iconURL", + get: function get() { + if (!this.icon) return null; + return "https://discordapp.com/api/guilds/" + this.id + "/icons/" + this.icon + ".jpg"; + } + }, { + key: "afkChannel", + get: function get() { + if (!this.afkChannelId) return false; + + return this.getChannel("id", this.afkChannelId); + } + }, { + key: "defaultChannel", + get: function get() { + return this.getChannel("name", "general"); + } + }, { + key: "owner", + get: function get() { + return this.client.getUser("id", this.ownerID); + } + }]); + + return Server; +})(); + +module.exports = Server; \ No newline at end of file diff --git a/lib/user.js b/lib/user.js index 2bf3cc51b..2f363dd1e 100644 --- a/lib/user.js +++ b/lib/user.js @@ -1,37 +1,56 @@ -exports.User = function( username, id, discriminator, avatar ) { +"use strict"; - if ( !id ) { //there's no second argument - var user = username; - username = user.username; - id = user.id; - discriminator = user.discriminator; - avatar = user.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; }; })(); + +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: "equalsStrict", + value: function equalsStrict(object) { + return object.id === this.id && object.avatar === this.avatar && object.username === this.username && object.discriminator === this.discriminator; + } + }, { + 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 ); - -} +module.exports = User; \ No newline at end of file diff --git a/src/Client.js b/src/Client.js new file mode 100644 index 000000000..d40222a4f --- /dev/null +++ b/src/Client.js @@ -0,0 +1,1053 @@ +//discord.js modules +var Endpoints = require("./Endpoints.js"); +var User = require("./User.js"); +var Server = require("./Server.js"); +var Channel = require("./Channel.js"); +var Message = require("./Message.js"); +var Invite = require("./Invite.js"); +var PMChannel = require("./PMChannel.js"); + +//node modules +var request = require("superagent"); +var WebSocket = require("ws"); + +var defaultOptions = { + cache_tokens: false +} + +class Client { + + constructor(options = defaultOptions, token = undefined) { + /* + When created, if a token is specified the Client will + try connecting with it. If the token is incorrect, no + further efforts will be made to connect. + */ + this.options = options; + this.token = token; + this.state = 0; + this.websocket = null; + this.events = new Map(); + this.user = null; + this.alreadySentData = false; + this.serverCreateListener = new Map(); + + this.email = "abc"; + this.password = "abc"; + + /* + State values: + 0 - idle + 1 - logging in + 2 - logged in + 3 - ready + 4 - disconnected + */ + + this.userCache = []; + this.channelCache = []; + this.serverCache = []; + this.pmChannelCache = []; + this.readyTime = null; + } + + get uptime() { + + return (this.readyTime ? Date.now() - this.readyTime : null); + + } + + get ready() { + return this.state === 3; + } + + get servers() { + return this.serverCache; + } + + get channels() { + return this.channelCache; + } + + get users() { + return this.userCache; + } + + get PMChannels() { + return this.pmChannelCache; + } + + get messages() { + + var msgs = []; + for (var channel of this.channelCache) { + msgs = msgs.concat(channel.messages); + } + return msgs; + + } + + sendPacket(JSONObject) { + if (this.websocket.readyState === 1) { + this.websocket.send(JSON.stringify(JSONObject)); + } + } + + //def debug + debug(message) { + console.log(message); + } + + on(event, fn) { + this.events.set(event, fn); + } + + off(event, fn) { + this.events.delete(event); + } + + keepAlive() { + this.debug("keep alive triggered"); + this.sendPacket({ + op: 1, + d: Date.now() + }); + } + + //def trigger + trigger(event) { + var args = []; + for (var arg in arguments) { + args.push(arguments[arg]); + } + var evt = this.events.get(event); + if (evt) { + evt.apply(this, args.slice(1)); + } + } + + //def login + login(email = "foo@bar.com", password = "pass1234", callback = function (err, token) { }) { + + var self = this; + + this.createws(); + return new Promise(function (resolve, reject) { + if (self.state === 0 || self.state === 4) { + + self.state = 1; //set the state to logging in + + self.email = email; + self.password = password; + + 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); + reject(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); + resolve(self.token); + } + + }); + + } else { + reject(new Error("Client already logging in or ready")); + } + }); + + } + + logout(callback = function (err) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + request + .post(Endpoints.LOGOUT) + .set("authorization", self.token) + .end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + self.state = 4; + callback(); + resolve(); + } + }); + + }); + + } + + createServer(name, region, callback = function (err, server) { }) { + var self = this; + return new Promise(function (resolve, reject) { + + request + .post(Endpoints.SERVERS) + .set("authorization", self.token) + .send({ + name: name, + region: region + }) + .end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + // potentially redundant in future + // creating here does NOT give us the channels of the server + // so we must wait for the guild_create event. + self.serverCreateListener.set(res.body.id, [resolve, callback]); + /*var srv = self.addServer(res.body); + callback(null, srv); + resolve(srv);*/ + } + }); + + }); + } + + createChannel(server, channelName, channelType, callback = function (err, chann) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + request + .post(`${Endpoints.SERVERS}/${self.resolveServerID(server) }/channels`) + .set("authorization", self.token) + .send({ + name: channelName, + type: channelType + }) + .end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + var server = self.getServer("id", res.body.guild_id); + var chann = self.addChannel(res.body, res.body.guild_id); + server.addChannel(chann); + callback(null, chann); + resolve(chann); + } + + }) + + }); + + } + + leaveServer(server, callback = function (err, server) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + request + .del(`${Endpoints.SERVERS}/${self.resolveServerID(server) }`) + .set("authorization", self.token) + .end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + self.serverCache.splice(self.serverCache.indexOf(server), 1); + callback(null); + resolve(); + } + + }); + + }); + + } + + createInvite(serverOrChannel, options, callback = function (err, invite) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + var destination; + + if (serverOrChannel instanceof Server) { + destination = serverOrChannel.id; + } else if (serverOrChannel instanceof Channel) { + destination = serverOrChannel.id; + } else { + destination = serverOrChannel; + } + + options = options || {}; + options.max_age = options.maxAge || 0; + options.max_uses = options.maxUses || 0; + options.temporary = options.temporary || false; + options.xkcdpass = options.xkcd || false; + + request + .post(`${Endpoints.CHANNELS}/${destination}/invites`) + .set("authorization", self.token) + .send(options) + .end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + var inv = new Invite(res.body, self); + callback(null, inv); + resolve(inv); + } + }); + }); + + } + + startPM(user) { + + var self = this; + + return new Promise(function (resolve, reject) { + var userId = user; + if (user instanceof User) { + userId = user.id; + } + request + .post(`${Endpoints.USERS}/${self.user.id}/channels`) + .set("authorization", self.token) + .send({ + recipient_id: userId + }) + .end(function (err, res) { + if (err) { + reject(err); + } else { + resolve(self.addPMChannel(res.body)); + } + }); + }); + + } + + reply(destination, message, callback = function (err, msg) { }) { + + var self = this; + + return new Promise(function (response, reject) { + + var user = destination.sender; + self.sendMessage(destination, message, callback, user + ", ").then(response).catch(reject); + + }); + + } + + deleteMessage(message, timeout, callback = function (err, msg) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + if (timeout) { + setTimeout(remove, timeout) + } else { + remove(); + } + + function remove() { + request + .del(`${Endpoints.CHANNELS}/${message.channel.id}/messages/${message.id}`) + .set("authorization", self.token) + .end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + callback(null); + resolve(); + } + }); + } + }); + } + + updateMessage(message, content, callback = function (err, msg) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + content = (content instanceof Array ? content.join("\n") : content); + + request + .patch(`${Endpoints.CHANNELS}/${message.channel.id}/messages/${message.id}`) + .set("authorization", self.token) + .send({ + content: content, + mentions: [] + }) + .end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + var msg = new Message(res.body, message.channel, message.mentions, message.sender); + callback(null, msg); + resolve(msg); + + message.channel.messages[message.channel.messages.indexOf(message)] = msg; + } + }); + + }); + } + + setUsername(newName, callback = function (err) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + request + .patch(`${Endpoints.API}/users/@me`) + .set("authorization", self.token) + .send({ + avatar: self.user.avatar, + email: self.email, + new_password: null, + password: self.password, + username: newName + }) + .end(function (err) { + callback(err); + if (err) + reject(err); + else + resolve(); + }); + }); + } + + getChannelLogs(channel, amount = 500, callback = function (err, logs) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + var channelID = channel; + if (channel instanceof Channel) { + channelID = channel.id; + } + + request + .get(`${Endpoints.CHANNELS}/${channelID}/messages?limit=${amount}`) + .set("authorization", self.token) + .end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + var logs = []; + + var channel = self.getChannel("id", channelID); + + for (var message of res.body) { + + var mentions = []; + for (var mention of message.mentions) { + mentions.push(self.addUser(mention)); + } + + var author = self.addUser(message.author); + + logs.push(new Message(message, channel, mentions, author)); + } + callback(null, logs); + resolve(logs); + } + + }); + + }); + + } + + deleteChannel(channel, callback = function (err) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + var channelID = channel; + if (channel instanceof Channel) { + channelID = channel.id; + } + + request + .del(`${Endpoints.CHANNELS}/${channelID}`) + .set("authorization", self.token) + .end(function (err) { + if (err) { + callback(err); + reject(err); + } else { + callback(null); + resolve(); + } + }); + + }); + + } + + joinServer(invite, callback = function (err, server) { }) { + + var self = this; + + return new Promise(function (resolve, reject) { + + var id = (invite instanceof Invite ? invite.code : invite); + + request + .post(`${Endpoints.API}/invite/${id}`) + .set("authorization", self.token) + .end(function (err, res) { + if (err) { + callback(err); + reject(err); + } else { + self.serverCreateListener.set(res.body.guild.id, [resolve, callback]); + } + }); + + }); + + } + + sendMessage(destination, message, callback = function (err, msg) { }, premessage = "") { + + var self = this; + + return new Promise(function (resolve, reject) { + + message = premessage + resolveMessage(message); + var mentions = resolveMentions(); + destination = resolveDestination(destination); + + if (destination) + send(); + + function send() { + + request + .post(`${Endpoints.CHANNELS}/${destination}/messages`) + .set("authorization", self.token) + .send({ + content: message, + mentions: mentions + }) + .end(function (err, res) { + + if (err) { + callback(err); + reject(err); + } else { + var data = res.body; + + var mentions = []; + + data.mentions = data.mentions || []; //for some reason this was not defined at some point? + + for (var mention of data.mentions) { + mentions.push(self.addUser(mention)); + } + + var channel = self.getChannel("id", data.channel_id); + if (channel) { + var msg = channel.addMessage(new Message(data, channel, mentions, self.addUser(data.author))); + callback(null, msg); + resolve(msg); + } + } + + }); + + } + + function resolveDestination() { + var channId = false; + + if (destination instanceof Server) { + channId = destination.id; //general is the same as server id + } else if (destination instanceof Channel) { + channId = destination.id; + } else if (destination instanceof Message) { + channId = destination.channel.id; + } else if (destination instanceof User) { + + //check if we have a PM + for (var pmc of self.pmChannelCache) { + if (pmc.user.equals(destination)) { + return pmc.id; + } + } + + //we don't, at this point we're late + self.startPM(destination).then(function (pmc) { + destination = pmc.id; + send(); + }); + + } else { + channId = destination; + } + + return channId; + } + + function resolveMessage() { + var msg = message; + if (message instanceof Array) { + msg = message.join("\n"); + } + return msg; + } + + function resolveMentions() { + var _mentions = []; + for (var mention of (message.match(/<@[^>]*>/g) || [])) { + _mentions.push(mention.substring(2, mention.length - 1)); + } + return _mentions; + } + + }); + } + + //def createws + 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 = {}; + + 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); + + for (var _server of data.guilds) { + + var server = self.addServer(_server); + + } + + for (var _pmc of data.private_channels) { + var pmc = self.addPMChannel(_pmc); + } + + self.trigger("ready"); + self.readyTime = Date.now(); + self.debug(`cached ${self.serverCache.length} servers, ${self.channelCache.length} channels, ${self.pmChannelCache.length} PMs and ${self.userCache.length} users.`); + self.state = 3; + setInterval(function () { + self.keepAlive.apply(self); + }, data.heartbeat_interval); + + break; + case "MESSAGE_CREATE": + self.debug("received message"); + + var mentions = []; + data.mentions = data.mentions || []; //for some reason this was not defined at some point? + for (var mention of data.mentions) { + mentions.push(self.addUser(mention)); + } + + var channel = self.getChannel("id", data.channel_id); + if (channel) { + var msg = channel.addMessage(new Message(data, channel, mentions, self.addUser(data.author))); + self.trigger("message", msg); + } + + break; + case "MESSAGE_DELETE": + self.debug("message deleted"); + + var channel = self.getChannel("id", data.channel_id); + var message = channel.getMessage("id", data.id); + if (message) { + self.trigger("messageDelete", channel, message); + channel.messages.splice(channel.messages.indexOf(message), 1); + } else { + //don't have the cache of that message ;( + self.trigger("messageDelete", channel); + } + break; + case "MESSAGE_UPDATE": + self.debug("message updated"); + + var channel = self.getChannel("id", data.channel_id); + var formerMessage = channel.getMessage("id", data.id); + + if (formerMessage) { + + //new message might be partial, so we need to fill it with whatever the old message was. + var info = {}; + + for (var key in formerMessage) { + info[key] = formerMessage[key]; + } + + for (var key in data) { + info[key] = data[key]; + } + + var mentions = []; + for (var mention of info.mentions) { + mentions.push(self.addUser(mention)); + } + + var newMessage = new Message(info, channel, mentions, formerMessage.author); + + self.trigger("messageUpdate", newMessage, formerMessage); + + channel.messages[channel.messages.indexOf(formerMessage)] = newMessage; + + } + + // message isn't in cache, and if it's a partial it could cause + // all hell to break loose... best to just act as if nothing happened + + break; + + case "GUILD_DELETE": + + var server = self.getServer("id", data.id); + + if (server) { + self.serverCache.splice(self.serverCache.indexOf(server), 1); + self.trigger("serverDelete", server); + } + + break; + + case "CHANNEL_DELETE": + + var channel = self.getChannel("id", data.id); + + if (channel) { + + var server = channel.server; + + if (server) { + + server.channels.splice(server.channels.indexOf(channel), 1); + + } + + self.trigger("channelDelete", channel); + + self.serverCache.splice(self.serverCache.indexOf(channel), 1); + + } + + break; + + case "GUILD_CREATE": + + var server = self.getServer("id", data.id); + + if (!server) { + //if server doesn't already exist because duh + server = self.addServer(data); + }/*else if(server.channels.length === 0){ + + var srv = new Server(data, self); + for(channel of data.channels){ + srv.channels.push(new Channel(channel, data.id)); + } + self.serverCache[self.serverCache.indexOf(server)] = srv; + + }*/ + + if (self.serverCreateListener.get(data.id)) { + var cbs = self.serverCreateListener.get(data.id); + cbs[0](server); //promise then callback + cbs[1](null, server); //legacy callback + self.serverCreateListener.delete(data.id); + } + + self.trigger("serverCreate", server); + + break; + + case "CHANNEL_CREATE": + + var channel = self.getChannel("id", data.id); + + if (!channel) { + + var chann = self.addChannel(data, data.guild_id); + var srv = self.getServer("id", data.guild_id); + if (srv) { + srv.addChannel(chann); + } + self.trigger("channelCreate", chann); + + } + + break; + + case "GUILD_MEMBER_ADD": + + var server = self.getServer("id", data.guild_id); + + if (server) { + + var user = self.addUser(data.user); //if for whatever reason it doesn't exist.. + + if (!~server.members.indexOf(user)) { + server.members.push(user); + } + + self.trigger("serverNewMember", user); + } + + break; + + case "GUILD_MEMBER_REMOVE": + + var server = self.getServer("id", data.guild_id); + + if (server) { + + var user = self.addUser(data.user); //if for whatever reason it doesn't exist.. + + if (~server.members.indexOf(user)) { + server.members.splice(server.members.indexOf(user), 1); + } + + self.trigger("serverRemoveMember", user); + } + + break; + + case "USER_UPDATE": + + if (self.user && data.id === self.user.id) { + + var newUser = new User(data); //not actually adding to the cache + + self.trigger("userUpdate", newUser, self.user); + + if (~self.userCache.indexOf(self.user)) { + self.userCache[self.userCache.indexOf(self.user)] = newUser; + } + + self.user = newUser; + + } + + break; + + case "PRESENCE_UPDATE": + + var userInCache = self.getUser("id", data.user.id); + + if (userInCache) { + //user exists + var presenceUser = new User(data.user); + if (presenceUser.equalsStrict(userInCache)) { + //they're exactly the same, an actual presence update + self.trigger("presence", { + user: userInCache, + status: data.status, + server: self.getServer("id", data.guild_id), + gameId: data.game_id + }); + } else { + //one of their details changed. + self.trigger("userUpdate", userInCache, presenceUser); + self.userCache[self.userCache.indexOf(userInCache)] = presenceUser; + } + } + + break; + + default: + self.debug("received unknown packet"); + self.trigger("unknown", dat); + break; + + } + + } + + } + + //def addUser + addUser(data) { + if (!this.getUser("id", data.id)) { + this.userCache.push(new User(data)); + } + return this.getUser("id", data.id); + } + + //def addChannel + addChannel(data, serverId) { + if (!this.getChannel("id", data.id)) { + this.channelCache.push(new Channel(data, this.getServer("id", serverId))); + } + return this.getChannel("id", data.id); + } + + addPMChannel(data) { + if (!this.getPMChannel("id", data.id)) { + this.pmChannelCache.push(new PMChannel(data, this)); + } + return this.getPMChannel("id", data.id); + } + + //def addServer + addServer(data) { + + var server = this.getServer("id", data.id); + + if (!server) { + server = new Server(data, this); + this.serverCache.push(server); + if (data.channels) { + for (var channel of data.channels) { + server.channels.push(this.addChannel(channel, server.id)); + } + } + } + + return server; + } + + //def getUser + getUser(key, value) { + for (var user of this.userCache) { + if (user[key] === value) { + return user; + } + } + return null; + } + + //def getChannel + getChannel(key, value) { + for (var channel of this.channelCache) { + if (channel[key] === value) { + return channel; + } + } + return this.getPMChannel(key, value); //might be a PM + } + + getPMChannel(key, value) { + for (var channel of this.pmChannelCache) { + if (channel[key] === value) { + return channel; + } + } + return null; + } + + //def getServer + getServer(key, value) { + for (var server of this.serverCache) { + if (server[key] === value) { + return server; + } + } + return null; + } + + //def trySendConnData + 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)); + } + } + + resolveServerID(resource) { + + if (resource instanceof Server) { + return resource.id; + } else if (!isNaN(resource) && resource.length && resource.length === 17) { + return resource; + } + + } + +} + +module.exports = Client; \ No newline at end of file diff --git a/src/Endpoints.js b/src/Endpoints.js new file mode 100644 index 000000000..450bf0426 --- /dev/null +++ b/src/Endpoints.js @@ -0,0 +1,11 @@ +exports.BASE_DOMAIN = "discordapp.com"; +exports.BASE = `https://${exports.BASE_DOMAIN}`; +exports.WEBSOCKET_HUB = `wss://${exports.BASE_DOMAIN}/hub`; + +exports.API = `${exports.BASE}/api`; +exports.AUTH = `${exports.API}/auth`; +exports.LOGIN = `${exports.AUTH}/login`; +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/PMChannel.js b/src/PMChannel.js new file mode 100644 index 000000000..6b069d50b --- /dev/null +++ b/src/PMChannel.js @@ -0,0 +1,25 @@ +class PMChannel { + constructor(data, client) { + this.user = client.getUser("id", data.recipient.id); + this.id = data.id; + this.messages = []; + } + + addMessage(data){ + if(!this.getMessage("id", data.id)){ + this.messages.push(data); + } + return this.getMessage("id", data.id); + } + + getMessage(key, value){ + for(var message of this.messages){ + if(message[key] === value){ + return message; + } + } + return null; + } +} + +module.exports = PMChannel; \ No newline at end of file diff --git a/src/TokenManager.js b/src/TokenManager.js new file mode 100644 index 000000000..ceeed9ded --- /dev/null +++ b/src/TokenManager.js @@ -0,0 +1,68 @@ +var fs = require( "fs" ); +var crypto = require( "crypto" ); +var md5 = require( "md5" ); + +var tokens = {}; + +exports.TokenManager = function( folder, file ) { + + this.path = folder + file; + + var self = this; + + try { + var fd = fs.openSync( self.path, "wx" ); + self.writeTokens(); + } catch ( e ) { + self.readTokens(); + } + +} + +exports.TokenManager.prototype.addToken = function( id, token, pass ) { + tokens[ md5( id ) ] = encrypt( token, pass ); + this.writeTokens(); +} + +exports.TokenManager.prototype.readTokens = function() { + tokens = JSON.parse( fs.readFileSync( this.path, "utf8" ) ); + for ( tkn in tokens ) { + tokens[ tkn ] = decrypt( tokens[ tkn ], tkn ); + } +} + +exports.TokenManager.prototype.writeTokens = function() { + var tkn = {}; + for ( token in tokens ) { + tkn[ token ] = encrypt( tokens[ token ], token ); + } + fs.writeFile( this.path, JSON.stringify( tkn ), function( err ) { + + } ); +} + +exports.TokenManager.prototype.exists = function( id ) { + return tokens[ md5( id ) ]; +} + +exports.TokenManager.prototype.getToken = function( id, pass ) { + try{ + return decrypt( tokens[ md5( id ) ], pass ); + }catch(e){ + return false; + } +} + +function encrypt( string, password ) { + var cipher = crypto.createCipher( "aes-256-ctr", password ) + var crypted = cipher.update( string, 'utf8', 'hex' ) + crypted += cipher.final( 'hex' ); + return crypted; +} + +function decrypt( string, password ) { + var decipher = crypto.createDecipher( "aes-256-ctr", password ) + var dec = decipher.update( string, 'hex', 'utf8' ) + dec += decipher.final( 'utf8' ); + return dec; +} diff --git a/src/channel.js b/src/channel.js new file mode 100644 index 000000000..4b92e834b --- /dev/null +++ b/src/channel.js @@ -0,0 +1,38 @@ +class Channel { + + constructor(data, server) { + this.server = server; + this.name = data.name; + this.type = data.type; + this.id = data.id; + this.messages = []; + //this.isPrivate = isPrivate; //not sure about the implementation of this... + } + + get client() { + return this.server.client; + } + + equals(object) { + return object.id === this.id; + } + + addMessage(data){ + if(!this.getMessage("id", data.id)){ + this.messages.push(data); + } + return this.getMessage("id", data.id); + } + + getMessage(key, value){ + for(var message of this.messages){ + if(message[key] === value){ + return message; + } + } + return null; + } + +} + +module.exports = Channel; \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 000000000..f64a62a7f --- /dev/null +++ b/src/index.js @@ -0,0 +1,6 @@ +var request = require("superagent"); +var Endpoints = require("./Endpoints.js"); +var Client = require("./Client.js"); + +exports.Endpoints = Endpoints; +exports.Client = Client; \ No newline at end of file diff --git a/src/internal.js b/src/internal.js new file mode 100644 index 000000000..42d0dc689 --- /dev/null +++ b/src/internal.js @@ -0,0 +1,277 @@ +var request = require( "superagent" ); +var Endpoints = require( "./endpoints.js" ); + +var Internal = {}; + +Internal.XHR = {}; +Internal.WebSocket = {}; + +Internal.WebSocket.properties = { + "$os": "discord.js", + "$browser": "discord.js", + "$device": "discord.js", + "$referrer": "", + "$referring_domain": "" +}; + +Internal.XHR.login = function( email, password, callback ) { + + request + .post( Endpoints.LOGIN ) + .send( { + email: email, + password: password + } ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body.token ); + } + } ); + +} + +Internal.XHR.logout = function( token, callback ) { + + request + .post( Endpoints.LOGOUT ) + .end( function( err, res ) { + + err ? callback( err ) : callback( null ); + + } ); + +} + +Internal.XHR.createServer = function( token, name, region, callback ) { + + request + .post( Endpoints.SERVERS ) + .set( "authorization", token ) + .send( { + name: name, + region: region + } ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + } ); +} + +Internal.XHR.leaveServer = function( token, serverId, callback ) { + + request + .del( Endpoints.SERVERS + "/" + serverId ) + .set( "authorization", token ) + .end( function( err, res ) { + + err ? callback( err ) : callback( null ); + + } ); + +} + +Internal.XHR.createInvite = function( token, channelId, options, callback ) { + request + .post( Endpoints.CHANNELS + "/" + channelId + "/invites" ) + .set( "authorization", token ) + .send( options ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + } ) +} + +Internal.XHR.startPM = function( token, selfID, userID, callback ) { + + request + .post( Endpoints.USERS + "/" + selfID + "/channels" ) + .set( "authorization", token ) + .send( { + recipient_id: userID + } ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + } ); + +} + +Internal.XHR.sendMessage = function( token, channelID, messageParameters, callback ) { + request + .post( Endpoints.CHANNELS + "/" + channelID + "/messages" ) + .set( "authorization", token ) + .send( messageParameters ) + .end( function( err, res ) { + + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + + } ); + +} + +Internal.XHR.sendFile = function( token, channelID, file, fileName, callback ) { + request + .post( Endpoints.CHANNELS + "/" + channelID + "/messages" ) + .set( "authorization", token ) + .attach("file", file, fileName) + .end( function( err, res ) { + + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + + } ); +} + +Internal.XHR.deleteMessage = function( token, channelID, messageID, callback ) { + request + .del( Endpoints.CHANNELS + "/" + channelID + "/messages/" + messageID ) + .set( "authorization", token ) + .end( function( err ) { + err ? callback( err ) : callback( null ); + } ); +} + +Internal.XHR.updateMessage = function( token, channelID, messageID, messageParameters, callback ) { + + request + .patch( Endpoints.CHANNELS + "/" + channelID + "/messages/" + messageID ) + .set( "authorization", token ) + .send( messageParameters ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + } ); +} + +Internal.XHR.getChannelLogs = function( token, channelID, amount, callback ) { + request + .get( Endpoints.CHANNELS + "/" + channelID + "/messages?limit=" + amount ) + .set( "authorization", token ) + .end( function( err, res ) { + + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + + } ); +} + +Internal.XHR.createChannel = function( token, serverID, name, type, callback ) { + request + .post( Endpoints.SERVERS + "/" + serverID + "/channels" ) + .set( "authorization", token ) + .send( { + name: name, + type: type + } ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + } ); +} + +Internal.XHR.deleteChannel = function( token, channelID, callback ) { + + request + .del( Endpoints.CHANNELS + "/" + channelID ) + .set( "authorization", token ) + .end( function( err ) { + err ? callback( err ) : callback( null ); + } ); + +} +Internal.XHR.deleteServer = function( token, serverID, callback ) { + request + .del( Endpoints.SERVERS + "/" + serverID ) + .set( "authorization", token ) + .end( function( err ) { + err ? callback( err ) : callback( null ); + } ); +} + +Internal.XHR.getChannels = function( token, serverID, callback ) { + request + .get( Endpoints.SERVERS + "/" + serverID + "/channels" ) + .set( "authorization", token ) + .end( function( err ) { + err ? callback( err ) : callback( null ); + } ); +} + +Internal.XHR.getServer = function( token, serverID, callback ) { + + request + .get( Endpoints.SERVERS + "/" + serverID ) + .set( "authorization", token ) + .end( function( err, res ) { + + if ( err ) { + callback( err ); + } else { + callback( null, res.body ); + } + + } ); + +} + +Internal.XHR.acceptInvite = function( token, inviteID, callback ) { + + request + .post( Endpoints.API + "/invite/" + inviteID ) + .set( "authorization", token ) + .end( function( err, res ) { + if ( err ) { + callback( err ); + } else { + callback( null, res.body ) + } + } ); + +} + +Internal.XHR.setUsername = function( token, avatar, email, newPassword, password, username, callback ) { + + request + .patch( Endpoints.API + "/users/@me" ) + .set( "authorization", token ) + .send( { + avatar: avatar, + email: email, + new_password: newPassword, + password: password, + username: username + } ) + .end( function( err ) { + callback( err ); + } ); + +} + +exports.Internal = Internal; diff --git a/src/invite.js b/src/invite.js new file mode 100644 index 000000000..6880a834f --- /dev/null +++ b/src/invite.js @@ -0,0 +1,24 @@ +var User = require("./user.js").User; + +class Invite { + constructor(data, client) { + this.max_age = data.max_age; + this.code = data.code; + this.server = client.getServer("id", data.guild.id); + this.revoked = data.revoked; + this.created_at = Date.parse(data.created_at); + this.temporary = data.temporary; + this.uses = data.uses; + this.max_uses = data.uses; + this.inviter = client.addUser(data.inviter); + this.xkcd = data.xkcdpass; + this.channel = client.getChannel("id", data.channel.id); + } + + get URL() { + var code = (this.xkcd ? this.xkcdpass : this.code); + return "https://discord.gg/" + code; + } +} + +module.exports = Invite; \ No newline at end of file diff --git a/src/message.js b/src/message.js new file mode 100644 index 000000000..ba869c1e1 --- /dev/null +++ b/src/message.js @@ -0,0 +1,40 @@ +var User = require( "./user.js" ).User; +var List = require( "./list.js" ).List; +var PMChannel = require( "./PMChannel.js" ).PMChannel; + +class Message{ + constructor(data, channel, mentions, author){ + this.tts = data.tts; + this.timestamp = Date.parse(data.timestamp); + this.nonce = data.nonce; + this.mentions = mentions; + this.everyoneMentioned = data.mention_everyone; + this.id = data.id; + this.embeds = data.embeds; + this.editedTimestamp = data.edited_timestamp; + this.content = data.content.trim(); + this.channel = channel; + this.author = author; + this.attachments = data.attachments; + } + + isMentioned( user ){ + var id = (user.id ? user.id : user); + for(var mention of this.mentions){ + if(mention.id === id){ + return true; + } + } + return false; + } + + get sender(){ + return this.author; + } +} + +/*exports.Message.prototype.isPM = function() { + return ( this.channel instanceof PMChannel ); +}*/ + +module.exports = Message; \ No newline at end of file diff --git a/src/server.js b/src/server.js new file mode 100644 index 000000000..eba1b2e14 --- /dev/null +++ b/src/server.js @@ -0,0 +1,89 @@ +class Server { + constructor(data, client) { + this.client = client; + this.region = data.region; + this.ownerID = data.owner_id; + this.name = data.name; + this.id = data.id; + this.members = []; + this.channels = []; + this.icon = data.icon; + this.afkTimeout = data.afk_timeout; + this.afkChannelId = data.afk_channel_id; + + if(!data.members){ + data.members = [ client.user ]; + return; + } + + for (var member of data.members) { + + // first we cache the user in our Discord Client, + // then we add it to our list. This way when we + // get a user from this server's member list, + // it will be identical (unless an async change occurred) + // to the client's cache. + if(member.user) + this.members.push(client.addUser(member.user)); + + } + } + + get iconURL() { + if (!this.icon) + return null; + return `https://discordapp.com/api/guilds/${this.id}/icons/${this.icon}.jpg`; + } + + get afkChannel() { + if (!this.afkChannelId) + return false; + + return this.getChannel("id", this.afkChannelId); + } + + get defaultChannel() { + return this.getChannel("name", "general"); + } + + get owner() { + return this.client.getUser("id", this.ownerID); + } + + // get/set + getChannel(key, value) { + for (var channel of this.channels) { + if (channel[key] === value) { + return channel; + } + } + + return null; + } + + getMember(key, value){ + for (var member of this.members) { + if (member[key] === value) { + return member; + } + } + + return null; + } + + addChannel(chann) { + if (!this.getChannel("id", chann.id)) { + this.channels.push(chann); + } + return chann; + } + + addMember(member){ + if (!this.getMember("id", member.id)){ + this.members.push(member); + } + return member; + } +} + +module.exports = Server; \ No newline at end of file diff --git a/src/user.js b/src/user.js new file mode 100644 index 000000000..375ec7c60 --- /dev/null +++ b/src/user.js @@ -0,0 +1,39 @@ +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; + } + + equalsStrict(object){ + return object.id === this.id && object.avatar === this.avatar && object.username === this.username && object.discriminator === this.discriminator; + } +} + +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..30b8dbacb --- /dev/null +++ b/test/bot.js @@ -0,0 +1,60 @@ +var Discord = require("../lib/index.js"); +var Auth = require("./auth.json"); +var mybot = new Discord.Client(); + +mybot.login(Auth.email, Auth.password) + + .then(function (token) { + console.log("wooo!"); + }).catch(function (error) { + console.log(error); + }); + +mybot.on("ready", function () { + console.log("Ready!"); +}) + +mybot.on("message", function (msg) { + + if(msg.content === "pmme"){ + console.log("yes we found it!"); + mybot.setUsername("hydrabot").catch(function(err){ + console.log(err); + }); + mybot.reply(msg, "You know what "+msg.sender+"? NO").then(function(msg){ + mybot.updateMessage(msg, "wat i sed nothin m8"); + }); + mybot.getChannelLogs(msg.channel).then(function(logs){ + console.log(logs[0]); + }).catch(function(error){ + console.log(error); + }); + } + +}); + +mybot.on("messageDelete", function (channel, message) { + + console.log("MESSAGE WAS DELETED BY " + (message ? message.author.username : channel.name)); + +}); + +mybot.on("messageUpdate", function (message, formerMessage) { + + console.log(message.author.username, "changed", formerMessage.content, "to", message.content); + +}); + +mybot.on("serverNewMember", function (user) { + console.log("new user", user.username); +}); +mybot.on("serverRemoveMember", function (user) { + console.log("left user", user.username); +}); +mybot.on("userUpdate", function (oldUser, newUser) { + console.log(oldUser, "vs", newUser); +}); + +mybot.on("channelCreate", function(chann){ + console.log(chann); +}) \ No newline at end of file