tweetlr 0.1.2 → 0.1.3

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/.autotest ADDED
@@ -0,0 +1,3 @@
1
+ # Include plugins
2
+ require 'autotest/fsevent'
3
+ require 'autotest/growl'
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ *.swp
2
+ *.log
3
+ pkg
4
+ *.pid
5
+ config
6
+ *.output
7
+ .rvmrc
8
+ Gemfile.lock
9
+ tweetlr.tid
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Rakefile CHANGED
@@ -1,43 +1,21 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/clean'
4
- require 'rubygems/package_task'
5
- require 'rdoc/task'
6
- require 'rake/testtask'
7
-
8
- spec = Gem::Specification.new do |s|
9
- s.name = 'tweetlr'
10
- s.version = '0.1.2'
11
- s.has_rdoc = true
12
- s.extra_rdoc_files = ['README.md', 'LICENSE']
13
- s.summary = %{tweetlr crawls twitter for a given term, extracts photos out of the collected tweets' short urls and posts the images to tumblr.}
14
- s.description = s.summary
15
- s.author = 'Sven Kraeuter'
16
- s.email = 'mail@svenkraeuter.com'
17
- s.homepage = "http://github.com/5v3n/#{s.name}"
18
- s.files = %w(LICENSE README.md Rakefile) + Dir.glob("{bin,lib}/**/*")
19
- s.require_path = "lib"
20
- s.executables = ['tweetlr']
21
- s.add_dependency('daemons')
22
- s.add_dependency('eventmachine')
23
- s.add_dependency('curb')
24
- end
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
25
3
 
26
- Gem::PackageTask.new(spec) do |p|
27
- p.gem_spec = spec
28
- p.need_tar = true
29
- p.need_zip = true
30
- end
4
+ require 'rdoc/task'
5
+ require 'rspec/core/rake_task'
31
6
 
32
7
  RDoc::Task.new do |rdoc|
33
- files =['README.md', 'LICENSE', 'lib/**/*.rb']
8
+ files = ['README.md', 'LICENSE', 'lib/**/*.rb']
34
9
  rdoc.rdoc_files.add(files)
35
- rdoc.main = "README.md" # page to start on
36
- rdoc.title = "tweetlr Docs" # <--- enter name manually!
37
- rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
10
+ rdoc.main = "README.md" # page to start on
11
+ rdoc.title = "tweetlr Docs" # <--- enter name manually!
12
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
38
13
  rdoc.options << '--line-numbers'
39
14
  end
40
15
 
41
- Rake::TestTask.new do |t|
42
- t.test_files = FileList['test/**/*.rb']
16
+ RSpec::Core::RakeTask.new do |t|
17
+ t.rspec_opts = %w(-c)
43
18
  end
19
+
20
+ task :default => :spec
21
+ task :test => :spec
data/bin/tweetlr CHANGED
@@ -4,15 +4,25 @@ require 'daemons'
4
4
  require 'eventmachine'
5
5
  require 'logger'
6
6
  require 'yaml'
7
- require_relative '../lib/tweetlr.rb'
7
+ require 'tweetlr'
8
8
 
9
9
  begin
10
10
  config_file = File.join( Dir.pwd, 'config', 'tweetlr.yml')
11
+ tid_file = File.join( Dir.pwd ,"tweetlr.tid")
12
+ start_at_tweet_id = lambda {
13
+ begin
14
+ File.open(tid_file, "r") { |io| io.gets.to_i }
15
+ rescue Errno::ENOENT => e
16
+ $stderr.puts "#{e} - we use the value from the configuration file"
17
+ nil
18
+ end
19
+ }.call
11
20
  CONFIG = YAML.load_file(config_file)
21
+ CONFIG['start_at_tweet_id'] = start_at_tweet_id || CONFIG['start_at_tweet_id'] || CONFIG['twitter_timestamp'] #check the latter for backwards compability
12
22
  TERM = CONFIG['search_term']
13
23
  USER = CONFIG['tumblr_username']
14
24
  PW = CONFIG['tumblr_password']
15
- TIMESTAMP = CONFIG['twitter_timestamp']
25
+ TIMESTAMP = start_at_tweet_id || CONFIG['start_at_tweet_id']
16
26
  UPDATE_PERIOD = CONFIG['update_period']
17
27
  LOGLEVEL = CONFIG['loglevel'] || Logger::INFO
18
28
  @tweetlr = Tweetlr.new(USER, PW, config_file, {:since_id => TIMESTAMP, :terms => TERM, :loglevel => LOGLEVEL})
@@ -37,20 +47,18 @@ Daemons.run_proc('tweetlr', :dir_mode => :script, :dir => './', :backtrace => tr
37
47
  if tumblr_post.nil? || tumblr_post[:source].nil?
38
48
  @log.warn "could not get image source: tweet: #{tweet} --- tumblr post: #{tumblr_post.inspect}"
39
49
  else
40
- #@log.debug tumblr_post
41
- #@log.debug @tweetlr.post_to_tumblr tumblr_post
42
- #puts "tumblr post: #{tumblr_post}"
50
+ @log.debug "tumblr post: #{tumblr_post}"
43
51
  res = @tweetlr.post_to_tumblr tumblr_post
44
52
  @log.warn "tumblr response: #{res.header_str} #{res.body_str}" unless res.response_code == 201
45
53
  end
54
+ # save the last received tweet id to tweetlr.tid
55
+ File.open(tid_file, "w+") { |io| io.write(tweets.last['id']) }
46
56
  end
47
57
  end
48
58
  else
49
- @log.error "twitter serach returned no response. hail the failwhale!"
59
+ @log.error "twitter search returned no response. hail the failwhale!"
50
60
  end
51
61
  @log.info "finished tweetlr crawl."
52
62
  }
53
63
  }
54
-
55
- end
56
-
64
+ end
@@ -0,0 +1,14 @@
1
+ results_per_page: 100
2
+ result_type: recent
3
+ search_term: 'cat+dog+unicorn' #find tweets containing any of these terms
4
+ start_at_tweet_id: 61847783463854082 # the tweet id to start searching at
5
+ api_endpoint_twitter: 'http://search.twitter.com/search.json'
6
+ api_endpoint_tumblr: 'http://www.tumblr.com'
7
+ tumblr_username: YOUR_TUMBLR_EMAIL
8
+ tumblr_password: YOUR_TUMBLR_PW
9
+ update_period: 300 #check for updates every 300 secs = 5 minutes
10
+ shouts: 'says' # will be concatenated after the username, before the message: @mr_x says: awesome things on a photo!
11
+ loglevel: 0 # 0: debug, 1: info (default), 2: warn, 3: error, 5: fatal
12
+ whitelist: #twitter accounts in that list will have their tweets published immediately. post from others will be saved as drafts
13
+ - whitey_mc_whitelist
14
+ - sven_kr
data/lib/tweetlr.rb CHANGED
@@ -5,9 +5,9 @@ require 'json'
5
5
 
6
6
  class Tweetlr
7
7
 
8
- VERSION = '0.1.2'
8
+ VERSION = '0.1.3'
9
9
  GENERATOR = %{tweetlr - http://github.com/5v3n/tweetlr}
10
- USER_AGENT = %{Mozilla/5.0 (compatible; tweetlr/#{VERSION};)}
10
+ USER_AGENT = %{Mozilla/5.0 (compatible; tweetlr/#{VERSION}; +http://github.com/5v3n/tweetlr/wiki)}
11
11
  LOCATION_START_INDICATOR = 'Location: '
12
12
  LOCATION_STOP_INDICATOR = "\r\n"
13
13
 
@@ -144,14 +144,20 @@ class Tweetlr
144
144
  url = image_url_tco link if link.index 't.co'
145
145
  url = image_url_lockerz link if link.index 'lockerz.com'
146
146
  url = image_url_foursquare link if link.index '4sq.com'
147
+ url = image_url_embedly link if url.nil? #just try embed.ly for anything else. could do all image url processing w/ embedly, but there's probably some kind of rate limit invovled.
147
148
  end
148
149
  url
149
150
  end
150
- #find the image's url for a foursquare link
151
- def image_url_foursquare(link_url)
151
+
152
+ #find the image's url via embed.ly
153
+ def image_url_embedly(link_url)
152
154
  response = http_get "http://api.embed.ly/1/oembed?url=#{link_url}"
153
155
  response['url'] if response
154
156
  end
157
+ #find the image's url for a foursquare link
158
+ def image_url_foursquare(link_url)
159
+ image_url_embedly link_url
160
+ end
155
161
  #find the image's url for a lockerz link
156
162
  def image_url_lockerz(link_url)
157
163
  response = http_get "http://api.plixi.com/api/tpapi.svc/json/metadatafromurl?details=false&url=#{link_url}"
@@ -239,7 +245,12 @@ class Tweetlr
239
245
  curl = Curl::Easy.new request
240
246
  curl.useragent = USER_AGENT
241
247
  curl.perform
242
- JSON.parse curl.body_str
248
+ begin
249
+ JSON.parse curl.body_str
250
+ rescue JSON::ParserError => err
251
+ @log.warn "#{err}: Could not parse response for #{request} - this is probably not a json response: #{curl.body_str}"
252
+ return nil
253
+ end
243
254
  rescue Curl::Err::ConnectionFailedError => err
244
255
  @log.error "Connection failed: #{err}"
245
256
  tries -= 1
@@ -267,6 +278,6 @@ class Tweetlr
267
278
  else
268
279
  nil
269
280
  end
270
- end
281
+ end
271
282
  end
272
283
  end
@@ -0,0 +1,2 @@
1
+ require "bundler"
2
+ Bundler.require :default, :development
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tweetlr do
4
+
5
+ config_file = File.join( Dir.pwd, 'config', 'tweetlr.yml')
6
+ config = YAML.load_file(config_file)
7
+ USER = config['tumblr_username']
8
+ PW = config['tumblr_password']
9
+ TIMESTAMP = config['twitter_timestamp']
10
+
11
+ before :each do
12
+ @credentials = {:email => USER, :password => PW}
13
+ @cookie = "tmgioct=as3u4KJr9COyJA9j4nwr6ZAn"
14
+ @searchterm = 'fail'
15
+ @twitter_response = {"from_user_id_str"=>"1915714", "profile_image_url"=>"http://a0.twimg.com/profile_images/386000279/2_normal.jpg", "created_at"=>"Sun, 17 Apr 2011 16:48:42 +0000", "from_user"=>"whitey_Mc_whIteLIst", "id_str"=>"59659561224765440", "metadata"=>{"result_type"=>"recent"}, "to_user_id"=>nil, "text"=>"Rigaer #wirsounterwegs @ Augenarzt Dr. Lierow http://instagr.am/p/DzCWn/", "id"=>59659561224765440, "from_user_id"=>1915714, "geo"=>{"type"=>"Point", "coordinates"=>[52.5182, 13.454]}, "iso_language_code"=>"de", "place"=>{"id"=>"3078869807f9dd36", "type"=>"city", "full_name"=>"Berlin, Berlin"}, "to_user_id_str"=>nil, "source"=>"&lt;a href=&quot;http://instagr.am&quot; rel=&quot;nofollow&quot;&gt;instagram&lt;/a&gt;"}
16
+ @non_whitelist_tweet = @twitter_response.merge 'from_user' => 'nonwhitelist user'
17
+ @retweet = @twitter_response.merge "text" => "bla bla RT @fgd: tueddelkram"
18
+ @new_style_retweet = @twitter_response.merge "text" => "and it scales! \u201c@moeffju: http://t.co/8gUSPKu #hktbl1 #origami success! :)\u201d"
19
+ @links = {
20
+ :instagram => "http://instagr.am/p/DzCWn/",
21
+ :twitpic => "http://twitpic.com/449o2x",
22
+ :yfrog => "http://yfrog.com/h4vlfp",
23
+ :picplz => "http://picplz.com/2hWv",
24
+ :imgly => "http://img.ly/3M1o",
25
+ :tco => 'http://t.co/MUGNayA',
26
+ :lockerz => 'http://lockerz.com/s/100269159',
27
+ :foursquare => 'http://4sq.com/mLKDdF',
28
+ :embedly => 'http://flic.kr/p/973hTv' #if no service matches, just try embedly
29
+ }
30
+ @pic_regexp = /(.*?)\.(jpg|jpeg|png|gif)$/i
31
+ @config_file = File.join( Dir.pwd, 'config', 'tweetlr.yml')
32
+ @tweetlr = Tweetlr.new(USER, PW, @config_file, {:since_id => TIMESTAMP, :terms => @searchterm, :loglevel => 4, :cookie => @cookie})
33
+ end
34
+ # it "should post to tumblr" do
35
+ # tweetlr = Tweetlr.new @credentials[:email], @credentials[:password], @cookie, nil, @searchterm, @config_file
36
+ # tumblr_post = tweetlr.generate_tumblr_photo_post @twitter_response
37
+ # tumblr_post[:date] = Time.now.to_s
38
+ # response = tweetlr.post_to_tumblr tumblr_post
39
+ # response.should be
40
+ # response.response_code.should be 201
41
+ # end
42
+ it "should search twitter for a given term" do
43
+ tweetlr = @tweetlr
44
+ response = tweetlr.search_twitter
45
+ tweets = response['results']
46
+ tweets.should be
47
+ tweets.should_not be_empty
48
+ end
49
+ it "should mark whitelist users' tweets as published" do
50
+ post = @tweetlr.generate_tumblr_photo_post @twitter_response
51
+ post[:state].should == 'published'
52
+ end
53
+ it "should mark non whitelist users' tweets as drafts" do
54
+ post = @tweetlr.generate_tumblr_photo_post @non_whitelist_tweet
55
+ post[:state].should == 'draft'
56
+ end
57
+ it "should not use retweets which would produce double blog posts" do
58
+ post = @tweetlr.generate_tumblr_photo_post @retweet
59
+ post.should_not be
60
+ end
61
+ it "should not use new style retweets which would produce double blog posts" do
62
+ post = @tweetlr.generate_tumblr_photo_post @new_style_retweet
63
+ post.should_not be
64
+ end
65
+ describe "image url processing" do
66
+ it "should find a picture's url from the supported services" do
67
+ @links.each do |key,value|
68
+ url = @tweetlr.find_image_url value
69
+ url.should be, "service #{key} not working!"
70
+ check_pic_url_extraction key if [:instagram,:picplz,:yfrog,:tco,:foursquare, :not_listed].index key
71
+ end
72
+ end
73
+ it "should not crash if embedly fallback won't find a link" do
74
+ url = @tweetlr.find_image_url "http://mopskopf"
75
+ end
76
+ end
77
+ describe "tweet api response processing" do
78
+ it "should extract links" do
79
+ tweetlr = @tweetlr
80
+ link = tweetlr.extract_link @twitter_response
81
+ link.should == @links[:instagram]
82
+ link = tweetlr.extract_link @twitter_response.merge 'text' => @links[:instagram].chop #check if it works w/o the trailing slash
83
+ link.should == @links[:instagram].chop
84
+ end
85
+ end
86
+ def check_pic_url_extraction(service)
87
+ tweetlr = @tweetlr
88
+ image_url = tweetlr.send "image_url_#{service}".to_sym, @links[service]
89
+ image_url.should =~ @pic_regexp
90
+ end
91
+
92
+ end
93
+
data/tweetlr.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "tweetlr"
3
+ s.version = "0.1.3"
4
+ s.author = "Sven Kraeuter"
5
+ s.email = "mail@svenkraeuter.com"
6
+ s.homepage = "http://github.com/5v3n/#{s.name}"
7
+ s.summary = "tweetlr crawls twitter for a given term, extracts photos out of the collected tweets' short urls and posts the images to tumblr."
8
+ s.description = s.summary
9
+
10
+ s.rubyforge_project = s.name
11
+ s.extra_rdoc_files = %w(README.md LICENSE)
12
+
13
+ s.add_dependency "daemons", "~> 1.1.3"
14
+ s.add_dependency "eventmachine", "~> 0.12.10"
15
+ s.add_dependency "curb", "~> 0.7.15"
16
+ s.add_dependency "json", "~> 1.5.1"
17
+
18
+ s.add_development_dependency "rspec", "~> 2.6.0"
19
+ s.add_development_dependency "autotest", "~> 4.4.6"
20
+ s.add_development_dependency "autotest-growl", "~> 0.2.9"
21
+ s.add_development_dependency "autotest-fsevent", "~> 0.2.5"
22
+ s.add_development_dependency "rdoc"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tweetlr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,42 +9,108 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-06-06 00:00:00.000000000 +02:00
12
+ date: 2011-06-14 00:00:00.000000000 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: daemons
17
- requirement: &2156428160 !ruby/object:Gem::Requirement
17
+ requirement: &2153694000 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
- - - ! '>='
20
+ - - ~>
21
21
  - !ruby/object:Gem::Version
22
- version: '0'
22
+ version: 1.1.3
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2156428160
25
+ version_requirements: *2153694000
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: eventmachine
28
- requirement: &2156427180 !ruby/object:Gem::Requirement
28
+ requirement: &2153693520 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
- - - ! '>='
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.12.10
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *2156427180
36
+ version_requirements: *2153693520
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: curb
39
- requirement: &2156426180 !ruby/object:Gem::Requirement
39
+ requirement: &2153693060 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 0.7.15
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *2153693060
48
+ - !ruby/object:Gem::Dependency
49
+ name: json
50
+ requirement: &2153692600 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.5.1
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2153692600
59
+ - !ruby/object:Gem::Dependency
60
+ name: rspec
61
+ requirement: &2153692140 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 2.6.0
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *2153692140
70
+ - !ruby/object:Gem::Dependency
71
+ name: autotest
72
+ requirement: &2153691680 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 4.4.6
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *2153691680
81
+ - !ruby/object:Gem::Dependency
82
+ name: autotest-growl
83
+ requirement: &2153691220 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: 0.2.9
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *2153691220
92
+ - !ruby/object:Gem::Dependency
93
+ name: autotest-fsevent
94
+ requirement: &2153713280 !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ version: 0.2.5
100
+ type: :development
101
+ prerelease: false
102
+ version_requirements: *2153713280
103
+ - !ruby/object:Gem::Dependency
104
+ name: rdoc
105
+ requirement: &2153712900 !ruby/object:Gem::Requirement
40
106
  none: false
41
107
  requirements:
42
108
  - - ! '>='
43
109
  - !ruby/object:Gem::Version
44
110
  version: '0'
45
- type: :runtime
111
+ type: :development
46
112
  prerelease: false
47
- version_requirements: *2156426180
113
+ version_requirements: *2153712900
48
114
  description: tweetlr crawls twitter for a given term, extracts photos out of the collected
49
115
  tweets' short urls and posts the images to tumblr.
50
116
  email: mail@svenkraeuter.com
@@ -55,11 +121,19 @@ extra_rdoc_files:
55
121
  - README.md
56
122
  - LICENSE
57
123
  files:
124
+ - .autotest
125
+ - .gitignore
126
+ - .rspec
127
+ - Gemfile
58
128
  - LICENSE
59
129
  - README.md
60
130
  - Rakefile
61
131
  - bin/tweetlr
132
+ - config/tweetlr.yml
62
133
  - lib/tweetlr.rb
134
+ - spec/spec_helper.rb
135
+ - spec/tweetlr_spec.rb
136
+ - tweetlr.gemspec
63
137
  has_rdoc: true
64
138
  homepage: http://github.com/5v3n/tweetlr
65
139
  licenses: []
@@ -80,10 +154,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
154
  - !ruby/object:Gem::Version
81
155
  version: '0'
82
156
  requirements: []
83
- rubyforge_project:
157
+ rubyforge_project: tweetlr
84
158
  rubygems_version: 1.6.2
85
159
  signing_key:
86
160
  specification_version: 3
87
161
  summary: tweetlr crawls twitter for a given term, extracts photos out of the collected
88
162
  tweets' short urls and posts the images to tumblr.
89
- test_files: []
163
+ test_files:
164
+ - spec/spec_helper.rb
165
+ - spec/tweetlr_spec.rb