waylon-slack 0.1.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 +7 -0
- data/.gitignore +14 -0
- data/.roxanne.yml +4 -0
- data/.rspec +3 -0
- data/.rubocop.yml +38 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +157 -0
- data/LICENSE.txt +21 -0
- data/README.md +55 -0
- data/Rakefile +18 -0
- data/bin/console +24 -0
- data/bin/setup +8 -0
- data/lib/waylon/senses/slack.rb +128 -0
- data/lib/waylon/slack/channel.rb +118 -0
- data/lib/waylon/slack/message.rb +98 -0
- data/lib/waylon/slack/user.rb +138 -0
- data/lib/waylon/slack/version.rb +7 -0
- data/lib/waylon/slack.rb +21 -0
- data/lib/waylon/webhooks/slack.rb +41 -0
- data/scripts/test.sh +5 -0
- data/waylon-slack.gemspec +44 -0
- metadata +214 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 909809c1f1e6121fb84667fb922a54cf22cbd801191ab646c9c9db5305c87e60
|
|
4
|
+
data.tar.gz: d776dd6a040f8278862e8df2c176254acac8a2695e4965e8c06c3b56daef57e0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 56e572e2fbc880b4ce64f5a61ba888712282e54334705b07d8ecb671d8064fd5b2c217b7f789979bbc1e64ec09ca204b01e301b0b5c1d22766f74a6997c19661
|
|
7
|
+
data.tar.gz: 5bfeadf428a8bc725c058955a5c0ca4987af49940d7c6523a02b8644e197ff495289f81aa8ea9fd9ef03d2126fa3431ec7ff9db46c5c2db2607f527bf4d33499
|
data/.gitignore
ADDED
data/.roxanne.yml
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.0
|
|
3
|
+
NewCops: enable
|
|
4
|
+
|
|
5
|
+
Style/MixinUsage:
|
|
6
|
+
Exclude:
|
|
7
|
+
- "bin/console"
|
|
8
|
+
|
|
9
|
+
Style/StringLiterals:
|
|
10
|
+
Enabled: true
|
|
11
|
+
EnforcedStyle: double_quotes
|
|
12
|
+
|
|
13
|
+
Style/StringLiteralsInInterpolation:
|
|
14
|
+
Enabled: true
|
|
15
|
+
EnforcedStyle: double_quotes
|
|
16
|
+
|
|
17
|
+
Layout/LineLength:
|
|
18
|
+
Max: 120
|
|
19
|
+
|
|
20
|
+
Gemspec/RequireMFA:
|
|
21
|
+
Enabled: false
|
|
22
|
+
|
|
23
|
+
Metrics/AbcSize:
|
|
24
|
+
Max: 19
|
|
25
|
+
|
|
26
|
+
Metrics/CyclomaticComplexity:
|
|
27
|
+
Max: 9
|
|
28
|
+
|
|
29
|
+
Metrics/PerceivedComplexity:
|
|
30
|
+
Max: 9
|
|
31
|
+
|
|
32
|
+
Metrics/MethodLength:
|
|
33
|
+
Max: 20
|
|
34
|
+
|
|
35
|
+
Metrics/BlockLength:
|
|
36
|
+
Exclude:
|
|
37
|
+
- "**/*_spec.rb"
|
|
38
|
+
- "*.gemspec"
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.1.0
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
waylon-slack (0.1.0)
|
|
5
|
+
slack-ruby-client (~> 1.0)
|
|
6
|
+
waylon-core (~> 0.1)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
addressable (2.8.0)
|
|
12
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
13
|
+
ast (2.4.2)
|
|
14
|
+
concurrent-ruby (1.1.9)
|
|
15
|
+
diff-lcs (1.5.0)
|
|
16
|
+
docile (1.4.0)
|
|
17
|
+
faraday (1.9.3)
|
|
18
|
+
faraday-em_http (~> 1.0)
|
|
19
|
+
faraday-em_synchrony (~> 1.0)
|
|
20
|
+
faraday-excon (~> 1.1)
|
|
21
|
+
faraday-httpclient (~> 1.0)
|
|
22
|
+
faraday-multipart (~> 1.0)
|
|
23
|
+
faraday-net_http (~> 1.0)
|
|
24
|
+
faraday-net_http_persistent (~> 1.0)
|
|
25
|
+
faraday-patron (~> 1.0)
|
|
26
|
+
faraday-rack (~> 1.0)
|
|
27
|
+
faraday-retry (~> 1.0)
|
|
28
|
+
ruby2_keywords (>= 0.0.4)
|
|
29
|
+
faraday-em_http (1.0.0)
|
|
30
|
+
faraday-em_synchrony (1.0.0)
|
|
31
|
+
faraday-excon (1.1.0)
|
|
32
|
+
faraday-httpclient (1.0.1)
|
|
33
|
+
faraday-multipart (1.0.3)
|
|
34
|
+
multipart-post (>= 1.2, < 3)
|
|
35
|
+
faraday-net_http (1.0.1)
|
|
36
|
+
faraday-net_http_persistent (1.2.0)
|
|
37
|
+
faraday-patron (1.0.0)
|
|
38
|
+
faraday-rack (1.0.0)
|
|
39
|
+
faraday-retry (1.0.3)
|
|
40
|
+
faraday_middleware (1.2.0)
|
|
41
|
+
faraday (~> 1.0)
|
|
42
|
+
gli (2.21.0)
|
|
43
|
+
hashie (5.0.0)
|
|
44
|
+
i18n (1.9.1)
|
|
45
|
+
concurrent-ruby (~> 1.0)
|
|
46
|
+
json (2.6.1)
|
|
47
|
+
moneta (1.4.2)
|
|
48
|
+
mono_logger (1.1.1)
|
|
49
|
+
multi_json (1.15.0)
|
|
50
|
+
multipart-post (2.1.1)
|
|
51
|
+
mustermann (1.1.1)
|
|
52
|
+
ruby2_keywords (~> 0.0.1)
|
|
53
|
+
nio4r (2.5.8)
|
|
54
|
+
parallel (1.21.0)
|
|
55
|
+
parser (3.1.0.0)
|
|
56
|
+
ast (~> 2.4.1)
|
|
57
|
+
public_suffix (4.0.6)
|
|
58
|
+
puma (5.6.1)
|
|
59
|
+
nio4r (~> 2.0)
|
|
60
|
+
rack (2.2.3)
|
|
61
|
+
rack-protection (2.1.0)
|
|
62
|
+
rack
|
|
63
|
+
rainbow (3.1.1)
|
|
64
|
+
rake (13.0.6)
|
|
65
|
+
redis (4.5.1)
|
|
66
|
+
redis-namespace (1.8.1)
|
|
67
|
+
redis (>= 3.0.4)
|
|
68
|
+
regexp_parser (2.2.0)
|
|
69
|
+
resque (2.2.0)
|
|
70
|
+
mono_logger (~> 1.0)
|
|
71
|
+
multi_json (~> 1.0)
|
|
72
|
+
redis-namespace (~> 1.6)
|
|
73
|
+
sinatra (>= 0.9.2)
|
|
74
|
+
vegas (~> 0.1.2)
|
|
75
|
+
rexml (3.2.5)
|
|
76
|
+
rspec (3.10.0)
|
|
77
|
+
rspec-core (~> 3.10.0)
|
|
78
|
+
rspec-expectations (~> 3.10.0)
|
|
79
|
+
rspec-mocks (~> 3.10.0)
|
|
80
|
+
rspec-core (3.10.2)
|
|
81
|
+
rspec-support (~> 3.10.0)
|
|
82
|
+
rspec-expectations (3.10.2)
|
|
83
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
84
|
+
rspec-support (~> 3.10.0)
|
|
85
|
+
rspec-mocks (3.10.3)
|
|
86
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
87
|
+
rspec-support (~> 3.10.0)
|
|
88
|
+
rspec-support (3.10.3)
|
|
89
|
+
rubocop (1.25.0)
|
|
90
|
+
parallel (~> 1.10)
|
|
91
|
+
parser (>= 3.1.0.0)
|
|
92
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
93
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
94
|
+
rexml
|
|
95
|
+
rubocop-ast (>= 1.15.1, < 2.0)
|
|
96
|
+
ruby-progressbar (~> 1.7)
|
|
97
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
98
|
+
rubocop-ast (1.15.1)
|
|
99
|
+
parser (>= 3.0.1.1)
|
|
100
|
+
rubocop-rake (0.6.0)
|
|
101
|
+
rubocop (~> 1.0)
|
|
102
|
+
rubocop-rspec (2.8.0)
|
|
103
|
+
rubocop (~> 1.19)
|
|
104
|
+
ruby-progressbar (1.11.0)
|
|
105
|
+
ruby2_keywords (0.0.5)
|
|
106
|
+
simplecov (0.21.2)
|
|
107
|
+
docile (~> 1.1)
|
|
108
|
+
simplecov-html (~> 0.11)
|
|
109
|
+
simplecov_json_formatter (~> 0.1)
|
|
110
|
+
simplecov-html (0.12.3)
|
|
111
|
+
simplecov_json_formatter (0.1.3)
|
|
112
|
+
sinatra (2.1.0)
|
|
113
|
+
mustermann (~> 1.0)
|
|
114
|
+
rack (~> 2.2)
|
|
115
|
+
rack-protection (= 2.1.0)
|
|
116
|
+
tilt (~> 2.0)
|
|
117
|
+
slack-ruby-client (1.0.0)
|
|
118
|
+
faraday (>= 1.0)
|
|
119
|
+
faraday_middleware
|
|
120
|
+
gli
|
|
121
|
+
hashie
|
|
122
|
+
websocket-driver
|
|
123
|
+
tilt (2.0.10)
|
|
124
|
+
unicode-display_width (2.1.0)
|
|
125
|
+
vegas (0.1.11)
|
|
126
|
+
rack (>= 1.0.0)
|
|
127
|
+
waylon-core (0.1.3)
|
|
128
|
+
addressable (~> 2.8)
|
|
129
|
+
faraday (~> 1.8)
|
|
130
|
+
i18n (~> 1.8)
|
|
131
|
+
json (~> 2.6)
|
|
132
|
+
moneta (~> 1.4)
|
|
133
|
+
puma (~> 5.5)
|
|
134
|
+
resque (~> 2.2)
|
|
135
|
+
webrick (1.7.0)
|
|
136
|
+
websocket-driver (0.7.5)
|
|
137
|
+
websocket-extensions (>= 0.1.0)
|
|
138
|
+
websocket-extensions (0.1.5)
|
|
139
|
+
yard (0.9.27)
|
|
140
|
+
webrick (~> 1.7.0)
|
|
141
|
+
|
|
142
|
+
PLATFORMS
|
|
143
|
+
arm64-darwin-21
|
|
144
|
+
|
|
145
|
+
DEPENDENCIES
|
|
146
|
+
bundler (~> 2.3)
|
|
147
|
+
rake (~> 13.0)
|
|
148
|
+
rspec (~> 3.10)
|
|
149
|
+
rubocop (~> 1.23)
|
|
150
|
+
rubocop-rake (~> 0.6)
|
|
151
|
+
rubocop-rspec (~> 2.6)
|
|
152
|
+
simplecov (~> 0.21)
|
|
153
|
+
waylon-slack!
|
|
154
|
+
yard (~> 0.9, >= 0.9.27)
|
|
155
|
+
|
|
156
|
+
BUNDLED WITH
|
|
157
|
+
2.3.4
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Jonathan Gnagy
|
|
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,55 @@
|
|
|
1
|
+
# Waylon::Slack
|
|
2
|
+
|
|
3
|
+
The Slack _Sense_ for the [Waylon](https://github.com/jgnagy/waylon-core) Bot Framework. This allows Waylon to interact with Slack via the Slack [Web API](https://api.slack.com/web) and [Events API](https://api.slack.com/events) (for sending and receiving messages, respectively).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your bot's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'waylon-slack'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or, if your bot is itself a gem, add this to your .gemspec:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
spec.add_dependency "waylon-slack", "~> 0.1"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
And then execute:
|
|
20
|
+
|
|
21
|
+
$ bundle install
|
|
22
|
+
|
|
23
|
+
Or install it yourself via:
|
|
24
|
+
|
|
25
|
+
$ gem install waylon-slack
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
You'll need both your webhook server and your workers to have a line like this:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# right after 'require "waylon"'...
|
|
33
|
+
require "waylon/slack"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
That should get it working in your bot. You'll also need to ensure that these environment variables are properly defined based on your bot's [Slack app setup](https://api.slack.com/authentication/basics):
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
SLACK_OAUTH_TOKEN="xoxb-..."
|
|
40
|
+
SLACK_SIGNING_SECRET="..."
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Development
|
|
44
|
+
|
|
45
|
+
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.
|
|
46
|
+
|
|
47
|
+
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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
48
|
+
|
|
49
|
+
## Contributing
|
|
50
|
+
|
|
51
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jgnagy/waylon-slack.
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
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,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "rubocop/rake_task"
|
|
6
|
+
require "yard"
|
|
7
|
+
require "resque/tasks"
|
|
8
|
+
require "waylon/core"
|
|
9
|
+
|
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
11
|
+
RuboCop::RakeTask.new
|
|
12
|
+
YARD::Rake::YardocTask.new do |y|
|
|
13
|
+
y.options = [
|
|
14
|
+
"--markup", "markdown"
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
task default: %i[spec rubocop yard]
|
data/bin/console
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "bundler/setup"
|
|
5
|
+
require "waylon"
|
|
6
|
+
config = Waylon::Config.instance
|
|
7
|
+
config.load_env
|
|
8
|
+
|
|
9
|
+
Waylon::Cache = Moneta.new(:Cookie)
|
|
10
|
+
Waylon::Storage = Moneta.new(:LRUHash)
|
|
11
|
+
|
|
12
|
+
require "waylon/slack"
|
|
13
|
+
|
|
14
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
15
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
16
|
+
|
|
17
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
18
|
+
# require "pry"
|
|
19
|
+
# Pry.start
|
|
20
|
+
|
|
21
|
+
require "irb"
|
|
22
|
+
|
|
23
|
+
include Waylon
|
|
24
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waylon
|
|
4
|
+
module Senses
|
|
5
|
+
# The Waylon Sense for interacting with Slack via the Events API
|
|
6
|
+
class Slack < Waylon::Sense
|
|
7
|
+
features %i[blocks private_messages reactions threads]
|
|
8
|
+
|
|
9
|
+
# Provides easy access to the Slack Web Client for interacting with the Slack API
|
|
10
|
+
# @return [Slack::Web::Client]
|
|
11
|
+
def self.client
|
|
12
|
+
@client ||= ::Slack::Web::Client.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Takes an incoming request from a webhook and converts it to a usable Waylon Message
|
|
16
|
+
# @param request [Hash,Waylon::Message]
|
|
17
|
+
# @return [Waylon::Message]
|
|
18
|
+
def self.message_from_request(request)
|
|
19
|
+
return request if request.is_a?(message_class)
|
|
20
|
+
|
|
21
|
+
if request["type"] == "event_callback" && %w[app_mention message].include?(request.dig("event", "type"))
|
|
22
|
+
# These are typical chat messages
|
|
23
|
+
message_class.new(request["event_id"], request["event"])
|
|
24
|
+
elsif request["type"] == "event_callback"
|
|
25
|
+
log("Support for events of type #{request.dig("event", "type")} not yet implemented")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# "At-mention" for Slack.
|
|
30
|
+
# @param user [Waylon::User] The User to mention
|
|
31
|
+
# @return [String]
|
|
32
|
+
def self.mention(user)
|
|
33
|
+
"<@#{user.handle}>"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Provides a simple means to privately reply to the author of a Message
|
|
37
|
+
# @param request [Hash,Waylon::Message]
|
|
38
|
+
# @param text [String] Reply contents
|
|
39
|
+
# @return [void]
|
|
40
|
+
def self.private_reply(request, text)
|
|
41
|
+
message = message_from_request(request)
|
|
42
|
+
message.author.dm(text: text)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Provides a simple means to privately reply to the author of a Message using Blocks
|
|
46
|
+
# @param request [Hash,Waylon::Message]
|
|
47
|
+
# @param blocks [Array] Blocks data to reply with
|
|
48
|
+
# @return [void]
|
|
49
|
+
def self.private_reply_with_blocks(request, blocks)
|
|
50
|
+
message = message_from_request(request)
|
|
51
|
+
message.author.dm(blocks: blocks)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Allows reacting to a request via the Sense's own mechanism
|
|
55
|
+
# @param request [Hash,Waylon::Message]
|
|
56
|
+
# @param reaction [String]
|
|
57
|
+
# @return [void]
|
|
58
|
+
def self.react(request, reaction)
|
|
59
|
+
message = message_from_request(request)
|
|
60
|
+
message.react(reaction)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Reply to a Message in a Channel with some text
|
|
64
|
+
# @param request [Hash,Waylon::Message]
|
|
65
|
+
# @param text [String] Reply contents
|
|
66
|
+
# @return [void]
|
|
67
|
+
def self.reply(request, text)
|
|
68
|
+
message = message_from_request(request)
|
|
69
|
+
message.channel.post(text: text)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Reply to a Message in a Channel with some blocks
|
|
73
|
+
# @param request [Hash,Waylon::Message]
|
|
74
|
+
# @param blocks [Array] Blocks to reply with
|
|
75
|
+
# @return [void]
|
|
76
|
+
def self.reply_with_blocks(request, blocks)
|
|
77
|
+
message = message_from_request(request)
|
|
78
|
+
message.channel.post(blocks: blocks)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Executed by Resque, this is how this Sense determines what to do with an incoming request
|
|
82
|
+
# @param received_web_content [Hash] The parsed web request content from the Webhook
|
|
83
|
+
# @return [void]
|
|
84
|
+
def self.run(received_web_content)
|
|
85
|
+
log("Received request of type #{received_web_content["type"]}", :debug)
|
|
86
|
+
message = message_from_request(received_web_content)
|
|
87
|
+
unless message
|
|
88
|
+
log("Unable to handle request")
|
|
89
|
+
return
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if message.author == Waylon::Slack::User.whoami
|
|
93
|
+
log("Ignoring my own message...", :debug)
|
|
94
|
+
return
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
log("Responding to message from bot '#{message.author.handle}'") if message.author.bot?
|
|
98
|
+
|
|
99
|
+
route = Waylon::SkillRegistry.route(message) || SkillRegistry.instance.default_route(message)
|
|
100
|
+
enqueue(route, received_web_content)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Reply to a Message in a Thread with some text
|
|
104
|
+
# @param request [Hash,Waylon::Message]
|
|
105
|
+
# @param text [String] Reply contents
|
|
106
|
+
# @return [void]
|
|
107
|
+
def self.threaded_reply(request, text)
|
|
108
|
+
message = message_from_request(request)
|
|
109
|
+
message.channel.post(text: text, thread: message.thread_parent)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Required by the Waylon framework, this provides the Sense's own Message class
|
|
113
|
+
# @return [Class]
|
|
114
|
+
def self.message_class
|
|
115
|
+
Waylon::Slack::Message
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Required by the Waylon framework, this provides the Sense's own User class
|
|
119
|
+
# @return [Class]
|
|
120
|
+
def self.user_class
|
|
121
|
+
Waylon::Slack::User
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Automatically informs Waylon about this Sense
|
|
125
|
+
SenseRegistry.register(:slack, self)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waylon
|
|
4
|
+
module Slack
|
|
5
|
+
# A representation of Slack channels for Waylon
|
|
6
|
+
class Channel
|
|
7
|
+
attr_reader :id
|
|
8
|
+
|
|
9
|
+
# Allows finding a channel based on its channel name
|
|
10
|
+
# @raise [Slack::Web::Api::Errors::ChannelNotFound] When the channel doesn't exist
|
|
11
|
+
# @return [Channel]
|
|
12
|
+
def self.from_name(name)
|
|
13
|
+
name = name.start_with?("#") ? name : "##{name}"
|
|
14
|
+
raw = sense.client.conversations_info(channel: name)
|
|
15
|
+
new(raw["channel"]["id"], data: raw["channel"])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Provides direct access to the Sense class
|
|
19
|
+
# @return [Class]
|
|
20
|
+
def self.sense
|
|
21
|
+
::Waylon::Senses::Slack
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def initialize(id = nil, data: {})
|
|
25
|
+
raise "Must provide ID or details" unless id || !data.empty?
|
|
26
|
+
|
|
27
|
+
@id = id || data["id"]
|
|
28
|
+
# @data should never be accessed directly... always use the wrapper instance method
|
|
29
|
+
@data = data
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Is channel archived (meaning no further messages are possible)?
|
|
33
|
+
# @return [Boolean]
|
|
34
|
+
def archived?
|
|
35
|
+
data["is_archived"].dup
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Provides lazy, cached access to the Channel's internal details
|
|
39
|
+
# @return [Hash]
|
|
40
|
+
def data
|
|
41
|
+
if !@data || @data.empty?
|
|
42
|
+
# Only cache channel info for 5 min
|
|
43
|
+
sense.cache("channels.#{id}", expires: 300) do
|
|
44
|
+
raw_data = sense.client.conversations_info(channel: id)
|
|
45
|
+
@data = raw_data["channel"]
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
@data
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Is this the "main" channel for this team?
|
|
53
|
+
# @return [Boolean]
|
|
54
|
+
def general?
|
|
55
|
+
data["is_general"].dup
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Lists channel members
|
|
59
|
+
# @return [Array<User>] channel members
|
|
60
|
+
def members
|
|
61
|
+
# Only cache channel member ids for 5 min
|
|
62
|
+
ids = sense.cache("channels.#{id}.member_ids", expires: 300) do
|
|
63
|
+
member_ids = []
|
|
64
|
+
sense.client.conversations_members(channel: id) do |raw|
|
|
65
|
+
member_ids += raw["members"]
|
|
66
|
+
end
|
|
67
|
+
member_ids.sort.uniq
|
|
68
|
+
end
|
|
69
|
+
ids.map { |m| User.new(m) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Is this bot a member of the channel?
|
|
73
|
+
# @return [Boolean]
|
|
74
|
+
def member?
|
|
75
|
+
data["is_member"]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# The proper channel name
|
|
79
|
+
# @return [String]
|
|
80
|
+
def name
|
|
81
|
+
"##{data["name"]}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Posts a message to a channel
|
|
85
|
+
# @param text [String] Message text or fallback text for blocks
|
|
86
|
+
# @param attachments [Array<Hash>] Old-style message attachments
|
|
87
|
+
# @param blocks [Array<Hash>] New-style block method of sending complex messages
|
|
88
|
+
# @param thread [Integer] The message timestamp for the thread id
|
|
89
|
+
# @return [void]
|
|
90
|
+
def post(text: nil, attachments: nil, blocks: nil, thread: nil)
|
|
91
|
+
options = { channel: id }
|
|
92
|
+
options[:text] = text if text
|
|
93
|
+
options[:attachments] = attachments if attachments
|
|
94
|
+
options[:blocks] = blocks if blocks
|
|
95
|
+
options[:thread_ts] = thread if thread
|
|
96
|
+
sense.client.chat_postMessage(options)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Is this a private channel? (meaning a direct message, NOT private in the Slack sense)
|
|
100
|
+
# @return [Boolean]
|
|
101
|
+
def private?
|
|
102
|
+
data["is_im"].dup
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# An instance-level helper to access the class-level method
|
|
106
|
+
# @return [Class]
|
|
107
|
+
def sense
|
|
108
|
+
self.class.sense
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Provides access to the Channel's topic
|
|
112
|
+
# @return [String]
|
|
113
|
+
def topic
|
|
114
|
+
data.dig("topic", "value").dup
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waylon
|
|
4
|
+
module Slack
|
|
5
|
+
# A representation of Slack messages for Waylon
|
|
6
|
+
class Message
|
|
7
|
+
include Waylon::Message
|
|
8
|
+
|
|
9
|
+
attr_reader :id, :data
|
|
10
|
+
|
|
11
|
+
def initialize(id, data = {})
|
|
12
|
+
@id = id
|
|
13
|
+
@data = data
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The User that authored this Message
|
|
17
|
+
# @return [User]
|
|
18
|
+
def author
|
|
19
|
+
User.new(data["user"])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The Channel where this Message was sent
|
|
23
|
+
# @return [Channel]
|
|
24
|
+
def channel
|
|
25
|
+
Channel.new(data["channel"])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Does the message text mention the bot?
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
def mentions_bot?
|
|
31
|
+
me = User.whoami
|
|
32
|
+
reg = /(,\s+)?\s*@#{me.id},?\s*/
|
|
33
|
+
::Slack::Messages::Formatting.unescape(data["text"]) =~ reg ? true : false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Is this Message a reply in a thread?
|
|
37
|
+
# @return [Boolean]
|
|
38
|
+
def part_of_thread?
|
|
39
|
+
thread_ts && thread_ts != ts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Is this a private Message / direct Message?
|
|
43
|
+
# @return [Boolean]
|
|
44
|
+
def private_message?
|
|
45
|
+
channel.private?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
alias private? private_message?
|
|
49
|
+
|
|
50
|
+
# Uses the Sense's Web Client to add a reaction to a Message
|
|
51
|
+
# @param reaction [String,Symbol] The reaction to add (not wrapped in ":")
|
|
52
|
+
# @return [void]
|
|
53
|
+
def react(reaction)
|
|
54
|
+
sense.client.reactions_add(channel: channel.id, name: reaction, timestamp: ts)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Easy access to the Sense class
|
|
58
|
+
# @return [Class]
|
|
59
|
+
def sense
|
|
60
|
+
::Waylon::Senses::Slack
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# The unescaped contents of the Message
|
|
64
|
+
# @return [String]
|
|
65
|
+
def text
|
|
66
|
+
me = User.whoami
|
|
67
|
+
reg = /(,\s+)?\s*@#{me.id},?\s*/
|
|
68
|
+
::Slack::Messages::Formatting.unescape(data["text"]).gsub(reg, "")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
alias body text
|
|
72
|
+
|
|
73
|
+
# The TS value of the parent of this Message's thread, or its own TS if it is the parent
|
|
74
|
+
# @return [String]
|
|
75
|
+
def thread_parent
|
|
76
|
+
thread_ts || ts
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The TS value of the parent of this Message's thread, if it exists
|
|
80
|
+
# @return [String,nil]
|
|
81
|
+
def thread_ts
|
|
82
|
+
data["thread_ts"].dup
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Does this Message either directly mention or is it directly to this bot?
|
|
86
|
+
# @return [Boolean]
|
|
87
|
+
def to_bot?
|
|
88
|
+
data["type"] == "app_mention" || private_message?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# This Message's own TS value (which should not be used for threading if it itself is a thread reply)
|
|
92
|
+
# @return [String]
|
|
93
|
+
def ts
|
|
94
|
+
data["ts"].dup
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waylon
|
|
4
|
+
module Slack
|
|
5
|
+
# A representation of Slack users for Waylon
|
|
6
|
+
class User
|
|
7
|
+
include Waylon::User
|
|
8
|
+
|
|
9
|
+
# Find a Slack User based on their IM handle
|
|
10
|
+
# @param handle [String]
|
|
11
|
+
# @return [User]
|
|
12
|
+
def self.find_by_handle(handle)
|
|
13
|
+
real_handle = handle.start_with?("@") ? handle : "@#{handle}"
|
|
14
|
+
from_response(sense.client.users_info(user: real_handle))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Find a Slack User based on their email address
|
|
18
|
+
# @param email [String]
|
|
19
|
+
# @return [User]
|
|
20
|
+
# @note Not recommended as it requires an additional scope on the OAuth token
|
|
21
|
+
def self.find_by_email(email)
|
|
22
|
+
from_response(sense.client.users_lookupByEmail(email))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Find a Slack User based on the text provided by Slack from a mention of that User
|
|
26
|
+
# @param mention_string [String]
|
|
27
|
+
# @return [User]
|
|
28
|
+
def self.from_mention(mention_string)
|
|
29
|
+
from_response(sense.client.users_info(user: mention_string[1..]))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Provide a Slask User based on a Web API response
|
|
33
|
+
# @param response [Hash] The response from a Web API request
|
|
34
|
+
# @return [User]
|
|
35
|
+
def self.from_response(response)
|
|
36
|
+
raise "Failed Request" unless response && response["ok"]
|
|
37
|
+
|
|
38
|
+
new(data: response["user"])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Allows easy access to the Sense class
|
|
42
|
+
# @return [Class]
|
|
43
|
+
def self.sense
|
|
44
|
+
::Waylon::Senses::Slack
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A convenient way use the Slack API to figure out the bot's own info
|
|
48
|
+
# @return [User]
|
|
49
|
+
def self.whoami
|
|
50
|
+
response = sense.cache("whoami") { sense.client.auth_test }
|
|
51
|
+
new(response["user_id"])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize(id = nil, data: {})
|
|
55
|
+
raise "Must provide ID or details" unless id || !data.empty?
|
|
56
|
+
|
|
57
|
+
@id = id || data["id"]
|
|
58
|
+
@data = data
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Provides lazy, cached access to the User's internal details
|
|
62
|
+
# @return [Hash]
|
|
63
|
+
def data
|
|
64
|
+
if !@data || @data.empty?
|
|
65
|
+
sense.cache("users.#{id}") do
|
|
66
|
+
raw_data = sense.client.users_info(user: id)
|
|
67
|
+
@data = raw_data["user"]
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
@data
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Is this user a bot?
|
|
75
|
+
# @return [Boolean]
|
|
76
|
+
def bot?
|
|
77
|
+
data["is_bot"]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Posts a direct (private) message to a user
|
|
81
|
+
# @param text [String] Message text or fallback text for blocks
|
|
82
|
+
# @param attachments [Array<Hash>] Old-style message attachments
|
|
83
|
+
# @param blocks [Array<Hash>] New-style block method of sending complex messages
|
|
84
|
+
# @param thread [Integer] The message timestamp for the thread id
|
|
85
|
+
# @return [void]
|
|
86
|
+
def dm(text: nil, attachments: nil, blocks: nil, thread: nil)
|
|
87
|
+
options = { channel: id } # Sends a message to the user's ID
|
|
88
|
+
options[:text] = text if text
|
|
89
|
+
options[:attachments] = attachments if attachments
|
|
90
|
+
options[:blocks] = blocks if blocks
|
|
91
|
+
options[:thread_ts] = thread if thread
|
|
92
|
+
sense.client.chat_postMessage(options)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# The User's email address
|
|
96
|
+
# @return [String]
|
|
97
|
+
def email
|
|
98
|
+
profile["email"]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The User's username/chat handle
|
|
102
|
+
# @return [String]
|
|
103
|
+
def handle
|
|
104
|
+
bot? ? data["real_name"] : data["name"]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# The User's profile information
|
|
108
|
+
# @return [Slack::Messages::Message]
|
|
109
|
+
def profile
|
|
110
|
+
data["profile"]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Easy access to the Sense class
|
|
114
|
+
# @return [Class]
|
|
115
|
+
def sense
|
|
116
|
+
self.class.sense
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The User's current Status (comes from cache so can be outdated)
|
|
120
|
+
# @return [String]
|
|
121
|
+
def status
|
|
122
|
+
profile["status_text"]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The User's Slack team ID
|
|
126
|
+
# @return [String]
|
|
127
|
+
def team
|
|
128
|
+
data["team_id"]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# The User's time zone
|
|
132
|
+
# @return [String]
|
|
133
|
+
def tz
|
|
134
|
+
data["tz"]
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
data/lib/waylon/slack.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "waylon/core"
|
|
4
|
+
require "slack-ruby-client"
|
|
5
|
+
|
|
6
|
+
::Slack.configure do |conf|
|
|
7
|
+
conf.token = ENV["SLACK_OAUTH_TOKEN"]
|
|
8
|
+
conf.logger = Waylon::Logger.logger
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
::Slack::Web::Client.configure do |conf|
|
|
12
|
+
conf.user_agent = "Waylon/#{Waylon::Core::VERSION}"
|
|
13
|
+
conf.logger = Waylon::Logger.logger
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
require_relative "slack/version"
|
|
17
|
+
require_relative "slack/user"
|
|
18
|
+
require_relative "slack/channel"
|
|
19
|
+
require_relative "slack/message"
|
|
20
|
+
require_relative "senses/slack"
|
|
21
|
+
require_relative "webhooks/slack"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Waylon
|
|
4
|
+
module Webhooks
|
|
5
|
+
# Webhook for the Slack Sense for Waylon
|
|
6
|
+
class Slack < Waylon::Webhook
|
|
7
|
+
before do
|
|
8
|
+
content_type "application/json"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
post "/" do
|
|
12
|
+
request.body.rewind
|
|
13
|
+
verify(request) unless ENV.fetch("LOCAL_MODE", false)
|
|
14
|
+
if @parsed_body.is_a?(Hash) && @parsed_body[:type] == "url_verification"
|
|
15
|
+
{ challenge: @parsed_body[:challenge] }.to_json
|
|
16
|
+
else
|
|
17
|
+
enqueue(@parsed_body)
|
|
18
|
+
{ status: :ok }.to_json
|
|
19
|
+
end
|
|
20
|
+
rescue ::Slack::Events::Request::InvalidSignature, ::Slack::Events::Request::TimestampExpired
|
|
21
|
+
halt(403, { error: "Unable to authenticate request" }.to_json)
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
log("Encountered #{e.message}", :warn)
|
|
24
|
+
halt(422, { error: "Unprocessable entity: #{e.message}" }.to_json)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
options "/" do
|
|
28
|
+
halt 200
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Used to verify incoming Slack requests
|
|
32
|
+
def verify(incoming_request)
|
|
33
|
+
slack_request = ::Slack::Events::Request.new(incoming_request)
|
|
34
|
+
slack_request.verify!
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Automatically informs Waylon about this Webhook
|
|
38
|
+
Waylon::WebhookRegistry.register(:slack, self)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/scripts/test.sh
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/waylon/slack/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "waylon-slack"
|
|
7
|
+
spec.version = Waylon::Slack::VERSION
|
|
8
|
+
spec.authors = ["Jonathan Gnagy"]
|
|
9
|
+
spec.email = ["jonathan.gnagy@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Slack Sense for the Waylon Bot Framework"
|
|
12
|
+
spec.description = "Full Slack integration for the Waylon Bot Framework"
|
|
13
|
+
spec.homepage = "https://github.com/jgnagy/waylon-slack"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
spec.required_ruby_version = "~> 3.0"
|
|
16
|
+
|
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/jgnagy/waylon-slack"
|
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/jgnagy/waylon-slack/blob/main/CHANGELOG.md"
|
|
20
|
+
|
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
25
|
+
end
|
|
26
|
+
spec.bindir = "exe"
|
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
28
|
+
spec.require_paths = ["lib"]
|
|
29
|
+
|
|
30
|
+
spec.add_dependency "slack-ruby-client", "~> 1.0"
|
|
31
|
+
spec.add_dependency "waylon-core", "~> 0.1"
|
|
32
|
+
|
|
33
|
+
spec.add_development_dependency "bundler", "~> 2.3"
|
|
34
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
35
|
+
spec.add_development_dependency "rspec", "~> 3.10"
|
|
36
|
+
spec.add_development_dependency "rubocop", "~> 1.23"
|
|
37
|
+
spec.add_development_dependency "rubocop-rake", "~> 0.6"
|
|
38
|
+
spec.add_development_dependency "rubocop-rspec", "~> 2.6"
|
|
39
|
+
spec.add_development_dependency "simplecov", "~> 0.21"
|
|
40
|
+
spec.add_development_dependency "yard", "~> 0.9", ">= 0.9.27"
|
|
41
|
+
|
|
42
|
+
# For more information and examples about making a new gem, checkout our
|
|
43
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
|
44
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: waylon-slack
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Jonathan Gnagy
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-02-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: slack-ruby-client
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
type: :runtime
|
|
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: waylon-core
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0.1'
|
|
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.3'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '2.3'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '13.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '13.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.10'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '3.10'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rubocop
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.23'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.23'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rubocop-rake
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - "~>"
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0.6'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - "~>"
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0.6'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rubocop-rspec
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - "~>"
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '2.6'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - "~>"
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '2.6'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: simplecov
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - "~>"
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0.21'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - "~>"
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0.21'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: yard
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - "~>"
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0.9'
|
|
146
|
+
- - ">="
|
|
147
|
+
- !ruby/object:Gem::Version
|
|
148
|
+
version: 0.9.27
|
|
149
|
+
type: :development
|
|
150
|
+
prerelease: false
|
|
151
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
152
|
+
requirements:
|
|
153
|
+
- - "~>"
|
|
154
|
+
- !ruby/object:Gem::Version
|
|
155
|
+
version: '0.9'
|
|
156
|
+
- - ">="
|
|
157
|
+
- !ruby/object:Gem::Version
|
|
158
|
+
version: 0.9.27
|
|
159
|
+
description: Full Slack integration for the Waylon Bot Framework
|
|
160
|
+
email:
|
|
161
|
+
- jonathan.gnagy@gmail.com
|
|
162
|
+
executables: []
|
|
163
|
+
extensions: []
|
|
164
|
+
extra_rdoc_files: []
|
|
165
|
+
files:
|
|
166
|
+
- ".gitignore"
|
|
167
|
+
- ".roxanne.yml"
|
|
168
|
+
- ".rspec"
|
|
169
|
+
- ".rubocop.yml"
|
|
170
|
+
- ".ruby-version"
|
|
171
|
+
- CHANGELOG.md
|
|
172
|
+
- Gemfile
|
|
173
|
+
- Gemfile.lock
|
|
174
|
+
- LICENSE.txt
|
|
175
|
+
- README.md
|
|
176
|
+
- Rakefile
|
|
177
|
+
- bin/console
|
|
178
|
+
- bin/setup
|
|
179
|
+
- lib/waylon/senses/slack.rb
|
|
180
|
+
- lib/waylon/slack.rb
|
|
181
|
+
- lib/waylon/slack/channel.rb
|
|
182
|
+
- lib/waylon/slack/message.rb
|
|
183
|
+
- lib/waylon/slack/user.rb
|
|
184
|
+
- lib/waylon/slack/version.rb
|
|
185
|
+
- lib/waylon/webhooks/slack.rb
|
|
186
|
+
- scripts/test.sh
|
|
187
|
+
- waylon-slack.gemspec
|
|
188
|
+
homepage: https://github.com/jgnagy/waylon-slack
|
|
189
|
+
licenses:
|
|
190
|
+
- MIT
|
|
191
|
+
metadata:
|
|
192
|
+
homepage_uri: https://github.com/jgnagy/waylon-slack
|
|
193
|
+
source_code_uri: https://github.com/jgnagy/waylon-slack
|
|
194
|
+
changelog_uri: https://github.com/jgnagy/waylon-slack/blob/main/CHANGELOG.md
|
|
195
|
+
post_install_message:
|
|
196
|
+
rdoc_options: []
|
|
197
|
+
require_paths:
|
|
198
|
+
- lib
|
|
199
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
|
+
requirements:
|
|
201
|
+
- - "~>"
|
|
202
|
+
- !ruby/object:Gem::Version
|
|
203
|
+
version: '3.0'
|
|
204
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
|
+
requirements:
|
|
206
|
+
- - ">="
|
|
207
|
+
- !ruby/object:Gem::Version
|
|
208
|
+
version: '0'
|
|
209
|
+
requirements: []
|
|
210
|
+
rubygems_version: 3.3.3
|
|
211
|
+
signing_key:
|
|
212
|
+
specification_version: 4
|
|
213
|
+
summary: Slack Sense for the Waylon Bot Framework
|
|
214
|
+
test_files: []
|