shin-irc_cat 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/icat +54 -0
- data/bin/iecho +49 -0
- data/bin/irccat +66 -0
- data/bin/irccat-config +67 -0
- data/lib/irc_cat.rb +9 -0
- data/lib/irc_cat/bot.rb +148 -0
- data/lib/irc_cat/http_server.rb +125 -0
- data/lib/irc_cat/indifferent_access.rb +75 -0
- data/lib/irc_cat/tcp_client.rb +9 -0
- data/lib/irc_cat/tcp_server.rb +38 -0
- data/lib/irc_cat/version.rb +9 -0
- metadata +93 -0
data/bin/icat
ADDED
@@ -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
|
data/bin/iecho
ADDED
@@ -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)
|
data/bin/irccat
ADDED
@@ -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
|
data/bin/irccat-config
ADDED
@@ -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
|
data/lib/irc_cat.rb
ADDED
data/lib/irc_cat/bot.rb
ADDED
@@ -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,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
|
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
|
+
|