brutalismbot 1.6.2 → 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 +12 -12
- data/lib/brutalismbot/base.rb +1 -1
- data/lib/brutalismbot/client.rb +6 -9
- data/lib/brutalismbot/posts/client.rb +11 -6
- data/lib/brutalismbot/reddit/post.rb +45 -99
- data/lib/brutalismbot/reddit/resource.rb +6 -3
- data/lib/brutalismbot/reddit/stub.rb +22 -7
- data/lib/brutalismbot/slack/auth.rb +4 -16
- data/lib/brutalismbot/slack/client.rb +45 -6
- data/lib/brutalismbot/twitter/client.rb +43 -11
- data/lib/brutalismbot/version.rb +1 -1
- metadata +2 -114
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
@@ -1,9 +1,9 @@
|
|
1
1
|
<img alt="brutalismbot" src="https://brutalismbot.com/banner.png"/>
|
2
2
|
|
3
|
-
|
4
|
-
[
|
4
|
+
[](https://github.com/brutalismbot/gem/actions)
|
5
|
+
[](https://codeclimate.com/github/brutalismbot/gem/test_coverage)
|
6
|
+
[](https://codeclimate.com/github/brutalismbot/gem/maintainability)
|
7
7
|
|
8
8
|
Brutalismbot RubyGem
|
9
9
|
|
@@ -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/base.rb
CHANGED
data/lib/brutalismbot/client.rb
CHANGED
@@ -14,16 +14,11 @@ module Brutalismbot
|
|
14
14
|
@twitter = twitter || Twitter::Client.new
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
lag = ENV["BRUTALISMBOT_LAG_TIME"].to_s
|
19
|
-
lag.empty? ? 9000 : lag.to_i
|
20
|
-
end
|
21
|
-
|
22
|
-
def pull(limit:nil, min_time:nil, max_time:nil, lag:nil, dryrun:nil)
|
17
|
+
def pull(limit:nil, min_time:nil, max_time:nil, min_age:nil, dryrun:nil)
|
23
18
|
# Get time window for new posts
|
24
|
-
|
19
|
+
min_age ||= 9000
|
25
20
|
min_time ||= @posts.max_time
|
26
|
-
max_time ||= Time.now.utc.to_i -
|
21
|
+
max_time ||= Time.now.utc.to_i - min_age.to_i
|
27
22
|
|
28
23
|
# Get posts
|
29
24
|
opts = {q:"self:no AND nsfw:no", restrict_sr: true, sort: "new"}
|
@@ -43,7 +38,9 @@ module Brutalismbot
|
|
43
38
|
@twitter.push(post, dryrun: dryrun)
|
44
39
|
|
45
40
|
# Push to Slack
|
46
|
-
@slack.
|
41
|
+
@slack.list.map(&:webhook_url).each do |webhook_url|
|
42
|
+
@slack.push(post, webhook_url, dryrun: dryrun)
|
43
|
+
end
|
47
44
|
|
48
45
|
nil
|
49
46
|
end
|
@@ -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,128 +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
|
-
|
54
|
+
def media_metadata
|
55
|
+
data["media_metadata"]
|
52
56
|
end
|
53
57
|
|
54
|
-
def
|
55
|
-
#
|
56
|
-
|
57
|
-
data["url"]
|
58
|
+
def permalink
|
59
|
+
"https://reddit.com#{data["permalink"]}"
|
60
|
+
end
|
58
61
|
|
59
|
-
|
60
|
-
|
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
|
62
|
+
def preview_images
|
63
|
+
data.dig("preview", "images")
|
67
64
|
end
|
68
65
|
|
69
|
-
def
|
70
|
-
|
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
|
66
|
+
def title
|
67
|
+
CGI.unescape_html(data["title"])
|
79
68
|
end
|
80
69
|
|
81
|
-
def
|
82
|
-
|
70
|
+
def url
|
71
|
+
data["url"]
|
83
72
|
end
|
84
73
|
|
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
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# S3 path
|
85
88
|
def path
|
86
89
|
created_utc.strftime("year=%Y/month=%Y-%m/day=%Y-%m-%d/%s.json")
|
87
90
|
end
|
88
91
|
|
89
|
-
|
90
|
-
"https://reddit.com#{data["permalink"]}"
|
91
|
-
end
|
92
|
+
private
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
{
|
101
|
-
bucket: bucket,
|
102
|
-
key: File.join(*[prefix, path].compact),
|
103
|
-
body: to_json,
|
104
|
-
}
|
105
|
-
end
|
106
|
-
|
107
|
-
def to_slack
|
108
|
-
is_self? ? to_slack_text : to_slack_image
|
109
|
-
end
|
110
|
-
|
111
|
-
def to_slack_image
|
112
|
-
{
|
113
|
-
blocks: [
|
114
|
-
{
|
115
|
-
type: "image",
|
116
|
-
title: {
|
117
|
-
type: "plain_text",
|
118
|
-
text: "/r/brutalism",
|
119
|
-
emoji: true,
|
120
|
-
},
|
121
|
-
image_url: media_url,
|
122
|
-
alt_text: title,
|
123
|
-
},
|
124
|
-
{
|
125
|
-
type: "context",
|
126
|
-
elements: [
|
127
|
-
{
|
128
|
-
type: "mrkdwn",
|
129
|
-
text: "<#{permalink}|#{title}>",
|
130
|
-
},
|
131
|
-
],
|
132
|
-
},
|
133
|
-
],
|
134
|
-
}
|
135
|
-
end
|
136
|
-
|
137
|
-
def to_slack_text
|
138
|
-
{
|
139
|
-
blocks: [
|
140
|
-
{
|
141
|
-
type: "section",
|
142
|
-
text: {
|
143
|
-
type: "mrkdwn",
|
144
|
-
text: "<#{permalink}|#{title}>",
|
145
|
-
},
|
146
|
-
accessory: {
|
147
|
-
type: "image",
|
148
|
-
image_url: "https://brutalismbot.com/logo-red-ppl.png",
|
149
|
-
alt_text: "/r/brutalism",
|
150
|
-
},
|
151
|
-
},
|
152
|
-
],
|
153
|
-
}
|
154
|
-
end
|
155
|
-
|
156
|
-
def to_twitter
|
157
|
-
max = 280 - permalink.length - 1
|
158
|
-
text = title.length <= max ? title : "#{title[0...max - 1]}…"
|
159
|
-
[text, permalink].join("\n")
|
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
|
160
101
|
end
|
161
102
|
|
162
|
-
|
163
|
-
|
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
|
164
110
|
end
|
165
111
|
end
|
166
112
|
end
|
@@ -24,9 +24,12 @@ module Brutalismbot
|
|
24
24
|
response = JSON.parse(http.request(request).body)
|
25
25
|
children = response.dig("data", "children") || []
|
26
26
|
children.each do |child|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
item = child.transform_keys(&:to_sym)
|
28
|
+
post = Post.new(**item)
|
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
|
30
33
|
end
|
31
34
|
end
|
32
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
|
},
|
@@ -20,24 +20,12 @@ module Brutalismbot
|
|
20
20
|
File.join("team=#{team_id}", "channel=#{channel_id}", "oauth.json")
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
uri = URI.parse(webhook_url)
|
26
|
-
Brutalismbot.logger.info("POST #{"DRYRUN " if dryrun}#{uri}")
|
27
|
-
unless dryrun
|
28
|
-
ssl = uri.scheme == "https"
|
29
|
-
req = Net::HTTP::Post.new(uri, "content-type" => "application/json")
|
30
|
-
req.body = body
|
31
|
-
Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) do |http|
|
32
|
-
http.request(req)
|
33
|
-
end
|
34
|
-
else
|
35
|
-
Net::HTTPOK.new("1.1", "204", "ok")
|
36
|
-
end
|
23
|
+
def team_id
|
24
|
+
@item["team_id"] || @item.dig("team", "id")
|
37
25
|
end
|
38
26
|
|
39
|
-
def
|
40
|
-
@item.dig("
|
27
|
+
def team_name
|
28
|
+
@item["team_name"] || @item.dig("team", "name")
|
41
29
|
end
|
42
30
|
|
43
31
|
def to_s3(bucket:nil, prefix:nil)
|
@@ -31,17 +31,28 @@ 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(post, dryrun:nil)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
def push(post, webhook_url, dryrun:nil)
|
41
|
+
blocks = blocks_for(post)
|
42
|
+
|
43
|
+
Brutalismbot.logger.info("POST #{"DRYRUN " if dryrun}#{webhook_url}")
|
44
|
+
unless dryrun
|
45
|
+
uri = URI.parse(webhook_url)
|
46
|
+
ssl = uri.scheme == "https"
|
47
|
+
req = Net::HTTP::Post.new(uri, "content-type" => "application/json")
|
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
|
54
|
+
else
|
55
|
+
Net::HTTPOK.new("1.1", "204", "ok")
|
45
56
|
end
|
46
57
|
end
|
47
58
|
|
@@ -53,6 +64,34 @@ module Brutalismbot
|
|
53
64
|
object.delete unless dryrun
|
54
65
|
end
|
55
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
|
56
95
|
end
|
57
96
|
end
|
58
97
|
end
|
@@ -20,21 +20,53 @@ module Brutalismbot
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def push(post, dryrun:nil)
|
23
|
-
|
24
|
-
|
23
|
+
opts = {}
|
24
|
+
slices_for(post).each_with_index.map do |slice, index|
|
25
|
+
status, media = slice
|
26
|
+
Brutalismbot.logger.info("PUSH #{"DRYRUN " if dryrun}twitter://@brutalismbot")
|
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
|
39
|
+
end
|
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)
|
25
49
|
end
|
26
50
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
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)
|
30
64
|
end
|
31
65
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
post.
|
36
|
-
@client.update_with_media(post.to_twitter, media)
|
37
|
-
end unless dryrun
|
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}"
|
38
70
|
end
|
39
71
|
end
|
40
72
|
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
|
@@ -24,118 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '7.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: aws-sdk-s3
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: bundler
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '2.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '2.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: dotenv
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '2.7'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '2.7'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0.13'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0.13'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rake
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '13.0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '13.0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: rspec
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '3.8'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '3.8'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: simplecov
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - "<"
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0.18'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - "<"
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0.18'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: webmock
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - "~>"
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '3.6'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - "~>"
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '3.6'
|
139
27
|
description: 'A Slack app that mirrors posts from /r/brutalism to a #channel of your
|
140
28
|
choosing using incoming webhooks.'
|
141
29
|
email:
|