net-irc 0.0.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/ChangeLog +0 -0
- data/README +92 -0
- data/Rakefile +131 -0
- data/examples/lig.rb +276 -0
- data/examples/tig.rb +446 -0
- data/examples/wig.rb +132 -0
- data/lib/net/irc.rb +844 -0
- metadata +72 -0
data/examples/tig.rb
ADDED
@@ -0,0 +1,446 @@
|
|
1
|
+
#!/usr/bin/env ruby -Ku
|
2
|
+
=begin
|
3
|
+
|
4
|
+
# tig.rb
|
5
|
+
|
6
|
+
Ruby version of TwitterIrcGateway
|
7
|
+
( http://www.misuzilla.org/dist/net/twitterircgateway/ )
|
8
|
+
|
9
|
+
|
10
|
+
## Client opts
|
11
|
+
|
12
|
+
Options specified by after irc realname.
|
13
|
+
|
14
|
+
Configuration example for tiarra ( http://coderepos.org/share/wiki/Tiarra ).
|
15
|
+
|
16
|
+
twitter {
|
17
|
+
host: localhost
|
18
|
+
port: 16668
|
19
|
+
name: username@example.com athack
|
20
|
+
password: password on twitter
|
21
|
+
in-encoding: utf8
|
22
|
+
out-encoding: utf8
|
23
|
+
}
|
24
|
+
|
25
|
+
### athack
|
26
|
+
|
27
|
+
If `athack` client options specified,
|
28
|
+
all nick in join message is leading with @.
|
29
|
+
|
30
|
+
So if you complemente nicks (ex. irssi),
|
31
|
+
it's good for twitter like reply command (@nick).
|
32
|
+
|
33
|
+
In this case, you will see torrent of join messages after connected,
|
34
|
+
because NAMES list can't send @ leading nick (it interpreted op.)
|
35
|
+
|
36
|
+
## Licence
|
37
|
+
|
38
|
+
Ruby's by cho45
|
39
|
+
|
40
|
+
=end
|
41
|
+
|
42
|
+
$LOAD_PATH << "lib"
|
43
|
+
$LOAD_PATH << "../lib"
|
44
|
+
|
45
|
+
$KCODE = "u" # json use this
|
46
|
+
|
47
|
+
require "rubygems"
|
48
|
+
require "net/http"
|
49
|
+
require "net/irc"
|
50
|
+
require "uri"
|
51
|
+
require "json"
|
52
|
+
require "socket"
|
53
|
+
require "time"
|
54
|
+
require "logger"
|
55
|
+
require "yaml"
|
56
|
+
require "pathname"
|
57
|
+
require "digest/md5"
|
58
|
+
|
59
|
+
Net::HTTP.version_1_2
|
60
|
+
|
61
|
+
class TwitterIrcGateway < Net::IRC::Server::Session
|
62
|
+
def server_name
|
63
|
+
"twittergw"
|
64
|
+
end
|
65
|
+
|
66
|
+
def server_version
|
67
|
+
"0.0.0"
|
68
|
+
end
|
69
|
+
|
70
|
+
def main_channel
|
71
|
+
"#twitter"
|
72
|
+
end
|
73
|
+
|
74
|
+
def api_base
|
75
|
+
@api_base ||= URI("http://twitter.com/")
|
76
|
+
end
|
77
|
+
|
78
|
+
class ApiFailed < StandardError; end
|
79
|
+
|
80
|
+
def initialize(*args)
|
81
|
+
super
|
82
|
+
@groups = {}
|
83
|
+
@channels = [] # join channels (groups)
|
84
|
+
@config = Pathname.new(ENV["HOME"]) + ".tig"
|
85
|
+
load_config
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_user(m)
|
89
|
+
super
|
90
|
+
post @mask, JOIN, main_channel
|
91
|
+
@real, @opts = @real.split(/\s/)
|
92
|
+
@opts ||= []
|
93
|
+
@log.info "Client Options: #{@opts.inspect}"
|
94
|
+
|
95
|
+
@timeline = []
|
96
|
+
Thread.start do
|
97
|
+
loop do
|
98
|
+
begin
|
99
|
+
check_friends
|
100
|
+
rescue ApiFailed => e
|
101
|
+
@log.error e.inspect
|
102
|
+
rescue Exception => e
|
103
|
+
puts e
|
104
|
+
puts e.backtrace
|
105
|
+
end
|
106
|
+
sleep 10 * 60
|
107
|
+
end
|
108
|
+
end
|
109
|
+
sleep 3
|
110
|
+
Thread.start do
|
111
|
+
loop do
|
112
|
+
begin
|
113
|
+
check_timeline
|
114
|
+
# check_direct_messages
|
115
|
+
rescue ApiFailed => e
|
116
|
+
@log.error e.inspect
|
117
|
+
rescue Exception => e
|
118
|
+
puts e
|
119
|
+
puts e.backtrace
|
120
|
+
end
|
121
|
+
sleep 90
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def on_privmsg(m)
|
127
|
+
retry_count = 3
|
128
|
+
ret = nil
|
129
|
+
target, message = *m.params
|
130
|
+
begin
|
131
|
+
if target =~ /^#/
|
132
|
+
ret = api("statuses/update.json", {"status" => message})
|
133
|
+
else
|
134
|
+
# direct message
|
135
|
+
ret = api("direct_messages/new.json", {"user" => target, "text" => message})
|
136
|
+
end
|
137
|
+
raise ApiFailed, "api failed" unless ret
|
138
|
+
log "Status Updated"
|
139
|
+
rescue => e
|
140
|
+
@log.error [retry_count, e.inspect].inspect
|
141
|
+
if retry_count > 0
|
142
|
+
retry_count -= 1
|
143
|
+
@log.debug "Retry to setting status..."
|
144
|
+
retry
|
145
|
+
else
|
146
|
+
log "Some Error Happened on Sending #{message}. #{e}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def on_whois(m)
|
152
|
+
nick = m.params[0]
|
153
|
+
f = (@friends || []).find {|i| i["screen_name"] == nick }
|
154
|
+
if f
|
155
|
+
post nil, RPL_WHOISUSER, nick, nick, nick, api_base.host, "*", NKF.nkf("-j", "#{f["name"]} / #{f["description"]}")
|
156
|
+
post nil, RPL_WHOISSERVER, nick, api_base.host, api_base.to_s
|
157
|
+
post nil, RPL_WHOISIDLE, nick, "0", "seconds idle"
|
158
|
+
post nil, RPL_ENDOFWHOIS, nick, "End of WHOIS list"
|
159
|
+
else
|
160
|
+
post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def on_who(m)
|
165
|
+
channel = m.params[0]
|
166
|
+
case
|
167
|
+
when channel == main_channel
|
168
|
+
# "<channel> <user> <host> <server> <nick>
|
169
|
+
# ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
|
170
|
+
# :<hopcount> <real name>"
|
171
|
+
@friends.each do |f|
|
172
|
+
user = nick = f["screen_name"]
|
173
|
+
host = serv = api_base.host
|
174
|
+
real = f["name"]
|
175
|
+
post nil, RPL_WHOREPLY, channel, user, host, serv, nick, "H", "0 #{real}"
|
176
|
+
end
|
177
|
+
post nil, RPL_ENDOFWHO, channel
|
178
|
+
when @groups.key?(channel)
|
179
|
+
@groups[channel].each do |name|
|
180
|
+
f = @friends.find {|i| i["screen_name"] == name }
|
181
|
+
user = nick = f["screen_name"]
|
182
|
+
host = serv = api_base.host
|
183
|
+
real = f["name"]
|
184
|
+
post nil, RPL_WHOREPLY, channel, user, host, serv, nick, "H", "0 #{real}"
|
185
|
+
end
|
186
|
+
post nil, RPL_ENDOFWHO, channel
|
187
|
+
else
|
188
|
+
post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def on_join(m)
|
193
|
+
channels = m.params[0].split(/\s*,\s*/)
|
194
|
+
channels.each do |channel|
|
195
|
+
next if channel == main_channel
|
196
|
+
|
197
|
+
@channels << channel
|
198
|
+
@channels.uniq!
|
199
|
+
post "#{@nick}!#{@nick}@#{api_base.host}", JOIN, channel
|
200
|
+
save_config
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def on_part(m)
|
205
|
+
channel = m.params[0]
|
206
|
+
return if channel == main_channel
|
207
|
+
|
208
|
+
@channels.delete(channel)
|
209
|
+
post @nick, PART, channel, "Ignore group #{channel}, but setting is alive yet."
|
210
|
+
end
|
211
|
+
|
212
|
+
def on_invite(m)
|
213
|
+
nick, channel = *m.params
|
214
|
+
return if channel == main_channel
|
215
|
+
|
216
|
+
if (@friends || []).find {|i| i["screen_name"] == nick }
|
217
|
+
((@groups[channel] ||= []) << nick).uniq!
|
218
|
+
post "#{nick}!#{nick}@#{api_base.host}", JOIN, channel
|
219
|
+
save_config
|
220
|
+
else
|
221
|
+
post ERR_NOSUCHNICK, nil, nick, "No such nick/channel"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def on_kick(m)
|
226
|
+
channel, nick, mes = *m.params
|
227
|
+
return if channel == main_channel
|
228
|
+
|
229
|
+
if (@friends || []).find {|i| i["screen_name"] == nick }
|
230
|
+
(@groups[channel] ||= []).delete(nick)
|
231
|
+
post nick, PART, channel
|
232
|
+
save_config
|
233
|
+
else
|
234
|
+
post ERR_NOSUCHNICK, nil, nick, "No such nick/channel"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
def check_timeline
|
240
|
+
first = true unless @prev_time
|
241
|
+
@prev_time = Time.at(0) if first
|
242
|
+
api("statuses/friends_timeline.json", {"since" => [@prev_time.httpdate] }).reverse_each do |s|
|
243
|
+
nick = s["user"]["screen_name"]
|
244
|
+
mesg = s["text"]
|
245
|
+
time = Time.parse(s["created_at"]) rescue Time.now
|
246
|
+
m = { """ => "\"", "<"=> "<", ">"=> ">", "&"=> "&", "\n" => " "}
|
247
|
+
mesg.gsub!(/(#{m.keys.join("|")})/) { m[$1] }
|
248
|
+
|
249
|
+
digest = Digest::MD5.hexdigest("#{nick}::#{mesg}")
|
250
|
+
unless @timeline.include?(digest)
|
251
|
+
@timeline << digest
|
252
|
+
@log.debug [nick, mesg, time].inspect
|
253
|
+
if nick == @nick # 自分のときは topic に
|
254
|
+
post nick, TOPIC, main_channel, mesg
|
255
|
+
else
|
256
|
+
message(nick, main_channel, mesg)
|
257
|
+
end
|
258
|
+
@groups.each do |channel,members|
|
259
|
+
if members.include?(nick)
|
260
|
+
message(nick, channel, mesg)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
@timeline = @timeline.last(100)
|
266
|
+
@prev_time = Time.now
|
267
|
+
end
|
268
|
+
|
269
|
+
def check_direct_messages
|
270
|
+
first = true unless @prev_time_d
|
271
|
+
@prev_time_d = Time.now if first
|
272
|
+
api("direct_messages.json", {"since" => [@prev_time_d.httpdate] }).reverse_each do |s|
|
273
|
+
nick = s["sender_screen_name"]
|
274
|
+
mesg = s["text"]
|
275
|
+
time = Time.parse(s["created_at"])
|
276
|
+
@log.debug [nick, mesg, time].inspect
|
277
|
+
message(nick, @nick, mesg)
|
278
|
+
end
|
279
|
+
@prev_time_d = Time.now
|
280
|
+
end
|
281
|
+
|
282
|
+
def check_friends
|
283
|
+
first = true unless @friends
|
284
|
+
@friends ||= []
|
285
|
+
friends = api("statuses/friends.json")
|
286
|
+
if first && !@opts.include?("athack")
|
287
|
+
@friends = friends
|
288
|
+
post nil, RPL_NAMREPLY, server_name, @nick, "=", main_channel, @friends.map{|i| i["screen_name"] }.join(" ")
|
289
|
+
post nil, RPL_ENDOFNAMES, server_name, @nick, main_channel, "End of NAMES list"
|
290
|
+
else
|
291
|
+
prv_friends = @friends.map {|i| i["screen_name"] }
|
292
|
+
now_friends = friends.map {|i| i["screen_name"] }
|
293
|
+
(now_friends - prv_friends).each do |join|
|
294
|
+
join = "@#{join}" if @opts.include?("athack")
|
295
|
+
post "#{join}!#{join}@#{api_base.host}", JOIN, main_channel
|
296
|
+
end
|
297
|
+
(prv_friends - now_friends).each do |part|
|
298
|
+
part = "@#{part}" if @opts.include?("athack")
|
299
|
+
post "#{part}!#{part}@#{api_base.host}", PART, main_channel, ""
|
300
|
+
end
|
301
|
+
@friends = friends
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def save_config
|
306
|
+
config = {
|
307
|
+
:channels => @channels,
|
308
|
+
:groups => @groups,
|
309
|
+
}
|
310
|
+
@config.open("w") do |f|
|
311
|
+
YAML.dump(config, f)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def load_config
|
316
|
+
@config.open do |f|
|
317
|
+
config = YAML.load(f)
|
318
|
+
@channels = config[:channels]
|
319
|
+
@groups = config[:groups]
|
320
|
+
end
|
321
|
+
rescue Errno::ENOENT
|
322
|
+
end
|
323
|
+
|
324
|
+
def api(path, q={})
|
325
|
+
ret = {}
|
326
|
+
q["source"] = "tigrb"
|
327
|
+
q = q.inject([]) {|r,(k,v)| v.inject(r) {|r,i| r << "#{k}=#{URI.escape(i, /[^-.!~*'()\w]/n)}" } }.join("&")
|
328
|
+
uri = api_base + "/#{path}?#{q}"
|
329
|
+
@log.debug uri.inspect
|
330
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
331
|
+
header = {
|
332
|
+
'Authorization' => "Basic " + ["#{@real}:#{@pass}"].pack("m"),
|
333
|
+
}
|
334
|
+
case path
|
335
|
+
when "statuses/update.json", "direct_messages/new.json"
|
336
|
+
ret = http.post(uri.request_uri, q, header)
|
337
|
+
else
|
338
|
+
ret = http.get(uri.request_uri, header)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
@log.debug ret.inspect
|
342
|
+
case ret.code
|
343
|
+
when "200"
|
344
|
+
JSON.parse(ret.body)
|
345
|
+
when "304"
|
346
|
+
[]
|
347
|
+
else
|
348
|
+
raise ApiFailed, "Server Returned #{ret.code}"
|
349
|
+
end
|
350
|
+
rescue Errno::ETIMEDOUT, JSON::ParserError, IOError, Timeout::Error, Errno::ECONNRESET => e
|
351
|
+
raise ApiFailed, e.inspect
|
352
|
+
end
|
353
|
+
|
354
|
+
def message(sender, target, str)
|
355
|
+
# str.gsub!(/&#(x)?([0-9a-f]+);/i) do |m|
|
356
|
+
# [$1 ? $2.hex : $2.to_i].pack("U")
|
357
|
+
# end
|
358
|
+
str = untinyurl(str)
|
359
|
+
sender = "#{sender}!#{sender}@#{api_base.host}"
|
360
|
+
post sender, PRIVMSG, target, str
|
361
|
+
end
|
362
|
+
|
363
|
+
def log(str)
|
364
|
+
str.gsub!(/\n/, " ")
|
365
|
+
post server_name, NOTICE, main_channel, str
|
366
|
+
end
|
367
|
+
|
368
|
+
def untinyurl(text)
|
369
|
+
text.gsub(%r|http://tinyurl.com/[0-9a-z=]+|i) {|m|
|
370
|
+
uri = URI(m)
|
371
|
+
Net::HTTP.start(uri.host, uri.port) {|http|
|
372
|
+
http.head(uri.request_uri)["Location"]
|
373
|
+
}
|
374
|
+
}
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
if __FILE__ == $0
|
379
|
+
require "optparse"
|
380
|
+
|
381
|
+
opts = {
|
382
|
+
:port => 16668,
|
383
|
+
:host => "localhost",
|
384
|
+
:debug => false,
|
385
|
+
:log => nil,
|
386
|
+
:debug => false,
|
387
|
+
}
|
388
|
+
|
389
|
+
OptionParser.new do |parser|
|
390
|
+
parser.instance_eval do
|
391
|
+
self.banner = <<-EOB.gsub(/^\t+/, "")
|
392
|
+
Usage: #{$0} [opts]
|
393
|
+
|
394
|
+
EOB
|
395
|
+
|
396
|
+
separator ""
|
397
|
+
|
398
|
+
separator "Options:"
|
399
|
+
on("-p", "--port [PORT=#{opts[:port]}]", "listen port number") do |port|
|
400
|
+
opts[:port] = port
|
401
|
+
end
|
402
|
+
|
403
|
+
on("-h", "--host [HOST=#{opts[:host]}]", "listen host") do |host|
|
404
|
+
opts[:host] = host
|
405
|
+
end
|
406
|
+
|
407
|
+
on("-l", "--log LOG", "log file") do |log|
|
408
|
+
opts[:log] = log
|
409
|
+
end
|
410
|
+
|
411
|
+
on("--debug", "Enable debug mode") do |debug|
|
412
|
+
opts[:log] = $stdout
|
413
|
+
opts[:debug] = true
|
414
|
+
end
|
415
|
+
|
416
|
+
parse!(ARGV)
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
opts[:logger] = Logger.new(opts[:log], "daily")
|
421
|
+
opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
|
422
|
+
|
423
|
+
def daemonize(debug=false)
|
424
|
+
return yield if $DEBUG || debug
|
425
|
+
Process.fork do
|
426
|
+
Process.setsid
|
427
|
+
Dir.chdir "/"
|
428
|
+
trap("SIGINT") { exit! 0 }
|
429
|
+
trap("SIGTERM") { exit! 0 }
|
430
|
+
trap("SIGHUP") { exit! 0 }
|
431
|
+
File.open("/dev/null") {|f|
|
432
|
+
STDIN.reopen f
|
433
|
+
STDOUT.reopen f
|
434
|
+
STDERR.reopen f
|
435
|
+
}
|
436
|
+
yield
|
437
|
+
end
|
438
|
+
exit! 0
|
439
|
+
end
|
440
|
+
|
441
|
+
daemonize(opts[:debug]) do
|
442
|
+
Net::IRC::Server.new(opts[:host], opts[:port], TwitterIrcGateway, opts).start
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
|
data/examples/wig.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
=begin
|
3
|
+
|
4
|
+
# wig.rb
|
5
|
+
|
6
|
+
Wassr IRC Gateway
|
7
|
+
|
8
|
+
|
9
|
+
## Client opts
|
10
|
+
|
11
|
+
Options specified by after irc realname.
|
12
|
+
|
13
|
+
Configuration example for tiarra ( http://coderepos.org/share/wiki/Tiarra ).
|
14
|
+
|
15
|
+
wassr {
|
16
|
+
host: localhost
|
17
|
+
port: 16670
|
18
|
+
name: username@example.com athack
|
19
|
+
password: password on wassr
|
20
|
+
in-encoding: utf8
|
21
|
+
out-encoding: utf8
|
22
|
+
}
|
23
|
+
|
24
|
+
### athack
|
25
|
+
|
26
|
+
If `athack` client options specified,
|
27
|
+
all nick in join message is leading with @.
|
28
|
+
|
29
|
+
So if you complemente nicks (ex. irssi),
|
30
|
+
it's good for twitter like reply command (@nick).
|
31
|
+
|
32
|
+
In this case, you will see torrent of join messages after connected,
|
33
|
+
because NAMES list can't send @ leading nick (it interpreted op.)
|
34
|
+
|
35
|
+
## Licence
|
36
|
+
|
37
|
+
Ruby's by cho45
|
38
|
+
|
39
|
+
=end
|
40
|
+
|
41
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
42
|
+
|
43
|
+
require "tig.rb"
|
44
|
+
|
45
|
+
class WassrIrcGateway < TwitterIrcGateway
|
46
|
+
def server_name
|
47
|
+
"wassrgw"
|
48
|
+
end
|
49
|
+
|
50
|
+
def server_version
|
51
|
+
"0.0.0"
|
52
|
+
end
|
53
|
+
|
54
|
+
def main_channel
|
55
|
+
"#wassr"
|
56
|
+
end
|
57
|
+
|
58
|
+
def api_base
|
59
|
+
@api_base ||= URI("http://api.wassr.jp/")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if __FILE__ == $0
|
64
|
+
require "optparse"
|
65
|
+
|
66
|
+
opts = {
|
67
|
+
:port => 16670,
|
68
|
+
:host => "localhost",
|
69
|
+
:debug => false,
|
70
|
+
:log => nil,
|
71
|
+
:debug => false,
|
72
|
+
}
|
73
|
+
|
74
|
+
OptionParser.new do |parser|
|
75
|
+
parser.instance_eval do
|
76
|
+
self.banner = <<-EOB.gsub(/^\t+/, "")
|
77
|
+
Usage: #{$0} [opts]
|
78
|
+
|
79
|
+
EOB
|
80
|
+
|
81
|
+
separator ""
|
82
|
+
|
83
|
+
separator "Options:"
|
84
|
+
on("-p", "--port [PORT=#{opts[:port]}]", "listen port number") do |port|
|
85
|
+
opts[:port] = port
|
86
|
+
end
|
87
|
+
|
88
|
+
on("-h", "--host [HOST=#{opts[:host]}]", "listen host") do |host|
|
89
|
+
opts[:host] = host
|
90
|
+
end
|
91
|
+
|
92
|
+
on("-l", "--log LOG", "log file") do |log|
|
93
|
+
opts[:log] = log
|
94
|
+
end
|
95
|
+
|
96
|
+
on("--debug", "Enable debug mode") do |debug|
|
97
|
+
opts[:log] = $stdout
|
98
|
+
opts[:debug] = true
|
99
|
+
end
|
100
|
+
|
101
|
+
parse!(ARGV)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
opts[:logger] = Logger.new(opts[:log], "daily")
|
106
|
+
opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
|
107
|
+
|
108
|
+
def daemonize(debug=false)
|
109
|
+
return yield if $DEBUG || debug
|
110
|
+
Process.fork do
|
111
|
+
Process.setsid
|
112
|
+
Dir.chdir "/"
|
113
|
+
trap("SIGINT") { exit! 0 }
|
114
|
+
trap("SIGTERM") { exit! 0 }
|
115
|
+
trap("SIGHUP") { exit! 0 }
|
116
|
+
File.open("/dev/null") {|f|
|
117
|
+
STDIN.reopen f
|
118
|
+
STDOUT.reopen f
|
119
|
+
STDERR.reopen f
|
120
|
+
}
|
121
|
+
yield
|
122
|
+
end
|
123
|
+
exit! 0
|
124
|
+
end
|
125
|
+
|
126
|
+
daemonize(opts[:debug]) do
|
127
|
+
Net::IRC::Server.new(opts[:host], opts[:port], WassrIrcGateway, opts).start
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
|