slack_bot-events 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/slack_bot/events/client.rb +32 -42
- data/lib/slack_bot/events/configuration.rb +12 -0
- data/lib/slack_bot/events/middleware/chain.rb +147 -0
- data/lib/slack_bot/events/middleware/event_tracer.rb +40 -0
- data/lib/slack_bot/events/schemas/socket_payload.rb +1 -1
- data/lib/slack_bot/events/schemas/type/message.rb +5 -1
- data/lib/slack_bot/events/schemas/type/reaction_modified.rb +1 -1
- data/lib/slack_bot/events/schematize.rb +27 -0
- data/lib/slack_bot/events/version.rb +1 -1
- data/lib/slack_bot/events.rb +15 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5be1bf5ab14cf6022e8a31b32015e328bd5b1939d928415bb06e610dc3e091e
|
4
|
+
data.tar.gz: e04f3734b913e8514f2ed50eb3f0905906013321f3fa6c26dc3992a7a5136ae4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f59e41eeb158988a8b02ba6b445b1b7ba8009e96c2ec404b313b614e9aa86d5e346518c9ad7e824b49dd29dfc4b04e9f84a827edde5698c07a3d1325b356814
|
7
|
+
data.tar.gz: 4e200610a557c49256745577bca3811e9830609a29009a6f4b09cc1251f972118d013c92a2a9d933e2df3391592c582112abd6911638115987bcdecf7a07fd15
|
data/Gemfile.lock
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
require "eventmachine"
|
4
4
|
require "faraday"
|
5
5
|
require "faye/websocket"
|
6
|
-
require "
|
6
|
+
require "slack_bot/events/schematize"
|
7
7
|
|
8
8
|
module SlackBot
|
9
9
|
module Events
|
@@ -11,64 +11,54 @@ module SlackBot
|
|
11
11
|
BASE_API = "https://slack.com/api"
|
12
12
|
|
13
13
|
def start!
|
14
|
-
|
15
|
-
websocket.on :open do |
|
16
|
-
|
14
|
+
EventMachine.run do
|
15
|
+
websocket.on :open do |socket_event|
|
16
|
+
process(type: :open, socket_event: socket_event) { |**| }
|
17
17
|
end
|
18
18
|
|
19
|
-
websocket.on :message do |
|
20
|
-
|
21
|
-
parsed_data = JSON.parse(event.data)
|
19
|
+
websocket.on :message do |socket_event|
|
20
|
+
process_message(socket_event: socket_event) do |parsed_data:, schema: nil|
|
22
21
|
case parsed_data["type"]
|
23
22
|
when "events_api"
|
24
|
-
events_api(parsed_data)
|
25
|
-
when "app_rate_limited"
|
26
|
-
# https://api.slack.com/apis/rate-limits#events
|
27
|
-
# Total allowed workspace events are 30,000 per hour
|
28
|
-
# This message type is received once you have gone beyond that
|
29
|
-
params = {
|
30
|
-
minute_rate_limited: parsed_data["minute_rate_limited"],
|
31
|
-
team_id: parsed_data["team_id"],
|
32
|
-
api_app_id: parsed_data["api_app_id"],
|
33
|
-
}
|
34
|
-
event_tracer("message:app_rate_limited", **params)
|
35
|
-
when "hello"
|
36
|
-
params = {
|
37
|
-
num_connections: parsed_data["num_connections"],
|
38
|
-
debug_info_host: parsed_data["debug_info"]["host"],
|
39
|
-
debug_info_connect_time: parsed_data["debug_info"]["approximate_connection_time"],
|
40
|
-
}
|
41
|
-
event_tracer("message:hello", **params)
|
23
|
+
events_api(schema: schema, parsed_data: parsed_data)
|
42
24
|
end
|
43
25
|
end
|
44
26
|
end
|
45
27
|
|
46
|
-
websocket.on :close do |
|
47
|
-
|
28
|
+
websocket.on :close do |socket_event|
|
29
|
+
process(type: :close, socket_event: socket_event) { |**| }
|
48
30
|
@websocket = nil
|
31
|
+
|
32
|
+
# The websocket is closed, explcitly stop the event machine to to end the loop and return to the parent
|
33
|
+
EventMachine.stop
|
49
34
|
end
|
50
35
|
end
|
51
36
|
end
|
52
37
|
|
53
|
-
def
|
54
|
-
|
55
|
-
Events.
|
56
|
-
|
57
|
-
|
58
|
-
|
38
|
+
def process_message(socket_event:)
|
39
|
+
schema_data = Schematize.call(data: socket_event.data)
|
40
|
+
SlackBot::Events.message_middleware.invoke_message(type: :message, socket_event: socket_event, **schema_data) do
|
41
|
+
yield(**schema_data) if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def process(type:, socket_event:)
|
46
|
+
SlackBot::Events.public_send(:"#{type}_middleware").invoke(type: type, socket_event: socket_event) do
|
47
|
+
yield if block_given?
|
59
48
|
end
|
60
|
-
websocket.send("#{{ envelope_id: schematized.envelope_id }.to_json}")
|
61
49
|
end
|
62
50
|
|
63
|
-
def
|
64
|
-
|
65
|
-
|
66
|
-
if block_given?
|
67
|
-
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
68
|
-
yield
|
69
|
-
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
70
|
-
Events.logger.info "[Event completed] [#{elapsed_time.round(3)}s] #{type} #{stringify}"
|
51
|
+
def events_api(schema:, parsed_data:)
|
52
|
+
if Events.config.print_tldr
|
53
|
+
Events.logger.info { schema.tldr }
|
71
54
|
end
|
55
|
+
|
56
|
+
object = Events.config.listeners[schema.type.to_sym]
|
57
|
+
if object
|
58
|
+
safe_handler(type: schema.type.to_sym, object: object, schema: schema, parsed_data: parsed_data)
|
59
|
+
end
|
60
|
+
|
61
|
+
websocket.send("#{{ envelope_id: schema.envelope_id }.to_json}")
|
72
62
|
end
|
73
63
|
|
74
64
|
private
|
@@ -2,17 +2,29 @@
|
|
2
2
|
|
3
3
|
require "class_composer"
|
4
4
|
require "logger"
|
5
|
+
require "slack_bot/events/middleware/chain"
|
5
6
|
|
6
7
|
module SlackBot
|
7
8
|
module Events
|
8
9
|
class Configuration
|
9
10
|
include ClassComposer::Generator
|
11
|
+
|
12
|
+
ALLOWED_ACKNOWLEDGE = [
|
13
|
+
DEFAULT_ACKNOWLEDGE = :on_complete,
|
14
|
+
:on_success,
|
15
|
+
:on_receive,
|
16
|
+
]
|
17
|
+
|
10
18
|
add_composer :client_id, allowed: String, default: ENV["SLACK_CLIENT_ID"]
|
11
19
|
add_composer :client_secret, allowed: String, default: ENV["SLACK_CLIENT_SECRET"]
|
12
20
|
add_composer :client_signing_secret, allowed: String, default: ENV["SLACK_SIGNING_SECRET"]
|
13
21
|
add_composer :client_socket_token, allowed: String, default: ENV["SLACK_SOCKET_TOKEN"]
|
14
22
|
add_composer :client_verification_token, allowed: String, default: ENV["SLACK_VERIFICATION_TOKEN"]
|
15
23
|
add_composer :print_tldr, allowed: [true.class, false.class], default: true
|
24
|
+
add_composer :message_middleware, allowed: Middleware::Chain, default: Middleware::Chain.new(type: :message)
|
25
|
+
add_composer :open_middleware, allowed: Middleware::Chain, default: Middleware::Chain.new(type: :open)
|
26
|
+
add_composer :close_middleware, allowed: Middleware::Chain, default: Middleware::Chain.new(type: :close)
|
27
|
+
add_composer :envelope_acknowledge, allowed: Symbol, default: DEFAULT_ACKNOWLEDGE, validator: ->(val) { ALLOWED_ACKNOWLEDGE.include?(val) }, invalid_message: ->(_) { "Must by a Symbol in #{ALLOWED_ACKNOWLEDGE}" }
|
16
28
|
|
17
29
|
def register_listener(name:, handler:, on_success: nil, on_failure: nil)
|
18
30
|
if event_handler = listeners[name.to_sym]
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "slack_bot/events/middleware/event_tracer"
|
4
|
+
|
5
|
+
####################
|
6
|
+
#
|
7
|
+
# Adapted from Sidekiq:
|
8
|
+
# https://github.com/sidekiq/sidekiq/blob/main/lib/sidekiq/middleware/chain.rb
|
9
|
+
#
|
10
|
+
####################
|
11
|
+
|
12
|
+
module SlackBot
|
13
|
+
module Events
|
14
|
+
module Middleware
|
15
|
+
class Chain
|
16
|
+
include Enumerable
|
17
|
+
|
18
|
+
attr_reader :type
|
19
|
+
|
20
|
+
DEFAULT_ENTRIES = {
|
21
|
+
message: [Middleware::EventTracer],
|
22
|
+
open: [Middleware::EventTracer],
|
23
|
+
close: [Middleware::EventTracer],
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(type:)
|
27
|
+
@type = type
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.default_entry(type)
|
31
|
+
DEFAULT_ENTRIES[type].map { Entry.new(_1) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def entries
|
35
|
+
@entries ||= self.class.default_entry(type)
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove(klass)
|
39
|
+
raise ArgumentError, "Unable to remove default Middleware #{klass}" if self.class.default_entry.map(:klass).include?(klass)
|
40
|
+
|
41
|
+
entries.delete_if { |entry| entry.klass == klass }
|
42
|
+
end
|
43
|
+
|
44
|
+
def add(klass, *args)
|
45
|
+
remove(klass)
|
46
|
+
entries << Entry.new(klass, args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def prepend(klass, *args)
|
50
|
+
remove(klass)
|
51
|
+
entries.insert(0, Entry.new(klass, args))
|
52
|
+
end
|
53
|
+
|
54
|
+
def insert_before(oldklass, newklass, *args)
|
55
|
+
i = entries.index { |entry| entry.klass == newklass }
|
56
|
+
new_entry = i.nil? ? Entry.new(klass, args) : entries.delete_at(i)
|
57
|
+
i = entries.index { |entry| entry.klass == oldklass } || 0
|
58
|
+
entries.insert(i, new_entry)
|
59
|
+
end
|
60
|
+
|
61
|
+
def insert_after(oldklass, newklass, *args)
|
62
|
+
i = entries.index { |entry| entry.klass == newklass }
|
63
|
+
new_entry = i.nil? ? Entry.new(klass, args) : entries.delete_at(i)
|
64
|
+
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
|
65
|
+
entries.insert(i + 1, new_entry)
|
66
|
+
end
|
67
|
+
|
68
|
+
def exists?(klass)
|
69
|
+
any? { |entry| entry.klass == klass }
|
70
|
+
end
|
71
|
+
|
72
|
+
def empty?
|
73
|
+
entries.nil? || entries.empty?
|
74
|
+
end
|
75
|
+
|
76
|
+
def retrieve
|
77
|
+
map(&:instance!)
|
78
|
+
end
|
79
|
+
|
80
|
+
def each(&block)
|
81
|
+
entries.each(&block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def clear
|
85
|
+
@entries = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def invoke_message(type:, socket_event:, parsed_data:, schema: nil)
|
89
|
+
return yield if empty?
|
90
|
+
|
91
|
+
chain = retrieve
|
92
|
+
traverse_chain = proc do
|
93
|
+
if chain.empty?
|
94
|
+
yield(yield: schema, parsed_data: parsed_data)
|
95
|
+
else
|
96
|
+
params = {
|
97
|
+
parsed_data: parsed_data,
|
98
|
+
schema: schema,
|
99
|
+
socket_event: socket_event,
|
100
|
+
type: type,
|
101
|
+
}
|
102
|
+
chain.shift.call(**params, &traverse_chain)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
traverse_chain.call
|
106
|
+
end
|
107
|
+
|
108
|
+
def invoke(type:, socket_event:)
|
109
|
+
return yield if empty?
|
110
|
+
|
111
|
+
chain = retrieve
|
112
|
+
traverse_chain = proc do
|
113
|
+
if chain.empty?
|
114
|
+
yield
|
115
|
+
else
|
116
|
+
chain.shift.call(type: type, socket_event: socket_event, &traverse_chain)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
traverse_chain.call
|
120
|
+
end
|
121
|
+
|
122
|
+
def inspect_me
|
123
|
+
entries.map do |e|
|
124
|
+
[
|
125
|
+
e.klass,
|
126
|
+
e.args
|
127
|
+
]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Entry
|
133
|
+
attr_reader :klass
|
134
|
+
attr_reader :args
|
135
|
+
|
136
|
+
def initialize(klass, args = [])
|
137
|
+
@klass = klass
|
138
|
+
@args = args
|
139
|
+
end
|
140
|
+
|
141
|
+
def instance!
|
142
|
+
klass.new(*args)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SlackBot
|
4
|
+
module Events
|
5
|
+
module Middleware
|
6
|
+
class EventTracer
|
7
|
+
def call(type:, socket_event:, schema: nil, parsed_data: nil)
|
8
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
9
|
+
temp_type = type.dup.to_s
|
10
|
+
case type
|
11
|
+
when :close
|
12
|
+
additional_info = "code: #{socket_event.code} reason:#{socket_event.reason}"
|
13
|
+
when :message
|
14
|
+
p_type = parsed_data.dig("type")
|
15
|
+
case p_type
|
16
|
+
when "app_rate_limited"
|
17
|
+
# https://api.slack.com/apis/rate-limits#events
|
18
|
+
# Total allowed workspace events are 30,000 per hour
|
19
|
+
# This message type is received once you have gone beyond that
|
20
|
+
temp_type += ":#{p_type}"
|
21
|
+
additional_info = "minute_rate_limited:#{parsed_data["minute_rate_limited"]} " \
|
22
|
+
"team_id:#{parsed_data["team_id"]} " \
|
23
|
+
"api_app_id:#{parsed_data["api_app_id"]}"
|
24
|
+
else
|
25
|
+
# Expected other types are `events_api` and `hello`
|
26
|
+
temp_type += ":#{p_type}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Events.logger.info { "[Event Received] #{temp_type} #{additional_info}" }
|
31
|
+
|
32
|
+
yield
|
33
|
+
|
34
|
+
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
35
|
+
Events.logger.info { "[Event Finished] [#{(elapsed_time * 1000).round(2)}ms] #{temp_type}" }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -39,7 +39,7 @@ module SlackBot
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def tldr
|
42
|
-
"type
|
42
|
+
"type:#{type}; user:#{user}; channel:#{channel}; ts_id:#{combined_id}"
|
43
43
|
end
|
44
44
|
|
45
45
|
def thread_ts
|
@@ -52,6 +52,10 @@ module SlackBot
|
|
52
52
|
|
53
53
|
private
|
54
54
|
|
55
|
+
def combined_id
|
56
|
+
thread_ts ? "#{ts}:#{thread_ts}" : ts
|
57
|
+
end
|
58
|
+
|
55
59
|
def return_nil?(val)
|
56
60
|
JsonSchematize::EmptyValue === val || val.nil?
|
57
61
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SlackBot
|
4
|
+
module Events
|
5
|
+
module Schematize
|
6
|
+
def self.message(parsed_data)
|
7
|
+
case parsed_data["type"]
|
8
|
+
when "events_api"
|
9
|
+
return SlackBot::Events::Schemas::SocketPayload
|
10
|
+
when "app_rate_limited"
|
11
|
+
when "hello"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.call(data:)
|
16
|
+
parsed_data = JSON.parse(data)
|
17
|
+
return { parsed_data: parsed_data } unless schema_klass = message(parsed_data)
|
18
|
+
|
19
|
+
if schema_klass.respond_to?(:call)
|
20
|
+
{ schema: schema_klass.call(parsed_data).new(parsed_data), parsed_data: parsed_data }
|
21
|
+
else
|
22
|
+
{ schema: schema_klass.new(parsed_data), parsed_data: parsed_data }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/slack_bot/events.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "pry"
|
4
|
+
|
3
5
|
require "slack_bot/events/configuration"
|
4
6
|
require "slack_bot/events/client"
|
7
|
+
require "slack_bot/events/middleware/chain"
|
5
8
|
require "slack_bot/events/schemas/socket_payload"
|
6
9
|
|
7
10
|
module SlackBot
|
@@ -40,5 +43,17 @@ module SlackBot
|
|
40
43
|
def self.logger
|
41
44
|
config.logger
|
42
45
|
end
|
46
|
+
|
47
|
+
def self.message_middleware
|
48
|
+
config.message_middleware
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.open_middleware
|
52
|
+
config.open_middleware
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.close_middleware
|
56
|
+
config.close_middleware
|
57
|
+
end
|
43
58
|
end
|
44
59
|
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
|
+
version: 0.3.0
|
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-
|
11
|
+
date: 2024-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -134,6 +134,8 @@ files:
|
|
134
134
|
- lib/slack_bot/events.rb
|
135
135
|
- lib/slack_bot/events/client.rb
|
136
136
|
- lib/slack_bot/events/configuration.rb
|
137
|
+
- lib/slack_bot/events/middleware/chain.rb
|
138
|
+
- lib/slack_bot/events/middleware/event_tracer.rb
|
137
139
|
- lib/slack_bot/events/schemas/authorization.rb
|
138
140
|
- lib/slack_bot/events/schemas/data_payload.rb
|
139
141
|
- lib/slack_bot/events/schemas/socket_payload.rb
|
@@ -144,6 +146,7 @@ files:
|
|
144
146
|
- lib/slack_bot/events/schemas/type/item.rb
|
145
147
|
- lib/slack_bot/events/schemas/type/message.rb
|
146
148
|
- lib/slack_bot/events/schemas/type/reaction_modified.rb
|
149
|
+
- lib/slack_bot/events/schematize.rb
|
147
150
|
- lib/slack_bot/events/version.rb
|
148
151
|
- slack_bot-events.gemspec
|
149
152
|
homepage: https://github.com/matt-taylor/slack_bot-events
|