diff options
author | Giuseppe Bilotta <giuseppe.bilotta@gmail.com> | 2006-07-31 15:33:15 +0000 |
---|---|---|
committer | Giuseppe Bilotta <giuseppe.bilotta@gmail.com> | 2006-07-31 15:33:15 +0000 |
commit | 2a27c12fffa359898c5601a211fe19425da82fa6 (patch) | |
tree | 68d08b03f5552c1c6da172b7359a2df005984832 /lib/rbot/ircbot.rb | |
parent | 808c62190d6ed88f29df92871f810177b540c8a1 (diff) |
First shot at the new Irc framework. Bot is usable (sort of), but not all functionality may work as expected (or at all). If you are testing it, please report. Auth is known to be nonfunctional
Diffstat (limited to 'lib/rbot/ircbot.rb')
-rw-r--r-- | lib/rbot/ircbot.rb | 301 |
1 files changed, 148 insertions, 153 deletions
diff --git a/lib/rbot/ircbot.rb b/lib/rbot/ircbot.rb index 6226e55e..65b94172 100644 --- a/lib/rbot/ircbot.rb +++ b/lib/rbot/ircbot.rb @@ -71,13 +71,14 @@ require 'rbot/rbotconfig' require 'rbot/config' require 'rbot/utils' +require 'rbot/irc' require 'rbot/rfc2812' require 'rbot/keywords' require 'rbot/ircsocket' require 'rbot/auth' require 'rbot/timer' require 'rbot/plugins' -require 'rbot/channel' +# require 'rbot/channel' require 'rbot/message' require 'rbot/language' require 'rbot/dbhash' @@ -89,9 +90,6 @@ module Irc # Main bot class, which manages the various components, receives messages, # handles them or passes them to plugins, and contains core functionality. class IrcBot - # the bot's current nickname - attr_reader :nick - # the bot's IrcAuth data attr_reader :auth @@ -108,13 +106,12 @@ class IrcBot # bot's Language data attr_reader :lang - # capabilities info for the server - attr_reader :capabilities - - # channel info for channels the bot is in - attr_reader :channels + # server the bot is connected to + # TODO multiserver + attr_reader :server # bot's irc socket + # TODO multiserver attr_reader :socket # bot's object registry, plugins get an interface to this for persistant @@ -129,6 +126,14 @@ class IrcBot # proxies etc as defined by the bot configuration/environment attr_reader :httputil + # bot User in the client/server connection + attr_reader :myself + + # bot User in the client/server connection + def nick + myself.nick + end + # create a new IrcBot with botclass +botclass+ def initialize(botclass, params = {}) # BotConfig for the core bot @@ -308,14 +313,19 @@ class IrcBot log_session_start - @timer = Timer::Timer.new(1.0) # only need per-second granularity @registry = BotRegistry.new self + + @timer = Timer::Timer.new(1.0) # only need per-second granularity @timer.add(@config['core.save_every']) { save } if @config['core.save_every'] - @channels = Hash.new + @logs = Hash.new + @httputil = Utils::HttpUtil.new(self) + @lang = Language::Language.new(@config['core.language']) + @keywords = Keywords.new(self) + begin @auth = IrcAuth.new(self) rescue => e @@ -329,28 +339,51 @@ class IrcBot @plugins = Plugins::Plugins.new(self, ["#{botclass}/plugins"]) @socket = IrcSocket.new(@config['server.name'], @config['server.port'], @config['server.bindhost'], @config['server.sendq_delay'], @config['server.sendq_burst']) - @nick = @config['irc.nick'] - @client = IrcClient.new + @server = @client.server + @myself = @client.client + @myself.nick = @config['irc.nick'] + + # Channels where we are quiet + # It's nil when we are not quiet, an empty list when we are quiet + # in all channels, a list of channels otherwise + @quiet = nil + + + @client[:welcome] = proc {|data| + irclog "joined server #{@client.server} as #{myself}", "server" + + @plugins.delegate("connect") + + @config['irc.join_channels'].each { |c| + debug "autojoining channel #{c}" + if(c =~ /^(\S+)\s+(\S+)$/i) + join $1, $2 + else + join c if(c) + end + } + } @client[:isupport] = proc { |data| - if data[:capab] - sendq "CAPAB IDENTIFY-MSG" - end + # TODO this needs to go into rfc2812.rb + # Since capabs are two-steps processes, server.supports[:capab] + # should be a three-state: nil, [], [....] + sendq "CAPAB IDENTIFY-MSG" if @server.supports[:capab] } @client[:datastr] = proc { |data| - debug data.inspect + # TODO this needs to go into rfc2812.rb if data[:text] == "IDENTIFY-MSG" - @capabilities["identify-msg".to_sym] = true + @server.capabilities["identify-msg".to_sym] = true else debug "Not handling RPL_DATASTR #{data[:servermessage]}" end } @client[:privmsg] = proc { |data| - message = PrivMessage.new(self, data[:source], data[:target], data[:message]) + message = PrivMessage.new(self, @server, data[:source], data[:target], data[:message]) onprivmsg(message) } @client[:notice] = proc { |data| - message = NoticeMessage.new(self, data[:source], data[:target], data[:message]) + message = NoticeMessage.new(self, @server, data[:source], data[:target], data[:message]) # pass it off to plugins that want to hear everything @plugins.delegate "listen", message } @@ -373,125 +406,99 @@ class IrcBot @last_ping = nil } @client[:nick] = proc {|data| - sourcenick = data[:sourcenick] - nick = data[:nick] - m = NickMessage.new(self, data[:source], data[:sourcenick], data[:nick]) - if(sourcenick == @nick) - debug "my nick is now #{nick}" - @nick = nick + source = data[:source] + old = data[:oldnick] + new = data[:newnick] + m = NickMessage.new(self, @server, source, old, new) + if source == myself + debug "my nick is now #{new}" end - @channels.each {|k,v| - if(v.users.has_key?(sourcenick)) - irclog "@ #{sourcenick} is now known as #{nick}", k - v.users[nick] = v.users[sourcenick] - v.users.delete(sourcenick) - end + data[:is_on].each { |ch| + irclog "@ #{data[:old]} is now known as #{data[:new]}", ch } @plugins.delegate("listen", m) @plugins.delegate("nick", m) } @client[:quit] = proc {|data| - source = data[:source] - sourcenick = data[:sourcenick] - sourceurl = data[:sourceaddress] - message = data[:message] - m = QuitMessage.new(self, data[:source], data[:sourcenick], data[:message]) - if(data[:sourcenick] =~ /#{Regexp.escape(@nick)}/i) - else - @channels.each {|k,v| - if(v.users.has_key?(sourcenick)) - irclog "@ Quit: #{sourcenick}: #{message}", k - v.users.delete(sourcenick) - end - } - end + m = QuitMessage.new(self, @server, data[:source], data[:source], data[:message]) + data[:was_on].each { |ch| + irclog "@ Quit: #{sourcenick}: #{message}", ch + } @plugins.delegate("listen", m) @plugins.delegate("quit", m) } @client[:mode] = proc {|data| - source = data[:source] - sourcenick = data[:sourcenick] - sourceurl = data[:sourceaddress] - channel = data[:channel] - targets = data[:targets] - modestring = data[:modestring] - irclog "@ Mode #{modestring} #{targets} by #{sourcenick}", channel - } - @client[:welcome] = proc {|data| - irclog "joined server #{data[:source]} as #{data[:nick]}", "server" - debug "I think my nick is #{@nick}, server thinks #{data[:nick]}" - if data[:nick] && data[:nick].length > 0 - @nick = data[:nick] - end - - @plugins.delegate("connect") - - @config['irc.join_channels'].each {|c| - debug "autojoining channel #{c}" - if(c =~ /^(\S+)\s+(\S+)$/i) - join $1, $2 - else - join c if(c) - end - } + irclog "@ Mode #{data[:modestring]} by #{data[:sourcenick]}", data[:channel] } @client[:join] = proc {|data| - m = JoinMessage.new(self, data[:source], data[:channel], data[:message]) + m = JoinMessage.new(self, @server, data[:source], data[:channel], data[:message]) onjoin(m) } @client[:part] = proc {|data| - m = PartMessage.new(self, data[:source], data[:channel], data[:message]) + m = PartMessage.new(self, @server, data[:source], data[:channel], data[:message]) onpart(m) } @client[:kick] = proc {|data| - m = KickMessage.new(self, data[:source], data[:target],data[:channel],data[:message]) + m = KickMessage.new(self, @server, data[:source], data[:target], data[:channel],data[:message]) onkick(m) } @client[:invite] = proc {|data| - if(data[:target] =~ /^#{Regexp.escape(@nick)}$/i) - join data[:channel] if (@auth.allow?("join", data[:source], data[:sourcenick])) + if data[:target] == myself + join data[:channel] if @auth.allow?("join", data[:source], data[:source].nick) end } @client[:changetopic] = proc {|data| channel = data[:channel] - sourcenick = data[:sourcenick] + source = data[:source] topic = data[:topic] - timestamp = data[:unixtime] || Time.now.to_i - if(sourcenick == @nick) + if source == myself irclog "@ I set topic \"#{topic}\"", channel else - irclog "@ #{sourcenick} set topic \"#{topic}\"", channel + irclog "@ #{source} set topic \"#{topic}\"", channel end - m = TopicMessage.new(self, data[:source], data[:channel], timestamp, data[:topic]) + m = TopicMessage.new(self, @server, data[:source], data[:channel], data[:topic]) ontopic(m) @plugins.delegate("listen", m) @plugins.delegate("topic", m) } - @client[:topic] = @client[:topicinfo] = proc {|data| - channel = data[:channel] - m = TopicMessage.new(self, data[:source], data[:channel], data[:unixtime], data[:topic]) - ontopic(m) + @client[:topic] = @client[:topicinfo] = proc { |data| + m = TopicMessage.new(self, @server, data[:source], data[:channel], data[:channel].topic) + ontopic(m) } - @client[:names] = proc {|data| - channel = data[:channel] - users = data[:users] - unless(@channels[channel]) - warning "got names for channel '#{channel}' I didn't think I was in\n" - # exit 2 - end - @channels[channel].users.clear - users.each {|u| - @channels[channel].users[u[0].sub(/^[@&~+]/, '')] = ["mode", u[1]] - } + @client[:names] = proc { |data| @plugins.delegate "names", data[:channel], data[:users] } - @client[:unknown] = proc {|data| + @client[:unknown] = proc { |data| #debug "UNKNOWN: #{data[:serverstring]}" irclog data[:serverstring], ".unknown" } end + # checks if we should be quiet on a channel + def quiet_on?(channel) + return false unless @quiet + return true if @quiet.empty? + return @quiet.include?(channel.to_s) + end + + def set_quiet(channel=nil) + if channel + @quiet << channel.to_s unless @quiet.include?(channel.to_s) + else + @quiet = [] + end + end + + def reset_quiet(channel=nil) + if channel + @quiet.delete_if { |x| x == channel.to_s } + else + @quiet = nil + end + end + + # things to do when we receive a signal def got_sig(sig) debug "received #{sig}, queueing quit" $interrupted += 1 @@ -524,8 +531,7 @@ class IrcBot raise e.class, "failed to connect to IRC server at #{@config['server.name']} #{@config['server.port']}: " + e end @socket.emergency_puts "PASS " + @config['server.password'] if @config['server.password'] - @socket.emergency_puts "NICK #{@nick}\nUSER #{@config['irc.user']} 4 #{@config['server.name']} :Ruby bot. (c) Tom Gilbert" - @capabilities = Hash.new + @socket.emergency_puts "NICK #{@config['irc.nick']}\nUSER #{@config['irc.user']} 4 #{@config['server.name']} :Ruby bot. (c) Tom Gilbert" start_server_pings end @@ -573,7 +579,7 @@ class IrcBot end stop_server_pings - @channels.clear + @server.clear if @socket.connected? @socket.clearq @socket.shutdown @@ -601,7 +607,7 @@ class IrcBot # and all the extra stuff # TODO allow something to do for commands that produce too many messages # TODO example: math 10**10000 - left = @socket.bytes_per - type.length - where.length - 4 + left = @socket.bytes_per - type.length - where.to_s.length - 4 begin if(left >= message.length) sendq "#{type} #{where} :#{message}", chan, ring @@ -626,17 +632,18 @@ class IrcBot end # send a notice message to channel/nick +where+ - def notice(where, message, mchan=nil, mring=-1) + def notice(where, message, mchan="", mring=-1) if mchan == "" chan = where else chan = mchan end if mring < 0 - if where =~ /^#/ - ring = 2 - else + case where + when User ring = 1 + else + ring = 2 end else ring = mring @@ -656,10 +663,11 @@ class IrcBot chan = mchan end if mring < 0 - if where =~ /^#/ - ring = 2 - else + case where + when User ring = 1 + else + ring = 2 end else ring = mring @@ -667,7 +675,7 @@ class IrcBot message.to_s.gsub(/[\r\n]+/, "\n").each_line { |line| line.chomp! next unless(line.length > 0) - unless((where =~ /^#/) && (@channels.has_key?(where) && @channels[where].quiet)) + unless quiet_on?(where) sendmsg "PRIVMSG", where, line, chan, ring end } @@ -681,7 +689,8 @@ class IrcBot chan = mchan end if mring < 0 - if where =~ /^#/ + case where + when Channel ring = 2 else ring = 1 @@ -690,12 +699,13 @@ class IrcBot ring = mring end sendq "PRIVMSG #{where} :\001ACTION #{message}\001", chan, ring - if(where =~ /^#/) - irclog "* #{@nick} #{message}", where - elsif (where =~ /^(\S*)!.*$/) - irclog "* #{@nick}[#{where}] #{message}", $1 + case where + when Channel + irclog "* #{myself} #{message}", where + when User + irclog "* #{myself}[#{where}] #{message}", $1 else - irclog "* #{@nick}[#{where}] #{message}", where + irclog "* #{myself}[#{where}] #{message}", where end end @@ -709,7 +719,7 @@ class IrcBot def irclog(message, where="server") message = message.chomp stamp = Time.now.strftime("%Y/%m/%d %H:%M:%S") - where = where.gsub(/[:!?$*()\/\\<>|"']/, "_") + where = where.to_s.gsub(/[:!?$*()\/\\<>|"']/, "_") unless(@logs.has_key?(where)) @logs[where] = File.new("#{@botclass}/logs/#{where}", "a") @logs[where].sync = true @@ -746,8 +756,8 @@ class IrcBot @socket.shutdown end debug "Logging quits" - @channels.each_value {|v| - irclog "@ quit (#{message})", v.name + @server.channels.each { |ch| + irclog "@ quit (#{message})", ch } debug "Saving" save @@ -910,7 +920,7 @@ class IrcBot when "restart" return "restart => completely stop and restart the bot (including reconnect)" when "join" - return "join <channel> [<key>] => join channel <channel> with secret key <key> if specified. #{@nick} also responds to invites if you have the required access level" + return "join <channel> [<key>] => join channel <channel> with secret key <key> if specified. #{myself} also responds to invites if you have the required access level" when "part" return "part <channel> => part channel <channel>" when "hide" @@ -934,9 +944,9 @@ class IrcBot when "version" return "version => describes software version" when "botsnack" - return "botsnack => reward #{@nick} for being good" + return "botsnack => reward #{myself} for being good" when "hello" - return "hello|hi|hey|yo [#{@nick}] => greet the bot" + return "hello|hi|hey|yo [#{myself}] => greet the bot" else return "Core help topics: quit, restart, config, join, part, hide, save, rescan, nick, say, action, topic, quiet, talk, version, botsnack, hello" end @@ -1015,25 +1025,25 @@ class IrcBot when (/^quiet$/i) if(auth.allow?("talk", m.source, m.replyto)) m.okay - @channels.each_value {|c| c.quiet = true } + set_quiet end when (/^quiet in (\S+)$/i) where = $1 if(auth.allow?("talk", m.source, m.replyto)) m.okay where.gsub!(/^here$/, m.target) if m.public? - @channels[where].quiet = true if(@channels.has_key?(where)) + set_quiet(where) end when (/^talk$/i) if(auth.allow?("talk", m.source, m.replyto)) - @channels.each_value {|c| c.quiet = false } + reset_quiet m.okay end when (/^talk in (\S+)$/i) where = $1 if(auth.allow?("talk", m.source, m.replyto)) where.gsub!(/^here$/, m.target) if m.public? - @channels[where].quiet = false if(@channels.has_key?(where)) + reset_quiet(where) m.okay end when (/^status\??$/i) @@ -1059,9 +1069,9 @@ class IrcBot else # stuff to handle when not addressed case m.message - when (/^\s*(hello|howdy|hola|salut|bonjour|sup|niihau|hey|hi|yo(\W|$))[\s,-.]+#{Regexp.escape(@nick)}$/i) + when (/^\s*(hello|howdy|hola|salut|bonjour|sup|niihau|hey|hi|yo(\W|$))[\s,-.]+#{Regexp.escape(self.nick)}$/i) say m.replyto, @lang.get("hello_X") % m.sourcenick - when (/^#{Regexp.escape(@nick)}!*$/) + when (/^#{Regexp.escape(self.nick)}!*$/) say m.replyto, @lang.get("hello_X") % m.sourcenick else @keywords.privmsg(m) @@ -1073,17 +1083,19 @@ class IrcBot def log_sent(type, where, message) case type when "NOTICE" - if(where =~ /^#/) - irclog "-=#{@nick}=- #{message}", where - elsif (where =~ /(\S*)!.*/) + case where + when Channel + irclog "-=#{myself}=- #{message}", where + when User irclog "[-=#{where}=-] #{message}", $1 else - irclog "[-=#{where}=-] #{message}" + irclog "[-=#{where}=-] #{message}", where end when "PRIVMSG" - if(where =~ /^#/) - irclog "<#{@nick}> #{message}", where - elsif (where =~ /^(\S*)!.*$/) + case where + when Channel + irclog "<#{myself}> #{message}", where + when User irclog "[msg(#{where})] #{message}", $1 else irclog "[msg(#{where})] #{message}", where @@ -1092,14 +1104,11 @@ class IrcBot end def onjoin(m) - @channels[m.channel] = IRCChannel.new(m.channel) unless(@channels.has_key?(m.channel)) - if(m.address?) + if m.address? debug "joined channel #{m.channel}" irclog "@ Joined channel #{m.channel}", m.channel else irclog "@ #{m.sourcenick} joined channel #{m.channel}", m.channel - @channels[m.channel].users[m.sourcenick] = Hash.new - @channels[m.channel].users[m.sourcenick]["mode"] = "" end @plugins.delegate("listen", m) @@ -1110,15 +1119,8 @@ class IrcBot if(m.address?) debug "left channel #{m.channel}" irclog "@ Left channel #{m.channel} (#{m.message})", m.channel - @channels.delete(m.channel) else irclog "@ #{m.sourcenick} left channel #{m.channel} (#{m.message})", m.channel - if @channels.has_key?(m.channel) - @channels[m.channel].users.delete(m.sourcenick) - else - warning "got part for channel '#{channel}' I didn't think I was in\n" - # exit 2 - end end # delegate to plugins @@ -1130,10 +1132,8 @@ class IrcBot def onkick(m) if(m.address?) debug "kicked from channel #{m.channel}" - @channels.delete(m.channel) irclog "@ You have been kicked from #{m.channel} by #{m.sourcenick} (#{m.message})", m.channel else - @channels[m.channel].users.delete(m.sourcenick) irclog "@ #{m.target} has been kicked from #{m.channel} by #{m.sourcenick} (#{m.message})", m.channel end @@ -1142,12 +1142,7 @@ class IrcBot end def ontopic(m) - @channels[m.channel] = IRCChannel.new(m.channel) unless(@channels.has_key?(m.channel)) - @channels[m.channel].topic = m.topic if !m.topic.nil? - @channels[m.channel].topic.timestamp = m.timestamp if !m.timestamp.nil? - @channels[m.channel].topic.by = m.source if !m.source.nil? - - debug "topic of channel #{m.channel} is now #{@channels[m.channel].topic}" + debug "topic of channel #{m.channel} is now #{m.topic}" end # delegate a privmsg to auth, keyword or plugin handlers |