atig 0.0.1
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/.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
|