brutalismbot 1.8.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|