Merge branch 'master' into indev

This commit is contained in:
hydrabolt
2015-08-29 15:55:29 +01:00
47 changed files with 7579 additions and 2825 deletions

3
.gitignore vendored
View File

@@ -4,6 +4,8 @@ hydrabot/config.json
hydrabot/authority.json
hydrabot/tokencache.json
.tmp/
### Node ###
# Logs
logs
@@ -32,3 +34,4 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
test/auth.json

3
.settings/settings.json Normal file
View File

@@ -0,0 +1,3 @@
// Place your settings in this file to overwrite default and user settings.
{
}

220
.settings/tasks.json Normal file
View File

@@ -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
}
}
}
*/

3
.travis.yml Normal file
View File

@@ -0,0 +1,3 @@
language: node_js
node_js:
- "stable"

116
README.md
View File

@@ -1,68 +1,74 @@
# discord.js
Discord.js is a node module that allows you to interface with the [Discord](https://discordapp.com/) API for creation of things such as bots or loggers.
The aim of this API is to make it *really* simple to start developing your bots. This API has server, channel and user tracking, as well as tools to make identification really simple.
[![Build Status](https://travis-ci.org/hydrabolt/discord.js.svg)](https://travis-ci.org/hydrabolt/discord.js)
New update features **big speed boosts** (everything cached and sorted with around 1 second of calling the login function) upon connection and allows editing of messages!
discord.js is a node module used as a way of interfacing with
[Discord](https://discordapp.com/). It is a very useful module for creating
bots.
**[Find the website here.](http://discord-js.github.io)**
**[For more information, click here.](https://github.com/hydrabolt/discord.js/wiki)**
### This module is still in alpha - especially the newer versions!
This node module is still in alpha, and some methods and functions may change or completely disappear!
**Updating to 3.1.1 is essential as it has new changes to be compatible with Discord's API,
and to make sure your application still works an update is a good idea.**
### Installation
``npm install --save discord.js``
`npm install --save discord.js`
### Features
---
* Send, Receive Delete and **Edit** messages from channels _and_ DMs! Auto-initiates DMs for you!
* Create, Delete and Leave servers and channels
* Create invites for Servers
* Silent Mention - trigger mention notification without actually @mentioning a user!
* Get complete metadata on users, channels and servers - including avatars.
* Get limitless logs from channels.
* Fast and efficient caching
* Auto-cache messages
### Example usage
### Example
```js
/*
* 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("discord.js");
var Discord = require( "discord.js" );
var mybot = new Discord.Client();
// Create the bot
var myBot = new Discord.Client();
mybot.on("message", function(message){
if(message.content === "ping")
mybot.reply(message, "pong");
});
// 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.channel, "pong" );
}
} );
mybot.login("email", "password");
```
### TODO
* Joining servers from an invite
* Stealthy Ninja support
---
### Related Projects
Here is a list of other Discord APIs:
#### Java:
[Discord4J](https://github.com/nerd/Discord4J)
#### .NET:
[Discord.Net](https://github.com/RogueException/Discord.Net)
[DiscordSharp](https://github.com/Luigifan/DiscordSharp)
#### NodeJS
[node-discord](https://github.com/izy521/node-discord) (similar to discord.js but lower level)
#### PHP
[DiscordPHP](https://github.com/teamreflex/DiscordPHP)
#### Python
[discord.py](https://github.com/Rapptz/discord.py)
#### Ruby
[discordrb](https://github.com/meew0/discordrb)
---
### Links
**[Documentation](https://github.com/discord-js/discord.js/wiki/Documentation)**
**[GitHub](https://github.com/discord-js/discord.js)**
**[Wiki](https://github.com/discord-js/discord.js/wiki)**
**[Website](http://discord-js.github.io/)**
**[NPM](npmjs.com/package/discord.js)**
---
### Contact
If you would like to contact me, you can create an issue on the GitHub repo
or send a DM to **hydrabolt** in [Discord API](https://discord.gg/0SBTUU1wZTY66OLO).

View File

@@ -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!" );
}
}
} );

View File

@@ -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~~" );
}
} );

View File

@@ -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" );
}
} );

View File

@@ -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 );
} );

View File

@@ -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!" )
}
} );
}
} );

View File

@@ -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 );
} );

48
gruntfile.js Normal file
View File

@@ -0,0 +1,48 @@
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
grunt.initConfig({
// define source files and their destinations
babel: {
dist: {
files: [{
expand: true,
cwd: "src/",
src: ["**.*"],
dest: "lib/",
ext: ".js"
}]
}
},
browserify: {
dist: {
files: {
'web-dist/discord.js': ["lib/index.js"],
},
options: {
browserifyOptions: {
standalone: "Discord"
}
}
}
},
uglify: {
min: {
files: {
"./web-dist/discord.min.js": "./web-dist/discord.js"
}
}
}
});
// load plugins
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-uglify');
// register at least this one task
grunt.registerTask('default', ['babel']);
grunt.registerTask('web', ['browserify', "uglify"]);
};

2
hydrabot/.gitignore vendored
View File

@@ -1,2 +0,0 @@
config.json
authority.json

View File

@@ -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"
}
```

View File

@@ -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);
} );
}

View File

@@ -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;
}

View File

@@ -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!";

1087
index.js

File diff suppressed because it is too large Load Diff

6
jsconfig.json Normal file
View File

@@ -0,0 +1,6 @@
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs"
}
}

1440
lib/Client.js Normal file

File diff suppressed because it is too large Load Diff

13
lib/Endpoints.js Normal file
View File

@@ -0,0 +1,13 @@
"use strict";
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";

View File

@@ -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;

View File

@@ -1,68 +0,0 @@
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;
}

View File

@@ -1,28 +1,79 @@
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 && 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: "toString",
value: function toString() {
return "#" + this.name;
}
}, {
key: "client",
get: function get() {
return this.server.client;
}
}]);
return Channel;
})();
module.exports = Channel;

View File

@@ -1,15 +0,0 @@
var base = "https://discordapp.com/";
var apibase = base + "api";
exports.API = apibase;
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";

12
lib/index.js Normal file
View File

@@ -0,0 +1,12 @@
"use strict";
var request = require("superagent");
var Endpoints = require("./Endpoints.js");
var Client = require("./Client.js");
var Discord = {
Endpoints: Endpoints,
Client: Client
};
module.exports = Discord;

View File

@@ -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;

View File

@@ -1,21 +1,35 @@
var User = require("./user.js").User;
"use strict";
exports.Invite = function(json){
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; }; })();
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;
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
exports.Invite.prototype.generateInviteURL = function(xkcd){
var code = (xkcd ? this.xkcdpass : this.code);
return "https://discord.gg/"+code;
}
var Invite = (function () {
function Invite(data, client) {
_classCallCheck(this, Invite);
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);
}
_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;

View File

@@ -1,248 +0,0 @@
/**
* Similar to a Java set. Contains no duplicate elements and includes filter
* functions. Discriminates between elements based on a discriminator passed
* when created. Generally "ID"
* @class List
*/
exports.List = function( discriminator, cap ) {
/**
* 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}
*/
this.cap = cap || Number.MAX_SAFE_INTEGER;
/**
* The Array version of the List.
* @type {Array}
* @attribute contents
*/
this.contents = [];
}
/**
* Adds an element to the list if it isn't already there.
* @method add
* @param {Object/Array} element The element(s) to add
* @example
* List.add( obj );
* List.add( [ obj, obj, obj ] );
*/
exports.List.prototype.add = function( child ) {
var self = this;
if ( child.constructor === Array ) {
children = child;
for ( child of children ) {
addChild( child );
}
} else {
addChild( child );
}
function addChild( child ) {
if ( self.length() > self.cap ) {
self.splice( 0, 1 );
}
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() {
return this.contents.length;
}
/**
* Gets the index of an element in the List or defaults to false
* @param {Object} object The element we want to get the index of
* @return {Number/Boolean} The index if the object is in the list, or false.
* @method getIndex
*/
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 ] ) {
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 );
}
/**
* Removes an element from the list
* @param {Object} element
* @method removeElement
* @return {Boolean} whether the operation was successful or not.
*/
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 );
return true;
}
}
return false;
}
/**
* Replaces an element in the list with a specified element
* @method updateElement
* @param {Object} element Element to update.
* @param {Object} newElement New Element
* @return {Boolean} whether the operation was successful or not.
*/
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;
return true;
}
}
return false;
}
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 );
for ( item of this.contents ) {
var itemList = item[ whereList ];
concatList.add( itemList.contents );
}
return concatList;
}
exports.List.prototype.filter = function( key, value, onlyOne, caseInsen ) {
var results = [];
value = change( value );
for ( index in this.contents ) {
var child = this.contents[ index ];
if ( change( child[ key ] ) == value ) {
if ( onlyOne ) {
return child;
} else {
results.push( child );
}
}
}
function change( val ) {
if ( caseInsen ) {
val = val.toUpperCase();
}
return val;
}
if ( onlyOne ) {
return false;
}
var retList = new exports.List( this.discriminator );
retList.contents = results;
return retList;
}
exports.List.prototype.getValues = function( key ){
var valList = [];
for( child of this.contents){
valList.push( child[key] );
}
return valList;
}
exports.List.prototype.deepFilter = function( keys, value, onlyOne, caseInsen ) {
var results = [];
value = change( value );
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;
}
}
}
buffer = buffer[ key ];
}
if ( change( buffer ) == value ) {
if ( onlyOne ) {
return child;
} else {
results.push( child );
}
}
}
function change( val ) {
if ( caseInsen ) {
val = val.toUpperCase();
}
return val;
}
if ( onlyOne ) {
return false;
}
var retList = new exports.List( this.discriminator );
retList.contents = results;
return retList;
}

View File

@@ -1,39 +1,72 @@
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 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;

View File

@@ -1,106 +1,173 @@
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: "toString",
value: function toString() {
return this.name;
}
}, {
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;

View File

@@ -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;

View File

@@ -1,14 +1,14 @@
{
"name": "discord.js",
"version": "2.7.1",
"version": "3.1.3",
"description": "A way to interface with the Discord API",
"main": "index.js",
"main": "./lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "node ./test/bot.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/discord-js/discord.js.git"
"url": "git+https://github.com/hydrabolt/discord.js.git"
},
"keywords": [
"discord",
@@ -21,12 +21,18 @@
"author": "Amish Shah <amishshah.2k@gmail.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/discord-js/discord.js/issues"
"url": "https://github.com/hydrabolt/discord.js/issues"
},
"homepage": "https://github.com/discord-js/discord.js#readme",
"homepage": "https://github.com/hydrabolt/discord.js#readme",
"dependencies": {
"md5": "^2.0.0",
"superagent": "^1.3.0",
"ws": "^0.7.2"
},
"devDependencies": {
"grunt": "~0.4.5",
"grunt-babel": "^5.0.1",
"grunt-browserify": "^4.0.0",
"grunt-contrib-uglify": "^0.9.2",
"load-grunt-tasks": "^3.2.0"
}
}

1137
src/Client.js Normal file

File diff suppressed because it is too large Load Diff

11
src/Endpoints.js Normal file
View File

@@ -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`;

25
src/PMChannel.js Normal file
View File

@@ -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;

41
src/channel.js Normal file
View File

@@ -0,0 +1,41 @@
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 && 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;
}
toString(){
return "#" + this.name;
}
}
module.exports = Channel;

10
src/index.js Normal file
View File

@@ -0,0 +1,10 @@
var request = require("superagent");
var Endpoints = require("./Endpoints.js");
var Client = require("./Client.js");
var Discord = {
Endpoints : Endpoints,
Client : Client
}
module.exports = Discord;

277
src/internal.js Normal file
View File

@@ -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;

22
src/invite.js Normal file
View File

@@ -0,0 +1,22 @@
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;

36
src/message.js Normal file
View File

@@ -0,0 +1,36 @@
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;

93
src/server.js Normal file
View File

@@ -0,0 +1,93 @@
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;
}
toString(){
return this.name;
}
}
module.exports = Server;

39
src/user.js Normal file
View File

@@ -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;

134
test/bot.js Normal file
View File

@@ -0,0 +1,134 @@
/*
this file should be used for travis builds only
*/
var Discord = require("../");
var mybot = new Discord.Client();
var server, channel, message, sentMessage = false;
function init(){
console.log("preparing...");
}
mybot.on("ready", function(){
console.log("ready! beginning tests");
success1();
});
function success1(){ //make server
mybot.createServer("test-server", "london").then(success2).catch(error);
}
function success2(_server){ //make channel
console.log("test 1 successful");
server = _server;
mybot.createChannel(server, "test-channel", "text").then(success3).catch(error);
}
function success3(_channel){ //send message
console.log("test 2 successful");
channel = _channel;
mybot.sendMessage(channel, [mybot.user.avatarURL, "an", "array", "of", "messages"]).then(success4).catch(error);
}
function success4(_message){ //delete message
console.log("test 3 successful");
message = _message;
mybot.deleteMessage(message).then(success5).catch(error);
}
function success5(){ //send ping
console.log("test 4 successful");
mybot.sendMessage(channel, "ping").then(function(msg){
message = msg;
}).catch(error);
setTimeout(checkError, 30 * 1000);
}
function success7(){
console.log("test 6 successful");
mybot.deleteChannel(channel).then(success8).catch(error);
}
function success8(){
console.log("test 7 successful");
mybot.createInvite(server).then(success9).catch(error);
}
function success9(invite){
console.log("test 8 successful");
if(invite.code){
success10();
}else{
error("reference error");
}
}
function success10(){
console.log("test 9 succesful");
mybot.leaveServer(server).then(success11).catch(error);
}
function success11(){
console.log("test 10 succesful");
mybot.joinServer(process.env["ds_invite"]).then(success12).catch(error);
}
function success12(_server){
console.log("test 11 successful");
server = mybot.getServer("id", _server.id);
if(server){
success13();
}else{
error("reference error");
}
}
function success13(){
console.log("test 12 successful");
mybot.sendFile(server, "./test/image.png").then(function(msg){
mybot.deleteMessage(msg).then(success14).catch(error);
}).catch(error);
}
function success14(msg){
console.log("test 13 successful");
mybot.leaveServer(server).then(success15).catch(error);
}
function success15(){
console.log("test 14 successful");
mybot.logout().then(done).catch(error);
}
function done(){
console.log("All tests completed succesfully.");
process.exit(0);
}
function checkError(){
if(!sentMessage){
error("failure receiving messages");
}
}
function error(err){
console.log("error", err);
process.exit(1);
}
mybot.on("message", function(message){
if(message.channel.equals(channel)){
if(message.content === "ping"){
console.log("test 5 successful");
sentMessage = true;
mybot.updateMessage(message, "pong").then(success7).catch(error);
}
}
});
mybot.login(process.env["ds_email"], process.env["ds_password"]).then(init).catch(error);

BIN
test/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

3330
web-dist/discord.js Normal file

File diff suppressed because it is too large Load Diff

2
web-dist/discord.min.js vendored Normal file

File diff suppressed because one or more lines are too long