earthquake 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -6,10 +6,12 @@ source "http://rubygems.org"
6
6
  gem 'twitter-stream', :require => 'twitter/json_stream'
7
7
  gem 'notify'
8
8
  gem 'i18n'
9
- gem 'activesupport', :require => 'active_support/core_ext'
9
+ gem 'activesupport', :require => ['active_support/core_ext', 'active_support/dependencies']
10
10
  gem 'awesome_print', :require => 'ap'
11
11
  gem 'launchy'
12
12
  gem 'oauth'
13
+ gem 'twitter_oauth'
14
+ gem 'termcolor'
13
15
 
14
16
  # Add dependencies to develop your gem here.
15
17
  # Include everything needed to run rake, tests, features, etc.
data/Gemfile.lock CHANGED
@@ -7,14 +7,17 @@ GEM
7
7
  diff-lcs (1.1.2)
8
8
  eventmachine (0.12.10)
9
9
  git (1.2.5)
10
+ highline (1.6.1)
10
11
  i18n (0.5.0)
11
12
  jeweler (1.5.2)
12
13
  bundler (~> 1.0.0)
13
14
  git (>= 1.2.5)
14
15
  rake
16
+ json (1.5.1)
15
17
  launchy (0.4.0)
16
18
  configuration (>= 0.0.5)
17
19
  rake (>= 0.8.1)
20
+ mime-types (1.16)
18
21
  notify (0.3.0)
19
22
  oauth (0.4.4)
20
23
  rake (0.8.7)
@@ -28,9 +31,15 @@ GEM
28
31
  rspec-expectations (2.3.0)
29
32
  diff-lcs (~> 1.1.2)
30
33
  rspec-mocks (2.3.0)
34
+ termcolor (1.2.1)
35
+ highline (>= 1.5.0)
31
36
  twitter-stream (0.1.12)
32
37
  eventmachine (>= 0.12.8)
33
38
  roauth (>= 0.0.2)
39
+ twitter_oauth (0.4.3)
40
+ json (>= 1.1.9)
41
+ mime-types (>= 1.16)
42
+ oauth (>= 0.4.1)
34
43
 
35
44
  PLATFORMS
36
45
  ruby
@@ -46,4 +55,6 @@ DEPENDENCIES
46
55
  oauth
47
56
  rcov
48
57
  rspec (~> 2.3.0)
58
+ termcolor
49
59
  twitter-stream
60
+ twitter_oauth
data/README.md CHANGED
@@ -3,6 +3,8 @@ Earthquake
3
3
 
4
4
  Twitter Client on Terminal with Streaming API.
5
5
 
6
+ It supports ruby 1.9 only.
7
+
6
8
  Install
7
9
  ----
8
10
 
@@ -19,9 +21,7 @@ TODO
19
21
  ----
20
22
 
21
23
  * plugin system
22
- * command
23
24
  * filter
24
- * colorize
25
25
  * reconnect
26
26
 
27
27
  Copyright
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.0
@@ -0,0 +1,95 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{earthquake}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["jugyo"]
12
+ s.date = %q{2011-03-16}
13
+ s.default_executable = %q{earthquake}
14
+ s.description = %q{Twitter Client on Terminal with Twitter Streaming API.}
15
+ s.email = %q{jugyo.org@gmail.com}
16
+ s.executables = ["earthquake"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".rspec",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.md",
28
+ "Rakefile",
29
+ "VERSION",
30
+ "bin/earthquake",
31
+ "earthquake.gemspec",
32
+ "lib/earthquake.rb",
33
+ "lib/earthquake/commands.rb",
34
+ "lib/earthquake/core.rb",
35
+ "lib/earthquake/ext.rb",
36
+ "lib/earthquake/get_access_token.rb",
37
+ "lib/earthquake/input.rb",
38
+ "lib/earthquake/output.rb",
39
+ "lib/earthquake/twitter.rb",
40
+ "spec/earthquake_spec.rb",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/jugyo/earthquake}
44
+ s.licenses = ["MIT"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.6.0}
47
+ s.summary = %q{Twitter Client on Terminal.}
48
+ s.test_files = [
49
+ "spec/earthquake_spec.rb",
50
+ "spec/spec_helper.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<twitter-stream>, [">= 0"])
58
+ s.add_runtime_dependency(%q<notify>, [">= 0"])
59
+ s.add_runtime_dependency(%q<i18n>, [">= 0"])
60
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
61
+ s.add_runtime_dependency(%q<awesome_print>, [">= 0"])
62
+ s.add_runtime_dependency(%q<launchy>, [">= 0"])
63
+ s.add_runtime_dependency(%q<oauth>, [">= 0"])
64
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
65
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
66
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
67
+ s.add_development_dependency(%q<rcov>, [">= 0"])
68
+ else
69
+ s.add_dependency(%q<twitter-stream>, [">= 0"])
70
+ s.add_dependency(%q<notify>, [">= 0"])
71
+ s.add_dependency(%q<i18n>, [">= 0"])
72
+ s.add_dependency(%q<activesupport>, [">= 0"])
73
+ s.add_dependency(%q<awesome_print>, [">= 0"])
74
+ s.add_dependency(%q<launchy>, [">= 0"])
75
+ s.add_dependency(%q<oauth>, [">= 0"])
76
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
77
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
78
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
79
+ s.add_dependency(%q<rcov>, [">= 0"])
80
+ end
81
+ else
82
+ s.add_dependency(%q<twitter-stream>, [">= 0"])
83
+ s.add_dependency(%q<notify>, [">= 0"])
84
+ s.add_dependency(%q<i18n>, [">= 0"])
85
+ s.add_dependency(%q<activesupport>, [">= 0"])
86
+ s.add_dependency(%q<awesome_print>, [">= 0"])
87
+ s.add_dependency(%q<launchy>, [">= 0"])
88
+ s.add_dependency(%q<oauth>, [">= 0"])
89
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
90
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
91
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
92
+ s.add_dependency(%q<rcov>, [">= 0"])
93
+ end
94
+ end
95
+
data/lib/earthquake.rb CHANGED
@@ -4,10 +4,14 @@ require 'readline'
4
4
  require 'bundler/setup'
5
5
  Bundler.require :default
6
6
 
7
- $:.unshift File.expand_path('..', __FILE__)
7
+ Thread.abort_on_exception = true
8
8
 
9
- require 'earthquake/core'
10
-
11
- module Earthquake
12
- extend Earthquake::Core
13
- end
9
+ %w(
10
+ ext
11
+ core
12
+ output
13
+ input
14
+ get_access_token
15
+ twitter
16
+ commands
17
+ ).each { |name| require_dependency File.expand_path("../earthquake/#{name}", __FILE__) }
@@ -0,0 +1,51 @@
1
+ # encoding: UTF-8
2
+ module Earthquake
3
+ init do
4
+ command :exit do |m|
5
+ stop
6
+ end
7
+
8
+ command :help do |m|
9
+ puts "TODO..."
10
+ end
11
+
12
+ command :restart do |m|
13
+ puts 'restarting...'
14
+ exec File.expand_path('../../..//bin/earthquake', __FILE__)
15
+ end
16
+
17
+ command :eval do |m|
18
+ ap eval(m[1])
19
+ end
20
+
21
+ # update
22
+ command %r|^[^/]+| do |m|
23
+ twitter.update(m[0]) if confirm(m[0])
24
+ end
25
+
26
+ command %r|^/reply (\d+)\s+(.*)|, :as => :reply do |m|
27
+ # TODO
28
+ ap m
29
+ end
30
+
31
+ command :status do |m|
32
+ puts_item twitter.status(m[1])
33
+ end
34
+
35
+ command :delete do |m|
36
+ twitter.status_destroy(m[1])
37
+ end
38
+
39
+ command :retweet do |m|
40
+ twitter.retweet(m[1])
41
+ end
42
+
43
+ command :favorite do |m|
44
+ twitter.favorite(m[1])
45
+ end
46
+
47
+ command :unfavorite do |m|
48
+ twitter.unfavorite(m[1])
49
+ end
50
+ end
51
+ end
@@ -1,9 +1,33 @@
1
+ # encoding: UTF-8
1
2
  module Earthquake
2
3
  module Core
3
4
  attr_accessor :config
4
- attr_reader :item_queue
5
5
 
6
- def init(*argv)
6
+ def item_queue
7
+ @item_queue ||= []
8
+ end
9
+
10
+ def inits
11
+ @inits ||= []
12
+ end
13
+
14
+ def init(&block)
15
+ inits << block
16
+ end
17
+
18
+ def init_all
19
+ inits.each { |block| block.call }
20
+ inits.clear
21
+ end
22
+
23
+ def reload
24
+ loaded = ActiveSupport::Dependencies.loaded.dup
25
+ ActiveSupport::Dependencies.clear
26
+ loaded.each { |lib| require_dependency lib }
27
+ init_all
28
+ end
29
+
30
+ def load_config(*argv)
7
31
  # TODO: parse argv
8
32
  self.config = {
9
33
  :dir => File.expand_path('~/.earthquake'),
@@ -13,84 +37,37 @@ module Earthquake
13
37
  config[:file] ||= File.join(config[:dir], 'config')
14
38
  load config[:file]
15
39
 
16
- get_access_token unless self.config[:access_key] && self.config[:access_secret]
17
- end
18
-
19
- def get_access_token
20
- consumer = OAuth::Consumer.new(
21
- self.config[:consumer_key],
22
- self.config[:consumer_secret],
23
- :site => 'http://api.twitter.com'
24
- )
25
- request_token = consumer.get_request_token
26
-
27
- puts "1) open: #{request_token.authorize_url}"
28
- Launchy::Browser.run(request_token.authorize_url)
29
-
30
- print "2) Enter the PIN: "
31
- pin = gets.strip
32
-
33
- access_token = request_token.get_access_token(:oauth_verifier => pin)
34
- config[:access_key] = access_token.token
35
- config[:access_secret] = access_token.secret
36
-
37
- puts "Saving 'access_key' and 'access_secret' to '#{config[:file]}'"
38
- File.open(config[:file], 'a') do |f|
39
- f << "\n"
40
- f << "Earthquake.config[:access_key] = '#{config[:access_key]}'"
41
- f << "\n"
42
- f << "Earthquake.config[:access_secret] = '#{config[:access_secret]}'"
43
- end
40
+ get_access_token unless self.config[:token] && self.config[:secret]
44
41
  end
45
42
 
46
43
  def start(*argv)
47
- init(*argv)
48
-
49
- @item_queue = []
44
+ load_config(*argv)
50
45
 
51
- Thread.abort_on_exception = true
46
+ init_all
52
47
 
53
48
  Thread.start do
54
- while buf = Readline.readline("[earthquake] ", true)
55
- begin
56
- buf = buf.strip
57
-
58
- if buf == '/exit'
59
- # TODO: refactor
60
- @stream.stop if @stream
61
- EventMachine.stop if EventMachine.reactor_running?
62
- end
63
- rescue => e
64
- notify "[ERROR] #{e}"
65
- end
49
+ while buf = Readline.readline(" ", true)
50
+ input(buf.strip)
66
51
  end
67
52
  end
68
53
 
69
54
  Thread.start do
70
55
  loop do
71
- # TODO: handle the response that include friends
72
- if Readline.line_buffer.empty?
73
- while item = item_queue.shift
74
- begin
75
- insert "#{item["user"]["screen_name"]}: #{item["text"]}"
76
- rescue => e
77
- insert do
78
- ap item
79
- end
80
- # notify "[ERROR] #{e}"
81
- end
82
- end
56
+ if Readline.line_buffer.nil? || Readline.line_buffer.empty?
57
+ output
58
+ sleep 1
59
+ else
60
+ sleep 2
83
61
  end
84
- sleep 1
85
62
  end
86
63
  end
87
64
 
88
65
  EventMachine::run {
89
- @stream = Twitter::JSONStream.connect(
66
+ @stream = ::Twitter::JSONStream.connect(
90
67
  :ssl => true,
91
68
  :host => 'userstream.twitter.com',
92
69
  :path => '/2/user.json',
93
- :oauth => config.slice(:consumer_key, :consumer_secret, :access_key, :access_secret)
70
+ :oauth => config.slice(:consumer_key, :consumer_secret).merge(:access_key => config[:token], :access_secret => config[:secret])
94
71
  )
95
72
 
96
73
  @stream.each_item do |item|
@@ -112,27 +89,19 @@ module Earthquake
112
89
  $stdout.flush
113
90
  end
114
91
 
115
- trap('TERM') {
116
- @stream.stop
117
- EventMachine.stop if EventMachine.reactor_running?
118
- }
92
+ trap('TERM') { stop }
119
93
  }
120
94
  end
121
95
 
122
- def notify(message)
123
- Notify.notify 'earthquake', message
96
+ def stop
97
+ @stream.stop
98
+ EventMachine.stop if EventMachine.reactor_running?
124
99
  end
125
100
 
126
- def insert(*messages)
127
- clear_line
128
- puts messages unless messages.empty?
129
- yield if block_given?
130
- ensure
131
- Readline.refresh_line
132
- end
133
-
134
- def clear_line
135
- print "\e[0G" + "\e[K"
101
+ def notify(message)
102
+ Notify.notify 'earthquake', message
136
103
  end
137
104
  end
105
+
106
+ extend Core
138
107
  end
@@ -0,0 +1,7 @@
1
+ class String
2
+ alias_method :t, :termcolor
3
+
4
+ def e
5
+ TermColor.escape(self)
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ module Earthquake
2
+ module GetAccessToken
3
+ def get_access_token
4
+ consumer = OAuth::Consumer.new(
5
+ self.config[:consumer_key],
6
+ self.config[:consumer_secret],
7
+ :site => 'http://api.twitter.com'
8
+ )
9
+ request_token = consumer.get_request_token
10
+
11
+ puts "1) open: #{request_token.authorize_url}"
12
+ Launchy::Browser.run(request_token.authorize_url)
13
+
14
+ print "2) Enter the PIN: "
15
+ pin = gets.strip
16
+
17
+ access_token = request_token.get_access_token(:oauth_verifier => pin)
18
+ config[:token] = access_token.token
19
+ config[:secret] = access_token.secret
20
+
21
+ puts "Saving 'token' and 'secret' to '#{config[:file]}'"
22
+ File.open(config[:file], 'a') do |f|
23
+ f << "\n"
24
+ f << "Earthquake.config[:token] = '#{config[:token]}'"
25
+ f << "\n"
26
+ f << "Earthquake.config[:secret] = '#{config[:secret]}'"
27
+ end
28
+ end
29
+ end
30
+
31
+ extend GetAccessToken
32
+ end
@@ -0,0 +1,64 @@
1
+ # encoding: UTF-8
2
+ module Earthquake
3
+ module Input
4
+ def commands
5
+ @commands ||= []
6
+ end
7
+
8
+ def command_names
9
+ @command_names ||= []
10
+ end
11
+
12
+ def input(text)
13
+ begin
14
+ reload if config[:debug]
15
+ if command = commands.detect { |c| c[:pattern] =~ text }
16
+ command[:block].call($~)
17
+ elsif !text.empty?
18
+ puts "<yellow>Command not found</yellow>".t
19
+ end
20
+ rescue Exception => e
21
+ puts "<on_red>[ERROR] #{e.message.e}\n#{e.backtrace.join("\n").e}</on_red>".t
22
+ end
23
+ end
24
+
25
+ def command(pattern, options = {}, &block)
26
+ if block
27
+ if pattern.is_a?(String) || pattern.is_a?(Symbol)
28
+ command_name = "#{config[:command_prefix]}#{pattern}"
29
+ command_names << command_name
30
+ pattern = /^#{Regexp.quote(command_name)}\s*(.*)$/
31
+ end
32
+ command_names << "#{config[:command_prefix]}#{options[:as]}" if options[:as]
33
+ commands << {:pattern => pattern, :block => block}
34
+ else
35
+ commands.detect { |c| c[:name] == name }
36
+ end
37
+ end
38
+
39
+ def confirm(message, type = :y)
40
+ case type
41
+ when :y
42
+ print "#{message} [Yn] "
43
+ return !(gets.strip =~ /^n$/i)
44
+ when :n
45
+ print "#{message} [yN] "
46
+ return !!(gets.strip =~ /^y$/i)
47
+ else
48
+ raise "type must be :y or :n"
49
+ end
50
+ end
51
+ end
52
+
53
+ init do
54
+ Readline.completion_proc = lambda { |text|
55
+ command_names.grep /^#{Regexp.quote(text)}/
56
+ }
57
+
58
+ config[:command_prefix] ||= '/'
59
+
60
+ commands.clear
61
+ end
62
+
63
+ extend Input
64
+ end
@@ -0,0 +1,71 @@
1
+ module Earthquake
2
+ module Output
3
+ def output
4
+ return if item_queue.empty?
5
+ insert do
6
+ while item = item_queue.shift
7
+ puts_item(item)
8
+ end
9
+ end
10
+ end
11
+
12
+ def puts_item(item)
13
+ output_handers.each { |p| p.call(item) }
14
+ rescue => e
15
+ puts e, e.backtrace
16
+ end
17
+
18
+ def output_handers
19
+ @output_handers ||= []
20
+ end
21
+
22
+ def output_hander(&block)
23
+ output_handers << block
24
+ end
25
+
26
+ def insert(*messages)
27
+ clear_line
28
+ puts messages unless messages.empty?
29
+ yield if block_given?
30
+ ensure
31
+ Readline.refresh_line
32
+ end
33
+
34
+ def clear_line
35
+ print "\e[0G" + "\e[K"
36
+ end
37
+
38
+ def color_of(screen_name)
39
+ config[:colors][screen_name.to_i(36) % config[:colors].size]
40
+ end
41
+ end
42
+
43
+ init do
44
+ output_handers.clear
45
+
46
+ config[:colors] = (31..36).to_a + (91..96).to_a
47
+
48
+ output_hander do |item|
49
+ if item["text"]
50
+ misc = (item["in_reply_to_status_id"] ? " (reply to #{item["in_reply_to_status_id"]})" : "")
51
+ user_color = color_of(item["user"]["screen_name"])
52
+ text = item["text"].e.gsub(/@([0-9A-Za-z_]+)/) do |i|
53
+ c = color_of($1)
54
+ "<#{c}>#{i}</#{c}>"
55
+ end
56
+ status = "<90>[#{item["id"].to_s.e}]</90> " +
57
+ "<#{user_color}>#{item["user"]["screen_name"].e}</#{user_color}>: " +
58
+ "#{text}<90>#{misc.e}</90>"
59
+ puts status.t
60
+ end
61
+ end
62
+
63
+ output_hander do |item|
64
+ if item["delete"]
65
+ puts "[deleted] #{item["delete"]["status"]["id"]}"
66
+ end
67
+ end
68
+ end
69
+
70
+ extend Output
71
+ end
@@ -0,0 +1,11 @@
1
+ module Earthquake
2
+ module Twitter
3
+ attr_reader :twitter
4
+ end
5
+
6
+ init do
7
+ @twitter = TwitterOAuth::Client.new(config.slice(:consumer_key, :consumer_secret, :token, :secret))
8
+ end
9
+
10
+ extend Twitter
11
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: earthquake
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.2
5
+ version: 0.1.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - jugyo
@@ -153,8 +153,15 @@ files:
153
153
  - Rakefile
154
154
  - VERSION
155
155
  - bin/earthquake
156
+ - earthquake.gemspec
156
157
  - lib/earthquake.rb
158
+ - lib/earthquake/commands.rb
157
159
  - lib/earthquake/core.rb
160
+ - lib/earthquake/ext.rb
161
+ - lib/earthquake/get_access_token.rb
162
+ - lib/earthquake/input.rb
163
+ - lib/earthquake/output.rb
164
+ - lib/earthquake/twitter.rb
158
165
  - spec/earthquake_spec.rb
159
166
  - spec/spec_helper.rb
160
167
  has_rdoc: true