cinch 1.1.3 → 2.0.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -0
 - data/README.md +3 -3
 - data/docs/bot_options.md +435 -0
 - data/docs/changes.md +440 -0
 - data/docs/common_mistakes.md +35 -0
 - data/docs/common_tasks.md +47 -0
 - data/docs/encodings.md +67 -0
 - data/docs/events.md +272 -0
 - data/docs/logging.md +5 -0
 - data/docs/migrating.md +267 -0
 - data/docs/readme.md +18 -0
 - data/examples/plugins/custom_prefix.rb +1 -1
 - data/examples/plugins/dice_roll.rb +38 -0
 - data/examples/plugins/lambdas.rb +1 -1
 - data/examples/plugins/memo.rb +16 -10
 - data/examples/plugins/url_shorten.rb +1 -0
 - data/lib/cinch.rb +5 -60
 - data/lib/cinch/ban.rb +13 -7
 - data/lib/cinch/bot.rb +228 -403
 - data/lib/cinch/{cache_manager.rb → cached_list.rb} +5 -1
 - data/lib/cinch/callback.rb +3 -0
 - data/lib/cinch/channel.rb +119 -195
 - data/lib/cinch/{channel_manager.rb → channel_list.rb} +6 -3
 - data/lib/cinch/configuration.rb +73 -0
 - data/lib/cinch/configuration/bot.rb +47 -0
 - data/lib/cinch/configuration/dcc.rb +16 -0
 - data/lib/cinch/configuration/plugins.rb +41 -0
 - data/lib/cinch/configuration/sasl.rb +17 -0
 - data/lib/cinch/configuration/ssl.rb +19 -0
 - data/lib/cinch/configuration/storage.rb +37 -0
 - data/lib/cinch/configuration/timeouts.rb +14 -0
 - data/lib/cinch/constants.rb +531 -369
 - data/lib/cinch/dcc.rb +12 -0
 - data/lib/cinch/dcc/dccable_object.rb +37 -0
 - data/lib/cinch/dcc/incoming.rb +1 -0
 - data/lib/cinch/dcc/incoming/send.rb +131 -0
 - data/lib/cinch/dcc/outgoing.rb +1 -0
 - data/lib/cinch/dcc/outgoing/send.rb +115 -0
 - data/lib/cinch/exceptions.rb +8 -1
 - data/lib/cinch/formatting.rb +106 -0
 - data/lib/cinch/handler.rb +104 -0
 - data/lib/cinch/handler_list.rb +86 -0
 - data/lib/cinch/helpers.rb +167 -10
 - data/lib/cinch/irc.rb +525 -110
 - data/lib/cinch/isupport.rb +11 -9
 - data/lib/cinch/logger.rb +168 -0
 - data/lib/cinch/logger/formatted_logger.rb +72 -55
 - data/lib/cinch/logger/zcbot_logger.rb +9 -24
 - data/lib/cinch/logger_list.rb +62 -0
 - data/lib/cinch/mask.rb +19 -10
 - data/lib/cinch/message.rb +94 -28
 - data/lib/cinch/message_queue.rb +70 -28
 - data/lib/cinch/mode_parser.rb +6 -1
 - data/lib/cinch/network.rb +104 -0
 - data/lib/cinch/{rubyext/queue.rb → open_ended_queue.rb} +8 -1
 - data/lib/cinch/pattern.rb +24 -4
 - data/lib/cinch/plugin.rb +352 -177
 - data/lib/cinch/plugin_list.rb +35 -0
 - data/lib/cinch/rubyext/float.rb +3 -0
 - data/lib/cinch/rubyext/module.rb +7 -0
 - data/lib/cinch/rubyext/string.rb +9 -0
 - data/lib/cinch/sasl.rb +34 -0
 - data/lib/cinch/sasl/dh_blowfish.rb +71 -0
 - data/lib/cinch/sasl/diffie_hellman.rb +47 -0
 - data/lib/cinch/sasl/mechanism.rb +6 -0
 - data/lib/cinch/sasl/plain.rb +26 -0
 - data/lib/cinch/storage.rb +62 -0
 - data/lib/cinch/storage/null.rb +12 -0
 - data/lib/cinch/storage/yaml.rb +96 -0
 - data/lib/cinch/syncable.rb +13 -1
 - data/lib/cinch/target.rb +144 -0
 - data/lib/cinch/timer.rb +145 -0
 - data/lib/cinch/user.rb +169 -225
 - data/lib/cinch/{user_manager.rb → user_list.rb} +7 -2
 - data/lib/cinch/utilities/deprecation.rb +12 -0
 - data/lib/cinch/utilities/encoding.rb +54 -0
 - data/lib/cinch/utilities/kernel.rb +13 -0
 - data/lib/cinch/utilities/string.rb +13 -0
 - data/lib/cinch/version.rb +4 -0
 - metadata +88 -47
 - data/lib/cinch/logger/logger.rb +0 -44
 - data/lib/cinch/logger/null_logger.rb +0 -18
 - data/lib/cinch/rubyext/infinity.rb +0 -1
 
    
        data/lib/cinch/dcc.rb
    ADDED
    
    | 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "cinch/dcc/outgoing"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "cinch/dcc/incoming"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Cinch
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Cinch supports the following DCC commands:
         
     | 
| 
      
 6 
     | 
    
         
            +
              #
         
     | 
| 
      
 7 
     | 
    
         
            +
              # - SEND (both {DCC::Incoming::Send incoming} and
         
     | 
| 
      
 8 
     | 
    
         
            +
              #   {DCC::Outgoing::Send outgoing})
         
     | 
| 
      
 9 
     | 
    
         
            +
              # @since 2.0.0
         
     | 
| 
      
 10 
     | 
    
         
            +
              module DCC
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Cinch
         
     | 
| 
      
 2 
     | 
    
         
            +
              module DCC
         
     | 
| 
      
 3 
     | 
    
         
            +
                # This module describes the required interface for objects that should
         
     | 
| 
      
 4 
     | 
    
         
            +
                # be sendable via DCC.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # @note `File` conforms to this interface.
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @since 2.0.0
         
     | 
| 
      
 8 
     | 
    
         
            +
                # @abstract
         
     | 
| 
      
 9 
     | 
    
         
            +
                module DCCableObject
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # Return the next `number` bytes of the object.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # @param [Number] number Read `number` bytes at most
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @return [String] The read data
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # @return [nil] If no more data can be read
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def read(number)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # Seek to a specific position.
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @param [Number] position The position in bytes to seek to
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def seek(position)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  # @return [String] A string representing the object's path or name.
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # @note This is only required if calling {User#dcc_send} with only
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #   one argument
         
     | 
| 
      
 29 
     | 
    
         
            +
                  def path
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  # @return [Number] The total size of the data, in bytes.
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def size
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "cinch/dcc/incoming/send"
         
     | 
| 
         @@ -0,0 +1,131 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "socket"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "ipaddr"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Cinch
         
     | 
| 
      
 5 
     | 
    
         
            +
              module DCC
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Incoming
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # DCC SEND is a protocol for transferring files, usually found
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # in IRC. While the handshake, i.e. the details of the file
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # transfer, are transferred over IRC, the actual file transfer
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # happens directly between two clients. As such it doesn't put
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # stress on the IRC server.
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # When someone tries to send a file to the bot, the `:dcc_send`
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # event will be triggered, in which the DCC request can be
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # inspected and optionally accepted.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # The event handler receives the plain message object as well as
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # an instance of this class. That instance contains information
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # about {#filename the suggested file name} (in a sanitized way)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # and allows for checking the origin.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # It is advised to reject transfers that seem to originate from
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # a {#from_private_ip? private IP} or {#from_localhost? the
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # local IP itself} unless that is expected. Otherwise, specially
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # crafted requests could cause the bot to connect to internal
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # services.
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # Finally, the file transfer can be {#accept accepted} and
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # written to any object that implements a `#<<` method, which
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # includes File objects as well as plain strings.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # @example Saving a transfer to a temporary file
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #   require "tempfile"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #   listen_to :dcc_send, method: :incoming_dcc
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #   def incoming_dcc(m, dcc)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #     if dcc.from_private_ip? || dcc.from_localhost?
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #       @bot.loggers.debug "Not accepting potentially dangerous file transfer"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #       return
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #     t = Tempfile.new(dcc.filename)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #     dcc.accept(t)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #     t.close
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  class Send
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 48 
     | 
    
         
            +
                    PRIVATE_NETS = [IPAddr.new("fc00::/7"),
         
     | 
| 
      
 49 
     | 
    
         
            +
                                    IPAddr.new("10.0.0.0/8"),
         
     | 
| 
      
 50 
     | 
    
         
            +
                                    IPAddr.new("172.16.0.0/12"),
         
     | 
| 
      
 51 
     | 
    
         
            +
                                    IPAddr.new("192.168.0.0/16")]
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    # @private
         
     | 
| 
      
 54 
     | 
    
         
            +
                    LOCAL_NETS = [IPAddr.new("127.0.0.0/8"),
         
     | 
| 
      
 55 
     | 
    
         
            +
                                  IPAddr.new("::1/128")]
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    # @return [User]
         
     | 
| 
      
 58 
     | 
    
         
            +
                    attr_reader :user
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 61 
     | 
    
         
            +
                    attr_reader :filename
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    # @return [Number]
         
     | 
| 
      
 64 
     | 
    
         
            +
                    attr_reader :size
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 67 
     | 
    
         
            +
                    attr_reader :ip
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    # @return [Number]
         
     | 
| 
      
 70 
     | 
    
         
            +
                    attr_reader :port
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    # @param [Hash] opts
         
     | 
| 
      
 73 
     | 
    
         
            +
                    # @option opts [User] user
         
     | 
| 
      
 74 
     | 
    
         
            +
                    # @option opts [String] filename
         
     | 
| 
      
 75 
     | 
    
         
            +
                    # @option opts [Number] size
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # @option opts [String] ip
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # @option opts [Number] port
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 79 
     | 
    
         
            +
                    def initialize(opts)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      @user, @filename, @size, @ip, @port = opts.values_at(:user, :filename, :size, :ip, :port)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    # @return [String] The basename of the file name, with
         
     | 
| 
      
 84 
     | 
    
         
            +
                    #   (back)slashes removed.
         
     | 
| 
      
 85 
     | 
    
         
            +
                    def filename
         
     | 
| 
      
 86 
     | 
    
         
            +
                      File.basename(File.expand_path(@filename)).delete("/\\")
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    # This method is used for accepting a DCC SEND offer. It
         
     | 
| 
      
 90 
     | 
    
         
            +
                    # expects an object to save the result to (usually an instance
         
     | 
| 
      
 91 
     | 
    
         
            +
                    # of IO or String).
         
     | 
| 
      
 92 
     | 
    
         
            +
                    #
         
     | 
| 
      
 93 
     | 
    
         
            +
                    # @param [#<<] io The object to write the data to.
         
     | 
| 
      
 94 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 95 
     | 
    
         
            +
                    # @note This method blocks.
         
     | 
| 
      
 96 
     | 
    
         
            +
                    # @example Saving to a file
         
     | 
| 
      
 97 
     | 
    
         
            +
                    #   f = File.open("/tmp/foo", "w")
         
     | 
| 
      
 98 
     | 
    
         
            +
                    #   dcc.accept(f)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    #   f.close
         
     | 
| 
      
 100 
     | 
    
         
            +
                    #
         
     | 
| 
      
 101 
     | 
    
         
            +
                    # @example Saving to a string
         
     | 
| 
      
 102 
     | 
    
         
            +
                    #   s = ""
         
     | 
| 
      
 103 
     | 
    
         
            +
                    #   dcc.accept(s)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    def accept(io)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      socket = TCPSocket.new(@ip, @port)
         
     | 
| 
      
 106 
     | 
    
         
            +
                      total = 0
         
     | 
| 
      
 107 
     | 
    
         
            +
                      while buf = socket.read(1024)
         
     | 
| 
      
 108 
     | 
    
         
            +
                        total += buf.bytesize
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                        socket.write [total].pack("N")
         
     | 
| 
      
 111 
     | 
    
         
            +
                        io << buf
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                    # @return [Boolean] True if the DCC originates from a private ip
         
     | 
| 
      
 116 
     | 
    
         
            +
                    # @see #from_localhost?
         
     | 
| 
      
 117 
     | 
    
         
            +
                    def from_private_ip?
         
     | 
| 
      
 118 
     | 
    
         
            +
                      ip   = IPAddr.new(@ip)
         
     | 
| 
      
 119 
     | 
    
         
            +
                      PRIVATE_NETS.any? {|n| n.include?(ip)}
         
     | 
| 
      
 120 
     | 
    
         
            +
                    end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                    # @return [Boolean] True if the DCC originates from localhost
         
     | 
| 
      
 123 
     | 
    
         
            +
                    # @see #from_private_ip?
         
     | 
| 
      
 124 
     | 
    
         
            +
                    def from_localhost?
         
     | 
| 
      
 125 
     | 
    
         
            +
                      ip   = IPAddr.new(@ip)
         
     | 
| 
      
 126 
     | 
    
         
            +
                      LOCAL_NETS.any? {|n| n.include?(ip)}
         
     | 
| 
      
 127 
     | 
    
         
            +
                    end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "cinch/dcc/outgoing/send"
         
     | 
| 
         @@ -0,0 +1,115 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "socket"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "ipaddr"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "timeout"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Cinch
         
     | 
| 
      
 6 
     | 
    
         
            +
              module DCC
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Outgoing
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # DCC SEND is a protocol for transferring files, usually found
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # in IRC. While the handshake, i.e. the details of the file
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # transfer, are transferred over IRC, the actual file transfer
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # happens directly between two clients. As such it doesn't put
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # stress on the IRC server.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # Cinch allows sending files by either using
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # {Cinch::User#dcc_send}, which takes care of all parameters as
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # well as setting up resume support, or by creating instances of
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # this class directly. The latter will only be useful to people
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # working on the Cinch code itself.
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # {Cinch::User#dcc_send} expects an object to send as well as
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # optionaly a file name, which is sent to the receiver as a
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # suggestion where to save the file. If no file name is
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # provided, the method will use the object's `#path` method to
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # determine it.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Any object that implements {DCC::DCCableObject} can be sent,
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # but sending files will probably be the most common case.
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # If you're behind a NAT it is necessary to explicitly set the
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # external IP using the {file:bot_options.md#dccownip dcc.own_ip
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # option}.
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @example Sending a file to a user
         
     | 
| 
      
 34 
     | 
    
         
            +
                  #   match "send me something"
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #   def execute(m)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #     m.user.dcc_send(open("/tmp/cookies"))
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  class Send
         
     | 
| 
      
 39 
     | 
    
         
            +
                    # @param [Hash] opts
         
     | 
| 
      
 40 
     | 
    
         
            +
                    # @option opts [User] receiver
         
     | 
| 
      
 41 
     | 
    
         
            +
                    # @option opts [String] filename
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # @option opts [File] io
         
     | 
| 
      
 43 
     | 
    
         
            +
                    # @option opts [String] own_ip
         
     | 
| 
      
 44 
     | 
    
         
            +
                    def initialize(opts = {})
         
     | 
| 
      
 45 
     | 
    
         
            +
                      @receiver, @filename, @io, @own_ip = opts.values_at(:receiver, :filename, :io, :own_ip)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    # Start the server
         
     | 
| 
      
 49 
     | 
    
         
            +
                    #
         
     | 
| 
      
 50 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 51 
     | 
    
         
            +
                    def start_server
         
     | 
| 
      
 52 
     | 
    
         
            +
                      @socket = TCPServer.new(0)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      @socket.listen(1)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    # Send the handshake to the user.
         
     | 
| 
      
 57 
     | 
    
         
            +
                    #
         
     | 
| 
      
 58 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 59 
     | 
    
         
            +
                    def send_handshake
         
     | 
| 
      
 60 
     | 
    
         
            +
                      handshake = "\001DCC SEND %s %d %d %d\001" % [@filename, IPAddr.new(@own_ip).to_i, port, @io.size]
         
     | 
| 
      
 61 
     | 
    
         
            +
                      @receiver.send(handshake)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    # Listen for an incoming connection.
         
     | 
| 
      
 65 
     | 
    
         
            +
                    #
         
     | 
| 
      
 66 
     | 
    
         
            +
                    # This starts listening for an incoming connection to the server
         
     | 
| 
      
 67 
     | 
    
         
            +
                    # started by {#start_server}. After a client successfully
         
     | 
| 
      
 68 
     | 
    
         
            +
                    # connected, the server socket will be closed and the file
         
     | 
| 
      
 69 
     | 
    
         
            +
                    # transferred to the client.
         
     | 
| 
      
 70 
     | 
    
         
            +
                    #
         
     | 
| 
      
 71 
     | 
    
         
            +
                    # @raise [Timeout::Error] Raised if the receiver did not connect
         
     | 
| 
      
 72 
     | 
    
         
            +
                    #   within 30 seconds
         
     | 
| 
      
 73 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 74 
     | 
    
         
            +
                    # @note This method blocks.
         
     | 
| 
      
 75 
     | 
    
         
            +
                    def listen
         
     | 
| 
      
 76 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 77 
     | 
    
         
            +
                        fd = nil
         
     | 
| 
      
 78 
     | 
    
         
            +
                        Timeout.timeout(30) do
         
     | 
| 
      
 79 
     | 
    
         
            +
                          fd, _ = @socket.accept
         
     | 
| 
      
 80 
     | 
    
         
            +
                          send_data(fd)
         
     | 
| 
      
 81 
     | 
    
         
            +
                          fd.close
         
     | 
| 
      
 82 
     | 
    
         
            +
                        end
         
     | 
| 
      
 83 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 84 
     | 
    
         
            +
                        @socket.close
         
     | 
| 
      
 85 
     | 
    
         
            +
                      end
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    # Seek to `pos` in the data.
         
     | 
| 
      
 89 
     | 
    
         
            +
                    #
         
     | 
| 
      
 90 
     | 
    
         
            +
                    # @param [Number] pos
         
     | 
| 
      
 91 
     | 
    
         
            +
                    # @return [void]
         
     | 
| 
      
 92 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 93 
     | 
    
         
            +
                    def seek(pos)
         
     | 
| 
      
 94 
     | 
    
         
            +
                      @io.seek(pos)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    # @return [Number] The port used for the socket
         
     | 
| 
      
 98 
     | 
    
         
            +
                    def port
         
     | 
| 
      
 99 
     | 
    
         
            +
                      @port ||= @socket.addr[1]
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    private
         
     | 
| 
      
 103 
     | 
    
         
            +
                    def send_data(fd)
         
     | 
| 
      
 104 
     | 
    
         
            +
                      @io.advise(:sequential)
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                      while chunk = @io.read(8096)
         
     | 
| 
      
 107 
     | 
    
         
            +
                        rs, ws = IO.select([fd], [fd])
         
     | 
| 
      
 108 
     | 
    
         
            +
                        rs.first.recv         unless rs.empty?
         
     | 
| 
      
 109 
     | 
    
         
            +
                        ws.first.write(chunk) unless ws.empty?
         
     | 
| 
      
 110 
     | 
    
         
            +
                      end
         
     | 
| 
      
 111 
     | 
    
         
            +
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
              end
         
     | 
| 
      
 115 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/cinch/exceptions.rb
    CHANGED
    
    | 
         @@ -1,9 +1,11 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Cinch
         
     | 
| 
      
 2 
     | 
    
         
            +
              # A collection of exceptions.
         
     | 
| 
       2 
3 
     | 
    
         
             
              module Exceptions
         
     | 
| 
       3 
4 
     | 
    
         
             
                # Generic error. Superclass for all Cinch-specific errors.
         
     | 
| 
       4 
5 
     | 
    
         
             
                class Generic < ::StandardError
         
     | 
| 
       5 
6 
     | 
    
         
             
                end
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
      
 8 
     | 
    
         
            +
                # Generic error when an argument is too long.
         
     | 
| 
       7 
9 
     | 
    
         
             
                class ArgumentTooLong < Generic
         
     | 
| 
       8 
10 
     | 
    
         
             
                end
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
         @@ -19,15 +21,20 @@ module Cinch 
     | 
|
| 
       19 
21 
     | 
    
         
             
                class KickReasonTooLong < ArgumentTooLong
         
     | 
| 
       20 
22 
     | 
    
         
             
                end
         
     | 
| 
       21 
23 
     | 
    
         | 
| 
      
 24 
     | 
    
         
            +
                # Raised whenever Cinch discovers a feature it doesn't support
         
     | 
| 
      
 25 
     | 
    
         
            +
                # yet.
         
     | 
| 
       22 
26 
     | 
    
         
             
                class UnsupportedFeature < Generic
         
     | 
| 
       23 
27 
     | 
    
         
             
                end
         
     | 
| 
       24 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
                # Raised when Cinch discovers a user or channel mode, which it
         
     | 
| 
      
 30 
     | 
    
         
            +
                # doesn't support yet.
         
     | 
| 
       25 
31 
     | 
    
         
             
                class UnsupportedMode < Generic
         
     | 
| 
       26 
32 
     | 
    
         
             
                  def initialize(mode)
         
     | 
| 
       27 
     | 
    
         
            -
                    super "Cinch does not support the mode #{mode} yet."
         
     | 
| 
      
 33 
     | 
    
         
            +
                    super "Cinch does not support the mode '#{mode}' yet."
         
     | 
| 
       28 
34 
     | 
    
         
             
                  end
         
     | 
| 
       29 
35 
     | 
    
         
             
                end
         
     | 
| 
       30 
36 
     | 
    
         | 
| 
      
 37 
     | 
    
         
            +
                # Error stating that an invalid mode string was encountered.
         
     | 
| 
       31 
38 
     | 
    
         
             
                class InvalidModeString < Generic
         
     | 
| 
       32 
39 
     | 
    
         
             
                end
         
     | 
| 
       33 
40 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Cinch
         
     | 
| 
      
 2 
     | 
    
         
            +
              # @since 2.0.0
         
     | 
| 
      
 3 
     | 
    
         
            +
              #
         
     | 
| 
      
 4 
     | 
    
         
            +
              # List of valid colors
         
     | 
| 
      
 5 
     | 
    
         
            +
              # =========================
         
     | 
| 
      
 6 
     | 
    
         
            +
              # - aqua
         
     | 
| 
      
 7 
     | 
    
         
            +
              # - black
         
     | 
| 
      
 8 
     | 
    
         
            +
              # - blue
         
     | 
| 
      
 9 
     | 
    
         
            +
              # - brown
         
     | 
| 
      
 10 
     | 
    
         
            +
              # - green
         
     | 
| 
      
 11 
     | 
    
         
            +
              # - grey
         
     | 
| 
      
 12 
     | 
    
         
            +
              # - lime
         
     | 
| 
      
 13 
     | 
    
         
            +
              # - orange
         
     | 
| 
      
 14 
     | 
    
         
            +
              # - pink
         
     | 
| 
      
 15 
     | 
    
         
            +
              # - purple
         
     | 
| 
      
 16 
     | 
    
         
            +
              # - red
         
     | 
| 
      
 17 
     | 
    
         
            +
              # - royal
         
     | 
| 
      
 18 
     | 
    
         
            +
              # - silver
         
     | 
| 
      
 19 
     | 
    
         
            +
              # - teal
         
     | 
| 
      
 20 
     | 
    
         
            +
              # - white
         
     | 
| 
      
 21 
     | 
    
         
            +
              # - yellow
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              # List of valid attributes
         
     | 
| 
      
 24 
     | 
    
         
            +
              # ========================
         
     | 
| 
      
 25 
     | 
    
         
            +
              # - bold
         
     | 
| 
      
 26 
     | 
    
         
            +
              # - italic
         
     | 
| 
      
 27 
     | 
    
         
            +
              # - reverse/reversed
         
     | 
| 
      
 28 
     | 
    
         
            +
              # - underline/underlined
         
     | 
| 
      
 29 
     | 
    
         
            +
              #
         
     | 
| 
      
 30 
     | 
    
         
            +
              # Other
         
     | 
| 
      
 31 
     | 
    
         
            +
              # =====
         
     | 
| 
      
 32 
     | 
    
         
            +
              # - reset (Resets all formatting to the client's defaults)
         
     | 
| 
      
 33 
     | 
    
         
            +
              module Formatting
         
     | 
| 
      
 34 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 35 
     | 
    
         
            +
                Colors = {
         
     | 
| 
      
 36 
     | 
    
         
            +
                  :white  => "00",
         
     | 
| 
      
 37 
     | 
    
         
            +
                  :black  => "01",
         
     | 
| 
      
 38 
     | 
    
         
            +
                  :blue   => "02",
         
     | 
| 
      
 39 
     | 
    
         
            +
                  :green  => "03",
         
     | 
| 
      
 40 
     | 
    
         
            +
                  :red    => "04",
         
     | 
| 
      
 41 
     | 
    
         
            +
                  :brown  => "05",
         
     | 
| 
      
 42 
     | 
    
         
            +
                  :purple => "06",
         
     | 
| 
      
 43 
     | 
    
         
            +
                  :orange => "07",
         
     | 
| 
      
 44 
     | 
    
         
            +
                  :yellow => "08",
         
     | 
| 
      
 45 
     | 
    
         
            +
                  :lime   => "09",
         
     | 
| 
      
 46 
     | 
    
         
            +
                  :teal   => "10",
         
     | 
| 
      
 47 
     | 
    
         
            +
                  :aqua   => "11",
         
     | 
| 
      
 48 
     | 
    
         
            +
                  :royal  => "12",
         
     | 
| 
      
 49 
     | 
    
         
            +
                  :pink   => "13",
         
     | 
| 
      
 50 
     | 
    
         
            +
                  :grey   => "14",
         
     | 
| 
      
 51 
     | 
    
         
            +
                  :silver => "15",
         
     | 
| 
      
 52 
     | 
    
         
            +
                }
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 55 
     | 
    
         
            +
                Attributes = {
         
     | 
| 
      
 56 
     | 
    
         
            +
                  :bold       => 2.chr,
         
     | 
| 
      
 57 
     | 
    
         
            +
                  :underlined => 31.chr,
         
     | 
| 
      
 58 
     | 
    
         
            +
                  :underline  => 31.chr,
         
     | 
| 
      
 59 
     | 
    
         
            +
                  :reversed   => 22.chr,
         
     | 
| 
      
 60 
     | 
    
         
            +
                  :reverse    => 22.chr,
         
     | 
| 
      
 61 
     | 
    
         
            +
                  :italic     => 22.chr,
         
     | 
| 
      
 62 
     | 
    
         
            +
                  :reset      => 15.chr,
         
     | 
| 
      
 63 
     | 
    
         
            +
                }
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                # @param [Array<Symbol>] *settings The colors and attributes to apply.
         
     | 
| 
      
 66 
     | 
    
         
            +
                #   When supplying two colors, the first will be used for the
         
     | 
| 
      
 67 
     | 
    
         
            +
                #   foreground and the second for the background.
         
     | 
| 
      
 68 
     | 
    
         
            +
                # @param [String] string The string to format.
         
     | 
| 
      
 69 
     | 
    
         
            +
                # @return [String] The formatted string
         
     | 
| 
      
 70 
     | 
    
         
            +
                # @since 2.0.0
         
     | 
| 
      
 71 
     | 
    
         
            +
                # @raise [ArgumentError] When passing more than two colors as arguments.
         
     | 
| 
      
 72 
     | 
    
         
            +
                # @see Helpers#Format Helpers#Format for easier access to this method.
         
     | 
| 
      
 73 
     | 
    
         
            +
                #
         
     | 
| 
      
 74 
     | 
    
         
            +
                # @example Nested formatting, combining text styles and colors
         
     | 
| 
      
 75 
     | 
    
         
            +
                #   reply = Format(:underline, "Hello %s! Is your favourite color %s?" % [Format(:bold, "stranger"), Format(:red, "red")])
         
     | 
| 
      
 76 
     | 
    
         
            +
                def self.format(*settings, string)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  string   = string.dup
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  attributes = settings.select {|k| Attributes.has_key?(k)}.map {|k| Attributes[k]}
         
     | 
| 
      
 80 
     | 
    
         
            +
                  colors = settings.select {|k| Colors.has_key?(k)}.map {|k| Colors[k]}
         
     | 
| 
      
 81 
     | 
    
         
            +
                  if colors.size > 2
         
     | 
| 
      
 82 
     | 
    
         
            +
                    raise ArgumentError, "At most two colors (foreground and background) might be specified"
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  attribute_string = attributes.join
         
     | 
| 
      
 86 
     | 
    
         
            +
                  color_string = if colors.empty?
         
     | 
| 
      
 87 
     | 
    
         
            +
                                   ""
         
     | 
| 
      
 88 
     | 
    
         
            +
                                 else
         
     | 
| 
      
 89 
     | 
    
         
            +
                                   "\x03#{colors.join(",")}"
         
     | 
| 
      
 90 
     | 
    
         
            +
                                 end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  prepend = attribute_string + color_string
         
     | 
| 
      
 93 
     | 
    
         
            +
                  append  = Attributes[:reset]
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  # attributes act as toggles, so e.g. underline+underline = no
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # underline. We thus have to delete all duplicate attributes
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # from nested strings.
         
     | 
| 
      
 98 
     | 
    
         
            +
                  string.delete!(attribute_string)
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  # Replace the reset code of nested strings to continue the
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # formattings of the outer string.
         
     | 
| 
      
 102 
     | 
    
         
            +
                  string.gsub!(/#{Attributes[:reset]}/, Attributes[:reset] + prepend)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  return prepend + string + append
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Cinch
         
     | 
| 
      
 2 
     | 
    
         
            +
              # @since 2.0.0
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Handler
         
     | 
| 
      
 4 
     | 
    
         
            +
                # @return [Bot]
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :bot
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                # @return [Symbol]
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :event
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # @return [Pattern]
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_reader :pattern
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                # @return [Array]
         
     | 
| 
      
 14 
     | 
    
         
            +
                attr_reader :args
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                # @return [Proc]
         
     | 
| 
      
 17 
     | 
    
         
            +
                attr_reader :block
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                # @return [Symbol]
         
     | 
| 
      
 20 
     | 
    
         
            +
                attr_reader :group
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                # @return [ThreadGroup]
         
     | 
| 
      
 23 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 24 
     | 
    
         
            +
                attr_reader :thread_group
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # @param [Bot] bot
         
     | 
| 
      
 27 
     | 
    
         
            +
                # @param [Symbol] event
         
     | 
| 
      
 28 
     | 
    
         
            +
                # @param [Pattern] pattern
         
     | 
| 
      
 29 
     | 
    
         
            +
                # @param [Hash] options
         
     | 
| 
      
 30 
     | 
    
         
            +
                # @option options [Symbol] :group (nil) Match group the h belongs
         
     | 
| 
      
 31 
     | 
    
         
            +
                #   to.
         
     | 
| 
      
 32 
     | 
    
         
            +
                # @option options [Boolean] :execute_in_callback (false) Whether
         
     | 
| 
      
 33 
     | 
    
         
            +
                #   to execute the handler in an instance of {Callback}
         
     | 
| 
      
 34 
     | 
    
         
            +
                # @option options [Array] :args ([]) Additional arguments to pass
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   to the block
         
     | 
| 
      
 36 
     | 
    
         
            +
                def initialize(bot, event, pattern, options = {}, &block)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  options              = {:group => nil, :execute_in_callback => false, :args => []}.merge(options)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  @bot                 = bot
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @event               = event
         
     | 
| 
      
 40 
     | 
    
         
            +
                  @pattern             = pattern
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @group               = options[:group]
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @execute_in_callback = options[:execute_in_callback]
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @args                = options[:args]
         
     | 
| 
      
 44 
     | 
    
         
            +
                  @block               = block
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  @thread_group = ThreadGroup.new
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                # Unregisters the handler.
         
     | 
| 
      
 50 
     | 
    
         
            +
                #
         
     | 
| 
      
 51 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 52 
     | 
    
         
            +
                def unregister
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @bot.handlers.unregister(self)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # Stops execution of the handler. This means stopping and killing
         
     | 
| 
      
 57 
     | 
    
         
            +
                # all associated threads.
         
     | 
| 
      
 58 
     | 
    
         
            +
                #
         
     | 
| 
      
 59 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 60 
     | 
    
         
            +
                def stop
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @bot.loggers.debug "[Stopping handler] Stopping all threads of handler #{self}: #{@thread_group.list.size} threads..."
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @thread_group.list.each do |thread|
         
     | 
| 
      
 63 
     | 
    
         
            +
                    Thread.new do
         
     | 
| 
      
 64 
     | 
    
         
            +
                      @bot.loggers.debug "[Ending thread] Waiting 10 seconds for #{thread} to finish..."
         
     | 
| 
      
 65 
     | 
    
         
            +
                      thread.join(10)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      @bot.loggers.debug "[Killing thread] Killing #{thread}"
         
     | 
| 
      
 67 
     | 
    
         
            +
                      thread.kill
         
     | 
| 
      
 68 
     | 
    
         
            +
                    end
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # Executes the handler.
         
     | 
| 
      
 73 
     | 
    
         
            +
                #
         
     | 
| 
      
 74 
     | 
    
         
            +
                # @param [Message] message Message that caused the invocation
         
     | 
| 
      
 75 
     | 
    
         
            +
                # @param [Array] captures Capture groups of the pattern that are
         
     | 
| 
      
 76 
     | 
    
         
            +
                #   being passed as arguments
         
     | 
| 
      
 77 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 78 
     | 
    
         
            +
                def call(message, captures, arguments)
         
     | 
| 
      
 79 
     | 
    
         
            +
                  bargs = captures + arguments
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  @thread_group.add Thread.new {
         
     | 
| 
      
 82 
     | 
    
         
            +
                    @bot.loggers.debug "[New thread] For #{self}: #{Thread.current} -- #{@thread_group.list.size} in total."
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 85 
     | 
    
         
            +
                      if @execute_in_callback
         
     | 
| 
      
 86 
     | 
    
         
            +
                        @bot.callback.instance_exec(message, *@args, *bargs, &@block)
         
     | 
| 
      
 87 
     | 
    
         
            +
                      else
         
     | 
| 
      
 88 
     | 
    
         
            +
                        @block.call(message, *@args, *bargs)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    rescue => e
         
     | 
| 
      
 91 
     | 
    
         
            +
                      @bot.loggers.exception(e)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    ensure
         
     | 
| 
      
 93 
     | 
    
         
            +
                      @bot.loggers.debug "[Thread done] For #{self}: #{Thread.current} -- #{@thread_group.list.size - 1} remaining."
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  }
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                # @return [String]
         
     | 
| 
      
 99 
     | 
    
         
            +
                def to_s
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # TODO maybe add the number of running threads to the output?
         
     | 
| 
      
 101 
     | 
    
         
            +
                  "#<Cinch::Handler @event=#{@event.inspect} pattern=#{@pattern.inspect}>"
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     |