streambot 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -12,12 +12,39 @@ to get streambot installed, you simply need to run
12
12
  == Usage
13
13
 
14
14
  require 'streambot'
15
-
16
- @auth = {:username=>'username',:password=>'password'}
15
+
16
+ @params = {"auth_type" => "oauth",
17
+ "oauth" => {"key" => "consumer key", "secret" => "consumer secret"},
18
+ "http" => {"username" => "username", "password" => "password"}
19
+ }
17
20
  @blacklist = ['mac_rt','apple_rt']
18
- bot = StreamBot::Tracker.new(@auth, @blacklist, 'apple','ipad','iphone os 4','steve jobs')
21
+ bot = StreamBot::Tracker.new(@params, @blacklist, 'apple','ipad','iphone os 4','steve jobs')
19
22
  bot.start
20
-
23
+
24
+ === Configuration
25
+
26
+ Twitter is shutting down http basic authentication with June 30th 2010. But only for the REST API. The Streaming API which the tracker is using still works with http basic authentication only.
27
+
28
+ So we still need the http basic authentication credentials for tracking the keywords after June 30th, but only for tracking and not for retweeting. So I introduced a new way of configuration, the params attribute that you need to pass into Tracker. Actually I don't know what happens when twitter is shutting down the http basic authentication.
29
+
30
+ ==== auth_type
31
+ The auth type is the authentication mechanism you want to use for retweeting.
32
+ *oauth* or *http*
33
+
34
+ ==== oauth
35
+ *Warning!* You need to register an application of the type desktop application on http://dev.twitter.com first!
36
+
37
+ *key*:: The consumer key Twitter provides you
38
+ *secret*:: The consumer secret Twitter provides you
39
+
40
+ ==== http
41
+ *username*:: Your login username
42
+ *password*:: Your login password
43
+
44
+ I wrote this stuff into my config.yml and load the params with
45
+
46
+ require 'yaml'
47
+ @params = YAML.load_file('config.yml')
21
48
 
22
49
  == Contribution
23
50
  === Testing
@@ -42,4 +69,4 @@ You want to add a feature or you want to patch streambot?
42
69
 
43
70
  == Copyright
44
71
 
45
- Copyright (c) 2010 Sascha Wessel. See LICENSE for details.
72
+ Copyright (c) 2010 Sascha Wessel. See LICENSE for details.
data/Rakefile CHANGED
@@ -16,8 +16,10 @@ begin
16
16
 
17
17
  # Dependency for dealing with twitter streaming API
18
18
  gem.add_dependency "tweetstream",">= 1.0.4"
19
+ gem.add_dependency "oauth",">= 0.4.0"
19
20
  gem.add_development_dependency "webmock",">= 1.0.0"
20
21
  gem.add_development_dependency "rcov",">= 0.9.8"
22
+ gem.add_development_dependency "reek",">= 1.2.8"
21
23
 
22
24
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
23
25
  end
@@ -46,7 +48,7 @@ rescue LoadError
46
48
  end
47
49
 
48
50
  task :build_gem => [:test,:rdoc,:build]
49
- task :default => [:check_dependencies,:rcov,:build_gem]
51
+ task :default => [:check_dependencies,:rcov,:reek,:build_gem]
50
52
 
51
53
  require 'rake/rdoctask'
52
54
  Rake::RDocTask.new do |rdoc|
@@ -56,3 +58,9 @@ Rake::RDocTask.new do |rdoc|
56
58
  rdoc.rdoc_files.include('README*')
57
59
  rdoc.rdoc_files.include('lib/**/*.rb')
58
60
  end
61
+
62
+ require 'reek/rake/task'
63
+ Reek::Rake::Task.new do |t|
64
+ t.fail_on_error = false
65
+ t.verbose = true
66
+ end
data/lib/streambot.rb CHANGED
@@ -1,12 +1,16 @@
1
1
  require 'rubygems'
2
2
  require 'open-uri'
3
3
  require 'net/http'
4
+ require 'oauth'
4
5
  require 'tweetstream'
5
6
  require 'logger'
7
+ require 'yaml'
6
8
 
7
9
  require 'streambot/tracker'
8
10
  require 'streambot/retweet'
11
+ require 'streambot/handler'
9
12
  require 'streambot/http'
13
+ require 'streambot/oauth'
10
14
 
11
15
  module StreamBot
12
16
  LOG = Logger.new(STDOUT)
@@ -0,0 +1,16 @@
1
+ module StreamBot
2
+ # :nodoc:
3
+ class Handler
4
+ # parse an response
5
+ def parse_response(object)
6
+ LOG.debug("response is #{object}")
7
+ case object
8
+ when ::Net::HTTPUnauthorized
9
+ ::File.delete(ACCESS_TOKEN)
10
+ raise 'user revoked oauth connection'
11
+ when ::Net::HTTPOK then
12
+ object.body
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,28 +1,58 @@
1
1
  module StreamBot
2
2
  # http communication wrapper
3
- class HTTP
4
- # initialize request with basic authentication and send
5
- def post_with_auth(auth, url)
6
- uri = URI.parse(url)
7
- request = init_request(uri)
8
- request.basic_auth auth[:username], auth[:password]
9
- post_request(request, uri)
3
+ class HTTP < StreamBot::Handler
4
+ BASE_URL = "http://api.twitter.com"
5
+ # intialize http communication wrapper
6
+ def initialize(auth)
7
+ @auth = auth
8
+ end
9
+
10
+ # post data to twitter api
11
+ #
12
+ # path takes the path like
13
+ # '/statuses/update.format'
14
+ #
15
+ # the body contains all data you send to twitter
16
+ # {:status => "a new status"}
17
+ #
18
+ def post(path, body)
19
+ LOG.debug("post #{body} to \"#{path}\"")
20
+ request = init_request(path, :post)
21
+ request.body = body if !body.nil?
22
+ do_request(request)
23
+ end
24
+
25
+ # get data from twitter api
26
+ #
27
+ # path takes the path like
28
+ # '/statuses/user_timeline.format'
29
+ #
30
+ def get(path)
31
+ LOG.debug("get data form \"#{path}\"")
32
+ request = init_request(path, :get)
33
+ do_request(request)
10
34
  end
11
35
 
12
36
  private
13
- # send request
14
- def post_request(request, url)
15
- Net::HTTP.new(url.host, url.port).start do |http|
16
- LOG.debug("#{http.inspect}")
37
+ def do_request(request)
38
+ request.basic_auth @auth['username'], @auth['password']
39
+ response = Net::HTTP.new(@uri.host, @uri.port).start do |http|
17
40
  http.request(request)
18
41
  end
42
+ parse_response(response)
19
43
  end
20
44
 
21
- # initialize an Net::HTTP::Post Request
22
- def init_request(url)
23
- LOG.debug("#{self.class}#init_request(#{url})")
24
- puts url.inspect
25
- Net::HTTP::Post.new(url.path)
45
+ # initialize an Net::HTTP::GET or Net::HTTP::POST request
46
+ # depends on type param
47
+ def init_request(path, type)
48
+ @url = "#{BASE_URL}#{path}"
49
+ @uri = URI.parse(@url)
50
+ case type
51
+ when :post then
52
+ Net::HTTP::Post.new(@uri.path)
53
+ when :get then
54
+ Net::HTTP::Get.new(@uri.path)
55
+ end
26
56
  end
27
57
  end
28
58
  end
@@ -0,0 +1,76 @@
1
+ module StreamBot
2
+ # oauth communication wrapper
3
+ class OAuth < StreamBot::Handler
4
+ TWITTER_OAUTH_SPEC = {
5
+ :site => 'http://api.twitter.com',
6
+ :request_token_pat1h => '/oauth/request_token',
7
+ :access_token_path => '/oauth/access_token',
8
+ :authorize_path => '/oauth/authorize'
9
+ }
10
+
11
+ ACCESS_TOKEN = 'access_token.yml'
12
+
13
+ # :nodoc:
14
+ def initialize(auth)
15
+ @auth = auth
16
+ @oauth_consumer = ::OAuth::Consumer.new(@auth['key'], @auth['secret'], TWITTER_OAUTH_SPEC)
17
+ get_access_token
18
+ end
19
+
20
+ # post data to twitter api
21
+ #
22
+ # url takes the path like
23
+ # '/statuses/update.format'
24
+ #
25
+ # the body contains all data you send to twitter
26
+ # {:status => "a new status"}
27
+ #
28
+ def post(path, body)
29
+ LOG.debug("post #{body} to \"#{path}\"")
30
+ response = @access_token.post(path, body)
31
+ parse_response(response)
32
+ end
33
+
34
+ # get data from twitter api
35
+ #
36
+ # path takes the path like
37
+ # '/statuses/user_timeline.format'
38
+ #
39
+ def get(path)
40
+ LOG.debug("get data form \"#{path}\"")
41
+ response = @access_token.get(path)
42
+ parse_response(response)
43
+ end
44
+
45
+ private
46
+ # get the request token from oauth consumer
47
+ def get_request_token
48
+ @oauth_consumer.get_request_token
49
+ end
50
+
51
+ # get the access token
52
+ def get_access_token
53
+ # get saved access token
54
+ if ::File.exists?(ACCESS_TOKEN)
55
+ @access_token = ::YAML.load_file(ACCESS_TOKEN)
56
+ end
57
+ # if access token is nil,
58
+ # then get a new initial token
59
+ if @access_token.nil?
60
+ get_initial_token
61
+ ::File.open(ACCESS_TOKEN, 'w') do |out|
62
+ YAML.dump(@access_token, out)
63
+ end
64
+ end
65
+ end
66
+
67
+ # get the initial access token
68
+ def get_initial_token
69
+ @request_token = get_request_token
70
+ puts "Place \"#{@request_token.authorize_url}\" in your browser"
71
+ print "Enter the number they give you: "
72
+ pin = STDIN.readline.chomp
73
+ @access_token = @request_token.get_access_token(:oauth_verifier => pin)
74
+ end
75
+ end
76
+ end
@@ -1,24 +1,19 @@
1
1
  module StreamBot
2
2
  # wrapper class for dealing with twitters native retweet api
3
3
  class Retweet
4
- # api base url
5
- $BASE_URL = "http://api.twitter.com/1/"
6
-
7
4
  # intitialize method aka constructor
8
- def initialize(auth)
9
- @auth = auth
10
- @http = StreamBot::HTTP.new
5
+ def initialize(params)
6
+ auth_type = params['auth_type']
7
+ auth_params = params[auth_type]
8
+ case auth_type
9
+ when "oauth" then @handler = StreamBot::OAuth.new(auth_params)
10
+ when "http" then @handler = StreamBot::HTTP.new(auth_params)
11
+ end
11
12
  end
12
-
13
+
13
14
  # retweets the status with given id
14
15
  def retweet(id)
15
- LOG.debug("#{self.class}#retweet")
16
- res = @http.post_with_auth(@auth, "#{$BASE_URL}statuses/retweet/#{id}.json")
17
- case res
18
- when Net::HTTPSuccess then return res
19
- else
20
- res.error!
21
- end
16
+ @handler.post("/statuses/retweet/#{id}.json",nil)
22
17
  end
23
18
  end
24
19
  end
@@ -2,11 +2,12 @@ module StreamBot
2
2
  # The Tracker class that provides a start and a stop method
3
3
  class Tracker
4
4
  # the initialization method aka the constructor
5
- def initialize(auth, blacklist, *keywords)
6
- LOG.debug("#{self.class}#initialize")
7
- @stream = TweetStream::Client.new(auth[:username],auth[:password])
5
+ def initialize(params, blacklist, *keywords)
6
+ LOG.debug("Tracker#initialize")
7
+ http_auth = params['http']
8
+ @stream = TweetStream::Client.new(http_auth['username'],http_auth['password'])
8
9
  # initializing retweet
9
- @retweet = StreamBot::Retweet.new(auth) #
10
+ @retweet = StreamBot::Retweet.new(params)
10
11
  # get string with keywords comma separated keywords
11
12
  @keywords=keywords.join(',')
12
13
  # set blacklist array if not nil
@@ -15,13 +16,13 @@ module StreamBot
15
16
 
16
17
  # start the bot
17
18
  def start
18
- LOG.debug("#{self.class}#start")
19
+ LOG.debug("Tracker#start")
19
20
  # starting to track the keywords via tweetstream
20
21
  @stream.track(@keywords) do |status|
21
22
  username = status.user.screen_name
22
23
  # if status.user is NOT in blacklist then retweet it
23
24
  if !@blacklist.include?(username)
24
- LOG.debug("#{self.class}#start - retweet ##{status.id} from @#{username}")
25
+ LOG.debug("Tracker#start - retweet ##{status.id} from @#{username}")
25
26
  @retweet.retweet(status.id)
26
27
  end
27
28
  end
@@ -5,23 +5,24 @@ module StreamBot
5
5
  class TestHttp < StreamBot::BaseTest
6
6
  # setup
7
7
  def setup
8
- @http = StreamBot::HTTP.new
9
- # override auth
10
- @auth = {:username=>'test', :password=>'test'}
11
- stub_request(:post, "http://test:test@testing.com/path/to/data").to_return(:body => @auth)
8
+ super
9
+ WebMock.disable_net_connect!
10
+ @http = StreamBot::HTTP.new(@params['http'])
11
+ stub_request(:post, "http://#{@params['http']['username']}:#{@params['http']['password']}@api.twitter.com/path/to/data").to_return(:body => "response")
12
+ end
13
+
14
+ # test a post with basic authentication
15
+ def test_post
16
+ res = @http.post('/path/to/data',nil)
17
+ assert_not_nil(res)
18
+ assert_equal("response", res)
12
19
  end
13
20
 
14
21
  # nil all variables
15
22
  def teardown
16
23
  @http = nil
24
+ WebMock.allow_net_connect!
17
25
  end
18
26
 
19
- # test a post with basic authentication
20
- def test_post
21
- WebMock.disable_net_connect!
22
- res = @http.post_with_auth(@auth, 'http://testing.com/path/to/data')
23
- assert_not_nil(res)
24
- assert_equal(@auth, res.body)
25
- end
26
27
  end
27
28
  end
@@ -0,0 +1,31 @@
1
+ require 'streambot/base_test'
2
+
3
+ module StreamBot
4
+ # test for http wrapper class
5
+ class TestOAuth < StreamBot::BaseTest
6
+ # setup
7
+ def setup
8
+ super
9
+ WebMock.allow_net_connect!
10
+ @oauth = StreamBot::OAuth.new(@params['oauth'])
11
+ stub_request(:post,"http://api.twitter.com/statuses/update.json").to_return(:body=>"response")
12
+ end
13
+
14
+ def test_verify_credentials
15
+ json = @oauth.get('/account/verify_credentials.json')
16
+ assert_not_nil(json)
17
+ end
18
+
19
+ def test_post
20
+ WebMock.disable_net_connect!
21
+ json = @oauth.post('/statuses/update.json',{:status => 'test'})
22
+ assert_not_nil(json)
23
+ assert_equal("response",json)
24
+ end
25
+
26
+ # nil all variables
27
+ def teardown
28
+ @oauth = nil
29
+ end
30
+ end
31
+ end
@@ -6,17 +6,15 @@ module StreamBot
6
6
  # setup the test
7
7
  def setup
8
8
  super
9
- @retweet = StreamBot::Retweet.new(@auth)
10
- stub_request(:post, "http://#{@auth[:username]}:#{@auth[:password]}@api.twitter.com/1/statuses/retweet/#{@id}.json").to_return(:body => 'content')
9
+ @retweet = StreamBot::Retweet.new(@params)
10
+ if @params['auth_type'] == "oauth"
11
+ stub_request(:post, "http://api.twitter.com/statuses/retweet/12355936428.json").to_return(:body => "response")
12
+ elsif @params['auth_type'] == "http"
13
+ stub_request(:post, "http://#{@params['http']['username']}:#{@params['http']['password']}@api.twitter.com/statuses/retweet/12355936428.json").to_return(:body => "response")
14
+ end
11
15
  end
12
16
 
13
- # teardown the test
14
- # nil all instance variables
15
- def teardown
16
- @id, @retweet = nil
17
- end
18
-
19
- # retweet
17
+ # retweet
20
18
  def test_retweet
21
19
  # no permission to access web!
22
20
  WebMock.disable_net_connect!
@@ -24,7 +22,13 @@ module StreamBot
24
22
  response = @retweet.retweet(@id)
25
23
  # assertion
26
24
  assert_not_nil(response)
27
- assert_equal('content', response.body)
25
+ assert_equal("response", response)
26
+ end
27
+
28
+ # teardown the test
29
+ # nil all instance variables
30
+ def teardown
31
+ @id, @retweet = nil
28
32
  end
29
33
  end
30
34
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: streambot
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 15
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
- - 3
8
+ - 4
8
9
  - 0
9
- version: 0.3.0
10
+ version: 0.4.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - Sascha Wessel
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-05-13 00:00:00 +02:00
18
+ date: 2010-06-06 00:00:00 +02:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: tweetstream
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 31
27
30
  segments:
28
31
  - 1
29
32
  - 0
@@ -32,33 +35,69 @@ dependencies:
32
35
  type: :runtime
33
36
  version_requirements: *id001
34
37
  - !ruby/object:Gem::Dependency
35
- name: webmock
38
+ name: oauth
36
39
  prerelease: false
37
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
38
42
  requirements:
39
43
  - - ">="
40
44
  - !ruby/object:Gem::Version
45
+ hash: 15
46
+ segments:
47
+ - 0
48
+ - 4
49
+ - 0
50
+ version: 0.4.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: webmock
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 23
41
62
  segments:
42
63
  - 1
43
64
  - 0
44
65
  - 0
45
66
  version: 1.0.0
46
67
  type: :development
47
- version_requirements: *id002
68
+ version_requirements: *id003
48
69
  - !ruby/object:Gem::Dependency
49
70
  name: rcov
50
71
  prerelease: false
51
- requirement: &id003 !ruby/object:Gem::Requirement
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
52
74
  requirements:
53
75
  - - ">="
54
76
  - !ruby/object:Gem::Version
77
+ hash: 43
55
78
  segments:
56
79
  - 0
57
80
  - 9
58
81
  - 8
59
82
  version: 0.9.8
60
83
  type: :development
61
- version_requirements: *id003
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: reek
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 15
94
+ segments:
95
+ - 1
96
+ - 2
97
+ - 8
98
+ version: 1.2.8
99
+ type: :development
100
+ version_requirements: *id005
62
101
  description: a simple gem that tracks several keywords via twitter streaming api and re-publish it on twitter
63
102
  email: swessel@gr4yweb.de
64
103
  executables: []
@@ -71,53 +110,56 @@ extra_rdoc_files:
71
110
  files:
72
111
  - Rakefile
73
112
  - lib/streambot.rb
113
+ - lib/streambot/handler.rb
74
114
  - lib/streambot/http.rb
115
+ - lib/streambot/oauth.rb
75
116
  - lib/streambot/retweet.rb
76
117
  - lib/streambot/tracker.rb
77
118
  - LICENSE
78
119
  - README.rdoc
120
+ - test/streambot/base_test.rb
121
+ - test/streambot/test_oauth.rb
122
+ - test/streambot/test_retweet.rb
123
+ - test/streambot/test_http.rb
79
124
  has_rdoc: true
80
125
  homepage: http://github.com/gr4y/streambot
81
126
  licenses: []
82
127
 
83
- post_install_message: "\n\
84
- === You are upgrading from an earlier version?\n\n\
85
- Maybe you need to change your code a little bit\n\
86
- \t\n\
87
- === A simple example how to use streambot\n\n\
88
- \trequire 'streambot'\n\n\
89
- \t@auth = {:username=>'username',:password=>'password'}\n\
90
- \t@blacklist = ['mac_rt','apple_rt']\n\
91
- \tbot = StreamBot::Tracker.new(@auth, @blacklist, 'apple','ipad','iphone os 4','steve jobs')\n\
92
- \tbot.start\n\n\
93
- === streambot is an open source project\n\n\
94
- the code is available on github[http://github.com/gr4y/streambot] "
128
+ post_install_message: "=== You are upgrading from an earlier version?\n\n\
129
+ Then you maybe need to change your code.\n\
130
+ In version 0.4.0 oauth was introduced and I recommend you to read the README\n\n\
131
+ code is available on github[http://github.com/gr4y/streambot] "
95
132
  rdoc_options:
96
133
  - --charset=UTF-8
97
134
  require_paths:
98
135
  - lib
99
136
  required_ruby_version: !ruby/object:Gem::Requirement
137
+ none: false
100
138
  requirements:
101
139
  - - ">="
102
140
  - !ruby/object:Gem::Version
141
+ hash: 3
103
142
  segments:
104
143
  - 0
105
144
  version: "0"
106
145
  required_rubygems_version: !ruby/object:Gem::Requirement
146
+ none: false
107
147
  requirements:
108
148
  - - ">="
109
149
  - !ruby/object:Gem::Version
150
+ hash: 3
110
151
  segments:
111
152
  - 0
112
153
  version: "0"
113
154
  requirements: []
114
155
 
115
156
  rubyforge_project:
116
- rubygems_version: 1.3.6
157
+ rubygems_version: 1.3.7
117
158
  signing_key:
118
159
  specification_version: 3
119
160
  summary: retweeting tweets with specified keywords on twitter
120
161
  test_files:
121
162
  - test/streambot/base_test.rb
163
+ - test/streambot/test_oauth.rb
122
164
  - test/streambot/test_retweet.rb
123
165
  - test/streambot/test_http.rb