net-irc2 0.0.10
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.
- checksums.yaml +7 -0
- data/AUTHORS.txt +33 -0
- data/ChangeLog +95 -0
- data/README +91 -0
- data/Rakefile +69 -0
- data/examples/2ch.rb +225 -0
- data/examples/2ig.rb +267 -0
- data/examples/client.rb +23 -0
- data/examples/echo_bot.rb +31 -0
- data/examples/echo_bot_celluloid.rb +33 -0
- data/examples/gig.rb +192 -0
- data/examples/gmail.rb +202 -0
- data/examples/gtig.rb +420 -0
- data/examples/hatena-star-stream.rb +270 -0
- data/examples/hcig.rb +285 -0
- data/examples/hig.rb +771 -0
- data/examples/iig.rb +819 -0
- data/examples/ircd.rb +358 -0
- data/examples/lig.rb +551 -0
- data/examples/lingr.rb +327 -0
- data/examples/mixi.rb +252 -0
- data/examples/sig.rb +188 -0
- data/examples/tig.rb +2712 -0
- data/lib/net/irc/client/channel_manager.rb +144 -0
- data/lib/net/irc/client.rb +117 -0
- data/lib/net/irc/constants.rb +214 -0
- data/lib/net/irc/message/modeparser.rb +85 -0
- data/lib/net/irc/message/serverconfig.rb +30 -0
- data/lib/net/irc/message.rb +109 -0
- data/lib/net/irc/pattern.rb +68 -0
- data/lib/net/irc/server.rb +186 -0
- data/lib/net/irc.rb +77 -0
- data/spec/channel_manager_spec.rb +184 -0
- data/spec/modeparser_spec.rb +165 -0
- data/spec/net-irc_spec.rb +337 -0
- data/spec/spec.opts +1 -0
- metadata +91 -0
@@ -0,0 +1,270 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:encoding=UTF-8:
|
3
|
+
=begin
|
4
|
+
|
5
|
+
## Licence
|
6
|
+
|
7
|
+
Ruby's by cho45
|
8
|
+
|
9
|
+
=end
|
10
|
+
|
11
|
+
$LOAD_PATH << "lib"
|
12
|
+
$LOAD_PATH << "../lib"
|
13
|
+
|
14
|
+
$KCODE = "u" unless defined? ::Encoding # json use this
|
15
|
+
|
16
|
+
require "rubygems"
|
17
|
+
require "json"
|
18
|
+
require "net/http"
|
19
|
+
require "net/irc"
|
20
|
+
require "sdbm"
|
21
|
+
require "tmpdir"
|
22
|
+
require "nkf"
|
23
|
+
require 'mechanize'
|
24
|
+
require 'nokogiri'
|
25
|
+
|
26
|
+
class HatenaStarStream < Net::IRC::Server::Session
|
27
|
+
def server_name
|
28
|
+
"hatenastarstream"
|
29
|
+
end
|
30
|
+
|
31
|
+
def server_version
|
32
|
+
"0.0.0"
|
33
|
+
end
|
34
|
+
|
35
|
+
def main_channel
|
36
|
+
"#star"
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(*a)
|
40
|
+
super
|
41
|
+
@ua = WWW::Mechanize.new
|
42
|
+
@ua.max_history = 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_user(m)
|
46
|
+
super
|
47
|
+
post @prefix, JOIN, main_channel
|
48
|
+
post server_name, MODE, main_channel, "+o", @prefix.nick
|
49
|
+
|
50
|
+
@real, *@opts = @real.split(/\s+/)
|
51
|
+
@opts = @opts.inject({}) {|r,i|
|
52
|
+
key, value = i.split("=")
|
53
|
+
r.update(key => value)
|
54
|
+
}
|
55
|
+
|
56
|
+
@uri = URI("http://s.hatena.ne.jp/#{@real}/report.json?api_key=#{@pass}")
|
57
|
+
start_observer
|
58
|
+
end
|
59
|
+
|
60
|
+
def on_disconnected
|
61
|
+
@observer.kill rescue nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_privmsg(m)
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_ctcp(target, message)
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_whois(m)
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_who(m)
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_join(m)
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_part(m)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def start_observer
|
84
|
+
@observer = Thread.start do
|
85
|
+
Thread.abort_on_exception = true
|
86
|
+
loop do
|
87
|
+
begin
|
88
|
+
@log.info "getting report..."
|
89
|
+
@log.debug @uri.to_s
|
90
|
+
data = JSON.parse(Net::HTTP.get(@uri.host, @uri.request_uri, @uri.port))
|
91
|
+
|
92
|
+
db = SDBM.open("#{Dir.tmpdir}/#{@real}.db", 0666)
|
93
|
+
data['entries'].reverse_each do |entry|
|
94
|
+
stars = ((entry['colored_stars'] || []) + [ entry ]).inject([]) {|r,i|
|
95
|
+
r.concat i['stars'].map {|s| Star.new(s, i['color'] || 'normal') }
|
96
|
+
}
|
97
|
+
|
98
|
+
indexes = Hash.new(1)
|
99
|
+
s = stars.select {|star|
|
100
|
+
id = "#{entry['uri']}::#{indexes[star.color]}"
|
101
|
+
indexes[star.color] += 1
|
102
|
+
if db.include?(id)
|
103
|
+
false
|
104
|
+
else
|
105
|
+
db[id] = "1"
|
106
|
+
true
|
107
|
+
end
|
108
|
+
}.inject([]) {|r,i|
|
109
|
+
if r.last == i
|
110
|
+
r.last.count += 1
|
111
|
+
else
|
112
|
+
r << i
|
113
|
+
end
|
114
|
+
r
|
115
|
+
}
|
116
|
+
|
117
|
+
if s.length > 0
|
118
|
+
post server_name, NOTICE, main_channel, "#{entry['uri']} #{title(entry['uri'])}"
|
119
|
+
if @opts.key?("metadata")
|
120
|
+
post "metadata", NOTICE, main_channel, JSON.generate({ "uri" => entry['uri'] })
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
s.each do |star|
|
125
|
+
post server_name, NOTICE, main_channel, "id:%s \x03%d%s%s\x030 %s" % [
|
126
|
+
star.name,
|
127
|
+
Star::Colors[star.color],
|
128
|
+
((star.color == "normal") ? "☆" : "★") * ([star.count, 10].min),
|
129
|
+
(star.count > 10) ? "(...#{star.count})" : "",
|
130
|
+
star.quote
|
131
|
+
]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
rescue Exception => e
|
136
|
+
@log.error e.inspect
|
137
|
+
@log.error e.backtrace
|
138
|
+
ensure
|
139
|
+
db.close rescue nil
|
140
|
+
end
|
141
|
+
sleep 60 * 5
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def title(url)
|
147
|
+
uri = URI(url)
|
148
|
+
@ua.get(uri)
|
149
|
+
|
150
|
+
text = ""
|
151
|
+
case
|
152
|
+
when uri.fragment
|
153
|
+
fragment = @ua.page.root.at("//*[@name = '#{uri.fragment}']") || @ua.page.root.at("//*[@id = '#{uri.fragment}']")
|
154
|
+
|
155
|
+
text = fragment.inner_text
|
156
|
+
while fragment.respond_to? :parent
|
157
|
+
text += (fragment.next && fragment.next.text.to_s).to_s
|
158
|
+
fragment = fragment.parent
|
159
|
+
end
|
160
|
+
when uri.to_s =~ %r|^http://h.hatena.ne.jp/[^/]+/\d+|
|
161
|
+
text = @ua.page.root.at("#main .entries .entry .list-body div.body").inner_text
|
162
|
+
else
|
163
|
+
text = @ua.page.root.at("//title").inner_text
|
164
|
+
end
|
165
|
+
text.gsub!(/\s+/, " ")
|
166
|
+
text.strip!
|
167
|
+
NKF.nkf("-w", text).split(//)[0..60].join
|
168
|
+
rescue Exception => e
|
169
|
+
@log.debug ["title:", e.inspect]
|
170
|
+
""
|
171
|
+
end
|
172
|
+
|
173
|
+
class Star < OpenStruct
|
174
|
+
Colors = {
|
175
|
+
"purple" => 6,
|
176
|
+
"blue" => 2,
|
177
|
+
"green" => 3,
|
178
|
+
"red" => 4,
|
179
|
+
"normal" => 8,
|
180
|
+
}
|
181
|
+
|
182
|
+
def initialize(obj, col="normal")
|
183
|
+
super(obj)
|
184
|
+
self.count = obj["count"].to_i + 1
|
185
|
+
self.color = col
|
186
|
+
end
|
187
|
+
|
188
|
+
def ==(other)
|
189
|
+
self.color == other.color &&
|
190
|
+
self.name == other.name
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if __FILE__ == $0
|
196
|
+
require "optparse"
|
197
|
+
|
198
|
+
opts = {
|
199
|
+
:port => 16702,
|
200
|
+
:host => "localhost",
|
201
|
+
:log => nil,
|
202
|
+
:debug => false,
|
203
|
+
:foreground => false,
|
204
|
+
}
|
205
|
+
|
206
|
+
OptionParser.new do |parser|
|
207
|
+
parser.instance_eval do
|
208
|
+
self.banner = <<-EOB.gsub(/^\t+/, "")
|
209
|
+
Usage: #{$0} [opts]
|
210
|
+
|
211
|
+
EOB
|
212
|
+
|
213
|
+
separator ""
|
214
|
+
|
215
|
+
separator "Options:"
|
216
|
+
on("-p", "--port [PORT=#{opts[:port]}]", "port number to listen") do |port|
|
217
|
+
opts[:port] = port
|
218
|
+
end
|
219
|
+
|
220
|
+
on("-h", "--host [HOST=#{opts[:host]}]", "host name or IP address to listen") do |host|
|
221
|
+
opts[:host] = host
|
222
|
+
end
|
223
|
+
|
224
|
+
on("-l", "--log LOG", "log file") do |log|
|
225
|
+
opts[:log] = log
|
226
|
+
end
|
227
|
+
|
228
|
+
on("--debug", "Enable debug mode") do |debug|
|
229
|
+
opts[:log] = $stdout
|
230
|
+
opts[:debug] = true
|
231
|
+
end
|
232
|
+
|
233
|
+
on("-f", "--foreground", "run foreground") do |foreground|
|
234
|
+
opts[:log] = $stdout
|
235
|
+
opts[:foreground] = true
|
236
|
+
end
|
237
|
+
|
238
|
+
parse!(ARGV)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
opts[:logger] = Logger.new(opts[:log], "daily")
|
243
|
+
opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
|
244
|
+
|
245
|
+
def daemonize(foreground=false)
|
246
|
+
trap("SIGINT") { exit! 0 }
|
247
|
+
trap("SIGTERM") { exit! 0 }
|
248
|
+
trap("SIGHUP") { exit! 0 }
|
249
|
+
return yield if $DEBUG || foreground
|
250
|
+
Process.fork do
|
251
|
+
Process.setsid
|
252
|
+
Dir.chdir "/"
|
253
|
+
File.open("/dev/null") {|f|
|
254
|
+
STDIN.reopen f
|
255
|
+
STDOUT.reopen f
|
256
|
+
STDERR.reopen f
|
257
|
+
}
|
258
|
+
yield
|
259
|
+
end
|
260
|
+
exit! 0
|
261
|
+
end
|
262
|
+
|
263
|
+
daemonize(opts[:debug] || opts[:foreground]) do
|
264
|
+
Net::IRC::Server.new(opts[:host], opts[:port], HatenaStarStream, opts).start
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Local Variables:
|
269
|
+
# coding: utf-8
|
270
|
+
# End:
|
data/examples/hcig.rb
ADDED
@@ -0,0 +1,285 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:encoding=utf-8:
|
3
|
+
|
4
|
+
$LOAD_PATH << "lib"
|
5
|
+
$LOAD_PATH << "../lib"
|
6
|
+
$LOAD_PATH.concat Dir.glob("/usr/local/ruby1.9/lib/ruby/gems/1.9.1/gems/*/lib")
|
7
|
+
|
8
|
+
$KCODE = "u" if RUBY_VERSION < "1.9" # json use this
|
9
|
+
|
10
|
+
require "rubygems"
|
11
|
+
require "net/irc"
|
12
|
+
require "logger"
|
13
|
+
require "pathname"
|
14
|
+
require "yaml"
|
15
|
+
require 'uri'
|
16
|
+
require 'net/http'
|
17
|
+
require 'nkf'
|
18
|
+
require 'stringio'
|
19
|
+
require 'zlib'
|
20
|
+
require 'mechanize'
|
21
|
+
require 'digest/sha1'
|
22
|
+
|
23
|
+
|
24
|
+
Net::HTTP.version_1_2
|
25
|
+
Thread.abort_on_exception = true
|
26
|
+
|
27
|
+
class HatenaCounterIrcGateway < Net::IRC::Server::Session
|
28
|
+
COLORS = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
29
|
+
|
30
|
+
def server_name
|
31
|
+
"hcig"
|
32
|
+
end
|
33
|
+
|
34
|
+
def server_version
|
35
|
+
"0.0.0"
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def initialize(*args)
|
40
|
+
super
|
41
|
+
@channels = {}
|
42
|
+
@ua = Mechanize.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_disconnected
|
46
|
+
@channels.each do |chan, info|
|
47
|
+
begin
|
48
|
+
info[:observer].kill if info[:observer]
|
49
|
+
rescue
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_user(m)
|
55
|
+
super
|
56
|
+
@real, *@opts = @real.split(/\s+/)
|
57
|
+
@opts ||= []
|
58
|
+
end
|
59
|
+
|
60
|
+
def on_pass(m)
|
61
|
+
super
|
62
|
+
self.rk = @pass
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_join(m)
|
66
|
+
channels = m.params.first.split(/,/)
|
67
|
+
channels.each do |channel|
|
68
|
+
@channels[channel] = {
|
69
|
+
:topic => "",
|
70
|
+
:time => Time.at(0),
|
71
|
+
:interval => 60,
|
72
|
+
:observer => nil,
|
73
|
+
} unless @channels.key?(channel)
|
74
|
+
create_observer(channel)
|
75
|
+
post @prefix, JOIN, channel
|
76
|
+
post nil, RPL_NAMREPLY, @prefix.nick, "=", channel, "@#{@prefix.nick}"
|
77
|
+
post nil, RPL_ENDOFNAMES, @prefix.nick, channel, "End of NAMES list"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def on_part(m)
|
82
|
+
channel = m.params[0]
|
83
|
+
if @channels.key?(channel)
|
84
|
+
info = @channels.delete(channel)
|
85
|
+
info[:observer].kill if info[:observer]
|
86
|
+
post @prefix, PART, channel
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def on_privmsg(m)
|
91
|
+
target, mesg = *m.params
|
92
|
+
m.ctcps.each {|ctcp| on_ctcp(target, ctcp) } if m.ctcp?
|
93
|
+
end
|
94
|
+
|
95
|
+
def on_ctcp(target, mesg)
|
96
|
+
type, mesg = mesg.split(" ", 2)
|
97
|
+
method = "on_ctcp_#{type.downcase}".to_sym
|
98
|
+
send(method, target, mesg) if respond_to? method, true
|
99
|
+
end
|
100
|
+
|
101
|
+
def on_ctcp_action(target, mesg)
|
102
|
+
command, *args = mesg.split(" ")
|
103
|
+
command.downcase!
|
104
|
+
|
105
|
+
case command
|
106
|
+
when 'rk'
|
107
|
+
self.rk = args[0]
|
108
|
+
end
|
109
|
+
rescue Exception => e
|
110
|
+
@log.error e.inspect
|
111
|
+
e.backtrace.each do |l|
|
112
|
+
@log.error "\t#{l}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_observer(channel)
|
117
|
+
info = @channels[channel]
|
118
|
+
info[:observer].kill if info[:observer]
|
119
|
+
|
120
|
+
@log.debug "create_observer %s, interval %d" % [channel, info[:interval]]
|
121
|
+
info[:observer] = Thread.start(info, channel) do |info, channel|
|
122
|
+
Thread.pass
|
123
|
+
pre, name, cid = *channel.split(/-/)
|
124
|
+
|
125
|
+
if !name || !cid
|
126
|
+
post @prefix, PART, channel, "You must join to #counter-[username]-[counter id]"
|
127
|
+
info[:observer].kill
|
128
|
+
next
|
129
|
+
end
|
130
|
+
|
131
|
+
loop do
|
132
|
+
begin
|
133
|
+
uri = "http://counter.hatena.ne.jp/#{name}/log?cid=#{cid}&date=&type="
|
134
|
+
@log.debug "Retriving... #{uri}"
|
135
|
+
ret = @ua.get(uri) do |page|
|
136
|
+
page.search('#log_table tr').reverse_each do |tr|
|
137
|
+
access = Access.new(*tr.search('td').map {|i| i.text.gsub("\302\240", ' ').gsub(/^\s+|\s+$/, '') })
|
138
|
+
next unless access.time
|
139
|
+
next if access.time < info[:time]
|
140
|
+
|
141
|
+
diff = Time.now - access.time
|
142
|
+
time = nil
|
143
|
+
case
|
144
|
+
when diff < 90
|
145
|
+
time = ''
|
146
|
+
when Time.now.strftime('%Y%m%d') == access.time.strftime('%Y%m%d')
|
147
|
+
time = access.time.strftime('%H:%M')
|
148
|
+
when Time.now.strftime('%Y') == access.time.strftime('%Y')
|
149
|
+
time = access.time.strftime('%m/%d %H:%M')
|
150
|
+
else
|
151
|
+
time = access.time.strftime('%Y/%m/%d %H:%M')
|
152
|
+
end
|
153
|
+
|
154
|
+
post access.ua_id, PRIVMSG, channel, "%s%s \003%.2d%s\017" % [
|
155
|
+
time.empty?? "" : "#{time} ",
|
156
|
+
access.request,
|
157
|
+
COLORS[access.ua_id(COLORS.size)],
|
158
|
+
access.host.gsub(/(\.\d+)+\./, '..').sub(/^[^.]+/, '')
|
159
|
+
]
|
160
|
+
info[:time] = access.time
|
161
|
+
end
|
162
|
+
info[:time] += 1
|
163
|
+
end
|
164
|
+
unless ret.code.to_i == 200 && ret.uri.to_s == uri
|
165
|
+
@log.error "Server returned [#{ret.code}] #{ret.uri}"
|
166
|
+
post nil, NOTICE, channel, "Server returned [#{ret.code}] #{ret.uri}. Please refresh rk by /me rk [new rk]"
|
167
|
+
end
|
168
|
+
rescue Exception => e
|
169
|
+
@log.error "Error: #{e.inspect}"
|
170
|
+
e.backtrace.each do |l|
|
171
|
+
@log.error "\t#{l}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
@log.debug "#{channel}: sleep #{info[:interval]}"
|
175
|
+
sleep info[:interval]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def rk=(rk)
|
181
|
+
uri = URI.parse('http://www.hatena.ne.jp/')
|
182
|
+
@ua.cookie_jar.add(
|
183
|
+
uri,
|
184
|
+
Mechanize::Cookie.parse(uri, "rk=#{rk}; domain=.hatena.ne.jp").first
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
Access = Struct.new(:time_raw, :request, :ua, :lang, :screen, :host, :referrer) do
|
189
|
+
require 'time'
|
190
|
+
|
191
|
+
def time
|
192
|
+
time_raw ? Time.parse(time_raw) : nil
|
193
|
+
end
|
194
|
+
|
195
|
+
def digest
|
196
|
+
hostc = host.gsub(/\.[^.]+\.jp$|\.com$/, '')[/[^.]+$/]
|
197
|
+
@digest ||= Digest::SHA1.digest([ua, lang, screen, hostc].join("\n"))
|
198
|
+
end
|
199
|
+
|
200
|
+
def ua_id(num=nil)
|
201
|
+
if num
|
202
|
+
digest.unpack("N*")[0] % num
|
203
|
+
else
|
204
|
+
@ua_id ||= [ digest ].pack('m')[0, 7]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
if __FILE__ == $0
|
212
|
+
require "optparse"
|
213
|
+
|
214
|
+
opts = {
|
215
|
+
:port => 16801,
|
216
|
+
:host => "localhost",
|
217
|
+
:log => nil,
|
218
|
+
:debug => false,
|
219
|
+
:foreground => false,
|
220
|
+
}
|
221
|
+
|
222
|
+
OptionParser.new do |parser|
|
223
|
+
parser.instance_eval do
|
224
|
+
self.banner = <<-EOB.gsub(/^\t+/, "")
|
225
|
+
Usage: #{$0} [opts]
|
226
|
+
|
227
|
+
EOB
|
228
|
+
|
229
|
+
separator ""
|
230
|
+
|
231
|
+
separator "Options:"
|
232
|
+
on("-p", "--port [PORT=#{opts[:port]}]", "port number to listen") do |port|
|
233
|
+
opts[:port] = port
|
234
|
+
end
|
235
|
+
|
236
|
+
on("-h", "--host [HOST=#{opts[:host]}]", "host name or IP address to listen") do |host|
|
237
|
+
opts[:host] = host
|
238
|
+
end
|
239
|
+
|
240
|
+
on("-l", "--log LOG", "log file") do |log|
|
241
|
+
opts[:log] = log
|
242
|
+
end
|
243
|
+
|
244
|
+
on("--debug", "Enable debug mode") do |debug|
|
245
|
+
opts[:log] = $stdout
|
246
|
+
opts[:debug] = true
|
247
|
+
end
|
248
|
+
|
249
|
+
on("-f", "--foreground", "run foreground") do |foreground|
|
250
|
+
opts[:log] = $stdout
|
251
|
+
opts[:foreground] = true
|
252
|
+
end
|
253
|
+
|
254
|
+
parse!(ARGV)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
opts[:logger] = Logger.new(opts[:log], "daily")
|
259
|
+
opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
|
260
|
+
|
261
|
+
def daemonize(foreground=false)
|
262
|
+
trap("SIGINT") { exit! 0 }
|
263
|
+
trap("SIGTERM") { exit! 0 }
|
264
|
+
trap("SIGHUP") { exit! 0 }
|
265
|
+
return yield if $DEBUG || foreground
|
266
|
+
Process.fork do
|
267
|
+
Process.setsid
|
268
|
+
Dir.chdir "/"
|
269
|
+
File.open("/dev/null") {|f|
|
270
|
+
STDIN.reopen f
|
271
|
+
STDOUT.reopen f
|
272
|
+
STDERR.reopen f
|
273
|
+
}
|
274
|
+
yield
|
275
|
+
end
|
276
|
+
exit! 0
|
277
|
+
end
|
278
|
+
|
279
|
+
daemonize(opts[:debug] || opts[:foreground]) do
|
280
|
+
Net::IRC::Server.new(opts[:host], opts[:port], HatenaCounterIrcGateway, opts).start
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
|