curate_tumblr 1.0.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.
Files changed (41) hide show
  1. data/.project +18 -0
  2. data/README.md +219 -0
  3. data/Rakefile +0 -0
  4. data/curate_tumblr.gemspec +12 -0
  5. data/example/kubricklove/kubricklove_config.yaml +20 -0
  6. data/example/kubricklove/links/kubricklove_links +4 -0
  7. data/example/kubricklove_follow.rb +6 -0
  8. data/example/kubricklove_reblog.rb +6 -0
  9. data/example/readme +9 -0
  10. data/lib/curate_tumblr.rb +25 -0
  11. data/lib/curate_tumblr/curator.rb +200 -0
  12. data/lib/curate_tumblr/publish/follow.rb +62 -0
  13. data/lib/curate_tumblr/publish/post.rb +21 -0
  14. data/lib/curate_tumblr/publish/reblog.rb +86 -0
  15. data/lib/curate_tumblr/render/render_follow.rb +29 -0
  16. data/lib/curate_tumblr/render/render_links.rb +132 -0
  17. data/lib/curate_tumblr/render/render_reblog.rb +36 -0
  18. data/lib/curate_tumblr/tumblr/client.rb +347 -0
  19. data/lib/curate_tumblr/tumblr/extract_links.rb +190 -0
  20. data/lib/curate_tumblr/tumblr/infos.rb +102 -0
  21. data/lib/curate_tumblr/utilities/monkey.rb +5 -0
  22. data/lib/curate_tumblr/utilities/utilities.rb +4 -0
  23. data/lib/curate_tumblr/utilities/utilities_client.rb +103 -0
  24. data/lib/curate_tumblr/utilities/utilities_file.rb +50 -0
  25. data/lib/curate_tumblr/utilities/utilities_format.rb +91 -0
  26. data/lib/curate_tumblr/utilities/utilities_validate.rb +54 -0
  27. data/lib/curate_tumblr/values.rb +22 -0
  28. data/spec/curate_tumblr/curator_spec.rb +36 -0
  29. data/spec/curate_tumblr/publish/follow_spec.rb +183 -0
  30. data/spec/curate_tumblr/publish/post_spec.rb +45 -0
  31. data/spec/curate_tumblr/publish/reblog_spec.rb +118 -0
  32. data/spec/curate_tumblr/render/render_follow_spec.rb +36 -0
  33. data/spec/curate_tumblr/render/render_reblog_spec.rb +73 -0
  34. data/spec/curate_tumblr/tumblr/client_spec.rb +69 -0
  35. data/spec/curate_tumblr/tumblr/extract_links_spec.rb +204 -0
  36. data/spec/curate_tumblr/utilities/utilities_validate_spec.rb +27 -0
  37. data/spec/factories.rb +24 -0
  38. data/spec/shared_examples.rb +2 -0
  39. data/spec/shared_values.rb +203 -0
  40. data/spec/spec_helper.rb +95 -0
  41. metadata +116 -0
@@ -0,0 +1,62 @@
1
+ module CurateTumblr
2
+ module Publish
3
+ module Follow
4
+
5
+ attr_accessor :all_tofollow_urls
6
+
7
+ def init_follow!( hash_config={} )
8
+ @all_tofollow_urls = Set.new
9
+ end
10
+
11
+ def follow_url( url )
12
+ tumblr_url = CurateTumblr::Tumblr::ExtractLinks.get_tumblr_url( url )
13
+ client_follow( tumblr_url )
14
+ end
15
+
16
+ def add_tofollow_url( url )
17
+ raise "no tumblr url" if !url
18
+ url = CurateTumblr::Tumblr::ExtractLinks.get_tumblr_url( url ) if !CurateTumblr::Tumblr::ExtractLinks.tumblr_url?( url )
19
+ return false if !url || !CurateTumblr::Tumblr::ExtractLinks.tumblr_url?( url )
20
+ tumblr_url = url.dup
21
+ CurateTumblr.format_tumblr_url!( tumblr_url )
22
+ @all_tofollow_urls << tumblr_url
23
+ @all_tofollow_urls.count
24
+ end
25
+
26
+ private
27
+
28
+ def add_tofollow_tumblr_links_from_caption( caption, source=false )
29
+ tumblr_links = CurateTumblr::Tumblr::ExtractLinks.get_tumblr_urls_from_text( caption )
30
+ tumblr_links.each { |link| add_tofollow_url( link ) if !source || source != link }
31
+ tumblr_links
32
+ end
33
+
34
+ def add_tofollow_source_from_hash_post( hash_post )
35
+ source = CurateTumblr.get_source_from_hash_post( hash_post )
36
+ return false if !source
37
+ add_tofollow_url( source )
38
+ end
39
+
40
+ def add_tofollow_from_post( tumblr_url, hash_post )
41
+ raise "no tumblr url" if !tumblr_url
42
+ if !add_tofollow_url( tumblr_url )
43
+ return_error( __method__, "can't follow tumblr url", { tumblr_url: tumblr_url, hash_post: CurateTumblr.get_summary_hash_post( hash_post ) } )
44
+ end
45
+ add_tofollow_source_from_hash_post( hash_post )
46
+ add_tofollow_link_url_from_hash_post( hash_post )
47
+ true
48
+ end
49
+
50
+ def add_tofollow_source_from_post_id( tumblr_url, post_id )
51
+ raise "no tumblr url" if !tumblr_url
52
+ add_tofollow_source_from_hash_post( get_hash_post( tumblr_url, post_id ) )
53
+ end
54
+
55
+ def add_tofollow_link_url_from_hash_post( hash_post )
56
+ link_url = CurateTumblr.get_link_url_from_hash_post( hash_post )
57
+ return false if !link_url
58
+ add_tofollow_url( link_url )
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,21 @@
1
+ module CurateTumblr
2
+ module Publish
3
+ module Post
4
+
5
+ def init_post!( hash_config={} )
6
+ end
7
+
8
+ def post_photos( photos_paths, caption='' )
9
+ caption = caption + @infos_caption
10
+ photos_paths = [photos_paths] if photos_paths.is_a? String
11
+ CurateTumblr.check_paths_ar_photos( photos_paths )
12
+ client_post_photos( photos_paths, body )
13
+ end
14
+
15
+ def post_text( title, body )
16
+ body = body + @infos_caption
17
+ client_post_text( title, body )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,86 @@
1
+ module CurateTumblr
2
+ module Publish
3
+ module Reblog
4
+
5
+ def init_reblog!( hash_config={} )
6
+ end
7
+
8
+ def reblog_and_extract( url )
9
+ return false if @is_stop
10
+ return false if !CurateTumblr::Tumblr::ExtractLinks.tumblr_url?( url )
11
+ if !CurateTumblr::Tumblr::ExtractLinks.tumblr_post_url?( url )
12
+ return reblog_and_add_tofollow_reblog_url( url ) if CurateTumblr::Tumblr::ExtractLinks.tumblr_reblog_url?( url )
13
+ return add_tofollow_url( url )
14
+ end
15
+ reblog_and_toadd_tofollow_from_post_url( url )
16
+ end
17
+
18
+ private
19
+
20
+ # --- reblog ---
21
+
22
+ def reblog_and_add_tofollow_reblog_url( reblog_url )
23
+ post_id = CurateTumblr::Tumblr::ExtractLinks.get_post_id_from_reblog_url( reblog_url )
24
+ if !CurateTumblr.post_id_valid?( post_id )
25
+ return return_error( __method__, "post_id not valid", { post_id: post_id, reblog_url: reblog_url } )
26
+ end
27
+ reblog_key = CurateTumblr::Tumblr::ExtractLinks.get_reblog_key_from_reblog_url( reblog_url )
28
+ if !CurateTumblr.reblog_key_valid?( reblog_key )
29
+ return return_error( __method__, "reblog_key not valid", { reblog_key: reblog_key, post_id: post_id, reblog_url: reblog_url } )
30
+ end
31
+ new_post_id = reblog_post_key( post_id, reblog_key )
32
+ add_tofollow_source_from_post_id( @tumblr_name, new_post_id )
33
+ new_post_id
34
+ end
35
+
36
+ def reblog_and_toadd_tofollow_from_post_url( post_url )
37
+ hash_url = CurateTumblr.get_hash_url_from_post_url( post_url )
38
+ if !CurateTumblr.hash_url_valid?( hash_url )
39
+ return return_error( __method__, "hash_url not valid", { hash_url: hash_url, post_url: post_url } )
40
+ end
41
+
42
+ reblog_and_add_tofollow_post_id( hash_url[:tumblr_url], hash_url[:post_id] )
43
+ end
44
+
45
+ def reblog_and_add_tofollow_post_id( tumblr_url, post_id )
46
+ hash_post = get_hash_post( tumblr_url, post_id )
47
+ return false if !hash_post
48
+ id = reblog_from_hash_post( hash_post )
49
+ add_tofollow_from_post( tumblr_url, hash_post )
50
+ extract_links_caption_from_post( hash_post )
51
+ id
52
+ end
53
+
54
+ def reblog_from_hash_post( hash_post )
55
+ post_id = hash_post["id"]
56
+ reblog_key = CurateTumblr.get_reblog_key_from_hash_post( hash_post )
57
+ return false if !reblog_key
58
+ reblog_post_key( post_id, reblog_key )
59
+ end
60
+
61
+ def reblog_post_key( post_id, reblog_key )
62
+ # @log_tumblr.debug "#{__method__} count=#{self.count} for #{self.max_posts}" if @is_debug
63
+ return false if !reblog_post?( post_id, reblog_key )
64
+ client_reblog( post_id, reblog_key )
65
+ end
66
+
67
+ def reblog_post?( post_id, reblog_key )
68
+ if !can_run?
69
+ @log_tumblr.debug "can't reblog #{post_id} because can't run" if @is_debug
70
+ return false
71
+ end
72
+
73
+ if !CurateTumblr.post_id_valid?( post_id )
74
+ @log_tumblr.warn "#{__method__} post_id #{post_id} is not valid"
75
+ return false
76
+ end
77
+
78
+ if !CurateTumblr.reblog_key_valid?( reblog_key )
79
+ @log_tumblr.warn "#{__method__} reblog_key #{reblog_key} is not valid for post_id #{post_id}"
80
+ return false
81
+ end
82
+ true
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,29 @@
1
+ module CurateTumblr
2
+ module Render
3
+ class RenderFollow < RenderLinks
4
+ def initialize( tumblr_name, directory='/home/tumblr' )
5
+ super( tumblr_name, directory )
6
+ @filename_links = @curator.get_filename_tofollow
7
+ end
8
+
9
+ def render_link( link, new_links, links_errors )
10
+ @curator.client_follow( link )
11
+ end
12
+
13
+ def get_max
14
+ @curator.max_followed
15
+ end
16
+
17
+ def get_count
18
+ @curator.count_followed
19
+ end
20
+
21
+ private
22
+
23
+ def after_render
24
+ super
25
+ add_info_render( "followed #{@curator.count_followed} links in #{to_s}" )
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,132 @@
1
+ module CurateTumblr
2
+ module Render
3
+ class RenderLinks
4
+ include Enumerable # links
5
+
6
+ attr_accessor :links_to_render, :filename_links, :filename_errors, :is_stop
7
+ attr_reader :info_render
8
+
9
+ class << self
10
+ def render( object_render, name, is_display_infos=true )
11
+ puts "\n#{name} begin at #{Time.now.strftime("%H:%m")} (max #{object_render.get_max})" if is_display_infos
12
+ object_render.render_links_from_file
13
+ puts "\n#{name} end at #{Time.now.strftime("%H:%m")}" if is_display_infos
14
+ puts "> #{object_render.info_render}" if is_display_infos
15
+ object_render
16
+ end
17
+ end
18
+
19
+ def initialize( tumblr_name, directory='/home/tumblr' )
20
+ @curator = Curator.new( tumblr_name, directory )
21
+ reboot!( false )
22
+ @filename_errors = @curator.get_filename_errors
23
+ end
24
+
25
+ def reboot!( is_init_curator=true )
26
+ @curator.init_tumblr! if is_init_curator
27
+ @links_to_render = Set.new
28
+ @info_render = ""
29
+ @is_stop = @curator.is_stop
30
+ end
31
+
32
+ def add_links_to_render( links )
33
+ links = links.to_a if !links.is_a?( Array ) && !links.is_a?( Set )
34
+ links = Set.new( links ) if !links.is_a? Set
35
+ links = Set.new( links.to_a.shuffle )
36
+ @links_to_render += links
37
+ end
38
+
39
+ def render_links_from_file
40
+ before_render
41
+ new_links = @links_to_render.dup
42
+ links_errors = Set.new
43
+
44
+ @links_to_render.each do |link|
45
+ break if check_if_stop
46
+ result = render_link( link.chomp, new_links, links_errors )
47
+ links_errors << link.chomp if !result
48
+ new_links.delete( link )
49
+ after_render_link( link, result )
50
+ end
51
+
52
+ @links_to_render = new_links
53
+ after_render
54
+ add_links_errors_file( links_errors )
55
+ get_count
56
+ end
57
+
58
+ def get_all_published_id
59
+ @curator.all_published_id
60
+ end
61
+
62
+ def get_count_all_requests_and_posts
63
+ @curator.count_all_requests_and_posts
64
+ end
65
+
66
+ def get_links_torender_from_file
67
+ CurateTumblr.get_ar_from_file( @filename_links )
68
+ end
69
+
70
+ def to_s
71
+ "#{@curator.state} #{@curator.tumblr_name} (#{@curator.count_all_requests_and_posts} total requests and posts)"
72
+ end
73
+
74
+ # --- methods to define in child ---
75
+
76
+ def render_link( link, new_links, links_errors )
77
+ raise "no render method"
78
+ end
79
+
80
+ def get_max
81
+ raise "no max method"
82
+ end
83
+
84
+ def get_count
85
+ raise "no count method"
86
+ end
87
+
88
+ private
89
+
90
+ def check_if_stop
91
+ @is_stop = true if get_count >= get_max
92
+ @is_stop = @curator.is_stop if !@is_stop
93
+ @is_stop
94
+ end
95
+
96
+ def after_render_link( link, result )
97
+ end
98
+
99
+ def before_render
100
+ raise "no filename for links" if !@filename_links
101
+ CurateTumblr.backup_file( @filename_links )
102
+ add_links_to_render( get_links_torender_from_file )
103
+ end
104
+
105
+ def after_render
106
+ replace_links_torender_in_file( @links_to_render )
107
+ end
108
+
109
+ def replace_links_torender_in_file( new_links )
110
+ new_links = Set.new( new_links.to_a.shuffle )
111
+ File.open( @filename_links , "w+" ) { |file| file.puts( new_links.to_a ) }
112
+ end
113
+
114
+ def add_links_errors_file( links_errors )
115
+ File.open( @filename_errors, "a" ) { |file| file << "\n" + links_errors.to_a.join("\n") }
116
+ end
117
+
118
+ def empty_file_links_reblog
119
+ File.open( @filename_links, "w+" ) { }
120
+ end
121
+
122
+ def add_links_errors_file( links_errors )
123
+ File.open( @filename_errors, "a" ) { |file| file << "\n" + links_errors.to_a.join("\n") }
124
+ end
125
+
126
+ def add_info_render( info )
127
+ @info_render = info
128
+ @curator.log_tumblr.info(info)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,36 @@
1
+ module CurateTumblr
2
+ module Render
3
+ class RenderReblog < RenderLinks
4
+
5
+ class << self
6
+
7
+ end
8
+
9
+ def initialize( tumblr_name, directory='/home/tumblr' )
10
+ super( tumblr_name, directory )
11
+ @filename_links = @curator.get_filename_links
12
+ end
13
+
14
+ def render_link( link, new_links, links_errors )
15
+ @curator.reblog_and_extract( link )
16
+ end
17
+
18
+ def get_max
19
+ @curator.max_reblogged
20
+ end
21
+
22
+ def get_count
23
+ @curator.count_rebloged
24
+ end
25
+
26
+ private
27
+
28
+ def after_render
29
+ super
30
+ @curator.add_tofollow_tofile
31
+ @curator.add_externallinks_tofile
32
+ add_info_render( "reblogged #{@curator.count_rebloged} links in #{to_s}" )
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,347 @@
1
+ module CurateTumblr
2
+ module Tumblr
3
+ module Client
4
+ HASH_CONFIG_CLIENT = "client"
5
+ HASH_CONFIG_IS_RUNNING = "is_running"
6
+ HASH_CONFIG_SLEEP_BEFORE_CLIENT = "sleep_before_client"
7
+ HASH_CONFIG_SLEEP_BEFORE_CLIENT_MIN = "sleep_before_client_min"
8
+ HASH_CONFIG_SLEEP_BEFORE_CLIENT_MAX = "sleep_before_client_max"
9
+ HASH_CONFIG_SLEEP_BEFORE_FOLLOW_MIN = "sleep_before_follow_min"
10
+ HASH_CONFIG_SLEEP_BEFORE_FOLLOW_MAX = "sleep_before_follow_max"
11
+ HASH_CONFIG_MAX_REQUESTS_AND_POSTS = "max_requests_and_posts"
12
+ HASH_CONFIG_MAX_POSTED = "max_posted"
13
+ HASH_CONFIG_MAX_REBLOGGED = "max_reblogged"
14
+ HASH_CONFIG_MAX_FOLLOWED = "max_followed"
15
+ HASH_CONFIG_CLIENT_OAUTH = "oauth"
16
+ HASH_CONFIG_CLIENT_OAUTH_CONSUMER_KEY = "consumer_key"
17
+ HASH_CONFIG_CLIENT_OAUTH_CONSUMER_SECRET = "consumer_secret"
18
+ HASH_CONFIG_CLIENT_OAUTH_TOKEN = "token"
19
+ HASH_CONFIG_CLIENT_OAUTH_TOKEN_SECRET = "token_secret"
20
+
21
+ attr_accessor :client, :sleep_before_client, :sleep_before_client_min, :sleep_before_client_max, :sleep_before_follow_min, :sleep_before_follow_max,
22
+ :max_requests_and_posts, :max_posted, :max_reblogged, :max_followed
23
+ attr_reader :all_published_id, :count_all_requests_and_posts, :count_continuous_bad_requests, :count_posted, :count_rebloged, :count_followed
24
+
25
+ class << self
26
+ def get_client_config_hash( oauth_consumer_key, oauth_consumer_secret, oauth_token, oauth_token_secret )
27
+ {
28
+ HASH_CONFIG_CLIENT => {
29
+ HASH_CONFIG_CLIENT_OAUTH => {
30
+ HASH_CONFIG_CLIENT_OAUTH_CONSUMER_KEY => oauth_consumer_key,
31
+ HASH_CONFIG_CLIENT_OAUTH_CONSUMER_SECRET => oauth_consumer_secret,
32
+ HASH_CONFIG_CLIENT_OAUTH_TOKEN => oauth_token,
33
+ HASH_CONFIG_CLIENT_OAUTH_TOKEN_SECRET => oauth_token_secret,
34
+ }
35
+ }
36
+ }
37
+ end
38
+
39
+ def get_string_yaml_from_client_config( hash_config )
40
+ hash_client_config = hash_config[HASH_CONFIG_CLIENT]
41
+ %Q{
42
+ #{HASH_CONFIG_CLIENT}:
43
+ #{HASH_CONFIG_CLIENT_OAUTH}:
44
+ #{HASH_CONFIG_CLIENT_OAUTH_CONSUMER_KEY}: "#{hash_client_config[HASH_CONFIG_CLIENT_OAUTH][HASH_CONFIG_CLIENT_OAUTH_CONSUMER_KEY]}"
45
+ #{HASH_CONFIG_CLIENT_OAUTH_CONSUMER_SECRET}: "#{hash_client_config[HASH_CONFIG_CLIENT_OAUTH][HASH_CONFIG_CLIENT_OAUTH_CONSUMER_SECRET]}"
46
+ #{HASH_CONFIG_CLIENT_OAUTH_TOKEN}: "#{hash_client_config[HASH_CONFIG_CLIENT_OAUTH][HASH_CONFIG_CLIENT_OAUTH_TOKEN]}"
47
+ #{HASH_CONFIG_CLIENT_OAUTH_TOKEN_SECRET}: "#{hash_client_config[HASH_CONFIG_CLIENT_OAUTH][HASH_CONFIG_CLIENT_OAUTH_TOKEN_SECRET]}"
48
+ }
49
+ end
50
+
51
+ def check_client_config_hash( hash_config )
52
+ hash_config.check_key( HASH_CONFIG_CLIENT )
53
+ hash_client_config = hash_config[HASH_CONFIG_CLIENT]
54
+ hash_client_config.check_key( HASH_CONFIG_CLIENT_OAUTH )
55
+ hash_client_config[HASH_CONFIG_CLIENT_OAUTH].check_key( HASH_CONFIG_CLIENT_OAUTH_CONSUMER_KEY )
56
+ hash_client_config[HASH_CONFIG_CLIENT_OAUTH].check_key( HASH_CONFIG_CLIENT_OAUTH_CONSUMER_SECRET )
57
+ hash_client_config[HASH_CONFIG_CLIENT_OAUTH].check_key( HASH_CONFIG_CLIENT_OAUTH_TOKEN )
58
+ hash_client_config[HASH_CONFIG_CLIENT_OAUTH].check_key( HASH_CONFIG_CLIENT_OAUTH_TOKEN_SECRET )
59
+ end
60
+ end
61
+
62
+ # --- config ---
63
+
64
+ def init_client!( hash_config={} )
65
+ @all_published_id = Set.new
66
+ @count_all_requests_and_posts = 0
67
+ @count_continuous_bad_requests = 0
68
+ @count_posted = 0
69
+ @count_rebloged = 0
70
+ @count_followed = 0
71
+ @sleep_before_client = false
72
+ @sleep_before_follow_min = false
73
+ @max_requests_and_posts = 50
74
+ @max_posted = 30
75
+ @max_reblogged = @max_posted
76
+ @max_followed = @max_posted
77
+ config_client( hash_config ) if !hash_config.empty?
78
+ end
79
+
80
+ def config_client( hash_config )
81
+ hash_client = hash_config[HASH_CONFIG_CLIENT]
82
+ config_oauth( hash_client[HASH_CONFIG_CLIENT_OAUTH] )
83
+ stop_it!("'#{HASH_CONFIG_IS_RUNNING}' is False in config") if hash_client.has_key?( HASH_CONFIG_IS_RUNNING ) && !hash_client[HASH_CONFIG_IS_RUNNING]
84
+ @sleep_before_client = hash_client[HASH_CONFIG_SLEEP_BEFORE_CLIENT] if hash_client.has_key?( HASH_CONFIG_SLEEP_BEFORE_CLIENT )
85
+ @sleep_before_client_min = hash_client[HASH_CONFIG_SLEEP_BEFORE_CLIENT_MIN] if hash_client.has_key?( HASH_CONFIG_SLEEP_BEFORE_CLIENT_MIN )
86
+ @sleep_before_client_max = hash_client[HASH_CONFIG_SLEEP_BEFORE_CLIENT_MAX] if hash_client.has_key?( HASH_CONFIG_SLEEP_BEFORE_CLIENT_MAX )
87
+ @sleep_before_follow_min = hash_client[HASH_CONFIG_SLEEP_BEFORE_FOLLOW_MIN] if hash_client.has_key?( HASH_CONFIG_SLEEP_BEFORE_FOLLOW_MIN )
88
+ @sleep_before_follow_max = hash_client[HASH_CONFIG_SLEEP_BEFORE_FOLLOW_MAX] if hash_client.has_key?( HASH_CONFIG_SLEEP_BEFORE_FOLLOW_MAX )
89
+ @max_requests_and_posts = hash_client[HASH_CONFIG_MAX_REQUESTS_AND_POSTS] if hash_client.has_key?( HASH_CONFIG_MAX_REQUESTS_AND_POSTS )
90
+ @max_posted = hash_client[HASH_CONFIG_MAX_POSTED] if hash_client.has_key?( HASH_CONFIG_MAX_POSTED )
91
+ @max_reblogged = hash_client[HASH_CONFIG_MAX_REBLOGGED] if hash_client.has_key?( HASH_CONFIG_MAX_REBLOGGED )
92
+ @max_followed = hash_client[HASH_CONFIG_MAX_FOLLOWED] if hash_client.has_key?( HASH_CONFIG_MAX_FOLLOWED )
93
+ end
94
+
95
+ def check_client_config
96
+ end
97
+
98
+ def config_oauth( hash_oauth )
99
+ ::Tumblr.configure do |config|
100
+ config.consumer_key = hash_oauth[HASH_CONFIG_CLIENT_OAUTH_CONSUMER_KEY]
101
+ config.consumer_secret = hash_oauth[HASH_CONFIG_CLIENT_OAUTH_CONSUMER_SECRET]
102
+ config.oauth_token = hash_oauth[HASH_CONFIG_CLIENT_OAUTH_TOKEN]
103
+ config.oauth_token_secret = hash_oauth[HASH_CONFIG_CLIENT_OAUTH_TOKEN_SECRET]
104
+ end
105
+ @client = ::Tumblr::Client.new
106
+ end
107
+
108
+ # --- request and post ---
109
+
110
+ def display_url_post( tumblr_url, post_id, is_short=false )
111
+ display_hash_post( get_hash_post(tumblr_url, post_id), is_short )
112
+ end
113
+
114
+ # tumblr_url must be xxxx.tumblr.com
115
+ # @todo check valid tumblr_url (not xxx.tumblr.compost or xxx.tumblr.com/post...)
116
+ def get_hash_post( tumblr_url, post_id )
117
+ CurateTumblr.format_tumblr_url!( tumblr_url )
118
+ tumblr_url = get_tumblr_domain if tumblr_url.empty?
119
+
120
+ hash_posts_multiple = client_get_posts( tumblr_url, post_id )
121
+ if !CurateTumblr.hash_multiple_posts_valid?( hash_posts_multiple )
122
+ log_tumblr.error "#{__method__} hash_posts_multiple #{hash_posts_multiple} are not valid for tumblr_url=#{tumblr_url} and post_id=#{post_id}"
123
+ return false
124
+ end
125
+ if hash_posts_multiple['posts'].count != 1
126
+ log_tumblr.error "#{__method__} hash_posts_multiple #{hash_posts_multiple} have not one element for tumblr_url=#{tumblr_url} and post_id=#{post_id}"
127
+ return false
128
+ end
129
+
130
+ hash_post = hash_posts_multiple['posts'][0]
131
+ if !CurateTumblr.hash_post_valid?( hash_post )
132
+ log_tumblr.error "#{__method__} hash_post #{hash_post} is not valid for tumblr_url=#{tumblr_url} and post_id=#{post_id}"
133
+ return false
134
+ end
135
+ hash_post
136
+ end
137
+
138
+ def get_post_global_options
139
+ if !@state
140
+ @state = POST_STATE_PUBLISH
141
+ log_tumblr.warn( "state was empty, so posts will be published" )
142
+ end
143
+ options = { state: @state }
144
+ options.merge!( { tags: @infos_tags } ) if !@infos_tags.empty?
145
+ options
146
+ end
147
+
148
+ def get_post_text_options( title, body )
149
+ { title: title, body: body }.merge( get_post_global_options )
150
+ end
151
+
152
+ def get_post_photos_options( photos_paths, caption='' )
153
+ { data: photos_paths, caption: caption }.merge( get_post_global_options )
154
+ end
155
+
156
+ def get_reblog_options( post_id, reblog_key )
157
+ { id: post_id, reblog_key: reblog_key, comment: @infos_caption }.merge( get_post_global_options )
158
+ end
159
+
160
+ def get_id_post_if_ok( hash_id )
161
+ return false if !status_result( hash_id )
162
+ id = CurateTumblr.get_id_from_direct_post( hash_id )
163
+ if !CurateTumblr.post_id_valid?( id )
164
+ return return_error( __method__, "id is not valid", { id: id } )
165
+ end
166
+ id
167
+ end
168
+
169
+ def sleep_before( seconds=false )
170
+ seconds = @sleep_before_client if !seconds
171
+ seconds = CurateTumblr.get_random_sleep( @sleep_before_client_min, @sleep_before_client_max ) if !seconds
172
+ sleep_print( seconds )
173
+ end
174
+
175
+ def delete_post!( id )
176
+ client_delete_post!( id )
177
+ @ar_posted_id.delete( id )
178
+ true
179
+ end
180
+
181
+ # --- tumblr api ---
182
+
183
+ def get_queue( limit='' )
184
+ @client.queue( get_tumblr_domain, {limit: limit} )
185
+ end
186
+
187
+ def client_post_text( title, body )
188
+ return false if !before_post
189
+ id = get_id_post_if_ok( @client.text( get_tumblr_domain, get_post_text_options( title, body ) ) )
190
+ after_posted( id )
191
+ end
192
+
193
+ def client_post_photos( photos_paths, caption='' )
194
+ return false if !before_post
195
+ id = get_id_post_if_ok( @client.photo( get_tumblr_domain, get_post_photos_options( photos_paths, caption ) ) )
196
+ after_posted( id )
197
+ end
198
+
199
+ def client_reblog( post_id, reblog_key )
200
+ return false if !before_reblog
201
+ id = get_id_post_if_ok( @client.reblog( get_tumblr_domain, get_reblog_options( post_id, reblog_key ) ) )
202
+ after_rebloged( id )
203
+ end
204
+
205
+ def client_follow( tumblr_url )
206
+ return false if !before_follow
207
+ result = status_result( @client.follow( tumblr_url ) )
208
+ after_followed( result )
209
+ end
210
+
211
+ def client_delete_post!( id )
212
+ return false if !before_client( false )
213
+ result = @client.delete(get_tumblr_domain, id)
214
+ after_deleted( result, id )
215
+ end
216
+
217
+ def client_get_posts( tumblr_url, post_id )
218
+ return false if !before_client( false )
219
+ result = @client.posts( tumblr_url, { :id => post_id } )
220
+ after_get_posts( result )
221
+ end
222
+
223
+ # --- manage tumblr errors ---
224
+
225
+ def status_result( hash_status, is_display=true, is_log=true )
226
+ status_ok = get_status_if_ok( hash_status )
227
+ if status_ok
228
+ @count_continuous_bad_requests = 0
229
+ return status_ok
230
+ end
231
+ return bad_request( is_display, is_log ) if CurateTumblr.hash_status_bad_request?( hash_status )
232
+ return rate_limit_exceeded( is_display, is_log ) if CurateTumblr.hash_status_rate_limit?( hash_status )
233
+ log_tumblr.error( "bad status result #{hash_status}" )
234
+ false
235
+ end
236
+
237
+ def get_status_if_ok( hash_status )
238
+ return hash_status if !hash_status.is_a? Hash
239
+ return hash_status if !hash_status.has_key?( "status" )
240
+ return true if CurateTumblr.hash_status_ok?( hash_status )
241
+ false
242
+ end
243
+
244
+ def rate_limit_exceeded( is_display=true, is_log=true )
245
+ stop_and_alert( "Tumblr Rate Limit Exceeded, better wait one hour", is_display, is_log )
246
+ false
247
+ end
248
+
249
+ def bad_request( is_display=true, is_log=true )
250
+ @count_continuous_bad_requests += 1
251
+ if @count_continuous_bad_requests >= COUNT_ALERT_IF_TOO_MUCH_BAD_REQUESTS
252
+ message = "Too much (#{@count_continuous_bad_requests}) bad requests (perhaps the Tumblr queue is full)"
253
+ stop_and_alert( message, is_display, is_log )
254
+ end
255
+ false
256
+ end
257
+
258
+ private
259
+
260
+ def sleep_print( seconds )
261
+ seconds = 1 if seconds <= 0 || !seconds
262
+ print DISPLAY_SLEEP_BEFORE if !DISPLAY_SLEEP_BEFORE.empty?
263
+ print seconds
264
+ sleep( seconds )
265
+ print DISPLAY_SLEEP_AFTER if !DISPLAY_SLEEP_AFTER.empty?
266
+ end
267
+
268
+ def before_client( is_add_count=true )
269
+ config_from_yaml
270
+ stop_it!("max (#{@count_all_requests_and_posts}) requests and posts") if @count_all_requests_and_posts >= @max_requests_and_posts
271
+ return false if !can_run?
272
+ sleep_before( @sleep_before_client )
273
+ @count_all_requests_and_posts += 1 if is_add_count
274
+ true
275
+ end
276
+
277
+ def before_post
278
+ return false if !before_client
279
+ if @count_posted >= @max_posted
280
+ log_tumblr.warn( "posted maximum (#{@max_posted})")
281
+ return false
282
+ end
283
+ true
284
+ end
285
+
286
+ def before_reblog
287
+ return false if !before_client
288
+ if @count_rebloged >= @max_reblogged
289
+ log_tumblr.warn( "reblogged maximum (#{@max_reblogged})")
290
+ return false
291
+ end
292
+ true
293
+ end
294
+
295
+ def before_follow
296
+ return false if !before_client
297
+ if @count_followed >= @max_followed
298
+ log_tumblr.warn( "followed maximum (#{@max_followed})")
299
+ return false
300
+ end
301
+ if @sleep_before_follow_min
302
+ sleep_print( CurateTumblr.get_random_sleep( @sleep_before_follow_min, @sleep_before_follow_max ) )
303
+ end
304
+ true
305
+ end
306
+
307
+ def after_published( id )
308
+ all_published_id << id
309
+ id
310
+ end
311
+
312
+ def after_posted( id )
313
+ return id if !id
314
+ @count_posted += 1
315
+ print DISPLAY_TUMBLR_POST if id && !DISPLAY_TUMBLR_POST.empty?
316
+ after_published( id )
317
+ end
318
+
319
+ def after_rebloged( id )
320
+ return id if !id
321
+ @count_rebloged += 1
322
+ print " "+@count_rebloged.to_s + DISPLAY_TUMBLR_REBLOG + " " if !DISPLAY_TUMBLR_REBLOG.empty?
323
+ after_published( id )
324
+ end
325
+
326
+ def after_followed( result )
327
+ return result if !result
328
+ @count_followed += 1
329
+ print " " + @count_followed.to_s + DISPLAY_TUMBLR_FOLLOW + " " if !DISPLAY_TUMBLR_FOLLOW.empty?
330
+ result
331
+ end
332
+
333
+ def after_deleted( result, id )
334
+ return result if !result
335
+ print " " + DISPLAY_TUMBLR_DELETE + " " if !DISPLAY_TUMBLR_DELETE.empty?
336
+ all_published_id.delete( id )
337
+ result
338
+ end
339
+
340
+ def after_get_posts( result )
341
+ return result if !result
342
+ print " " + DISPLAY_TUMBLR_GETPOSTS + " " if !DISPLAY_TUMBLR_GETPOSTS.empty?
343
+ result
344
+ end
345
+ end
346
+ end
347
+ end