net-irc 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # -*- coding: UTF-8 -*-
2
3
  =begin
3
4
 
4
5
  # wig.rb
@@ -21,7 +22,7 @@ Configuration example for Tiarra ( http://coderepos.org/share/wiki/Tiarra ).
21
22
 
22
23
  wassr {
23
24
  host: localhost
24
- port: 16668
25
+ port: 16670
25
26
  name: username@example.com athack jabber=username@example.com:jabberpasswd tid=10 ratio=10:3:5
26
27
  password: password on Wassr
27
28
  in-encoding: utf8
@@ -181,8 +182,8 @@ class WassrIrcGateway < Net::IRC::Server::Session
181
182
  log "Client Options: #{@opts.inspect}"
182
183
  @log.info "Client Options: #{@opts.inspect}"
183
184
 
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
185
+ @ratio = Struct.new(:timeline, :friends, :channel).new(*(@opts["ratio"] || "10:3:5").split(":").map {|ratio| ratio.to_f })
186
+ @footing = @ratio.inject {|r,i| r + i }
186
187
 
187
188
  @timeline = []
188
189
  @check_friends_thread = Thread.start do
@@ -197,7 +198,7 @@ class WassrIrcGateway < Net::IRC::Server::Session
197
198
  @log.error "\t#{l}"
198
199
  end
199
200
  end
200
- sleep freq(friends_ratio / footing)
201
+ sleep freq(@ratio[:friends] / @footing)
201
202
  end
202
203
  end
203
204
 
@@ -217,7 +218,7 @@ class WassrIrcGateway < Net::IRC::Server::Session
217
218
  @log.error "\t#{l}"
218
219
  end
219
220
  end
220
- sleep freq(timeline_ratio / footing)
221
+ sleep freq(@ratio[:timeline] / @footing)
221
222
  end
222
223
  end
223
224
 
@@ -236,7 +237,7 @@ class WassrIrcGateway < Net::IRC::Server::Session
236
237
  @log.error "\t#{l}"
237
238
  end
238
239
  end
239
- sleep freq(channel_ratio / footing)
240
+ sleep freq(@ratio[:channel] / @footing)
240
241
  end
241
242
  end
242
243
  end
@@ -257,23 +258,26 @@ class WassrIrcGateway < Net::IRC::Server::Session
257
258
  begin
258
259
  if target =~ /^#(.+)/
259
260
  channel = Regexp.last_match[1]
260
- if @opts.key?("alwaysim") && @im && @im.connected? # in jabber mode, using jabber post
261
+ reply = message[/\s+>(.+)$/, 1]
262
+ message = Iconv.iconv("UTF-7", "UTF-8", message).join if @utf7
263
+ if !reply && @opts.key?("alwaysim") && @im && @im.connected? # in jabber mode, using jabber post
261
264
  message = "##{channel} #{message}" unless "##{channel}" == main_channel
262
265
  ret = @im.deliver(jabber_bot_id, message)
263
266
  post "#{nick}!#{nick}@#{api_base.host}", TOPIC, channel, untinyurl(message)
264
267
  else
265
268
  if "##{channel}" == main_channel
266
- ret = api("statuses/update", {"status" => message})
269
+ rid = rid_for(reply) if reply
270
+ ret = api("statuses/update", {"status" => message, "reply_status_rid" => rid})
267
271
  else
268
272
  ret = api("channel_message/update", {"name_en" => channel, "body" => message})
269
273
  end
274
+ log "Status Updated via API"
270
275
  end
271
276
  else
272
277
  # direct message
273
278
  ret = api("direct_messages/new", {"user" => target, "text" => message})
274
279
  end
275
280
  raise ApiFailed, "API failed" unless ret
276
- log "Status Updated" unless @im && @im.connected?
277
281
  rescue => e
278
282
  @log.error [retry_count, e.inspect].inspect
279
283
  if retry_count > 0
@@ -289,6 +293,14 @@ class WassrIrcGateway < Net::IRC::Server::Session
289
293
  def on_ctcp(target, message)
290
294
  _, command, *args = message.split(/\s+/)
291
295
  case command
296
+ when "utf7"
297
+ begin
298
+ require "iconv"
299
+ @utf7 = !@utf7
300
+ log "utf7 mode: #{@utf7 ? 'on' : 'off'}"
301
+ rescue LoadError => e
302
+ log "Can't load iconv."
303
+ end
292
304
  when "list"
293
305
  nick = args[0]
294
306
  @log.debug([ nick, message ])
@@ -298,41 +310,49 @@ class WassrIrcGateway < Net::IRC::Server::Session
298
310
  end
299
311
 
300
312
  unless res
301
- post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
313
+ post server_name, ERR_NOSUCHNICK, nick, "No such nick/channel"
302
314
  end
303
315
  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"]
316
+ target = args[0]
317
+ st = @tmap[target]
318
+ id = rid_for(target)
319
+ if st || id
320
+ unless id
321
+ if @im && @im.connected?
322
+ # IM のときはいろいろめんどうなことする
323
+ nick, count = *st
324
+ pos = @counters[nick] - count
325
+ @log.debug "%p %s %d/%d => %d" % [
326
+ st,
327
+ nick,
328
+ count,
329
+ @counters[nick],
330
+ pos
331
+ ]
332
+ res = api("statuses/user_timeline", { "id" => nick })
333
+ raise ApiFailed, "#{nick} may be private mode" if res.empty?
334
+ if res[pos]
335
+ id = res[pos]["rid"]
336
+ else
337
+ raise ApiFailed, "#{pos} of #{nick} is not found."
338
+ end
339
+ else
340
+ id = st["id"] || st["rid"]
341
+ end
322
342
  end
323
343
  res = api("favorites/create/#{id}", {})
324
- post nil, NOTICE, main_channel, "Fav: #{res["screen_name"]}: #{res["text"]}"
344
+ post server_name, NOTICE, main_channel, "Fav: #{res["screen_name"]}: #{res["text"]}"
325
345
  else
326
- post nil, NOTICE, main_channel, "No such id #{tid}"
346
+ post server_name, NOTICE, main_channel, "No such id or status #{target}"
327
347
  end
328
348
  when "link"
329
349
  tid = args[0]
330
350
  st = @tmap[tid]
331
351
  if st
332
352
  st["link"] = (api_base + "/#{st["user"]["screen_name"]}/statuses/#{st["id"]}").to_s unless st["link"]
333
- post nil, NOTICE, main_channel, st["link"]
353
+ post server_name, NOTICE, main_channel, st["link"]
334
354
  else
335
- post nil, NOTICE, main_channel, "No such id #{tid}"
355
+ post server_name, NOTICE, main_channel, "No such id #{tid}"
336
356
  end
337
357
  end
338
358
  rescue ApiFailed => e
@@ -343,12 +363,12 @@ class WassrIrcGateway < Net::IRC::Server::Session
343
363
  nick = m.params[0]
344
364
  f = (@friends || []).find {|i| i["screen_name"] == nick }
345
365
  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"
366
+ post server_name, RPL_WHOISUSER, @nick, nick, nick, api_base.host, "*", "#{f["name"]} / #{f["description"]}"
367
+ post server_name, RPL_WHOISSERVER, @nick, nick, api_base.host, api_base.to_s
368
+ post server_name, RPL_WHOISIDLE, @nick, nick, "0", "seconds idle"
369
+ post server_name, RPL_ENDOFWHOIS, @nick, nick, "End of WHOIS list"
350
370
  else
351
- post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
371
+ post server_name, ERR_NOSUCHNICK, nick, "No such nick/channel"
352
372
  end
353
373
  end
354
374
 
@@ -356,10 +376,15 @@ class WassrIrcGateway < Net::IRC::Server::Session
356
376
  channels = m.params[0].split(/\s*,\s*/)
357
377
  channels.each do |channel|
358
378
  next if channel == main_channel
359
- @channels[channel] = {
360
- :read => []
361
- }
362
- post "#{@nick}!#{@nick}@#{api_base.host}", JOIN, channel
379
+ res = api("channel/exists", { "name_en" => channel.sub(/^#/, "") })
380
+ if res["exists"]
381
+ @channels[channel] = {
382
+ :read => []
383
+ }
384
+ post "#{@nick}!#{@nick}@#{api_base.host}", JOIN, channel
385
+ else
386
+ post server_name, ERR_NOSUCHNICK, channel, "No such nick/channel"
387
+ end
363
388
  end
364
389
  end
365
390
 
@@ -381,20 +406,20 @@ class WassrIrcGateway < Net::IRC::Server::Session
381
406
  user = nick = f["screen_name"]
382
407
  host = serv = api_base.host
383
408
  real = f["name"]
384
- post nil, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
409
+ post server_name, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
385
410
  end
386
- post nil, RPL_ENDOFWHO, @nick, channel
411
+ post server_name, RPL_ENDOFWHO, @nick, channel
387
412
  when @groups.key?(channel)
388
413
  @groups[channel].each do |name|
389
414
  f = @friends.find {|i| i["screen_name"] == name }
390
415
  user = nick = f["screen_name"]
391
416
  host = serv = api_base.host
392
417
  real = f["name"]
393
- post nil, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
418
+ post server_name, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
394
419
  end
395
- post nil, RPL_ENDOFWHO, @nick, channel
420
+ post server_name, RPL_ENDOFWHO, @nick, channel
396
421
  else
397
- post nil, ERR_NOSUCHNICK, @nick, nick, "No such nick/channel"
422
+ post server_name, ERR_NOSUCHNICK, @nick, nick, "No such nick/channel"
398
423
  end
399
424
  end
400
425
 
@@ -455,6 +480,14 @@ class WassrIrcGateway < Net::IRC::Server::Session
455
480
  mesg = s["text"]
456
481
  @log.debug(mesg)
457
482
 
483
+ begin
484
+ require 'iconv'
485
+ mesg = mesg.sub(/^.+ > |^.+/) {|str| Iconv.iconv("UTF-8", "UTF-7", str).join }
486
+ mesg = "[utf7]: #{mesg}" if body =~ /[^a-z0-9\s]/i
487
+ rescue LoadError
488
+ rescue Iconv::IllegalSequence
489
+ end
490
+
458
491
  # added @user in no use @user reply message (Wassr only)
459
492
  if s.has_key?("reply_status_url") and s["reply_status_url"] and s["text"] !~ /^@.*/ and %r{([^/]+)/statuses/[^/]+}.match(s["reply_status_url"])
460
493
  reply_user_id = $1
@@ -490,11 +523,16 @@ class WassrIrcGateway < Net::IRC::Server::Session
490
523
  def check_friends
491
524
  first = true unless @friends
492
525
  @friends ||= []
493
- friends = api("statuses/friends")
526
+ friends = []
527
+ 1.upto(5) do |i|
528
+ f = api("statuses/friends", {"page" => i.to_s})
529
+ friends += f
530
+ break if f.length < 100
531
+ end
494
532
  if first && !@opts.key?("athack")
495
533
  @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"
534
+ post server_name, RPL_NAMREPLY, @nick, "=", main_channel, @friends.map{|i| "@#{i["screen_name"]}" }.join(" ")
535
+ post server_name, RPL_ENDOFNAMES, @nick, main_channel, "End of NAMES list"
498
536
  else
499
537
  prv_friends = @friends.map {|i| i["screen_name"] }
500
538
  now_friends = friends.map {|i| i["screen_name"] }
@@ -535,6 +573,13 @@ class WassrIrcGateway < Net::IRC::Server::Session
535
573
  if Regexp.last_match
536
574
  nick, id = Regexp.last_match.captures
537
575
  body = CGI.unescapeHTML(body)
576
+ begin
577
+ require 'iconv'
578
+ body = body.sub(/^.+ > |^.+/) {|str| Iconv.iconv("UTF-8", "UTF-7", str).join }
579
+ body = "[utf7]: #{body}" if body =~ /[^a-z0-9\s]/i
580
+ rescue LoadError
581
+ rescue Iconv::IllegalSequence
582
+ end
538
583
 
539
584
  case
540
585
  when nick == "投稿完了"
@@ -582,7 +627,7 @@ class WassrIrcGateway < Net::IRC::Server::Session
582
627
 
583
628
  uri = api_base.dup
584
629
  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("&")
630
+ uri.query = q.inject([]) {|r,(k,v)| v ? r << "#{k}=#{URI.escape(v, /[^-.!~*'()\w]/n)}" : r }.join("&")
586
631
 
587
632
 
588
633
  req = nil
@@ -642,6 +687,18 @@ class WassrIrcGateway < Net::IRC::Server::Session
642
687
  }
643
688
  end
644
689
 
690
+ # return rid of most recent matched status with text
691
+ def rid_for(text)
692
+ target = Regexp.new(Regexp.quote(text.strip), "i")
693
+ status = api("statuses/friends_timeline").find {|i|
694
+ next false if i["user_login_id"] == @nick # 自分は除外
695
+ i["text"] =~ target
696
+ }
697
+
698
+ @log.debug "Looking up status contains #{text.inspect} -> #{status.inspect}"
699
+ status ? status["rid"] : nil
700
+ end
701
+
645
702
  class TypableMap < Hash
646
703
  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
704
  case
@@ -9,7 +9,7 @@ require "monitor"
9
9
  module Net; end
10
10
 
11
11
  module Net::IRC
12
- VERSION = "0.0.5"
12
+ VERSION = "0.0.6"
13
13
  class IRCException < StandardError; end
14
14
 
15
15
  require "net/irc/constants"
@@ -209,6 +209,6 @@ module Net::IRC::Constants # :nodoc:
209
209
  end
210
210
 
211
211
  Net::IRC::COMMANDS = Net::IRC::Constants.constants.inject({}) {|r,i| # :nodoc:
212
- r.update(Net::IRC::Constants.const_get(i) => i)
212
+ r.update(Net::IRC::Constants.const_get(i).to_s => i.to_s)
213
213
  }
214
214
 
@@ -82,7 +82,7 @@ class Net::IRC::Message
82
82
  # If the message is CTCP, return true.
83
83
  def ctcp?
84
84
  message = @params[1]
85
- message[0] == 1 && message[message.length-1] == 1
85
+ message[0] == "\01"[0] && message[message.length-1] == "\01"[0]
86
86
  end
87
87
 
88
88
  def inspect
@@ -1,4 +1,4 @@
1
-
1
+ # coding: ASCII-8BIT
2
2
  module Net::IRC::PATTERN # :nodoc:
3
3
  # letter = %x41-5A / %x61-7A ; A-Z / a-z
4
4
  # digit = %x30-39 ; 0-9
@@ -56,7 +56,7 @@ module Net::IRC::PATTERN # :nodoc:
56
56
  # =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
57
57
  MIDDLE = "[#{NOSPCRLFCL}][:#{NOSPCRLFCL}]*"
58
58
  TRAILING = "[: #{NOSPCRLFCL}]*"
59
- PARAMS = "(?:((?: #{MIDDLE}){0,14})(?: :(#{TRAILING}))?|((?: #{MIDDLE}){14})(?::?)?(#{TRAILING}))"
59
+ PARAMS = "(?:((?: #{MIDDLE}){0,14})(?: :(#{TRAILING}))?|((?: #{MIDDLE}){14}):?(#{TRAILING}))"
60
60
 
61
61
  # crlf = %x0D %x0A ; "carriage return" "linefeed"
62
62
  # message = [ ":" prefix SPACE ] command [ params ] crlf
@@ -56,6 +56,7 @@ describe Net::IRC, "server and client" do
56
56
  @server.start
57
57
  end
58
58
 
59
+ Thread.pass
59
60
  true until @server.instance_variable_get(:@serv)
60
61
 
61
62
  @port = @server.instance_variable_get(:@serv).addr[1]
@@ -70,6 +71,9 @@ describe Net::IRC, "server and client" do
70
71
  })
71
72
  @client.start
72
73
  end
74
+
75
+ Thread.pass
76
+ true until @client
73
77
  end
74
78
 
75
79
  server_q = ChannelManagerTestServerSession.testq
@@ -79,6 +83,7 @@ describe Net::IRC, "server and client" do
79
83
  client = @client
80
84
  client.instance_variable_set(:@prefix, Prefix.new("foonick!foouser@localhost"))
81
85
 
86
+ true until ChannelManagerTestServerSession.instance
82
87
  ChannelManagerTestServerSession.instance.instance_eval do
83
88
  Thread.exclusive do
84
89
  post client.prefix, JOIN, "#test"
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
+ # ENCODING=ASCII-8BIT
3
+ # :vim:encoding=utf-8:
4
+ # ↑ どうするのが正解?
2
5
 
3
6
  $LOAD_PATH << "lib"
4
7
  $LOAD_PATH << "../lib"
@@ -229,18 +232,19 @@ describe Net::IRC, "server and client" do
229
232
  @server, @client = nil, nil
230
233
 
231
234
  Thread.abort_on_exception = true
232
- Thread.start do
235
+ @tserver = Thread.start do
233
236
  @server = Net::IRC::Server.new("localhost", @port, TestServerSession, {
234
237
  :logger => Logger.new(nil),
235
238
  })
236
239
  @server.start
237
240
  end
238
241
 
242
+ Thread.pass
239
243
  true until @server.instance_variable_get(:@serv)
240
244
 
241
245
  @port = @server.instance_variable_get(:@serv).addr[1]
242
246
 
243
- Thread.start do
247
+ @tclient = Thread.start do
244
248
  @client = TestClient.new("localhost", @port, {
245
249
  :nick => "foonick",
246
250
  :user => "foouser",
@@ -250,6 +254,9 @@ describe Net::IRC, "server and client" do
250
254
  })
251
255
  @client.start
252
256
  end
257
+
258
+ Thread.pass
259
+ true until @client
253
260
  end
254
261
 
255
262
  server_q = TestServerSession.testq
@@ -285,41 +292,20 @@ describe Net::IRC, "server and client" do
285
292
  post nil, NOTICE, "#test", "sep1"
286
293
  end
287
294
  end
295
+ Thread.pass
288
296
  true until client_q.pop.to_s == "NOTICE #test sep1\r\n"
289
297
  client.prefix.should == "foonick"
290
298
  end
291
299
 
292
- it "should destroy closed session" do
293
- oldclient = @client
294
- @client.finish
295
-
296
- Thread.start do
297
- @client = TestClient.new("localhost", @port, {
298
- :nick => "foonick",
299
- :user => "foouser",
300
- :real => "foo real name",
301
- :pass => "foopass",
302
- :logger => Logger.new(nil),
303
- })
304
- @client.start
305
- end
306
-
307
- Thread.pass
308
- true while @client == oldclient
309
-
310
- c = @client.instance_variable_get(:@channels)
311
- TestServerSession.instance.instance_eval do
312
- Thread.exclusive do
313
- post nil, NOTICE, "#test", "sep1"
314
- end
315
- end
316
-
317
- true until client_q.pop.to_s == "NOTICE #test sep1\r\n"
318
- end
300
+ # it "should destroy closed session" do
301
+ # end
319
302
 
320
303
  after :all do
321
304
  @server.finish
322
305
  @client.finish
306
+ @tserver.kill
307
+ @tclient.kill
308
+ @server = @client = @tserver = @tclient = nil
323
309
  end
324
310
  end
325
311