brutalismbot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0575dbf2dc598354942add56d256d220f37563b8d03da3b7812fb2db2a48be16
4
+ data.tar.gz: 64fa9760fa8285dda284a88fd282371e2161bf168bcc688293962d66aee1bcd5
5
+ SHA512:
6
+ metadata.gz: 410823519a00d106d051ad76881700bb06f140460a473bee3166bb13d9c93ae4cdfcdb795dfd3ccc73d771834c0a3e7f8e34c7ce5e38a437aa0f6698e15768ac
7
+ data.tar.gz: 37ecfb424bf139740385f3f5e9b8afc25b1f2ff5eb3e4d545ea2ec7bcf4d842f32ebef38cb70240731741673c02dfa87fd89c6e9884d952ec7f13d7626977747
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Alexander Mancevice
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Brutalismbot
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/brutalismbot`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'brutalismbot'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install brutalismbot
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/brutalismbot.
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,30 @@
1
+ module Brutalismbot
2
+ module Event
3
+ class RecordCollection < Hash
4
+ include Enumerable
5
+
6
+ def each
7
+ puts "EVENT #{to_json}"
8
+ dig("Records").each{|x| yield x }
9
+ end
10
+ end
11
+
12
+ class SNS < RecordCollection
13
+ def each
14
+ super do |record|
15
+ yield JSON.parse record.dig("Sns", "Message")
16
+ end
17
+ end
18
+ end
19
+
20
+ class S3 < RecordCollection
21
+ def each
22
+ super do |record|
23
+ bucket = URI.unescape record.dig("s3", "bucket", "name")
24
+ key = URI.unescape record.dig("s3", "object", "key")
25
+ yield bucket: bucket, key: key
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ module Brutalismbot
2
+ class OAuth < Hash
3
+ def channel_id
4
+ dig "incoming_webhook", "channel_id"
5
+ end
6
+
7
+ def post(body:, dryrun:nil)
8
+ uri = URI.parse webhook_url
9
+ ssl = uri.scheme == "https"
10
+ res = Net::HTTP.start(uri.host, uri.port, use_ssl: ssl) do |http|
11
+ if dryrun
12
+ puts "POST DRYRUN #{uri}"
13
+ OpenStruct.new code: 200, body: JSON.parse(body)
14
+ else
15
+ puts "POST #{uri}"
16
+ req = Net::HTTP::Post.new uri, "content-type" => "application/json"
17
+ req.body = body
18
+ http.request req
19
+ end
20
+ end
21
+ {statusCode: res.code, body: res.body}
22
+ end
23
+
24
+ def team_id
25
+ dig "team_id"
26
+ end
27
+
28
+ def webhook_url
29
+ dig "incoming_webhook", "url"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,113 @@
1
+ module R
2
+ class Subreddit
3
+ def initialize(endpoint:nil, user_agent:nil)
4
+ @endpoint = endpoint
5
+ @user_agent = user_agent
6
+ end
7
+
8
+ def new_posts(**params)
9
+ url = File.join @endpoint, "new.json"
10
+ qry = URI.encode_www_form params
11
+ uri = URI.parse "#{url}?#{qry}"
12
+ PostCollection.new uri: uri, user_agent: @user_agent
13
+ end
14
+
15
+ def top_post(**params)
16
+ url = File.join @endpoint, "top.json"
17
+ qry = URI.encode_www_form params
18
+ uri = URI.parse "#{url}?#{qry}"
19
+ PostCollection.new(uri: uri, user_agent: @user_agent).each do |post|
20
+ break post unless post.url.nil?
21
+ end
22
+ end
23
+ end
24
+
25
+ class PostCollection
26
+ include Enumerable
27
+
28
+ def initialize(uri:, user_agent:, min_time:nil)
29
+ @uri = uri
30
+ @ssl = uri.scheme == "https"
31
+ @user_agent = user_agent
32
+ @min_time = min_time.to_i
33
+ end
34
+
35
+ def after(time)
36
+ PostCollection.new uri: @uri, user_agent: @user_agent, min_time: time
37
+ end
38
+
39
+ def each
40
+ puts "GET #{@uri}"
41
+ Net::HTTP.start(@uri.host, @uri.port, use_ssl: @ssl) do |http|
42
+ request = Net::HTTP::Get.new @uri, "user-agent" => @user_agent
43
+ response = JSON.parse http.request(request).body
44
+ children = response.dig("data", "children") || []
45
+ children.reverse.each do |child|
46
+ post = R::Brutalism::Post[child]
47
+ yield post if post.created_after @min_time
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ class Brutalism < Subreddit
54
+ def initialize(endpoint:nil, user_agent:nil)
55
+ super endpoint: endpoint || "https://www.reddit.com/r/brutalism",
56
+ user_agent: user_agent || "Brutalismbot #{Brutalismbot::VERSION}"
57
+ end
58
+
59
+ class Post < Hash
60
+ def created_after(time)
61
+ created_utc.to_i > time.to_i
62
+ end
63
+
64
+ def created_utc
65
+ Time.at(dig("data", "created_utc").to_i).utc
66
+ end
67
+
68
+ def permalink
69
+ dig "data", "permalink"
70
+ end
71
+
72
+ def title
73
+ dig "data", "title"
74
+ end
75
+
76
+ def to_slack
77
+ {
78
+ blocks: [
79
+ {
80
+ type: "image",
81
+ title: {
82
+ type: "plain_text",
83
+ text: "/r/brutalism",
84
+ emoji: true,
85
+ },
86
+ image_url: url,
87
+ alt_text: title,
88
+ },
89
+ {
90
+ type: "context",
91
+ elements: [
92
+ {
93
+ type: "mrkdwn",
94
+ text: "<https://reddit.com#{permalink}|#{title}>",
95
+ },
96
+ ],
97
+ },
98
+ ],
99
+ }
100
+ end
101
+
102
+ def url
103
+ images = dig "data", "preview", "images"
104
+ source = images.map{|x| x["source"] }.compact.max do |a,b|
105
+ a.slice("width", "height").values <=> b.slice("width", "height").values
106
+ end
107
+ CGI.unescapeHTML source.dig("url")
108
+ rescue NoMethodError
109
+ dig("data", "media_metadata")&.values&.first&.dig("s", "u")
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,125 @@
1
+ module Brutalismbot
2
+ module S3
3
+ class Client
4
+ VERSION = "v1"
5
+
6
+ def initialize(bucket:, prefix:nil)
7
+ @bucket = bucket
8
+ @prefix = prefix
9
+ end
10
+
11
+ def subreddit(endpoint:nil, user_agent:nil)
12
+ R::Brutalism.new endpoint: endpoint,
13
+ user_agent: user_agent
14
+ end
15
+
16
+ def auths
17
+ AuthCollection.new bucket: @bucket,
18
+ prefix: "#{@prefix}oauth/#{VERSION}/"
19
+ end
20
+
21
+ def posts
22
+ PostCollection.new bucket: @bucket,
23
+ prefix: "#{@prefix}posts/#{VERSION}/"
24
+ end
25
+ end
26
+
27
+ class Collection
28
+ include Enumerable
29
+
30
+ def initialize(bucket:, prefix:)
31
+ @bucket = bucket
32
+ @prefix = prefix
33
+ end
34
+
35
+ def each
36
+ puts "GET s3://#{@bucket.name}/#{@prefix}*"
37
+ @bucket.objects(prefix: @prefix).each do |object|
38
+ yield object
39
+ end
40
+ end
41
+
42
+ def put(body:, key:, dryrun:nil)
43
+ if dryrun
44
+ puts "PUT DRYRUN s3://#{@bucket.name}/#{key}"
45
+ else
46
+ puts "PUT s3://#{@bucket.name}/#{key}"
47
+ @bucket.put_object key: key, body: body
48
+ end
49
+
50
+ {bucket: @bucket.name, key: key}
51
+ end
52
+ end
53
+
54
+ class AuthCollection < Collection
55
+ def each
56
+ super do |object|
57
+ yield Brutalismbot::OAuth[JSON.parse object.get.body.read]
58
+ end
59
+ end
60
+
61
+ def delete(team_id:, dryrun:nil)
62
+ prefix = "#{@prefix}team=#{team_id}/"
63
+ puts "GET s3://#{@bucket.name}/#{prefix}*"
64
+ @bucket.objects(prefix: prefix).map do |object|
65
+ if dryrun
66
+ puts "DELETE DRYRUN s3://#{@bucket.name}/#{object.key}"
67
+ {bucket: @bucket.name, key: object.key}
68
+ else
69
+ puts "DELETE s3://#{@bucket.name}/#{object.key}"
70
+ object.delete
71
+ end
72
+ end
73
+ end
74
+
75
+ def put(auth:, dryrun:nil)
76
+ key = "#{@prefix}team=#{auth.team_id}/channel=#{auth.channel_id}/oauth.json"
77
+ super key: key, body: auth.to_json, dryrun: dryrun
78
+ end
79
+ end
80
+
81
+ class PostCollection < Collection
82
+ def each
83
+ super do |object|
84
+ yield R::Brutalism::Post[JSON.parse object.get.body.read]
85
+ end
86
+ end
87
+
88
+ def latest
89
+ R::Brutalism::Post[JSON.parse max_key.get.body.read]
90
+ end
91
+
92
+ def max_key
93
+ # Dig for max key
94
+ prefix = prefix_for Time.now.utc
95
+ puts "GET s3://#{@bucket.name}/#{prefix}*"
96
+
97
+ # Go up a level in prefix if no keys found
98
+ until (keys = @bucket.objects(prefix: prefix)).any?
99
+ prefix = prefix.split(/[^\/]+\/\z/).first
100
+ puts "GET s3://#{@bucket.name}/#{prefix}*"
101
+ end
102
+
103
+ # Return max by key
104
+ keys.max{|a,b| a.key <=> b.key }
105
+ end
106
+
107
+ def max_time
108
+ max_key.key.match(/(\d+).json\z/).to_a.last.to_i
109
+ end
110
+
111
+ def prefix_for(time)
112
+ time = Time.at(time.to_i).utc
113
+ year = time.strftime '%Y'
114
+ month = time.strftime '%Y-%m'
115
+ day = time.strftime '%Y-%m-%d'
116
+ "#{@prefix}year=#{year}/month=#{month}/day=#{day}/"
117
+ end
118
+
119
+ def put(post:, dryrun:nil)
120
+ key = "#{prefix_for post.created_utc}#{post.created_utc.to_i}.json"
121
+ super key: key, body: post.to_json, dryrun: dryrun
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,3 @@
1
+ module Brutalismbot
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ require "brutalismbot/event"
2
+ require "brutalismbot/oauth"
3
+ require "brutalismbot/r"
4
+ require "brutalismbot/s3"
5
+ require "brutalismbot/version"
6
+
7
+ module Brutalismbot
8
+ class Error < StandardError
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: brutalismbot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Mancevice
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-s3
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: 'A Slack app that mirrors posts from /r/brutalism to a #channel of your
84
+ choosing using incoming webhooks.'
85
+ email:
86
+ - smallweirdnum@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - LICENSE.txt
92
+ - README.md
93
+ - Rakefile
94
+ - lib/brutalismbot.rb
95
+ - lib/brutalismbot/event.rb
96
+ - lib/brutalismbot/oauth.rb
97
+ - lib/brutalismbot/r.rb
98
+ - lib/brutalismbot/s3.rb
99
+ - lib/brutalismbot/version.rb
100
+ homepage: https://brutalismbot.com
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.7.6.2
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Mirror posts from /r/brutalism to Slack
124
+ test_files: []