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 +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/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( )
|
data/examples/nig.rb
CHANGED
data/examples/tig.rb
CHANGED
@@ -8,7 +8,7 @@ Ruby version of TwitterIrcGateway
|
|
8
8
|
|
9
9
|
## Launch
|
10
10
|
|
11
|
-
$ ruby tig.rb
|
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 (
|
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
|
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
|
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 (
|
83
|
+
Use IM instead of any APIs (e.g. post)
|
84
84
|
|
85
|
+
### ratio=<timeline>:<friends>
|
85
86
|
|
86
|
-
|
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
|
168
|
+
@tmap = TypableMap.new
|
160
169
|
|
161
170
|
if @opts["jabber"]
|
162
|
-
jid, pass = @opts["jabber"].split(
|
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
|
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
|
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
|
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
|
222
|
-
@
|
223
|
-
@
|
224
|
-
@
|
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, "
|
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(
|
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["
|
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 = { """ => "\"", "<"=> "<", ">"=> ">", "&"=> "&", "\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
|
-
#
|
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
|
-
|
535
|
-
"User-Agent"
|
536
|
-
"Authorization"
|
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
|
-
|
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,
|
639
|
+
ret = http.post(uri.request_uri, q, headers)
|
560
640
|
else
|
561
|
-
ret = http.get(uri.request_uri,
|
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
|
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 =
|
613
|
-
|
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
|
618
|
-
@map
|
619
|
-
@n
|
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
|
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:
|