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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc9aa96ddf426ba53cd94234a85ab6b3b11237dcdfb8414ca522f983a58c807c
4
- data.tar.gz: 87ccc44e65a2565e31a3e60c399ab2d6725f6594a12da582c273e9b95292020d
3
+ metadata.gz: b7a72ed65ec740aef21cb54256d7f8f9b9e3128a039eb217711ee02c372683bf
4
+ data.tar.gz: 75b2ebef6b98ec94b716fe00cb1f657b386e9d9c40d89211b37443c8f0d98fb0
5
5
  SHA512:
6
- metadata.gz: a8160fd997677a8ab632be035009b1cea925c4791cc45109c9cba086eda92408b4d931394efa92a1b438357a9784206b2479ccf191397dd66f0beeb809af7d6f
7
- data.tar.gz: bba9c3dda2a30a14209d74599a4bcd01e0cce55caa138970135fd001a0a9db79272c8acdb4d0bef6dce89970ac0cda8bbfca181d7d4febb0bcb8103e581e96f0
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/s3"
19
+ require "brutalismbot"
20
20
 
21
- brutbot = Brutalismbot::S3::Client.new bucket: "my-bucket", prefix: "my/prefix/"
21
+ bot = Brutalismbot::Client.new
22
22
 
23
23
  # Get latest cached post
24
- post = brutbot.posts.last
24
+ post = bot.posts.last
25
25
 
26
26
  # Get newest posts
27
- brutbot.subreddit.posts(:new).all
27
+ bot.reddit.list(:new).all
28
28
 
29
29
  # Get new posts since latest
30
- brutbot.subreddit.posts(:new, before: post.fullname).all
30
+ bot.reddit.list(:new, before: post.fullname).all
31
31
 
32
32
  # Get current top post
33
- brutbot.subreddit.posts(:top, limit: 1).first
33
+ bot.reddit.list(:top, limit: 1).first
34
34
 
35
35
  # Pull latest posts
36
- brutbot.posts.pull
36
+ bot.pull
37
37
 
38
38
  # Mirror a post to all clients
39
- brutbot.auths.mirror post
39
+ bot.push post
40
40
  ```
41
41
 
42
42
  ## Contributing
@@ -38,7 +38,9 @@ module Brutalismbot
38
38
  @twitter.push(post, dryrun: dryrun)
39
39
 
40
40
  # Push to Slack
41
- @slack.push(post, dryrun: dryrun)
41
+ @slack.list.map(&:webhook_url).each do |webhook_url|
42
+ @slack.push(post, webhook_url, dryrun: dryrun)
43
+ end
42
44
 
43
45
  nil
44
46
  end
@@ -15,7 +15,8 @@ module Brutalismbot
15
15
  end
16
16
 
17
17
  def key_for(post)
18
- File.join(@prefix, post.path)
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(**options) do |object|
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
- options = post.to_s3(bucket: @bucket, prefix: @prefix)
58
- Brutalismbot.logger.info("PUT #{"DRYRUN " if dryrun}s3://#{options[:bucket]}/#{options[:key]}")
59
- @client.put_object(**options) unless dryrun
60
- options.slice(:bucket, :key)
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 media_uri
51
- URI.parse(media_url)
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 title
94
- CGI.unescapeHTML(data["title"])
62
+ def preview_images
63
+ data.dig("preview", "images")
95
64
  end
96
65
 
97
- def to_s3(bucket:nil, prefix:nil)
98
- bucket ||= ENV["POSTS_S3_BUCKET"] || "brutalismbot"
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 to_slack
108
- is_self? ? to_slack_text : to_slack_image
70
+ def url
71
+ data["url"]
109
72
  end
110
73
 
111
- def to_twitter
112
- max = 280 - permalink.length - 1
113
- status = title.length <= max ? title : "#{title[0...max - 1]}…"
114
- status << "\n#{permalink}"
115
- {status: status, media_url: is_self? ? nil : media_url}
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
- def url
119
- data["url"]
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
- def to_slack_image
125
- {
126
- blocks: [
127
- {
128
- type: "image",
129
- title: {
130
- type: "plain_text",
131
- text: "/r/brutalism",
132
- emoji: true,
133
- },
134
- image_url: media_url,
135
- alt_text: title,
136
- },
137
- {
138
- type: "context",
139
- elements: [
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
- Brutalismbot.logger.warn("NO PHOTO URL for #{post.permalink}") if post.url.nil?
30
- yield post
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
  },
@@ -21,7 +21,11 @@ module Brutalismbot
21
21
  end
22
22
 
23
23
  def team_id
24
- @item.dig("team_id")
24
+ @item["team_id"] || @item.dig("team", "id")
25
+ end
26
+
27
+ def team_name
28
+ @item["team_name"] || @item.dig("team", "name")
25
29
  end
26
30
 
27
31
  def to_s3(bucket:nil, prefix:nil)
@@ -31,20 +31,26 @@ module Brutalismbot
31
31
  end
32
32
 
33
33
  def list(**options)
34
- super(**options) do |object|
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(body:, webhook_url:, dryrun:nil)
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 = body.to_json
47
- Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) {|http| http.request(req) }
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(status:, media_url:nil, dryrun:nil)
23
- if media_url.nil?
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
- @client.update(status) unless dryrun
26
- else
27
- Brutalismbot.logger.info("GET #{media_url}")
28
- Brutalismbot.logger.info("PUSH #{"DRYRUN " if dryrun}twitter://@brutalismbot")
29
- URI.open(media_url) {|media| @client.update_with_media(status, media) } unless dryrun
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
@@ -1,3 +1,3 @@
1
1
  module Brutalismbot
2
- VERSION = "1.8.0"
2
+ VERSION = "2.0.0"
3
3
  end
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: 1.8.0
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-08-17 00:00:00.000000000 Z
11
+ date: 2020-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: twitter