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 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: