net-irc 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,9 +1,25 @@
1
+ 2008-07-06 SATOH Hiroh <cho45@lowreal.net>
2
+
3
+ * [interface]:
4
+ Removed around @channels and separeted to
5
+ Net::IRC::Client::ChannelManager as just a sample of managing
6
+ channels.
7
+ * [release]:
8
+ Released 0.0.5
9
+
10
+ 2008-07-06 Satoshi Nakagawa <psychs@limechat.net>
11
+
12
+ * [new]:
13
+ Added a mode parser which can be configured automatically from 005 replies.
14
+
1
15
  2008-06-28 cho45
2
16
 
3
17
  * [interface]:
4
18
  Change mode character to symbol.
5
19
  * [new]:
6
20
  Seperate each class to some files.
21
+ * [release]:
22
+ Released 0.0.4
7
23
 
8
24
  2008-06-14 cho45
9
25
 
data/Rakefile CHANGED
@@ -18,7 +18,7 @@ require "net/irc"
18
18
  NAME = "net-irc"
19
19
  AUTHOR = "cho45"
20
20
  EMAIL = "cho45@lowreal.net"
21
- DESCRIPTION = ""
21
+ DESCRIPTION = "library for implementing IRC server and client"
22
22
  RUBYFORGE_PROJECT = "lowreal"
23
23
  HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
24
24
  BIN_FILES = %w( )
@@ -73,6 +73,10 @@ class NowaIrcGateway < TwitterIrcGateway
73
73
  def jabber_bot_id
74
74
  nil
75
75
  end
76
+
77
+ def hourly_limit
78
+ 30
79
+ end
76
80
  end
77
81
 
78
82
  if __FILE__ == $0
@@ -8,7 +8,7 @@ Ruby version of TwitterIrcGateway
8
8
 
9
9
  ## Launch
10
10
 
11
- $ ruby tig.rb # daemonized
11
+ $ ruby tig.rb
12
12
 
13
13
  If you want to help:
14
14
 
@@ -23,7 +23,7 @@ Configuration example for Tiarra ( http://coderepos.org/share/wiki/Tiarra ).
23
23
  twitter {
24
24
  host: localhost
25
25
  port: 16668
26
- name: username@example.com athack jabber=username@example.com:jabberpasswd tid=10
26
+ name: username@example.com athack jabber=username@example.com:jabberpasswd tid=10 ratio=32:1 replies=6
27
27
  password: password on Twitter
28
28
  in-encoding: utf8
29
29
  out-encoding: utf8
@@ -34,7 +34,7 @@ Configuration example for Tiarra ( http://coderepos.org/share/wiki/Tiarra ).
34
34
  If `athack` client option specified,
35
35
  all nick in join message is leading with @.
36
36
 
37
- So if you complemente nicks (ex. Irssi),
37
+ So if you complemente nicks (e.g. Irssi),
38
38
  it's good for Twitter like reply command (@nick).
39
39
 
40
40
  In this case, you will see torrent of join messages after connected,
@@ -61,7 +61,7 @@ Apply id to each message for make favorites by CTCP ACTION.
61
61
  10 => teal
62
62
  11 => lightcyan cyan aqua
63
63
  12 => lightblue royal
64
- 13 => pink lightpurple fuchsia
64
+ 13 => pink lightpurple fuchsia
65
65
  14 => grey
66
66
  15 => lightgrey silver
67
67
 
@@ -72,7 +72,7 @@ If `jabber=<jid>:<pass>` option specified,
72
72
  use jabber to get friends timeline.
73
73
 
74
74
  You must setup im notifing settings in the site and
75
- install 'xmpp4r-simple' gem.
75
+ install "xmpp4r-simple" gem.
76
76
 
77
77
  $ sudo gem install xmpp4r-simple
78
78
 
@@ -80,10 +80,15 @@ Be careful for managing password.
80
80
 
81
81
  ### alwaysim
82
82
 
83
- Use IM instead of any APIs (ex. post)
83
+ Use IM instead of any APIs (e.g. post)
84
84
 
85
+ ### ratio=<timeline>:<friends>
85
86
 
86
- ## Licence
87
+ ### replies[=<ratio>]
88
+
89
+ ### checkrls[=<interval seconds>]
90
+
91
+ ## License
87
92
 
88
93
  Ruby's by cho45
89
94
 
@@ -134,6 +139,10 @@ class TwitterIrcGateway < Net::IRC::Server::Session
134
139
  "twitter@twitter.com"
135
140
  end
136
141
 
142
+ def hourly_limit
143
+ 20
144
+ end
145
+
137
146
  class ApiFailed < StandardError; end
138
147
 
139
148
  def initialize(*args)
@@ -153,13 +162,13 @@ class TwitterIrcGateway < Net::IRC::Server::Session
153
162
 
154
163
  @real, *@opts = @opts.name || @real.split(/\s+/)
155
164
  @opts = @opts.inject({}) {|r,i|
156
- key, value = i.split(/=/)
165
+ key, value = i.split("=")
157
166
  r.update(key => value)
158
167
  }
159
- @tmap = TypableMap.new
168
+ @tmap = TypableMap.new
160
169
 
161
170
  if @opts["jabber"]
162
- jid, pass = @opts["jabber"].split(/:/, 2)
171
+ jid, pass = @opts["jabber"].split(":", 2)
163
172
  @opts["jabber"].replace("jabber=#{jid}:********")
164
173
  if jabber_bot_id
165
174
  begin
@@ -167,7 +176,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
167
176
  start_jabber(jid, pass)
168
177
  rescue LoadError
169
178
  log "Failed to start Jabber."
170
- log "Installl 'xmpp4r-simple' gem or check your id/pass."
179
+ log 'Installl "xmpp4r-simple" gem or check your id/pass.'
171
180
  finish
172
181
  end
173
182
  else
@@ -179,6 +188,32 @@ class TwitterIrcGateway < Net::IRC::Server::Session
179
188
  log "Client Options: #{@opts.inspect}"
180
189
  @log.info "Client Options: #{@opts.inspect}"
181
190
 
191
+ @hourly_limit = hourly_limit
192
+ @check_rate_limit_thread = Thread.start do
193
+ loop do
194
+ begin
195
+ check_rate_limit
196
+ rescue ApiFailed => e
197
+ @log.error e.inspect
198
+ rescue Exception => e
199
+ @log.error e.inspect
200
+ e.backtrace.each do |l|
201
+ @log.error "\t#{l}"
202
+ end
203
+ end
204
+ sleep @opts["checkrls"] || 3600 # 1 time / hour
205
+ end
206
+ end
207
+ sleep 5
208
+
209
+ timeline_ratio, friends_ratio = (@opts["ratio"] || "10:3").split(":").map {|ratio| ratio.to_i }
210
+ footing = (timeline_ratio + friends_ratio).to_f
211
+
212
+ if @opts.key?("replies")
213
+ replies_ratio ||= (@opts["replies"] || 5).to_i
214
+ footing += replies_ratio
215
+ end
216
+
182
217
  @timeline = []
183
218
  @check_friends_thread = Thread.start do
184
219
  loop do
@@ -192,13 +227,13 @@ class TwitterIrcGateway < Net::IRC::Server::Session
192
227
  @log.error "\t#{l}"
193
228
  end
194
229
  end
195
- sleep 10 * 60 # 6 times/hour
230
+ sleep freq(friends_ratio / footing)
196
231
  end
197
232
  end
198
- sleep 3
199
233
 
200
234
  return if @opts["jabber"]
201
235
 
236
+ sleep 3
202
237
  @check_timeline_thread = Thread.start do
203
238
  loop do
204
239
  begin
@@ -212,16 +247,37 @@ class TwitterIrcGateway < Net::IRC::Server::Session
212
247
  @log.error "\t#{l}"
213
248
  end
214
249
  end
215
- sleep 180 # 20 times/hour
250
+ sleep freq(timeline_ratio / footing)
251
+ end
252
+ end
253
+
254
+ return unless @opts.key?("replies")
255
+
256
+ sleep 10
257
+ @check_replies_thread = Thread.start do
258
+ loop do
259
+ begin
260
+ check_replies
261
+ rescue ApiFailed => e
262
+ @log.error e.inspect
263
+ rescue Exception => e
264
+ @log.error e.inspect
265
+ e.backtrace.each do |l|
266
+ @log.error "\t#{l}"
267
+ end
268
+ end
269
+ sleep freq(replies_ratio / footing)
216
270
  end
217
271
  end
218
272
  end
219
273
 
220
274
  def on_disconnected
221
- @check_friends_thread.kill rescue nil
222
- @check_timeline_thread.kill rescue nil
223
- @im_thread.kill rescue nil
224
- @im.disconnect rescue nil
275
+ @check_friends_thread.kill rescue nil
276
+ @check_replies_thread.kill rescue nil
277
+ @check_timeline_thread.kill rescue nil
278
+ @check_rate_limit_thread.kill rescue nil
279
+ @im_thread.kill rescue nil
280
+ @im.disconnect rescue nil
225
281
  end
226
282
 
227
283
  def on_privmsg(m)
@@ -241,7 +297,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
241
297
  # direct message
242
298
  ret = api("direct_messages/new", {"user" => target, "text" => message})
243
299
  end
244
- raise ApiFailed, "api failed" unless ret
300
+ raise ApiFailed, "API failed" unless ret
245
301
  log "Status Updated"
246
302
  rescue => e
247
303
  @log.error [retry_count, e.inspect].inspect
@@ -261,7 +317,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
261
317
  when "list"
262
318
  nick = args[0]
263
319
  @log.debug([ nick, message ])
264
- res = api('statuses/user_timeline', { 'id' => nick }).reverse_each do |s|
320
+ res = api("statuses/user_timeline", { "id" => nick }).reverse_each do |s|
265
321
  @log.debug(s)
266
322
  post nick, NOTICE, main_channel, "#{generate_status_message(s)}"
267
323
  end
@@ -389,7 +445,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
389
445
  id = s["id"] || s["rid"]
390
446
  next if id.nil? || @timeline.include?(id)
391
447
  @timeline << id
392
- nick = s["user_login_id"] || s["user"]["screen_name"] # it may be better to use user_login_id in Wassr
448
+ nick = s["user"]["screen_name"]
393
449
  mesg = generate_status_message(s)
394
450
 
395
451
  tid = @tmap.push(s)
@@ -420,26 +476,37 @@ class TwitterIrcGateway < Net::IRC::Server::Session
420
476
  mesg = s["text"]
421
477
  @log.debug(mesg)
422
478
 
423
- # added @user in no use @user reply message ( Wassr only )
424
- if s.has_key?('reply_status_url') and s['reply_status_url'] and s['text'] !~ /^@.*/ and %r{([^/]+)/statuses/[^/]+}.match(s['reply_status_url'])
425
- reply_user_id = $1
426
- mesg = "@#{reply_user_id} #{mesg}"
427
- end
428
- # display area name(Wassr only)
429
- if s.has_key?('areaname') and s["areaname"]
430
- mesg += " L: #{s['areaname']}"
431
- end
432
- # display photo url(Wassr only)
433
- if s.has_key?('photo_url') and s["photo_url"]
434
- mesg += " #{s['photo_url']}"
435
- end
436
-
437
479
  # time = Time.parse(s["created_at"]) rescue Time.now
438
480
  m = { "&quot;" => "\"", "&lt;"=> "<", "&gt;"=> ">", "&amp;"=> "&", "\n" => " "}
439
481
  mesg.gsub!(/(#{m.keys.join("|")})/) { m[$1] }
440
482
  mesg
441
483
  end
442
484
 
485
+ def check_replies
486
+ @prev_time_r ||= Time.now
487
+ api("statuses/replies").reverse_each do |s|
488
+ id = s["id"] || s["rid"]
489
+ 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"]
494
+ mesg = generate_status_message(s)
495
+
496
+ tid = @tmap.push(s)
497
+
498
+ @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
504
+ end
505
+ @log.debug "@timeline.size = #{@timeline.size}"
506
+ @timeline = @timeline.last(100)
507
+ @prev_time_r = Time.now
508
+ end
509
+
443
510
  def check_direct_messages
444
511
  @prev_time_d ||= Time.now
445
512
  api("direct_messages", {"since" => @prev_time_d.httpdate}).reverse_each do |s|
@@ -464,7 +531,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
464
531
  prv_friends = @friends.map {|i| i["screen_name"] }
465
532
  now_friends = friends.map {|i| i["screen_name"] }
466
533
 
467
- # twitter api bug?
534
+ # Twitter API bug?
468
535
  return if !first && (now_friends.length - prv_friends.length).abs > 10
469
536
 
470
537
  (now_friends - prv_friends).each do |join|
@@ -479,6 +546,23 @@ class TwitterIrcGateway < Net::IRC::Server::Session
479
546
  end
480
547
  end
481
548
 
549
+ def check_rate_limit
550
+ @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"]}"
554
+ @hourly_limit = rate_limit["hourly_limit"]
555
+ end
556
+ # rate_limit["remaining_hits"] < 1
557
+ # rate_limit["reset_time_in_seconds"] - Time.now.to_i
558
+ end
559
+
560
+ def freq(ratio)
561
+ ret = 3600 / (@hourly_limit * ratio).round
562
+ @log.debug "Frequency: #{ret}"
563
+ ret
564
+ end
565
+
482
566
  def start_jabber(jid, pass)
483
567
  @log.info "Logging-in with #{jid} -> jabber_bot_id: #{jabber_bot_id}"
484
568
  @im = Jabber::Simple.new(jid, pass)
@@ -490,7 +574,6 @@ class TwitterIrcGateway < Net::IRC::Server::Session
490
574
  @log.debug [msg.from, msg.body]
491
575
  if msg.from.strip == jabber_bot_id
492
576
  # Twitter -> 'id: msg'
493
- # Wassr -> 'nick(id): msg'
494
577
  body = msg.body.sub(/^(.+?)(?:\((.+?)\))?: /, "")
495
578
  if Regexp.last_match
496
579
  nick, id = Regexp.last_match.captures
@@ -531,14 +614,11 @@ class TwitterIrcGateway < Net::IRC::Server::Session
531
614
 
532
615
  def api(path, q={})
533
616
  ret = {}
534
- header = {
535
- "User-Agent" => @user_agent,
536
- "Authorization" => "Basic " + ["#{@real}:#{@pass}"].pack("m"),
537
- # "X-Twitter-Client" => api_source,
538
- # "X-Twitter-Client-Version" => server_version,
539
- # "X-Twitter-Client-URL" => "http://coderepos.org/share/browser/lang/ruby/misc/tig.rb",
617
+ headers = {
618
+ "User-Agent" => @user_agent,
619
+ "Authorization" => "Basic " + ["#{@real}:#{@pass}"].pack("m"),
540
620
  }
541
- header["If-Modified-Since"] = q["since"] if q.key?("since")
621
+ headers["If-Modified-Since"] = q["since"] if q.key?("since")
542
622
 
543
623
  q["source"] ||= api_source
544
624
  q = q.inject([]) {|r,(k,v)| v.inject(r) {|r,i| r << "#{k}=#{URI.escape(i, /[^-.!~*'()\w]/n)}" } }.join("&")
@@ -556,9 +636,9 @@ class TwitterIrcGateway < Net::IRC::Server::Session
556
636
  http.start do
557
637
  case uri.path
558
638
  when "/statuses/update.json", "/direct_messages/new.json"
559
- ret = http.post(uri.request_uri, q, header)
639
+ ret = http.post(uri.request_uri, q, headers)
560
640
  else
561
- ret = http.get(uri.request_uri, header)
641
+ ret = http.get(uri.request_uri, headers)
562
642
  end
563
643
  end
564
644
 
@@ -571,7 +651,7 @@ class TwitterIrcGateway < Net::IRC::Server::Session
571
651
  []
572
652
  when Net::HTTPBadRequest # 400
573
653
  # exceeded the rate limitation
574
- raise ApiFailed, "#{ret.code}: #{ret["error"]}"
654
+ raise ApiFailed, "#{ret.code}: #{ret.message}"
575
655
  else
576
656
  raise ApiFailed, "Server Returned #{ret.code} #{ret.message}"
577
657
  end
@@ -609,14 +689,21 @@ class TwitterIrcGateway < Net::IRC::Server::Session
609
689
  end
610
690
 
611
691
  class TypableMap < Hash
612
- Roma = "a i u e o k g s z t d n h b p m y r w j v l q".split(/ /).map {|k|
613
- %w|a i u e o|.map {|v| "#{k}#{v}" }
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|
693
+ case
694
+ when consonant.size > 1, consonant == "y"
695
+ %w|a u o|
696
+ when consonant == "q"
697
+ %w|a i e o|
698
+ else
699
+ %w|a i u e o|
700
+ end.map {|vowel| "#{consonant}#{vowel}" }
614
701
  }.flatten
615
702
 
616
703
  def initialize(size=1)
617
- @seq = Roma
618
- @map = {}
619
- @n = 0
704
+ @seq = Roma
705
+ @map = {}
706
+ @n = 0
620
707
  @size = size
621
708
  end
622
709
 
@@ -663,7 +750,7 @@ if __FILE__ == $0
663
750
 
664
751
  OptionParser.new do |parser|
665
752
  parser.instance_eval do
666
- self.banner = <<-EOB.gsub(/^\t+/, "")
753
+ self.banner = <<-EOB.gsub(/^\t+/, "")
667
754
  Usage: #{$0} [opts]
668
755
 
669
756
  EOB
@@ -704,27 +791,27 @@ if __FILE__ == $0
704
791
  opts[:logger] = Logger.new(opts[:log], "daily")
705
792
  opts[:logger].level = opts[:debug] ? Logger::DEBUG : Logger::INFO
706
793
 
707
- def daemonize(foreground=false)
708
- trap("SIGINT") { exit! 0 }
709
- trap("SIGTERM") { exit! 0 }
710
- trap("SIGHUP") { exit! 0 }
711
- return yield if $DEBUG || foreground
712
- Process.fork do
713
- Process.setsid
714
- Dir.chdir "/"
715
- File.open("/dev/null") {|f|
716
- STDIN.reopen f
717
- STDOUT.reopen f
718
- STDERR.reopen f
719
- }
720
- yield
721
- end
722
- exit! 0
723
- end
794
+ # def daemonize(foreground=false)
795
+ # trap("SIGINT") { exit! 0 }
796
+ # trap("SIGTERM") { exit! 0 }
797
+ # trap("SIGHUP") { exit! 0 }
798
+ # return yield if $DEBUG || foreground
799
+ # Process.fork do
800
+ # Process.setsid
801
+ # Dir.chdir "/"
802
+ # File.open("/dev/null") {|f|
803
+ # STDIN.reopen f
804
+ # STDOUT.reopen f
805
+ # STDERR.reopen f
806
+ # }
807
+ # yield
808
+ # end
809
+ # exit! 0
810
+ # end
724
811
 
725
- daemonize(opts[:debug] || opts[:foreground]) do
812
+ # daemonize(opts[:debug] || opts[:foreground]) do
726
813
  Net::IRC::Server.new(opts[:host], opts[:port], TwitterIrcGateway, opts).start
727
- end
814
+ # end
728
815
  end
729
816
 
730
817
  # Local Variables: