Commit a57fcb549d9929abc93cc510337424e50fc407fd

Renamed IRCXy.rb to xy.rb and moved to lib/irc, added an example
  
1#!/usr/bin/env ruby
2
3$: << File.join(File.dirname(__FILE__), "../lib/")
4
5require 'irc/bot'
6
7class MyBot < IRC::Bot
8 def initialize(nick, server, port, name)
9 super
10 end
11
12 def on_endofmotd(event)
13 join("#channel1", "#channel2")
14 end
15
16 def on_message(event)
17 puts "<#{event.nick}> says '#{event.message}' in #{event.channel}"
18 end
19
20 def on_action(event)
21 puts "*** '#{event.nick} #{event.message}' in #{event.channel}"
22 end
23
24 def on_join(event)
25 puts "%%%% #{event.nick} join to #{event.channel}"
26 end
27
28 def do_join(event, args)
29 args.each { |channel|
30 join(channel)
31 }
32 end
33
34 def do_say(event, args)
35 send_message(event.channel, args.join(" "))
36 end
37
38 def do_me(event, args)
39 send_message(event.channel, "I'm too lame!")
40 end
41end
42
43NICK = "ruby-irc"
44SERVER = "localhost"
45PORT = "6667"
46IRC_NAME = "My Bot"
47
48bot = MyBot.new(NICK, SERVER, PORT, IRC_NAME)
49bot.connect
  
1
2require 'socket'
3require 'IRCConnection'
4require 'IRCEvent'
5require 'IRCChannel'
6require 'IRCUser'
7require 'IRCUtil'
8
9# Class IRC is a master class that handles connection to the irc
10# server and pasring of IRC events, through the IRCEvent class.
11class IRC
12 attr_reader :nick, :server, :port
13
14 @channels = nil
15 # Create a new IRC Object instance
16 def initialize( nick, server, port, realname='RBot')
17 @nick = nick
18 @server = server
19 @port = port
20 @realname = realname
21 @channels = Array.new(0)
22 # Some good default Event handlers. These can and will be overridden by users.
23 # Thses make changes on the IRCbot object. So they need to be here.
24
25 # Topic events can come on two tags, so we create on proc to handle them.
26
27 topic_proc = Proc.new { |event|
28 self.channels.each { |chan|
29 if chan == event.channel
30 chan.topic = event.message
31 end
32 }
33 }
34
35 IRCEvent.add_handler('332', topic_proc)
36 IRCEvent.add_handler('topic', topic_proc)
37
38 unhandle_proc = Proc.new { |event|
39 method_id = "on_#{event.type}".to_sym
40 if respond_to?(method_id)
41 __send__(method_id, event)
42 elsif $DEBUG
43 $stderr.puts "=> Undefined method #{method_id}"
44 end
45 }
46
47 IRCEvent.add_handler("unhandled", unhandle_proc)
48 end
49
50 # Join a channel, adding it to the list of joined channels
51 def add_channel channel
52 join(channel)
53 self
54 end
55
56 # Returns a list of channels joined
57 def channels
58 @channels
59 end
60
61 # Open a connection to the server using the IRC Connect
62 # method. Events yielded from the IRCConnection handler are
63 # processed and then control is returned to IRCConnection
64 def connect
65 quithandler = lambda { send_quit(); IRCConnection.quit; exit 0 }
66 trap("INT", quithandler)
67 trap("TERM", quithandler)
68
69 IRCConnection.handle_connection(@server, @port, @nick, @realname) do
70 # Log in information moved to IRCConnection
71 @threads = []
72 IRCConnection.main do |event|
73 if event.kind_of?(Array)
74 event.each {|event|
75 thread_event(event)
76 }
77 else
78 thread_event(event)
79 end
80 end
81 @threads.each {|thr| thr.join }
82 end
83 end
84 alias start connect
85
86 # Joins a channel on a server.
87 def join(*channels)
88 channels.each { |channel|
89 if (IRCConnection.send_to_server("JOIN #{channel}"))
90 @channels.push(IRCChannel.new(channel));
91 end
92 }
93 end
94
95 # Leaves a channel on a server
96 def part(channel)
97 if (IRCConnection.send_to_server("PART #{channel}"))
98 @channels.delete_if {|chan| chan.name == channel }
99 end
100 end
101
102 # kicks a user from a channel (does not check for operator privledge)
103 def kick(channel, user, message)
104 IRCConnection.send_to_server("KICK #{channel} #{user} :#{message || user || 'kicked'}")
105 end
106
107 # sets the topic of the given channel
108 def set_topic(channel, topic)
109 IRCConnection.send_to_server("TOPIC #{channel} :#{topic}");
110 end
111
112 # Sends a private message, or channel message
113 def send_message(to, message)
114 IRCConnection.send_to_server("privmsg #{to} :#{message}");
115 end
116
117 # Sends a notice
118 def send_notice(to, message)
119 IRCConnection.send_to_server("NOTICE #{to} :#{message}");
120 end
121
122 # performs an action
123 def send_action(to, action)
124 send_ctcp(to, 'ACTION', action);
125 end
126
127 # send CTCP
128 def send_ctcp(to, type, message)
129 IRCConnection.send_to_server("privmsg #{to} :\001#{type} #{message}");
130 end
131
132 # Quits the IRC Server
133 def send_quit
134 IRCConnection.send_to_server("QUIT : Quit ordered by user")
135 end
136
137 # Ops selected user.
138 def op(channel, user)
139 IRCConnection.send_to_server("MODE #{channel} +o #{user}")
140 end
141
142 # Changes the current nickname
143 def ch_nick(nick)
144 IRCConnection.send_to_server("NICK #{nick}")
145 @nick = nick
146 end
147
148 # Removes operator status from a user
149 def deop(channel, user)
150 IRCConnection.send_to_server("MODE #{channel} -o #{user}")
151 end
152
153 # Changes target users mode
154 def mode(channel, user, mode)
155 IRCConnection.send_to_server("MODE #{channel} #{mode} #{user}")
156 end
157
158 # Retrievs user information from the server
159 def get_user_info(user)
160 IRCConnection.send_to_server("WHO #{user}")
161 end
162 private
163 def thread_event(event)
164 @threads << Thread.new(event) { |localevent|
165 begin
166 localevent.process
167 rescue => e
168 puts "Error: #{e.message}"
169 puts e.backtrace.map { |e| " from #{e}\n" }
170 exit -1
171 end
172 }
173 end
174end
  
1
2require 'IRC'
3
4class IRCBot < IRC
5 attr_accessor :operator
6 def initialize(nick, server, port, realname='RBot')
7 super
8
9 @operator = "%"
10 end
11
12 def on_privmsg(event)
13 if event.message =~ /^#{@operator}(\w+)(\s|.+)*/
14 command = $1
15 args = $2.to_s.split(/\s+/)
16 method = "do_#{command}".to_sym
17
18 if respond_to?(method)
19 if respond_to?(method)
20 __send__(method, event, args)
21 end
22 else
23 $stderr.puts "Invalid action: #{command} from #{event.from} in #{event.channel}"
24 end
25 end
26 end
27end
  
1require "IRCUser"
2
3# Represents an IRC Channel
4class IRCChannel
5 def initialize(name)
6 @name = name
7 @users = Array.new(0)
8 end
9 attr_reader :name
10
11 # set the topic on this channel
12 def topic=(topic)
13 @topic = topic
14 end
15
16 # get the topic on this channel
17 def topic
18 if @topic
19 return @topic
20 end
21 return "No Topic set"
22 end
23
24 # add a user to this channel's userlist
25 def add_user(username)
26 @users.push(IRCUser.create_user(username))
27 end
28
29 # returns the current user list for this channel
30 def users
31 @users
32 end
33end
  
1
2# Handles connection to IRC Server
3class IRCConnection
4 @@quit = 0
5 @@readsockets = Array.new(0)
6 @@output_buffer = Array.new(0)
7 @@events = Hash.new()
8 @@last_send = Time.now.to_f
9 @@message_delay = 0.2 # Default delay to 1 fifth of a second.
10 # Creates a socket connection and then yields.
11 def IRCConnection.handle_connection(server, port, nick='ChangeMe', realname='MeToo' )
12 @server = server;
13 @port = port
14 @nick = nick
15 @realname = realname
16 socket = create_tcp_socket(server, port)
17 add_IO_socket(socket) {|sock|
18 begin
19 IRCEvent.new(sock.readline.chomp)
20 rescue Errno::ECONNRESET
21 # Catches connection reset by peer, attempts to reconnect
22 # after sleeping for 10 second.
23 remove_IO_socket(sock)
24 sleep 10
25 handle_connection(@server, @port, @nick, @realname)
26 end
27 }
28 send_to_server "NICK #{nick}"
29 send_to_server "USER #{nick} 8 * :#{realname}"
30 if block_given?
31 yield
32 @@socket.close
33 end
34 end
35
36 def IRCConnection.create_tcp_socket(server, port)
37 @@socket = TCPsocket.open(server, port)
38 if block_given?
39 yield
40 @@socket.close
41 return
42 end
43 return @@socket
44 end
45
46 # Sends a line of text to the server
47 def IRCConnection.send_to_server(line)
48 @@socket.write(line + "\n")
49 end
50
51 # Adds data an output buffer. This let's us keep a handle on how
52 # fast we send things. Yay.
53 def IRCConnection.output_push(line)
54 @@output_buffer.push(line)
55 end
56
57 # This loop monitors all IO_Sockets IRCConnection controls
58 # (including the IRC socket) and yields events to the IO_Sockets
59 # event handler.
60 def IRCConnection.main
61 while(@@quit == 0)
62 do_one_loop { |event|
63 yield event
64 }
65 end
66 end
67
68 # Makes one single loop pass, checking all sockets for data to read,
69 # and yields the data to the sockets event handler.
70 def IRCConnection.do_one_loop
71 read_sockets = select(@@readsockets, nil, nil, 0.1);
72 if !read_sockets.nil?
73 read_sockets[0].each {|sock|
74 if sock.eof? && sock == @@socket
75 p "Detected Socket Close"
76 remove_IO_socket(sock)
77 sleep 10
78 handle_connection(@server, @port, @nick, @realname)
79 else
80 yield @@events[sock.to_i].call(sock)
81 end
82 }
83 end
84 if @@output_buffer.length > 0
85 timer = Time.now.to_f
86 if (timer > @@last_send + @@message_delay)
87 message = @@output_buffer.shift();
88 if !message.nil?
89 IRCConnection.send_to_server(message);
90 @@last_send = timer
91 end
92 end
93 end
94 end
95
96 # Ends connection to the irc server
97 def IRCConnection.quit
98 @@quit = 1
99 end
100 def IRCConnection.delay=(delay)
101 @@message_delay = delay.to_f
102 end
103 # Retrieves user info from the server
104 def IRCConnection.get_user_info(user)
105 IRCConnection.send_to_server("WHOIS #{user}")
106 end
107
108 # Adds a new socket to the list of sockets to monitor for new data.
109 def IRCConnection.add_IO_socket(socket, &event_generator)
110 @@readsockets.push(socket)
111 @@events[socket.to_i] = event_generator
112 end
113
114 def IRCConnection.remove_IO_socket(sock)
115 sock.close
116 @@readsockets.delete_if {|item| item == sock }
117 end
118end
  
1require 'yaml'
2
3# This is a lookup class for IRC event name mapping
4class EventLookup
5 @@lookup = YAML.load_file("#{File.dirname(__FILE__)}/eventmap.yml")
6
7 # returns the event name, given a number
8 def EventLookup::find_by_number(num)
9 return @@lookup[num.to_i]
10 end
11end
12
13
14# Handles an IRC generated event.
15# Handlers are for the IRC framework to use
16# Callbacks are for users to add.
17# Both handlers and callbacks can be called for the same event.
18class IRCEvent
19 @@handlers = { 'ping' => lambda {|event| IRCConnection.send_to_server("PONG #{event.message}") } }
20 @@callbacks = Hash.new()
21 attr_reader :hostmask, :message, :type, :from, :channel, :target, :mode, :stats, :nick, :ident
22 def initialize (line)
23 puts "FROM SERVER: #{line}" if $DEBUG
24
25 line.sub!(/^:/, '')
26 mess_parts = line.split(':', 2);
27 # mess_parts[0] is server info
28 # mess_parts[1] is the message that was sent
29 @message = (mess_parts[1] ? mess_parts[1] : "" )
30 @from = ""
31 @channel = ""
32
33 @stats = mess_parts[0].split(" ")
34
35 puts @stats.join(" | ") if $DEBUG
36
37 if @stats[0].match(/^PING/)
38 @type = 'ping'
39 elsif @message.match(/^(\x1(\w+))/) # ctcp
40 @from = @stats[0]
41 ctcp = $2.downcase
42 @type = "ctcp_#{ctcp}"
43
44 @message.gsub!($1, "")
45 elsif @stats[1] && @stats[1].match(/^\d+/)
46 @type = EventLookup::find_by_number(@stats[1]);
47 @channel = @stats[2]
48 else
49 @type = @stats[1].downcase if @stats[1]
50 end
51
52 if @type != 'ping'
53 @from = @stats[0]
54 @user = IRCUser.create_user(@from)
55 end
56
57 @hostmask = @from.split("@").last
58 @nick = @from.split("!").first
59 @ident = ""
60
61 if @from =~ /!(.+)@/
62 @ident = $1
63 end
64
65 @channel = @stats[2] if @stats[2] and @channel.empty?
66 @target = @stats[3] if @stats[3]
67 @mode = @stats[4] if @stats[4]
68
69 # Unfortunatly, not all messages are created equal. This is our
70 # special exceptions section
71 if @type == 'join'
72 @channel = @message
73 end
74
75 puts "EVENT: #{@type}" if $DEBUG
76
77 end
78
79 # Adds a callback for the specified irc message.
80 def IRCEvent.add_callback(message_id, &callback)
81 @@callbacks[message_id] = callback
82 end
83
84 # Adds a handler to the handler function hash.
85 def IRCEvent.add_handler(message_id, proc=nil, &handler)
86 if block_given?
87 @@handlers[message_id] = handler
88 elsif proc
89 @@handlers[message_id] = proc
90 end
91 end
92
93 # Process this event, preforming which ever handler and callback is specified
94 # for this event.
95 def process
96 handled = false
97 if @@handlers[@type]
98 @@handlers[@type].call(self)
99 handled = true
100 end
101
102 if @@callbacks[@type]
103 @@callbacks[@type].call(self)
104 handled = true
105 end
106
107 if not handled
108 if @@handlers["unhandled"]
109 @@handlers["unhandled"].call(self)
110 else
111 $stderr.puts "No handler for event type #@type in #{self.class}" if $DEBUG
112 end
113 end
114 end
115end
  
1# Represents IRC Users
2class IRCUser
3 @@users = Hash.new()
4 @modes = Hash.new()
5
6 def IRCUser.create_user(username)
7 username.sub!(/^[\@\%]/,'')
8
9 if @@users[username]
10 return @@users[username]
11 end
12 @@users[username] = self.new(username)
13 @@users[username]
14 end
15
16 attr_reader :username, :mask
17 attr_writer :mask
18
19 private
20 def initialize (username)
21 @username = username
22 end
23end
  
1#
2# IRCUtil is a module that contains utility functions for use with the
3# rest of Ruby-IRC. There is nothing required of the user to know or
4# even use these functions, but they are useful for certain tasks
5# regarding IRC connections.
6#
7
8module IRCUtil
9 #
10 # Matches hostmasks against hosts. Returns t/f on success/fail.
11 #
12 # A hostmask consists of a simple wildcard that describes a
13 # host or class of hosts.
14 #
15 # f.e., where the host is 'bar.example.com', a host mask
16 # of '*.example.com' would assert.
17 #
18
19 def assert_hostmask(host, hostmask)
20 return !!host.match(quote_regexp_for_mask(hostmask))
21 end
22
23 module_function :assert_hostmask
24
25 #
26 # A utility function used by assert_hostmask() to turn hostmasks
27 # into regular expressions.
28 #
29 # Rarely, if ever, should be used by outside code. It's public
30 # exposure is merely for those who are interested in it's
31 # functionality.
32 #
33
34 def quote_regexp_for_mask(hostmask)
35 # Big thanks to Jesse Williamson for his consultation while writing this.
36 #
37 # escape all other regexp specials except for . and *.
38 # properly escape . and place an unescaped . before *.
39 # confine the regexp to scan the whole line.
40 # return the edited hostmask as a string.
41 hostmask.gsub(/([\[\]\(\)\?\^\$])\\/, '\\1').
42 gsub(/\./, '\.').
43 gsub(/\*/, '.*').
44 sub(/^/, '^').
45 sub(/$/, '$')
46 end
47
48 module_function :quote_regexp_for_mask
49end
  
1
2require 'irc/botbase'
3
4module IRC
5
6class Bot < BotBase
7 attr_accessor :operator
8 def initialize(nick, server, port, realname='RBot')
9 super
10
11 @operator = "%"
12 end
13
14 def on_privmsg(event)
15 if event.message =~ /^#{@operator}(\w+)(\s|.+)*/
16 command = $1
17 args = $2.to_s.split(/\s+/)
18 method = "do_#{command}".to_sym
19
20 if respond_to?(method)
21 if respond_to?(method)
22 __send__(method, event, args)
23 return true
24 end
25 else
26 $stderr.puts "Invalid action: #{command} from #{event.from} in #{event.channel}"
27 end
28 end
29
30 on_message(event) if respond_to?(:on_message)
31 end
32end
33
34end
  
1
2require 'socket'
3require 'irc/connection'
4require 'irc/event'
5require 'irc/channel'
6require 'irc/user'
7require 'irc/util'
8
9module IRC
10
11# Class IRC is a master class that handles connection to the irc
12# server and pasring of IRC events, through the IRC::Event class.
13class BotBase
14 attr_reader :nick, :server, :port
15
16 @channels = nil
17 # Create a new IRC Object instance
18 def initialize( nick, server, port, realname='RBot')
19 @nick = nick
20 @server = server
21 @port = port
22 @realname = realname
23 @channels = Array.new(0)
24 # Some good default Event handlers. These can and will be overridden by users.
25 # Thses make changes on the IRCbot object. So they need to be here.
26
27 # Topic events can come on two tags, so we create on proc to handle them.
28
29 topic_proc = Proc.new { |event|
30 self.channels.each { |chan|
31 if chan == event.channel
32 chan.topic = event.message
33 end
34 }
35 }
36
37 IRC::Event.add_handler('332', topic_proc)
38 IRC::Event.add_handler('topic', topic_proc)
39
40 unhandle_proc = Proc.new { |event|
41 method_id = "on_#{event.type}".to_sym
42 if respond_to?(method_id)
43 __send__(method_id, event)
44 elsif $DEBUG
45 $stderr.puts "=> Undefined method #{method_id}"
46 end
47 }
48
49 IRC::Event.add_handler("unhandled", unhandle_proc)
50 end
51
52 # Join a channel, adding it to the list of joined channels
53 def add_channel channel
54 join(channel)
55 self
56 end
57
58 # Returns a list of channels joined
59 def channels
60 @channels
61 end
62
63 # Open a connection to the server using the IRC Connect
64 # method. Events yielded from the IRC::Connection handler are
65 # processed and then control is returned to IRC::Connection
66 def connect
67 quithandler = lambda { send_quit(); IRC::Connection.quit; exit 0 }
68 trap("INT", quithandler)
69 trap("TERM", quithandler)
70
71 IRC::Connection.handle_connection(@server, @port, @nick, @realname) do
72 # Log in information moved to IRC::Connection
73 @threads = []
74 IRC::Connection.main do |event|
75 if event.kind_of?(Array)
76 event.each {|event|
77 thread_event(event)
78 }
79 else
80 thread_event(event)
81 end
82 end
83 @threads.each {|thr| thr.join }
84 end
85 end
86 alias start connect
87
88 # Joins a channel on a server.
89 def join(*channels)
90 channels.each { |channel|
91 if (IRC::Connection.send_to_server("JOIN #{channel}"))
92 @channels.push(IRC::Channel.new(channel));
93 end
94 }
95 end
96
97 # Leaves a channel on a server
98 def part(channel)
99 if (IRC::Connection.send_to_server("PART #{channel}"))
100 @channels.delete_if {|chan| chan.name == channel }
101 end
102 end
103
104 # kicks a user from a channel (does not check for operator privledge)
105 def kick(channel, user, message)
106 IRC::Connection.send_to_server("KICK #{channel} #{user} :#{message || user || 'kicked'}")
107 end
108
109 # sets the topic of the given channel
110 def set_topic(channel, topic)
111 IRC::Connection.send_to_server("TOPIC #{channel} :#{topic}");
112 end
113
114 # Sends a private message, or channel message
115 def send_message(to, message)
116 IRC::Connection.send_to_server("privmsg #{to} :#{message}");
117 end
118
119 # Sends a notice
120 def send_notice(to, message)
121 IRC::Connection.send_to_server("NOTICE #{to} :#{message}");
122 end
123
124 # performs an action
125 def send_action(to, action)
126 send_ctcp(to, 'ACTION', action);
127 end
128
129 # send CTCP
130 def send_ctcp(to, type, message)
131 IRC::Connection.send_to_server("privmsg #{to} :\001#{type} #{message}");
132 end
133
134 # Quits the IRC Server
135 def send_quit
136 IRC::Connection.send_to_server("QUIT : Quit ordered by user")
137 end
138
139 # Ops selected user.
140 def op(channel, user)
141 IRC::Connection.send_to_server("MODE #{channel} +o #{user}")
142 end
143
144 # Changes the current nickname
145 def ch_nick(nick)
146 IRC::Connection.send_to_server("NICK #{nick}")
147 @nick = nick
148 end
149
150 # Removes operator status from a user
151 def deop(channel, user)
152 IRC::Connection.send_to_server("MODE #{channel} -o #{user}")
153 end
154
155 # Changes target users mode
156 def mode(channel, user, mode)
157 IRC::Connection.send_to_server("MODE #{channel} #{mode} #{user}")
158 end
159
160 # Retrievs user information from the server
161 def get_user_info(user)
162 IRC::Connection.send_to_server("WHO #{user}")
163 end
164 private
165 def thread_event(event)
166 @threads << Thread.new(event) { |localevent|
167 begin
168 localevent.process
169 rescue => e
170 puts "Error: #{e.message}"
171 puts e.backtrace.map { |e| " from #{e}\n" }
172 exit -1
173 end
174 }
175 end
176end
177
178end
  
1require "irc/user"
2
3module IRC
4
5# Represents an IRC Channel
6class Channel
7 def initialize(name)
8 @name = name
9 @users = Array.new(0)
10 end
11 attr_reader :name
12
13 # set the topic on this channel
14 def topic=(topic)
15 @topic = topic
16 end
17
18 # get the topic on this channel
19 def topic
20 if @topic
21 return @topic
22 end
23 return "No Topic set"
24 end
25
26 # add a user to this channel's userlist
27 def add_user(username)
28 @users.push(IRC::User.create_user(username))
29 end
30
31 # returns the current user list for this channel
32 def users
33 @users
34 end
35end
36
37
38end
  
1
2
3
4module IRC
5
6# Handles connection to IRC Server
7class Connection
8 @@quit = 0
9 @@readsockets = Array.new(0)
10 @@output_buffer = Array.new(0)
11 @@events = Hash.new()
12 @@last_send = Time.now.to_f
13 @@message_delay = 0.2 # Default delay to 1 fifth of a second.
14 # Creates a socket connection and then yields.
15 def self.handle_connection(server, port, nick='ChangeMe', realname='MeToo' )
16 @server = server;
17 @port = port
18 @nick = nick
19 @realname = realname
20 socket = create_tcp_socket(server, port)
21 add_IO_socket(socket) {|sock|
22 begin
23 IRC::Event.new(sock.readline.chomp)
24 rescue Errno::ECONNRESET
25 # Catches connection reset by peer, attempts to reconnect
26 # after sleeping for 10 second.
27 remove_IO_socket(sock)
28 sleep 10
29 handle_connection(@server, @port, @nick, @realname)
30 end
31 }
32 send_to_server "NICK #{nick}"
33 send_to_server "USER #{nick} 8 * :#{realname}"
34 if block_given?
35 yield
36 @@socket.close
37 end
38 end
39
40 def self.create_tcp_socket(server, port)
41 @@socket = TCPsocket.open(server, port)
42 if block_given?
43 yield
44 @@socket.close
45 return
46 end
47 return @@socket
48 end
49
50 # Sends a line of text to the server
51 def self.send_to_server(line)
52 @@socket.write(line + "\n")
53 end
54
55 # Adds data an output buffer. This let's us keep a handle on how
56 # fast we send things. Yay.
57 def self.output_push(line)
58 @@output_buffer.push(line)
59 end
60
61 # This loop monitors all IO_Sockets self controls
62 # (including the IRC socket) and yields events to the IO_Sockets
63 # event handler.
64 def self.main
65 while(@@quit == 0)
66 do_one_loop { |event|
67 yield event
68 }
69 end
70 end
71
72 # Makes one single loop pass, checking all sockets for data to read,
73 # and yields the data to the sockets event handler.
74 def self.do_one_loop
75 read_sockets = select(@@readsockets, nil, nil, 0.1);
76 if !read_sockets.nil?
77 read_sockets[0].each {|sock|
78 if sock.eof? && sock == @@socket
79 p "Detected Socket Close"
80 remove_IO_socket(sock)
81 sleep 10
82 handle_connection(@server, @port, @nick, @realname)
83 else
84 yield @@events[sock.to_i].call(sock)
85 end
86 }
87 end
88 if @@output_buffer.length > 0
89 timer = Time.now.to_f
90 if (timer > @@last_send + @@message_delay)
91 message = @@output_buffer.shift();
92 if !message.nil?
93 self.send_to_server(message);
94 @@last_send = timer
95 end
96 end
97 end
98 end
99
100 # Ends connection to the irc server
101 def self.quit
102 @@quit = 1
103 end
104 def self.delay=(delay)
105 @@message_delay = delay.to_f
106 end
107 # Retrieves user info from the server
108 def self.get_user_info(user)
109 self.send_to_server("WHOIS #{user}")
110 end
111
112 # Adds a new socket to the list of sockets to monitor for new data.
113 def self.add_IO_socket(socket, &event_generator)
114 @@readsockets.push(socket)
115 @@events[socket.to_i] = event_generator
116 end
117
118 def self.remove_IO_socket(sock)
119 sock.close
120 @@readsockets.delete_if {|item| item == sock }
121 end
122end
123
124end
  
1require 'yaml'
2
3module IRC
4
5# This is a lookup class for IRC event name mapping
6class EventLookup
7 @@lookup = YAML.load_file("#{File.dirname(__FILE__)}/eventmap.yml")
8
9 # returns the event name, given a number
10 def EventLookup::find_by_number(num)
11 return @@lookup[num.to_i]
12 end
13end
14
15
16# Handles an IRC generated event.
17# Handlers are for the IRC framework to use
18# Callbacks are for users to add.
19# Both handlers and callbacks can be called for the same event.
20class Event
21 @@handlers = { 'ping' => lambda {|event| IRC::Connection.send_to_server("PONG #{event.message}") } }
22 @@callbacks = Hash.new()
23 attr_reader :hostmask, :message, :type, :from, :channel, :target, :mode, :stats, :nick, :ident
24 def initialize (line)
25 puts "FROM SERVER: #{line}" if $DEBUG
26
27 line.sub!(/^:/, '')
28 mess_parts = line.split(':', 2);
29 # mess_parts[0] is server info
30 # mess_parts[1] is the message that was sent
31 @message = (mess_parts[1] ? mess_parts[1] : "" )
32 @from = ""
33 @channel = ""
34
35 @stats = mess_parts[0].split(" ")
36
37 puts @stats.join(" | ") if $DEBUG
38
39 if @stats[0].match(/^PING/)
40 @type = 'ping'
41 elsif @message.match(/^(\x1(\w+))/) # ctcp
42 @from = @stats[0]
43 ctcp = $2.downcase
44 @type = "ctcp_#{ctcp}"
45
46 @message.gsub!($1, "")
47 elsif @stats[1] && @stats[1].match(/^\d+/)
48 @type = EventLookup::find_by_number(@stats[1]);
49 @channel = @stats[2]
50 else
51 @type = @stats[1].downcase if @stats[1]
52 end
53
54 if @type != 'ping'
55 @from = @stats[0]
56 @user = IRC::User.create_user(@from)
57 end
58
59 @hostmask = @from.split("@").last
60 @nick = @from.split("!").first
61 @ident = ""
62
63 if @from =~ /!(.+)@/
64 @ident = $1
65 end
66
67 @channel = @stats[2] if @stats[2] and @channel.empty?
68 @target = @stats[3] if @stats[3]
69 @mode = @stats[4] if @stats[4]
70
71 # Unfortunatly, not all messages are created equal. This is our
72 # special exceptions section
73 if @type == 'join'
74 @channel = @message
75 end
76
77 puts "EVENT: #{@type}" if $DEBUG
78
79 end
80
81 # Adds a callback for the specified irc message.
82 def self.add_callback(message_id, &callback)
83 @@callbacks[message_id] = callback
84 end
85
86 # Adds a handler to the handler function hash.
87 def self.add_handler(message_id, proc=nil, &handler)
88 if block_given?
89 @@handlers[message_id] = handler
90 elsif proc
91 @@handlers[message_id] = proc
92 end
93 end
94
95 # Process this event, preforming which ever handler and callback is specified
96 # for this event.
97 def process
98 handled = false
99 if @@handlers[@type]
100 @@handlers[@type].call(self)
101 handled = true
102 end
103
104 if @@callbacks[@type]
105 @@callbacks[@type].call(self)
106 handled = true
107 end
108
109 if not handled
110 if @@handlers["unhandled"]
111 @@handlers["unhandled"].call(self)
112 else
113 $stderr.puts "No handler for event type #@type in #{self.class}" if $DEBUG
114 end
115 end
116 end
117end
118
119end
  
1# Represents IRC Users
2module IRC
3
4class User
5 @@users = Hash.new()
6 @modes = Hash.new()
7
8 def self.create_user(username)
9 username.sub!(/^[\@\%]/,'')
10
11 if @@users[username]
12 return @@users[username]
13 end
14 @@users[username] = self.new(username)
15 @@users[username]
16 end
17
18 attr_reader :username, :mask
19 attr_writer :mask
20
21 private
22 def initialize (username)
23 @username = username
24 end
25end
26
27end
  
1#
2# IRCUtil is a module that contains utility functions for use with the
3# rest of Ruby-IRC. There is nothing required of the user to know or
4# even use these functions, but they are useful for certain tasks
5# regarding IRC connections.
6#
7
8module IRC
9
10module Util
11 #
12 # Matches hostmasks against hosts. Returns t/f on success/fail.
13 #
14 # A hostmask consists of a simple wildcard that describes a
15 # host or class of hosts.
16 #
17 # f.e., where the host is 'bar.example.com', a host mask
18 # of '*.example.com' would assert.
19 #
20
21 def assert_hostmask(host, hostmask)
22 return !!host.match(quote_regexp_for_mask(hostmask))
23 end
24
25 module_function :assert_hostmask
26
27 #
28 # A utility function used by assert_hostmask() to turn hostmasks
29 # into regular expressions.
30 #
31 # Rarely, if ever, should be used by outside code. It's public
32 # exposure is merely for those who are interested in it's
33 # functionality.
34 #
35
36 def quote_regexp_for_mask(hostmask)
37 # Big thanks to Jesse Williamson for his consultation while writing this.
38 #
39 # escape all other regexp specials except for . and *.
40 # properly escape . and place an unescaped . before *.
41 # confine the regexp to scan the whole line.
42 # return the edited hostmask as a string.
43 hostmask.gsub(/([\[\]\(\)\?\^\$])\\/, '\\1').
44 gsub(/\./, '\.').
45 gsub(/\*/, '.*').
46 sub(/^/, '^').
47 sub(/$/, '$')
48 end
49
50 module_function :quote_regexp_for_mask
51end
52
53end