feed2gram 0.0.2 → 0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Dockerfile +14 -0
- data/README.md +40 -2
- data/lib/feed2gram/loads_cache.rb +5 -3
- data/lib/feed2gram/loads_config.rb +3 -2
- data/lib/feed2gram/parses_options.rb +5 -1
- data/lib/feed2gram/publishes_posts.rb +7 -2
- data/lib/feed2gram/refreshes_token.rb +4 -2
- data/lib/feed2gram/updates_cache.rb +3 -2
- data/lib/feed2gram/version.rb +1 -1
- data/lib/feed2gram.rb +11 -7
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c86578cd42608fcc00119f206582ff6c2812f8ba50a92a47a8e11360a635210e
|
4
|
+
data.tar.gz: 4a322434134e5bd515b4985feb53cd03054a36d6df2f9399ec2a08e3518a3816
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 025aa21dc8973cf0cc69d9d84cf2b019cf88bdf0cf5e02db48dc7fd69a3d3a9ed1a04f3290f0cb8c4fce8761cb2a4af5c76ed363221f4b4b6b442bbb4b432fbb
|
7
|
+
data.tar.gz: 1a257665bfa5ccf815c3c97039aa16b73d4f9dae3471792828ab0e4345733dd380fe2262f8034122e35efac6ee20140d9a7cbb99765706f52d89ae2f0d2559b0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.0.3]
|
4
|
+
|
5
|
+
* Fix the `--populate-cache` so that all entries are marked `skipped`
|
6
|
+
* Include a `bin/daemon` script
|
7
|
+
* Ship a Dockerfile to build an image that can run feed2gram on a schedule
|
8
|
+
|
3
9
|
## [0.0.2] - 2023-10-29
|
4
10
|
|
5
11
|
- Initial release
|
data/Dockerfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
FROM ruby:3.2.2
|
2
|
+
|
3
|
+
LABEL org.opencontainers.image.source=https://github.com/searls/feed2gram
|
4
|
+
LABEL org.opencontainers.image.description="Reads an Atom feed and posts its entries to Instagram (basically feed2toot, but for Instagram)"
|
5
|
+
LABEL org.opencontainers.image.licenses=GPLv3
|
6
|
+
|
7
|
+
WORKDIR /srv
|
8
|
+
COPY Gemfile Gemfile.lock feed2gram.gemspec .
|
9
|
+
COPY lib/feed2gram/version.rb lib/feed2gram/
|
10
|
+
RUN bundle install
|
11
|
+
ADD . .
|
12
|
+
VOLUME /config
|
13
|
+
CMD ["--config", "/config/feed2gram.yml"]
|
14
|
+
ENTRYPOINT ["/srv/bin/daemon"]
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# feed2gram
|
2
2
|
|
3
3
|
I've joined the [POSSE](https://indieweb.org/POSSE) and publish as much as I can
|
4
4
|
to [justin.searls.co](https://justin.searls.co) and syndicate it elsewhere. I'm
|
@@ -82,12 +82,23 @@ directory. This file is used internally by feed2gram to keep track of which
|
|
82
82
|
entry URLs in the atom feed have been processed and can be ignored on the next
|
83
83
|
run.
|
84
84
|
|
85
|
+
## Docker
|
86
|
+
|
87
|
+
You can also use Docker to run this on your own automation platform like Proxmox or Kubernetes.
|
88
|
+
|
89
|
+
```
|
90
|
+
docker run --rm -it \
|
91
|
+
-v ./feed2gram.yml:/srv/feed2gram.yml \
|
92
|
+
-v ./feed2gram.cache.yml:/srv/feed2gram.cache.yml \
|
93
|
+
ghcr.io/searls/feed2gram
|
94
|
+
```
|
95
|
+
|
85
96
|
## Options
|
86
97
|
|
87
98
|
For available options, run `feed2gram --help`:
|
88
99
|
|
89
100
|
```
|
90
|
-
$
|
101
|
+
$ feed2gram --help
|
91
102
|
Usage: feed2gram [options]
|
92
103
|
--config PATH Path of feed2gram YAML configuration (default: feed2gram.yml)
|
93
104
|
--cache-path PATH Path of feed2gram's cache file to track processed entries (default: feed2gram.cache.yml)
|
@@ -147,6 +158,32 @@ See more at http://localhost:1313/
|
|
147
158
|
</entry>
|
148
159
|
```
|
149
160
|
|
161
|
+
## Running continuously with Docker
|
162
|
+
|
163
|
+
We publish a Docker image [using GitHub
|
164
|
+
actions](https://github.com/searls/feed2gram/blob/main/.github/workflows/main.yml)
|
165
|
+
tagged as `latest` for every new commit to the `main` branch, as well as with a
|
166
|
+
release tag tracking every release of the gem on
|
167
|
+
[rubygems.org](https://rubygems.org). The images are hosted [here on GitHub's
|
168
|
+
container
|
169
|
+
registry](https://github.com/searls/feed2gram/pkgs/container/feed2gram)
|
170
|
+
|
171
|
+
```
|
172
|
+
$ docker pull ghcr.io/searls/feed2gram:latest
|
173
|
+
```
|
174
|
+
|
175
|
+
To configure your container, there are just three things to know:
|
176
|
+
|
177
|
+
1. A volume containing your configuration and cache files must be mounted to `/config`
|
178
|
+
2. By default, feed2gram will run with `--config /config/feed2gram.yml`, but you can
|
179
|
+
customize this by configuring the command value as needed
|
180
|
+
3. By default, feed2gram is run as a daemon every 60 seconds, and that duration can be overridden
|
181
|
+
by setting a `SLEEP_TIME` environment variable to the number of seconds you'd like
|
182
|
+
to wait between runs
|
183
|
+
4. If you'd rather run `feed2gram` as ad hoc as opposed to via the included daemon
|
184
|
+
(presumably to handle scheduling it yourself), simply change the entrypoint to
|
185
|
+
`/srv/exe/feed2gram`
|
186
|
+
|
150
187
|
## Frequently Asked Questions
|
151
188
|
|
152
189
|
### Why didn't my post show up?
|
@@ -166,3 +203,4 @@ The submitted image with aspect ratio ('719/194',) cannot be published. Please s
|
|
166
203
|
It means your photo is too avant garde for a mainstream normie platform like
|
167
204
|
Instagram. Make sure all images' aspect ratiosa re between 4:5 and 1.91:1 or
|
168
205
|
else the post will fail.
|
206
|
+
|
@@ -6,11 +6,13 @@ module Feed2Gram
|
|
6
6
|
end
|
7
7
|
|
8
8
|
class LoadsCache
|
9
|
-
def load(
|
10
|
-
if File.exist?(cache_path)
|
11
|
-
|
9
|
+
def load(options)
|
10
|
+
if File.exist?(options.cache_path)
|
11
|
+
puts "Loading cache from: #{options.cache_path}" if options.verbose
|
12
|
+
yaml = YAML.load_file(options.cache_path, permitted_classes: [Time])
|
12
13
|
Cache.new(**yaml)
|
13
14
|
else
|
15
|
+
puts "No cache found (looked at '#{options.cache_path}'), initializing a new one" if options.verbose
|
14
16
|
Cache.new(posted: [], failed: [], skipped: [])
|
15
17
|
end
|
16
18
|
end
|
@@ -17,8 +17,9 @@ module Feed2Gram
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class LoadsConfig
|
20
|
-
def load(
|
21
|
-
|
20
|
+
def load(options)
|
21
|
+
puts "Loading config from: #{options.config_path}" if options.verbose
|
22
|
+
yaml = YAML.load_file(options.config_path, permitted_classes: [Time])
|
22
23
|
Config.new(**yaml)
|
23
24
|
end
|
24
25
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require "optparse"
|
2
2
|
|
3
3
|
module Feed2Gram
|
4
|
-
Options = Struct.new(:config_path, :cache_path, :limit, :skip_token_refresh, :populate_cache, keyword_init: true) do
|
4
|
+
Options = Struct.new(:config_path, :cache_path, :limit, :skip_token_refresh, :populate_cache, :verbose, keyword_init: true) do
|
5
5
|
undef_method :cache_path
|
6
6
|
def cache_path
|
7
7
|
@cache_path || config_path.sub(/\.yml$/, ".cache.yml")
|
@@ -36,6 +36,10 @@ module Feed2Gram
|
|
36
36
|
opts.on "--populate-cache", "Populate the cache file with any posts found in the feed WITHOUT posting them to Instagram" do
|
37
37
|
options.populate_cache = true
|
38
38
|
end
|
39
|
+
|
40
|
+
opts.on "-v", "--verbose", "Enable verbose output" do
|
41
|
+
options.verbose = true
|
42
|
+
end
|
39
43
|
end.parse!(argv)
|
40
44
|
|
41
45
|
options
|
@@ -2,13 +2,18 @@ module Feed2Gram
|
|
2
2
|
Result = Struct.new(:post, :status, keyword_init: true)
|
3
3
|
|
4
4
|
class PublishesPosts
|
5
|
-
def publish(posts, config,
|
5
|
+
def publish(posts, config, options)
|
6
|
+
post_limit = options.limit || posts.size
|
7
|
+
puts "Publishing #{post_limit} posts to Instagram" if options.verbose
|
8
|
+
|
6
9
|
# reverse to post oldest first (most Atom feeds are reverse-chronological)
|
7
|
-
posts.reverse.take(
|
10
|
+
posts.reverse.take(post_limit).map { |post|
|
8
11
|
begin
|
9
12
|
if post.images.size == 1
|
13
|
+
puts "Publishing single image post for: #{post.url}" if options.verbose
|
10
14
|
publish_single_image(post, config)
|
11
15
|
else
|
16
|
+
puts "Publishing carousel post for: #{post.url}" if options.verbose
|
12
17
|
publish_carousel(post, config)
|
13
18
|
end
|
14
19
|
rescue => e
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module Feed2Gram
|
2
2
|
class RefreshesToken
|
3
|
-
def refresh!(config,
|
3
|
+
def refresh!(config, options)
|
4
4
|
return unless config.access_token_refreshed_at.nil? ||
|
5
5
|
config.access_token_refreshed_at < Time.now - (60 * 60)
|
6
6
|
|
7
|
+
puts "Refreshing Facebook OAuth token" if options.verbose
|
7
8
|
data = Http.get("/oauth/access_token", {
|
8
9
|
grant_type: "fb_exchange_token",
|
9
10
|
client_id: config.facebook_app_id,
|
@@ -14,7 +15,8 @@ module Feed2Gram
|
|
14
15
|
config.access_token = data[:access_token]
|
15
16
|
config.access_token_refreshed_at = Time.now.utc
|
16
17
|
|
17
|
-
|
18
|
+
puts "Updating Facebook OAuth token in: #{options.config_path}" if options.verbose
|
19
|
+
File.write(options.config_path, config.as_yaml)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Feed2Gram
|
2
2
|
class UpdatesCache
|
3
|
-
def update!(cache, results,
|
3
|
+
def update!(cache, results, options)
|
4
4
|
cache.updated_at = Time.now
|
5
5
|
results.group_by { |result| result.status }
|
6
6
|
.transform_values { |results| results.map { |result| result.post.url } }
|
@@ -8,7 +8,8 @@ module Feed2Gram
|
|
8
8
|
cache[status] += urls
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
puts "Writing updated cache to: #{options.cache_path}" if options.verbose
|
12
|
+
File.write(options.cache_path, cache.as_yaml)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
data/lib/feed2gram/version.rb
CHANGED
data/lib/feed2gram.rb
CHANGED
@@ -18,16 +18,20 @@ module Feed2Gram
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def self.run(options)
|
21
|
-
config = LoadsConfig.new.load(options
|
22
|
-
RefreshesToken.new.refresh!(config, options
|
21
|
+
config = LoadsConfig.new.load(options)
|
22
|
+
RefreshesToken.new.refresh!(config, options) unless options.skip_token_refresh
|
23
23
|
|
24
|
-
cache = LoadsCache.new.load(options
|
25
|
-
|
24
|
+
cache = LoadsCache.new.load(options)
|
25
|
+
puts "Loading entries from feed: #{config.feed_url}" if options.verbose
|
26
|
+
entries = ParsesEntries.new.parse(config.feed_url)
|
27
|
+
puts "Found #{entries.size} entries in feed" if options.verbose
|
28
|
+
posts = FiltersPosts.new.filter(entries, cache)
|
26
29
|
results = if options.populate_cache
|
27
|
-
|
30
|
+
puts "Populating cache, marking #{posts.size} posts as skipped" if options.verbose
|
31
|
+
posts.map { |post| Result.new(post: post, status: :skipped) }
|
28
32
|
else
|
29
|
-
PublishesPosts.new.publish(posts, config, options
|
33
|
+
PublishesPosts.new.publish(posts, config, options)
|
30
34
|
end
|
31
|
-
UpdatesCache.new.update!(cache, results, options
|
35
|
+
UpdatesCache.new.update!(cache, results, options)
|
32
36
|
end
|
33
37
|
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: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -34,6 +34,7 @@ extra_rdoc_files: []
|
|
34
34
|
files:
|
35
35
|
- ".standard.yml"
|
36
36
|
- CHANGELOG.md
|
37
|
+
- Dockerfile
|
37
38
|
- LICENSE.txt
|
38
39
|
- README.md
|
39
40
|
- Rakefile
|
@@ -72,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
73
|
- !ruby/object:Gem::Version
|
73
74
|
version: '0'
|
74
75
|
requirements: []
|
75
|
-
rubygems_version: 3.4.
|
76
|
+
rubygems_version: 3.4.17
|
76
77
|
signing_key:
|
77
78
|
specification_version: 4
|
78
79
|
summary: Reads an Atom feed and posts its entries to Instagram
|