lib/net/irc.rb in net-irc-0.0.3 vs lib/net/irc.rb in net-irc-0.0.4
- old
+ new
@@ -7,295 +7,20 @@
require "monitor"
module Net; end
module Net::IRC
- VERSION = "0.0.3"
+ VERSION = "0.0.4"
class IRCException < StandardError; end
- module PATTERN # :nodoc:
- # letter = %x41-5A / %x61-7A ; A-Z / a-z
- # digit = %x30-39 ; 0-9
- # hexdigit = digit / "A" / "B" / "C" / "D" / "E" / "F"
- # special = %x5B-60 / %x7B-7D
- # ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
- LETTER = 'A-Za-z'
- DIGIT = '\d'
- HEXDIGIT = "#{DIGIT}A-Fa-f"
- SPECIAL = '\x5B-\x60\x7B-\x7D'
+ require "net/irc/constants"
+ require "net/irc/pattern"
- # shortname = ( letter / digit ) *( letter / digit / "-" )
- # *( letter / digit )
- # ; as specified in RFC 1123 [HNAME]
- # hostname = shortname *( "." shortname )
- SHORTNAME = "[#{LETTER}#{DIGIT}](?:[-#{LETTER}#{DIGIT}]*[#{LETTER}#{DIGIT}])?"
- HOSTNAME = "#{SHORTNAME}(?:\\.#{SHORTNAME})*"
+ autoload :Message, "net/irc/message"
+ autoload :Client, "net/irc/client"
+ autoload :Server, "net/irc/server"
- # servername = hostname
- SERVERNAME = HOSTNAME
-
- # nickname = ( letter / special ) *8( letter / digit / special / "-" )
- #NICKNAME = "[#{LETTER}#{SPECIAL}\\w][-#{LETTER}#{DIGIT}#{SPECIAL}]*"
- NICKNAME = "\\S+" # for multibytes
-
- # user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
- # ; any octet except NUL, CR, LF, " " and "@"
- USER = '[\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x3F\x41-\xFF]+'
-
- # ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
- IP4ADDR = "[#{DIGIT}]{1,3}(?:\\.[#{DIGIT}]{1,3}){3}"
- # ip6addr = 1*hexdigit 7( ":" 1*hexdigit )
- # ip6addr =/ "0:0:0:0:0:" ( "0" / "FFFF" ) ":" ip4addr
- IP6ADDR = "(?:[#{HEXDIGIT}]+(?::[#{HEXDIGIT}]+){7}|0:0:0:0:0:(?:0|FFFF):#{IP4ADDR})"
- # hostaddr = ip4addr / ip6addr
- HOSTADDR = "(?:#{IP4ADDR}|#{IP6ADDR})"
-
- # host = hostname / hostaddr
- HOST = "(?:#{HOSTNAME}|#{HOSTADDR})"
-
- # prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
- PREFIX = "(?:#{NICKNAME}(?:(?:!#{USER})?@#{HOST})?|#{SERVERNAME})"
-
- # nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF
- # ; any octet except NUL, CR, LF, " " and ":"
- NOSPCRLFCL = '\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x39\x3B-\xFF'
-
- # command = 1*letter / 3digit
- COMMAND = "(?:[#{LETTER}]+|[#{DIGIT}]{3})"
-
- # SPACE = %x20 ; space character
- # middle = nospcrlfcl *( ":" / nospcrlfcl )
- # trailing = *( ":" / " " / nospcrlfcl )
- # params = *14( SPACE middle ) [ SPACE ":" trailing ]
- # =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
- MIDDLE = "[#{NOSPCRLFCL}][:#{NOSPCRLFCL}]*"
- TRAILING = "[: #{NOSPCRLFCL}]*"
- PARAMS = "(?:((?: #{MIDDLE}){0,14})(?: :(#{TRAILING}))?|((?: #{MIDDLE}){14})(?::?)?(#{TRAILING}))"
-
- # crlf = %x0D %x0A ; "carriage return" "linefeed"
- # message = [ ":" prefix SPACE ] command [ params ] crlf
- CRLF = '\x0D\x0A'
- MESSAGE = "(?::(#{PREFIX}) )?(#{COMMAND})#{PARAMS}\s*#{CRLF}"
-
- CLIENT_PATTERN = /\A#{NICKNAME}(?:(?:!#{USER})?@#{HOST})\z/on
- MESSAGE_PATTERN = /\A#{MESSAGE}\z/on
- end # PATTERN
-
- module Constants # :nodoc:
- RPL_WELCOME = '001'
- RPL_YOURHOST = '002'
- RPL_CREATED = '003'
- RPL_MYINFO = '004'
- RPL_BOUNCE = '005'
- RPL_USERHOST = '302'
- RPL_ISON = '303'
- RPL_AWAY = '301'
- RPL_UNAWAY = '305'
- RPL_NOWAWAY = '306'
- RPL_WHOISUSER = '311'
- RPL_WHOISSERVER = '312'
- RPL_WHOISOPERATOR = '313'
- RPL_WHOISIDLE = '317'
- RPL_ENDOFWHOIS = '318'
- RPL_WHOISCHANNELS = '319'
- RPL_WHOWASUSER = '314'
- RPL_ENDOFWHOWAS = '369'
- RPL_LISTSTART = '321'
- RPL_LIST = '322'
- RPL_LISTEND = '323'
- RPL_UNIQOPIS = '325'
- RPL_CHANNELMODEIS = '324'
- RPL_NOTOPIC = '331'
- RPL_TOPIC = '332'
- RPL_INVITING = '341'
- RPL_SUMMONING = '342'
- RPL_INVITELIST = '346'
- RPL_ENDOFINVITELIST = '347'
- RPL_EXCEPTLIST = '348'
- RPL_ENDOFEXCEPTLIST = '349'
- RPL_VERSION = '351'
- RPL_WHOREPLY = '352'
- RPL_ENDOFWHO = '315'
- RPL_NAMREPLY = '353'
- RPL_ENDOFNAMES = '366'
- RPL_LINKS = '364'
- RPL_ENDOFLINKS = '365'
- RPL_BANLIST = '367'
- RPL_ENDOFBANLIST = '368'
- RPL_INFO = '371'
- RPL_ENDOFINFO = '374'
- RPL_MOTDSTART = '375'
- RPL_MOTD = '372'
- RPL_ENDOFMOTD = '376'
- RPL_YOUREOPER = '381'
- RPL_REHASHING = '382'
- RPL_YOURESERVICE = '383'
- RPL_TIME = '391'
- RPL_USERSSTART = '392'
- RPL_USERS = '393'
- RPL_ENDOFUSERS = '394'
- RPL_NOUSERS = '395'
- RPL_TRACELINK = '200'
- RPL_TRACECONNECTING = '201'
- RPL_TRACEHANDSHAKE = '202'
- RPL_TRACEUNKNOWN = '203'
- RPL_TRACEOPERATOR = '204'
- RPL_TRACEUSER = '205'
- RPL_TRACESERVER = '206'
- RPL_TRACESERVICE = '207'
- RPL_TRACENEWTYPE = '208'
- RPL_TRACECLASS = '209'
- RPL_TRACERECONNECT = '210'
- RPL_TRACELOG = '261'
- RPL_TRACEEND = '262'
- RPL_STATSLINKINFO = '211'
- RPL_STATSCOMMANDS = '212'
- RPL_ENDOFSTATS = '219'
- RPL_STATSUPTIME = '242'
- RPL_STATSOLINE = '243'
- RPL_UMODEIS = '221'
- RPL_SERVLIST = '234'
- RPL_SERVLISTEND = '235'
- RPL_LUSERCLIENT = '251'
- RPL_LUSEROP = '252'
- RPL_LUSERUNKNOWN = '253'
- RPL_LUSERCHANNELS = '254'
- RPL_LUSERME = '255'
- RPL_ADMINME = '256'
- RPL_ADMINLOC1 = '257'
- RPL_ADMINLOC2 = '258'
- RPL_ADMINEMAIL = '259'
- RPL_TRYAGAIN = '263'
- ERR_NOSUCHNICK = '401'
- ERR_NOSUCHSERVER = '402'
- ERR_NOSUCHCHANNEL = '403'
- ERR_CANNOTSENDTOCHAN = '404'
- ERR_TOOMANYCHANNELS = '405'
- ERR_WASNOSUCHNICK = '406'
- ERR_TOOMANYTARGETS = '407'
- ERR_NOSUCHSERVICE = '408'
- ERR_NOORIGIN = '409'
- ERR_NORECIPIENT = '411'
- ERR_NOTEXTTOSEND = '412'
- ERR_NOTOPLEVEL = '413'
- ERR_WILDTOPLEVEL = '414'
- ERR_BADMASK = '415'
- ERR_UNKNOWNCOMMAND = '421'
- ERR_NOMOTD = '422'
- ERR_NOADMININFO = '423'
- ERR_FILEERROR = '424'
- ERR_NONICKNAMEGIVEN = '431'
- ERR_ERRONEUSNICKNAME = '432'
- ERR_NICKNAMEINUSE = '433'
- ERR_NICKCOLLISION = '436'
- ERR_UNAVAILRESOURCE = '437'
- ERR_USERNOTINCHANNEL = '441'
- ERR_NOTONCHANNEL = '442'
- ERR_USERONCHANNEL = '443'
- ERR_NOLOGIN = '444'
- ERR_SUMMONDISABLED = '445'
- ERR_USERSDISABLED = '446'
- ERR_NOTREGISTERED = '451'
- ERR_NEEDMOREPARAMS = '461'
- ERR_ALREADYREGISTRED = '462'
- ERR_NOPERMFORHOST = '463'
- ERR_PASSWDMISMATCH = '464'
- ERR_YOUREBANNEDCREEP = '465'
- ERR_YOUWILLBEBANNED = '466'
- ERR_KEYSET = '467'
- ERR_CHANNELISFULL = '471'
- ERR_UNKNOWNMODE = '472'
- ERR_INVITEONLYCHAN = '473'
- ERR_BANNEDFROMCHAN = '474'
- ERR_BADCHANNELKEY = '475'
- ERR_BADCHANMASK = '476'
- ERR_NOCHANMODES = '477'
- ERR_BANLISTFULL = '478'
- ERR_NOPRIVILEGES = '481'
- ERR_CHANOPRIVSNEEDED = '482'
- ERR_CANTKILLSERVER = '483'
- ERR_RESTRICTED = '484'
- ERR_UNIQOPPRIVSNEEDED = '485'
- ERR_NOOPERHOST = '491'
- ERR_UMODEUNKNOWNFLAG = '501'
- ERR_USERSDONTMATCH = '502'
- RPL_SERVICEINFO = '231'
- RPL_ENDOFSERVICES = '232'
- RPL_SERVICE = '233'
- RPL_NONE = '300'
- RPL_WHOISCHANOP = '316'
- RPL_KILLDONE = '361'
- RPL_CLOSING = '362'
- RPL_CLOSEEND = '363'
- RPL_INFOSTART = '373'
- RPL_MYPORTIS = '384'
- RPL_STATSCLINE = '213'
- RPL_STATSNLINE = '214'
- RPL_STATSILINE = '215'
- RPL_STATSKLINE = '216'
- RPL_STATSQLINE = '217'
- RPL_STATSYLINE = '218'
- RPL_STATSVLINE = '240'
- RPL_STATSLLINE = '241'
- RPL_STATSHLINE = '244'
- RPL_STATSSLINE = '244'
- RPL_STATSPING = '246'
- RPL_STATSBLINE = '247'
- RPL_STATSDLINE = '250'
- ERR_NOSERVICEHOST = '492'
-
- PASS = 'PASS'
- NICK = 'NICK'
- USER = 'USER'
- OPER = 'OPER'
- MODE = 'MODE'
- SERVICE = 'SERVICE'
- QUIT = 'QUIT'
- SQUIT = 'SQUIT'
- JOIN = 'JOIN'
- PART = 'PART'
- TOPIC = 'TOPIC'
- NAMES = 'NAMES'
- LIST = 'LIST'
- INVITE = 'INVITE'
- KICK = 'KICK'
- PRIVMSG = 'PRIVMSG'
- NOTICE = 'NOTICE'
- MOTD = 'MOTD'
- LUSERS = 'LUSERS'
- VERSION = 'VERSION'
- STATS = 'STATS'
- LINKS = 'LINKS'
- TIME = 'TIME'
- CONNECT = 'CONNECT'
- TRACE = 'TRACE'
- ADMIN = 'ADMIN'
- INFO = 'INFO'
- SERVLIST = 'SERVLIST'
- SQUERY = 'SQUERY'
- WHO = 'WHO'
- WHOIS = 'WHOIS'
- WHOWAS = 'WHOWAS'
- KILL = 'KILL'
- PING = 'PING'
- PONG = 'PONG'
- ERROR = 'ERROR'
- AWAY = 'AWAY'
- REHASH = 'REHASH'
- DIE = 'DIE'
- RESTART = 'RESTART'
- SUMMON = 'SUMMON'
- USERS = 'USERS'
- WALLOPS = 'WALLOPS'
- USERHOST = 'USERHOST'
- ISON = 'ISON'
- end
-
- COMMANDS = Constants.constants.inject({}) {|r,i| # :nodoc:
- r.update(Constants.const_get(i) => i)
- }
-
class Prefix < String
def nick
extract[0]
end
@@ -330,535 +55,5 @@
str
end
module_function :ctcp_decoding
end
-class Net::IRC::Message
- include Net::IRC
-
- class InvalidMessage < Net::IRC::IRCException; end
-
- attr_reader :prefix, :command, :params
-
- # Parse string and return new Message.
- # If the string is invalid message, this method raises Net::IRC::Message::InvalidMessage.
- def self.parse(str)
- _, prefix, command, *rest = *PATTERN::MESSAGE_PATTERN.match(str)
- raise InvalidMessage, "Invalid message: #{str.dump}" unless _
-
- case
- when rest[0] && !rest[0].empty?
- middle, trailer, = *rest
- when rest[2] && !rest[2].empty?
- middle, trailer, = *rest[2, 2]
- when rest[1]
- params = []
- trailer = rest[1]
- when rest[3]
- params = []
- trailer = rest[3]
- else
- params = []
- end
-
- params ||= middle.split(/ /)[1..-1]
- params << trailer if trailer
-
- new(prefix, command, params)
- end
-
- def initialize(prefix, command, params)
- @prefix = Prefix.new(prefix.to_s)
- @command = command
- @params = params
- end
-
- # Same as @params[n].
- def [](n)
- @params[n]
- end
-
- # Iterate params.
- def each(&block)
- @params.each(&block)
- end
-
- # Stringfy message to raw IRC message.
- def to_s
- str = ""
-
- str << ":#{@prefix} " unless @prefix.empty?
- str << @command
-
- if @params
- f = false
- @params.each do |param|
- str << " "
- if !f && (param.size == 0 || / / =~ param || /^:/ =~ param)
- str << ":#{param}"
- f = true
- else
- str << param
- end
- end
- end
-
- str << "\x0D\x0A"
-
- str
- end
- alias to_str to_s
-
- # Same as params.
- def to_a
- @params
- end
-
- # If the message is CTCP, return true.
- def ctcp?
- message = @params[1]
- message[0] == 1 && message[message.length-1] == 1
- end
-
- def inspect
- '#<%s:0x%x prefix:%s command:%s params:%s>' % [
- self.class,
- self.object_id,
- @prefix,
- @command,
- @params.inspect
- ]
- end
-
-end # Message
-
-class Net::IRC::Client
- include Net::IRC
- include Constants
-
- attr_reader :host, :port, :opts
- attr_reader :prefix, :channels
-
- def initialize(host, port, opts={})
- @host = host
- @port = port
- @opts = OpenStruct.new(opts)
- @log = @opts.logger || Logger.new($stdout)
- @channels = {
-# "#channel" => {
-# :modes => [],
-# :users => [],
-# }
- }
- @channels.extend(MonitorMixin)
- end
-
- # Connect to server and start loop.
- def start
- @socket = TCPSocket.open(@host, @port)
- on_connected
- post PASS, @opts.pass if @opts.pass
- post NICK, @opts.nick
- post USER, @opts.user, "0", "*", @opts.real
- while l = @socket.gets
- begin
- @log.debug "RECEIVE: #{l.chomp}"
- m = Message.parse(l)
- next if on_message(m) === true
- name = "on_#{(COMMANDS[m.command.upcase] || m.command).downcase}"
- send(name, m) if respond_to?(name)
- rescue Exception => e
- warn e
- warn e.backtrace.join("\r\t")
- raise
- rescue Message::InvalidMessage
- @log.error "MessageParse: " + l.inspect
- end
- end
- rescue IOError
- ensure
- finish
- end
-
- # Close connection to server.
- def finish
- begin
- @socket.close
- rescue
- end
- on_disconnected
- end
-
- # Catch all messages.
- # If this method return true, aother callback will not be called.
- def on_message(m)
- end
-
- # Default RPL_WELCOME callback.
- # This sets @prefix from the message.
- def on_rpl_welcome(m)
- @prefix = Prefix.new(m[1][/\S+$/])
- end
-
- # Default PING callback. Response PONG.
- def on_ping(m)
- post PONG, @prefix ? @prefix.nick : ""
- end
-
- # For managing channel
- def on_rpl_namreply(m)
- type = m[1]
- channel = m[2]
- init_channel(channel)
-
- @channels.synchronize do
- m[3].split(/\s+/).each do |u|
- _, mode, nick = *u.match(/^([@+]?)(.+)/)
-
- @channels[channel][:users] << nick
- @channels[channel][:users].uniq!
-
- case mode
- when "@" # channel operator
- @channels[channel][:modes] << ["o", nick]
- when "+" # voiced (under moderating mode)
- @channels[channel][:modes] << ["v", nick]
- end
- end
-
- case type
- when "@" # secret
- @channels[channel][:modes] << ["s", nil]
- when "*" # private
- @channels[channel][:modes] << ["p", nil]
- when "=" # public
- end
-
- @channels[channel][:modes].uniq!
- end
- end
-
- # For managing channel
- def on_part(m)
- nick = m.prefix.nick
- channel = m[0]
- init_channel(channel)
-
- @channels.synchronize do
- info = @channels[channel]
- if info
- info[:users].delete(nick)
- info[:modes].delete_if {|u|
- u[1] == nick
- }
- end
- end
- end
-
- # For managing channel
- def on_quit(m)
- nick = m.prefix.nick
-
- @channels.synchronize do
- @channels.each do |channel, info|
- info[:users].delete(nick)
- info[:modes].delete_if {|u|
- u[1] == nick
- }
- end
- end
- end
-
- # For managing channel
- def on_kick(m)
- users = m[1].split(/,/)
-
- @channels.synchronize do
- m[0].split(/,/).each do |chan|
- init_channel(chan)
- info = @channels[chan]
- if info
- users.each do |nick|
- info[:users].delete(nick)
- info[:modes].delete_if {|u|
- u[1] == nick
- }
- end
- end
- end
- end
- end
-
- # For managing channel
- def on_join(m)
- nick = m.prefix.nick
- channel = m[0]
-
- @channels.synchronize do
- init_channel(channel)
-
- @channels[channel][:users] << nick
- @channels[channel][:users].uniq!
- end
- end
-
- # For managing channel
- def on_mode(m)
- channel = m[0]
- @channels.synchronize do
- init_channel(channel)
-
- positive_mode = []
- negative_mode = []
-
- mode = positive_mode
- arg_pos = 0
- m[1].each_byte do |c|
- case c
- when ?+
- mode = positive_mode
- when ?-
- mode = negative_mode
- when ?o, ?v, ?k, ?l, ?b, ?e, ?I
- mode << [c.chr, m[arg_pos + 2]]
- arg_pos += 1
- else
- mode << [c.chr, nil]
- end
- end
- mode = nil
-
- negative_mode.each do |m|
- @channels[channel][:modes].delete(m)
- end
-
- positive_mode.each do |m|
- @channels[channel][:modes] << m
- end
-
- @channels[channel][:modes].uniq!
- [negative_mode, positive_mode]
- end
- end
-
- # For managing channel
- def init_channel(channel)
- @channels[channel] ||= {
- :modes => [],
- :users => [],
- }
- end
-
- # Do nothing.
- # This is for avoiding error on calling super.
- # So you can always call super at subclass.
- def method_missing(name, *args)
- end
-
- # Call when socket connected.
- def on_connected
- end
-
- # Call when socket closed.
- def on_disconnected
- end
-
- private
-
- # Post message to server.
- #
- # include Net::IRC::Constans
- # post PRIVMSG, "#channel", "foobar"
- def post(command, *params)
- m = Message.new(nil, command, params.map {|s|
- s ? s.gsub(/[\r\n]/, " ") : ""
- })
-
- @log.debug "SEND: #{m.to_s.chomp}"
- @socket << m
- end
-end # Client
-
-class Net::IRC::Server
- def initialize(host, port, session_class, opts={})
- @host = host
- @port = port
- @session_class = session_class
- @opts = OpenStruct.new(opts)
- @sessions = []
- end
-
- # Start server loop.
- def start
- @serv = TCPServer.new(@host, @port)
- @log = @opts.logger || Logger.new($stdout)
- @log.info "Host: #{@host} Port:#{@port}"
- @accept = Thread.start do
- loop do
- Thread.start(@serv.accept) do |s|
- begin
- @log.info "Client connected, new session starting..."
- s = @session_class.new(self, s, @log, @opts)
- @sessions << s
- s.start
- rescue Exception => e
- puts e
- puts e.backtrace
- ensure
- @sessions.delete(s)
- end
- end
- end
- end
- @accept.join
- end
-
- # Close all sessions.
- def finish
- Thread.exclusive do
- @accept.kill
- begin
- @serv.close
- rescue
- end
- @sessions.each do |s|
- s.finish
- end
- end
- end
-
-
- class Session
- include Net::IRC
- include Constants
-
- attr_reader :prefix, :nick, :real, :host
-
- # Override subclass.
- def server_name
- "net-irc"
- end
-
- # Override subclass.
- def server_version
- "0.0.0"
- end
-
- # Override subclass.
- def avaiable_user_modes
- "eixwy"
- end
-
- # Override subclass.
- def avaiable_channel_modes
- "spknm"
- end
-
- def initialize(server, socket, logger, opts={})
- @server, @socket, @log, @opts = server, socket, logger, opts
- end
-
- def self.start(*args)
- new(*args).start
- end
-
- # Start session loop.
- def start
- on_connected
- while l = @socket.gets
- begin
- @log.debug "RECEIVE: #{l.chomp}"
- m = Message.parse(l)
- next if on_message(m) === true
-
- name = "on_#{(COMMANDS[m.command.upcase] || m.command).downcase}"
- send(name, m) if respond_to?(name)
-
- break if m.command == QUIT
- rescue Message::InvalidMessage
- @log.error "MessageParse: " + l.inspect
- end
- end
- rescue IOError
- ensure
- finish
- end
-
- # Close this session.
- def finish
- begin
- @socket.close
- rescue
- end
- on_disconnected
- end
-
- # Default PASS callback.
- # Set @pass.
- def on_pass(m)
- @pass = m.params[0]
- end
-
- # Default NICK callback.
- # Set @nick.
- def on_nick(m)
- @nick = m.params[0]
- end
-
- # Default USER callback.
- # Set @user, @real, @host and call initial_message.
- def on_user(m)
- @user, @real = m.params[0], m.params[3]
- @host = @socket.peeraddr[2]
- @prefix = Prefix.new("#{@nick}!#{@user}@#{@host}")
- initial_message
- end
-
- # Call when socket connected.
- def on_connected
- end
-
- # Call when socket closed.
- def on_disconnected
- end
-
- # Catch all messages.
- # If this method return true, aother callback will not be called.
- def on_message(m)
- end
-
- # Default PING callback. Response PONG.
- def on_ping(m)
- post server_name, PONG, m.params[0]
- end
-
- # Do nothing.
- # This is for avoiding error on calling super.
- # So you can always call super at subclass.
- def method_missing(name, *args)
- end
-
- private
- # Post message to server.
- #
- # include Net::IRC::Constans
- # post prefix, PRIVMSG, "#channel", "foobar"
- def post(prefix, command, *params)
- m = Message.new(prefix, command, params.map {|s|
- s.gsub(/[\r\n]/, " ")
- })
- @log.debug "SEND: #{m.to_s.chomp}"
- @socket << m
- rescue IOError
- finish
- end
-
- # Call when client connected.
- # Send RPL_WELCOME sequence. If you want to customize, override this method at subclass.
- def initial_message
- post server_name, RPL_WELCOME, @nick, "Welcome to the Internet Relay Network #{@prefix}"
- post server_name, RPL_YOURHOST, @nick, "Your host is #{server_name}, running version #{server_version}"
- post server_name, RPL_CREATED, @nick, "This server was created #{Time.now}"
- post server_name, RPL_MYINFO, @nick, "#{server_name} #{server_version} #{avaiable_user_modes} #{avaiable_channel_modes}"
- end
- end
-end # Server