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.
- data/ChangeLog +7 -0
- data/Rakefile +1 -1
- data/examples/echo_bot.rb +31 -0
- data/examples/gmail.rb +201 -0
- data/examples/hatena-star-stream.rb +1 -1
- data/examples/hig.rb +733 -0
- data/examples/iig.rb +842 -0
- data/examples/lig.rb +2 -2
- data/examples/lingr.rb +1 -0
- data/examples/mixi.rb +23 -8
- data/examples/nig.rb +1 -1
- data/examples/sig.rb +5 -4
- data/examples/tig.rb +324 -124
- data/examples/wig.rb +107 -50
- data/lib/net/irc.rb +1 -1
- data/lib/net/irc/constants.rb +1 -1
- data/lib/net/irc/message.rb +1 -1
- data/lib/net/irc/pattern.rb +2 -2
- data/spec/channel_manager_spec.rb +5 -0
- data/spec/net-irc_spec.rb +15 -29
- metadata +6 -2
data/examples/lig.rb
CHANGED
data/examples/lingr.rb
CHANGED
@@ -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))
|
data/examples/mixi.rb
CHANGED
@@ -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
|
-
|
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[
|
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[
|
63
|
-
page.links[
|
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
|
|
data/examples/nig.rb
CHANGED
data/examples/sig.rb
CHANGED
@@ -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,
|
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
|
-
|
104
|
-
|
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
|
data/examples/tig.rb
CHANGED
@@ -16,14 +16,14 @@ If you want to help:
|
|
16
16
|
|
17
17
|
## Configuration
|
18
18
|
|
19
|
-
Options specified by after
|
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
|
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
|
45
|
+
Apply ID to each message for make favorites by CTCP ACTION.
|
46
46
|
|
47
|
-
/me fav
|
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
|
-
###
|
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
|
-
|
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
|
-
@
|
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
|
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
|
243
|
+
sleep @opts["checkrls"] || 3600 # 1 hour
|
205
244
|
end
|
206
245
|
end
|
207
246
|
sleep 5
|
208
247
|
|
209
|
-
|
210
|
-
|
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
|
-
|
213
|
-
|
214
|
-
|
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(
|
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(
|
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(
|
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", {
|
333
|
+
ret = api("statuses/update", { :status => message })
|
295
334
|
end
|
296
335
|
else
|
297
336
|
# direct message
|
298
|
-
ret = api("direct_messages/new", {
|
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 "
|
318
|
-
|
319
|
-
|
320
|
-
|
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
|
383
|
+
post to, NOTICE, main_channel, "#{generate_status_message(s)}"
|
323
384
|
end
|
324
|
-
|
325
385
|
unless res
|
326
|
-
post
|
386
|
+
post server_name, ERR_NOSUCHNICK, nick, "No such nick/channel"
|
327
387
|
end
|
328
|
-
when
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
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 "
|
339
|
-
|
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
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
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
|
357
|
-
post
|
358
|
-
post
|
359
|
-
post
|
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
|
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
|
511
|
+
post server_name, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
|
377
512
|
end
|
378
|
-
post
|
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
|
520
|
+
post server_name, RPL_WHOREPLY, @nick, channel, user, host, serv, nick, "H*@", "0 #{real}"
|
386
521
|
end
|
387
|
-
post
|
522
|
+
post server_name, RPL_ENDOFWHO, @nick, channel
|
388
523
|
else
|
389
|
-
post
|
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
|
-
|
444
|
-
|
445
|
-
|
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
|
-
|
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 # 自分のときは
|
594
|
+
if nick == @nick # 自分のときは TOPIC に
|
455
595
|
post "#{nick}!#{nick}@#{api_base.host}", TOPIC, main_channel, untinyurl(mesg)
|
456
596
|
else
|
457
|
-
|
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
|
-
|
465
|
-
|
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
|
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 = { """ => "\"", "<"=> "<", ">"=> ">", "&"=> "&", "\n" => " "}
|
481
|
-
mesg.gsub!(
|
622
|
+
m = { """ => "\"", "<" => "<", ">" => ">", "&" => "&", "\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
|
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"]
|
631
|
+
id = s["id"]
|
489
632
|
next if id.nil? || @timeline.include?(id)
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
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
|
-
|
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
|
-
|
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
|
512
|
-
|
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
|
529
|
-
post
|
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
|
-
|
553
|
-
|
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
|
-
|
562
|
-
@
|
563
|
-
|
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
|
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.
|
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
|
-
|
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
|
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
|
-
|
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 =
|
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 }
|