net-irc 0.0.4 → 0.0.5
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 +16 -0
- data/Rakefile +1 -1
- data/examples/nig.rb +4 -0
- data/examples/tig.rb +159 -72
- data/examples/wig.rb +638 -32
- data/lib/net/irc.rb +1 -1
- data/lib/net/irc/client.rb +13 -129
- data/lib/net/irc/client/channel_manager.rb +144 -0
- data/lib/net/irc/constants.rb +1 -1
- data/lib/net/irc/message.rb +3 -1
- data/lib/net/irc/message/modeparser.rb +66 -25
- data/lib/net/irc/message/serverconfig.rb +30 -0
- data/spec/channel_manager_spec.rb +176 -0
- data/spec/modeparser_spec.rb +141 -19
- data/spec/net-irc_spec.rb +9 -69
- metadata +8 -6
- data/lib/net/irc/message/modeparser/hyperion.rb +0 -88
- data/lib/net/irc/message/modeparser/rfc1459.rb +0 -21
data/examples/wig.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
|
4
4
|
# wig.rb
|
5
5
|
|
6
|
-
|
6
|
+
wig.rb channel: http://wassr.jp/channel/wigrb
|
7
7
|
|
8
8
|
## Launch
|
9
9
|
|
10
|
-
$ ruby wig.rb
|
10
|
+
$ ruby wig.rb
|
11
11
|
|
12
12
|
If you want to help:
|
13
13
|
|
@@ -21,8 +21,8 @@ Configuration example for Tiarra ( http://coderepos.org/share/wiki/Tiarra ).
|
|
21
21
|
|
22
22
|
wassr {
|
23
23
|
host: localhost
|
24
|
-
port:
|
25
|
-
name: username@example.com athack jabber=username@example.com:jabberpasswd
|
24
|
+
port: 16668
|
25
|
+
name: username@example.com athack jabber=username@example.com:jabberpasswd tid=10 ratio=10:3:5
|
26
26
|
password: password on Wassr
|
27
27
|
in-encoding: utf8
|
28
28
|
out-encoding: utf8
|
@@ -33,12 +33,37 @@ Configuration example for Tiarra ( http://coderepos.org/share/wiki/Tiarra ).
|
|
33
33
|
If `athack` client option specified,
|
34
34
|
all nick in join message is leading with @.
|
35
35
|
|
36
|
-
So if you complemente nicks (
|
36
|
+
So if you complemente nicks (e.g. Irssi),
|
37
37
|
it's good for Twitter like reply command (@nick).
|
38
38
|
|
39
39
|
In this case, you will see torrent of join messages after connected,
|
40
40
|
because NAMES list can't send @ leading nick (it interpreted op.)
|
41
41
|
|
42
|
+
### tid=<color>
|
43
|
+
|
44
|
+
Apply id to each message for make favorites by CTCP ACTION.
|
45
|
+
|
46
|
+
/me fav id
|
47
|
+
|
48
|
+
<color> can be
|
49
|
+
|
50
|
+
0 => white
|
51
|
+
1 => black
|
52
|
+
2 => blue navy
|
53
|
+
3 => green
|
54
|
+
4 => red
|
55
|
+
5 => brown maroon
|
56
|
+
6 => purple
|
57
|
+
7 => orange olive
|
58
|
+
8 => yellow
|
59
|
+
9 => lightgreen lime
|
60
|
+
10 => teal
|
61
|
+
11 => lightcyan cyan aqua
|
62
|
+
12 => lightblue royal
|
63
|
+
13 => pink lightpurple fuchsia
|
64
|
+
14 => grey
|
65
|
+
15 => lightgrey silver
|
66
|
+
|
42
67
|
|
43
68
|
### jabber=<jid>:<pass>
|
44
69
|
|
@@ -46,23 +71,45 @@ If `jabber=<jid>:<pass>` option specified,
|
|
46
71
|
use jabber to get friends timeline.
|
47
72
|
|
48
73
|
You must setup im notifing settings in the site and
|
49
|
-
install
|
74
|
+
install "xmpp4r-simple" gem.
|
50
75
|
|
51
76
|
$ sudo gem install xmpp4r-simple
|
52
77
|
|
53
78
|
Be careful for managing password.
|
54
79
|
|
55
|
-
|
80
|
+
### alwaysim
|
81
|
+
|
82
|
+
Use IM instead of any APIs (e.g. post)
|
83
|
+
|
84
|
+
### ratio=<timeline>:<friends>:<channel>
|
85
|
+
|
86
|
+
## License
|
56
87
|
|
57
88
|
Ruby's by cho45
|
58
89
|
|
59
90
|
=end
|
60
91
|
|
61
|
-
$LOAD_PATH <<
|
92
|
+
$LOAD_PATH << "lib"
|
93
|
+
$LOAD_PATH << "../lib"
|
62
94
|
|
63
|
-
|
95
|
+
$KCODE = "u" # json use this
|
64
96
|
|
65
|
-
|
97
|
+
require "rubygems"
|
98
|
+
require "net/irc"
|
99
|
+
require "net/http"
|
100
|
+
require "uri"
|
101
|
+
require "json"
|
102
|
+
require "socket"
|
103
|
+
require "time"
|
104
|
+
require "logger"
|
105
|
+
require "yaml"
|
106
|
+
require "pathname"
|
107
|
+
require "cgi"
|
108
|
+
require "digest/md5"
|
109
|
+
|
110
|
+
Net::HTTP.version_1_2
|
111
|
+
|
112
|
+
class WassrIrcGateway < Net::IRC::Server::Session
|
66
113
|
def server_name
|
67
114
|
"wassrgw"
|
68
115
|
end
|
@@ -86,6 +133,562 @@ class WassrIrcGateway < TwitterIrcGateway
|
|
86
133
|
def jabber_bot_id
|
87
134
|
"wassr-bot@wassr.jp"
|
88
135
|
end
|
136
|
+
|
137
|
+
def hourly_limit
|
138
|
+
60
|
139
|
+
end
|
140
|
+
|
141
|
+
class ApiFailed < StandardError; end
|
142
|
+
|
143
|
+
def initialize(*args)
|
144
|
+
super
|
145
|
+
@channels = {}
|
146
|
+
@user_agent = "#{self.class}/#{server_version} (wig.rb)"
|
147
|
+
@map = nil
|
148
|
+
@counters = {} # for jabber fav
|
149
|
+
end
|
150
|
+
|
151
|
+
def on_user(m)
|
152
|
+
super
|
153
|
+
post @prefix, JOIN, main_channel
|
154
|
+
post server_name, MODE, main_channel, "+o", @prefix.nick
|
155
|
+
|
156
|
+
@real, *@opts = @opts.name || @real.split(/\s+/)
|
157
|
+
@opts = @opts.inject({}) {|r,i|
|
158
|
+
key, value = i.split("=")
|
159
|
+
r.update(key => value)
|
160
|
+
}
|
161
|
+
@tmap = TypableMap.new
|
162
|
+
|
163
|
+
if @opts["jabber"]
|
164
|
+
jid, pass = @opts["jabber"].split(":", 2)
|
165
|
+
@opts["jabber"].replace("jabber=#{jid}:********")
|
166
|
+
if jabber_bot_id
|
167
|
+
begin
|
168
|
+
require "xmpp4r-simple"
|
169
|
+
start_jabber(jid, pass)
|
170
|
+
rescue LoadError
|
171
|
+
log "Failed to start Jabber."
|
172
|
+
log 'Installl "xmpp4r-simple" gem or check your id/pass.'
|
173
|
+
finish
|
174
|
+
end
|
175
|
+
else
|
176
|
+
@opts.delete("jabber")
|
177
|
+
log "This gateway does not support Jabber bot."
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
log "Client Options: #{@opts.inspect}"
|
182
|
+
@log.info "Client Options: #{@opts.inspect}"
|
183
|
+
|
184
|
+
timeline_ratio, friends_ratio, channel_ratio = (@opts["ratio"] || "10:3:5").split(":").map {|ratio| ratio.to_i }
|
185
|
+
footing = (timeline_ratio + friends_ratio + channel_ratio).to_f
|
186
|
+
|
187
|
+
@timeline = []
|
188
|
+
@check_friends_thread = Thread.start do
|
189
|
+
loop do
|
190
|
+
begin
|
191
|
+
check_friends
|
192
|
+
rescue ApiFailed => e
|
193
|
+
@log.error e.inspect
|
194
|
+
rescue Exception => e
|
195
|
+
@log.error e.inspect
|
196
|
+
e.backtrace.each do |l|
|
197
|
+
@log.error "\t#{l}"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
sleep freq(friends_ratio / footing)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
return if @opts["jabber"]
|
205
|
+
|
206
|
+
@check_timeline_thread = Thread.start do
|
207
|
+
sleep 3
|
208
|
+
loop do
|
209
|
+
begin
|
210
|
+
check_timeline
|
211
|
+
# check_direct_messages
|
212
|
+
rescue ApiFailed => e
|
213
|
+
@log.error e.inspect
|
214
|
+
rescue Exception => e
|
215
|
+
@log.error e.inspect
|
216
|
+
e.backtrace.each do |l|
|
217
|
+
@log.error "\t#{l}"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
sleep freq(timeline_ratio / footing)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
@check_channel_thread = Thread.start do
|
225
|
+
sleep 5
|
226
|
+
Thread.abort_on_exception= true
|
227
|
+
loop do
|
228
|
+
begin
|
229
|
+
check_channel
|
230
|
+
# check_direct_messages
|
231
|
+
rescue ApiFailed => e
|
232
|
+
@log.error e.inspect
|
233
|
+
rescue Exception => e
|
234
|
+
@log.error e.inspect
|
235
|
+
e.backtrace.each do |l|
|
236
|
+
@log.error "\t#{l}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
sleep freq(channel_ratio / footing)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def on_disconnected
|
245
|
+
@check_friends_thread.kill rescue nil
|
246
|
+
@check_timeline_thread.kill rescue nil
|
247
|
+
@check_channel_thread.kill rescue nil
|
248
|
+
@im_thread.kill rescue nil
|
249
|
+
@im.disconnect rescue nil
|
250
|
+
end
|
251
|
+
|
252
|
+
def on_privmsg(m)
|
253
|
+
return on_ctcp(m[0], ctcp_decoding(m[1])) if m.ctcp?
|
254
|
+
retry_count = 3
|
255
|
+
ret = nil
|
256
|
+
target, message = *m.params
|
257
|
+
begin
|
258
|
+
if target =~ /^#(.+)/
|
259
|
+
channel = Regexp.last_match[1]
|
260
|
+
if @opts.key?("alwaysim") && @im && @im.connected? # in jabber mode, using jabber post
|
261
|
+
message = "##{channel} #{message}" unless "##{channel}" == main_channel
|
262
|
+
ret = @im.deliver(jabber_bot_id, message)
|
263
|
+
post "#{nick}!#{nick}@#{api_base.host}", TOPIC, channel, untinyurl(message)
|
264
|
+
else
|
265
|
+
if "##{channel}" == main_channel
|
266
|
+
ret = api("statuses/update", {"status" => message})
|
267
|
+
else
|
268
|
+
ret = api("channel_message/update", {"name_en" => channel, "body" => message})
|
269
|
+
end
|
270
|
+
end
|
271
|
+
else
|
272
|
+
# direct message
|
273
|
+
ret = api("direct_messages/new", {"user" => target, "text" => message})
|
274
|
+
end
|
275
|
+
raise ApiFailed, "API failed" unless ret
|
276
|
+
log "Status Updated" unless @im && @im.connected?
|
277
|
+
rescue => e
|
278
|
+
@log.error [retry_count, e.inspect].inspect
|
279
|
+
if retry_count > 0
|
280
|
+
retry_count -= 1
|
281
|
+
@log.debug "Retry to setting status..."
|
282
|
+
retry
|
283
|
+
else
|
284
|
+
log "Some Error Happened on Sending #{message}. #{e}"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def on_ctcp(target, message)
|
290
|
+
_, command, *args = message.split(/\s+/)
|
291
|
+
case command
|
292
|
+
when "list"
|
293
|
+
nick = args[0]
|
294
|
+
@log.debug([ nick, message ])
|
295
|
+
res = api("statuses/user_timeline", { "id" => nick }).reverse_each do |s|
|
296
|
+
@log.debug(s)
|
297
|
+
post nick, NOTICE, main_channel, "#{generate_status_message(s)}"
|
298
|
+
end
|
299
|
+
|
300
|
+
unless res
|
301
|
+
post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
|
302
|
+
end
|
303
|
+
when "fav"
|
304
|
+
tid = args[0]
|
305
|
+
st = @tmap[tid]
|
306
|
+
if st
|
307
|
+
if @im && @im.connected?
|
308
|
+
# IM のときはいろいろめんどうなことする
|
309
|
+
nick, count = *st
|
310
|
+
pos = @counters[nick] - count
|
311
|
+
@log.debug "%p %s %d/%d => %d" % [
|
312
|
+
st,
|
313
|
+
nick,
|
314
|
+
count,
|
315
|
+
@counters[nick],
|
316
|
+
pos
|
317
|
+
]
|
318
|
+
res = api("statuses/user_timeline", { "id" => nick })[pos]
|
319
|
+
id = res["rid"]
|
320
|
+
else
|
321
|
+
id = st["id"] || st["rid"]
|
322
|
+
end
|
323
|
+
res = api("favorites/create/#{id}", {})
|
324
|
+
post nil, NOTICE, main_channel, "Fav: #{res["screen_name"]}: #{res["text"]}"
|
325
|
+
else
|
326
|
+
post nil, NOTICE, main_channel, "No such id #{tid}"
|
327
|
+
end
|
328
|
+
when "link"
|
329
|
+
tid = args[0]
|
330
|
+
st = @tmap[tid]
|
331
|
+
if st
|
332
|
+
st["link"] = (api_base + "/#{st["user"]["screen_name"]}/statuses/#{st["id"]}").to_s unless st["link"]
|
333
|
+
post nil, NOTICE, main_channel, st["link"]
|
334
|
+
else
|
335
|
+
post nil, NOTICE, main_channel, "No such id #{tid}"
|
336
|
+
end
|
337
|
+
end
|
338
|
+
rescue ApiFailed => e
|
339
|
+
log e.inspect
|
340
|
+
end
|
341
|
+
|
342
|
+
def on_whois(m)
|
343
|
+
nick = m.params[0]
|
344
|
+
f = (@friends || []).find {|i| i["screen_name"] == nick }
|
345
|
+
if f
|
346
|
+
post nil, RPL_WHOISUSER, @nick, nick, nick, api_base.host, "*", "#{f["name"]} / #{f["description"]}"
|
347
|
+
post nil, RPL_WHOISSERVER, @nick, nick, api_base.host, api_base.to_s
|
348
|
+
post nil, RPL_WHOISIDLE, @nick, nick, "0", "seconds idle"
|
349
|
+
post nil, RPL_ENDOFWHOIS, @nick, nick, "End of WHOIS list"
|
350
|
+
else
|
351
|
+
post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def on_join(m)
|
356
|
+
channels = m.params[0].split(/\s*,\s*/)
|
357
|
+
channels.each do |channel|
|
358
|
+
next if channel == main_channel
|
359
|
+
@channels[channel] = {
|
360
|
+
:read => []
|
361
|
+
}
|
362
|
+
post "#{@nick}!#{@nick}@#{api_base.host}", JOIN, channel
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def on_part(m)
|
367
|
+
channel = m.params[0]
|
368
|
+
return if channel == main_channel
|
369
|
+
@channels.delete(channel)
|
370
|
+
post "#{@nick}!#{@nick}@#{api_base.host}", PART, channel
|
371
|
+
end
|
372
|
+
|
373
|
+
def on_who(m)
|
374
|
+
channel = m.params[0]
|
375
|
+
case
|
376
|
+
when channel == main_channel
|
377
|
+
# "<channel> <user> <host> <server> <nick>
|
378
|
+
# ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
|
379
|
+
# :<hopcount> <real name>"
|
380
|
+
@friends.each do |f|
|
381
|
+
user = nick = f["screen_name"]
|
382
|
+
host = serv = api_base.host
|
383
|
+
real = f["name"]
|
384
|
+
post nil, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
|
385
|
+
end
|
386
|
+
post nil, RPL_ENDOFWHO, @nick, channel
|
387
|
+
when @groups.key?(channel)
|
388
|
+
@groups[channel].each do |name|
|
389
|
+
f = @friends.find {|i| i["screen_name"] == name }
|
390
|
+
user = nick = f["screen_name"]
|
391
|
+
host = serv = api_base.host
|
392
|
+
real = f["name"]
|
393
|
+
post nil, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
|
394
|
+
end
|
395
|
+
post nil, RPL_ENDOFWHO, @nick, channel
|
396
|
+
else
|
397
|
+
post nil, ERR_NOSUCHNICK, @nick, nick, "No such nick/channel"
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
private
|
402
|
+
def check_timeline
|
403
|
+
@prev_time ||= Time.at(0)
|
404
|
+
api("statuses/friends_timeline", {"since" => @prev_time.httpdate}).reverse_each do |s|
|
405
|
+
id = s["id"] || s["rid"]
|
406
|
+
next if id.nil? || @timeline.include?(id)
|
407
|
+
@timeline << id
|
408
|
+
nick = s["user_login_id"]
|
409
|
+
mesg = generate_status_message(s)
|
410
|
+
|
411
|
+
tid = @tmap.push(s)
|
412
|
+
|
413
|
+
@log.debug [id, nick, mesg]
|
414
|
+
if nick == @nick # 自分のときは topic に
|
415
|
+
post "#{nick}!#{nick}@#{api_base.host}", TOPIC, main_channel, untinyurl(mesg)
|
416
|
+
else
|
417
|
+
if @opts["tid"]
|
418
|
+
message(nick, main_channel, "%s \x03%s [%s]" % [mesg, @opts["tid"], tid])
|
419
|
+
else
|
420
|
+
message(nick, main_channel, "%s" % [mesg, tid])
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
@log.debug "@timeline.size = #{@timeline.size}"
|
425
|
+
@timeline = @timeline.last(100)
|
426
|
+
@prev_time = Time.now
|
427
|
+
end
|
428
|
+
|
429
|
+
def check_channel
|
430
|
+
@channels.keys.each do |channel|
|
431
|
+
@log.debug "getting channel -> #{channel}..."
|
432
|
+
api("channel_message/list", { "name_en" => channel.sub(/^#/, "") }).reverse_each do |s|
|
433
|
+
begin
|
434
|
+
id = Digest::MD5.hexdigest(s["user"]["login_id"] + s["body"])
|
435
|
+
next if @channels[channel][:read].include?(id)
|
436
|
+
@channels[channel][:read] << id
|
437
|
+
nick = s["user"]["login_id"]
|
438
|
+
mesg = s["body"]
|
439
|
+
|
440
|
+
if nick == @nick
|
441
|
+
post nick, NOTICE, channel, mesg
|
442
|
+
else
|
443
|
+
message(nick, channel, mesg)
|
444
|
+
end
|
445
|
+
rescue Execepton => e
|
446
|
+
post server_name, NOTICE, channel, e.inspect
|
447
|
+
end
|
448
|
+
end
|
449
|
+
@channels[channel][:read] = @channels[channel][:read].last(100)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
def generate_status_message(status)
|
454
|
+
s = status
|
455
|
+
mesg = s["text"]
|
456
|
+
@log.debug(mesg)
|
457
|
+
|
458
|
+
# added @user in no use @user reply message (Wassr only)
|
459
|
+
if s.has_key?("reply_status_url") and s["reply_status_url"] and s["text"] !~ /^@.*/ and %r{([^/]+)/statuses/[^/]+}.match(s["reply_status_url"])
|
460
|
+
reply_user_id = $1
|
461
|
+
mesg = "@#{reply_user_id} #{mesg}"
|
462
|
+
end
|
463
|
+
# display area name (Wassr only)
|
464
|
+
if s.has_key?("areaname") and s["areaname"]
|
465
|
+
mesg += " L: #{s["areaname"]}"
|
466
|
+
end
|
467
|
+
# display photo URL (Wassr only)
|
468
|
+
if s.has_key?("photo_url") and s["photo_url"]
|
469
|
+
mesg += " #{s["photo_url"]}"
|
470
|
+
end
|
471
|
+
|
472
|
+
# time = Time.parse(s["created_at"]) rescue Time.now
|
473
|
+
m = { """ => "\"", "<"=> "<", ">"=> ">", "&"=> "&", "\n" => " "}
|
474
|
+
mesg.gsub!(/(#{m.keys.join("|")})/) { m[$1] }
|
475
|
+
mesg
|
476
|
+
end
|
477
|
+
|
478
|
+
def check_direct_messages
|
479
|
+
@prev_time_d ||= Time.now
|
480
|
+
api("direct_messages", {"since" => @prev_time_d.httpdate}).reverse_each do |s|
|
481
|
+
nick = s["sender_screen_name"]
|
482
|
+
mesg = s["text"]
|
483
|
+
time = Time.parse(s["created_at"])
|
484
|
+
@log.debug [nick, mesg, time].inspect
|
485
|
+
message(nick, @nick, mesg)
|
486
|
+
end
|
487
|
+
@prev_time_d = Time.now
|
488
|
+
end
|
489
|
+
|
490
|
+
def check_friends
|
491
|
+
first = true unless @friends
|
492
|
+
@friends ||= []
|
493
|
+
friends = api("statuses/friends")
|
494
|
+
if first && !@opts.key?("athack")
|
495
|
+
@friends = friends
|
496
|
+
post nil, RPL_NAMREPLY, @nick, "=", main_channel, @friends.map{|i| "@#{i["screen_name"]}" }.join(" ")
|
497
|
+
post nil, RPL_ENDOFNAMES, @nick, main_channel, "End of NAMES list"
|
498
|
+
else
|
499
|
+
prv_friends = @friends.map {|i| i["screen_name"] }
|
500
|
+
now_friends = friends.map {|i| i["screen_name"] }
|
501
|
+
|
502
|
+
# Twitter API bug?
|
503
|
+
return if !first && (now_friends.length - prv_friends.length).abs > 10
|
504
|
+
|
505
|
+
(now_friends - prv_friends).each do |join|
|
506
|
+
join = "@#{join}" if @opts.key?("athack")
|
507
|
+
post "#{join}!#{join}@#{api_base.host}", JOIN, main_channel
|
508
|
+
end
|
509
|
+
(prv_friends - now_friends).each do |part|
|
510
|
+
part = "@#{part}" if @opts.key?("athack")
|
511
|
+
post "#{part}!#{part}@#{api_base.host}", PART, main_channel, ""
|
512
|
+
end
|
513
|
+
@friends = friends
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
def freq(ratio)
|
518
|
+
ret = 3600 / (hourly_limit * ratio).round
|
519
|
+
@log.debug "Frequency: #{ret}"
|
520
|
+
ret
|
521
|
+
end
|
522
|
+
|
523
|
+
def start_jabber(jid, pass)
|
524
|
+
@log.info "Logging-in with #{jid} -> jabber_bot_id: #{jabber_bot_id}"
|
525
|
+
@im = Jabber::Simple.new(jid, pass)
|
526
|
+
@im.add(jabber_bot_id)
|
527
|
+
@im_thread = Thread.start do
|
528
|
+
loop do
|
529
|
+
begin
|
530
|
+
@im.received_messages.each do |msg|
|
531
|
+
@log.debug [msg.from, msg.body]
|
532
|
+
if msg.from.strip == jabber_bot_id
|
533
|
+
# Wassr -> 'nick(id): msg'
|
534
|
+
body = msg.body.sub(/^(.+?)(?:\((.+?)\))?: /, "")
|
535
|
+
if Regexp.last_match
|
536
|
+
nick, id = Regexp.last_match.captures
|
537
|
+
body = CGI.unescapeHTML(body)
|
538
|
+
|
539
|
+
case
|
540
|
+
when nick == "投稿完了"
|
541
|
+
log "#{nick}: #{body}"
|
542
|
+
when nick == "チャンネル投稿完了"
|
543
|
+
log "#{nick}: #{body}"
|
544
|
+
when body =~ /^#([a-z_]+)\s+(.+)$/i
|
545
|
+
# channel message or not
|
546
|
+
message(id || nick, "##{Regexp.last_match[1]}", Regexp.last_match[2])
|
547
|
+
when nick == "photo" && body =~ %r|^http://wassr\.jp/user/([^/]+)/|
|
548
|
+
nick = Regexp.last_match[1]
|
549
|
+
message(nick, main_channel, body)
|
550
|
+
else
|
551
|
+
@counters[nick] ||= 0
|
552
|
+
@counters[nick] += 1
|
553
|
+
tid = @tmap.push([nick, @counters[nick]])
|
554
|
+
message(nick, main_channel, "%s \x03%s [%s]" % [body, @opts["tid"], tid])
|
555
|
+
end
|
556
|
+
end
|
557
|
+
end
|
558
|
+
end
|
559
|
+
rescue Exception => e
|
560
|
+
@log.error "Error on Jabber loop: #{e.inspect}"
|
561
|
+
e.backtrace.each do |l|
|
562
|
+
@log.error "\t#{l}"
|
563
|
+
end
|
564
|
+
end
|
565
|
+
sleep 1
|
566
|
+
end
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def require_post?(path)
|
571
|
+
[
|
572
|
+
"statuses/update",
|
573
|
+
"direct_messages/new",
|
574
|
+
"channel_message/update",
|
575
|
+
%r|^favorites/create|,
|
576
|
+
].any? {|i| i === path }
|
577
|
+
end
|
578
|
+
|
579
|
+
def api(path, q={})
|
580
|
+
ret = {}
|
581
|
+
q["source"] ||= api_source
|
582
|
+
|
583
|
+
uri = api_base.dup
|
584
|
+
uri.path = "/#{path}.json"
|
585
|
+
uri.query = q.inject([]) {|r,(k,v)| v.inject(r) {|r,i| r << "#{k}=#{URI.escape(i, /[^-.!~*'()\w]/n)}" } }.join("&")
|
586
|
+
|
587
|
+
|
588
|
+
req = nil
|
589
|
+
if require_post?(path)
|
590
|
+
req = Net::HTTP::Post.new(uri.path)
|
591
|
+
req.body = uri.query
|
592
|
+
else
|
593
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
594
|
+
end
|
595
|
+
req.basic_auth(@real, @pass)
|
596
|
+
req["User-Agent"] = @user_agent
|
597
|
+
req["If-Modified-Since"] = q["since"] if q.key?("since")
|
598
|
+
|
599
|
+
@log.debug uri.inspect
|
600
|
+
ret = Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }
|
601
|
+
|
602
|
+
case ret
|
603
|
+
when Net::HTTPOK # 200
|
604
|
+
ret = JSON.parse(ret.body.gsub(/'(y(?:es)?|no?|true|false|null)'/, '"\1"'))
|
605
|
+
raise ApiFailed, "Server Returned Error: #{ret["error"]}" if ret.kind_of?(Hash) && ret["error"]
|
606
|
+
ret
|
607
|
+
when Net::HTTPNotModified # 304
|
608
|
+
[]
|
609
|
+
when Net::HTTPBadRequest # 400
|
610
|
+
# exceeded the rate limitation
|
611
|
+
raise ApiFailed, "#{ret.code}: #{ret.message}"
|
612
|
+
else
|
613
|
+
raise ApiFailed, "Server Returned #{ret.code} #{ret.message}"
|
614
|
+
end
|
615
|
+
rescue Errno::ETIMEDOUT, JSON::ParserError, IOError, Timeout::Error, Errno::ECONNRESET => e
|
616
|
+
raise ApiFailed, e.inspect
|
617
|
+
end
|
618
|
+
|
619
|
+
def message(sender, target, str)
|
620
|
+
str = untinyurl(str)
|
621
|
+
sender = "#{sender}!#{sender}@#{api_base.host}"
|
622
|
+
post sender, PRIVMSG, target, str
|
623
|
+
end
|
624
|
+
|
625
|
+
def log(str)
|
626
|
+
str.gsub!(/\n/, " ")
|
627
|
+
post server_name, NOTICE, main_channel, str
|
628
|
+
end
|
629
|
+
|
630
|
+
def untinyurl(text)
|
631
|
+
text.gsub(%r|http://(preview\.)?tinyurl\.com/[0-9a-z=]+|i) {|m|
|
632
|
+
uri = URI(m)
|
633
|
+
uri.host = uri.host.sub($1, "") if $1
|
634
|
+
Net::HTTP.start(uri.host, uri.port) {|http|
|
635
|
+
http.open_timeout = 3
|
636
|
+
begin
|
637
|
+
http.head(uri.request_uri, { "User-Agent" => @user_agent })["Location"] || m
|
638
|
+
rescue Timeout::Error
|
639
|
+
m
|
640
|
+
end
|
641
|
+
}
|
642
|
+
}
|
643
|
+
end
|
644
|
+
|
645
|
+
class TypableMap < Hash
|
646
|
+
Roma = %w|k g ky gy s z sh j t d ch n ny h b p hy by py m my y r ry w v q|.unshift("").map {|consonant|
|
647
|
+
case
|
648
|
+
when consonant.size > 1, consonant == "y"
|
649
|
+
%w|a u o|
|
650
|
+
when consonant == "q"
|
651
|
+
%w|a i e o|
|
652
|
+
else
|
653
|
+
%w|a i u e o|
|
654
|
+
end.map {|vowel| "#{consonant}#{vowel}" }
|
655
|
+
}.flatten
|
656
|
+
|
657
|
+
def initialize(size=1)
|
658
|
+
@seq = Roma
|
659
|
+
@map = {}
|
660
|
+
@n = 0
|
661
|
+
@size = size
|
662
|
+
end
|
663
|
+
|
664
|
+
def generate(n)
|
665
|
+
ret = []
|
666
|
+
begin
|
667
|
+
n, r = n.divmod(@seq.size)
|
668
|
+
ret << @seq[r]
|
669
|
+
end while n > 0
|
670
|
+
ret.reverse.join
|
671
|
+
end
|
672
|
+
|
673
|
+
def push(obj)
|
674
|
+
id = generate(@n)
|
675
|
+
self[id] = obj
|
676
|
+
@n += 1
|
677
|
+
@n = @n % (@seq.size ** @size)
|
678
|
+
id
|
679
|
+
end
|
680
|
+
alias << push
|
681
|
+
|
682
|
+
def clear
|
683
|
+
@n = 0
|
684
|
+
super
|
685
|
+
end
|
686
|
+
|
687
|
+
private :[]=
|
688
|
+
undef update, merge, merge!, replace
|
689
|
+
end
|
690
|
+
|
691
|
+
|
89
692
|
end
|
90
693
|
|
91
694
|
if __FILE__ == $0
|
@@ -101,7 +704,7 @@ if __FILE__ == $0
|
|
101
704
|
|
102
705
|
OptionParser.new do |parser|
|
103
706
|
parser.instance_eval do
|
104
|
-
self.banner
|
707
|
+
self.banner = <<-EOB.gsub(/^\t+/, "")
|
105
708
|
Usage: #{$0} [opts]
|
106
709
|
|
107
710
|
EOB
|
@@ -131,7 +734,7 @@ if __FILE__ == $0
|
|
131
734
|
opts[:foreground] = true
|
132
735
|
end
|
133
736
|
|
134
|
-
on("-n", "--name [
|
737
|
+
on("-n", "--name [user name or email address]") do |name|
|
135
738
|
opts[:name] = name
|
136
739
|
end
|
137
740
|
|
@@ -142,26 +745,29 @@ if __FILE__ == $0
|
|
142
745
|
opts[:logger] = Logger.new(opts[:log], "daily")
|
143
746
|
opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
|
144
747
|
|
145
|
-
def daemonize(foreground=false)
|
146
|
-
trap("SIGINT") { exit! 0 }
|
147
|
-
trap("SIGTERM") { exit! 0 }
|
148
|
-
trap("SIGHUP") { exit! 0 }
|
149
|
-
return yield if $DEBUG || foreground
|
150
|
-
Process.fork do
|
151
|
-
Process.setsid
|
152
|
-
Dir.chdir "/"
|
153
|
-
File.open("/dev/null") {|f|
|
154
|
-
STDIN.reopen f
|
155
|
-
STDOUT.reopen f
|
156
|
-
STDERR.reopen f
|
157
|
-
}
|
158
|
-
yield
|
159
|
-
end
|
160
|
-
exit! 0
|
161
|
-
end
|
162
|
-
|
163
|
-
daemonize(opts[:debug] || opts[:foreground]) do
|
748
|
+
# def daemonize(foreground=false)
|
749
|
+
# trap("SIGINT") { exit! 0 }
|
750
|
+
# trap("SIGTERM") { exit! 0 }
|
751
|
+
# trap("SIGHUP") { exit! 0 }
|
752
|
+
# return yield if $DEBUG || foreground
|
753
|
+
# Process.fork do
|
754
|
+
# Process.setsid
|
755
|
+
# Dir.chdir "/"
|
756
|
+
# File.open("/dev/null") {|f|
|
757
|
+
# STDIN.reopen f
|
758
|
+
# STDOUT.reopen f
|
759
|
+
# STDERR.reopen f
|
760
|
+
# }
|
761
|
+
# yield
|
762
|
+
# end
|
763
|
+
# exit! 0
|
764
|
+
# end
|
765
|
+
|
766
|
+
# daemonize(opts[:debug] || opts[:foreground]) do
|
164
767
|
Net::IRC::Server.new(opts[:host], opts[:port], WassrIrcGateway, opts).start
|
165
|
-
end
|
768
|
+
# end
|
166
769
|
end
|
167
770
|
|
771
|
+
# Local Variables:
|
772
|
+
# coding: utf-8
|
773
|
+
# End:
|