All pastes #729175 Raw Edit

IRC Lib

public text v1 · immutable
·published 2007-10-07 18:49 UTC
rendered paste body
Class.rb
---------------------------------------------------------
class IRCBot
  # Constructor
  def initialize(connection_info)
    @settings = { 
      :nick =>      connection_info[:nick],
      :server =>    connection_info[:server],
      :owner =>     connection_info[:owner],
      :ident =>     connection_info[:ident],
      :real_name => connection_info[:real_name],
      :ping =>      connection_info[:ping],
      :output =>    connection_info[:output]
    }
    
    @channel = Struct.new("Channel", :topic, :topic_set, :topic_set_by, :modes, :users, :created)
    @user = Struct.new("User", :nick, :mode)
    @chans = {}
    @scripts = []
    @sock = nil
  end
  
  def start
    @sock = TCPSocket.open(@settings[:server].split(":")[0], @settings[:server].split(":")[1])
    @sock.puts("USER #{@settings[:ident]} debug debug :#{@settings[:real_name]}")
    @sock.puts("NICK #{@settings[:nick]}")
    
    if (@settings[:ping] == :thread) then
      start_ping_thread
    end
    
    main_loop
  end
  
  ######################## SCRIPT SYSTEM METHODS ######################
  
  def load_script(script)
    new = script.new
    name = new.script_info[:name]
    
    unless name
      puts "Missing script name! Aborting."
    else
      @scripts << new
      puts "Loaded '#{name}' :)"
    end
  end
  
  def unload_script(script)
    @scripts.each do |script_inst|
      if script_inst.class == script
        @scripts.delete(script_inst)
      end
    end
  end
  
  def reload_script(script)
    unload_script(script)
    load_script(script)
  end
  
  ########################### SCRIPT END ########################
  
  def is_master?(address)
    if (@settings[:owner].match(address)) then
      return true
    else 
      return false
    end
  end
  
  def has_op?(who, channel)
    if (@chans[channel].users[who].mode.include? "o") then 
      return true
    else    
      return false
    end
  end
  
  def has_voice?(who, channel)
    if (@chans[channel].users[who].mode.include? "v") then 
      return true
    else    
      return false
    end
  end
  
  # Receive and pcocess
  def main_loop
    while (buffer = @sock.gets.chomp)
      handle_input(buffer)
    end
  end
  
  def start_ping_thread
    puts "Using alternate ping..."
    
    Thread.new do
      loop do
        @Sock.puts("PING :#{@settings[:server].split(":")[0]}\r\n");
        sleep(60);
      end
    end
    
    puts "Thread running!"
  end
  
  # Strip address / ident
  def nick_from_source(source)
    if (source == nil) then
      return "SERVER"
    else
      return source.split("!")[0]
    end
  end
  
  def remove_user(nick, channel=nil)
    if (channel.nil?)
      @chans.each do |chan|
        @chans[chan[0]].users.each_key do |user|
          if (user == nick) then
            @chans[chan[0]].users.delete(nick)
          end
        end
      end
    else
      @chans[channel].users.each_key do |user|
        if (user == nick) then
          @chans[channel].users.delete(nick)
        end
      end
    end
  end
  
  # Send a raw string
  def sendline(what)
    @sock.puts(what)
  end
  
  def join_channel(channel)
    sendline("JOIN #{channel}")
  end
  
  def invite(who, to)
    sendline("INVITE #{who} #{to}")
  end
  
  def part_channel(channel, reason=nil)
    if (reason) then
      sendline("PART #{channel} :#{reason}")
    else
      sendline("PART #{channel}")
    end
  end
  
  def rejoin_channel(channel)
    part_channel(channel, "Rejoining...")
    join_channel(channel)
  end
  
  def say(target, what)
    sendline("PRIVMSG #{target} :#{what}")
  end
  
  def action(target, what)
    sendline("PRIVMSG #{target} :ACTION #{what}")
  end
  
  def kick(who, from, why=nil)
    if (why) then
      sendline("KICK #{from} #{who} :#{why}") 
    else
      sendline("KICK #{from} #{who} :#{who}") 
    end
  end
  
  def change_topic(where, what)
    sendline("TOPIC #{where} :#{what}")
  end
  
  def channel_mode(where, mode)
    sendline("MODE #{where} #{mode}")
  end
  
  def channel_user_mode(where, who, what)
    sendline("MODE #{where} #{mode} #{who}")
  end
  
  # Handling the raw input from the server
  def handle_input(input)
    split = /\A(?::([^ \0]+) )?([A-Za-z]+|\d{3})(?: (.*))?\z/
    parts = split.match(input)

    case (parts[2])
      when "PRIVMSG": 
        who = parts[1]
        where = parts[3].split(" ")[0]
        what = (parts[3].split(" ")[1..parts[3].length]).join(" "); what = what[1..what.length]
        
        if (where[0..0] == "#") then
          puts "(#{where}) <#{nick_from_source(who)}> #{what}" if @settings[:output]
                    
          @scripts.each do |script|
            script.on_channel_message(who, where, what)
          end
          
          on_channel_message(who, where, what)
        else
          puts "(QUERY) <#{nick_from_source(who)}> #{what}" if @settings[:output]
                    
          @scripts.each do |script|
            script.on_private_message(who, what)
          end
          
          on_private_message(who, what)
        end
      when "PING":
        returnstring = parts[3][1..parts[3].length]
        
        sendline("PONG :#{returnstring}")
        
        @scripts.each do |script|
          script.on_ping(returnstring)
        end
        
        on_ping(returnstring)
      when "376":
        on_connect()
      when "NOTICE"
        who = parts[1]
        what = (parts[3].split(" ")[1..parts[3].length]).join(" "); what = what[1..what.length]
        
        puts "-#{nick_from_source(who)}- #{what}" if @settings[:output]
        
        @scripts.each do |script|
          script.on_notice(who, what)
        end
          
        on_notice(who, what)
      when "JOIN"
        who = parts[1]
        where = parts[3]
        
        if (nick_from_source(who) == @settings[:nick]) then
          @chans[where] = @channel.new
          @chans[where].users = {}
          sendline("NAMES #{where}")
          sendline("MODE #{where}")
        else
          @chans[where].users[nick_from_source(who)] = @user.new
          @chans[where].users[nick_from_source(who)].nick = nick_from_source(who)
          @chans[where].users[nick_from_source(who)].mode = ''
          
          puts @chans.inspect
        end
        
        puts "#{nick_from_source(who)} has joined #{where}" if @settings[:output]
        
        @scripts.each do |script|
          script.on_join(who, where)
        end
        
        on_join(who, where)
      when /(\d\d\d)/
        numeric = $1
        params = parts[3]
        
        if ($1 == "353") then
          where = parts[3].split(" ")[2]
          list = parts[3].split(" ")[3..parts[3].split(" ").length].join(" "); list = list[1..list.length].split(" ")
          
          list.each do |user|
            if (user[0..0] == '@') or (user[0..0] == '+') then
              clean = user[1..user.length]
              prefix = user[0..0]
              
              pre_mode = prefix.sub(/@/, "o")
              pre_mode = pre_mode.sub(/\+/, "v")
              
              @chans[where].users[clean] = @user.new
              @chans[where].users[clean].nick = clean
              @chans[where].users[clean].mode = pre_mode
            else
              @chans[where].users[user] = @user.new
              @chans[where].users[user].nick = user
              @chans[where].users[user].mode = ''
            end
          end
          
          #puts @chans.inspect
        elsif ($1 == "332") then
          where = parts[3].split(" ")[1]
          topic = parts[3].split(" ")[2..parts[3].split(" ").length].join(" "); topic = topic[1..topic.length]
          
          @chans[where].topic = topic
        elsif ($1 == "333")
          where = parts[3].split(" ")[1]
          setter = parts[3].split(" ")[2]
          time = parts[3].split(" ")[3]
          
          @chans[where].topic_set_by = setter
          @chans[where].topic_set = time.to_i        
        elsif ($1 == "324")
          where = parts[3].split(" ")[1]
          modes = parts[3].split(" ")[2..parts[3].split(" ").length]
          
          @chans[where].modes = modes
        elsif ($1 == "329")
          where = parts[3].split(" ")[1]
          time = parts[3].split(" ")[2]
          
          @chans[where].created = time.to_i
        end
        
        @scripts.each do |script|
          script.on_numeric(numeric, params)
        end
        
        on_numeric(numeric, params)
      when "TOPIC"
        who = parts[1]
        where = parts[3].split(" ")[0]
        what = (parts[3].split(" ")[1..parts[3].length]).join(" "); what = what[1..what.length]
        
        sendline("TOPIC #{where}")
        
        puts "#{nick_from_source(who)} changed #{where} Topic to: #{what}" if @settings[:output]
        
        @scripts.each do |script|
          script.on_topic(who, where, what)
        end
        
        on_topic(who, where, what)
      when "KICK"
        who = parts[1]
        where = parts[3].split(" ")[0]
        target = parts[3].split(" ")[1]
        why = (parts[3].split(" ")[2..parts[3].split(" ").length]).join(" "); why = why[1..why.length]
        
        remove_user(target, where)
        
        puts "#{nick_from_source(who)} kicked #{target} from #{where} (#{why})" if @settings[:output]
        
        @scripts.each do |script|
          script.on_kick(who, where, target, why)
        end
        
        on_kick(who, where, target, why)
      when "NICK"
        old = parts[1]
        new = parts[3]
        
        @chans.each do |channel|
          @chans[channel[0]].users.each_key do |user|
            if (user == nick_from_source(old)) then
              @chans[channel[0]].users[new] = @chans[channel[0]].users[nick_from_source(old)]
              @chans[channel[0]].users[new].nick = new
              @chans[channel[0]].users.delete(nick_from_source(old))
            end
          end
        end
        
        puts "#{nick_from_source(old)} is now known as #{new}" if @settings[:output]
        
        @scripts.each do |script|
          script.on_nick(old, new)
        end
        
        on_nick(old, new)
      when "INVITE"
        who = parts[1]
        target = parts[3].split(" ")[0]
        to = parts[3].split(" ")[1]
        
        puts "#{nick_from_source(who)} invites me to join #{to}" if @settings[:output]
        
        @scripts.each do |script|
          script.on_invite(who, to)
        end
        
        on_invite(who, to)
      when "PART"
        who = parts[1]
        where = parts[3].split(" ")[0]
        why = (parts[3].split(" ")[1..parts[3].split(" ").length]).join(" "); why = why[1..why.length]

        remove_user(nick_from_source(who), where)

        puts "#{nick_from_source(who)} has left #{where} (#{why})" if @settings[:output]
        
        @scripts.each do |script|
          script.on_part(who, where, why)
        end
        
        on_part(who, where, why)
      when "QUIT"
        who = parts[1]
        why = (parts[3].split(" ")[0..parts[3].split(" ").length]).join(" "); why = why[1..why.length]
        
        remove_user(nick_from_source(who))
        
        puts "#{nick_from_source(who)} has Quit IRC (#{why})" if @settings[:output]
        
        @scripts.each do |script|
          script.on_quit(who, why)
        end
        
        on_quit(who, why)
      when "MODE"
        who = parts[1]
        where = parts[3].split(" ")[0]
        mode = parts[3].split(" ")[1]
        target = (parts[3].split(" ")[2..parts[3].split(" ").length]).join(" ")
        
        if (target == "") or (mode =~ /k|l|b/) then
          sendline("MODE #{where}")
          
          if (target == "") then
            puts "#{nick_from_source(who)} sets mode #{mode} #{where}" if @settings[:output]
            
            @scripts.each do |script|
              script.on_channel_mode(who, where, mode, nil)
            end
            
            on_channel_mode(who, where, mode, nil)       
          else
            puts "#{nick_from_source(who)} sets mode #{mode} #{target} #{where}" if @settings[:output]
            
            @scripts.each do |script|
              script.on_channel_mode(who, where, mode, target)
            end
            
            on_channel_mode(who, where, mode, target)
          end
        else
          modes = mode.split("")
          targets = target.split(" ")
          cur = :null
          targets.length.times do |i|
            if (modes[i] == '+') then cur = :give; modes.delete_at(i); end
            if (modes[i] == '-') then cur = :take; modes.delete_at(i); end
            
            
            if (cur == :give) then 
              @chans[where].users[targets[i]].mode += modes[i]
            else
              @chans[where].users[targets[i]].mode = @chans[where].users[targets[i]].mode.delete(modes[i])
            end
          end
          
          puts "#{nick_from_source(who)} sets mode #{mode} #{target} on #{where}" if @settings[:output]
          
          @scripts.each do |script|
            script.on_channel_user_mode(who, where, mode, target)
          end
          
          on_channel_user_mode(who, where, mode, target)
        end
    end
  end
  
  def method_missing(method_name, *args)
    #puts "Missing method '#{method_name}'!"
  end

  protected :handle_input
  attr_accessor :settings, :chans, :sock
end


class Script < IRCBot
  def initialize()
    script_loaded
  end
  
  def method_missing(name, *args)
    
  end
end

--------- Bot.rb
------------------------------
require "Class"

class MyBot < IRCBot
  def on_connect
    puts "Logged in!"
    join_channel("#111")
  end
  
  def on_channel_message(who, where, what)
    case what
      when "!test"
        if has_op?(nick_from_source(who), where) then
          say(where, "True!")
        else
          action(where, "shakes head...")
        end
    end
  end
end

class Gamble < Script
  def script_info
    { :name => "Gamble Script" }
    puts 
  end
  
  def on_channel_message(who, where, what)
    if what == "!gamble"
      sendline("test")
    end
  end
  
  def script_loaded
    puts "Gamble script"
  end
end

settings = {
  :nick => "SomeFunnyBot",
  :server => "irc.quakenet.org:6667",
  :owner => /Flippeh.users.quakenet.org/,
  :ident => "ngbot",
  :real_name => "Something",
  :ping => :thread,
  :output => false
}

bot = MyBot.new(settings)
bot.load_script(Gamble)
bot.start


-------- Error:
./Class.rb:136:in `sendline': private method `puts' called for nil:NilClass (NoMethodError)
	from NewIRC.rb:28:in `on_channel_message'
	from ./Class.rb:203:in `handle_input'
	from ./Class.rb:202:in `each'
	from ./Class.rb:202:in `handle_input'
	from ./Class.rb:90:in `main_loop'
	from ./Class.rb:31:in `start'
	from NewIRC.rb:49