Skip to content

Instantly share code, notes, and snippets.

@evilbetty
Created March 20, 2012 00:44
Show Gist options
  • Save evilbetty/2129169 to your computer and use it in GitHub Desktop.
Save evilbetty/2129169 to your computer and use it in GitHub Desktop.
Simple chat server with eventmachine
#!/usr/bin/env ruby
#
# Chat server attempt with Ruby and EventMachine.
#
# TODO:
#
# - Prevent chat from acting on \n (pressing enters in chat)
# - Handle (unexpected) disconnects properly (with unbind event).
# - Implement proper leaving with /quit.
# - Force names to be unique.
# - Implements private messages (using the unique names)
# - Handle annoying escape codes and arrow keys etc on prompt better.
#
# - Improvements
# - Funn Stuff
#
# - Implement channels?
# - Make a version for the real IRC protocol?
#
#
# CHANGELOG:
#
# - 21 Mar 2012
# - Name validation added using regex
# - Added /me for the purpose of testing regex $1
# - Added error handling with rescues
# - Removed @name from sendpublic to have a more general use, and added it in the else case statement
# - Replaced if elsif elsif else with a case statement using regex when checking for known commands
#
#
# Created on: 20 Mar 2012 01:28
# Updated on: 21 Mar 2012 21:25
#
#
# EvilBetty
#
require 'eventmachine'
# Config
IP = '0.0.0.0'
PORT = 8081
class EmChat < EventMachine::Connection
#Accessor for name attribute and initialise the connections array
attr_accessor :name
@@clients = []
#Own functions here
#Send data provided to all clients except the sending client by looping though all connections in array
def sendpublic(data)
@@clients.each do |client|
begin
#A bit messy way to remove the > that was already printed before with 2 backspaces, and readd > again after
client.send_data "\b\b#{data.strip}\n> " unless client == self
rescue
#if we cannot send to a client anymore remove it from clients list
@@clients.delete client
end
end
end
#EventMachine built in events below
#When a new connection is made welcome the client and ask name for initialisation
def post_init
send_data("Welcome to this chat server.\nWhat is your name? ")
end
#When data is received from a client this event runs
def receive_data(data)
#If name is not yet set do this first,set @initiated to true, and only now add client to connections array
if !@initiated
#Make sure name is 3 to 15 chars, and only contains letters and numbers
if data.strip =~ /^[a-zA-Z0-9]{3,15}$/
@name = data.strip
@@clients << self
send_data "Welcome #{@name}.\n"
@initiated = true
sendpublic "#{@name} has joined.\n > "
else
send_data "Invalid Name. Max 15 chars. Only letters or numbers.\nName : "
end
#After initialisation of a connection continue here
else
#Check for recognised commands and handle them
case (data.strip)
# /names
when /^\/names$/ then
send_data "Current Users: " + @@clients.find_all.collect{ |client| client.name }.sort.join(" ") + "\n > "
# /help
when /^\/help$/ then
send_data "Help not implemented yet\n > "
# /me text
when /^\/me (.*)$/ then
sendpublic "* #{@name} #{$1.strip}"
send_data "* #{@name} #{$1.strip}\n >"
#If data is not a recognised command assume its chat text and send to all clients
else
sendpublic "#{@name} > #{data}"
send_data " > "
end
end
end
end
EM::run do
begin
EM::start_server IP, PORT, EmChat
puts "Service started on #{IP}:#{PORT}."
rescue RuntimeError
puts "Failed to start service on #{IP}:#{PORT}"
exit 1
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment