atig 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +24 -0
- data/Gemfile +3 -0
- data/README.mkdn +52 -0
- data/Rakefile +15 -0
- data/atig.gemspec +25 -0
- data/bin/atig +74 -0
- data/docs/OMakefile +32 -0
- data/docs/OMakeroot +45 -0
- data/docs/_static/allow.png +0 -0
- data/docs/_static/emacs.png +0 -0
- data/docs/_static/irc_setting.png +0 -0
- data/docs/_static/irssi.png +0 -0
- data/docs/_static/limechat.png +0 -0
- data/docs/_static/limechat_s.png +0 -0
- data/docs/_static/oauth_channel.png +0 -0
- data/docs/_static/screenshot.png +0 -0
- data/docs/_static/structure.png +0 -0
- data/docs/_static/verify.png +0 -0
- data/docs/changelog.rst +96 -0
- data/docs/commandline_options.rst +21 -0
- data/docs/commands.rst +84 -0
- data/docs/conf.py +194 -0
- data/docs/config.rst +159 -0
- data/docs/feature.rst +41 -0
- data/docs/graphics.graffle +1995 -0
- data/docs/hacking_guide.rst +43 -0
- data/docs/index.rst +109 -0
- data/docs/irc.rst +31 -0
- data/docs/options.rst +75 -0
- data/docs/quickstart.rst +89 -0
- data/docs/resize.sh +7 -0
- data/docs/tiarra.rst +2 -0
- data/docs/tig.rst +21 -0
- data/lib/atig.rb +19 -0
- data/lib/atig/agent.rb +8 -0
- data/lib/atig/agent/agent.rb +38 -0
- data/lib/atig/agent/clenup.rb +23 -0
- data/lib/atig/agent/dm.rb +35 -0
- data/lib/atig/agent/following.rb +45 -0
- data/lib/atig/agent/full_list.rb +20 -0
- data/lib/atig/agent/list.rb +55 -0
- data/lib/atig/agent/list_status.rb +46 -0
- data/lib/atig/agent/mention.rb +13 -0
- data/lib/atig/agent/other_list.rb +18 -0
- data/lib/atig/agent/own_list.rb +18 -0
- data/lib/atig/agent/stream_follow.rb +38 -0
- data/lib/atig/agent/timeline.rb +13 -0
- data/lib/atig/agent/user_stream.rb +31 -0
- data/lib/atig/basic_twitter.rb +116 -0
- data/lib/atig/bitly.rb +52 -0
- data/lib/atig/channel.rb +5 -0
- data/lib/atig/channel/channel.rb +17 -0
- data/lib/atig/channel/dm.rb +14 -0
- data/lib/atig/channel/list.rb +76 -0
- data/lib/atig/channel/mention.rb +20 -0
- data/lib/atig/channel/retweet.rb +28 -0
- data/lib/atig/channel/timeline.rb +74 -0
- data/lib/atig/command.rb +21 -0
- data/lib/atig/command/autofix.rb +58 -0
- data/lib/atig/command/command.rb +24 -0
- data/lib/atig/command/command_helper.rb +95 -0
- data/lib/atig/command/destroy.rb +44 -0
- data/lib/atig/command/dm.rb +31 -0
- data/lib/atig/command/favorite.rb +27 -0
- data/lib/atig/command/info.rb +50 -0
- data/lib/atig/command/limit.rb +15 -0
- data/lib/atig/command/location.rb +23 -0
- data/lib/atig/command/name.rb +18 -0
- data/lib/atig/command/option.rb +37 -0
- data/lib/atig/command/refresh.rb +18 -0
- data/lib/atig/command/reply.rb +37 -0
- data/lib/atig/command/retweet.rb +63 -0
- data/lib/atig/command/search.rb +51 -0
- data/lib/atig/command/spam.rb +26 -0
- data/lib/atig/command/status.rb +41 -0
- data/lib/atig/command/thread.rb +44 -0
- data/lib/atig/command/time.rb +32 -0
- data/lib/atig/command/uptime.rb +32 -0
- data/lib/atig/command/user.rb +42 -0
- data/lib/atig/command/user_info.rb +27 -0
- data/lib/atig/command/version.rb +49 -0
- data/lib/atig/command/whois.rb +39 -0
- data/lib/atig/db/db.rb +60 -0
- data/lib/atig/db/followings.rb +131 -0
- data/lib/atig/db/listenable.rb +22 -0
- data/lib/atig/db/lists.rb +76 -0
- data/lib/atig/db/roman.rb +30 -0
- data/lib/atig/db/sized_uniq_array.rb +62 -0
- data/lib/atig/db/sql.rb +35 -0
- data/lib/atig/db/statuses.rb +147 -0
- data/lib/atig/db/transaction.rb +47 -0
- data/lib/atig/exception_util.rb +26 -0
- data/lib/atig/gateway.rb +62 -0
- data/lib/atig/gateway/channel.rb +99 -0
- data/lib/atig/gateway/session.rb +326 -0
- data/lib/atig/http.rb +95 -0
- data/lib/atig/ifilter.rb +7 -0
- data/lib/atig/ifilter/expand_url.rb +74 -0
- data/lib/atig/ifilter/retweet.rb +14 -0
- data/lib/atig/ifilter/retweet_time.rb +16 -0
- data/lib/atig/ifilter/sanitize.rb +18 -0
- data/lib/atig/ifilter/strip.rb +15 -0
- data/lib/atig/ifilter/utf7.rb +26 -0
- data/lib/atig/ifilter/xid.rb +36 -0
- data/lib/atig/levenshtein.rb +49 -0
- data/lib/atig/monkey.rb +4 -0
- data/lib/atig/oauth-patch.rb +40 -0
- data/lib/atig/oauth.rb +55 -0
- data/lib/atig/ofilter.rb +4 -0
- data/lib/atig/ofilter/escape_url.rb +102 -0
- data/lib/atig/ofilter/footer.rb +20 -0
- data/lib/atig/ofilter/geo.rb +17 -0
- data/lib/atig/ofilter/short_url.rb +47 -0
- data/lib/atig/option.rb +90 -0
- data/lib/atig/scheduler.rb +79 -0
- data/lib/atig/search.rb +22 -0
- data/lib/atig/search_twitter.rb +21 -0
- data/lib/atig/sized_hash.rb +33 -0
- data/lib/atig/stream.rb +66 -0
- data/lib/atig/twitter.rb +79 -0
- data/lib/atig/twitter_struct.rb +63 -0
- data/lib/atig/unu.rb +27 -0
- data/lib/atig/update_checker.rb +53 -0
- data/lib/atig/url_escape.rb +62 -0
- data/lib/atig/util.rb +16 -0
- data/lib/atig/version.rb +3 -0
- data/lib/memory_profiler.rb +77 -0
- data/spec/command/autofix_spec.rb +35 -0
- data/spec/command/destroy_spec.rb +98 -0
- data/spec/command/dm_spec.rb +28 -0
- data/spec/command/favorite_spec.rb +55 -0
- data/spec/command/limit_spec.rb +27 -0
- data/spec/command/location_spec.rb +25 -0
- data/spec/command/name_spec.rb +19 -0
- data/spec/command/option_spec.rb +133 -0
- data/spec/command/refresh_spec.rb +22 -0
- data/spec/command/reply_spec.rb +79 -0
- data/spec/command/retweet_spec.rb +66 -0
- data/spec/command/spam_spec.rb +27 -0
- data/spec/command/status_spec.rb +44 -0
- data/spec/command/thread_spec.rb +91 -0
- data/spec/command/time_spec.rb +52 -0
- data/spec/command/uptime_spec.rb +55 -0
- data/spec/command/user_info_spec.rb +42 -0
- data/spec/command/user_spec.rb +50 -0
- data/spec/command/version_spec.rb +67 -0
- data/spec/command/whois_spec.rb +78 -0
- data/spec/db/followings_spec.rb +100 -0
- data/spec/db/listenable_spec.rb +32 -0
- data/spec/db/lists_spec.rb +104 -0
- data/spec/db/roman_spec.rb +17 -0
- data/spec/db/sized_uniq_array_spec.rb +63 -0
- data/spec/db/statuses_spec.rb +180 -0
- data/spec/ifilter/expand_url_spec.rb +44 -0
- data/spec/ifilter/retweet_spec.rb +28 -0
- data/spec/ifilter/retweet_time_spec.rb +25 -0
- data/spec/ifilter/sanitize_spec.rb +25 -0
- data/spec/ifilter/sid_spec.rb +29 -0
- data/spec/ifilter/strip_spec.rb +23 -0
- data/spec/ifilter/tid_spec.rb +29 -0
- data/spec/ifilter/utf7_spec.rb +30 -0
- data/spec/levenshtein_spec.rb +24 -0
- data/spec/ofilter/escape_url_spec.rb +50 -0
- data/spec/ofilter/footer_spec.rb +32 -0
- data/spec/ofilter/geo_spec.rb +33 -0
- data/spec/ofilter/short_url_spec.rb +127 -0
- data/spec/option_spec.rb +91 -0
- data/spec/sized_hash_spec.rb +45 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/update_checker_spec.rb +55 -0
- metadata +326 -0
data/lib/atig/ofilter.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
|
3
|
+
require 'atig/util'
|
4
|
+
require 'atig/http'
|
5
|
+
require 'atig/url_escape'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "punycode"
|
9
|
+
rescue LoadError
|
10
|
+
end
|
11
|
+
|
12
|
+
module Atig
|
13
|
+
module OFilter
|
14
|
+
class EscapeUrl
|
15
|
+
include Util
|
16
|
+
def initialize(context)
|
17
|
+
@log = context.log
|
18
|
+
@http = Atig::Http.new @log
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(status)
|
22
|
+
status.merge(:status => escape_http_urls(status[:status]))
|
23
|
+
end
|
24
|
+
|
25
|
+
def exist_uri?(uri, limit = 1)
|
26
|
+
ret = nil
|
27
|
+
#raise "Not supported." unless uri.is_a?(URI::HTTP)
|
28
|
+
return ret if limit.zero? or uri.nil? or not uri.is_a?(URI::HTTP)
|
29
|
+
@log.debug uri.inspect
|
30
|
+
|
31
|
+
req = @http.req :head, uri
|
32
|
+
@http.http(uri, 3, 2).request(req) do |res|
|
33
|
+
ret = case res
|
34
|
+
when Net::HTTPSuccess
|
35
|
+
true
|
36
|
+
when Net::HTTPRedirection
|
37
|
+
uri = resolve_http_redirect(uri)
|
38
|
+
exist_uri?(uri, limit - 1)
|
39
|
+
when Net::HTTPClientError
|
40
|
+
false
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
ret
|
46
|
+
rescue => e
|
47
|
+
@log.error e.inspect
|
48
|
+
ret
|
49
|
+
end
|
50
|
+
|
51
|
+
def escape_http_urls(text)
|
52
|
+
original_text = text.encoding!("UTF-8").dup
|
53
|
+
|
54
|
+
if defined? ::Punycode
|
55
|
+
# TODO: Nameprep
|
56
|
+
text.gsub!(%r{(https?://)([^\x00-\x2C\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+)}) do
|
57
|
+
domain = $2
|
58
|
+
# Dots:
|
59
|
+
# * U+002E (full stop) * U+3002 (ideographic full stop)
|
60
|
+
# * U+FF0E (fullwidth full stop) * U+FF61 (halfwidth ideographic full stop)
|
61
|
+
# => /[.\u3002\uFF0E\uFF61] # Ruby 1.9 /x
|
62
|
+
$1 + domain.split(/\.|\343\200\202|\357\274\216|\357\275\241/).map do |label|
|
63
|
+
break [domain] if /\A-|[\x00-\x2C\x2E\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]|-\z/ === label
|
64
|
+
next label unless /[^-A-Za-z0-9]/ === label
|
65
|
+
punycode = Punycode.encode(label)
|
66
|
+
break [domain] if punycode.size > 59
|
67
|
+
"xn--#{punycode}"
|
68
|
+
end.join(".")
|
69
|
+
end
|
70
|
+
if text != original_text
|
71
|
+
log :info, "Punycode encoded: #{text}"
|
72
|
+
original_text = text.dup
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
urls = []
|
77
|
+
text.split(/[\s<>]+/).each do |str|
|
78
|
+
next if /%[0-9A-Fa-f]{2}/ === str
|
79
|
+
# URI::UNSAFE + "#"
|
80
|
+
escaped_str = URI.escape(str, %r{[^-_.!~*'()a-zA-Z0-9;/?:@&=+$,\[\]#]}) #'
|
81
|
+
URI.extract(escaped_str, %w[http https]).each do |url|
|
82
|
+
uri = URI(URI.rstrip(url))
|
83
|
+
if not urls.include?(uri.to_s) and self.exist_uri?(uri)
|
84
|
+
urls << uri.to_s
|
85
|
+
end
|
86
|
+
end if escaped_str != str
|
87
|
+
end
|
88
|
+
urls.each do |url|
|
89
|
+
unescaped_url = URI.unescape(url).encoding!("UTF-8")
|
90
|
+
text.gsub!(unescaped_url, url)
|
91
|
+
end
|
92
|
+
log :info, "Percent encoded: #{text}" if text != original_text
|
93
|
+
|
94
|
+
text.encoding!("UTF-8")
|
95
|
+
rescue => e
|
96
|
+
log :error, e
|
97
|
+
text
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
|
3
|
+
module Atig
|
4
|
+
module OFilter
|
5
|
+
class Footer
|
6
|
+
def initialize(context)
|
7
|
+
@opts = context.opts
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(q)
|
11
|
+
if @opts.footer && !@opts.footer.empty? then
|
12
|
+
q.merge :status => "#{q[:status]} #{@opts.footer}"
|
13
|
+
else
|
14
|
+
q
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
|
3
|
+
module Atig
|
4
|
+
module OFilter
|
5
|
+
class Geo
|
6
|
+
def initialize(context)
|
7
|
+
@opts = context.opts
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(q)
|
11
|
+
return q unless @opts.ll
|
12
|
+
lat, long = @opts.ll.split(",", 2)
|
13
|
+
q.merge :lat => lat.to_f, :long => long.to_f
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
|
3
|
+
require 'atig/unu'
|
4
|
+
require 'atig/bitly'
|
5
|
+
|
6
|
+
module Atig
|
7
|
+
module OFilter
|
8
|
+
class ShortUrl
|
9
|
+
MIN_LEN = 20
|
10
|
+
|
11
|
+
def initialize(context)
|
12
|
+
@log = context.log
|
13
|
+
@opts = context.opts
|
14
|
+
@http = Atig::Http.new @log
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(status)
|
18
|
+
mesg = status[:status]
|
19
|
+
status.merge(:status => short_urls(mesg))
|
20
|
+
end
|
21
|
+
|
22
|
+
def short_urls(mesg)
|
23
|
+
shorten = case
|
24
|
+
when @opts.bitlify.to_s.include?(":")
|
25
|
+
login, key, len = @opts.bitlify.to_s.split(":", 3)
|
26
|
+
@len = (len || MIN_LEN).to_i
|
27
|
+
Bitly.login @log, login, key
|
28
|
+
when @opts.bitlify
|
29
|
+
@len = (@opts.bitlify.to_s || MIN_LEN).to_i
|
30
|
+
Bitly.no_login @log
|
31
|
+
when @opts.unuify
|
32
|
+
@len = (@opts.unuify.to_s || MIN_LEN).to_i
|
33
|
+
Unu.new @log
|
34
|
+
else
|
35
|
+
return mesg
|
36
|
+
end
|
37
|
+
mesg.gsub(URI.regexp(%w[http https])) do|url|
|
38
|
+
if URI.rstrip(url).size < @len then
|
39
|
+
url
|
40
|
+
else
|
41
|
+
shorten.shorten url
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/atig/option.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Atig
|
3
|
+
class Option
|
4
|
+
class << self
|
5
|
+
def default_value(name, value)
|
6
|
+
@default ||= {}
|
7
|
+
@default[name.to_sym] = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(str)
|
11
|
+
@default ||= {}
|
12
|
+
opts = str.split(" ")
|
13
|
+
opts = opts.inject({}) do |r, i|
|
14
|
+
key, value = i.split("=", 2)
|
15
|
+
|
16
|
+
r.update key.to_sym => parse_value(value)
|
17
|
+
end
|
18
|
+
self.new(@default.merge(opts))
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse_value(value)
|
22
|
+
case value
|
23
|
+
when nil, /\Atrue\z/ then true
|
24
|
+
when /\Afalse\z/ then false
|
25
|
+
when /\A\d+\z/ then value.to_i
|
26
|
+
when /\A(?:\d+\.\d*|\.\d+)\z/ then value.to_f
|
27
|
+
else value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
default_value :api_base, 'https://api.twitter.com/1/'
|
33
|
+
default_value :stream_api_base, 'http://chirpstream.twitter.com/2b/'
|
34
|
+
default_value :search_api_base, 'http://search.twitter.com/'
|
35
|
+
|
36
|
+
def initialize(table)
|
37
|
+
@table = {}
|
38
|
+
table.each do|key,value|
|
39
|
+
key = key.to_sym
|
40
|
+
@table[key] = value
|
41
|
+
new_ostruct_member key
|
42
|
+
end
|
43
|
+
|
44
|
+
# Ruby 1.8だとidというフィールドが作れないので作っておく
|
45
|
+
new_ostruct_member :id, true
|
46
|
+
end
|
47
|
+
|
48
|
+
def marshal_dump; @table end
|
49
|
+
def fields;
|
50
|
+
@table.keys
|
51
|
+
end
|
52
|
+
|
53
|
+
def [](name)
|
54
|
+
@table[name.to_sym]
|
55
|
+
end
|
56
|
+
|
57
|
+
def []=(name, value)
|
58
|
+
@table[name.to_sym] = value
|
59
|
+
end
|
60
|
+
|
61
|
+
def new_ostruct_member(name, force=false)
|
62
|
+
if force or (not self.respond_to?(name))
|
63
|
+
class << self; self; end.class_eval do
|
64
|
+
define_method(name) { @table[name] }
|
65
|
+
define_method(:"#{name}=") { |x| @table[name] = x }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def method_missing(mid, *args) # :nodoc:
|
71
|
+
mname = mid.id2name
|
72
|
+
len = args.length
|
73
|
+
if mname =~ /=$/
|
74
|
+
if len != 1
|
75
|
+
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
|
76
|
+
end
|
77
|
+
if self.frozen?
|
78
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(1)
|
79
|
+
end
|
80
|
+
name = mname.chop.to_sym
|
81
|
+
@table[name] = args[0]
|
82
|
+
self.new_ostruct_member(name)
|
83
|
+
elsif len == 0
|
84
|
+
@table[mid]
|
85
|
+
else
|
86
|
+
raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
|
3
|
+
require 'atig/util'
|
4
|
+
|
5
|
+
module Atig
|
6
|
+
class Scheduler
|
7
|
+
include Util
|
8
|
+
|
9
|
+
attr_reader :search
|
10
|
+
|
11
|
+
def initialize(context, api, search, stream)
|
12
|
+
@log = context.log
|
13
|
+
@api = api
|
14
|
+
@search = search
|
15
|
+
@stream = stream
|
16
|
+
@agents = []
|
17
|
+
|
18
|
+
@queue = SizedQueue.new 10
|
19
|
+
daemon do
|
20
|
+
f = @queue.pop
|
21
|
+
f.call
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def repeat(interval,opts={}, &f)
|
26
|
+
@queue.push(lambda{ safe { f.call @api } })
|
27
|
+
t = daemon do
|
28
|
+
sleep interval
|
29
|
+
log :debug, "agent #{t.inspect} is invoked"
|
30
|
+
@queue.push(lambda{ safe { f.call @api } })
|
31
|
+
end
|
32
|
+
|
33
|
+
log :info, "repeat agent #{t.inspect} is registered"
|
34
|
+
@agents << t
|
35
|
+
t
|
36
|
+
end
|
37
|
+
|
38
|
+
def delay(interval, opts={}, &f)
|
39
|
+
sleep interval
|
40
|
+
@queue.push(lambda{ safe { f.call @api } })
|
41
|
+
end
|
42
|
+
|
43
|
+
def stream(&f)
|
44
|
+
return nil unless @stream
|
45
|
+
@stream_thread.kill if @stream_thread
|
46
|
+
@stream_thread = daemon {
|
47
|
+
f.call @stream
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def re_try(count, &f)
|
52
|
+
begin
|
53
|
+
f.call
|
54
|
+
rescue => e
|
55
|
+
log :error, [count, e.inspect].inspect
|
56
|
+
if count > 0
|
57
|
+
count -= 1
|
58
|
+
sleep 1
|
59
|
+
log :debug, "retry"
|
60
|
+
retry
|
61
|
+
end
|
62
|
+
log :error, "Some Error Happened: #{e}"
|
63
|
+
raise e
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def limit
|
68
|
+
@api.limit
|
69
|
+
end
|
70
|
+
|
71
|
+
def remain
|
72
|
+
@api.remain
|
73
|
+
end
|
74
|
+
|
75
|
+
def reset
|
76
|
+
@api.reset
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/atig/search.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'json'
|
3
|
+
require 'atig/http'
|
4
|
+
require 'atig/url_escape'
|
5
|
+
|
6
|
+
module Atig
|
7
|
+
class Search
|
8
|
+
def search(query, options = {})
|
9
|
+
search = URI("http://search.twitter.com/search.json")
|
10
|
+
search.path = "/search.json"
|
11
|
+
params = options; options[:q] = query
|
12
|
+
search.query = options.to_query_str
|
13
|
+
http = Http.new nil
|
14
|
+
req = http.req(:get, search)
|
15
|
+
res = http.http(search, 5, 10).request(req)
|
16
|
+
res = JSON.parse(res.body)
|
17
|
+
rescue Errno::ETIMEDOUT, JSON::ParserError, IOError, Timeout::Error, Errno::ECONNRESET => e
|
18
|
+
@log.error e
|
19
|
+
text
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require 'atig/basic_twitter'
|
3
|
+
require 'atig/http'
|
4
|
+
|
5
|
+
module Atig
|
6
|
+
# from tig.rb
|
7
|
+
class SearchTwitter < BasicTwitter
|
8
|
+
def initialize(context)
|
9
|
+
super context, :search_api_base
|
10
|
+
@http = Atig::Http.new @log
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
def request(uri, opts)
|
15
|
+
header = {}
|
16
|
+
req = @http.req :get, uri, header
|
17
|
+
@log.debug [req.method, uri.to_s]
|
18
|
+
@http.http(uri, 30, 30).request req
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Atig
|
5
|
+
class SizedHash
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@xs, :size
|
9
|
+
|
10
|
+
def initialize(size)
|
11
|
+
@size = size
|
12
|
+
@xs = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
kv = @xs.assoc(key)
|
17
|
+
if kv then
|
18
|
+
@xs.delete kv
|
19
|
+
@xs.push kv
|
20
|
+
kv[1]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(key,value)
|
25
|
+
@xs.push [key, value]
|
26
|
+
@xs.shift if @xs.size > @size
|
27
|
+
end
|
28
|
+
|
29
|
+
def key?(key)
|
30
|
+
@xs.any?{|k,_| key == k }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|