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.
- 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 }
|