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
#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