shin-irc_cat 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # `cat` for irccat :)
4
+ #
5
+ # Created on 2008-2-17.
6
+ # Copyright (c) 2008. All rights reserved.
7
+
8
+ require 'optparse'
9
+ require 'yaml'
10
+
11
+ OPTIONS = {
12
+ :profile => 'default'
13
+ }
14
+ MANDATORY_OPTIONS = %w( )
15
+
16
+ parser = OptionParser.new do |opts|
17
+ opts.banner = <<BANNER
18
+ icat - just like cat but for irccat
19
+
20
+ Usage: #{File.basename($0)} [options] <files or STDIN>
21
+
22
+ Options are:
23
+ BANNER
24
+ opts.separator ""
25
+ opts.on("-p", "--profile=PROFILE", String,
26
+ "Profile to use (in ~/.irccat.yml).",
27
+ "Default: 'default'") { |OPTIONS[:profile]| }
28
+ opts.on("-h", "--help",
29
+ "Show this help message.") { puts opts; exit }
30
+ opts.parse!(ARGV)
31
+
32
+ if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
33
+ puts opts; exit
34
+ end
35
+ end
36
+
37
+ raise "Can't read configuration file (~/.irccat.yml)" unless File.exists?(File.expand_path("~/.irccat.yml"))
38
+ @config = YAML.load_file(File.expand_path("~/.irccat.yml"))
39
+
40
+ @profile = @config["#{OPTIONS[:profile]}"]
41
+ raise "Can't find profile #{OPTIONS[:profile]} in ~/.irccat.yml" if @profile.nil?
42
+
43
+ require File.dirname(__FILE__) + '/../lib/irc_cat/tcp_client'
44
+
45
+ if ARGV.empty?
46
+ puts "has stdin"
47
+ message = STDIN.read
48
+ IrcCat::TcpClient.notify(@profile['host'], @profile['port'], message)
49
+ else
50
+ ARGV.each do |file|
51
+ raise "Can't read #{file}" unless File.exists?(File.expand_path(file))
52
+ IrcCat::TcpClient.notify(@profile['host'], @profile['port'], File.read(File.expand_path(file)))
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # `echo` for irccat :)
4
+ #
5
+ # Created on 2008-2-17.
6
+ # Copyright (c) 2008. All rights reserved.
7
+
8
+ require 'optparse'
9
+ require 'yaml'
10
+
11
+ OPTIONS = {
12
+ :profile => 'default'
13
+ }
14
+ MANDATORY_OPTIONS = %w( )
15
+
16
+ parser = OptionParser.new do |opts|
17
+ opts.banner = <<BANNER
18
+ iecho - just like echo but for irccat
19
+
20
+ Usage: #{File.basename($0)} [options] text
21
+
22
+ Options are:
23
+ BANNER
24
+ opts.separator ""
25
+ opts.on("-p", "--profile=PROFILE", String,
26
+ "Profile to use (in ~/.irccat.yml).",
27
+ "Default: 'default'") { |OPTIONS[:profile]| }
28
+ opts.on("-h", "--help",
29
+ "Show this help message.") { puts opts; exit }
30
+ opts.parse!(ARGV)
31
+
32
+ if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
33
+ puts opts; exit
34
+ end
35
+ end
36
+
37
+ raise "Can't read configuration file (~/.irccat.yml)" unless File.exists?(File.expand_path("~/.irccat.yml"))
38
+ @config = YAML.load_file(File.expand_path("~/.irccat.yml"))
39
+
40
+ @profile = @config["#{OPTIONS[:profile]}"]
41
+ raise "Can't find profile #{OPTIONS[:profile]} in ~/.irccat.yml" if @profile.nil?
42
+
43
+ raise "Nothing to send" if ARGV.empty?
44
+
45
+ require File.dirname(__FILE__) + '/../lib/irc_cat/tcp_client'
46
+
47
+ message = ARGV.join(' ')
48
+
49
+ IrcCat::TcpClient.notify(@profile['host'], @profile['port'], message)
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2008-2-17.
4
+ # Copyright (c) 2008. All rights reserved.
5
+
6
+ require 'optparse'
7
+ require 'yaml'
8
+
9
+ options = {
10
+ :config_filename => './config.yml'
11
+ }
12
+
13
+ parser = OptionParser.new do |opts|
14
+ opts.banner = <<BANNER
15
+ irc_cat - stdout for irc
16
+
17
+ Usage: #{File.basename($0)} [options]
18
+
19
+ Options are:
20
+ BANNER
21
+
22
+ opts.separator ""
23
+
24
+ opts.on("-c", "--config=CONFIG", String, "Path to the config file.", "Default: ./config.yml") do |filename|
25
+ options[:config_filename] = filename
26
+ end
27
+
28
+ opts.on("-h", "--help", "Show this help message.") {
29
+ puts opts
30
+ exit
31
+ }
32
+ end
33
+
34
+ parser.parse!(ARGV)
35
+
36
+ raise "Can't read configuration file (#{options[:config_filename]})" unless File.exists?(options[:config_filename])
37
+ config = YAML.load_file(options[:config_filename])
38
+
39
+ lib_dir = File.dirname(__FILE__) + '/../lib/'
40
+ require lib_dir + '/irc_cat'
41
+
42
+ threads = []
43
+ puts "irccat #{IrcCat::VERSION::STRING} (http://irccat.rubyforge.org/)"
44
+
45
+ Thread.abort_on_exception = true
46
+
47
+ irc_config = config['irc']
48
+ IrcCat::Bot.run(irc_config['host'], irc_config['port'], irc_config['nick'], irc_config['pass']) do |bot|
49
+ puts "Connecting..."
50
+ irc_config['channels'].each do |channel|
51
+ name, pass = channel.split(" ")
52
+ bot.join_channel(name, pass)
53
+ end
54
+
55
+ tcp_config = config['tcp']
56
+ if tcp_config['enabled']
57
+ require lib_dir + 'irc_cat/tcp_server'
58
+ IrcCat::TcpServer.run(bot, tcp_config)
59
+ end
60
+
61
+ http_config = config['http']
62
+ if http_config['enabled']
63
+ require lib_dir + 'irc_cat/http_server'
64
+ IrcCat::HttpServer.run(bot, http_config)
65
+ end
66
+ end
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Configuration generator for irccat
4
+ #
5
+ # Created on 2008-2-17.
6
+ # Copyright (c) 2008. All rights reserved.
7
+
8
+ require 'rubygems'
9
+ require 'optparse'
10
+ require 'yaml'
11
+ require 'highline/import'
12
+
13
+ def irccat_config
14
+ config = Hash.new
15
+ puts "== IRC Configuration"
16
+ config['irc'] = Hash.new
17
+ config['irc']['host'] = ask("Hostname: ") { |q| q.default = "irc.freenode.net" }
18
+ config['irc']['port'] = ask("Port: ") { |q| q.default = "6667" }
19
+ config['irc']['nick'] = ask("Nickname: ") { |q| q.default = "irc_cat" }
20
+ config['irc']['channel'] = ask("Channel: ") { |q| q.default = "#irc_cat" }
21
+ puts "\n== HTTP Configuration"
22
+ config['http'] = Hash.new
23
+ if agree("Enable HTTP? ", true); config['http']['enabled'] = true; else; config['http']['enabled'] = false; end
24
+ if config['http']['enabled']
25
+ config['http']['host'] = ask("Hostname: ") { |q| q.default = "0.0.0.0" }
26
+ config['http']['port'] = ask("Port: ") { |q| q.default = "3489" }
27
+ if agree("Enable Basic HTTP send? ", true); config['http']['send'] = true; else; config['http']['send'] = false; end
28
+ if agree("Enable Github? ", true); config['http']['github'] = true; else; config['http']['github'] = false; end
29
+ end
30
+ puts "\n== TCP Configuration"
31
+ config['tcp'] = Hash.new
32
+ if agree("Enable TCP? ", true); config['tcp']['enabled'] = true; else; config['tcp']['enabled'] = false; end
33
+ if config['tcp']['enabled']
34
+ config['tcp']['host'] = ask("Hostname: ") { |q| q.default = "0.0.0.0" }
35
+ config['tcp']['port'] = ask("Port: ") { |q| q.default = "5678" }
36
+ config['tcp']['size'] = ask("Socket size: ") { |q| q.default = "400" }
37
+ end
38
+ puts "\n== Final steps"
39
+ file = ask("Configuration filename: ") { |q| q.default = "config.yml" }
40
+ puts "Warning: File exists! If you choose yes now the file will be overwritten" if File.exists?(File.expand_path(file))
41
+ if agree("Write configuration file? ", true)
42
+ File.open(File.expand_path(file), "w") { |yfile| YAML.dump(config, yfile) }
43
+ else
44
+ exit
45
+ end
46
+ end
47
+
48
+ def help
49
+ puts "irccat-config - configuration generator for irccat\n"
50
+ puts "Usage: #{File.basename($0)} irccat|client > configfile.yml"
51
+ exit 0
52
+ end
53
+
54
+ if ARGV[0].nil?
55
+ help
56
+ else
57
+ case ARGV[0]
58
+ when 'irccat'
59
+ irccat_config
60
+ when 'client'
61
+ #client_config
62
+ # TODO: todo :D
63
+ help
64
+ else
65
+ help
66
+ end
67
+ end
@@ -0,0 +1,9 @@
1
+ require 'socket'
2
+ require 'rack'
3
+ require 'json'
4
+
5
+ current_dir = File.dirname(__FILE__)
6
+
7
+ require current_dir + '/irc_cat/version'
8
+ require current_dir + '/irc_cat/indifferent_access'
9
+ require current_dir + '/irc_cat/bot'
@@ -0,0 +1,148 @@
1
+ # This is the bot.
2
+ # Original code by Madx (yapok.org)
3
+
4
+ module IrcCat
5
+ class Bot
6
+ def self.run(host, port, nick, nick_pass = nil, &block)
7
+ new(host, port, nick, nick_pass, &block).run
8
+ end
9
+
10
+ def initialize(host, port, nick, nick_pass, &block)
11
+ @host, @port, @nick, @nick_pass = host, port, nick, nick_pass
12
+ @online, @connect_block = false, block
13
+
14
+ @realname = "irccat v#{VERSION::STRING}"
15
+ @refresh_rate = 10
16
+ @channels = {}
17
+
18
+ puts "Connecting to IRC #{@host}:#{@port}"
19
+ end
20
+
21
+ def online?
22
+ @online
23
+ end
24
+
25
+ def run(&block)
26
+ @socket = TCPSocket.open(@host, @port)
27
+ login
28
+
29
+ trap(:INT) {
30
+ puts "Bye bye."
31
+ sexit('God^WConsole killed me')
32
+ sleep 1
33
+ @socket.close
34
+ exit
35
+ }
36
+
37
+ threads = []
38
+
39
+ threads << Thread.new {
40
+ begin
41
+ while line = @socket.gets do
42
+ # Remove all formatting
43
+ line.gsub!(/[\x02\x1f\x16\x0f]/,'')
44
+ # Remove CTCP ASCII
45
+ line.gsub!(/\001/,'')
46
+ # Send to event handler
47
+ handle line
48
+ # Handle Pings from Server
49
+ sendln "PONG #{$1}" if /^PING\s(.*)/ =~ line
50
+ end
51
+ rescue EOFError
52
+ err 'Server Reset Connection'
53
+ rescue Exception => e
54
+ err e
55
+ end
56
+ }
57
+ threads << Thread.new {
58
+ }
59
+ threads.each { |th| th.join }
60
+ end
61
+
62
+ # Announces states
63
+ def announce(msg)
64
+ @channels.each do |channel,key|
65
+ say(channel, msg)
66
+ end
67
+ end
68
+
69
+ # Say something funkeh
70
+ def say(chan, msg)
71
+ sendln "PRIVMSG #{chan} :#{msg}"
72
+ end
73
+
74
+ def topic(chan, msg)
75
+ sendln "TOPIC #{chan} :#{msg}"
76
+ end
77
+
78
+ # Send EXIT
79
+ def sexit(message = 'quit')
80
+ sendln "QUIT :#{message}"
81
+ end
82
+
83
+ # Sends a message to the server
84
+
85
+ def sendln(cmd)
86
+ puts "Send: #{cmd}"
87
+ if cmd.size <= 510
88
+ @socket.write("#{cmd}\r\n")
89
+ STDOUT.flush
90
+ else
91
+ end
92
+ end
93
+
94
+ # Handle a received message
95
+
96
+ def handle(line)
97
+ puts "Got: #{line}"
98
+ case line
99
+ when /^:.+\s376/
100
+ puts "We're online"
101
+ @online = true
102
+ @connect_block.call(self) if @connect_block
103
+ when /^:.+KICK (#[^\s]+)/
104
+ auto_rejoin($1)
105
+ end
106
+ end
107
+
108
+ # Automatic events
109
+
110
+ def join_channel(channel, key)
111
+ @channels[channel] = key
112
+ $stdout.flush
113
+ if key
114
+ sendln "JOIN #{channel} #{key}"
115
+ else
116
+ sendln "JOIN #{channel}"
117
+ end
118
+ end
119
+
120
+ def auto_rejoin(channel)
121
+ join_channel(channel, @channels[channel])
122
+ end
123
+
124
+ def login
125
+ begin
126
+ sendln "NICK #{@nick}"
127
+ sendln "USER irc_cat . . :#{@realname}"
128
+ if @nick_pass
129
+ puts "logging in to NickServ"
130
+ sendln "PRIVMSG NICKSERV :identify #{@nick_pass}"
131
+ end
132
+ rescue Exception => e
133
+ err e
134
+ end
135
+ end
136
+
137
+ # Logging methods
138
+
139
+ def log(str)
140
+ $stdout.puts "DEBUG: #{str}"
141
+ end
142
+
143
+ def err(exception)
144
+ $stderr.puts "ERROR: #{exception}"
145
+ $stderr.puts "TRACE: #{exception.backtrace}"
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,125 @@
1
+ module IrcCat
2
+ class HttpServer
3
+ class Unauthorized < StandardError; end
4
+ class BadRequest < StandardError; end
5
+
6
+ def self.run(bot, config)
7
+ new(bot, config).run
8
+ end
9
+
10
+ def initialize(bot, config)
11
+ @bot, @config = bot, config
12
+ end
13
+
14
+ def run
15
+ host, port = @config['host'], @config['port']
16
+ if handler = Rack::Handler.get(handler_name)
17
+ Thread.new {
18
+ handler.run(app, :Host => host, :Port => port)
19
+ }
20
+ else
21
+ raise "Could not find a valid Rack handler for #{handler_name.inspect}"
22
+ end
23
+ end
24
+
25
+ def handler_name
26
+ @config["server"] || "mongrel"
27
+ end
28
+
29
+ def app
30
+ endpoint = self
31
+ builder = Rack::Builder.new
32
+ if requires_auth?
33
+ builder.use Rack::Auth::Basic, "irccat" do |user,pass|
34
+ auth_user == user && auth_pass == pass
35
+ end
36
+ end
37
+ if prefix = @config["prefix"]
38
+ builder.map prefix do
39
+ run endpoint
40
+ end
41
+ else
42
+ builder.run endpoint
43
+ end
44
+ builder
45
+ end
46
+
47
+ def auth_user
48
+ @config["user"]
49
+ end
50
+
51
+ def auth_pass
52
+ @config["pass"]
53
+ end
54
+
55
+ def requires_auth?
56
+ if auth_user
57
+ if auth_pass
58
+ if auth_pass.empty?
59
+ raise "Empty HTTP password in config"
60
+ end
61
+ true
62
+ end
63
+ end
64
+ end
65
+
66
+ def call(env)
67
+ request = Rack::Request.new(env)
68
+
69
+ case request.path_info
70
+ when "/"
71
+ [200, {"Content-Type" => "text/plain"}, "OK"]
72
+ when "/topic"
73
+ message = request.params["message"]
74
+ if channel = request.params["channel"]
75
+ @bot.join_channel("##{channel}", request.params["key"])
76
+ @bot.topic("##{channel}", message)
77
+ else
78
+ raise "Need a channel"
79
+ end
80
+
81
+ [200, {"Content-Type" => "text/plain"}, "OK"]
82
+ when "/send"
83
+ raise "Send support is not enabled" unless @config["send"]
84
+
85
+ message = request.params["message"]
86
+ if channel = request.params["channel"]
87
+ @bot.join_channel("##{channel}", request.params["key"])
88
+ @bot.say("##{channel}", message)
89
+ elsif nick = request.params["nick"]
90
+ @bot.say(nick, message)
91
+ else
92
+ raise "Unknown action"
93
+ end
94
+
95
+ [200, {"Content-Type" => "text/plain"}, "OK"]
96
+ when "/github"
97
+ raise "GitHub support is not enabled" unless @config["github"]
98
+
99
+ data = JSON.parse(request.POST['payload'])
100
+
101
+ repository = data['repository']['name']
102
+ data['commits'].each do |commit|
103
+ topic = commit['message'].split("\n").first
104
+ ref = commit['id'][0,7]
105
+ author = commit['author']['name']
106
+ @bot.announce "#{topic} - #{ref} - #{author}"
107
+ end
108
+
109
+ [200, {"Content-Type" => "text/plain"}, "OK"]
110
+ else
111
+ [404, {}, ""]
112
+ end
113
+ rescue Unauthorized
114
+ [401, {'WWW-Authenticate' => %(Basic realm="IrcCat")}, 'Authorization Required']
115
+ rescue BadRequest
116
+ [400, {}, 'Bad Request']
117
+ rescue Exception => e
118
+ puts "Got an error: #{e.class}, #{e.message}"
119
+ e.backtrace.each do |b|
120
+ puts "- #{b}"
121
+ end
122
+ [503, {}, "ERR"]
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,75 @@
1
+ # this class has dubious semantics and we only have it so that
2
+ # people can write params[:key] instead of params['key']
3
+
4
+ class HashWithIndifferentAccess < Hash
5
+ def initialize(constructor = {})
6
+ if constructor.is_a?(Hash)
7
+ super()
8
+ update(constructor)
9
+ else
10
+ super(constructor)
11
+ end
12
+ end
13
+
14
+ def default(key = nil)
15
+ if key.is_a?(Symbol) && include?(key = key.to_s)
16
+ self[key]
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
23
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
24
+
25
+ def []=(key, value)
26
+ regular_writer(convert_key(key), convert_value(value))
27
+ end
28
+
29
+ def update(other_hash)
30
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
31
+ self
32
+ end
33
+
34
+ alias_method :merge!, :update
35
+
36
+ def key?(key)
37
+ super(convert_key(key))
38
+ end
39
+
40
+ alias_method :include?, :key?
41
+ alias_method :has_key?, :key?
42
+ alias_method :member?, :key?
43
+
44
+ def fetch(key, *extras)
45
+ super(convert_key(key), *extras)
46
+ end
47
+
48
+ def values_at(*indices)
49
+ indices.collect {|key| self[convert_key(key)]}
50
+ end
51
+
52
+ def dup
53
+ HashWithIndifferentAccess.new(self)
54
+ end
55
+
56
+ def merge(hash)
57
+ self.dup.update(hash)
58
+ end
59
+
60
+ def delete(key)
61
+ super(convert_key(key))
62
+ end
63
+
64
+ def stringify_keys!; self end
65
+ def symbolize_keys!; self end
66
+
67
+ protected
68
+ def convert_key(key)
69
+ key.kind_of?(Symbol) ? key.to_s : key
70
+ end
71
+ def convert_value(value)
72
+ value.is_a?(Hash) ? value.with_indifferent_access : value
73
+ end
74
+ end
75
+
@@ -0,0 +1,9 @@
1
+ require 'socket'
2
+ module IrcCat
3
+ class TcpClient
4
+ def self.notify(ip,port,message)
5
+ socket = TCPSocket.open(ip,port)
6
+ socket.write(message + "\r\n")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ # The TCP Server
2
+ module IrcCat
3
+ class TcpServer
4
+ def self.run(bot, config)
5
+ new(bot, config).run
6
+ end
7
+
8
+ def initialize(bot, config)
9
+ @bot, @config = bot, config
10
+ end
11
+
12
+ def run
13
+ Thread.new do
14
+ socket = TCPserver.new(ip, port)
15
+ puts "Starting TCP (#{ip}:#{port})"
16
+
17
+ loop do
18
+ Thread.start(socket.accept) do |s|
19
+ str = s.recv(@config['size'])
20
+ sstr = str.split(/\n/)
21
+ sstr.each do |l|
22
+ @bot.announce("#{l}")
23
+ end
24
+ s.close
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def ip
31
+ @config["ip"] || '127.0.0.1'
32
+ end
33
+
34
+ def port
35
+ @config["port"] || '8080'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ module IrcCat #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 4
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shin-irc_cat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Jordan Bracco
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-21 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rake
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: irccat is like `cat`, but here, the STDOUT is an IRC channel.
46
+ email: jordan@bracco.name
47
+ executables:
48
+ - icat
49
+ - iecho
50
+ - irccat
51
+ - irccat-config
52
+ extensions: []
53
+
54
+ extra_rdoc_files: []
55
+
56
+ files:
57
+ - lib/irc_cat/bot.rb
58
+ - lib/irc_cat/http_server.rb
59
+ - lib/irc_cat/indifferent_access.rb
60
+ - lib/irc_cat/tcp_client.rb
61
+ - lib/irc_cat/tcp_server.rb
62
+ - lib/irc_cat/version.rb
63
+ - lib/irc_cat.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/webs/irccat
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options: []
70
+
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.5
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: irccat is like `cat`, but here, the STDOUT is an IRC channel.
92
+ test_files: []
93
+