1 #!/usr/local/bin/ruby
 2 
 3 require "socket"
 4 
 5 # Don't allow use of "tainted" data by potentially dangerous operations
 6 $SAFE=1
 7 
 8 # The irc class, which talks to the server and holds the main event loop
 9 class IRC
10     def initialize(server, port, nick, channel)
11         @server = server
12         @port = port
13         @nick = nick
14         @channel = channel
15     end
16     def send(s)
17         # Send a message to the irc server and print it to the screen
18         puts "--> #{s}"
19         @irc.send "#{s}\n", 0 
20     end
21     def connect()
22         # Connect to the IRC server
23         @irc = TCPSocket.open(@server, @port)
24         send "USER blah blah blah :blah blah"
25         send "NICK #{@nick}"
26         send "JOIN #{@channel}"
27     end
28     def evaluate(s)
29         # Make sure we have a valid expression (for security reasons), and
30         # evaluate it if we do, otherwise return an error message
31         if s =~ /^[-+*\/\d\s\eE.()]*$/ then
32             begin
33                 s.untaint
34                 return eval(s).to_s
35             rescue Exception => detail
36                 puts detail.message()
37             end
38         end
39         return "Error"
40     end
41     def handle_server_input(s)
42         # This isn't at all efficient, but it shows what we can do with Ruby
43         # (Dave Thomas calls this construct "a multiway if on steroids")
44         case s.strip
45             when /^PING :(.+)$/i
46                 puts "[ Server ping ]"
47                 send "PONG :#{$1}"
48             when /^:(.+?)!(.+?)@(.+?)\sPRIVMSG\s.+\s:[\001]PING (.+)[\001]$/i
49                 puts "[ CTCP PING from #{$1}!#{$2}@#{$3} ]"
50                 send "NOTICE #{$1} :\001PING #{$4}\001"
51             when /^:(.+?)!(.+?)@(.+?)\sPRIVMSG\s.+\s:[\001]VERSION[\001]$/i
52                 puts "[ CTCP VERSION from #{$1}!#{$2}@#{$3} ]"
53                 send "NOTICE #{$1} :\001VERSION Ruby-irc v0.042\001"
54             when /^:(.+?)!(.+?)@(.+?)\sPRIVMSG\s(.+)\s:EVAL (.+)$/i
55                 puts "[ EVAL #{$5} from #{$1}!#{$2}@#{$3} ]"
56                 send "PRIVMSG #{(($4==@nick)?$1:$4)} :#{evaluate($5)}"
57             else
58                 puts s
59         end
60     end
61     def main_loop()
62         # Just keep on truckin' until we disconnect
63         while true
64             ready = select([@irc, $stdin], nil, nil, nil)
65             next if !ready
66             for s in ready[0]
67                 if s == $stdin then
68                     return if $stdin.eof
69                     s = $stdin.gets
70                     send s
71                 elsif s == @irc then
72                     return if @irc.eof
73                     s = @irc.gets
74                     handle_server_input(s)
75                 end
76             end
77         end
78     end
79 end
80 
81 # The main program
82 # If we get an exception, then print it out and keep going (we do NOT want
83 # to disconnect unexpectedly!)
84 irc = IRC.new('efnet.skynet.be', 6667, 'Alt-255', '#cout')
85 irc.connect()
86 begin
87     irc.main_loop()
88 rescue Interrupt
89 rescue Exception => detail
90     puts detail.message()
91     print detail.backtrace.join("\n")
92     retry
93 end