slackened 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b0533a76d3e20e531fb744a74faa507a0fdedc6879f3c33e94834f13f90f107a
4
+ data.tar.gz: 76aa533b1f39ca68127328c846d2550887f9001d4a26877b2e8ec8a302202cea
5
+ SHA512:
6
+ metadata.gz: 0ad787bfc7030af7acc25c992ed8d00e6ef8a9f1481cbc5a924184340646013f885c3f98a02d34eb9132e275398a3c09aaaf5aee0b9dd066f6140f882677f8f2
7
+ data.tar.gz: 8b7aa0d123e9a9337306c13efe766bd17bcdd267ded343dc1c08a86a2fa2d3317d52fdc810218d6eb3d8bfc038961f70d8843a481c520aa7e5a1f4ea81ab6da8
checksums.yaml.gz.sig ADDED
Binary file
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slackened
4
+ # Determine whether the incoming request is fraudulent
5
+ module Authentication
6
+ class Request
7
+ attr_reader :signature, :timestamp, :body
8
+
9
+ def initialize(timestamp:, signature:, body:)
10
+ @timestamp = timestamp.to_i
11
+ @signature = signature
12
+ @body = body
13
+ end
14
+
15
+ # The signature depends on the timestamp to protect against replay attacks. While you're extracting the timestamp,
16
+ # check to make sure that the request occurred recently. In this example, we verify that the timestamp does not
17
+ # differ from local time by more than five minutes.
18
+ # https://api.slack.com/authentication/verifying-requests-from-slack
19
+ def stale?
20
+ # is it less than 5 minutes old?
21
+ five_minutes_ago = Time.now - 60 * 5
22
+
23
+ Time.at(@timestamp) > five_minutes_ago
24
+ end
25
+
26
+ # Slack creates a unique string for your app and shares it with you.
27
+ # Verify requests from Slack with confidence by verifying signatures using your signing secret.
28
+ # https://api.slack.com/authentication/verifying-requests-from-slack
29
+ def valid?
30
+ return false if stale?
31
+
32
+ sig_basestring = "v0:#{@timestamp}:#{@body}"
33
+
34
+ secret = Slackened.configuration.signing_secret
35
+
36
+ digest = OpenSSL::HMAC.hexdigest('SHA256', secret, sig_basestring)
37
+
38
+ @signature == "v0=#{digest}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Slackened
6
+ # proof of concept
7
+ module Authentication
8
+ extend Forwardable
9
+
10
+ # alphabetical
11
+ def_delegators self, :validate_request
12
+
13
+ # alphabetical
14
+ class << self
15
+ def validate_request(**kwargs)
16
+ Request.new(**kwargs).valid?
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Actions < Slackened::BlockKit::Blocks::Base
10
+ MAX_LENGTH = 25
11
+
12
+ def initialize(*elements)
13
+ raise MinimumElementsError if elements.length.zero?
14
+ raise MaximumElementsError, "#{elements.count} can't be greater than #{MAX_LENGTH}" if elements.length > MAX_LENGTH
15
+
16
+ set({
17
+ type: :actions,
18
+ elements:
19
+ })
20
+
21
+ self
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slackened
4
+ module BlockKit
5
+ # proof of concept
6
+ module Blocks
7
+ class MinimumCharactersError < StandardError; end
8
+ class MaximumCharactersError < StandardError; end
9
+
10
+ class MinimumFieldsError < StandardError; end
11
+ class MaximumFieldsError < StandardError; end
12
+
13
+ class MinimumElementsError < StandardError; end
14
+ class MaximumElementsError < StandardError; end
15
+
16
+ class MustBeString < StandardError; end
17
+
18
+ class Base
19
+ attr_accessor :block
20
+
21
+ def set(block)
22
+ @block = block
23
+ end
24
+
25
+ # recursion?
26
+ def to_h
27
+ @block.to_h do |key, value|
28
+ case value
29
+ when Array
30
+ [key, value.map(&:to_h)]
31
+ when Base
32
+ [key, value.to_h]
33
+ else
34
+ [key, value]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Button < Slackened::BlockKit::Blocks::Base
10
+ MAX_LENGTH = 75
11
+
12
+ def initialize(plain_text:, action_id:, value:) # rubocop:disable Metrics/MethodLength
13
+ raise MinimumCharactersError if plain_text.length.zero?
14
+ raise MaximumCharactersError, "#{plain_text} can't be greater than #{MAX_LENGTH}" if plain_text.length > MAX_LENGTH
15
+
16
+ set({
17
+ action_id:,
18
+ text: {
19
+ type: :plain_text,
20
+ text: plain_text
21
+ },
22
+ type: :button,
23
+ value:
24
+ })
25
+
26
+ self
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Context < Slackened::BlockKit::Blocks::Base
10
+ MAX_LENGTH = 10
11
+
12
+ def initialize(*elements)
13
+ raise TooManyElementsError, "#{elements.count} can't be greater than #{MAX_LENGTH}" if elements.length > MAX_LENGTH
14
+
15
+ # TODO: need to allow for image
16
+ # https://api.slack.com/reference/block-kit/blocks#context
17
+ set({
18
+ type: :context,
19
+ elements: elements.map do |element|
20
+ raise MustBeString, "#{element} must be a string" unless element.is_a? String
21
+
22
+ Text.new(element)
23
+ end
24
+ })
25
+
26
+ self
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Divider < Slackened::BlockKit::Blocks::Base
10
+ def initialize
11
+ set({ type: :divider })
12
+
13
+ self
14
+ end
15
+
16
+ def to_h
17
+ @block.to_h
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Header < Slackened::BlockKit::Blocks::Base
10
+ MAX_LENGTH = 150
11
+
12
+ def initialize(plain_text)
13
+ raise TooManyCharactersError, "#{plain_text} can't be greater than #{MAX_LENGTH}" if plain_text.length > MAX_LENGTH
14
+ raise MustBeString unless plain_text.is_a? String
15
+
16
+ set({
17
+ type: :header,
18
+ text: {
19
+ type: :plain_text, # cannot be :mrkdwn
20
+ text: plain_text
21
+ }
22
+ })
23
+
24
+ self
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Section < Slackened::BlockKit::Blocks::Base
10
+ MAX_LENGTH = 10
11
+
12
+ def initialize(*fields) # rubocop:disable Metrics/MethodLength
13
+ raise TooManyFieldsError, "#{fields.count} can't be greater than #{MAX_LENGTH}" if fields.length > MAX_LENGTH
14
+ raise MustBeString unless fields.all? { |f| f.is_a? String }
15
+
16
+ if fields.one?
17
+ set({
18
+ type: :section,
19
+ text: Text.new(fields.first)
20
+ })
21
+
22
+ return self
23
+ end
24
+
25
+ set({
26
+ type: :section,
27
+ fields: fields.map { |f| Text.new(f) }
28
+ })
29
+
30
+ self
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ class Text < Slackened::BlockKit::Blocks::Base
10
+ MAX_LENGTH = 3000
11
+
12
+ def initialize(markdown)
13
+ raise TooManyCharactersError, "#{markdown.length} can't be greater than #{MAX_LENGTH}" if markdown.length > MAX_LENGTH
14
+
15
+ set({
16
+ type: :mrkdwn,
17
+ text: markdown
18
+ })
19
+
20
+ self
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Slackened
6
+ module BlockKit
7
+ # proof of concept
8
+ module Blocks
9
+ extend Forwardable
10
+
11
+ # alphabetical
12
+ def_delegators self, :actions, :button, :context, :divider, :header, :section, :text
13
+
14
+ # alphabetical
15
+ class << self
16
+ def actions(*elements)
17
+ Actions.new(*elements)
18
+ end
19
+
20
+ def button(**kwargs)
21
+ Button.new(**kwargs)
22
+ end
23
+
24
+ def context(*elements)
25
+ Context.new(*elements)
26
+ end
27
+
28
+ def divider
29
+ Divider.new
30
+ end
31
+
32
+ def header(*args)
33
+ Header.new(*args)
34
+ end
35
+
36
+ def section(*args)
37
+ Section.new(*args)
38
+ end
39
+
40
+ def text(*args)
41
+ Text.new(*args)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slackened
4
+ module BlockKit
5
+ # where the blocks are stored
6
+ class Builder
7
+ attr_accessor :blocks
8
+
9
+ def initialize
10
+ @blocks = []
11
+ end
12
+
13
+ def row(row)
14
+ blocks.push(row)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Slackened
4
+ # Settings
5
+ class Configuration
6
+ # alphabetize
7
+ attr_accessor :signing_secret, :webhook_url
8
+
9
+ def initialize
10
+ # alphabetize
11
+ @signing_secret = ''
12
+ @webhook_url = 'https://slack.com'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'json'
5
+ require 'net/http'
6
+ require 'uri'
7
+
8
+ module Slackened
9
+ # Post the message
10
+ class HTTP
11
+ extend Forwardable
12
+
13
+ def_delegators self, :post
14
+
15
+ HEADERS = {
16
+ 'Content-Type' => 'application/json'
17
+ }.freeze
18
+
19
+ class << self
20
+ def post(blocks: [], headers: {}, url: Slackened.configuration.webhook_url)
21
+
22
+ body = JSON.dump({ blocks: })
23
+
24
+ uri = URI(url)
25
+
26
+ Net::HTTP.post(uri, body, HEADERS.merge(headers))
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module Slackened
6
+ # proof of concept
7
+ module Logger
8
+ def log
9
+ @log ||= ::Logger.new($stdout)
10
+ end
11
+
12
+ def debug!
13
+ @log.level = ::Logger::DEBUG
14
+ end
15
+
16
+ def info!
17
+ @log.level = ::Logger::INFO
18
+ end
19
+
20
+ def warn!
21
+ @log.level = ::Logger::WARN
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Slackened
6
+ module Surface
7
+ # Proof of concept
8
+ # https://api.slack.com/surfaces/messages
9
+ class Message
10
+ extend Forwardable
11
+ extend Slackened::BlockKit::Blocks
12
+
13
+ # alphabetical
14
+ def_delegators self, :build, :builder, :layout, :post
15
+
16
+ # alphabetical
17
+ class << self
18
+ def builder
19
+ @builder ||= Slackened::BlockKit::Builder.new
20
+ end
21
+
22
+ def build
23
+ yield builder
24
+ end
25
+
26
+ def post(url: Slackened.configuration.webhook_url, **kwargs)
27
+ layout(**kwargs)
28
+
29
+ Slackened::HTTP.post(
30
+ blocks: payload,
31
+ url:
32
+ )
33
+ end
34
+
35
+ def layout
36
+ raise NotImplementedError, 'You must define `self.layout` and build a layout'
37
+ end
38
+
39
+ private
40
+
41
+ def payload
42
+ builder.blocks.map(&:to_h)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
data/lib/slackened.rb ADDED
@@ -0,0 +1,32 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # Slack Incoming Webhook
5
+ module Slackened
6
+ require_relative 'slackened/authentication/request'
7
+ require_relative 'slackened/block_kit/blocks'
8
+ require_relative 'slackened/block_kit/blocks/actions'
9
+ require_relative 'slackened/block_kit/blocks/button'
10
+ require_relative 'slackened/block_kit/blocks/context'
11
+ require_relative 'slackened/block_kit/blocks/divider'
12
+ require_relative 'slackened/block_kit/blocks/header'
13
+ require_relative 'slackened/block_kit/blocks/section'
14
+ require_relative 'slackened/block_kit/blocks/text'
15
+ require_relative 'slackened/block_kit/builder'
16
+ require_relative 'slackened/configuration'
17
+ require_relative 'slackened/http'
18
+ require_relative 'slackened/logger'
19
+ require_relative 'slackened/surface/message'
20
+
21
+ extend Slackened::Logger
22
+
23
+ class << self
24
+ def configuration
25
+ @configuration ||= Configuration.new
26
+ end
27
+
28
+ def configure
29
+ yield configuration
30
+ end
31
+ end
32
+ end
data.tar.gz.sig ADDED
@@ -0,0 +1,2 @@
1
+ U
2
+ ƛiLK�ۛ��՚�X*5#���;Z�j֨LY6��&BC�����f<0��TŃ*1(��)�`\ޥ�4��kA����ŕ����Ѻ�1�p^�z���u�yo4&K&�����'�9�-��i���7����"��\�HRO
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slackened
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael Matyus
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEZDCCAsygAwIBAgIBATANBgkqhkiG9w0BAQsFADA8MRAwDgYDVQQDDAdtaWNo
14
+ YWVsMRQwEgYKCZImiZPyLGQBGRYEbWF0eTESMBAGCgmSJomT8ixkARkWAnVzMB4X
15
+ DTI0MDcwMTEyMDIxMFoXDTI1MDcwMTEyMDIxMFowPDEQMA4GA1UEAwwHbWljaGFl
16
+ bDEUMBIGCgmSJomT8ixkARkWBG1hdHkxEjAQBgoJkiaJk/IsZAEZFgJ1czCCAaIw
17
+ DQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBALdWoH+SLiruQkWj49ozmet32XEo
18
+ qsdDgz/3wYbVYWsobKGlvkwQf8k1bdjrpS4Ns9FA7sEJ9dM88GpPp1QViDayeNoO
19
+ ox08b92evCTdfsDC2WJy30pYhDu9kANJPCXNGMyhzNbHFGS9Q1+W9UbE0N4UfGpB
20
+ I4Zmz6NPMWBZ6oLCdjFfoKrJ0R9VgTOOjvaNzWQj81PFKzBFVy9HD8oBxZ105rlS
21
+ RhPKR2aeA+NB/7Q9RTETPtaS9vyA6a9L/eF5BO4C/cQOGaRp45D4eSSmYhqg0deL
22
+ NOFCA7dzxTIYBnwMK4DX2hiXi83cK9DrohA4Z2f4HybjRI8ys0zq59ESJRsWWoCq
23
+ lRZOZ/gZxnMGcjK5ICXfDUpLMfBIyso2XXtxfgARM9ypR11uZLu+Uk5tf6VuO74T
24
+ m6136/TfZbIStirgkpIIAQtdqVGNzgRqE9Xm48QREuNgEdjbUYabn0XxOvw/VY1h
25
+ h3Y+v1V2S7ERDEO0fSSXa0gHlJmv/y1R7HgDtQIDAQABo3EwbzAJBgNVHRMEAjAA
26
+ MAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUEnqPdYWlVwXtf4JqTDCK71bHelowGgYD
27
+ VR0RBBMwEYEPbWljaGFlbEBtYXR5LnVzMBoGA1UdEgQTMBGBD21pY2hhZWxAbWF0
28
+ eS51czANBgkqhkiG9w0BAQsFAAOCAYEAcBByxEg8NTpu4U29HBKjqajKS9CAOg2z
29
+ KYEoRleKd/NYapQWKkdzD1Ly8pG9F421c15uQx0gdrwoTwTod/YmL0k5DyHukkfX
30
+ j5v7EUl7N0XCzZICAUJ7K/bjasMN2Q3W0dS2bBkeNAloZP1WoHA2CcBTiA6Wbwx7
31
+ WMiR8+addSAmXYc08fy4mKJDOiqCpNCu9GGh6Ij8S6N8SH+1lCIdkJIAKUIYISZ6
32
+ Dnt3NCUYdw/rfbehWPwlSG98sA8MEreNTtJWWqyjpqP2VfqA1fmLyPqhUhGrpfeb
33
+ 9xpLFh4jRCWtdCy8cuvr/5CAvihnS03TI4BZ3W2QuDXwRPeetKwpokXG123JU6LF
34
+ 7mYaOEX3bj5HG426MuKj8lyHMa4ooe7Cm48eMDyL9oNMI7M9L+0+5xtuYPDEKIlJ
35
+ kSxIyBhjGtfFTmutQUKLBZmOzNf1g4sxY0BylDvd+Ce270mbViFvmpwkpiKpPqQQ
36
+ 2UTSf+Edp6/W5RDQoUAObJKcsvzDv5tp
37
+ -----END CERTIFICATE-----
38
+ date: 2024-07-01 00:00:00.000000000 Z
39
+ dependencies: []
40
+ description: A lightweight interface interacting with the Slack APIs
41
+ email: michael@maty.us
42
+ executables: []
43
+ extensions: []
44
+ extra_rdoc_files: []
45
+ files:
46
+ - lib/slackened.rb
47
+ - lib/slackened/authentication.rb
48
+ - lib/slackened/authentication/request.rb
49
+ - lib/slackened/block_kit/blocks.rb
50
+ - lib/slackened/block_kit/blocks/actions.rb
51
+ - lib/slackened/block_kit/blocks/base.rb
52
+ - lib/slackened/block_kit/blocks/button.rb
53
+ - lib/slackened/block_kit/blocks/context.rb
54
+ - lib/slackened/block_kit/blocks/divider.rb
55
+ - lib/slackened/block_kit/blocks/header.rb
56
+ - lib/slackened/block_kit/blocks/section.rb
57
+ - lib/slackened/block_kit/blocks/text.rb
58
+ - lib/slackened/block_kit/builder.rb
59
+ - lib/slackened/configuration.rb
60
+ - lib/slackened/http.rb
61
+ - lib/slackened/logger.rb
62
+ - lib/slackened/surface/message.rb
63
+ homepage: https://rubygems.org/gems/slackened
64
+ licenses:
65
+ - MIT
66
+ metadata:
67
+ rubygems_mfa_requirea: 'true'
68
+ source_code_uri: https://github.com/matyus/slackened
69
+ post_install_message: 'Slack stands for: Searchable Log of All Communication and Knowledge'
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 3.2.2
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements:
84
+ - '"Activate incoming webhooks" feature via https://api.slack.com/messaging/webhooks'
85
+ - Create a valid webhook endpoint URL provided by Slack
86
+ - Store the entire URL in an ENV variable outside your application
87
+ rubygems_version: 3.4.10
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Interact with Slack
91
+ test_files: []
metadata.gz.sig ADDED
Binary file