require 'socket' # TCPSocket require 'openssl' require 'array-unique' #require "fantasy-irc/rooms" #require "fantasy-irc/users" #require "fantasy-irc/events" #require "fantasy-irc/plugins" module Fantasy class IRC attr_reader :events, :rooms, :users, :plugins attr_reader :started, :session, :loggedin, :connected def initialize options={} $bot = self @data = Hash.new @started = Time.now.to_i @running = 0 @loggedin = 0 @connected = 0 @prefix = options[:prefix] || '!' @events = Event::Factory.new @rooms = Room::Factory.new self @users = User::Factory.new self @plugins = Plugins.new %w{tick loggedin connected user_joined channel_message}.each do |e| @events.create(e) end end def login data if not data[:nickname] and data[:username] then data[:nickname] = data[:username] end if not data[:username] and data[:nickname] then data[:username] = data[:nickname] end if not data[:realname] then data[:realname] = "https://bitbucket.org/v2px/fantasy-irc" end if not data[:nickname] then raise "you need to specify :nickname and/or :username on login" end if data[:password] then self.send("PASS #{data[:password]}") end self.send("USER #{data[:username]} fantasy fantasy :#{data[:realname]}") self.send("NICK #{data[:nickname]}") end def send(s) # Send a message to the server if not @connected then raise "not connected to a server!" end puts "<-- #{s}" @data[:socket].puts "#{s}\n" end def connect data if not data[:server] then raise "you need to specify :server on connect" end if not data[:ssl] then data[:ssl] = false end if not data[:port] then data[:port] = data[:ssl] ? 6697 : 6667 end # Connect to the chat server @data[:socket] = @data[:realsocket] = TCPSocket.open(data[:server], data[:port]) if !!data[:ssl] then @data[:socket] = OpenSSL::SSL::SSLSocket.new(@data[:realsocket]) @data[:socket].connect end @connected = Time.now.to_i self.events.by_name('connected').call end def cleanup puts "[!][Cleanup] Closing socket." @data[:socket].close() end def parse(s) s.chomp! if (s[0,4] == "PING") then tok = s.split(' ', 2) self.send "PONG #{tok[1]}" elsif (s[0,1] == ":") then # Server stuff tok = s.split(' ', 4) if (tok[1] == "001") then # loggedin @loggedin = Time.now.to_i # ignore ourself self.users.create(tok[2]).ignore! self.events.by_name('loggedin').call elsif (tok[1] == "JOIN") then # user joined room = self.rooms.by_name(tok[2][1,tok[2].length]) user = self.users.create(tok[0][1,tok[0].length]) # add user to room and room to user room.users << user user.rooms << room return if user.ignored? self.events.by_name('user_joined').call([room, user]) elsif (tok[1] == "PRIVMSG") then # tok = s.split(' ', 4) # channel or private message if (tok[2][0,1] == "#") then # channel message room = self.rooms.by_name(tok[2]) user = self.users.create(tok[0][1,tok[0].length]) # add user to room and room to user room.users << user user.rooms << room return if user.ignored? text = tok[3][1,tok[3].length] self.events.by_name('channel_message').call([room, user, text]) if text[0] == @prefix then command, args = text.split(' ', 2) self.plugins.command(command[1,command.length], {:room => room, :user => user}, args) end end else # puts "[!] UNKNOWN PROTOCOL PART: #{s}" end else puts "[!] UNKNOWN PROTOCOL PART: #{s}" end end def run if @running.nonzero? then return false end @running = Time.now.to_i last_tick = @running last_ping = @running last_session_ping = @running loop do time_now = Time.now # tick every second if time_now.to_i > last_tick then self.events.by_name('tick').call(time_now) last_tick = time_now.to_i end # chatserver ping, every 5 minutes if @connected.nonzero? and time_now.to_i-300 >= last_ping then self.send("PING :"+time_now.to_f.to_s) last_ping = time_now.to_i end # connection if @connected.nonzero? then ready = select([@data[:socket]], nil, nil, 0.1) next if !ready for s in ready[0] if s == @data[:socket] then return if @data[:socket].eof # XXX? s = @data[:socket].gets puts "--> #{s}" self.parse(s) end end else # no connection, less cpu usage sleep(0.5) end end end end end