Commit a57fcb549d9929abc93cc510337424e50fc407fd
- Diff rendering mode:
- inline
- side by side
example/bot.rb
(49 / 0)
|   | |||
| 1 | #!/usr/bin/env ruby | ||
| 2 | |||
| 3 | $: << File.join(File.dirname(__FILE__), "../lib/") | ||
| 4 | |||
| 5 | require 'irc/bot' | ||
| 6 | |||
| 7 | class 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 | ||
| 41 | end | ||
| 42 | |||
| 43 | NICK = "ruby-irc" | ||
| 44 | SERVER = "localhost" | ||
| 45 | PORT = "6667" | ||
| 46 | IRC_NAME = "My Bot" | ||
| 47 | |||
| 48 | bot = MyBot.new(NICK, SERVER, PORT, IRC_NAME) | ||
| 49 | bot.connect |
lib/irc/IRC.rb
(0 / 174)
|   | |||
| 1 | |||
| 2 | require 'socket' | ||
| 3 | require 'IRCConnection' | ||
| 4 | require 'IRCEvent' | ||
| 5 | require 'IRCChannel' | ||
| 6 | require 'IRCUser' | ||
| 7 | require '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. | ||
| 11 | class 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 | ||
| 174 | end |
lib/irc/IRCBot.rb
(0 / 27)
|   | |||
| 1 | |||
| 2 | require 'IRC' | ||
| 3 | |||
| 4 | class 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 | ||
| 27 | end |
lib/irc/IRCChannel.rb
(0 / 33)
|   | |||
| 1 | require "IRCUser" | ||
| 2 | |||
| 3 | # Represents an IRC Channel | ||
| 4 | class 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 | ||
| 33 | end |
lib/irc/IRCConnection.rb
(0 / 118)
|   | |||
| 1 | |||
| 2 | # Handles connection to IRC Server | ||
| 3 | class 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 | ||
| 118 | end |
lib/irc/IRCEvent.rb
(0 / 115)
|   | |||
| 1 | require 'yaml' | ||
| 2 | |||
| 3 | # This is a lookup class for IRC event name mapping | ||
| 4 | class 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 | ||
| 11 | end | ||
| 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. | ||
| 18 | class 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 | ||
| 115 | end |
lib/irc/IRCUser.rb
(0 / 23)
|   | |||
| 1 | # Represents IRC Users | ||
| 2 | class 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 | ||
| 23 | end |
lib/irc/IRCUtil.rb
(0 / 49)
|   | |||
| 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 | |||
| 8 | module 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 | ||
| 49 | end |
lib/irc/bot.rb
(34 / 0)
|   | |||
| 1 | |||
| 2 | require 'irc/botbase' | ||
| 3 | |||
| 4 | module IRC | ||
| 5 | |||
| 6 | class 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 | ||
| 32 | end | ||
| 33 | |||
| 34 | end |
lib/irc/botbase.rb
(178 / 0)
|   | |||
| 1 | |||
| 2 | require 'socket' | ||
| 3 | require 'irc/connection' | ||
| 4 | require 'irc/event' | ||
| 5 | require 'irc/channel' | ||
| 6 | require 'irc/user' | ||
| 7 | require 'irc/util' | ||
| 8 | |||
| 9 | module 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. | ||
| 13 | class 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 | ||
| 176 | end | ||
| 177 | |||
| 178 | end |
lib/irc/channel.rb
(38 / 0)
|   | |||
| 1 | require "irc/user" | ||
| 2 | |||
| 3 | module IRC | ||
| 4 | |||
| 5 | # Represents an IRC Channel | ||
| 6 | class 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 | ||
| 35 | end | ||
| 36 | |||
| 37 | |||
| 38 | end |
lib/irc/connection.rb
(124 / 0)
|   | |||
| 1 | |||
| 2 | |||
| 3 | |||
| 4 | module IRC | ||
| 5 | |||
| 6 | # Handles connection to IRC Server | ||
| 7 | class 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 | ||
| 122 | end | ||
| 123 | |||
| 124 | end |
lib/irc/event.rb
(119 / 0)
|   | |||
| 1 | require 'yaml' | ||
| 2 | |||
| 3 | module IRC | ||
| 4 | |||
| 5 | # This is a lookup class for IRC event name mapping | ||
| 6 | class 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 | ||
| 13 | end | ||
| 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. | ||
| 20 | class 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 | ||
| 117 | end | ||
| 118 | |||
| 119 | end |
lib/irc/user.rb
(27 / 0)
|   | |||
| 1 | # Represents IRC Users | ||
| 2 | module IRC | ||
| 3 | |||
| 4 | class 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 | ||
| 25 | end | ||
| 26 | |||
| 27 | end |
lib/irc/util.rb
(53 / 0)
|   | |||
| 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 | |||
| 8 | module IRC | ||
| 9 | |||
| 10 | module 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 | ||
| 51 | end | ||
| 52 | |||
| 53 | end |

