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.
Files changed (171) hide show
  1. data/.gitignore +24 -0
  2. data/Gemfile +3 -0
  3. data/README.mkdn +52 -0
  4. data/Rakefile +15 -0
  5. data/atig.gemspec +25 -0
  6. data/bin/atig +74 -0
  7. data/docs/OMakefile +32 -0
  8. data/docs/OMakeroot +45 -0
  9. data/docs/_static/allow.png +0 -0
  10. data/docs/_static/emacs.png +0 -0
  11. data/docs/_static/irc_setting.png +0 -0
  12. data/docs/_static/irssi.png +0 -0
  13. data/docs/_static/limechat.png +0 -0
  14. data/docs/_static/limechat_s.png +0 -0
  15. data/docs/_static/oauth_channel.png +0 -0
  16. data/docs/_static/screenshot.png +0 -0
  17. data/docs/_static/structure.png +0 -0
  18. data/docs/_static/verify.png +0 -0
  19. data/docs/changelog.rst +96 -0
  20. data/docs/commandline_options.rst +21 -0
  21. data/docs/commands.rst +84 -0
  22. data/docs/conf.py +194 -0
  23. data/docs/config.rst +159 -0
  24. data/docs/feature.rst +41 -0
  25. data/docs/graphics.graffle +1995 -0
  26. data/docs/hacking_guide.rst +43 -0
  27. data/docs/index.rst +109 -0
  28. data/docs/irc.rst +31 -0
  29. data/docs/options.rst +75 -0
  30. data/docs/quickstart.rst +89 -0
  31. data/docs/resize.sh +7 -0
  32. data/docs/tiarra.rst +2 -0
  33. data/docs/tig.rst +21 -0
  34. data/lib/atig.rb +19 -0
  35. data/lib/atig/agent.rb +8 -0
  36. data/lib/atig/agent/agent.rb +38 -0
  37. data/lib/atig/agent/clenup.rb +23 -0
  38. data/lib/atig/agent/dm.rb +35 -0
  39. data/lib/atig/agent/following.rb +45 -0
  40. data/lib/atig/agent/full_list.rb +20 -0
  41. data/lib/atig/agent/list.rb +55 -0
  42. data/lib/atig/agent/list_status.rb +46 -0
  43. data/lib/atig/agent/mention.rb +13 -0
  44. data/lib/atig/agent/other_list.rb +18 -0
  45. data/lib/atig/agent/own_list.rb +18 -0
  46. data/lib/atig/agent/stream_follow.rb +38 -0
  47. data/lib/atig/agent/timeline.rb +13 -0
  48. data/lib/atig/agent/user_stream.rb +31 -0
  49. data/lib/atig/basic_twitter.rb +116 -0
  50. data/lib/atig/bitly.rb +52 -0
  51. data/lib/atig/channel.rb +5 -0
  52. data/lib/atig/channel/channel.rb +17 -0
  53. data/lib/atig/channel/dm.rb +14 -0
  54. data/lib/atig/channel/list.rb +76 -0
  55. data/lib/atig/channel/mention.rb +20 -0
  56. data/lib/atig/channel/retweet.rb +28 -0
  57. data/lib/atig/channel/timeline.rb +74 -0
  58. data/lib/atig/command.rb +21 -0
  59. data/lib/atig/command/autofix.rb +58 -0
  60. data/lib/atig/command/command.rb +24 -0
  61. data/lib/atig/command/command_helper.rb +95 -0
  62. data/lib/atig/command/destroy.rb +44 -0
  63. data/lib/atig/command/dm.rb +31 -0
  64. data/lib/atig/command/favorite.rb +27 -0
  65. data/lib/atig/command/info.rb +50 -0
  66. data/lib/atig/command/limit.rb +15 -0
  67. data/lib/atig/command/location.rb +23 -0
  68. data/lib/atig/command/name.rb +18 -0
  69. data/lib/atig/command/option.rb +37 -0
  70. data/lib/atig/command/refresh.rb +18 -0
  71. data/lib/atig/command/reply.rb +37 -0
  72. data/lib/atig/command/retweet.rb +63 -0
  73. data/lib/atig/command/search.rb +51 -0
  74. data/lib/atig/command/spam.rb +26 -0
  75. data/lib/atig/command/status.rb +41 -0
  76. data/lib/atig/command/thread.rb +44 -0
  77. data/lib/atig/command/time.rb +32 -0
  78. data/lib/atig/command/uptime.rb +32 -0
  79. data/lib/atig/command/user.rb +42 -0
  80. data/lib/atig/command/user_info.rb +27 -0
  81. data/lib/atig/command/version.rb +49 -0
  82. data/lib/atig/command/whois.rb +39 -0
  83. data/lib/atig/db/db.rb +60 -0
  84. data/lib/atig/db/followings.rb +131 -0
  85. data/lib/atig/db/listenable.rb +22 -0
  86. data/lib/atig/db/lists.rb +76 -0
  87. data/lib/atig/db/roman.rb +30 -0
  88. data/lib/atig/db/sized_uniq_array.rb +62 -0
  89. data/lib/atig/db/sql.rb +35 -0
  90. data/lib/atig/db/statuses.rb +147 -0
  91. data/lib/atig/db/transaction.rb +47 -0
  92. data/lib/atig/exception_util.rb +26 -0
  93. data/lib/atig/gateway.rb +62 -0
  94. data/lib/atig/gateway/channel.rb +99 -0
  95. data/lib/atig/gateway/session.rb +326 -0
  96. data/lib/atig/http.rb +95 -0
  97. data/lib/atig/ifilter.rb +7 -0
  98. data/lib/atig/ifilter/expand_url.rb +74 -0
  99. data/lib/atig/ifilter/retweet.rb +14 -0
  100. data/lib/atig/ifilter/retweet_time.rb +16 -0
  101. data/lib/atig/ifilter/sanitize.rb +18 -0
  102. data/lib/atig/ifilter/strip.rb +15 -0
  103. data/lib/atig/ifilter/utf7.rb +26 -0
  104. data/lib/atig/ifilter/xid.rb +36 -0
  105. data/lib/atig/levenshtein.rb +49 -0
  106. data/lib/atig/monkey.rb +4 -0
  107. data/lib/atig/oauth-patch.rb +40 -0
  108. data/lib/atig/oauth.rb +55 -0
  109. data/lib/atig/ofilter.rb +4 -0
  110. data/lib/atig/ofilter/escape_url.rb +102 -0
  111. data/lib/atig/ofilter/footer.rb +20 -0
  112. data/lib/atig/ofilter/geo.rb +17 -0
  113. data/lib/atig/ofilter/short_url.rb +47 -0
  114. data/lib/atig/option.rb +90 -0
  115. data/lib/atig/scheduler.rb +79 -0
  116. data/lib/atig/search.rb +22 -0
  117. data/lib/atig/search_twitter.rb +21 -0
  118. data/lib/atig/sized_hash.rb +33 -0
  119. data/lib/atig/stream.rb +66 -0
  120. data/lib/atig/twitter.rb +79 -0
  121. data/lib/atig/twitter_struct.rb +63 -0
  122. data/lib/atig/unu.rb +27 -0
  123. data/lib/atig/update_checker.rb +53 -0
  124. data/lib/atig/url_escape.rb +62 -0
  125. data/lib/atig/util.rb +16 -0
  126. data/lib/atig/version.rb +3 -0
  127. data/lib/memory_profiler.rb +77 -0
  128. data/spec/command/autofix_spec.rb +35 -0
  129. data/spec/command/destroy_spec.rb +98 -0
  130. data/spec/command/dm_spec.rb +28 -0
  131. data/spec/command/favorite_spec.rb +55 -0
  132. data/spec/command/limit_spec.rb +27 -0
  133. data/spec/command/location_spec.rb +25 -0
  134. data/spec/command/name_spec.rb +19 -0
  135. data/spec/command/option_spec.rb +133 -0
  136. data/spec/command/refresh_spec.rb +22 -0
  137. data/spec/command/reply_spec.rb +79 -0
  138. data/spec/command/retweet_spec.rb +66 -0
  139. data/spec/command/spam_spec.rb +27 -0
  140. data/spec/command/status_spec.rb +44 -0
  141. data/spec/command/thread_spec.rb +91 -0
  142. data/spec/command/time_spec.rb +52 -0
  143. data/spec/command/uptime_spec.rb +55 -0
  144. data/spec/command/user_info_spec.rb +42 -0
  145. data/spec/command/user_spec.rb +50 -0
  146. data/spec/command/version_spec.rb +67 -0
  147. data/spec/command/whois_spec.rb +78 -0
  148. data/spec/db/followings_spec.rb +100 -0
  149. data/spec/db/listenable_spec.rb +32 -0
  150. data/spec/db/lists_spec.rb +104 -0
  151. data/spec/db/roman_spec.rb +17 -0
  152. data/spec/db/sized_uniq_array_spec.rb +63 -0
  153. data/spec/db/statuses_spec.rb +180 -0
  154. data/spec/ifilter/expand_url_spec.rb +44 -0
  155. data/spec/ifilter/retweet_spec.rb +28 -0
  156. data/spec/ifilter/retweet_time_spec.rb +25 -0
  157. data/spec/ifilter/sanitize_spec.rb +25 -0
  158. data/spec/ifilter/sid_spec.rb +29 -0
  159. data/spec/ifilter/strip_spec.rb +23 -0
  160. data/spec/ifilter/tid_spec.rb +29 -0
  161. data/spec/ifilter/utf7_spec.rb +30 -0
  162. data/spec/levenshtein_spec.rb +24 -0
  163. data/spec/ofilter/escape_url_spec.rb +50 -0
  164. data/spec/ofilter/footer_spec.rb +32 -0
  165. data/spec/ofilter/geo_spec.rb +33 -0
  166. data/spec/ofilter/short_url_spec.rb +127 -0
  167. data/spec/option_spec.rb +91 -0
  168. data/spec/sized_hash_spec.rb +45 -0
  169. data/spec/spec_helper.rb +35 -0
  170. data/spec/update_checker_spec.rb +55 -0
  171. metadata +326 -0
@@ -0,0 +1,66 @@
1
+ # -*- mode:ruby; coding:utf-8 -*-
2
+
3
+ require 'json'
4
+ require 'uri'
5
+ require 'logger'
6
+ require 'atig/twitter_struct'
7
+ require 'atig/util'
8
+ require 'atig/url_escape'
9
+
10
+ module Atig
11
+ class Stream
12
+ include Util
13
+
14
+ class APIFailed < StandardError; end
15
+ def initialize(context, user, password)
16
+ @log = context.log
17
+ @opts = context.opts
18
+ @user = user
19
+ @password = password
20
+ end
21
+
22
+ def watch(path, query={}, &f)
23
+ path.sub!(%r{\A/+}, "")
24
+
25
+ uri = api_base
26
+ uri.path += path
27
+ uri.path += ".json"
28
+ uri.query = query.to_query_str unless query.empty?
29
+
30
+ @log.debug [uri.to_s]
31
+
32
+ Net::HTTP.start(uri.host, uri.port) do |http|
33
+ request = Net::HTTP::Post.new uri.request_uri
34
+ request.basic_auth @user, @password
35
+
36
+ http.request(request) do |response|
37
+ unless response.code == '200' then
38
+ raise APIFailed,"#{response.code} #{response.message}"
39
+ end
40
+
41
+ begin
42
+ buffer = ''
43
+ response.read_body do |chunk|
44
+ next if chunk.chomp.empty?
45
+ buffer << chunk.to_s
46
+
47
+ if buffer =~ /\A(.*)\n/ then
48
+ text = $1
49
+ unless text.strip.empty?
50
+ f.call TwitterStruct.make(JSON.parse(text))
51
+ end
52
+ buffer = ''
53
+ end
54
+ end
55
+ rescue => e
56
+ raise APIFailed,e.to_s
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def api_base
63
+ URI(@opts.stream_api_base)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ require 'atig/basic_twitter'
4
+ require 'atig/http'
5
+
6
+ module Atig
7
+ # from tig.rb
8
+ class Twitter < BasicTwitter
9
+ def initialize(context, oauth)
10
+ super context, :api_base
11
+ @oauth = oauth
12
+ @http = Atig::Http.new @log
13
+ end
14
+
15
+ # authenticate = trueでないとSSL verified errorがでることがある
16
+ def page(path, name, authenticate = true, &block)
17
+ limit = 0.98 * @remain # 98% of IP based rate limit
18
+ r = []
19
+ cursor = -1
20
+ 1.upto(limit) do |num|
21
+ # next_cursor にアクセスするとNot found が返ってくることがあるので,その時はbreak
22
+ ret = api(path, { :cursor => cursor }, { :authenticate => authenticate }) rescue break
23
+ arr = ret[name.to_s]
24
+ r.concat arr
25
+ cursor = ret[:next_cursor]
26
+ break if cursor.zero?
27
+ end
28
+ r
29
+ end
30
+
31
+ def self.http_methods(*methods)
32
+ methods.each do |m|
33
+ self.module_eval <<END
34
+ def #{m}(path, query = {}, opts = {})
35
+ opts.update( :method => :#{m})
36
+ api path, query, opts
37
+ end
38
+ END
39
+ end
40
+ end
41
+ http_methods :get, :post, :put, :delete
42
+
43
+ protected
44
+ def request(uri, opts)
45
+ authenticate = opts.fetch(:authenticate, true)
46
+ method = opts.fetch(:method, :get)
47
+
48
+ header = {}
49
+ req = @http.req method, uri, header
50
+ @log.debug [req.method, uri.to_s]
51
+
52
+ if authenticate
53
+ oauth 30, req
54
+ else
55
+ @http.http(uri, 30, 30).request req
56
+ end
57
+ end
58
+
59
+ def oauth(time, req)
60
+ timeout(time) do
61
+ headers = {}
62
+ req.each{|k,v| headers[k] = v }
63
+
64
+ case req
65
+ when Net::HTTP::Get
66
+ @oauth.get req.path,headers
67
+ when Net::HTTP::Head
68
+ @oauth.head req.path,headers
69
+ when Net::HTTP::Post
70
+ @oauth.post req.path,req.body,headers
71
+ when Net::HTTP::Put
72
+ @oauth.put req.path,req.body,headers
73
+ when Net::HTTP::Delete
74
+ @oauth.delete req.path,headers
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,63 @@
1
+ # -*- mode:ruby; coding:utf-8 -*-
2
+ #
3
+
4
+ module Atig
5
+ # from tig.rb
6
+ class TwitterStruct
7
+ def self.make(obj)
8
+ case obj
9
+ when Hash
10
+ obj = obj.dup
11
+ obj.each do |k, v|
12
+ obj[k] = TwitterStruct.make(v)
13
+ end
14
+ TwitterStruct.new(obj)
15
+ when Array
16
+ obj.map {|i| TwitterStruct.make(i) }
17
+ else
18
+ obj
19
+ end
20
+ end
21
+
22
+ def initialize(obj)
23
+ @obj = obj
24
+ end
25
+
26
+ def id
27
+ @obj["id"]
28
+ end
29
+
30
+ def [](name)
31
+ @obj[name.to_s]
32
+ end
33
+
34
+ def []=(name,val)
35
+ @obj[name.to_s] = val
36
+ end
37
+
38
+ def merge(hash)
39
+ obj = @obj.dup
40
+ hash.each do|key,value|
41
+ obj[key.to_s] = value
42
+ end
43
+ TwitterStruct.make obj
44
+ end
45
+
46
+ def hash
47
+ self.id ? self.id.hash : super
48
+ end
49
+
50
+ def eql?(other)
51
+ self.hash == other.hash
52
+ end
53
+
54
+ def ==(other)
55
+ self.hash == other.hash
56
+ end
57
+
58
+ def method_missing(sym, *args)
59
+ # XXX
60
+ @obj[sym.to_s]
61
+ end
62
+ end
63
+ end
data/lib/atig/unu.rb ADDED
@@ -0,0 +1,27 @@
1
+ # -*- mode:ruby; coding:utf-8 -*-
2
+ module Atig
3
+ class Unu
4
+ def initialize(logger)
5
+ @log = logger
6
+ @http = Atig::Http.new logger
7
+ end
8
+
9
+ def shorten(url)
10
+ unu_url = "http://u.nu/"
11
+ unu = URI("#{unu_url}unu-api-simple")
12
+ url = URI.rstrip url
13
+ unu.query = { :url => url }.to_query_str
14
+ res = @http.http(unu, 5, 5).request(@http.req(:get, unu)).body
15
+
16
+ if res[0, 12] == unu_url
17
+ res
18
+ else
19
+ @log.error res
20
+ url
21
+ end
22
+ rescue Errno::ETIMEDOUT, JSON::ParserError, IOError, Timeout::Error, Errno::ECONNRESET => e
23
+ @log.error e
24
+ url
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ # -*- mode:ruby; coding:utf-8 -*-
2
+
3
+ module Atig
4
+ module UpdateChecker
5
+ def commits
6
+ uri = URI("http://github.com/api/v1/json/mzp/atig/commits/master")
7
+ http = Atig::Http.new
8
+ res = http.http(uri).request http.req(:get, uri)
9
+ JSON.parse(res.body)['commits']
10
+ end
11
+
12
+ def server_version
13
+ @server_version ||= instance_eval {
14
+ head = `git rev-parse HEAD 2>/dev/null`.chomp
15
+ head.empty?? "unknown" : head
16
+ }
17
+ end
18
+
19
+ def local_repos?(rev)
20
+ system("git show #{rev} > /dev/null 2>&1")
21
+ end
22
+
23
+ def git?
24
+ system('which git > /dev/null 2>&1')
25
+ end
26
+
27
+ def latest
28
+ unless git? then
29
+ []
30
+ else
31
+ cs = commits
32
+ latest = cs.first['id'][/^[0-9a-z]{40}$/]
33
+ raise "github API changed?" unless latest
34
+
35
+ if local_repos?(latest) then
36
+ []
37
+ else
38
+ current = cs.map {|i| i['id'] }.index(server_version)
39
+ if current then
40
+ cs[0...current]
41
+ else
42
+ cs
43
+ end.map {|i| i['message'] }
44
+ end
45
+ end
46
+ rescue Errno::ECONNREFUSED, Timeout::Error => e
47
+ []
48
+ end
49
+
50
+ module_function :latest, :commits, :server_version, :local_repos?, :git?
51
+ end
52
+ end
53
+
@@ -0,0 +1,62 @@
1
+ # -*- mode:ruby; coding:utf-8 -*-
2
+
3
+ class Hash
4
+ # { :f => "v" } #=> "f=v"
5
+ # { "f" => [1, 2] } #=> "f=1&f=2"
6
+ # { "f" => "" } #=> "f="
7
+ # { "f" => nil } #=> "f"
8
+ def to_query_str separator = "&"
9
+ inject([]) do |r, (k, v)|
10
+ k = URI.encode_component k.to_s
11
+ (v.is_a?(Array) ? v : [v]).each do |i|
12
+ if i.nil?
13
+ r << k
14
+ else
15
+ r << "#{k}=#{URI.encode_component i.to_s}"
16
+ end
17
+ end
18
+ r
19
+ end.join separator
20
+ end
21
+ end
22
+
23
+ class String
24
+ def ch?
25
+ /\A[&#+!][^ \007,]{1,50}\z/ === self
26
+ end
27
+
28
+ def screen_name?
29
+ /\A[A-Za-z0-9_]{1,15}\z/ === self
30
+ end
31
+
32
+ def encoding! enc
33
+ return self unless respond_to? :force_encoding
34
+ force_encoding enc
35
+ end
36
+ end
37
+
38
+ module URI::Escape
39
+ alias :_orig_escape :escape
40
+
41
+ if defined? ::RUBY_REVISION and RUBY_REVISION < 24544
42
+ # URI.escape("あ1") #=> "%E3%81%82\xEF\xBC\x91"
43
+ # URI("file:///4") #=> #<URI::Generic:0x9d09db0 URL:file:/4>
44
+ # "\\d" -> "[0-9]" for Ruby 1.9
45
+ def escape str, unsafe = %r{[^-_.!~*'()a-zA-Z0-9;/?:@&=+$,\[\]]} #'
46
+ _orig_escape(str, unsafe)
47
+ end
48
+ alias :encode :escape
49
+ end
50
+
51
+ def encode_component str, unsafe = /[^-_.!~*'()a-zA-Z0-9 ]/
52
+ _orig_escape(str, unsafe).tr(" ", "+")
53
+ end
54
+
55
+ def rstrip str
56
+ str.sub(%r{
57
+ (?: ( / [^/?#()]* (?: \( [^/?#()]* \) [^/?#()]* )* ) \) [^/?#()]*
58
+ | \.
59
+ ) \z
60
+ }x, "\\1")
61
+ end
62
+ end
data/lib/atig/util.rb ADDED
@@ -0,0 +1,16 @@
1
+ # -*- mode:ruby; coding:utf-8 -*-
2
+
3
+ require 'atig/exception_util'
4
+ module Atig
5
+ module Util
6
+ private
7
+ include ExceptionUtil
8
+ def log(type, s)
9
+ if @log then
10
+ @log.send type, "[#{self.class}] #{s}"
11
+ else
12
+ STDERR.puts s
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ module Atig
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,77 @@
1
+ # This is a memory profiler for Ruby. Once started, it runs in a thread in the
2
+ # background, periodically inspecting Ruby's ObjectSpace to look for new
3
+ # objects and printing a count of objects added and removed since the previous
4
+ # cycle.
5
+ #
6
+ # To use the profiler, do something like this:
7
+ #
8
+ # require 'memory_profiler'
9
+ #
10
+ # MemoryProfiler.start
11
+ #
12
+ # The profiler will write logs to ./log/memory_profiler.log.
13
+ #
14
+ # If you start MemoryProfiler with the ':string_debug => true' option, then it
15
+ # will dump a list of all strings in the app into the log/ directory after
16
+ # each cycle. You can then use 'diff' to spot which strings were added
17
+ # between runs.
18
+ class MemoryProfiler
19
+ DEFAULTS = {:delay => 10, :string_debug => false}
20
+
21
+ def self.start(opt={})
22
+ opt = DEFAULTS.dup.merge(opt)
23
+
24
+ Thread.new do
25
+ prev = Hash.new(0)
26
+ curr = Hash.new(0)
27
+ curr_strings = []
28
+ delta = Hash.new(0)
29
+
30
+ file = File.open("log/memory_profiler.#{Time.now.to_i}.log",'w')
31
+
32
+ loop do
33
+ begin
34
+ GC.start
35
+ curr.clear
36
+
37
+ curr_strings = [] if opt[:string_debug]
38
+
39
+ ObjectSpace.each_object do |o|
40
+ curr[o.class] += 1 #Marshal.dump(o).size rescue 1
41
+ if opt[:string_debug] and o.class == String
42
+ curr_strings.push o
43
+ end
44
+ end
45
+
46
+ if opt[:string_debug]
47
+ File.open("log/memory_profiler_strings.log.#{Time.now.to_i}",'w') do |f|
48
+ curr_strings.sort.each do |s|
49
+ f.puts s
50
+ end
51
+ end
52
+ curr_strings.clear
53
+ end
54
+
55
+ delta.clear
56
+ (curr.keys + delta.keys).uniq.each do |k,v|
57
+ delta[k] = curr[k]-prev[k]
58
+ end
59
+
60
+ file.puts "Top 20: #{Time.now}"
61
+ delta.sort_by { |k,v| -v.abs }[0..19].sort_by { |k,v| -v}.each do |k,v|
62
+ file.printf "%+5d: %s (%d)\n", v, k.name, curr[k] unless v == 0
63
+ end
64
+ file.flush
65
+
66
+ delta.clear
67
+ prev.clear
68
+ prev.update curr
69
+ GC.start
70
+ rescue Exception => err
71
+ STDERR.puts "** memory_profiler error: #{err}"
72
+ end
73
+ sleep opt[:delay]
74
+ end
75
+ end
76
+ end
77
+ end