sinatra-slack 0.8.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1001e651845588b445467b51c39c70088b645578db40e9a3ab8912fbca3627bb
4
+ data.tar.gz: 7bfd5bb48ed346097648304703bb55a8976a99af2018788f2bbd683a64b113d8
5
+ SHA512:
6
+ metadata.gz: c7cd89a308c80e2d57c021e31a52ca345906b1807480eed6dc88ee1fd82a283899650d6c7920ff02b369155ce59e3364b646c6afb1e2abb99e2b54fe896c35ee
7
+ data.tar.gz: 95cd9900d8bdc6d890b57319826da7d17b2538d1d12548f922ee2342a5f8127fc94a47d83e9a2e6b451934e15fd6b5698257e8dd917c4523ce88371dbe8f2d80
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at TODO: Write your email address. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 TODO: Write your name
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,68 @@
1
+ # Sinatra::Slack
2
+
3
+ > Note: WIP
4
+
5
+ Creating your first Slack Slash Command application has never been to easy. Combining `sinatra` and `sinatra-slack` DSL, you can quickly create a app that processes Slach Commands with ease.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'sinatra-slack'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install sinatra-slack
22
+
23
+ ## Example
24
+
25
+ Go to folder example for more information. Learn how to create a Slack App in https://api.slack.com
26
+
27
+ ## Usage
28
+
29
+ ``` ruby
30
+ require "sinatra/base"
31
+ require "sinatra/slack"
32
+
33
+ class App < Sinatra::Base
34
+ register Sinatra::Slack
35
+
36
+ configure :production, :development do
37
+ enable :logging
38
+
39
+ before { logger.info "Received: #{params}" }
40
+ end
41
+
42
+ set :slack_secret, ENV['SLACK_SIGNING_SECRET']
43
+ commands_endpoint "/slack/commands"
44
+ actions_endpoint "/slack/actions"
45
+
46
+ command "/command *sub_command :spot_name" do |sub_command, spot_name|
47
+ "Executed *command* command \n[subcommand]: #{sub_command} \n[args]: #{spot_name}"
48
+ end
49
+ end
50
+ ```
51
+
52
+ ## Development
53
+
54
+ 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.
55
+
56
+ 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).
57
+
58
+ ## Contributing
59
+
60
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nunonamorado/sinatra-slack. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
61
+
62
+ ## License
63
+
64
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
65
+
66
+ ## Code of Conduct
67
+
68
+ Everyone interacting in the Sinatra::Slack project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/sinatra-slack/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra/async'
4
+ require_relative './slack/instance_helpers'
5
+
6
+ # Sinatra
7
+ module Sinatra
8
+ # Sinatra Module for creating Slack apps with ease
9
+ module Slack
10
+ def self.registered(app)
11
+ # We will use async request handling so that it's easier
12
+ # so offload heavier proccessing to a background thread.
13
+ # Sinatra::Async uses EventMachine.
14
+ app.register Sinatra::Async
15
+
16
+ # Slack signing secret, used for request verification
17
+ app.set :slack_secret, nil
18
+ app.helpers Slack::InstanceHelpers
19
+ end
20
+
21
+ # Defines a new HTTP POST Handler to receive
22
+ # Slash Command notifications.
23
+ def commands_endpoint(path, quick_reply: '')
24
+ set_endpoint(path, quick_reply) do
25
+ signature = "#{command.command} #{command.text}"
26
+ command_pattern = self.class.get_pattern(signature)
27
+ request_params = command_pattern.params(signature).values
28
+
29
+ {
30
+ request_handler: self.class.get_handler(signature),
31
+ request_params: request_params
32
+ }
33
+ end
34
+ end
35
+
36
+ # Defines a new HTTP POST Handler to receive
37
+ # Actions notifications.
38
+ def actions_endpoint(path, quick_reply: '')
39
+ set_endpoint(path, quick_reply) do
40
+ action_pattern = self.class.get_pattern(action.name)
41
+ request_params = action_pattern.params(action.name).values || []
42
+ request_params << action.value
43
+
44
+ {
45
+ request_handler: self.class.get_handler(action.name),
46
+ request_params: request_params
47
+ }
48
+ end
49
+ end
50
+
51
+ def register_handler(signature, &block)
52
+ pattern = parse_signature(signature)
53
+ method_name = get_handler_name(pattern)
54
+ define_method(method_name, &block)
55
+ end
56
+ alias action register_handler
57
+ alias command register_handler
58
+
59
+ def get_handler(signature)
60
+ pattern = get_pattern(signature)
61
+ return unless pattern
62
+
63
+ method_name = get_handler_name(pattern)
64
+ instance_method method_name
65
+ end
66
+
67
+ def get_pattern(signature)
68
+ @patterns.find { |p| p.match(signature) }
69
+ end
70
+
71
+ private
72
+
73
+ def get_handler_name(pattern)
74
+ "#{pattern.safe_string}_handler"
75
+ end
76
+
77
+ def parse_signature(signature)
78
+ @patterns ||= []
79
+ raise 'Signature already defined' if get_pattern(signature)
80
+
81
+ @patterns << Mustermann.new(signature)
82
+ @patterns.last
83
+ end
84
+
85
+ def set_endpoint(path, quick_reply, &block)
86
+ raise 'No block given' unless block_given?
87
+
88
+ define_method("#{path}_options", &block)
89
+
90
+ settings.apost(path) do
91
+ halt 401, 'Invalid Headers' unless authorized?
92
+
93
+ get_options = self.class.instance_method "#{path}_options"
94
+ options = get_options.bind(self).call
95
+
96
+ halt 400 unless options
97
+
98
+ options[:quick_reply] = quick_reply
99
+
100
+ handle_request(options)
101
+ rescue StandardError => ex
102
+ logger.error ex.full_message
103
+ channel.send(slack_error_notification)
104
+ end
105
+ end
106
+ end
107
+
108
+ register Sinatra::Slack
109
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sinatra
4
+ module Slack
5
+ module Helpers
6
+ # Representation of a Action request
7
+ # sent from Slack Servers.
8
+ class ActionRequest
9
+ attr_reader :name, :value
10
+
11
+ def initialize(name, value)
12
+ @name = name
13
+ @value = value
14
+ end
15
+
16
+ def self.parse(params)
17
+ payload = JSON.parse params['payload']
18
+
19
+ return unless payload['type'] == 'interactive_message'
20
+
21
+ action = payload['actions'].first
22
+ value = action['value'] if action.key?('value')
23
+ value = action['selected_options'].first['value'] if action.key?('selected_options')
24
+ name = payload['callback_id']
25
+
26
+ new(name, value)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ require "httparty"
2
+
3
+ module Sinatra
4
+ module Slack
5
+ module Helpers
6
+ class Channel
7
+ def initialize(opts = {})
8
+ @id = opts["channel_id"]
9
+ @name = opts["channel_name"]
10
+ @url = opts["response_url"]
11
+ end
12
+
13
+ def send(response)
14
+ options = {
15
+ headers: {
16
+ "Content-type": "application/json"
17
+ },
18
+ body: response.to_json
19
+ }
20
+ HTTParty.post(@url, options)
21
+ end
22
+
23
+ def to_s
24
+ "Slack Channel: #{@name}"
25
+ end
26
+
27
+ def self.parse(params)
28
+ payload = params.clone
29
+ if payload["payload"]
30
+ payload = JSON.parse(payload["payload"])
31
+ payload["channel_id"] = payload.dig("channel", "id")
32
+ payload["channel_name"] = payload.dig("channel", "name")
33
+ end
34
+
35
+ self.new(payload)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sinatra
4
+ module Slack
5
+ module Helpers
6
+ # Representation of a Slash Command request sent
7
+ # from Slack servers.
8
+ class CommandRequest
9
+ attr_reader :token, :command, :text, :response_url, :trigger_id,
10
+ :user_id, :user_name, :team_id, :team_name, :channel_id,
11
+ :channel_name
12
+
13
+ def initialize(params = {})
14
+ @params = params
15
+ params.keys.each { |k| instance_variable_set("@#{k}", params[k]) }
16
+ end
17
+
18
+ def to_s
19
+ @params.to_s
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sinatra
4
+ module Slack
5
+ module Helpers
6
+ # Represents a message attachment sent to the Slack Channel.
7
+ class Attachment
8
+ attr_accessor :title, :color, :attachment_type,
9
+ :text, :fallback, :image_url
10
+
11
+ def initialize(callback_id)
12
+ @callback_id = callback_id
13
+ @attachment_type = 'default'
14
+ @color = '#3AA3E3'
15
+ @actions = []
16
+
17
+ @text = ''
18
+ @fallback = ''
19
+ @image_url = ''
20
+ @title = ''
21
+ end
22
+
23
+ def action_button(name, text, value)
24
+ @actions << {
25
+ name: name,
26
+ text: text,
27
+ type: 'button',
28
+ value: value
29
+ }
30
+ end
31
+
32
+ def action_menu(name, text, options)
33
+ @actions << {
34
+ name: name,
35
+ text: text,
36
+ type: 'select',
37
+ options: options
38
+ }
39
+ end
40
+
41
+ def to_json
42
+ att_obj = {}
43
+
44
+ att_obj[:callback_id] = @callback_id
45
+
46
+ att_obj[:title] = title unless title.empty?
47
+ att_obj[:color] = color unless color.empty?
48
+ att_obj[:attachment_type] = attachment_type unless attachment_type.empty?
49
+ att_obj[:text] = text unless text.empty?
50
+ att_obj[:fallback] = fallback unless fallback.empty?
51
+ att_obj[:image_url] = image_url unless image_url.empty?
52
+
53
+ att_obj[:actions] = @actions unless @actions.empty?
54
+
55
+ att_obj
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './slack_attachment'
4
+
5
+ module Sinatra
6
+ module Slack
7
+ module Helpers
8
+ # Represents a message sent to the Slack Channel.
9
+ class SlackResponse
10
+ attr_accessor :text, :replace_original, :mrkdwn
11
+
12
+ def initialize(callback_id)
13
+ @callback_id = callback_id
14
+ @text = nil
15
+ @attachments = []
16
+ @replace_original = true
17
+ @mrkdwn = false
18
+ end
19
+
20
+ def attachment
21
+ return unless block_given?
22
+
23
+ attachment = Helpers::Attachment.new(@callback_id)
24
+ yield attachment
25
+ @attachments << attachment
26
+ end
27
+
28
+ def to_json
29
+ response = {}
30
+
31
+ response[:text] = @text if @text
32
+ response[:mrkdwn] = @mrkdwn
33
+ response[:replace_original] = @replace_original
34
+
35
+ response[:attachments] = @attachments.map(&:to_json) unless @attachments.empty?
36
+
37
+ response.to_json
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './helpers/command_request'
4
+ require_relative './helpers/channel'
5
+ require_relative './helpers/slack_response'
6
+ require_relative './helpers/action_request'
7
+
8
+ module Sinatra
9
+ module Slack
10
+ # Instance level helper methods
11
+ module InstanceHelpers
12
+ def command
13
+ @command ||= Helpers::CommandRequest.new(params)
14
+ end
15
+
16
+ def action
17
+ @action ||= Helpers::ActionRequest.parse(params)
18
+ end
19
+
20
+ def channel
21
+ @channel ||= Helpers::Channel.parse(params)
22
+ end
23
+
24
+ def slack_response(callback_id)
25
+ s_resp = Helpers::SlackResponse.new(callback_id)
26
+ yield s_resp if block_given?
27
+ s_resp
28
+ end
29
+
30
+ def slack_error_notification
31
+ slack_response '' do |r|
32
+ r.text = 'Ups, something went wrong'
33
+ end
34
+ end
35
+
36
+ def handle_request(request_handler:, request_params:, quick_reply: '...')
37
+ EM.defer do
38
+ deferred_message = request_handler.bind(self).call(*request_params)
39
+ channel.send(deferred_message)
40
+ rescue StandardError => ex
41
+ logger.error ex.full_message
42
+ channel.send(slack_error_notification)
43
+ end
44
+
45
+ body quick_reply
46
+ end
47
+
48
+ # Checks for Slack defined HTTP headers
49
+ # and computes the request signature (HMAC). If provided signature
50
+ # is the same as the computed one, the request is valid.
51
+ #
52
+ # Go to this page for the verification process:
53
+ # https://api.slack.com/docs/verifying-requests-from-slack
54
+ def authorized?
55
+ logger.warn 'Missing Slack signing token' unless settings.slack_secret
56
+ return true unless settings.slack_secret
57
+
58
+ valid_headers? &&
59
+ compute_signature(settings.slack_secret) == slack_signature
60
+ end
61
+
62
+ # Helper methods for Slack request validation
63
+ def slack_signature
64
+ @slack_signature ||= env['HTTP_X_SLACK_SIGNATURE']
65
+ end
66
+
67
+ def slack_timestamp
68
+ @slack_timestamp ||= env['HTTP_X_SLACK_REQUEST_TIMESTAMP']
69
+ end
70
+
71
+ def valid_headers?
72
+ return false unless slack_signature || slack_timestamp
73
+
74
+ # The request timestamp is more than five minutes from local time.
75
+ # It could be a replay attack, so let's ignore it.
76
+ (Time.now.to_i - slack_timestamp.to_i).abs <= 60 * 5
77
+ end
78
+
79
+ def compute_signature(secret)
80
+ # in case someone already read it
81
+ request.body.rewind
82
+
83
+ # From Slack API docs, the "v0" is always fixed for now
84
+ sig_basestring = "v0:#{slack_timestamp}:#{request.body.read}"
85
+ "v0=#{hmac_signed(sig_basestring, secret)}"
86
+ end
87
+
88
+ def hmac_signed(to_sign, hmac_key)
89
+ sha256 = OpenSSL::Digest.new('sha256')
90
+ OpenSSL::HMAC.hexdigest(sha256, hmac_key, to_sign)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sinatra
4
+ module Slack
5
+ VERSION = '0.8.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-slack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ platform: ruby
6
+ authors:
7
+ - Nuno Namorado
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: async_sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.3.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.3.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: httparty
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.16'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.16.4
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.16'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.16.4
53
+ - !ruby/object:Gem::Dependency
54
+ name: mustermann
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.0'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.0.3
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.0'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.0.3
73
+ - !ruby/object:Gem::Dependency
74
+ name: sinatra
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '2.0'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.0.5
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 2.0.5
93
+ - !ruby/object:Gem::Dependency
94
+ name: thin
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.7'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.7.2
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.7'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.7.2
113
+ description:
114
+ email:
115
+ - n.namorado@gmail.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - CODE_OF_CONDUCT.md
121
+ - LICENSE.txt
122
+ - README.md
123
+ - lib/sinatra/slack.rb
124
+ - lib/sinatra/slack/helpers/action_request.rb
125
+ - lib/sinatra/slack/helpers/channel.rb
126
+ - lib/sinatra/slack/helpers/command_request.rb
127
+ - lib/sinatra/slack/helpers/slack_attachment.rb
128
+ - lib/sinatra/slack/helpers/slack_response.rb
129
+ - lib/sinatra/slack/instance_helpers.rb
130
+ - lib/sinatra/slack/version.rb
131
+ homepage: https://github.com/nunonamorado/sinatra-slack
132
+ licenses:
133
+ - MIT
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 2.7.6
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: Create Slack apps with a simple Sinatra specific DSL
155
+ test_files: []