feed2gram 1.2.4 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cafa61ecf677b96b07c3d83aa855d165dcbeb95c34c550b335ae605e6103734
4
- data.tar.gz: 6bb6d2b824e81d738e28cdf143e4278a0979e1a40b03d064e04370478a12328a
3
+ metadata.gz: 73e7d2df466144a3e956cc38742d783cce241a65894beaa90e7b332ee1b99ea6
4
+ data.tar.gz: 9b58a6e07f42adcae0b86969f01d84df77480e2db7831a6b98dd48ab67159ac1
5
5
  SHA512:
6
- metadata.gz: 47f0207f0c8da0e90ef63b176f5a1c9bbff08d9da467028b58a1bb4a54177d2b72ce9a8419d0b00372e53b0ae4180051cd95cb69de0f2373b5b029db55581359
7
- data.tar.gz: 98af19289a16b92af530000d32275aa5a471a70024c0fa88b7749bcb4e07046f2eafed9f041f9f35b3072af2e130c81ee403f7468a6400ea04926afc48be983b
6
+ metadata.gz: a46c7e7ca41c516c88ed3f12fee71506a5b24f280dd784d53c787cc910027bebb3b37fda1f364ad6b8dd84e9d5460ee569c4e7ff3a2eb480195b3ce6a6ddb8a4
7
+ data.tar.gz: 4a1c63c08f0ddf6693e587f2a2bd78c94e1638cd68471f54a50cd1d1df4c67ac8fbb876a402ac9b8d63e018f20ad5c415ce90b0d487bfc26b1950524c3f07054
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [1.3.0]
2
+
3
+ * Add undocumented methods for using the gem as an API. I leave figuring out its
4
+ usage as an exercise to the reader
5
+
1
6
  ## [1.2.4]
2
7
 
3
8
  * When uploads fail, output a message that includes the error code
data/example/Gemfile CHANGED
@@ -2,5 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  gem "rackup"
4
4
  gem "sinatra"
5
+ gem "standard"
5
6
 
6
7
  gem "feed2gram", path: ".."
@@ -31,6 +31,79 @@ module Feed2Gram
31
31
  }
32
32
  end
33
33
 
34
+ # Pseudo-public "API mode"
35
+ def create_single_media_container(post, config)
36
+ media = post.medias.first
37
+ Http.post("/#{config.instagram_id}/media", {
38
+ :media_type => post.media_type,
39
+ :caption => post.caption,
40
+ :access_token => config.access_token,
41
+ :cover_url => media.cover_url,
42
+ media.video? ? :video_url : :image_url => media.url
43
+ }.compact)[:id]
44
+ end
45
+
46
+ # Pseudo-public "API mode"
47
+ def create_carousel_media_container(media, config)
48
+ res = Http.post("/#{config.instagram_id}/media", {
49
+ :media_type => media.media_type,
50
+ :is_carousel_item => true,
51
+ :access_token => config.access_token,
52
+ media.video? ? :video_url : :image_url => media.url
53
+ }.compact)
54
+ res[:id]
55
+ end
56
+
57
+ # Pseudo-public "API mode"
58
+ def create_carousel_container(post, media_containers, config)
59
+ Http.post("/#{config.instagram_id}/media", {
60
+ caption: post.caption,
61
+ media_type: post.media_type,
62
+ children: media_containers.join(","),
63
+ access_token: config.access_token
64
+ })[:id]
65
+ end
66
+
67
+ # Pseudo-public "API mode"
68
+ def upload_finished?(container_id, config)
69
+ res = Http.get("/#{container_id}", {
70
+ fields: "status_code,status",
71
+ access_token: config.access_token
72
+ })
73
+
74
+ if res[:status_code] == "FINISHED"
75
+ true
76
+ elsif res[:status_code] == "IN_PROGRESS"
77
+ false
78
+ else
79
+ raise <<~MSG
80
+ Unexpected status code (#{res[:status_code]}) uploading container: #{container_id}"
81
+
82
+ API sent back this: #{res[:status]}
83
+
84
+ Error codes can be looked up here: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/error-codes/
85
+ MSG
86
+ end
87
+ end
88
+
89
+ # Pseudo-public "API mode"
90
+ def publish_container(container_id, config)
91
+ res = Http.post("/#{config.instagram_id}/media_publish", {
92
+ creation_id: container_id,
93
+ access_token: config.access_token
94
+ })
95
+ res[:id]
96
+ end
97
+
98
+ # Pseudo-public "API mode"
99
+ def get_media_permalink(media_id, config)
100
+ res = Http.get("/#{media_id}", {
101
+ fields: "permalink",
102
+ access_token: config.access_token
103
+ })
104
+ res[:permalink]
105
+ end
106
+
34
107
  private
35
108
 
36
109
  def retry_if_upload_times_out(times_remaining, post, options, &blk)
@@ -49,55 +122,35 @@ module Feed2Gram
49
122
  media = post.medias.first
50
123
 
51
124
  puts "Creating media resource for URL - #{media.url}" if options.verbose
52
- container_id = Http.post("/#{config.instagram_id}/media", {
53
- :media_type => post.media_type,
54
- :caption => post.caption,
55
- :access_token => config.access_token,
56
- :cover_url => media.cover_url,
57
- media.video? ? :video_url : :image_url => media.url
58
- }.compact)[:id]
125
+ container_id = create_single_media_container(post, config)
59
126
 
60
127
  if media.video?
61
128
  wait_for_media_to_upload!(media.url, container_id, config, options)
62
129
  end
63
130
 
64
131
  puts "Publishing media for URL - #{media.url}" if options.verbose
65
- Http.post("/#{config.instagram_id}/media_publish", {
66
- creation_id: container_id,
67
- access_token: config.access_token
68
- })
132
+ publish_container(container_id, config)
69
133
  Result.new(post: post, status: :posted)
70
134
  end
71
135
 
72
136
  def publish_carousel(post, config, options)
73
137
  media_containers = post.medias.take(10).map { |media|
74
138
  puts "Creating media resource for URL - #{media.url}" if options.verbose
75
- res = Http.post("/#{config.instagram_id}/media", {
76
- :media_type => media.media_type,
77
- :is_carousel_item => true,
78
- :access_token => config.access_token,
79
- media.video? ? :video_url : :image_url => media.url
80
- }.compact)
81
- res[:id]
139
+ create_carousel_media_container(media, config)
82
140
  }
83
- post.medias.select(&:video?).zip(media_containers).each { |media, container_id|
84
- wait_for_media_to_upload!(media.url, container_id, config, options)
141
+ post.medias.zip(media_containers).each { |media, container_id|
142
+ if media.video?
143
+ wait_for_media_to_upload!(media.url, container_id, config, options)
144
+ end
85
145
  }
86
146
 
87
147
  puts "Creating carousel media resource for post - #{post.url}" if options.verbose
88
- carousel_id = Http.post("/#{config.instagram_id}/media", {
89
- caption: post.caption,
90
- media_type: post.media_type,
91
- children: media_containers.join(","),
92
- access_token: config.access_token
93
- })[:id]
148
+ carousel_id = create_carousel_container(post, media_containers, config)
149
+
94
150
  wait_for_media_to_upload!(post.url, carousel_id, config, options)
95
151
 
96
152
  puts "Publishing carousel media for post - #{post.url}" if options.verbose
97
- Http.post("/#{config.instagram_id}/media_publish", {
98
- creation_id: carousel_id,
99
- access_token: config.access_token
100
- })
153
+ publish_container(carousel_id, config)
101
154
  Result.new(post: post, status: :posted)
102
155
  end
103
156
 
@@ -110,24 +163,16 @@ module Feed2Gram
110
163
  raise FacebookSucksAtDownloadingFilesError
111
164
  end
112
165
 
113
- res = Http.get("/#{container_id}", {
114
- fields: "status_code,status",
115
- access_token: config.access_token
116
- })
117
- puts "Upload status #{res[:status_code]} after waiting #{wait_attempts * SECONDS_PER_UPLOAD_CHECK} seconds for IG to download #{url}" if options.verbose
118
- if res[:status_code] == "FINISHED"
119
- break
120
- elsif res[:status_code] == "IN_PROGRESS"
121
- wait_attempts += 1
122
- sleep SECONDS_PER_UPLOAD_CHECK
123
- else
124
- warn <<~MSG
125
- Unexpected status code (#{res[:status_code]}) uploading: #{url}"
126
-
127
- API sent back this: #{res[:status]}
128
-
129
- Error codes can be looked up here: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/error-codes/
130
- MSG
166
+ begin
167
+ puts "Uploading after waiting #{wait_attempts * SECONDS_PER_UPLOAD_CHECK} seconds for IG to download #{url}" if options.verbose
168
+ if upload_finished?(container_id, config)
169
+ break
170
+ else
171
+ wait_attempts += 1
172
+ sleep SECONDS_PER_UPLOAD_CHECK
173
+ end
174
+ rescue => e
175
+ warn e.message
131
176
  break
132
177
  end
133
178
  end
@@ -1,10 +1,22 @@
1
1
  module Feed2Gram
2
2
  class RefreshesToken
3
3
  def refresh!(config, options)
4
+ puts "Refreshing Facebook OAuth token" if options.verbose
5
+ new_access_token = call(config)
6
+ return unless new_access_token
7
+
8
+ config.access_token = new_access_token
9
+ config.access_token_refreshed_at = Time.now.utc
10
+
11
+ puts "Updating Facebook OAuth token in: #{options.config_path}" if options.verbose
12
+ File.write(options.config_path, config.as_yaml)
13
+ end
14
+
15
+ # "API mode"
16
+ def call(config)
4
17
  return unless config.access_token_refreshed_at.nil? ||
5
18
  config.access_token_refreshed_at < Time.now - (60 * 60)
6
19
 
7
- puts "Refreshing Facebook OAuth token" if options.verbose
8
20
  data = Http.get("/oauth/access_token", {
9
21
  grant_type: "fb_exchange_token",
10
22
  client_id: config.facebook_app_id,
@@ -12,11 +24,7 @@ module Feed2Gram
12
24
  fb_exchange_token: config.access_token
13
25
  })
14
26
 
15
- config.access_token = data[:access_token]
16
- config.access_token_refreshed_at = Time.now.utc
17
-
18
- puts "Updating Facebook OAuth token in: #{options.config_path}" if options.verbose
19
- File.write(options.config_path, config.as_yaml)
27
+ data[:access_token]
20
28
  end
21
29
  end
22
30
  end
@@ -1,3 +1,3 @@
1
1
  module Feed2Gram
2
- VERSION = "1.2.4"
2
+ VERSION = "1.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: feed2gram
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.4
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-14 00:00:00.000000000 Z
11
+ date: 2024-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -77,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
77
  - !ruby/object:Gem::Version
78
78
  version: '0'
79
79
  requirements: []
80
- rubygems_version: 3.5.14
80
+ rubygems_version: 3.5.11
81
81
  signing_key:
82
82
  specification_version: 4
83
83
  summary: Reads an Atom feed and posts its entries to Instagram