slack_bot-events 0.4.2 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26e9e0a0edd93455cbc0cff5043f9a5e9a93a1bdb1a6ace9107df71cabd01856
4
- data.tar.gz: 4eb9d15cc14f868f8e1f8bab0ccaaca876e256899a84d2b53cf5939df2496205
3
+ metadata.gz: 4db0e3fd554d51024ebf0d4c1b6d63856ccd4d72c6c83901d0542926db70c2ac
4
+ data.tar.gz: 154cc348c600534eda58f68d3b66bcae1ef924f73964f9787e98fc18882c363c
5
5
  SHA512:
6
- metadata.gz: 44891a0644cd45f3b8c22a8256f48e509933b7a35a4504f9ac046d8b761fcd5a8bee09d2ecefa89d85616953e7a70e18b7fc84a1a5435a97f4d5dc4eb2fbcef2
7
- data.tar.gz: 8af558314cab90d174ac1555f590eaeaab3bd87530928da99eea571486c962ef2926f41d82ff030107fc4ad4d615a7835030579b5497970bfd65a3653ec8991d
6
+ metadata.gz: 6dca807680fbd02f2c8ce30ba2a019c03e5acf47373fbc73133ff61fcd981973f3bee7b1257f014d1b1d05bb46b1af92a8e07336186a3a20118a851ffe8713f3
7
+ data.tar.gz: 52449fa2a7f111fd5dc06fd605995bac87994822dda2e483d0ab012a2df330d25bd22e42692b0428f6967340a99b1fa4e71a5c0dccb4475abcd4b124b15d3ba6
data/.gitignore CHANGED
@@ -18,3 +18,7 @@
18
18
  # generated gems
19
19
  *.gem
20
20
  .env
21
+
22
+ # Example Gemfiles
23
+ /examples/**/Gemfile.lock
24
+
data/README.md CHANGED
@@ -0,0 +1,125 @@
1
+ # SlackBot Events
2
+
3
+ Welcome to SlackBot Events Gem! This gem provides a seamless way to hook into your paid Slack Workspace's Events API and run complex or simple automation. You can run automations based on events like: Message Deleted, Reactions Added, Reactions Removed, Message sent to channel, Message sent to Thread, User added to Channel, and so many more. [View Full list of Events Here](https://api.slack.com/events).
4
+
5
+ SlackBot Events provides the foundational tooling to run a SlackBot, automate Jira tickets based on Events, Customize and Automate AI responses to messages, Automate tagging relavant groups in threads, track time to respond and time to resolve threads, and so much more.
6
+
7
+ SlackBot Events Gem connects directly into your paid Slack workspace by utilizing websockets. Websockets provides a resilient, safe, and reliable connection to retreive events without the need to expose a public endpoint for Slack Events.
8
+
9
+ ## Installation
10
+
11
+ ```ruby
12
+ source 'http://rubygems.org'
13
+ gem 'slack_bot-events'
14
+ ```
15
+
16
+ [Example Use Cases](/examples)
17
+
18
+ ## SlackBot Events inspiration
19
+
20
+ There already exists No to Low code Slack workflow Engines like Zappier. However, these workflows are often slow and buggy. They cost money based on usage. Additionally, creating complex workflows with low code is a bit messy.
21
+
22
+ SlackBot Events provides a free alternative to exposing events from your paid Slack Workspace.
23
+
24
+ ## Configuration
25
+
26
+ ### Required Slack Bot Configuration
27
+ This bit is rather boring. Check out the [Boring Slack Configuration Setup](/boring_slack_configuration.md). You will need the App Level Token for the next step
28
+
29
+ ### Required ENV variables:
30
+ `SLACK_SOCKET_TOKEN` is the only ENV variable that is required for this Gem. Using the token that was saved from an earlier step, set the token to the ENV variable `SLACK_SOCKET_TOKEN`. There is an alternate assignment option below.
31
+
32
+ ### Configuration Options:
33
+ ``` ruby
34
+ SlackBot::Events.configure do |config|
35
+ # This token is needed to retreive a WebSocket connection to the Events API
36
+ # Default value: ENV["SLACK_SOCKET_TOKEN"]
37
+ config.client_socket_token = "AppLevelToken"
38
+
39
+ # By default, SlackBot::Events will print out a TLDR of every events message that comes through
40
+ # Default value: true
41
+ config.print_tldr = true
42
+
43
+ # By default, SlackBot::Events will acknowledge at the end of the middleware chain after it passes the message to the event listener. Available options:
44
+ # => on_complete: Acknowledge after listener has completed on failure and on success
45
+ # => on_success: Acknowledge only on succesful listener events (Use with caution)
46
+ # => on_receive: Acknowledge at the beginning of the middleware chain before it gets to listener events
47
+ # Default value: :on_complete
48
+ config.envelope_acknowledge = :on_complete
49
+
50
+ # By default, this gem outputs to STDOUT. You can set your own logger ot set it to the Rails logger if desired
51
+ # Default value: Logger.new(STDOUT)
52
+ config.logger = Rails.logger
53
+ end
54
+ ```
55
+
56
+ ### Event Listeners:
57
+ Event Listeners is where the configurable power comes in. Listeners are custom code that gets run on specific event_api actions as defined in [Slack Event Types](https://api.slack.com/events).
58
+
59
+ There can be at most 1 configured listener listeing to any given event type.
60
+
61
+ To Register a new listener:
62
+ ```ruby
63
+ SlackBot::Events.register_listener(name: "event_type_name", handler: handler_object)
64
+ SlackBot::Events.register_listener(name: "event_type_name_2", handler: handler_object2, on_success: on_success_proc)
65
+ SlackBot::Events.register_listener(name: "event_type_name_3", handler: handler_object3, on_failure: on_failure_proc)
66
+
67
+ ### Or via the config
68
+
69
+ SlackBot::Events.configure do |c|
70
+ c.register_listener(name: "event_type_name4", handler: handler_object4)
71
+ end
72
+
73
+ ```
74
+
75
+ #### Handler
76
+ The Handler argument must be an object that responds to `call` with KWargs `schema` and `raw_data`.
77
+
78
+ #### On Failure (Optional Argument)
79
+ The `on_failure` argument must be an object that resoonds to `call` with 2 arguments. The first argument will be the converted schema if available. The second argument will be the error that caused the Handler to fail
80
+
81
+ #### On Success (Optional Argument)
82
+ The `on_success` argument must be an object that resoonds to `call` with 1 argument. The argument will be the converted schema if available.
83
+
84
+ [Example with Basic Listeners](/examples/basic)
85
+
86
+ [Example with Multiple Listeners](/examples/multi_listener)
87
+
88
+ ## Middleware
89
+
90
+ Middlewares can help add additional observability into a unit of work. For the Websocket message type, you can add any number of middlewars via the Configure block.
91
+
92
+
93
+ ```ruby
94
+ SlackBot::Events.configure do |c|
95
+ c.message_middleware.add(DataDogObeservabilityMiddleware)
96
+ end
97
+ ```
98
+
99
+ ## Known Restrictions
100
+ ### Limited number of events per hour per workspace
101
+ [Slack Events API](https://api.slack.com/apis/rate-limits#events) has a limit of 30,000 events sent per hour per workspace. When this limit is hit, Slack will send the message type `app_rate_limited`.
102
+
103
+ You can see an example of how this can get handled in the [EventTracer Middleware](lib/slack_bot/events/middleware/event_tracer.rb)
104
+
105
+
106
+ ### 10 open Sockets per App
107
+ A Slack app can have at most 10 open sockets to Slack simultaneously. On WebSocket aquisition, it will first send the `open` type. This will reveal how many open connections there currently are.
108
+
109
+ In regards to SlackBot::Events, this limitation means that you can have at most 10 instances of SlackBot::Events running per bod.
110
+
111
+ For more information, Visit [Using Multiple Connections](https://api.slack.com/apis/socket-mode#connections) on Slack API page
112
+
113
+ ### Message Acknowledgment
114
+ `SlackBot::Events` by default will handle message acknowledgment on your behalf. This can get taken care of before or after the handler is executed.
115
+
116
+ Slack expects an Aknowledgment within 3 seconds. If your Middleware combined with the handler execution takes longer than the expected 3 seconds, Slack will immediately attempt to send the same envelope package again. This will cause duplication in your application.
117
+
118
+ Retry attempts can occur on any open socket.
119
+ - Attempt 3 Seconds after no response
120
+ - Attempt 60 Seconds after no response
121
+ - Attempt 5 minutes after no response
122
+
123
+ Over the course of an hour, if you fail to send acknowledgement before the first retry, you will be rate limited and your app cordoned off
124
+
125
+ Ideally, your listener executes quickly and returns. This means your execution is quick or you ship the data off to an async job like Sidekiq or spawn a new thread.
@@ -0,0 +1,39 @@
1
+ # Slack Bot Requirements:
2
+ SlackBot::Events gem utilizes the [Slack Events API](https://api.slack.com/apis/events-api) with Socket Mode. Events are pushed directly to any socket that is open and accepting requets.
3
+
4
+ **Warning** This Slack Bot requires the ability to connect to the events API using a WebSocket. This is only available in paid workspaces and development workspaces. Not available in free Workspaces.
5
+
6
+ You will need:
7
+ ## Slack App created
8
+ To create a Slack App, checkout out [Slacks App Quickstart Guide](https://api.slack.com/quickstart). If you are familiar with Slack App Creation, you can go directly to the [Slack Apps Homepage](https://api.slack.com/apps)
9
+
10
+ ## Slack App Socket Enable
11
+ SlackBot::Events is based on Socket mode. Socket mode must get enabled before you can subscribe to events without a `Request URL` public endpoint in your application.
12
+
13
+ Navigate to your app and select the `Socket Mode` tab on the left. Ensure the switch to `Enable Socket Mode` is toggled on before proceeding
14
+
15
+ You will be asked to create a new token. Name it whatever you would like. This will create a new token for you.
16
+
17
+ You will **NEED** to retain the token for this Gem. (But don't worry, you can create a new one using with [Slack App Level tokens](https://api.slack.com/concepts/token-types#app-level) later as well)
18
+
19
+ ## Slack App Events received
20
+ Every workpace has a limitation on the number of events the entire workspace can receive. This means that you should choose carefully which events your App can recieve.
21
+
22
+ Navigate to your app and select the `Event Subscriptions` tab on the left.
23
+
24
+ Carefully pick the events to subscribe to under the `Subscribe to bot events`.
25
+
26
+ A good place to start is by subscribing to the following events:
27
+ ```
28
+ message.channels
29
+ reaction_added
30
+ reaction_removed
31
+ ```
32
+ *Note*: This will automatically add the correct OAuth permissions to the bot user. When you want to add additional subscriptions, come back here.
33
+
34
+ ## Install the Slack app to your workspace
35
+ Navigate to your app and select the `Install App` tab on the left. Click on `Install to Workspace`. In some cases, you may need to get Workspace Admin approval before the app becomes available in the workspace.
36
+
37
+ ## Add the Bot to specific Channels
38
+ Once the App is intalled into the workspace, invite the new bot to channels. Once the bot is in a channel, it will have the ability to send subscribed events to SlackBot::Events gem
39
+
data/changelog.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## 0.4.4
4
+ - Introduce Changelog
5
+ - Introduce Example workflows
6
+ - Introduce Readme
7
+
@@ -0,0 +1,30 @@
1
+ # Examples
2
+ This is the root directory for examples. Provided you have the correct `SLACK_SOCKET_TOKEN` ENV variable created, these examples will work with your workspace.
3
+
4
+ ## [Basic Example](/examples/basic)
5
+ ```bash
6
+ cd basic
7
+ bundle i
8
+ ruby main.rb
9
+ ```
10
+
11
+ ## [Middleware Example](/examples/middleware)
12
+ ```bash
13
+ cd middleware
14
+ bundle i
15
+ ruby main.rb
16
+ ```
17
+
18
+ ## [Middleware No Yield Example](/examples/middleware_no_yield)
19
+ ```bash
20
+ cd middleware_no_yield
21
+ bundle i
22
+ ruby main.rb
23
+ ```
24
+
25
+ ## [Multi Listener Example](/examples/multi_listener)
26
+ ```bash
27
+ cd multi_listener
28
+ bundle i
29
+ ruby main.rb
30
+ ```
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source 'http://rubygems.org'
3
+
4
+ gem 'slack_bot-events', path: '../..'
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require "slack_bot-events"
6
+ require_relative "message_listener.rb"
7
+
8
+ # The `name` must match a Event subscription type found
9
+ # https://api.slack.com/events
10
+
11
+ # Register a listener for all reaction removed events
12
+ SlackBot::Events.register_listener(
13
+ name: "message",
14
+ # Handler must respond to call with schema: and raw_data: KWARGS
15
+ handler: MessageListener,
16
+ # on_success must respond to call with 1 arg of schema
17
+ on_success: MessageListener.method(:on_success),
18
+ # on_failure must respond to call with 2 args of schema and error
19
+ on_failure: MessageListener.method(:on_failure),
20
+ )
21
+
22
+
23
+ # This is a blocking way to start the Websocket client
24
+ # EventMachine is used to keep the socket open
25
+ # The process responds to all SigQuit/Term/Kill events as expected
26
+ SlackBot::Events::Client.new.start!
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessageListener
4
+ def self.call(schema:, raw_data:)
5
+ # Do some cool(quick returning) things here
6
+ # schema.payload.event will be a SlackBot::Events::Schemas::Type::Message
7
+ puts "Heya! I found a message! #{schema.payload.event.text}"
8
+ raise StandardError, "I Randomly decided to Barf" if rand > 0.9
9
+ end
10
+
11
+ def self.on_success(schema)
12
+ # Send a metric maybe?
13
+ # Or a Log Message
14
+ puts "Congrats! You executed it succesfully"
15
+ end
16
+
17
+ def self.on_failure(schema, error)
18
+ # Send job to sidekiq to try again?
19
+ # Or send a log message; but make sure to do it quick
20
+ puts "Yikes, You died a misreable death"
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source 'http://rubygems.org'
3
+
4
+ gem 'slack_bot-events', path: '../..'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require "slack_bot-events"
6
+ require_relative "message_listener.rb"
7
+ require_relative "meddling_message_middleware.rb"
8
+
9
+ SlackBot::Events.configure do |c|
10
+ c.message_middleware.add(MeddlingMessageMiddleware)
11
+ c.print_tldr = false
12
+ end
13
+
14
+ SlackBot::Events.register_listener(
15
+ name: "message",
16
+ handler: MessageListener,
17
+ on_success: MessageListener.method(:on_success),
18
+ on_failure: MessageListener.method(:on_failure),
19
+ )
20
+
21
+ SlackBot::Events::Client.new.start!
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MeddlingMessageMiddleware
4
+ # Message middlewares have the following KWargs available to them
5
+
6
+ # parsed_data => Raw JSON of schema
7
+ # schema => Schematized objected of data. schema.payload.event has much of the data you will want to mess with
8
+ # socket_event => The socket_event object that was given -- Unlikely you need to play with this
9
+ # listener => { handler:, on_success:, on_failure: } if the listener exists for the schema type; will be nil otherwise
10
+ # type => will be one of :message, :ope, :close
11
+ # websocket => The websocket client attached to Slack
12
+ def call(parsed_data:, schema:, socket_event:, listener:, type:, websocket:)
13
+ ###
14
+ # Code to execute BEFORE the listener handler is called
15
+ ###
16
+ puts "I get called BEFORE the Handler gets executed"
17
+
18
+
19
+
20
+ # The middleware chain is broken if yield is not called
21
+ # If provided, the listener handler will not get called either
22
+ # There may be occasions when you want to halt the middleware chain, but
23
+ # for the most part, ensure you always yield
24
+ yield
25
+
26
+
27
+
28
+ ###
29
+ # Code to execute AFTER the listener handler is called
30
+ ###
31
+ puts "I get called AFTER the Handler gets executed"
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessageListener
4
+ def self.call(schema:, raw_data:)
5
+ # Do some cool(quick returning) things here
6
+ # schema.payload.event will be a SlackBot::Events::Schemas::Type::Message
7
+ puts "Heya! I found a message! #{schema.payload.event.text}"
8
+ raise StandardError, "I Randomly decided to Barf" if rand > 0.9
9
+ end
10
+
11
+ def self.on_success(schema)
12
+ # Send a metric maybe?
13
+ # Or a Log Message
14
+ puts "Congrats! You executed it succesfully"
15
+ end
16
+
17
+ def self.on_failure(schema, error)
18
+ # Send job to sidekiq to try again?
19
+ # Or send a log message; but make sure to do it quick
20
+ puts "Yikes, You died a misreable death"
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source 'http://rubygems.org'
3
+
4
+ gem 'slack_bot-events', path: '../..'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require "slack_bot-events"
6
+ require_relative "message_listener.rb"
7
+ require_relative "meddling_message_middleware.rb"
8
+
9
+ SlackBot::Events.configure do |c|
10
+ c.message_middleware.add(MeddlingMessageMiddleware)
11
+ c.print_tldr = false
12
+ end
13
+
14
+ SlackBot::Events.register_listener(
15
+ name: "message",
16
+ handler: MessageListener,
17
+ on_success: MessageListener.method(:on_success),
18
+ on_failure: MessageListener.method(:on_failure),
19
+ )
20
+
21
+ SlackBot::Events::Client.new.start!
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MeddlingMessageMiddleware
4
+ # Message middlewares have the following KWargs available to them
5
+ def call(parsed_data:, schema:, socket_event:, listener:, type:, websocket:)
6
+ puts "I get called BEFORE the Handler gets executed"
7
+
8
+ puts "But wait! There is no yield, Message Handler never gets called"
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessageListener
4
+
5
+ def self.call(schema:, raw_data:)
6
+ raise "Its okay, It never actually makes it here"
7
+ end
8
+
9
+ def self.on_success(schema)
10
+ # Send a metric maybe?
11
+ # Or a Log Message
12
+ puts "I did still make it to the on_success handler"
13
+ end
14
+
15
+ def self.on_failure(schema, error)
16
+ # Send job to sidekiq to try again?
17
+ # Or send a log message; but make sure to do it quick
18
+ puts "Yikes, You died a misreable death"
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source 'http://rubygems.org'
3
+
4
+ gem 'slack_bot-events', path: '../..'
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ require "slack_bot-events"
6
+ require_relative "message_listener.rb"
7
+ require_relative "reaction_removed_listener.rb"
8
+
9
+ # The `name` must match a Event subscription type found
10
+ # https://api.slack.com/events
11
+
12
+ ###
13
+ # In the examples below, on_success and on_failure are methods
14
+ # They can be left empty or a be a proc
15
+ ###
16
+
17
+ # Register a listener for all reaction removed events
18
+ SlackBot::Events.register_listener(
19
+ name: "reaction_removed",
20
+ # Handler must respond to call with schema: and raw_data: KWARGS
21
+ handler: ReactionRemovedListener,
22
+ # on_success must respond to call with 1 arg of schema
23
+ on_success: ReactionRemovedListener.method(:on_success),
24
+ # on_failure must respond to call with 2 args of schema and error
25
+ on_failure: ReactionRemovedListener.method(:on_failure),
26
+ )
27
+
28
+ # Register a listener for all messages sent to a channel the bot is added to
29
+ SlackBot::Events.register_listener(
30
+ name: "message",
31
+ # Handler must respond to call with schema: and raw_data: KWARGS
32
+ handler: MessageListener,
33
+ # on_success must respond to call with 1 arg of schema
34
+ on_success: MessageListener.method(:on_success),
35
+ # on_failure must respond to call with 2 args of schema and error
36
+ on_failure: MessageListener.method(:on_failure),
37
+ )
38
+
39
+ # This is a blocking way to start the Websocket client
40
+ # EventMachine is used to keep the socket open
41
+ # The process responds to all SigQuit/Term/Kill events as expected
42
+ SlackBot::Events::Client.new.start!
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MessageListener
4
+
5
+ def self.call(schema:, raw_data:)
6
+ # Do some cool(quick returning) things here
7
+ # schema.payload.event will be a SlackBot::Events::Schemas::Type::Message
8
+ puts "Heya! I found a message! #{schema.payload.event.text}"
9
+ raise StandardError, "I Randomly decided to Barf" if rand > 0.9
10
+ end
11
+
12
+ def self.on_success(schema)
13
+ # Send a metric maybe?
14
+ # Or a Log Message
15
+ puts "Congrats! You executed it succesfully"
16
+ end
17
+
18
+ def self.on_failure(schema, error)
19
+ # Send job to sidekiq to try again?
20
+ # Or send a log message; but make sure to do it quick
21
+ puts "Yikes, You died a misreable death"
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ReactionRemovedListener
4
+
5
+ def self.call(schema:, raw_data:)
6
+ # Do some cool(quick returning) things here
7
+ # schema.payload.event will be a SlackBot::Events::Schemas::Type::ReactionModified
8
+ puts "Heya! I found a reaction! #{schema.payload.event.reaction}"
9
+ raise StandardError, "I Randomly decided to Barf" if rand > 0.9
10
+ end
11
+
12
+ def self.on_success(schema)
13
+ # Send a metric maybe?
14
+ # Or a Log Message
15
+ puts "Congrats! You executed it succesfully"
16
+ end
17
+
18
+ def self.on_failure(schema, error)
19
+ # Send job to sidekiq to try again?
20
+ # Or send a log message; but make sure to do it quick
21
+ puts "Yikes, You died a misreable death"
22
+ end
23
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module SlackBot
4
4
  module Events
5
- VERSION = "0.4.2"
5
+ VERSION = "0.4.4"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slack_bot-events
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Taylor
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-21 00:00:00.000000000 Z
11
+ date: 2024-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -129,7 +129,25 @@ files:
129
129
  - bin/console
130
130
  - bin/setup
131
131
  - bin/start
132
+ - boring_slack_configuration.md
133
+ - changelog.md
132
134
  - docker-compose.yml
135
+ - examples/README.md
136
+ - examples/basic/Gemfile
137
+ - examples/basic/main.rb
138
+ - examples/basic/message_listener.rb
139
+ - examples/middleware/Gemfile
140
+ - examples/middleware/main.rb
141
+ - examples/middleware/meddling_message_middleware.rb
142
+ - examples/middleware/message_listener.rb
143
+ - examples/middleware_no_yield/Gemfile
144
+ - examples/middleware_no_yield/main.rb
145
+ - examples/middleware_no_yield/meddling_message_middleware.rb
146
+ - examples/middleware_no_yield/message_listener.rb
147
+ - examples/multi_listener/Gemfile
148
+ - examples/multi_listener/main.rb
149
+ - examples/multi_listener/message_listener.rb
150
+ - examples/multi_listener/reaction_removed_listener.rb
133
151
  - lib/slack_bot-events.rb
134
152
  - lib/slack_bot/events.rb
135
153
  - lib/slack_bot/events/client.rb