diff --git a/lab-yana/.eslintrc b/lab-yana/.eslintrc new file mode 100644 index 0000000..8dc6807 --- /dev/null +++ b/lab-yana/.eslintrc @@ -0,0 +1,21 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} diff --git a/lab-yana/.gitignore b/lab-yana/.gitignore new file mode 100644 index 0000000..0992345 --- /dev/null +++ b/lab-yana/.gitignore @@ -0,0 +1,117 @@ +# Created by https://www.gitignore.io/api/node,vim,osx,macos,linux + +*node_modules + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + + + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon +# Thumbnails +._* +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### macOS ### +# Icon must end with two \r +# Thumbnails +# Files that might appear in the root of a volume +# Directories potentially created on remote AFP share + + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# End of https://www.gitignore.io/api/node,vim,osx,macos,linux diff --git a/lab-yana/README.md b/lab-yana/README.md new file mode 100644 index 0000000..4bc9782 --- /dev/null +++ b/lab-yana/README.md @@ -0,0 +1,44 @@ + +## TCP Chat Server + +Using node.js, this app allows users to chat in real time. They have the option of sending messages to the entire chatroom, which are seen by all users in the chat at the time, or to individual users. A user can also change their nickname (the default being a randomly generated number), and look at a list of users (by nickname) that are currently in the chat. Built in user input validation gives feedback to the user when they use commands incorrectly, and errors are logged on the server console. + +### How to set up the chat server + +* open a terminal window clone the Chat Server + ``` + $ git clone https://github.com/radenska/06-tcp_servers.git + ``` +* navigate to the lab-yana directory +* run + ``` + $ npm i + ``` +* run + ``` + $ node server + ``` +This should start the server. + +### How to connect to the chat server +In order for users to join the chat, they need to open a terminal window and run telnet . This is information that the person running the server should provide to users. + +### User commands + +``` +@quit to exit the chat +``` +``` +@users to get a list of users (by nickname) currently in the chat +``` +``` +@dm to send a message to a specific user +``` +``` +@all to send a message to everyone currently in the chat +``` +``` +@nickname to change your nickname +``` + +_created by_ Yana Radenska diff --git a/lab-yana/gulpfile.js b/lab-yana/gulpfile.js new file mode 100644 index 0000000..9a6f78f --- /dev/null +++ b/lab-yana/gulpfile.js @@ -0,0 +1,14 @@ +'use strict'; + +const gulp = require('gulp'); +const eslint = require('gulp-eslint'); + +gulp.task('lint', function() { + return gulp.src(['./**/*.js', '!node_modules/**']).pipe(eslint()).pipe(eslint.format()).pipe(eslint.failAfterError()); +}); + +gulp.task('dev', function() { + gulp.watch(['**/*.js', '!node_modules/**'], ['lint']); +}); + +gulp.task('default', ['dev']); diff --git a/lab-yana/models/client.js b/lab-yana/models/client.js new file mode 100644 index 0000000..ba0418d --- /dev/null +++ b/lab-yana/models/client.js @@ -0,0 +1,8 @@ +'use strict'; + +const uuid = require('node-uuid'); +const Client = module.exports = function(socket) { + this.socket = socket; + this.nickname = `user_${Math.floor(Math.random()*1000000000)}`; + this.id = uuid.v4(); +} diff --git a/lab-yana/package.json b/lab-yana/package.json new file mode 100644 index 0000000..6a40af3 --- /dev/null +++ b/lab-yana/package.json @@ -0,0 +1,22 @@ +{ + "name": "lab-yana", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "node": "0.0.0", + "node-uuid": "^1.4.7", + "uuid": "^3.0.1" + }, + "devDependencies": { + "gulp": "^3.9.1", + "gulp-eslint": "^3.0.1" + } +} diff --git a/lab-yana/server.js b/lab-yana/server.js new file mode 100644 index 0000000..5ea77c3 --- /dev/null +++ b/lab-yana/server.js @@ -0,0 +1,100 @@ +'use strict'; + +const net = require('net'); +const EE = require('events'); +const Client = require('./models/client.js'); +const PORT = process.env.port || 3000; +const server = net.createServer(); +const ee = new EE(); +const pool = []; //array to store all clients/users + +//telnet 172.16.13.51 3000 to connect to server on Yana's computer + +ee.on('default', function(client) { + client.socket.write('Not a valid command. Try again!\n'); +}); + +ee.on('@all', function(client, string) { + pool.forEach (c => { c.socket.write(`${client.nickname}: ${string}\n`) }); + if (pool.length === 1 && client.socket.destroyed === false) client.socket.write('You\'e by yourself in here! :-(\n'); +}); + +ee.on('@dm', function(client, string) { + console.log('client id', client.id); + let nickname = string.split(' ').shift().trim(); + let message = string.split(' ').slice(1).join(' ').trim(); + var inArray = false; + pool.forEach (c => { + if (c.nickname === nickname) { + c.socket.write(`${client.nickname}: ${message}\n`); + inArray = true; + } + }); + if (!inArray) client.socket.write(`User does not exist.\n`); +}); + +ee.on('@quit', function(client, string) { + client.socket.destroy(); +}); + +ee.on('@nickname', function(client, string) { + let tempNickname = string.split(' ').shift().trim(); + let inArray = false; + pool.forEach(c => { + if (c.nickname === tempNickname) { + if (c.nickname === client.nickname) { + c.socket.write(`${client.nickname} is already your nickname, stupid!\n`); + return; + } + client.socket.write('Bummer! That nickname is already taken!\n'); + } + }); + if (!inArray) client.nickname = tempNickname; + client.socket.write(`${tempNickname} is your new nickname.`); +}); + +ee.on('@users', function (client, string) { + pool.forEach(c => { + client.socket.write(`${c.nickname}\n`); + }); +}); + +function parseInput(data, client) { + const command = data.toString().split(' ').shift().trim(); + console.log(`user input: ${data}`); + console.log('command:', command); + if (command === '@all' || command === '@dm' || command === '@nickname' || command === '@quit' || command === '@users') { + ee.emit(command, client, data.toString().split(' ').slice(1).join(' ')); + return; + } + ee.emit('default', client); +} + +function greeting(client) { + client.socket.write('Wecome to chat!\n You can use the following commands: \n @all to send a message to everyone \n @dm to send a message to a single person\n @nickname to change your nickname\n @users to list the nicknames of users currently in chat\n @quit to exit the chat\n'); +} + +server.on('connection', function(socket) { + var client = new Client(socket); //instantiate new client object + pool.push(client); //push client into pool; + greeting(client); + socket.on('close', function() { + pool.forEach(c => { + let index = pool.indexOf(client); + console.log(`client ${pool[index].id} disconnected`); + ee.emit('@all', client, 'has left the chat'); + pool.slice(index, 1); + }); + }); + socket.on('data', function(data) { + parseInput(data, client); + }); + socket.on('error', function(err) { + console.error(err); + }); +}); + + +server.listen(PORT, function() { + console.log(`server up: ${PORT}`); +});