brutalismbot 1.8.0 → 2.0.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 +4 -4
- data/README.md +8 -8
- data/lib/brutalismbot/client.rb +3 -1
- data/lib/brutalismbot/posts/client.rb +11 -6
- data/lib/brutalismbot/reddit/post.rb +43 -100
- data/lib/brutalismbot/reddit/resource.rb +4 -2
- data/lib/brutalismbot/reddit/stub.rb +22 -7
- data/lib/brutalismbot/slack/auth.rb +5 -1
- data/lib/brutalismbot/slack/client.rb +38 -4
- data/lib/brutalismbot/twitter/client.rb +46 -7
- data/lib/brutalismbot/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7a72ed65ec740aef21cb54256d7f8f9b9e3128a039eb217711ee02c372683bf
|
4
|
+
data.tar.gz: 75b2ebef6b98ec94b716fe00cb1f657b386e9d9c40d89211b37443c8f0d98fb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80fb1a88e38e9d1fc5988d7586aebd1859339cf9c3bcd67b08cb7a587f5b0c6c0ab6c13db295f2569c19088637ade5a5e2a28c7ed7064ed57a63e5329a1747b5
|
7
|
+
data.tar.gz: eca017057b7c3a4d0c2d184ea8a328af49e319d96ac02f42e51977a65a5677e31fedd770a484c960c78e5b1f5bcc1ed834c0239ebc9bd12c7d6f320ea24bbe9b
|
data/README.md
CHANGED
@@ -16,27 +16,27 @@ gem install brutalismbot
|
|
16
16
|
## Usage
|
17
17
|
|
18
18
|
```ruby
|
19
|
-
require "brutalismbot
|
19
|
+
require "brutalismbot"
|
20
20
|
|
21
|
-
|
21
|
+
bot = Brutalismbot::Client.new
|
22
22
|
|
23
23
|
# Get latest cached post
|
24
|
-
post =
|
24
|
+
post = bot.posts.last
|
25
25
|
|
26
26
|
# Get newest posts
|
27
|
-
|
27
|
+
bot.reddit.list(:new).all
|
28
28
|
|
29
29
|
# Get new posts since latest
|
30
|
-
|
30
|
+
bot.reddit.list(:new, before: post.fullname).all
|
31
31
|
|
32
32
|
# Get current top post
|
33
|
-
|
33
|
+
bot.reddit.list(:top, limit: 1).first
|
34
34
|
|
35
35
|
# Pull latest posts
|
36
|
-
|
36
|
+
bot.pull
|
37
37
|
|
38
38
|
# Mirror a post to all clients
|
39
|
-
|
39
|
+
bot.push post
|
40
40
|
```
|
41
41
|
|
42
42
|
## Contributing
|
data/lib/brutalismbot/client.rb
CHANGED
@@ -15,7 +15,8 @@ module Brutalismbot
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def key_for(post)
|
18
|
-
|
18
|
+
path = post.created_utc.strftime("year=%Y/month=%Y-%m/day=%Y-%m-%d/%s.json")
|
19
|
+
File.join(@prefix, path)
|
19
20
|
end
|
20
21
|
|
21
22
|
def get(**options)
|
@@ -27,7 +28,7 @@ module Brutalismbot
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def list(**options)
|
30
|
-
super
|
31
|
+
super do |object|
|
31
32
|
Brutalismbot.logger.info("GET s3://#{@bucket}/#{object.key}")
|
32
33
|
Reddit::Post.parse(object.get.body.read)
|
33
34
|
end
|
@@ -54,10 +55,14 @@ module Brutalismbot
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def push(post, dryrun:nil)
|
57
|
-
|
58
|
-
Brutalismbot.logger.info("PUT #{"DRYRUN " if dryrun}s3://#{
|
59
|
-
@client.put_object(
|
60
|
-
|
58
|
+
key = key_for(post)
|
59
|
+
Brutalismbot.logger.info("PUT #{"DRYRUN " if dryrun}s3://#{@bucket}/#{key}")
|
60
|
+
@client.put_object(bucket: @bucket, key: key, body: post.to_json) unless dryrun
|
61
|
+
{
|
62
|
+
bucket: @bucket,
|
63
|
+
key: key,
|
64
|
+
url: post.permalink,
|
65
|
+
}
|
61
66
|
end
|
62
67
|
end
|
63
68
|
end
|
@@ -39,131 +39,74 @@ module Brutalismbot
|
|
39
39
|
"#<#{self.class} #{data["permalink"]}>"
|
40
40
|
end
|
41
41
|
|
42
|
+
def is_gallery?
|
43
|
+
data["is_gallery"] || false
|
44
|
+
end
|
45
|
+
|
42
46
|
def is_self?
|
43
|
-
data["is_self"]
|
47
|
+
data["is_self"] || false
|
44
48
|
end
|
45
49
|
|
46
50
|
def kind
|
47
51
|
@item["kind"]
|
48
52
|
end
|
49
53
|
|
50
|
-
def
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
def media_url
|
55
|
-
# Use URL if it's an image
|
56
|
-
if mime_type.start_with?("image/")
|
57
|
-
data["url"]
|
58
|
-
|
59
|
-
# Extract preview image URL
|
60
|
-
else
|
61
|
-
images = data.dig("preview", "images") || {}
|
62
|
-
source = images.map{|x| x["source"] }.compact.max do |a,b|
|
63
|
-
a.slice("width", "height").values <=> b.slice("width", "height").values
|
64
|
-
end
|
65
|
-
CGI.unescape_html(source["url"])
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def mime_type
|
70
|
-
@mime_type ||= begin
|
71
|
-
uri = URI.parse(data["url"])
|
72
|
-
ssl = uri.scheme == "https"
|
73
|
-
Brutalismbot.logger.info("HEAD #{uri}")
|
74
|
-
Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) do |http|
|
75
|
-
req = Net::HTTP::Head.new(uri)
|
76
|
-
http.request(req)["Content-Type"]
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def mime_type=(value)
|
82
|
-
@mime_type = value
|
83
|
-
end
|
84
|
-
|
85
|
-
def path
|
86
|
-
created_utc.strftime("year=%Y/month=%Y-%m/day=%Y-%m-%d/%s.json")
|
54
|
+
def media_metadata
|
55
|
+
data["media_metadata"]
|
87
56
|
end
|
88
57
|
|
89
58
|
def permalink
|
90
59
|
"https://reddit.com#{data["permalink"]}"
|
91
60
|
end
|
92
61
|
|
93
|
-
def
|
94
|
-
|
62
|
+
def preview_images
|
63
|
+
data.dig("preview", "images")
|
95
64
|
end
|
96
65
|
|
97
|
-
def
|
98
|
-
|
99
|
-
prefix ||= ENV["POSTS_S3_PREFIX"] || "data/v1/posts/"
|
100
|
-
{
|
101
|
-
bucket: bucket,
|
102
|
-
key: File.join(*[prefix, path].compact),
|
103
|
-
body: to_json,
|
104
|
-
}
|
66
|
+
def title
|
67
|
+
CGI.unescape_html(data["title"])
|
105
68
|
end
|
106
69
|
|
107
|
-
def
|
108
|
-
|
70
|
+
def url
|
71
|
+
data["url"]
|
109
72
|
end
|
110
73
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
74
|
+
##
|
75
|
+
# Get media URLs for post
|
76
|
+
def media_urls(&block)
|
77
|
+
if is_gallery?
|
78
|
+
media_urls_gallery(&block)
|
79
|
+
elsif preview_images
|
80
|
+
media_urls_preview(&block)
|
81
|
+
else
|
82
|
+
[]
|
83
|
+
end
|
116
84
|
end
|
117
85
|
|
118
|
-
|
119
|
-
|
86
|
+
##
|
87
|
+
# S3 path
|
88
|
+
def path
|
89
|
+
created_utc.strftime("year=%Y/month=%Y-%m/day=%Y-%m-%d/%s.json")
|
120
90
|
end
|
121
91
|
|
122
92
|
private
|
123
93
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
{
|
141
|
-
type: "mrkdwn",
|
142
|
-
text: "<#{permalink}|#{title}>",
|
143
|
-
},
|
144
|
-
],
|
145
|
-
},
|
146
|
-
],
|
147
|
-
}
|
148
|
-
end
|
149
|
-
|
150
|
-
def to_slack_text
|
151
|
-
{
|
152
|
-
blocks: [
|
153
|
-
{
|
154
|
-
type: "section",
|
155
|
-
text: {
|
156
|
-
type: "mrkdwn",
|
157
|
-
text: "<#{permalink}|#{title}>",
|
158
|
-
},
|
159
|
-
accessory: {
|
160
|
-
type: "image",
|
161
|
-
image_url: "https://brutalismbot.com/logo-red-ppl.png",
|
162
|
-
alt_text: "/r/brutalism",
|
163
|
-
},
|
164
|
-
},
|
165
|
-
],
|
166
|
-
}
|
94
|
+
##
|
95
|
+
# Get media URLs from gallery
|
96
|
+
def media_urls_gallery(&block)
|
97
|
+
media_metadata.values.map do |image|
|
98
|
+
url = block_given? ? yield(image) : image.dig("s", "u")
|
99
|
+
CGI.unescape_html(url) unless url.nil?
|
100
|
+
end.compact
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Get media URLs from previews
|
105
|
+
def media_urls_preview(&block)
|
106
|
+
preview_images.map do |image|
|
107
|
+
url = block_given? ? yield(image) : image.dig("source", "url")
|
108
|
+
CGI.unescape_html(url) unless url.nil?
|
109
|
+
end.compact
|
167
110
|
end
|
168
111
|
end
|
169
112
|
end
|
@@ -26,8 +26,10 @@ module Brutalismbot
|
|
26
26
|
children.each do |child|
|
27
27
|
item = child.transform_keys(&:to_sym)
|
28
28
|
post = Post.new(**item)
|
29
|
-
|
30
|
-
|
29
|
+
unless post.is_self?
|
30
|
+
Brutalismbot.logger.warn("NO MEDIA URLs for #{post.permalink}") if post.media_urls.empty?
|
31
|
+
yield post
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
@@ -17,6 +17,28 @@ module Brutalismbot
|
|
17
17
|
permalink: "/r/brutalism/comments/#{permalink_id}/test/",
|
18
18
|
title: "Post to /r/brutalism",
|
19
19
|
url: "https://image.host/#{image_id}.jpg",
|
20
|
+
media_metadata: {
|
21
|
+
abcdef: {
|
22
|
+
s: {
|
23
|
+
u: "https://preview.image.host/#{image_id}_1.jpg",
|
24
|
+
},
|
25
|
+
p: [
|
26
|
+
{x: 1, y: 1, u: "https://preview.image.host/#{image_id}_1.jpg"},
|
27
|
+
{x: 2, y: 2, u: "https://preview.image.host/#{image_id}_2.jpg"},
|
28
|
+
{x: 3, y: 3, u: "https://preview.image.host/#{image_id}_3.jpg"},
|
29
|
+
],
|
30
|
+
},
|
31
|
+
ghijkl: {
|
32
|
+
s: {
|
33
|
+
u: "https://preview.image.host/#{image_id}_2.jpg",
|
34
|
+
},
|
35
|
+
p: [
|
36
|
+
{x: 1, y: 1, u: "https://preview.image.host/#{image_id}_1.jpg"},
|
37
|
+
{x: 2, y: 2, u: "https://preview.image.host/#{image_id}_2.jpg"},
|
38
|
+
{x: 3, y: 3, u: "https://preview.image.host/#{image_id}_3.jpg"},
|
39
|
+
],
|
40
|
+
},
|
41
|
+
},
|
20
42
|
preview: {
|
21
43
|
images: [
|
22
44
|
{
|
@@ -26,13 +48,6 @@ module Brutalismbot
|
|
26
48
|
height: 1000,
|
27
49
|
},
|
28
50
|
},
|
29
|
-
{
|
30
|
-
source: {
|
31
|
-
url: "https://preview.image.host/#{image_id}_small.jpg",
|
32
|
-
width: 500,
|
33
|
-
height: 500,
|
34
|
-
}
|
35
|
-
}
|
36
51
|
],
|
37
52
|
},
|
38
53
|
},
|
@@ -31,20 +31,26 @@ module Brutalismbot
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def list(**options)
|
34
|
-
super
|
34
|
+
super do |object|
|
35
35
|
Brutalismbot.logger.info("GET s3://#{@bucket}/#{object.key}")
|
36
36
|
Auth.parse(object.get.body.read)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
def push(
|
40
|
+
def push(post, webhook_url, dryrun:nil)
|
41
|
+
blocks = blocks_for(post)
|
42
|
+
|
41
43
|
Brutalismbot.logger.info("POST #{"DRYRUN " if dryrun}#{webhook_url}")
|
42
44
|
unless dryrun
|
43
45
|
uri = URI.parse(webhook_url)
|
44
46
|
ssl = uri.scheme == "https"
|
45
47
|
req = Net::HTTP::Post.new(uri, "content-type" => "application/json")
|
46
|
-
req.body =
|
47
|
-
Net::HTTP.start(uri.host, uri.port, use_ssl: ssl)
|
48
|
+
req.body = { blocks: blocks }.to_json
|
49
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) do |http|
|
50
|
+
http.request(req)
|
51
|
+
end.tap do |res|
|
52
|
+
Brutalismbot.logger.error("RESPONSE [#{res.code}] #{res.body}") unless res.kind_of?(Net::HTTPSuccess)
|
53
|
+
end
|
48
54
|
else
|
49
55
|
Net::HTTPOK.new("1.1", "204", "ok")
|
50
56
|
end
|
@@ -58,6 +64,34 @@ module Brutalismbot
|
|
58
64
|
object.delete unless dryrun
|
59
65
|
end
|
60
66
|
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def blocks_for(post)
|
71
|
+
post.media_urls.map do |media_url|
|
72
|
+
[
|
73
|
+
{
|
74
|
+
type: "image",
|
75
|
+
title: {
|
76
|
+
type: "plain_text",
|
77
|
+
text: "/r/brutalism",
|
78
|
+
emoji: true,
|
79
|
+
},
|
80
|
+
image_url: media_url,
|
81
|
+
alt_text: post.title,
|
82
|
+
},
|
83
|
+
{
|
84
|
+
type: "context",
|
85
|
+
elements: [
|
86
|
+
{
|
87
|
+
type: "mrkdwn",
|
88
|
+
text: "<#{post.permalink}|#{post.title}>",
|
89
|
+
},
|
90
|
+
],
|
91
|
+
},
|
92
|
+
]
|
93
|
+
end.flatten
|
94
|
+
end
|
61
95
|
end
|
62
96
|
end
|
63
97
|
end
|
@@ -19,16 +19,55 @@ module Brutalismbot
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
def push(
|
23
|
-
|
22
|
+
def push(post, dryrun:nil)
|
23
|
+
opts = {}
|
24
|
+
slices_for(post).each_with_index.map do |slice, index|
|
25
|
+
status, media = slice
|
24
26
|
Brutalismbot.logger.info("PUSH #{"DRYRUN " if dryrun}twitter://@brutalismbot")
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
begin
|
28
|
+
res = @client.update_with_media(status, media, opts)
|
29
|
+
opts[:in_reply_to_status_id] = res.id
|
30
|
+
rescue ::Twitter::Error::BadRequest => err
|
31
|
+
if err.message =~ /Image file size must be <= \d+ bytes/
|
32
|
+
Brutalismbot.logger.warn("IMAGE TOO LARGE - RETRYING WITH PREVIEWS")
|
33
|
+
res = push_preview(post, opts, index)
|
34
|
+
opts[:in_reply_to_status_id] = res.id
|
35
|
+
end
|
36
|
+
end unless dryrun
|
37
|
+
|
38
|
+
res&.id
|
30
39
|
end
|
31
40
|
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def push_preview(post, opts, index)
|
45
|
+
status, media = slices_for(post) do |i|
|
46
|
+
i["p"].max {|a,b| a["x"] * a["y"] <=> b["x"] * b["y"] }["u"]
|
47
|
+
end.to_a[index]
|
48
|
+
@client.update_with_media(status, media, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
def slices_for(post, &block)
|
52
|
+
status = status_for(post)
|
53
|
+
media_urls = post.media_urls(&block)
|
54
|
+
case media_urls.count % 4
|
55
|
+
when 1 then media_urls.each_slice(3).to_a
|
56
|
+
when 2 then media_urls.each_slice(3).to_a
|
57
|
+
else media_urls.each_slice(4).to_a
|
58
|
+
end.map do |media_urls_slice|
|
59
|
+
media_urls_slice.map do |media_url|
|
60
|
+
Brutalismbot.logger.info("GET #{media_url}")
|
61
|
+
URI.open(media_url)
|
62
|
+
end
|
63
|
+
end.zip([status]).map(&:reverse)
|
64
|
+
end
|
65
|
+
|
66
|
+
def status_for(post)
|
67
|
+
max = 280 - post.permalink.length - 1
|
68
|
+
status = post.title.length <= max ? post.title : "#{post.title[0...max - 1]}…"
|
69
|
+
status << "\n#{post.permalink}"
|
70
|
+
end
|
32
71
|
end
|
33
72
|
end
|
34
73
|
end
|
data/lib/brutalismbot/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brutalismbot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Mancevice
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: twitter
|