relayer 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in relayer.gemspec
4
+ gemspec
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
@@ -0,0 +1,16 @@
1
+ [Relayer](http://github.com/brianewing/relayer) is a dead simple, high-performance, event-driven IRC library written in Ruby.
2
+ -----------------------------------------------------------------------------------------------------------------------------
3
+
4
+ It uses IO#select to achieve asynchronous IO, and has been tested to handle many hundreds of concurrent IRC connections from one instance.
5
+
6
+ Sample usage:
7
+
8
+ ```Ruby
9
+ echo = Relayer::IRCClient.new(:hostname => 'irc.esper.net', :nick => 'EchoBot', :channels => ['#echo'])
10
+
11
+ echo.events.channel_msg do |irc, event|
12
+ irc.message event[:channel], event[:message]
13
+ end
14
+
15
+ Relayer::start! echo
16
+ ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require "relayer/relayer"
@@ -0,0 +1,124 @@
1
+ require "relayer/events"
2
+ require "relayer/protocol"
3
+ require "relayer/version"
4
+
5
+ module Relayer
6
+ class IRCClient
7
+ attr_accessor :events
8
+ attr_accessor :nick
9
+ attr_accessor :protocol
10
+
11
+ def initialize(options = {})
12
+ @ssl = options[:ssl]
13
+
14
+ @hostname = options[:hostname]
15
+ @port = options[:port]
16
+
17
+ @ping_reply = options[:ping_reply]
18
+ @version_reply = options[:version_reply]
19
+
20
+ @version_string = options[:version_string]
21
+
22
+ @nick = options[:nick]
23
+ @nick_pass = options[:pass]
24
+ @ident = options[:ident]
25
+ @real_name = options[:real_name]
26
+
27
+ @first_channels = options[:channels] || []
28
+ default_options!
29
+
30
+ @users = {}
31
+ @channels = {}
32
+
33
+ @protocol = IRCProtocol.new(self)
34
+ @events = IRCEvents.new(@protocol, self)
35
+
36
+ @mutex = Mutex.new
37
+
38
+ default_handlers!
39
+ end
40
+
41
+ def version_string
42
+ @version_string || "Relayer IRC Library v#{Relayer::VERSION}"
43
+ end
44
+
45
+ def register
46
+ @protocol.user(@ident, @real_name)
47
+ @protocol.nick(@nick)
48
+ end
49
+
50
+ def default_handlers!
51
+ @events.ping do |irc, event|
52
+ @protocol.pong event[:token]
53
+ end
54
+
55
+ @events.connected do
56
+ unless @nick_pass.nil?
57
+ @protocol.message('NickServ', "identify #{@nick_pass}")
58
+ end
59
+
60
+ @first_channels.each do |channel|
61
+ @protocol.join(channel)
62
+ end
63
+ end
64
+
65
+ @events.ctcp do |irc, event|
66
+ @protocol.ctcp_reply :version, event[:actor], version_string if event[:query] == :version
67
+ end
68
+ end
69
+
70
+ def default_options!
71
+ if @ssl.nil?
72
+ @ssl = false
73
+ end
74
+
75
+ unless @port
76
+ @port = 6697 if @ssl
77
+ @port ||= 6667
78
+ end
79
+
80
+ @nick ||= "Relayer"
81
+ @ident ||= "relayer"
82
+ @real_name ||= "Relayer"
83
+
84
+ @hostname ||= 'irc.freenode.net'
85
+ end
86
+
87
+ def start
88
+ @socket = IRCSocket.new(self, @hostname, @port, @ssl)
89
+ register
90
+ end
91
+
92
+ def readable
93
+ @socket.read
94
+ end
95
+
96
+ def writable
97
+ @mutex.synchronize do
98
+ @socket.write
99
+ end
100
+ end
101
+
102
+ def process_raw(line)
103
+ @protocol.process_line(line)
104
+ end
105
+
106
+ def send_raw(line)
107
+ @mutex.synchronize do
108
+ @socket.send line
109
+ end
110
+ end
111
+
112
+ def user(hostmask)
113
+ parts = IRC::User.parse_hostmask(hostmask)
114
+
115
+ nickname = parts[:nick].downcase
116
+ @users[nickname] ||= IRC::User.new(hostmask) if IRC::User.is_user?(hostmask)
117
+ end
118
+
119
+ def channel(channel)
120
+ channel = channel.downcase
121
+ @channels[channel] ||= IRC::Channel.new(channel) if IRC::Channel.is_channel?(channel)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,7 @@
1
+ class Relayer::Event::Event
2
+ attr_accessor :time
3
+
4
+ def time!
5
+ @time = Time.now
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ require "relayer/event/event"
2
+
3
+ module Relayer::Event
4
+ class Message < Event
5
+ attr_accessor :sender, :message, :channel
6
+
7
+ def initialize(sender, message, channel)
8
+ @sender = sender
9
+ @message = message
10
+ @channel = channel
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ module Relayer
2
+ class IRCEvents
3
+ def initialize(protocol, client)
4
+ @protocol = protocol
5
+ @client = client
6
+
7
+ @handlers = {
8
+ :ping => [],
9
+ :connected => [],
10
+ :channel_msg => [],
11
+ :ctcp => []
12
+ }
13
+ end
14
+
15
+ def method_missing(event, *args, &handler)
16
+ raise ArgumentError if handler.nil?
17
+
18
+ if @handlers.has_key? event
19
+ @handlers[event].push handler
20
+ else
21
+ raise NoMethodError
22
+ end
23
+ end
24
+
25
+ def dispatch(event, args)
26
+ @handlers[event].each do |handler|
27
+ handler.call(@protocol, args)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ module Relayer::IRC
2
+ class Channel
3
+ attr_accessor :users
4
+
5
+ def self.is_channel?(channel)
6
+ channel[0] == '#'
7
+ end
8
+
9
+ def to_s
10
+ @channel
11
+ end
12
+
13
+ def initialize(channel)
14
+ @channel = channel
15
+ @users = []
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ module Relayer::IRC
2
+ class User
3
+ IRC_HOSTMASK_REGEX = /^([^\!@]+)(\!([^\!@]+))(@(.+))$/
4
+
5
+ attr_accessor :nick, :ident, :hostname, :hostmask
6
+
7
+ def self.is_user?(hostmask)
8
+ not IRC_HOSTMASK_REGEX.match(hostmask).nil?
9
+ end
10
+
11
+ def to_s
12
+ @nick
13
+ end
14
+
15
+ def self.parse_hostmask(hostmask)
16
+ match = IRC_HOSTMASK_REGEX.match hostmask
17
+ return if match.nil?
18
+
19
+ raw, nick, ident_tmp, ident, hostname_tmp, hostname = match.to_a
20
+ return :nick => nick, :ident => ident, :hostname => hostname
21
+ end
22
+
23
+ def parse_hostmask!
24
+ parts = User.parse_hostmask @hostmask
25
+
26
+ @nick = parts[:nick]
27
+ @ident = parts[:ident]
28
+ @hostname = parts[:hostname]
29
+ end
30
+
31
+ def initialize(hostmask)
32
+ @hostmask = hostmask
33
+ parse_hostmask!
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,118 @@
1
+ require "relayer/irc/user"
2
+ require "relayer/irc/channel"
3
+
4
+ module Relayer
5
+ class IRCProtocolException < Exception; end
6
+
7
+ class IRCProtocol
8
+ IRC_LINE_REGEX = /^(:([^ ]+) )?([A-Z0-9]+) ([^:]+)? ?(:(.*))?$/
9
+
10
+ def initialize(client)
11
+ @client = client
12
+ @registration_commands = []
13
+ end
14
+
15
+ def dispatch(event, args = {})
16
+ @client.events.dispatch(event, args)
17
+ end
18
+
19
+ def process_line(line)
20
+ match = IRC_LINE_REGEX.match(line)
21
+ raise IRCProtocolException if match.nil?
22
+
23
+ raw, actor_tmp, actor, command, args, ext_tmp, extended_arg = match.to_a
24
+
25
+ if Relayer::IRC::User.is_user? actor
26
+ actor = @client.user(actor)
27
+ else
28
+ actor = nil #todo: implement server actors
29
+ end
30
+
31
+ args = args.split unless args.nil?
32
+ command = command.downcase.to_sym unless command.nil?
33
+
34
+ if actor.nil?
35
+ # match server-only commands like 'PING'
36
+ case command
37
+ when :ping
38
+ dispatch :ping, :token => extended_arg
39
+ end
40
+
41
+ if ("001".."004").include? command.to_s
42
+ @registration_commands.push command.to_s
43
+
44
+ dispatch :connected if registered?
45
+ end
46
+ else
47
+ case command
48
+ when :privmsg
49
+ to = args[0]
50
+
51
+ message = extended_arg
52
+
53
+ is_ctcp = message.start_with?("\001") and message.end_with?("\001")
54
+ message = message[1..-2] if is_ctcp
55
+
56
+ if is_ctcp
57
+ query_args = message.split
58
+ query = query_args.shift.downcase.to_sym
59
+
60
+ dispatch :ctcp, :actor => actor, :channel => @client.channel(to), :query => query, :args => query_args
61
+ elsif IRC::Channel.is_channel?(to)
62
+ dispatch :channel_msg, :actor => actor, :channel => @client.channel(to), :message => extended_arg
63
+ elsif to.downcase == @nick.downcase
64
+ dispatch :private_msg, :actor => actor, :message => extended_arg
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def registered?
71
+ (("001".."004").to_a - @registration_commands).empty?
72
+ end
73
+
74
+ def send_command(command, *args)
75
+ if args.last.include?(' ')
76
+ arg = args.pop
77
+ arg.insert(0, ':')
78
+ args.push(arg)
79
+ end
80
+
81
+ @client.send_raw "#{command.to_s.upcase} #{args.join ' '}"
82
+ end
83
+
84
+ def pong(token = false)
85
+ send_command :pong, token
86
+ end
87
+
88
+ def ctcp(label, to)
89
+ extended_arg = "\001#{label.to_s.upcase}\001"
90
+
91
+ send_command :privmsg, to, extended_arg
92
+ end
93
+
94
+ def ctcp_reply(label, to, data)
95
+ data = " #{data}" if data
96
+ extended_arg = "\001#{label.to_s.upcase}#{data}\001"
97
+
98
+ send_command :notice, to, extended_arg
99
+ end
100
+
101
+ def message(channel, message)
102
+ send_command :privmsg, channel, message
103
+ end
104
+
105
+ def nick(nickname)
106
+ send_command :nick, nickname
107
+ end
108
+
109
+ def join(channel)
110
+ channel = "\##{channel}" unless channel[0] == '#'
111
+ send_command :join, channel
112
+ end
113
+
114
+ def user(ident, real_name)
115
+ send_command :user, ident, '0', '*', real_name
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,19 @@
1
+ require "relayer/client"
2
+ require "relayer/socket"
3
+ require "relayer/version"
4
+
5
+ module Relayer
6
+ def Relayer::start!(bots)
7
+ if bots.respond_to? :each
8
+ bots.each do |bot|
9
+ bot.start
10
+ end
11
+ else
12
+ bots.start
13
+ end
14
+
15
+ unless IRCSocketSelector.started?
16
+ IRCSocketSelector.start!
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,75 @@
1
+ require "socket"
2
+ require "openssl"
3
+
4
+ module Relayer
5
+ class IRCSocket
6
+ def initialize(client, host, port, ssl = false)
7
+ @client = client
8
+ @socket = TCPSocket.new(host, port)
9
+
10
+ if ssl
11
+ @socket = OpenSSL::SSL::SSLSocket.new(@socket)
12
+ @socket.sync_close = true # close both wrapper and irc socket on close
13
+ @socket.connect
14
+ end
15
+
16
+ IRCSocketSelector.add_irc_socket(@socket, @client)
17
+ end
18
+
19
+ def read
20
+ data = @socket.gets
21
+ data = data.sub "\r", ''
22
+
23
+ lines = data.split "\r\n"
24
+
25
+ lines.each do |line|
26
+ @client.process_raw line
27
+ end
28
+ end
29
+
30
+ def send(line)
31
+ @socket.puts line
32
+ end
33
+
34
+ def close
35
+ @socket.close
36
+ end
37
+ end
38
+
39
+ class IRCSocketSelector
40
+ @@sockets = {}
41
+ @@started = false
42
+
43
+ def self.instance
44
+ @@instance ||= IRCSocketSelector.new
45
+ end
46
+
47
+ def self.add_irc_socket(socket, client)
48
+ @@sockets[socket] = client
49
+ end
50
+
51
+ def self.select
52
+ sockets = @@sockets.keys
53
+
54
+ readable, writable, exceptioned = IO.select(sockets, nil, nil)
55
+
56
+ readable.each do |socket|
57
+ client = @@sockets[socket]
58
+ client.readable
59
+ end
60
+ end
61
+
62
+ def self.started?
63
+ @@started
64
+ end
65
+
66
+ def self.start!
67
+ return if @@sockets.empty?
68
+
69
+ @@started = true
70
+ while true
71
+ IRCSocketSelector.select
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module Relayer
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "relayer/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "relayer"
7
+ s.version = Relayer::VERSION
8
+ s.authors = ["Brian Ewing"]
9
+ s.email = ["me@brianewing.co.uk"]
10
+ s.homepage = "http://brianewing.me/pages/relayer"
11
+ s.summary = "Dead simple, high-performance, event-driven IRC library"
12
+ s.description = "Relayer is a dead simple, high-performance, event-driven IRC library"
13
+
14
+ s.rubyforge_project = "relayer"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.license = "LGPL"
22
+
23
+ # specify any dependencies here; for example:s
24
+ # s.add_development_dependency "rspec"
25
+ # s.add_runtime_dependency "rest-client"
26
+ end
data/test.rb ADDED
@@ -0,0 +1,10 @@
1
+ require './lib/relayer'
2
+
3
+ echo = Relayer::IRCClient.new(:hostname => 'irc.alphachat.net', :nick => 'EchoBot', :channels => ['#lobby'])
4
+
5
+ echo.events.channel_msg do |irc, event|
6
+ msg = event[:message].sub('!say ', '')
7
+ irc.message(event[:channel], msg) unless msg.blank?
8
+ end
9
+
10
+ Relayer::start! echo
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: relayer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Ewing
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-03 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Relayer is a dead simple, high-performance, event-driven IRC library
15
+ email:
16
+ - me@brianewing.co.uk
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - lib/relayer.rb
27
+ - lib/relayer/client.rb
28
+ - lib/relayer/event/event.rb
29
+ - lib/relayer/event/message_event.rb
30
+ - lib/relayer/events.rb
31
+ - lib/relayer/irc/channel.rb
32
+ - lib/relayer/irc/user.rb
33
+ - lib/relayer/protocol.rb
34
+ - lib/relayer/relayer.rb
35
+ - lib/relayer/socket.rb
36
+ - lib/relayer/version.rb
37
+ - relayer.gemspec
38
+ - test.rb
39
+ homepage: http://brianewing.me/pages/relayer
40
+ licenses:
41
+ - LGPL
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project: relayer
60
+ rubygems_version: 1.8.24
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Dead simple, high-performance, event-driven IRC library
64
+ test_files: []