net-irc 0.0.5 → 0.0.6

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.
@@ -530,8 +530,8 @@ if __FILE__ == $0
530
530
  Dir.chdir "/"
531
531
  File.open("/dev/null") {|f|
532
532
  STDIN.reopen f
533
- STDOUT.reopen f
534
- STDERR.reopen f
533
+ STDOUT.reopen f, 'w'
534
+ STDERR.reopen f, 'w'
535
535
  }
536
536
  yield
537
537
  end
@@ -275,6 +275,7 @@ module Lingr
275
275
 
276
276
  def post(url, params)
277
277
  if !params.find {|p| p[1].is_a?(Hash)}
278
+ params = params.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash}
278
279
  parse_result Net::HTTP.post_form(URI.parse(url), params)
279
280
  else
280
281
  boundary = 'lingr-api-client' + (0x1000000 + rand(0x1000000).to_s(16))
@@ -40,8 +40,10 @@ class Mixi
40
40
  @agent.submit form
41
41
 
42
42
  page = @agent.get "http://mixi.jp/home.pl"
43
- page = @agent.get page.links[18].uri
44
- form = page.forms[(@mixi_premium ? 1 : 0)]
43
+ #page = @agent.get page.links[18].uri
44
+ page = @agent.get page.links[14].uri
45
+ form = page.forms[1]
46
+ #form = page.forms[(@mixi_premium ? 1 : 0)]
45
47
  form.diary_title = title
46
48
  form.diary_body = self.class.magic_body(body)
47
49
  get_image images
@@ -54,13 +56,13 @@ class Mixi
54
56
  form.file_uploads[i].file_name = img
55
57
  end
56
58
  page = @agent.submit form
57
- page = @agent.submit page.forms[0]
59
+ page = @agent.submit page.forms[1]
58
60
  end
59
61
 
60
62
  def get_latest
61
63
  page = @agent.get 'http://mixi.jp/list_diary.pl'
62
- ["http://mixi.jp/" << page.links[37].uri.to_s.toutf8,
63
- page.links[37].text.toutf8]
64
+ ["http://mixi.jp/" << page.links[33].uri.to_s.toutf8,
65
+ page.links[33].text.toutf8]
64
66
  end
65
67
 
66
68
  def self.magic_body(body)
@@ -128,17 +130,30 @@ class MixiDiary < Net::IRC::Server::Session
128
130
  def on_privmsg(m)
129
131
  super
130
132
 
133
+ # CTCP にしたほうがよくないか?
131
134
  case m[1]
132
135
  when "."
133
- title, body = *@cont
134
- @mixi.post ">_<× < #{title}".toeuc, body.toeuc, []
136
+ title, *body = *@cont
137
+ @mixi.post ">_<× < #{title}".toeuc, body.join("\n").toeuc, []
135
138
  @mixi.get_latest.each do |line|
136
139
  post server_name, NOTICE, main_channel, line.chomp
137
140
  end
138
- when " "
141
+ when "c"
139
142
  @cont.clear
143
+ post server_name, NOTICE, main_channel, "cleared."
144
+ when "p"
145
+ @cont.each do |l|
146
+ post server_name, NOTICE, main_channel, l
147
+ end
148
+ post server_name, NOTICE, main_channel, "^^end"
149
+ when "d"
150
+ post server_name, NOTICE, main_channel, "Deleted last line: #{@cont.pop}"
140
151
  else
141
152
  @cont << m[1]
153
+ if @cont.size == 1
154
+ post server_name, NOTICE, main_channel, "start with title: #{@cont.first}"
155
+ else
156
+ end
142
157
  end
143
158
  end
144
159
 
@@ -63,7 +63,7 @@ class NowaIrcGateway < TwitterIrcGateway
63
63
  end
64
64
 
65
65
  def api_base
66
- URI("https://api.nowa.jp/")
66
+ URI("https://api.nowa.jp:443/")
67
67
  end
68
68
 
69
69
  def api_source
@@ -62,7 +62,7 @@ class ServerLogIrcGateway < Net::IRC::Server::Session
62
62
  :topic => "",
63
63
  :observer => nil,
64
64
  } unless @channels.key?(channel)
65
- post @prefix, JOIN, m.params.first
65
+ post @prefix, JOIN, channel
66
66
  post nil, RPL_NAMREPLY, @prefix.nick, "=", channel, "@#{@prefix.nick}"
67
67
  post nil, RPL_ENDOFNAMES, @prefix.nick, channel, "End of NAMES list"
68
68
  end
@@ -99,9 +99,10 @@ class ServerLogIrcGateway < Net::IRC::Server::Session
99
99
  nsize = File.size(f)
100
100
  if nsize > size
101
101
  @log.debug "follow up log"
102
- l = f.gets
103
- if grep === l
104
- post name, PRIVMSG, chan, l
102
+ while l = f.gets
103
+ if grep === l
104
+ post name, PRIVMSG, chan, l
105
+ end
105
106
  end
106
107
  end
107
108
  size = nsize
@@ -16,14 +16,14 @@ If you want to help:
16
16
 
17
17
  ## Configuration
18
18
 
19
- Options specified by after irc realname.
19
+ Options specified by after IRC realname.
20
20
 
21
21
  Configuration example for Tiarra ( http://coderepos.org/share/wiki/Tiarra ).
22
22
 
23
23
  twitter {
24
24
  host: localhost
25
25
  port: 16668
26
- name: username@example.com athack jabber=username@example.com:jabberpasswd tid=10 ratio=32:1 replies=6
26
+ name: username@example.com athack jabber=username@example.com:jabberpasswd tid ratio=32:1 replies=6 maxlimit=70
27
27
  password: password on Twitter
28
28
  in-encoding: utf8
29
29
  out-encoding: utf8
@@ -40,11 +40,11 @@ it's good for Twitter like reply command (@nick).
40
40
  In this case, you will see torrent of join messages after connected,
41
41
  because NAMES list can't send @ leading nick (it interpreted op.)
42
42
 
43
- ### tid=<color>
43
+ ### tid[=<color>]
44
44
 
45
- Apply id to each message for make favorites by CTCP ACTION.
45
+ Apply ID to each message for make favorites by CTCP ACTION.
46
46
 
47
- /me fav id
47
+ /me fav ID [ID...]
48
48
 
49
49
  <color> can be
50
50
 
@@ -86,7 +86,44 @@ Use IM instead of any APIs (e.g. post)
86
86
 
87
87
  ### replies[=<ratio>]
88
88
 
89
- ### checkrls[=<interval seconds>]
89
+ ### maxlimit=<hourly limit>
90
+
91
+ ### checkrls=<interval seconds>
92
+
93
+ ### secure
94
+
95
+ Force SSL for API.
96
+
97
+ ## Extended commands through the CTCP ACTION
98
+
99
+ ### list (ls)
100
+
101
+ /me list NICK_or_screen_name
102
+
103
+ ### fav (favorite, favourite, unfav, unfavorite, unfavourite)
104
+
105
+ /me fav ID [ID...]
106
+ /me unfav ID [ID...]
107
+
108
+ ### link (ln)
109
+
110
+ /me link ID [ID...]
111
+
112
+ ### destroy (del, delete, miss, oops, remove, rm)
113
+
114
+ /me destroy ID [ID...]
115
+
116
+ ### in (location)
117
+
118
+ /me in Tokyo, Japan
119
+
120
+ ### reply (re)
121
+
122
+ /me reply ID blah, blah...
123
+
124
+ ### utf7
125
+
126
+ /me utf7
90
127
 
91
128
  ## License
92
129
 
@@ -101,7 +138,6 @@ $KCODE = "u" # json use this
101
138
 
102
139
  require "rubygems"
103
140
  require "net/irc"
104
- require "net/http"
105
141
  require "net/https"
106
142
  require "uri"
107
143
  require "json"
@@ -140,7 +176,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
140
176
  end
141
177
 
142
178
  def hourly_limit
143
- 20
179
+ 60
144
180
  end
145
181
 
146
182
  class ApiFailed < StandardError; end
@@ -149,7 +185,8 @@ class TwitterIrcGateway < Net::IRC::Server::Session
149
185
  super
150
186
  @groups = {}
151
187
  @channels = [] # joined channels (groups)
152
- @user_agent = "#{self.class}/#{server_version} (tig.rb)"
188
+ @nicknames = {}
189
+ @user_agent = "#{self.class}/#{server_version} (#{File.basename(__FILE__)})"
153
190
  @config = Pathname.new(ENV["HOME"]) + ".tig"
154
191
  @map = nil
155
192
  load_config
@@ -176,7 +213,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
176
213
  start_jabber(jid, pass)
177
214
  rescue LoadError
178
215
  log "Failed to start Jabber."
179
- log 'Installl "xmpp4r-simple" gem or check your id/pass.'
216
+ log 'Installl "xmpp4r-simple" gem or check your ID/pass.'
180
217
  finish
181
218
  end
182
219
  else
@@ -189,9 +226,11 @@ class TwitterIrcGateway < Net::IRC::Server::Session
189
226
  @log.info "Client Options: #{@opts.inspect}"
190
227
 
191
228
  @hourly_limit = hourly_limit
229
+
192
230
  @check_rate_limit_thread = Thread.start do
193
231
  loop do
194
232
  begin
233
+ check_downtime
195
234
  check_rate_limit
196
235
  rescue ApiFailed => e
197
236
  @log.error e.inspect
@@ -201,18 +240,17 @@ class TwitterIrcGateway < Net::IRC::Server::Session
201
240
  @log.error "\t#{l}"
202
241
  end
203
242
  end
204
- sleep @opts["checkrls"] || 3600 # 1 time / hour
243
+ sleep @opts["checkrls"] || 3600 # 1 hour
205
244
  end
206
245
  end
207
246
  sleep 5
208
247
 
209
- timeline_ratio, friends_ratio = (@opts["ratio"] || "10:3").split(":").map {|ratio| ratio.to_i }
210
- footing = (timeline_ratio + friends_ratio).to_f
248
+ @ratio = Struct.new(:timeline, :friends, :replies).new(*(@opts["ratio"] || "10:3").split(":").map {|ratio| ratio.to_f })
249
+ @ratio[:replies] = @opts.key?("replies") ? (@opts["replies"] || 5).to_f : 0.0
211
250
 
212
- if @opts.key?("replies")
213
- replies_ratio ||= (@opts["replies"] || 5).to_i
214
- footing += replies_ratio
215
- end
251
+ footing = @ratio.inject {|sum, ratio| sum + ratio }
252
+
253
+ @ratio.each_pair {|m, v| @ratio[m] = v / footing }
216
254
 
217
255
  @timeline = []
218
256
  @check_friends_thread = Thread.start do
@@ -227,7 +265,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
227
265
  @log.error "\t#{l}"
228
266
  end
229
267
  end
230
- sleep freq(friends_ratio / footing)
268
+ sleep freq(@ratio[:friends])
231
269
  end
232
270
  end
233
271
 
@@ -247,7 +285,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
247
285
  @log.error "\t#{l}"
248
286
  end
249
287
  end
250
- sleep freq(timeline_ratio / footing)
288
+ sleep freq(@ratio[:timeline])
251
289
  end
252
290
  end
253
291
 
@@ -266,7 +304,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
266
304
  @log.error "\t#{l}"
267
305
  end
268
306
  end
269
- sleep freq(replies_ratio / footing)
307
+ sleep freq(@ratio[:replies])
270
308
  end
271
309
  end
272
310
  end
@@ -285,17 +323,18 @@ class TwitterIrcGateway < Net::IRC::Server::Session
285
323
  retry_count = 3
286
324
  ret = nil
287
325
  target, message = *m.params
326
+ message = Iconv.iconv("UTF-7", "UTF-8", message).join if @utf7
288
327
  begin
289
328
  if target =~ /^#/
290
329
  if @opts.key?("alwaysim") && @im && @im.connected? # in jabber mode, using jabber post
291
330
  ret = @im.deliver(jabber_bot_id, message)
292
331
  post "#{nick}!#{nick}@#{api_base.host}", TOPIC, main_channel, untinyurl(message)
293
332
  else
294
- ret = api("statuses/update", {"status" => message})
333
+ ret = api("statuses/update", { :status => message })
295
334
  end
296
335
  else
297
336
  # direct message
298
- ret = api("direct_messages/new", {"user" => target, "text" => message})
337
+ ret = api("direct_messages/new", { :user => target, :text => message })
299
338
  end
300
339
  raise ApiFailed, "API failed" unless ret
301
340
  log "Status Updated"
@@ -314,35 +353,131 @@ class TwitterIrcGateway < Net::IRC::Server::Session
314
353
  def on_ctcp(target, message)
315
354
  _, command, *args = message.split(/\s+/)
316
355
  case command
317
- when "list"
318
- nick = args[0]
319
- @log.debug([ nick, message ])
320
- res = api("statuses/user_timeline", { "id" => nick }).reverse_each do |s|
356
+ when "call" # /me call <twitter-id> as <nickname>
357
+ twitter_id = args[0]
358
+ nickname = args[2] || args[1] # allow omitting 'as'
359
+ if nickname == "is"
360
+ @nicknames.delete(twitter_id)
361
+ post server_name, NOTICE, main_channel, "Removed nickname for #{twitter_id}"
362
+ else
363
+ @nicknames[twitter_id] = nickname
364
+ post server_name, NOTICE, main_channel, "Call #{twitter_id} as #{nickname}"
365
+ end
366
+ when "utf7"
367
+ begin
368
+ require "iconv"
369
+ @utf7 = !@utf7
370
+ log "UTF-7 mode: #{@utf7 ? 'on' : 'off'}"
371
+ rescue LoadError => e
372
+ log "Can't load iconv."
373
+ end
374
+ when "list", "ls"
375
+ nick = args.first
376
+ unless (1..200).include?(count = args[1].to_i)
377
+ count = 20
378
+ end
379
+ @log.debug([nick, message])
380
+ to = nick == @nick ? server_name : nick
381
+ res = api("statuses/user_timeline", { :id => nick, :count => "#{count}" }).reverse_each do |s|
321
382
  @log.debug(s)
322
- post nick, NOTICE, main_channel, "#{generate_status_message(s)}"
383
+ post to, NOTICE, main_channel, "#{generate_status_message(s)}"
323
384
  end
324
-
325
385
  unless res
326
- post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
386
+ post server_name, ERR_NOSUCHNICK, nick, "No such nick/channel"
327
387
  end
328
- when "fav"
329
- tid = args[0]
330
- st = @tmap[tid]
331
- if st
332
- id = st["id"] || st["rid"]
333
- res = api("favorites/create/#{id}", {})
334
- post nil, NOTICE, main_channel, "Fav: #{res["screen_name"]}: #{res["text"]}"
335
- else
336
- post nil, NOTICE, main_channel, "No such id #{tid}"
388
+ when /^(un)?fav(?:ou?rite)?$/
389
+ method, pfx = $1.nil? ? ["create", "F"] : ["destroy", "Unf"]
390
+ args.each_with_index do |tid, i|
391
+ st = @tmap[tid]
392
+ if st
393
+ sleep 1 if i > 0
394
+ res = api("favorites/#{method}/#{st["id"]}")
395
+ post server_name, NOTICE, main_channel, "#{pfx}av: #{res["user"]["screen_name"]}: #{res["text"]}"
396
+ else
397
+ post server_name, NOTICE, main_channel, "No such ID #{tid}"
398
+ end
399
+ end
400
+ when "link", "ln"
401
+ args.each do |tid|
402
+ st = @tmap[tid]
403
+ if st
404
+ post server_name, NOTICE, main_channel, "#{api_base + st["user"]["screen_name"]}/statuses/#{st["id"]}"
405
+ else
406
+ post server_name, NOTICE, main_channel, "No such ID #{tid}"
407
+ end
408
+ end
409
+ # when /^ratios?$/
410
+ # if args[1].nil? ||
411
+ # @opts.key?("replies") && args[2].nil?
412
+ # return post server_name, NOTICE, main_channel, "/me ratios <timeline> <frends>[ <replies>]"
413
+ # end
414
+ # ratios = args.map {|ratio| ratio.to_f }
415
+ # if ratios.any? {|ratio| ratio <= 0.0 }
416
+ # return post server_name, NOTICE, main_channel, "Ratios must be greater than 0."
417
+ # end
418
+ # footing = ratios.inject {|sum, ratio| sum + ratio }
419
+ # @ratio[:timeline] = ratios[0]
420
+ # @ratio[:friends] = ratios[1]
421
+ # @ratio[:replies] = ratios[2] || 0.0
422
+ # @ratio.each_pair {|m, v| @ratio[m] = v / footing }
423
+ # intervals = @ratio.map {|ratio| freq ratio }
424
+ # post server_name, NOTICE, main_channel, "Intervals: #{intervals.join(", ")}"
425
+ when /^(?:de(?:stroy|l(?:ete)?)|miss|oops|r(?:emove|m))$/ # destroy, delete, del, remove, rm, miss, oops
426
+ args.each_with_index do |tid, i|
427
+ st = @tmap[tid]
428
+ if st
429
+ sleep 1 if i > 0
430
+ res = api("statuses/destroy/#{st["id"]}")
431
+ post server_name, NOTICE, main_channel, "Destroyed: #{res["text"]}"
432
+ else
433
+ post server_name, NOTICE, main_channel, "No such ID #{tid}"
434
+ end
435
+ end
436
+ when "name"
437
+ name = message.split(/\s+/, 3)[2]
438
+ unless name.nil?
439
+ api("account/update_profile", { :name => name })
440
+ post server_name, NOTICE, main_channel, "You are named #{name}."
337
441
  end
338
- when "link"
339
- tid = args[0]
442
+ when "email"
443
+ # FIXME
444
+ email = args.first
445
+ unless email.nil?
446
+ api("account/update_profile", { :email => email })
447
+ end
448
+ when "url"
449
+ # FIXME
450
+ url = args.first || ""
451
+ api("account/update_profile", { :url => url })
452
+ when "in", "location"
453
+ location = message.split(/\s+/, 3)[2] || ""
454
+ api("account/update_profile", { :location => location })
455
+ location = location.empty? ? "nowhere" : "in #{location}"
456
+ post server_name, NOTICE, main_channel, "You are #{location} now."
457
+ when /^desc(?:ription)?$/
458
+ # FIXME
459
+ description = message.split(/\s+/, 3)[2] || ""
460
+ api("account/update_profile", { :description => description })
461
+ when /^colou?rs?$/
462
+ # FIXME
463
+ # bg, text, link, fill and border
464
+ when "image", "img"
465
+ # FIXME
466
+ url = args.first
467
+ # TODO: DCC SEND
468
+ when "follow"
469
+ # FIXME
470
+ when "leave"
471
+ # FIXME
472
+ when /^re(?:ply)?$/
473
+ tid = args.first
340
474
  st = @tmap[tid]
341
475
  if st
342
- st["link"] = (api_base + "/#{st["user"]["screen_name"]}/statuses/#{st["id"]}").to_s unless st["link"]
343
- post nil, NOTICE, main_channel, st["link"]
344
- else
345
- post nil, NOTICE, main_channel, "No such id #{tid}"
476
+ msg = message.split(/\s+/, 4)[3]
477
+ ret = api("statuses/update", { :status => msg, :in_reply_to_status_id => "#{st["id"]}" })
478
+ if ret
479
+ log "Status updated (In reply to \x03#{@opts["tid"] || 10}[#{tid}]\x0f <#{api_base + st["user"]["screen_name"]}/statuses/#{st["id"]}>)"
480
+ end
346
481
  end
347
482
  end
348
483
  rescue ApiFailed => e
@@ -353,12 +488,12 @@ class TwitterIrcGateway < Net::IRC::Server::Session
353
488
  nick = m.params[0]
354
489
  f = (@friends || []).find {|i| i["screen_name"] == nick }
355
490
  if f
356
- post nil, RPL_WHOISUSER, @nick, nick, nick, api_base.host, "*", "#{f["name"]} / #{f["description"]}"
357
- post nil, RPL_WHOISSERVER, @nick, nick, api_base.host, api_base.to_s
358
- post nil, RPL_WHOISIDLE, @nick, nick, "0", "seconds idle"
359
- post nil, RPL_ENDOFWHOIS, @nick, nick, "End of WHOIS list"
491
+ post server_name, RPL_WHOISUSER, @nick, nick, nick, api_base.host, "*", "#{f["name"]} / #{f["description"]}"
492
+ post server_name, RPL_WHOISSERVER, @nick, nick, api_base.host, api_base.to_s
493
+ post server_name, RPL_WHOISIDLE, @nick, nick, "0", "seconds idle"
494
+ post server_name, RPL_ENDOFWHOIS, @nick, nick, "End of WHOIS list"
360
495
  else
361
- post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
496
+ post server_name, ERR_NOSUCHNICK, nick, "No such nick/channel"
362
497
  end
363
498
  end
364
499
 
@@ -373,20 +508,20 @@ class TwitterIrcGateway < Net::IRC::Server::Session
373
508
  user = nick = f["screen_name"]
374
509
  host = serv = api_base.host
375
510
  real = f["name"]
376
- post nil, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
511
+ post server_name, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
377
512
  end
378
- post nil, RPL_ENDOFWHO, @nick, channel
513
+ post server_name, RPL_ENDOFWHO, @nick, channel
379
514
  when @groups.key?(channel)
380
515
  @groups[channel].each do |name|
381
516
  f = @friends.find {|i| i["screen_name"] == name }
382
517
  user = nick = f["screen_name"]
383
518
  host = serv = api_base.host
384
519
  real = f["name"]
385
- post nil, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
520
+ post server_name, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
386
521
  end
387
- post nil, RPL_ENDOFWHO, @nick, channel
522
+ post server_name, RPL_ENDOFWHO, @nick, channel
388
523
  else
389
- post nil, ERR_NOSUCHNICK, @nick, nick, "No such nick/channel"
524
+ post server_name, ERR_NOSUCHNICK, @nick, nick, "No such nick/channel"
390
525
  end
391
526
  end
392
527
 
@@ -440,35 +575,34 @@ class TwitterIrcGateway < Net::IRC::Server::Session
440
575
 
441
576
  private
442
577
  def check_timeline
443
- @prev_time ||= Time.at(0)
444
- api("statuses/friends_timeline", {"since" => @prev_time.httpdate}).reverse_each do |s|
445
- id = s["id"] || s["rid"]
578
+ q = { :count => "117" }
579
+ q[:since_id] = @timeline.last.to_s if @timeline.last
580
+ api("statuses/friends_timeline", q).reverse_each do |s|
581
+ id = s["id"]
446
582
  next if id.nil? || @timeline.include?(id)
583
+
447
584
  @timeline << id
448
585
  nick = s["user"]["screen_name"]
449
586
  mesg = generate_status_message(s)
587
+ tid = @tmap.push(s)
450
588
 
451
- tid = @tmap.push(s)
589
+ if @opts.key?("tid")
590
+ mesg = "%s \x03%s[%s]" % [mesg, @opts["tid"] || 10, tid]
591
+ end
452
592
 
453
593
  @log.debug [id, nick, mesg]
454
- if nick == @nick # 自分のときは topic
594
+ if nick == @nick # 自分のときは TOPIC
455
595
  post "#{nick}!#{nick}@#{api_base.host}", TOPIC, main_channel, untinyurl(mesg)
456
596
  else
457
- if @opts["tid"]
458
- message(nick, main_channel, "%s \x03%s [%s]" % [mesg, @opts["tid"], tid])
459
- else
460
- message(nick, main_channel, "%s" % [mesg, tid])
461
- end
597
+ message(nick, main_channel, mesg)
462
598
  end
463
599
  @groups.each do |channel, members|
464
- if members.include?(nick)
465
- message(nick, channel, "%s [%s]" % [mesg, tid])
466
- end
600
+ next unless members.include?(nick)
601
+ message(nick, channel, mesg)
467
602
  end
468
603
  end
469
604
  @log.debug "@timeline.size = #{@timeline.size}"
470
- @timeline = @timeline.last(100)
471
- @prev_time = Time.now
605
+ @timeline = @timeline.last(200)
472
606
  end
473
607
 
474
608
  def generate_status_message(status)
@@ -476,47 +610,53 @@ class TwitterIrcGateway < Net::IRC::Server::Session
476
610
  mesg = s["text"]
477
611
  @log.debug(mesg)
478
612
 
613
+ begin
614
+ require "iconv"
615
+ mesg = mesg.sub(/^.+ > |^.+/) {|str| Iconv.iconv("UTF-8", "UTF-7", str).join }
616
+ mesg = "[utf7]: #{mesg}" if body =~ /[^a-z0-9\s]/i
617
+ rescue LoadError
618
+ rescue Iconv::IllegalSequence
619
+ end
620
+
479
621
  # time = Time.parse(s["created_at"]) rescue Time.now
480
- m = { "&quot;" => "\"", "&lt;"=> "<", "&gt;"=> ">", "&amp;"=> "&", "\n" => " "}
481
- mesg.gsub!(/(#{m.keys.join("|")})/) { m[$1] }
622
+ m = { "&quot;" => "\"", "&lt;" => "<", "&gt;" => ">", "&amp;" => "&", "\n" => " " }
623
+ mesg.gsub!(/#{m.keys.join("|")}/) { m[$&] }
482
624
  mesg
483
625
  end
484
626
 
485
627
  def check_replies
486
- @prev_time_r ||= Time.now
628
+ time = @prev_time_r || Time.now
629
+ @prev_time_r = Time.now
487
630
  api("statuses/replies").reverse_each do |s|
488
- id = s["id"] || s["rid"]
631
+ id = s["id"]
489
632
  next if id.nil? || @timeline.include?(id)
490
- time = Time.parse(s["created_at"]) rescue next
491
- next if time < @prev_time_r
492
- @timeline << id
493
- nick = s["user_login_id"] || s["user"]["screen_name"]
633
+
634
+ created_at = Time.parse(s["created_at"]) rescue next
635
+ next if created_at < time
636
+
637
+ nick = s["user"]["screen_name"]
494
638
  mesg = generate_status_message(s)
639
+ tid = @tmap.push(s)
495
640
 
496
- tid = @tmap.push(s)
641
+ if @opts.key?("tid")
642
+ mesg = "%s \x03%s[%s]" % [mesg, @opts["tid"] || 10, tid]
643
+ end
497
644
 
498
645
  @log.debug [id, nick, mesg]
499
- if @opts["tid"]
500
- message(nick, main_channel, "%s \x03%s [%s]" % [mesg, @opts["tid"], tid])
501
- else
502
- message(nick, main_channel, "%s" % mesg)
503
- end
646
+ message nick, main_channel, mesg
504
647
  end
505
- @log.debug "@timeline.size = #{@timeline.size}"
506
- @timeline = @timeline.last(100)
507
- @prev_time_r = Time.now
508
648
  end
509
649
 
510
650
  def check_direct_messages
511
- @prev_time_d ||= Time.now
512
- api("direct_messages", {"since" => @prev_time_d.httpdate}).reverse_each do |s|
651
+ time = @prev_time_d || Time.now
652
+ @prev_time_d = Time.now
653
+ api("direct_messages", { :since => time.httpdate }).reverse_each do |s|
513
654
  nick = s["sender_screen_name"]
514
655
  mesg = s["text"]
515
656
  time = Time.parse(s["created_at"])
516
657
  @log.debug [nick, mesg, time].inspect
517
658
  message(nick, @nick, mesg)
518
659
  end
519
- @prev_time_d = Time.now
520
660
  end
521
661
 
522
662
  def check_friends
@@ -525,8 +665,8 @@ class TwitterIrcGateway < Net::IRC::Server::Session
525
665
  friends = api("statuses/friends")
526
666
  if first && !@opts.key?("athack")
527
667
  @friends = friends
528
- post nil, RPL_NAMREPLY, @nick, "=", main_channel, @friends.map{|i| "@#{i["screen_name"]}" }.join(" ")
529
- post nil, RPL_ENDOFNAMES, @nick, main_channel, "End of NAMES list"
668
+ post server_name, RPL_NAMREPLY, @nick, "=", main_channel, @friends.map{|i| "@#{i["screen_name"]}" }.join(" ")
669
+ post server_name, RPL_ENDOFNAMES, @nick, main_channel, "End of NAMES list"
530
670
  else
531
671
  prv_friends = @friends.map {|i| i["screen_name"] }
532
672
  now_friends = friends.map {|i| i["screen_name"] }
@@ -548,19 +688,37 @@ class TwitterIrcGateway < Net::IRC::Server::Session
548
688
 
549
689
  def check_rate_limit
550
690
  @log.debug rate_limit = api("account/rate_limit_status")
551
- if @hourly_limit != rate_limit["hourly_limit"]
552
- log "Rate limit changed: #{@hourly_limit} to #{rate_limit["hourly_limit"]}"
553
- @log.info "Rate limit changed: #{@hourly_limit} to #{rate_limit["hourly_limit"]}"
691
+ if rate_limit.key?("hourly_limit") && @hourly_limit != rate_limit["hourly_limit"]
692
+ msg = "Rate limit was changed: #{@hourly_limit} to #{rate_limit["hourly_limit"]}"
693
+ log msg
694
+ @log.info msg
554
695
  @hourly_limit = rate_limit["hourly_limit"]
555
696
  end
556
697
  # rate_limit["remaining_hits"] < 1
557
698
  # rate_limit["reset_time_in_seconds"] - Time.now.to_i
558
699
  end
559
700
 
701
+ def check_downtime
702
+ @prev_downtime ||= nil
703
+ schedule = api("help/downtime_schedule", {}, { :suppress_errors => true })["error"]
704
+ if @prev_downtime != schedule && @prev_downtime = schedule
705
+ msg = schedule.gsub(%r{[\r\n]|<style(?:\s[^>]*)?>.*?</style\s*>}m, "")
706
+ uris = URI.extract(msg)
707
+ uris.each do |uri|
708
+ msg << " #{uri}"
709
+ end
710
+ msg.gsub!(/<[^>]+>/, "")
711
+ log "\002\037#{msg}\017"
712
+ # TODO: sleeping for the downtime
713
+ end
714
+ end
715
+
560
716
  def freq(ratio)
561
- ret = 3600 / (@hourly_limit * ratio).round
562
- @log.debug "Frequency: #{ret}"
563
- ret
717
+ max = (@opts["maxlimit"] || 100).to_i
718
+ limit = @hourly_limit < max ? @hourly_limit : max
719
+ f = 3600 / (limit * ratio).round
720
+ @log.debug "Frequency: #{f}"
721
+ f
564
722
  end
565
723
 
566
724
  def start_jabber(jid, pass)
@@ -575,6 +733,15 @@ class TwitterIrcGateway < Net::IRC::Server::Session
575
733
  if msg.from.strip == jabber_bot_id
576
734
  # Twitter -> 'id: msg'
577
735
  body = msg.body.sub(/^(.+?)(?:\((.+?)\))?: /, "")
736
+
737
+ begin
738
+ require "iconv"
739
+ body = body.sub(/^.+ > |^.+/) {|str| Iconv.iconv("UTF-8", "UTF-7", str).join }
740
+ body = "[utf7]: #{body}" if body =~ /[^a-z0-9\s]/i
741
+ rescue LoadError
742
+ rescue Iconv::IllegalSequence
743
+ end
744
+
578
745
  if Regexp.last_match
579
746
  nick, id = Regexp.last_match.captures
580
747
  body = CGI.unescapeHTML(body)
@@ -612,40 +779,72 @@ class TwitterIrcGateway < Net::IRC::Server::Session
612
779
  rescue Errno::ENOENT
613
780
  end
614
781
 
615
- def api(path, q={})
782
+ def require_post?(path)
783
+ %r{
784
+ ^
785
+ (?: status(?:es)?/update $
786
+ | direct_messages/new $
787
+ | friendships/create/
788
+ | account/ (?: end_session $
789
+ | update_ )
790
+ | favou?ri(?:ing|tes)/create/
791
+ | notifications/
792
+ | blocks/create/ )
793
+ }x === path
794
+ end
795
+
796
+ def require_delete?(path)
797
+ #%r{
798
+ # ^
799
+ # (?: status(?:es)?
800
+ # | direct_messages
801
+ # | friendships
802
+ # | favou?ri(?:ing|tes) )
803
+ # | blocks
804
+ # /destroy/
805
+ #}x === path
806
+ path.include? "/destroy/"
807
+ end
808
+
809
+ def api(path, q = {}, opt = {})
616
810
  ret = {}
617
- headers = {
618
- "User-Agent" => @user_agent,
619
- "Authorization" => "Basic " + ["#{@real}:#{@pass}"].pack("m"),
620
- }
811
+ headers = { "User-Agent" => @user_agent }
621
812
  headers["If-Modified-Since"] = q["since"] if q.key?("since")
622
813
 
623
814
  q["source"] ||= api_source
624
- q = q.inject([]) {|r,(k,v)| v.inject(r) {|r,i| r << "#{k}=#{URI.escape(i, /[^-.!~*'()\w]/n)}" } }.join("&")
625
-
626
- uri = api_base.dup
627
- uri.path = path.sub(%r{^/*}, "/") << ".json"
628
- uri.query = q
629
815
 
816
+ path = path.sub(%r{^/+}, "")
817
+ uri = api_base.dup
818
+ if @opts.key?("secure")
819
+ uri.scheme = "https"
820
+ uri.port = 443
821
+ end
822
+ uri.path += "#{path}.json"
823
+ uri.query = q.inject([]) {|r,(k,v)| v ? r << "#{k}=#{URI.escape(v, /[^-.!~*'()\w]/n)}" : r }.join("&")
824
+ case
825
+ when require_post?(path)
826
+ req = Net::HTTP::Post.new(uri.path, headers)
827
+ req.body = uri.query
828
+ when require_delete?(path)
829
+ req = Net::HTTP::Delete.new(uri.path, headers)
830
+ req.body = uri.query
831
+ else
832
+ req = Net::HTTP::Get.new(uri.request_uri, headers)
833
+ end
834
+ req.basic_auth(@real, @pass)
630
835
  @log.debug uri.inspect
836
+
631
837
  http = Net::HTTP.new(uri.host, uri.port)
632
838
  if uri.scheme == "https"
633
839
  http.use_ssl = true
634
840
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE # FIXME
635
841
  end
636
- http.start do
637
- case uri.path
638
- when "/statuses/update.json", "/direct_messages/new.json"
639
- ret = http.post(uri.request_uri, q, headers)
640
- else
641
- ret = http.get(uri.request_uri, headers)
642
- end
643
- end
644
-
645
- case ret
842
+ case ret = http.request(req)
646
843
  when Net::HTTPOK # 200
647
844
  ret = JSON.parse(ret.body.gsub(/'(y(?:es)?|no?|true|false|null)'/, '"\1"'))
648
- raise ApiFailed, "Server Returned Error: #{ret["error"]}" if ret.kind_of?(Hash) && ret["error"]
845
+ if ret.kind_of?(Hash) && !opt[:suppress_errors] && ret["error"]
846
+ raise ApiFailed, "Server Returned Error: #{ret["error"]}"
847
+ end
649
848
  ret
650
849
  when Net::HTTPNotModified # 304
651
850
  []
@@ -664,17 +863,18 @@ class TwitterIrcGateway < Net::IRC::Server::Session
664
863
  # [$1 ? $2.hex : $2.to_i].pack("U")
665
864
  # end
666
865
  str = untinyurl(str)
866
+ sender = @nicknames[sender] || sender
667
867
  sender = "#{sender}!#{sender}@#{api_base.host}"
668
868
  post sender, PRIVMSG, target, str
669
869
  end
670
870
 
671
871
  def log(str)
672
- str.gsub!(/\n/, " ")
872
+ str.gsub!(/\r?\n|\r/, " ")
673
873
  post server_name, NOTICE, main_channel, str
674
874
  end
675
875
 
676
876
  def untinyurl(text)
677
- text.gsub(%r|http://(preview\.)?tinyurl\.com/[0-9a-z=]+|i) {|m|
877
+ text.gsub(%r"http://(?:(preview\.)?tin|rub)yurl\.com/[0-9a-z=]+"i) {|m|
678
878
  uri = URI(m)
679
879
  uri.host = uri.host.sub($1, "") if $1
680
880
  Net::HTTP.start(uri.host, uri.port) {|http|
@@ -689,7 +889,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
689
889
  end
690
890
 
691
891
  class TypableMap < Hash
692
- 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|
892
+ Roman = %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|
693
893
  case
694
894
  when consonant.size > 1, consonant == "y"
695
895
  %w|a u o|
@@ -700,8 +900,8 @@ class TwitterIrcGateway < Net::IRC::Server::Session
700
900
  end.map {|vowel| "#{consonant}#{vowel}" }
701
901
  }.flatten
702
902
 
703
- def initialize(size=1)
704
- @seq = Roma
903
+ def initialize(size = 1)
904
+ @seq = Roman
705
905
  @map = {}
706
906
  @n = 0
707
907
  @size = size
@@ -791,7 +991,7 @@ if __FILE__ == $0
791
991
  opts[:logger] = Logger.new(opts[:log], "daily")
792
992
  opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
793
993
 
794
- # def daemonize(foreground=false)
994
+ # def daemonize(foreground = false)
795
995
  # trap("SIGINT") { exit! 0 }
796
996
  # trap("SIGTERM") { exit! 0 }
797
997
  # trap("SIGHUP") { exit! 0 }