From f68c4125542d6bae8891d0cb741979ca0f1e1beb Mon Sep 17 00:00:00 2001 From: Neel Kamath Date: Sun, 20 May 2018 16:54:45 +0530 Subject: [PATCH] Document output --- README.md | 18 ++-- documentation/DEPLOY.md | 2 + documentation/DOCUMENTATION.md | 106 +++++++++++++++--------- server/src/commands/admin/addmod.js | 30 +++---- server/src/commands/admin/listusers.js | 7 +- server/src/commands/admin/reload.js | 16 ++-- server/src/commands/admin/saveconfig.js | 17 ++-- server/src/commands/admin/shout.js | 9 +- server/src/commands/core/changenick.js | 44 +++++----- server/src/commands/core/chat.js | 12 ++- server/src/commands/core/disconnect.js | 5 +- server/src/commands/core/help.js | 13 +-- server/src/commands/core/invite.js | 14 +++- server/src/commands/core/join.js | 41 ++++----- server/src/commands/core/morestats.js | 5 +- server/src/commands/core/move.js | 32 ++++--- server/src/commands/core/stats.js | 5 +- server/src/commands/mod/ban.js | 10 ++- server/src/commands/mod/kick.js | 11 ++- server/src/commands/mod/unban.js | 9 +- 20 files changed, 250 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index a6fd45b..545d521 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,18 @@ This is a backwards compatible continuation of the [work by Andrew Belt](https:/ - [node.js 8.10.0](https://nodejs.org/en/download/package-manager/#windows) or higher - [npm 5.7.1](https://nodejs.org/en/download/package-manager/#windows) or higher -## Developer Installation +## Local Installation -1. [Clone](https://help.github.com/articles/cloning-a-repository/) the repository: `git clone https://github.com/hack-chat/main.git` -1. Change the directory: `cd main` -1. Install the dependencies: `npm install` -1. Launch: `npm start` +``` +git clone https://github.com/hack-chat/main.git +cd main +npm install +npm start +``` - If you change the `websocketPort` option during the config setup then these changes will need to be reflected on [line 60 of client.js](https://github.com/hack-chat/main/blob/master/client/client.js#L60). +If you change the `websocketPort` option during the config setup then these changes will need to be reflected on [line 60 of client.js](https://github.com/hack-chat/main/blob/master/client/client.js#L60). -## Live Deployment Installation - -See [DEPLOY.md](documentation/DEPLOY.md) +## [Server Installation](documentation/DEPLOY.md) # Contributing diff --git a/documentation/DEPLOY.md b/documentation/DEPLOY.md index 344165c..78f5bbc 100644 --- a/documentation/DEPLOY.md +++ b/documentation/DEPLOY.md @@ -1,5 +1,7 @@ # Live Deployment Installation +If you're running your own instance of hack.chat, you can retain backwards-compatibility in order to ensure that software created for the main server will work on yours too. + 1. [Clone](https://help.github.com/articles/cloning-a-repository/) the repository: `git clone https://github.com/hack-chat/main.git` 1. Change the directory: `cd main/server` 1. Install server dependencies: `npm install` diff --git a/documentation/DOCUMENTATION.md b/documentation/DOCUMENTATION.md index b070dfa..bead4b0 100644 --- a/documentation/DOCUMENTATION.md +++ b/documentation/DOCUMENTATION.md @@ -1,46 +1,78 @@ -You can programmatically access hack.chat using the following commands via a websocket. A list of wrappers written for accessing hack.chat can be found [here](https://github.com/hack-chat/3rd-party-software-list#libraries). +You can programmatically access hack.chat using the following commands via a websocket. A list of wrappers written for hack.chat can be found at the [3rd party software list repository](https://github.com/hack-chat/3rd-party-software-list#libraries). -The commands are to be sent through a websocket to the URL `wss://hack.chat/chat-ws` (everything sent and received are JSON). If you are sending messages locally or to another domain, replace 'hack.chat' with the respective domain. If you're running your own instance of hack.chat, you can retain backwards-compatibility in order to ensure that software created for the main server will work on yours too. +The commands are to be sent through a websocket to the URL `wss://hack.chat/chat-ws`. If you are sending messages locally or to another domain, replace 'hack.chat' with the respective domain. Everything sent and recieved is JSON. -All commands sent must be JSON objects with the command specified in the `"cmd"` key. For example: -```json -{ - "cmd": "join", - "channel": "programming", - "nick": "john#doe" -} -``` +Sending commands (as documented in [the input section](#Input)) will trigger a response. Responses may also be recieved when someone else's client sends a command relating to you, such as inviting you to a channel. Responses are documented in [the output section](#Output) with a `"name"` column, indicating which command would've triggered it to be sent (e.g., sending an 'addmod' command would result in your client, and other clients, recieving an 'addmod' response). -hack.chat has three permission levels. When you access a command, hack.chat automatically knows your permission level from your trip code. The lowest permission level is `user`. `mod` is above `user`, so it can access `user` commands in addition to `mod` commands. `admin` is similarly above `mod`. +Since hack.chat is an anonymous chatting site, you can use a trip code to identify yourself on each visit to prevent impersonation. Simply enter your passphrase after a pound sign in your nickname (e.g., john#doe instead of john). Furthermore, nicknames must: +- be no longer than 24 characters +- comprise of letters and/or numbers and/or underscores +- not be the admin's name +- not be the same as a nickname already in the channel connecting to -# `user` +New commands can be created, by using [this template](templateCommand.js). -|Command|Parameters|Explanation| -|-------|----------|-----------| -|`changenick`|`nick`|Changes the current connection's nickname.| -|`chat`|`text`|This broadcasts `text` to the channel the user is connected to.| -|`disconnect`||An event handler or forced disconnect.| -|`invite`|`nick`|Generates a pseudo-unique channel name and passes it to both the calling user and `nick`.| -|`join`|`channel`, `nick`|Places the calling socket into the target channel with the target nick and broadcasts the event to the channel.| -|`morestats`||Sends back the current server's stats to the calling client.| -|`move`|`channel`|This will change the current channel to `channel`.| -|`stats`||Sends back legacy server stats to the calling client. Use `morestats` when possible.| -|`help`|`category` or `command`|Gives documentation programmatically. If `category` (the permission level, such as `mod`) is sent, a list of commands available to that permission level will be sent back (as a `string` and not an `array`). This list only includes what is unique to that category and not every command a user with that permission level could perform. If `command` (e.g., `chat`) is sent, a description of the command will be sent back.| +There are three permission levels. When you send a command, hack.chat knows your permission level from your trip code. The lowest permission level is `user`. `mod` is above `user`, so it can access `user` commands in addition to `mod` commands. `admin` is similarly above `mod`. There can only be one admin. -# `mod` +# Input -|Command|Parameters|Explanation| -|-------|----------|-----------| -|`ban`|`nick`|Disconnects the target nickname in the same channel as the calling socket and adds it to the rate limiter.| -|`kick`|`nick`|Silently forces target client(s) into another channel. `nick` may be `string` or `array` of `string`s.| -|`unban`|`ip` or `hash`|Removes the target ip from the rate limiter.| +|Access Level|`"cmd"`|Additional Fields (Data type of actual argument)|Explanation|Example| +|------------|-------|-----------------|-----------|-------| +|`user`|`"changenick"`|`"nick"` (`string`)|Changes the current connection's nickname to the one specified in `"nick"`.|`{"cmd": "changenick", "nick": "njQ3Cz"}`| +|`user`|`"chat"`|`"text"` (`string`)|This broadcasts the value of `"text"` to the channel the user is connected to.|`{"cmd": "chat", "text": "hi!"}`| +|`user`|`"disconnect"`||Forcefully disconnects.|`{"cmd": "disconnect"}`| +|`user`|`"invite"`|`"nick"` (`string`)|Generates a pseudo-unique channel name and passes it to both the caller and callee (specified in `"nick"`).|`{"cmd": "invite", "nick": "john"}`| +|`user`|`"join"`|`"channel"` (`string`), `"nick"` (`string`)|Places the calling socket into the target channel (specified in `"channel"`) with the target nickname (specified in `"nick"`).|`{"cmd": "join", "channel": "programming", "nick": "harry"}`| +|`user`|`"morestats"`||Requests the server's stats.|`{"cmd": "morestats"}`| +|`user`|`"move"`|`"channel"` (`string`)|This will change the current channel to the new one (specified in `"channel"`).|`{"cmd": "move", "channel": "botDev"}`| +|`user`|`"stats"`||This command exists for backwards-compaitbility. Use the 'morestats' command instead.|`{"cmd": "stats"}`| +|`user`|`"help"`|`"category"` (`string`)|This programmatically requests the list of commands exclusive to the permission level specified in `"category"`.|`{"cmd": "help", "category": "mod"}`| +|`user`|`"help"`|`"command"` (`string`)|This programmatically requests a description of the command specified in `"command"`.|`{"cmd": "help", "command": "chat"}`| +|`mod`|`"ban"`|`"nick"` (`string`)|Disconnects the user having the nickname specified in `"nick"` and prevents them from joining for the next twenty-four hours.|`{"cmd": "ban", "nick": "neelkamath"}`| +|`mod`|`"kick"`|`"nick"` (`array` of `string`s)|Silently forces users (specified in `"nick"`) into another channel.|`{"cmd": "kick", "nick": "bob_the_builder"}`| +|`mod`|`"unban"`|`"ip"` (`string`)|Unbans a user based with the IP address specified in `"ip"`.|`{"cmd": "unban", "ip": "217.23.3.92"}`| +|`mod`|`"unban"`|`"hash"` (`string`)|Unbans a user having the hash specified in `"hash"`.|`{"cmd": "unban", "hash": "d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa"}`| +|`admin`|`"addmod"`|`"nick"` (`string`)|Upgrades the user having the nickname specified in `"nick"` to the `mod` permission level.|`{"cmd": "addmod", "nick": "M4GNV5"}`| +|`admin`|`"listusers"`||Outputs all the current channels and their users.|`{"cmd": "listusers"}`| +|`admin`|`"reload"`||(Re)loads any new commands into memory and outputs errors, if any.|`{"cmd": "reload"}`| +|`admin`|`"saveconfig"`||Saves the current config.|`{"cmd": "saveconfig"}`| +|`admin`|`"shout"`|`"text"` (`string`)|Displays what is specified in `"text"` to each user.|`{"cmd": "shout", "text": "hack.chat will be offline for the next 2 hours to update stuff"}`| -# `admin` +# Output -|Command|Parameters|Explanation| -|-------|----------|-----------| -|`addmod`|`nick`|Adds the target trip to the config as a mod and upgrades the socket type.| -|`listusers`||Outputs all current channels and sockets in those channels.| -|`reload`||(Re)loads any new commands into memory and outputs errors, if any.| -|`saveconfig`||Saves the current config.| -|`shout`|`text`|Displays the passed text to each client connected.| +|`"name"`|`"cmd"`|Additional Fields (Data type of formal parameter)|Explanation|Sendees|Example| +|-------|--------|-----------------|-----------|-------|-------| +|`"addmod"`|`"info"`|`"text"` (`string`)|`"text"` contains the text explaining to the new mod that they are now a mod.|The new mod.|`{"cmd": "info", "name": "addmod", "text": "You are now a mod."}`| +|`"addmod"`|`"info"`|`"text"` (`string`)|`"text"` contains information for informing mods that their is a new mod.|All mods.|`{"cmd": "info", "name": "addmod", "text": "Added mod trip: njQ3Cz"}`| +|`"listusers"`|`"info"`|`"text"` (`string`)|`"text"` has a list of all the users in all the channels.|The admin.|`{"cmd": "info", "name": "listusers", "text": "?programming wwandrew, neelkamath, Rut\n?lobby bacon, notrut, Zed"}`| +|`"reload"`|`"info"`|`"text"` (`string`)|`"text"` contains errors due to the reload (if any).|The sendee and all the mods.|`{"cmd": "info", "name": "reload", "text": "Loaded 2 commands, 0 errors"}`| +|`"saveconfig"`|`"warn"`|`"text"` (`string`)|`"text"` contains information as to why the config couldn't save.|The sendee.|`{"cmd": "warn", "name": "saveconfig", "text": "Failed to save config, check logs."}`| +|`"saveconfig"`|`"info"`|`"text"` (`string`)|`"text"` contains information on how the config was successfully saved.|The sendee and all the mods.|`{"cmd": "info", "name": "saveconfig", "text": "Config saved!"}`| +|`"shout"`|`"info"`|`"text'` (`string`)|Sends the value specified in `"text"` to each user.|Each client.|`{"cmd": "info", "name": "shout", "text": "Server Notice: hack.chat will be offline on Tuesday because I said so"}`| +|`"changenick"`|`"warn"`|`"text"` (`string`)|`"text"` contains the information as to why you cannot change your nick to the new one.|The sendee.|`{"cmd": "warn", "name": "changenick", "text": "You are changing nicknames too fast. Wait a moment before trying again."}`| +|`"changenick"`|`"onlineRemove"`|`"nick"` (`string`)|The user specified in `"nick"` is attempting to rejoin under a new nickname.|Every user in the channel connected to.|`{"cmd": "onlineRemove", "name": "changenick", "nick": "malfoy"}`| +|`"changenick"`|`"onlineAdd"`|`"nick"` (`string`), `"trip"` (`string` or `null`), `"hash"` (`string`)|A user has reconnected under a new nickname. `"nick"` specifies the new nickname, `"trip"` holds the user's trip code (if they don't have one, it'll be `null`), and `"hash"` holds a SHA256 sum to uniquely identify the individual.|Every user in the channel connected to.|`{"cmd": "onlineAdd", "name": "changenick", "nick": "ron", "trip": null, "hash": "d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa"}`| +|`"changenick"`|`"info"`|`"text'` (`string`)|`"text"` holds the information necessary for notifying users that a user has reconnected with a new nickname.|Everyone in the channel of the user.|`{"cmd": "info", "name": "changenick", "text": "andy is now andrew"}`| +|`"chat"`|`"warn"`|`"text"` (`string`)|`"text"` contains information explaining why your message wasn't sent.|The sendee.|`{"cmd": "warn", "name": "chat", "text": "You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message."}`| +|`"chat"`|`"chat"`|`"nick"` (`string`), `"text"` (`string`), `"admin"` (`bool` or `undefined`), `"mod"` (`bool` or `undefined`)|The new message (specified in `"text"`) in the channel connected to from the user specified in `"nick"`. `"admin"` or `"mod"` will be `true` if the message was sent from a user with that permission level, otherwise they will not be in the packet.|Everyone in the channel messaged in.|`{"cmd": "chat", "name": "chat", "nick": "raf924", "text": "no", "mod": true}`| +|`"disconnect"`|`"onlineRemove"`|`"nick"` (`string`)|The user specified in `"nick"` forced a disconnect for themselves.|Every user in the channel in question.|`{"cmd": "onlineRemove", "name": "disconnect", "nick": "neelkamath"}`| +|`"help"`|`"info"`|`"text"` (`string`)|An explanation.|The sendee.|`{"cmd": "info", "name": "help", "text": "Event handler or force disconnect (if your into that kind of thing)"}`| +|`"invite"`|`"warn"`|`"text"` (`string`)|`"text"` contains an explanation on why your invite wasn't sent.|The sendee.|`{"cmd": "warn", "name": "invite", "text": "You are sending invites too fast. Wait a moment before trying again."}`| +|`"join"`|`"warn"`|`"text"` (`string`)|`"text"` contains an explanation for why you couldn't join a channel.|The sendee.|`{"cmd": "warn", "name": "join", "text": "You are joining channels too fast. Wait a moment and try again."}`| +|`"join"`|`"onlineAdd"`|`"nick"` (`string`), `"trip"` (`string` or `null`), `"hash"` (`string`)|A user (specified in `"nick"`) with the trip code specified in `"trip"` (if non-existent, this will be `null`) joined the channel with the SHA256 sum specified in `"hash"`.|Everyone in that channel.|`{"cmd": "onlineAdd", "name": "join", "nick": "neelkamath", "trip": "null", hash: "d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa"}`| +|`"join"`|`"onlineSet"`|`"nicks"` (`array` of `string`s)|`"nicks"` contains each user's nickname in the channel connected to.|The sendee.|`{"cmd": "onlineSet", "name": "join", "nicks": ["neel", "Porygon"]}`| +|`"morestats"`|`"info"`|`"text"` (`string`)|The new server's statistics (specified in `"text"`).|The sendee.|`{"cmd": "info", "name": "morestats", "text": "current-connections: 20\ncurrent-channels: 3\nusers-joined: 0\ninvites-sent: 2\nmessages-sent: 4\nusers-banned: 5\nusers-kicked: 1\nstats-requested: 0\nserver-uptime: 7"}`| +|`"move"`|`"warn"`|`"text"` (`string`)|An explanation of why you weren't allowed to switch channels (specified in `"text"`).|The sendee.|`{"cmd": "warn", "name": "move", "text": "You are changing channels too fast. Wait a moment before trying again."}`| +|`"move"`|`"onlineRemove"`|`"nick"` (`string`)|States that the user (specified in `"nick"`) has left the channel.|Each user in the channel.|`{"cmd": "onlineRemove", "name": "move", "nick": "neelkamath"}`| +|`"move"`|`"onlineAdd"`|`"nick"` (`string`), `"trip"` (`string` or `null`), `"hash"` (`string`)|The user (specified in `"nick"`) with the trip code specified in `"trip"` (this will be `null` if there isn't one) has joined the channel. The hash specified in `"hash"` can be used to uniquely identify this individual.|Each user in the channel connected to.|`{"cmd": "onlineAdd", "name": "move", "nick": "volie_moldie", "hash": "d04b98f48e8f8bcc15c6ae5ac050801cd6dcfd428fb5f9e65c4e16e7807340fa"}`| +|`"move"`|`"onlineSet"`|`"nicks"` (`array` of `string`s)|`"nicks"`contains the nicknames present in the channel.|Each user in the channel.|`{"cmd": "onlineSet", "name": "move", "nicks": ["neel", "Porygon", "Xen0"]}`| +|`"stats"`|`"info"`|`"text"`|`"text"` contains the stats for the server.|The sendee.|`{"cmd": "info", "name": "stats", "text": "69 unique IPs in 7 channels"}`| +|`"ban"`|`"warn"`|`"text"` (`string`)|An explanation is specified in `"text"` for as to why the user couldn't be banned.|The sendee.|`{"cmd": "warn", "name": "ban", "text": "Could not find user in channel"}`| +|`"ban"`|`"info"`|`"text"` (`string`)|`"text"` holds information required to notify users of the ban.|Each client connected to the channel in which the user was banned in and all the mods everywhere.|`{"cmd": "info", "name": "ban", "text": "Banned neelkamath"}`| +|`"kick"`|`"warn"`|`"text"` (`string`)|An explanation as to why the kick failed is specified in `"text"`.|The sendee.|`{"cmd": "warn", "name": "kick", "text": "Could not find user(s) in channel"}`| +|`"kick"`|`"info"`|`"text"` (`string`)|A notification about the kicked user in specified in `"text"`.|All the mods in the channel in which the kick occurred.|`{"cmd": "info", "name": "kick", "text": "neelkamath was banished to ?m9bjmnnd"}`| +|`"kick"`|`"onlineRemove"`|`"nick"` (`string`)|`"nick"` specifies the user who has left the channel.|Each user in the channel in question.|`{"cmd": "onlineRemove", "name": "kick", "nick": "neelkamath"}`| +|`"kick"`|`"info"`|`"text"` (`string`)|An explanation of the kick (specified in `"text"`).|Each user in the channel in which a user was kicked in.|`{"cmd": "info", "name": "kick", "text": "Kicked neelkamath, Porygon, toilet_master"}`| +|`"unban"`|`"warn"`|`"text"` (`string`)|An explanation of why the unban was unsuccessful isspecified in `"text"`.|The sendee.|`{"cmd": "warn", "name": "unban", "text": "hash:'targethash' or ip:'1.2.3.4' is required"}`| +|`"unban"`|`"info"`|`"text"` (`string`)|`"text"` holds information on the unban's success.|The sendee.|`{"cmd": "info", "name": "unban", "text": "Unbanned neelkamath"}`| +|`"unban"`|`"info"`|`"text"` (`string`)|`"text"` holds information on the unban's success.|All mods.|`{"cmd": "info", "name": "unban", "text": "marzavec unbanned neelkamath"}`| \ No newline at end of file diff --git a/server/src/commands/admin/addmod.js b/server/src/commands/admin/addmod.js index 4c13b22..9e75300 100644 --- a/server/src/commands/admin/addmod.js +++ b/server/src/commands/admin/addmod.js @@ -2,6 +2,8 @@ Description: Adds the target trip to the mod list then elevates the uType */ +const name = 'addmod'; + exports.run = async (core, server, socket, data) => { if (socket.uType != 'admin') { // ignore if not admin @@ -16,32 +18,30 @@ exports.run = async (core, server, socket, data) => { let newMod = server.findSockets({ trip: data.trip }); + var obj = { + cmd: 'info', + name + }; + if (newMod.length !== 0) { + obj.text = 'You are now a mod.'; + for (let i = 0, l = newMod.length; i < l; i++) { newMod[i].uType = 'mod'; - server.send({ - cmd: 'info', - text: 'You are now a mod.' - }, newMod[i]); + server.send(obj, newMod[i]); } } - server.reply({ - cmd: 'info', - text: `Added mod trip: ${data.trip}` - }, socket); - - server.broadcast({ - cmd: 'info', - text: `Added mod trip: ${data.trip}` - }, { uType: 'mod' }); + obj.text = `Added mod trip: ${data.trip}`; + server.reply(obj, socket); + server.broadcast(obj, { uType: 'mod' }); }; exports.requiredData = ['trip']; exports.info = { - name: 'addmod', - usage: 'addmod {trip}', + name, + usage: `${name} {trip}`, description: 'Adds target trip to the config as a mod and upgrades the socket type' }; diff --git a/server/src/commands/admin/listusers.js b/server/src/commands/admin/listusers.js index a539a3c..b2f165e 100644 --- a/server/src/commands/admin/listusers.js +++ b/server/src/commands/admin/listusers.js @@ -2,6 +2,8 @@ Description: Outputs all current channels and their user nicks */ +const name = 'listusers'; + exports.run = async (core, server, socket, data) => { if (socket.uType != 'admin') { // ignore if not admin @@ -28,11 +30,12 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'info', - text: text + name, + text }, socket); }; exports.info = { - name: 'listusers', + name, description: 'Outputs all current channels and sockets in those channels' }; diff --git a/server/src/commands/admin/reload.js b/server/src/commands/admin/reload.js index e2cfbe6..afa6c84 100644 --- a/server/src/commands/admin/reload.js +++ b/server/src/commands/admin/reload.js @@ -2,6 +2,8 @@ Description: Clears and resets the command modules, outputting any errors */ +const name = 'reload'; + exports.run = async (core, server, socket, data) => { if (socket.uType != 'admin') { // ignore if not admin @@ -17,19 +19,19 @@ exports.run = async (core, server, socket, data) => { loadResult = `Loaded ${core.commands._commands.length} commands, error(s): ${loadResult}`; } - server.reply({ + var obj = { cmd: 'info', + name, text: loadResult - }, socket); + }; - server.broadcast({ - cmd: 'info', - text: loadResult - }, { uType: 'mod' }); + server.reply(obj, socket); + + server.broadcast(obj, { uType: 'mod' }); }; exports.info = { - name: 'reload', + name, description: '(Re)loads any new commands into memory, outputs errors if any' }; diff --git a/server/src/commands/admin/saveconfig.js b/server/src/commands/admin/saveconfig.js index ed3a312..1f600d0 100644 --- a/server/src/commands/admin/saveconfig.js +++ b/server/src/commands/admin/saveconfig.js @@ -2,6 +2,8 @@ Description: Writes any changes to the config to the disk */ +const name = 'saveconfig'; + exports.run = async (core, server, socket, data) => { if (socket.uType != 'admin') { // ignore if not admin @@ -13,24 +15,25 @@ exports.run = async (core, server, socket, data) => { if (!saveResult) { server.reply({ cmd: 'warn', + name, text: 'Failed to save config, check logs.' }, client); return; } - server.reply({ + var obj = { cmd: 'info', + name, text: 'Config saved!' - }, socket); + }; - server.broadcast({ - cmd: 'info', - text: 'Config saved!' - }, { uType: 'mod' }); + server.reply(obj, socket); + + server.broadcast(obj, { uType: 'mod' }); }; exports.info = { - name: 'saveconfig', + name, description: 'Saves current config' }; diff --git a/server/src/commands/admin/shout.js b/server/src/commands/admin/shout.js index 1358dd9..91ddff0 100644 --- a/server/src/commands/admin/shout.js +++ b/server/src/commands/admin/shout.js @@ -2,14 +2,17 @@ Description: Emmits a server-wide message as `info` */ +const name = 'shout'; + exports.run = async (core, server, socket, data) => { if (socket.uType != 'admin') { // ignore if not admin return; } - server.broadcast( { + server.broadcast({ cmd: 'info', + name, text: `Server Notice: ${data.text}` }, {}); }; @@ -17,7 +20,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['text']; exports.info = { - name: 'shout', - usage: 'shout {text}', + name, + usage: `${name} {text}`, description: 'Displays passed text to every client connected' }; \ No newline at end of file diff --git a/server/src/commands/core/changenick.js b/server/src/commands/core/changenick.js index 4041bb0..e84db27 100644 --- a/server/src/commands/core/changenick.js +++ b/server/src/commands/core/changenick.js @@ -2,16 +2,21 @@ Description: Generates a semi-unique channel name then broadcasts it to each client */ +const name = 'changenick'; + const verifyNickname = (nick) => { return /^[a-zA-Z0-9_]{1,24}$/.test(nick); }; exports.run = async (core, server, socket, data) => { + let warnObj = { + cmd: 'warn', + name + }; + if (server._police.frisk(socket.remoteAddress, 6)) { - server.reply({ - cmd: 'warn', - text: 'You are changing nicknames too fast. Wait a moment before trying again.' - }, socket); + warnObj.text = 'You are changing nicknames too fast. Wait a moment before trying again.'; + server.reply(warnObj, socket); return; } @@ -23,10 +28,8 @@ exports.run = async (core, server, socket, data) => { let newNick = data.nick.trim(); if (!verifyNickname(newNick)) { - server.reply({ - cmd: 'warn', - text: 'Nickname must consist of up to 24 letters, numbers, and underscores' - }, socket); + warnObj.text = 'Nickname must consist of up to 24 letters, numbers, and underscores'; + server.reply(warnObj, socket); return; } @@ -34,10 +37,8 @@ exports.run = async (core, server, socket, data) => { if (newNick.toLowerCase() == core.config.adminName.toLowerCase()) { server._police.frisk(socket.remoteAddress, 4); - server.reply({ - cmd: 'warn', - text: 'Gtfo' - }, socket); + warnObj.text = 'Gtfo'; + server.reply(warnObj, socket); return; } @@ -49,10 +50,8 @@ exports.run = async (core, server, socket, data) => { if (userExists.length > 0) { // That nickname is already in that channel - server.reply({ - cmd: 'warn', - text: 'Nickname taken' - }, socket); + warnObj.text = 'Nickname taken'; + server.reply(warnObj, socket); return; } @@ -60,19 +59,22 @@ exports.run = async (core, server, socket, data) => { let peerList = server.findSockets({ channel: socket.channel }); let leaveNotice = { cmd: 'onlineRemove', + name, nick: socket.nick }; let joinNotice = { cmd: 'onlineAdd', + name, nick: newNick, trip: socket.trip || 'null', hash: server.getSocketHash(socket) }; - server.broadcast( leaveNotice, { channel: socket.channel }); - server.broadcast( joinNotice, { channel: socket.channel }); - server.broadcast( { + server.broadcast(leaveNotice, { channel: socket.channel }); + server.broadcast(joinNotice, { channel: socket.channel }); + server.broadcast({ cmd: 'info', + name, text: `${socket.nick} is now ${newNick}` }, { channel: socket.channel }); @@ -82,7 +84,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['nick']; exports.info = { - name: 'changenick', - usage: 'changenick {nick}', + name, + usage: `${name} {nick}`, description: 'This will change your current connections nickname' }; diff --git a/server/src/commands/core/chat.js b/server/src/commands/core/chat.js index bce6adb..865d2ac 100644 --- a/server/src/commands/core/chat.js +++ b/server/src/commands/core/chat.js @@ -2,6 +2,8 @@ Description: Rebroadcasts any `text` to all clients in a `channel` */ +const name = 'chat'; + const parseText = (text) => { if (typeof text !== 'string') { return false; @@ -26,6 +28,7 @@ exports.run = async (core, server, socket, data) => { if (server._police.frisk(socket.remoteAddress, score)) { server.reply({ cmd: 'warn', + name, text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.' }, socket); @@ -34,8 +37,9 @@ exports.run = async (core, server, socket, data) => { let payload = { cmd: 'chat', + name, nick: socket.nick, - text: text + text }; if (socket.uType == 'admin') { @@ -48,7 +52,7 @@ exports.run = async (core, server, socket, data) => { payload.trip = socket.trip; } - server.broadcast( payload, { channel: socket.channel }); + server.broadcast(payload, { channel: socket.channel }); core.managers.stats.increment('messages-sent'); }; @@ -56,7 +60,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['text']; exports.info = { - name: 'chat', - usage: 'chat {text}', + name, + usage: `${name} {text}`, description: 'Broadcasts passed `text` field to the calling users channel' }; \ No newline at end of file diff --git a/server/src/commands/core/disconnect.js b/server/src/commands/core/disconnect.js index 9b54214..fa23aec 100644 --- a/server/src/commands/core/disconnect.js +++ b/server/src/commands/core/disconnect.js @@ -4,10 +4,13 @@ by a client to have the connection severed. */ +const name = 'disconnect'; + exports.run = async (core, server, socket, data) => { if (socket.channel) { server.broadcast({ cmd: 'onlineRemove', + name, nick: socket.nick }, { channel: socket.channel }); } @@ -16,6 +19,6 @@ exports.run = async (core, server, socket, data) => { }; exports.info = { - name: 'disconnect', + name, description: 'Event handler or force disconnect (if your into that kind of thing)' }; \ No newline at end of file diff --git a/server/src/commands/core/help.js b/server/src/commands/core/help.js index 7f63d3d..e088782 100644 --- a/server/src/commands/core/help.js +++ b/server/src/commands/core/help.js @@ -2,6 +2,8 @@ Description: Outputs the current command module list or command categories */ +const name = 'help'; + const stripIndents = require('common-tags').stripIndents; exports.run = async (core, server, socket, data) => { @@ -9,11 +11,11 @@ exports.run = async (core, server, socket, data) => { let typeDt = typeof data.type; let catDt = typeof data.category; let cmdDt = typeof data.command; - if (typeDt !== 'undefined' && typeDt !== 'string' ) { + if (typeDt !== 'undefined' && typeDt !== 'string') { return; - } else if (catDt !== 'undefined' && catDt !== 'string' ) { + } else if (catDt !== 'undefined' && catDt !== 'string') { return; - } else if (cmdDt !== 'undefined' && cmdDt !== 'string' ) { + } else if (cmdDt !== 'undefined' && cmdDt !== 'string') { return; } @@ -38,12 +40,13 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'info', + name, text: reply }, socket); }; exports.info = { - name: 'help', - usage: 'help ([ type:categories] | [category: | command: ])', + name, + usage: `${name} ([ type:categories] | [category: | command: ])`, description: 'Outputs information about the servers current protocol' }; \ No newline at end of file diff --git a/server/src/commands/core/invite.js b/server/src/commands/core/invite.js index bcf9097..46a5a83 100644 --- a/server/src/commands/core/invite.js +++ b/server/src/commands/core/invite.js @@ -2,6 +2,8 @@ Description: Generates a semi-unique channel name then broadcasts it to each client */ +const name = 'invite'; + const verifyNickname = (nick) => { return /^[a-zA-Z0-9_]{1,24}$/.test(nick); }; @@ -10,6 +12,7 @@ exports.run = async (core, server, socket, data) => { if (server._police.frisk(socket.remoteAddress, 2)) { server.reply({ cmd: 'warn', + name, text: 'You are sending invites too fast. Wait a moment before trying again.' }, socket); @@ -29,19 +32,21 @@ exports.run = async (core, server, socket, data) => { // They invited themself return; } - + let channel = Math.random().toString(36).substr(2, 8); let payload = { cmd: 'info', + name, invite: channel, text: `${socket.nick} invited you to ?${channel}` }; - let inviteSent = server.broadcast( payload, { channel: socket.channel, nick: data.nick }); + let inviteSent = server.broadcast(payload, { channel: socket.channel, nick: data.nick }); if (!inviteSent) { server.reply({ cmd: 'warn', + name, text: 'Could not find user in channel' }, socket); @@ -50,6 +55,7 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'info', + name, text: `You invited ${data.nick} to ?${channel}` }, socket); @@ -59,7 +65,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['nick']; exports.info = { - name: 'invite', - usage: 'invite {nick}', + name, + usage: `${name} {nick}`, description: 'Generates a unique (more or less) room name and passes it to two clients' }; \ No newline at end of file diff --git a/server/src/commands/core/join.js b/server/src/commands/core/join.js index f2b2c9d..1364020 100644 --- a/server/src/commands/core/join.js +++ b/server/src/commands/core/join.js @@ -2,6 +2,8 @@ Description: Initial entry point, applies `channel` and `nick` to the calling socket */ +const name = 'join'; + const crypto = require('crypto'); const hash = (password) => { @@ -15,11 +17,14 @@ const verifyNickname = (nick) => { }; exports.run = async (core, server, socket, data) => { + let warnObj = { + cmd: 'warn', + name + }; + if (server._police.frisk(socket.remoteAddress, 3)) { - server.reply({ - cmd: 'warn', - text: 'You are joining channels too fast. Wait a moment and try again.' - }, socket); + warnObj.text = 'You are joining channels too fast. Wait a moment and try again.'; + server.reply(warnObj, socket); return; } @@ -45,10 +50,8 @@ exports.run = async (core, server, socket, data) => { nick = nickArray[0].trim(); if (!verifyNickname(nick)) { - server.reply({ - cmd: 'warn', - text: 'Nickname must consist of up to 24 letters, numbers, and underscores' - }, socket); + warnObj.text = 'Nickname must consist of up to 24 letters, numbers, and underscores'; + server.reply(warnObj, socket); return; } @@ -60,10 +63,8 @@ exports.run = async (core, server, socket, data) => { if (userExists.length > 0) { // That nickname is already in that channel - server.reply({ - cmd: 'warn', - text: 'Nickname taken' - }, socket); + warnObj.text = 'Nickname taken'; + server.reply(warnObj, socket); return; } @@ -76,10 +77,8 @@ exports.run = async (core, server, socket, data) => { if (password != core.config.adminPass) { server._police.frisk(socket.remoteAddress, 4); - server.reply({ - cmd: 'warn', - text: 'Gtfo' - }, socket); + warnObj.text = 'Gtfo'; + server.reply(warnObj, socket); return; } else { @@ -101,7 +100,8 @@ exports.run = async (core, server, socket, data) => { let newPeerList = server.findSockets({ channel: data.channel }); let joinAnnouncement = { cmd: 'onlineAdd', - nick: nick, + name, + nick, trip: trip || 'null', hash: server.getSocketHash(socket) }; @@ -120,7 +120,8 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'onlineSet', - nicks: nicks + name, + nicks }, socket); core.managers.stats.increment('users-joined'); @@ -129,7 +130,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['channel', 'nick']; exports.info = { - name: 'join', - usage: 'join {channel} {nick}', + name, + usage: `${name} {channel} {nick}`, description: 'Place calling socket into target channel with target nick & broadcast event to channel' }; \ No newline at end of file diff --git a/server/src/commands/core/morestats.js b/server/src/commands/core/morestats.js index 5510cb1..5db21c7 100644 --- a/server/src/commands/core/morestats.js +++ b/server/src/commands/core/morestats.js @@ -2,6 +2,8 @@ Description: Outputs more info than the legacy stats command */ +const name = 'morestats'; + const stripIndents = require('common-tags').stripIndents; const formatTime = (time) => { @@ -33,6 +35,7 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'info', + name, text: stripIndents`current-connections: ${uniqueClientCount} current-channels: ${uniqueChannels} users-joined: ${(core.managers.stats.get('users-joined') || 0)} @@ -48,6 +51,6 @@ exports.run = async (core, server, socket, data) => { }; exports.info = { - name: 'morestats', + name, description: 'Sends back current server stats to the calling client' }; \ No newline at end of file diff --git a/server/src/commands/core/move.js b/server/src/commands/core/move.js index c5efafd..9c5d2a9 100644 --- a/server/src/commands/core/move.js +++ b/server/src/commands/core/move.js @@ -2,10 +2,13 @@ Description: Generates a semi-unique channel name then broadcasts it to each client */ +const name = 'move'; + exports.run = async (core, server, socket, data) => { if (server._police.frisk(socket.remoteAddress, 6)) { server.reply({ cmd: 'warn', + name, text: 'You are changing channels too fast. Wait a moment before trying again.' }, socket); @@ -35,17 +38,18 @@ exports.run = async (core, server, socket, data) => { let peerList = server.findSockets({ channel: socket.channel }); if (peerList.length > 1) { - for (let i = 0, l = peerList.length; i < l; i++) { - server.reply({ - cmd: 'onlineRemove', - nick: peerList[i].nick - }, socket); + var rmObj = { + cmd: 'onlineRemove', + name + }; - if (socket.nick !== peerList[i].nick){ - server.reply({ - cmd: 'onlineRemove', - nick: socket.nick - }, peerList[i]); + for (let i = 0, l = peerList.length; i < l; i++) { + rmObj.nick = peerList[i].nick; + server.reply(rmObj, socket); + + if (socket.nick !== peerList[i].nick) { + rmObj.nick = socket.nick; + server.reply(rmObj, peerList[i]); } } } @@ -53,6 +57,7 @@ exports.run = async (core, server, socket, data) => { let newPeerList = server.findSockets({ channel: data.channel }); let moveAnnouncement = { cmd: 'onlineAdd', + name, nick: socket.nick, trip: socket.trip || 'null', hash: server.getSocketHash(socket) @@ -68,7 +73,8 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'onlineSet', - nicks: nicks + name, + nicks }, socket); socket.channel = data.channel; @@ -77,7 +83,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['channel']; exports.info = { - name: 'move', - usage: 'move {channel}', + name, + usage: `${name} {channel}`, description: 'This will change the current channel to the new one provided' }; \ No newline at end of file diff --git a/server/src/commands/core/stats.js b/server/src/commands/core/stats.js index b9dc002..b95b3a0 100644 --- a/server/src/commands/core/stats.js +++ b/server/src/commands/core/stats.js @@ -2,6 +2,8 @@ Description: Legacy stats output, kept for compatibility, outputs user and channel count */ +const name = 'stats'; + exports.run = async (core, server, socket, data) => { let ips = {}; let channels = {}; @@ -20,6 +22,7 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'info', + name, text: `${uniqueClientCount} unique IPs in ${uniqueChannels} channels` }, socket); @@ -27,6 +30,6 @@ exports.run = async (core, server, socket, data) => { }; exports.info = { - name: 'stats', + name, description: 'Sends back legacy server stats to the calling client' }; \ No newline at end of file diff --git a/server/src/commands/mod/ban.js b/server/src/commands/mod/ban.js index e19efc2..d1878fc 100644 --- a/server/src/commands/mod/ban.js +++ b/server/src/commands/mod/ban.js @@ -2,6 +2,8 @@ Description: Adds the target socket's ip to the ratelimiter */ +const name = 'ban'; + exports.run = async (core, server, socket, data) => { if (socket.uType == 'user') { // ignore if not mod or admin @@ -18,6 +20,7 @@ exports.run = async (core, server, socket, data) => { if (badClient.length === 0) { server.reply({ cmd: 'warn', + name, text: 'Could not find user in channel' }, socket); @@ -29,6 +32,7 @@ exports.run = async (core, server, socket, data) => { if (badClient.uType !== 'user') { server.reply({ cmd: 'warn', + name, text: 'Cannot ban other mods, how rude' }, socket); @@ -42,11 +46,13 @@ exports.run = async (core, server, socket, data) => { server.broadcast({ cmd: 'info', + name, text: `Banned ${targetNick}` }, { channel: socket.channel, uType: 'user' }); server.broadcast({ cmd: 'info', + name, text: `${socket.nick} banned ${targetNick} in ${socket.channel}, userhash: ${clientHash}` }, { uType: 'mod' }); @@ -58,7 +64,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['nick']; exports.info = { - name: 'ban', - usage: 'ban {nick}', + name, + usage: `${name} {nick}`, description: 'Disconnects the target nickname in the same channel as calling socket & adds to ratelimiter' }; diff --git a/server/src/commands/mod/kick.js b/server/src/commands/mod/kick.js index 157592d..4121292 100644 --- a/server/src/commands/mod/kick.js +++ b/server/src/commands/mod/kick.js @@ -2,6 +2,8 @@ Description: Forces a change on the target socket's channel, then broadcasts event */ +const name = 'kick'; + exports.run = async (core, server, socket, data) => { if (socket.uType === 'user') { // ignore if not mod or admin @@ -19,6 +21,7 @@ exports.run = async (core, server, socket, data) => { if (badClients.length === 0) { server.reply({ cmd: 'warn', + name, text: 'Could not find user(s) in channel' }, socket); @@ -31,6 +34,7 @@ exports.run = async (core, server, socket, data) => { if (badClients[i].uType !== 'user') { server.reply({ cmd: 'warn', + name, text: 'Cannot kick other mods, how rude' }, socket); } else { @@ -40,6 +44,7 @@ exports.run = async (core, server, socket, data) => { // inform mods with where they were sent server.broadcast({ cmd: 'info', + name, text: `${badClients[i].nick} was banished to ?${newChannel}` }, { channel: socket.channel, uType: 'mod' }); @@ -56,6 +61,7 @@ exports.run = async (core, server, socket, data) => { for (let i = 0, j = kicked.length; i < j; i++) { server.broadcast({ cmd: 'onlineRemove', + name, nick: kicked[i] }, { channel: socket.channel }); } @@ -63,6 +69,7 @@ exports.run = async (core, server, socket, data) => { // publicly broadcast kick event server.broadcast({ cmd: 'info', + name, text: `Kicked ${kicked.join(', ')}` }, { channel: socket.channel, uType: 'user' }); @@ -72,7 +79,7 @@ exports.run = async (core, server, socket, data) => { exports.requiredData = ['nick']; exports.info = { - name: 'kick', - usage: 'kick {nick}', + name, + usage: `${name} {nick}`, description: 'Silently forces target client(s) into another channel. `nick` may be string or array of strings' }; \ No newline at end of file diff --git a/server/src/commands/mod/unban.js b/server/src/commands/mod/unban.js index e82f0b9..6215a8c 100644 --- a/server/src/commands/mod/unban.js +++ b/server/src/commands/mod/unban.js @@ -2,6 +2,8 @@ Description: Removes a target ip from the ratelimiter */ +const name = 'unban'; + exports.run = async (core, server, socket, data) => { if (socket.uType == 'user') { // ignore if not mod or admin @@ -11,6 +13,7 @@ exports.run = async (core, server, socket, data) => { if (typeof data.ip !== 'string' && typeof data.hash !== 'string') { server.reply({ cmd: 'warn', + name, text: "hash:'targethash' or ip:'1.2.3.4' is required" }, socket); @@ -37,11 +40,13 @@ exports.run = async (core, server, socket, data) => { server.reply({ cmd: 'info', + name, text: `Unbanned ${target}` }, socket); server.broadcast({ cmd: 'info', + name, text: `${socket.nick} unbanned: ${target}` }, { uType: 'mod' }); @@ -49,7 +54,7 @@ exports.run = async (core, server, socket, data) => { }; exports.info = { - name: 'unban', - usage: 'unban {[ip || hash]}', + name, + usage: `${name} {[ip || hash]}`, description: 'Removes target ip from the ratelimiter' }; \ No newline at end of file