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