chat_sdk 1.1.0 → 1.2.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 +4 -4
- data/lib/chat_sdk/adapter/base.rb +13 -5
- data/lib/chat_sdk/adapter/invocation.rb +18 -0
- data/lib/chat_sdk/ai/conversation_scope.rb +11 -1
- data/lib/chat_sdk/ai/tool_builder.rb +2 -1
- data/lib/chat_sdk/ai/tool_executor.rb +32 -15
- data/lib/chat_sdk/author.rb +6 -1
- data/lib/chat_sdk/cards/actions_context.rb +4 -2
- data/lib/chat_sdk/cards/builder.rb +18 -1
- data/lib/chat_sdk/cards/node.rb +2 -0
- data/lib/chat_sdk/cards/renderer.rb +1 -0
- data/lib/chat_sdk/chat.rb +15 -1
- data/lib/chat_sdk/dispatcher.rb +62 -8
- data/lib/chat_sdk/event_registry.rb +7 -2
- data/lib/chat_sdk/event_serializer.rb +34 -4
- data/lib/chat_sdk/events/action.rb +3 -2
- data/lib/chat_sdk/events/installed.rb +19 -0
- data/lib/chat_sdk/events/uninstalled.rb +19 -0
- data/lib/chat_sdk/format/mentions.rb +61 -0
- data/lib/chat_sdk/message.rb +4 -1
- data/lib/chat_sdk/postable_message.rb +3 -2
- data/lib/chat_sdk/streaming/stream.rb +6 -2
- data/lib/chat_sdk/testing/fake_adapter.rb +6 -6
- data/lib/chat_sdk/thread.rb +10 -4
- data/lib/chat_sdk/version.rb +1 -1
- data/lib/chat_sdk/webhook/endpoint.rb +3 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc45b8149b772db87ae0793f070a3f068353ef09a177d59c3526eb0ec37d1fb9
|
|
4
|
+
data.tar.gz: 16eb68d587eb53a43337f22dd2cd7d5b564383d892ecda9b64b77b5ae45a05a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a6cb0121846bf8a75b849f0d6bedfc48601c738dba99c3ad838ecdcb3b31fd7b4a27cd16bfa75cee66a4f70e62b62ae1c4730546b22f430e72d1af1bf6219c2
|
|
7
|
+
data.tar.gz: 602d33c5bba332dac427d61e8566c396f10c057e6b17a3422d1ab4846e4d7e8857ece123b50d2d357e310072f119bee00436db593e6138e46aabc34ba65f777c
|
|
@@ -22,6 +22,10 @@ module ChatSDK
|
|
|
22
22
|
nil
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
def webhook_response(_events)
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
25
29
|
def parse_events(rack_request)
|
|
26
30
|
raise NotImplementedError
|
|
27
31
|
end
|
|
@@ -41,12 +45,12 @@ module ChatSDK
|
|
|
41
45
|
raise NotImplementedError
|
|
42
46
|
end
|
|
43
47
|
|
|
44
|
-
def edit_message(channel_id:, message_id:, message:)
|
|
48
|
+
def edit_message(channel_id:, message_id:, message:, thread_id: nil)
|
|
45
49
|
require_capability!(:edit_messages)
|
|
46
50
|
raise NotImplementedError
|
|
47
51
|
end
|
|
48
52
|
|
|
49
|
-
def delete_message(channel_id:, message_id:)
|
|
53
|
+
def delete_message(channel_id:, message_id:, thread_id: nil)
|
|
50
54
|
require_capability!(:delete_messages)
|
|
51
55
|
raise NotImplementedError
|
|
52
56
|
end
|
|
@@ -61,12 +65,12 @@ module ChatSDK
|
|
|
61
65
|
raise NotImplementedError
|
|
62
66
|
end
|
|
63
67
|
|
|
64
|
-
def add_reaction(channel_id:, message_id:, emoji:)
|
|
68
|
+
def add_reaction(channel_id:, message_id:, emoji:, thread_id: nil)
|
|
65
69
|
require_capability!(:reactions)
|
|
66
70
|
raise NotImplementedError
|
|
67
71
|
end
|
|
68
72
|
|
|
69
|
-
def remove_reaction(channel_id:, message_id:, emoji:)
|
|
73
|
+
def remove_reaction(channel_id:, message_id:, emoji:, thread_id: nil)
|
|
70
74
|
require_capability!(:reactions)
|
|
71
75
|
raise NotImplementedError
|
|
72
76
|
end
|
|
@@ -96,11 +100,15 @@ module ChatSDK
|
|
|
96
100
|
raise NotImplementedError
|
|
97
101
|
end
|
|
98
102
|
|
|
99
|
-
def start_typing(channel_id:, thread_id: nil)
|
|
103
|
+
def start_typing(channel_id:, thread_id: nil, message_id: nil)
|
|
100
104
|
require_capability!(:typing_indicator)
|
|
101
105
|
raise NotImplementedError
|
|
102
106
|
end
|
|
103
107
|
|
|
108
|
+
def concurrency_scope(_event)
|
|
109
|
+
nil
|
|
110
|
+
end
|
|
111
|
+
|
|
104
112
|
def mention(user_id)
|
|
105
113
|
raise NotImplementedError
|
|
106
114
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ChatSDK
|
|
4
|
+
module Adapter
|
|
5
|
+
module Invocation
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def call_adapter(method_name, **kwargs)
|
|
9
|
+
params = adapter.method(method_name).parameters
|
|
10
|
+
accepts_thread_id = params.any? do |type, name|
|
|
11
|
+
type == :keyrest || (%i[key keyreq].include?(type) && name == :thread_id)
|
|
12
|
+
end
|
|
13
|
+
kwargs.delete(:thread_id) unless accepts_thread_id
|
|
14
|
+
adapter.public_send(method_name, **kwargs)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -4,29 +4,39 @@ module ChatSDK
|
|
|
4
4
|
module AI
|
|
5
5
|
module ConversationScope
|
|
6
6
|
THREAD_KEY = :chat_sdk_ai_conversation_scope
|
|
7
|
+
ACTIVE_KEY = :chat_sdk_ai_conversation_scope_active
|
|
7
8
|
|
|
8
9
|
class << self
|
|
9
10
|
def current
|
|
10
11
|
::Thread.current[THREAD_KEY]
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
def active?
|
|
15
|
+
!!::Thread.current[ACTIVE_KEY]
|
|
16
|
+
end
|
|
17
|
+
|
|
13
18
|
def with(thread)
|
|
14
19
|
previous = current
|
|
20
|
+
previous_active = active?
|
|
15
21
|
::Thread.current[THREAD_KEY] = scope_for(thread)
|
|
22
|
+
::Thread.current[ACTIVE_KEY] = true
|
|
16
23
|
yield
|
|
17
24
|
ensure
|
|
18
25
|
::Thread.current[THREAD_KEY] = previous
|
|
26
|
+
::Thread.current[ACTIVE_KEY] = previous_active
|
|
19
27
|
end
|
|
20
28
|
|
|
21
29
|
def scope_for(value)
|
|
22
30
|
return if value.nil?
|
|
23
31
|
return value.transform_keys(&:to_sym) if value.is_a?(Hash)
|
|
24
32
|
|
|
25
|
-
{
|
|
33
|
+
scope = {
|
|
26
34
|
adapter_name: value.adapter.name.to_sym,
|
|
27
35
|
channel_id: value.respond_to?(:channel_id) ? value.channel_id.to_s : value.id.to_s,
|
|
28
36
|
thread_id: value.is_a?(ChatSDK::Thread) ? value.id.to_s : nil
|
|
29
37
|
}
|
|
38
|
+
scope[:message_id] = value.current_message.id if value.respond_to?(:current_message) && value.current_message
|
|
39
|
+
scope
|
|
30
40
|
end
|
|
31
41
|
end
|
|
32
42
|
end
|
|
@@ -131,7 +131,8 @@ module ChatSDK
|
|
|
131
131
|
properties: {
|
|
132
132
|
adapter_name: {type: "string", description: "Adapter name"},
|
|
133
133
|
channel_id: {type: "string", description: "Channel ID"},
|
|
134
|
-
thread_id: {type: "string", description: "Thread ID (optional)"}
|
|
134
|
+
thread_id: {type: "string", description: "Thread ID (optional)"},
|
|
135
|
+
message_id: {type: "string", description: "Current message ID (optional)"}
|
|
135
136
|
},
|
|
136
137
|
required: %w[adapter_name channel_id]
|
|
137
138
|
},
|
|
@@ -5,13 +5,11 @@ module ChatSDK
|
|
|
5
5
|
class ToolExecutor
|
|
6
6
|
def initialize(chat:, scope: nil, strict_scope: false)
|
|
7
7
|
@chat = chat
|
|
8
|
-
@scope =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
ConversationScope.scope_for(scope) || ConversationScope.current&.dup
|
|
12
|
-
end
|
|
8
|
+
@scope = (scope == false) ? false : (ConversationScope.scope_for(scope) || ConversationScope.current&.dup)
|
|
9
|
+
@created_in_handler = ConversationScope.active?
|
|
10
|
+
validate_scope!(@scope) if @scope
|
|
13
11
|
@strict_scope = strict_scope
|
|
14
|
-
@warned_unscoped =
|
|
12
|
+
@warned_unscoped = {}
|
|
15
13
|
end
|
|
16
14
|
|
|
17
15
|
def execute(tool_name, arguments)
|
|
@@ -20,7 +18,7 @@ module ChatSDK
|
|
|
20
18
|
|
|
21
19
|
args = arguments.transform_keys(&:to_sym)
|
|
22
20
|
adapter_name = args[:adapter_name].to_sym
|
|
23
|
-
guard_scope!(adapter_name, args)
|
|
21
|
+
guard_scope!(tool_name, adapter_name, args)
|
|
24
22
|
|
|
25
23
|
send(:"execute_#{tool_name}", adapter_name, args)
|
|
26
24
|
end
|
|
@@ -48,24 +46,25 @@ module ChatSDK
|
|
|
48
46
|
|
|
49
47
|
def execute_post_message(adapter_name, args)
|
|
50
48
|
channel = @chat.channel(args[:channel_id], adapter_name: adapter_name)
|
|
49
|
+
message = ChatSDK::PostableMessage.new(text: args[:text], ai_generated: true)
|
|
51
50
|
if args[:thread_id]
|
|
52
51
|
thread = channel.thread(args[:thread_id])
|
|
53
|
-
result = thread.post(
|
|
52
|
+
result = thread.post(message)
|
|
54
53
|
else
|
|
55
|
-
result = channel.post(
|
|
54
|
+
result = channel.post(message)
|
|
56
55
|
end
|
|
57
56
|
{id: result.id, text: args[:text]}
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def execute_send_direct_message(adapter_name, args)
|
|
61
60
|
dm_channel = @chat.open_dm(args[:user_id], adapter_name: adapter_name)
|
|
62
|
-
result = dm_channel.post(args[:text])
|
|
61
|
+
result = dm_channel.post(ChatSDK::PostableMessage.new(text: args[:text], ai_generated: true))
|
|
63
62
|
{id: result.id, channel_id: dm_channel.id}
|
|
64
63
|
end
|
|
65
64
|
|
|
66
65
|
def execute_edit_message(adapter_name, args)
|
|
67
66
|
thread = target_thread(adapter_name, args)
|
|
68
|
-
thread.edit(args[:message_id], args[:text])
|
|
67
|
+
thread.edit(args[:message_id], ChatSDK::PostableMessage.new(text: args[:text], ai_generated: true))
|
|
69
68
|
{success: true}
|
|
70
69
|
end
|
|
71
70
|
|
|
@@ -89,7 +88,9 @@ module ChatSDK
|
|
|
89
88
|
|
|
90
89
|
def execute_start_typing(adapter_name, args)
|
|
91
90
|
adapter = @chat.adapter(adapter_name)
|
|
92
|
-
|
|
91
|
+
scoped_message_id = @scope.is_a?(Hash) ? @scope[:message_id] : nil
|
|
92
|
+
message_id = args[:message_id] || ConversationScope.current&.[](:message_id) || scoped_message_id
|
|
93
|
+
adapter.start_typing(channel_id: args[:channel_id], thread_id: args[:thread_id], message_id: message_id)
|
|
93
94
|
{success: true}
|
|
94
95
|
end
|
|
95
96
|
|
|
@@ -102,18 +103,25 @@ module ChatSDK
|
|
|
102
103
|
channel.thread(args[:thread_id] || args[:channel_id])
|
|
103
104
|
end
|
|
104
105
|
|
|
105
|
-
def guard_scope!(adapter_name, args)
|
|
106
|
+
def guard_scope!(tool_name, adapter_name, args)
|
|
106
107
|
return if @scope == false
|
|
107
108
|
|
|
108
109
|
scope = @scope || ConversationScope.current
|
|
109
110
|
unless scope
|
|
110
|
-
|
|
111
|
+
if ConversationScope.active? || @created_in_handler
|
|
112
|
+
raise ChatSDK::Error, "AI tool call requires an explicit scope when run inside a handler without a conversation"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
unless @warned_unscoped[tool_name]
|
|
111
116
|
ChatSDK::Log.warn("AI tool ran without a conversation scope; pass scope: to create_executor to confine access")
|
|
112
|
-
@warned_unscoped = true
|
|
117
|
+
@warned_unscoped[tool_name] = true
|
|
113
118
|
end
|
|
119
|
+
|
|
114
120
|
return
|
|
115
121
|
end
|
|
116
122
|
|
|
123
|
+
return if tool_name == :send_direct_message
|
|
124
|
+
|
|
117
125
|
target_channel = args[:channel_id].to_s
|
|
118
126
|
target_thread = args[:thread_id]&.to_s
|
|
119
127
|
same_channel = adapter_name == scope.fetch(:adapter_name).to_sym && target_channel == scope.fetch(:channel_id).to_s
|
|
@@ -125,6 +133,15 @@ module ChatSDK
|
|
|
125
133
|
active = [scope[:adapter_name], scope[:channel_id], scope[:thread_id]].compact.join(":")
|
|
126
134
|
raise ChatSDK::Error, "AI tool call blocked: executor is scoped to #{active.inspect}, but targeted #{target.inspect}"
|
|
127
135
|
end
|
|
136
|
+
|
|
137
|
+
def validate_scope!(scope)
|
|
138
|
+
%i[channel_id thread_id].each do |key|
|
|
139
|
+
next if key == :thread_id && scope[key].nil?
|
|
140
|
+
next unless scope.key?(key) && scope[key].to_s.empty?
|
|
141
|
+
|
|
142
|
+
raise ChatSDK::ConfigurationError, "AI tool scope #{key} must not be empty"
|
|
143
|
+
end
|
|
144
|
+
end
|
|
128
145
|
end
|
|
129
146
|
end
|
|
130
147
|
end
|
data/lib/chat_sdk/author.rb
CHANGED
|
@@ -4,11 +4,12 @@ module ChatSDK
|
|
|
4
4
|
class Author
|
|
5
5
|
attr_reader :id, :name, :platform, :locale, :email, :raw
|
|
6
6
|
|
|
7
|
-
def initialize(id:, name:, platform:, bot: false, system: false, locale: nil, email: nil, raw: nil)
|
|
7
|
+
def initialize(id:, name:, platform:, bot: false, me: false, system: false, locale: nil, email: nil, raw: nil)
|
|
8
8
|
@id = id
|
|
9
9
|
@name = name
|
|
10
10
|
@platform = platform
|
|
11
11
|
@bot = bot
|
|
12
|
+
@me = !!me
|
|
12
13
|
@system = system
|
|
13
14
|
@locale = locale
|
|
14
15
|
@email = email
|
|
@@ -19,6 +20,10 @@ module ChatSDK
|
|
|
19
20
|
@bot
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
def me?
|
|
24
|
+
@me
|
|
25
|
+
end
|
|
26
|
+
|
|
22
27
|
def system?
|
|
23
28
|
@system
|
|
24
29
|
end
|
|
@@ -9,18 +9,20 @@ module ChatSDK
|
|
|
9
9
|
@nodes = []
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def button(text, id:, style: nil, value: nil, tooltip: nil)
|
|
12
|
+
def button(text, id:, style: nil, value: nil, tooltip: nil, visible_to: nil)
|
|
13
13
|
attrs = {text: text, id: id}
|
|
14
14
|
attrs[:style] = style if style
|
|
15
15
|
attrs[:value] = value if value
|
|
16
16
|
attrs[:tooltip] = tooltip if tooltip
|
|
17
|
+
attrs[:visible_to] = visible_to if visible_to
|
|
17
18
|
@nodes << Node.new(:button, attributes: attrs)
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def link_button(text, url:, id: nil, tooltip: nil)
|
|
21
|
+
def link_button(text, url:, id: nil, tooltip: nil, visible_to: nil)
|
|
21
22
|
attrs = {text: text, url: url}
|
|
22
23
|
attrs[:id] = id if id
|
|
23
24
|
attrs[:tooltip] = tooltip if tooltip
|
|
25
|
+
attrs[:visible_to] = visible_to if visible_to
|
|
24
26
|
@nodes << Node.new(:link_button, attributes: attrs)
|
|
25
27
|
end
|
|
26
28
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
module Cards
|
|
5
5
|
class Builder
|
|
6
|
+
RESERVED_TEXT_INPUT_IDS = %w[actionId value msteams].freeze
|
|
7
|
+
|
|
6
8
|
def initialize(title: nil, subtitle: nil, width: nil, &block)
|
|
7
9
|
@title = title
|
|
8
10
|
@subtitle = subtitle
|
|
@@ -31,6 +33,17 @@ module ChatSDK
|
|
|
31
33
|
@children << Node.new(:image, attributes: {url: url, alt: alt})
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
def text_input(id:, label:, placeholder: nil, multiline: false, optional: false, value: nil)
|
|
37
|
+
if RESERVED_TEXT_INPUT_IDS.include?(id.to_s)
|
|
38
|
+
raise ArgumentError, "Text input id #{id.inspect} is reserved"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attrs = {id: id, label: label, multiline: multiline, optional: optional}
|
|
42
|
+
attrs[:placeholder] = placeholder if placeholder
|
|
43
|
+
attrs[:value] = value unless value.nil?
|
|
44
|
+
@children << Node.new(:text_input, attributes: attrs)
|
|
45
|
+
end
|
|
46
|
+
|
|
34
47
|
def fields(&block)
|
|
35
48
|
ctx = FieldsContext.new
|
|
36
49
|
ctx.instance_eval(&block)
|
|
@@ -50,11 +63,15 @@ module ChatSDK
|
|
|
50
63
|
@children << Node.new(:actions, children: ctx.nodes)
|
|
51
64
|
end
|
|
52
65
|
|
|
53
|
-
def table(headers:, rows:, align: nil, caption: nil, page_size: nil)
|
|
66
|
+
def table(headers:, rows:, align: nil, caption: nil, page_size: nil, widths: nil, grid_lines: nil, grid_style: nil, vertical_align: nil)
|
|
54
67
|
attrs = {headers: headers, rows: rows}
|
|
55
68
|
attrs[:align] = align if align
|
|
56
69
|
attrs[:caption] = caption if caption
|
|
57
70
|
attrs[:page_size] = page_size if page_size
|
|
71
|
+
attrs[:widths] = widths if widths
|
|
72
|
+
attrs[:grid_lines] = grid_lines unless grid_lines.nil?
|
|
73
|
+
attrs[:grid_style] = grid_style if grid_style
|
|
74
|
+
attrs[:vertical_align] = vertical_align if vertical_align
|
|
58
75
|
@children << Node.new(:table, attributes: attrs)
|
|
59
76
|
end
|
|
60
77
|
|
data/lib/chat_sdk/cards/node.rb
CHANGED
|
@@ -15,6 +15,7 @@ module ChatSDK
|
|
|
15
15
|
when :actions then render_actions(node)
|
|
16
16
|
when :button then "[#{node.attributes[:text]}]"
|
|
17
17
|
when :link_button then "[#{node.attributes[:text]}](#{node.attributes[:url]})"
|
|
18
|
+
when :text_input then node.attributes[:label]
|
|
18
19
|
when :select then "_#{node.attributes[:placeholder] || "Select"}_"
|
|
19
20
|
when :table then render_table(node)
|
|
20
21
|
when :chart then render_chart(node)
|
data/lib/chat_sdk/chat.rb
CHANGED
|
@@ -8,6 +8,7 @@ module ChatSDK
|
|
|
8
8
|
@config = Config.new(user_name: user_name, adapters: adapters, state: state, **options)
|
|
9
9
|
@state = state
|
|
10
10
|
@adapters = adapters
|
|
11
|
+
@adapters.each_value { |adapter| adapter.set_state(state) if adapter.respond_to?(:set_state) }
|
|
11
12
|
@registry = EventRegistry.new
|
|
12
13
|
@history = History.new(self)
|
|
13
14
|
@webhooks = {}
|
|
@@ -26,7 +27,12 @@ module ChatSDK
|
|
|
26
27
|
end
|
|
27
28
|
|
|
28
29
|
def on_new_message(pattern = nil, &block)
|
|
29
|
-
|
|
30
|
+
unless pattern.nil? || pattern.is_a?(Regexp)
|
|
31
|
+
raise ChatSDK::ConfigurationError, "on_new_message pattern must be a Regexp or nil"
|
|
32
|
+
end
|
|
33
|
+
raise ChatSDK::ConfigurationError, "on_new_message requires a block" unless block
|
|
34
|
+
|
|
35
|
+
@registry.register(:new_message, matcher: pattern, &block)
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
def on_direct_message(&block)
|
|
@@ -53,6 +59,14 @@ module ChatSDK
|
|
|
53
59
|
@registry.register(:slash_command, matcher: command, &block)
|
|
54
60
|
end
|
|
55
61
|
|
|
62
|
+
def on_installed(&block)
|
|
63
|
+
@registry.register(:installed, &block)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def on_uninstalled(&block)
|
|
67
|
+
@registry.register(:uninstalled, &block)
|
|
68
|
+
end
|
|
69
|
+
|
|
56
70
|
# Adapter access
|
|
57
71
|
def adapter(name)
|
|
58
72
|
@adapters.fetch(name) { raise ChatSDK::ConfigurationError, "Unknown adapter: #{name}" }
|
data/lib/chat_sdk/dispatcher.rb
CHANGED
|
@@ -27,10 +27,11 @@ module ChatSDK
|
|
|
27
27
|
return execute_event(event, build_thread(event, adapter))
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
concurrency_scope = adapter.concurrency_scope(event)
|
|
31
|
+
return unless dedupe(event, adapter_name, concurrency_scope)
|
|
31
32
|
|
|
32
33
|
thread = build_thread(event, adapter)
|
|
33
|
-
thread_key = thread_key_for(event, adapter_name)
|
|
34
|
+
thread_key = thread_key_for(event, adapter_name, concurrency_scope)
|
|
34
35
|
strategy = @config.concurrency[:strategy]
|
|
35
36
|
|
|
36
37
|
if strategy == :concurrent
|
|
@@ -155,12 +156,12 @@ module ChatSDK
|
|
|
155
156
|
end
|
|
156
157
|
end
|
|
157
158
|
|
|
158
|
-
def dedupe(event, adapter_name)
|
|
159
|
+
def dedupe(event, adapter_name, concurrency_scope)
|
|
159
160
|
event_id = extract_event_id(event)
|
|
160
161
|
return true unless event_id
|
|
161
162
|
|
|
162
163
|
is_new = @state.set_if_absent(
|
|
163
|
-
|
|
164
|
+
dedupe_key(adapter_name, concurrency_scope, event_id),
|
|
164
165
|
true,
|
|
165
166
|
ttl: @config.dedupe_ttl
|
|
166
167
|
)
|
|
@@ -178,12 +179,18 @@ module ChatSDK
|
|
|
178
179
|
end
|
|
179
180
|
end
|
|
180
181
|
|
|
181
|
-
def
|
|
182
|
+
def dedupe_key(adapter_name, concurrency_scope, event_id)
|
|
183
|
+
prefix = ["chat_sdk:dedupe", adapter_name]
|
|
184
|
+
prefix << concurrency_scope if concurrency_scope
|
|
185
|
+
"#{prefix.join(":")}:#{event_id}"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def thread_key_for(event, adapter_name, concurrency_scope)
|
|
182
189
|
channel_id = event.respond_to?(:channel_id) ? event.channel_id : nil
|
|
183
190
|
thread_id = event.respond_to?(:thread_id) ? event.thread_id : nil
|
|
184
191
|
lock_scope = @config.concurrency[:lock_scope]
|
|
185
192
|
scope_id = (lock_scope&.to_sym == :channel) ? channel_id : thread_id
|
|
186
|
-
|
|
193
|
+
[adapter_name, concurrency_scope, channel_id, scope_id].compact.join(":")
|
|
187
194
|
end
|
|
188
195
|
|
|
189
196
|
def acquire_lock(thread_key, event, owner, force: false, apply_conflict_policy: true)
|
|
@@ -256,20 +263,67 @@ module ChatSDK
|
|
|
256
263
|
end
|
|
257
264
|
|
|
258
265
|
def execute_event(event, thread, context: nil)
|
|
266
|
+
if %i[mention subscribed_message direct_message].include?(event.type)
|
|
267
|
+
route_message_event(event, thread, context)
|
|
268
|
+
return
|
|
269
|
+
end
|
|
270
|
+
|
|
259
271
|
@registry.handlers_for(event).each do |handler|
|
|
260
272
|
execute_handler(handler, event, thread, context: context)
|
|
261
273
|
end
|
|
262
274
|
end
|
|
263
275
|
|
|
276
|
+
def route_message_event(event, thread, context)
|
|
277
|
+
message = event.message
|
|
278
|
+
dm_handlers = @registry.handlers_for_type(:direct_message, event: event)
|
|
279
|
+
if event.type == :direct_message && dm_handlers.any?
|
|
280
|
+
run_handlers(dm_handlers, event, thread, context)
|
|
281
|
+
return
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
message.is_mention = true if event.type == :direct_message
|
|
285
|
+
|
|
286
|
+
if thread&.subscribed?
|
|
287
|
+
run_handlers(@registry.handlers_for_type(:subscribed_message, event: event), event, thread, context)
|
|
288
|
+
return
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if message.is_mention.nil?
|
|
292
|
+
message.is_mention = if event.type == :mention
|
|
293
|
+
true
|
|
294
|
+
else
|
|
295
|
+
detect_mention(message.text)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
if message.is_mention
|
|
300
|
+
run_handlers(@registry.handlers_for_type(:mention, event: event), event, thread, context)
|
|
301
|
+
return
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
@registry.handlers_for_type(:new_message, event: event).each do |handler|
|
|
305
|
+
execute_handler(handler, event, thread, context: context)
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def run_handlers(handlers, event, thread, context)
|
|
310
|
+
handlers.each { |handler| execute_handler(handler, event, thread, context: context) }
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
def detect_mention(text)
|
|
314
|
+
masked_text = ChatSDK::Format::Mentions.mask_code_spans(text)
|
|
315
|
+
/(?<![[:word:]])@#{Regexp.escape(@config.user_name)}\b/i.match?(masked_text)
|
|
316
|
+
end
|
|
317
|
+
|
|
264
318
|
def execute_handler(handler, event, thread, context: nil)
|
|
265
319
|
ChatSDK::Instrumentation.instrument("handler.chat_sdk", handler_type: event.type) do
|
|
266
320
|
ChatSDK::AI::ConversationScope.with(thread) do
|
|
267
321
|
case event.type
|
|
268
|
-
when :mention, :subscribed_message, :direct_message
|
|
322
|
+
when :mention, :subscribed_message, :direct_message, :new_message
|
|
269
323
|
handler.block.call(thread, event.message, context)
|
|
270
324
|
when :message_updated
|
|
271
325
|
handler.block.call(thread, event.message, event.previous_message)
|
|
272
|
-
when :reaction, :action, :slash_command, :message_deleted
|
|
326
|
+
when :reaction, :action, :slash_command, :message_deleted, :installed, :uninstalled
|
|
273
327
|
add_thread_to_event(event, thread)
|
|
274
328
|
handler.block.call(event)
|
|
275
329
|
end
|
|
@@ -13,13 +13,18 @@ module ChatSDK
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def handlers_for(event)
|
|
16
|
-
|
|
16
|
+
handlers_for_type(event.type, event: event)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def handlers_for_type(type, event: nil)
|
|
20
|
+
@handlers.select do |handler|
|
|
21
|
+
handler.type == type && (!event || matches?(handler, event))
|
|
22
|
+
end
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
private
|
|
20
26
|
|
|
21
27
|
def matches?(handler, event)
|
|
22
|
-
return false unless handler.type == event.type
|
|
23
28
|
return true if handler.matcher.nil?
|
|
24
29
|
|
|
25
30
|
case handler.matcher
|
|
@@ -21,9 +21,18 @@ module ChatSDK
|
|
|
21
21
|
when :reaction
|
|
22
22
|
data.merge!("emoji" => event.emoji, "user_id" => event.user_id, "message_id" => event.message_id, "added" => event.added?)
|
|
23
23
|
when :action
|
|
24
|
-
data.merge!("action_id" => event.action_id, "value" => event.value, "user" => dump_author(event.user), "trigger_id" => event.trigger_id)
|
|
24
|
+
data.merge!("action_id" => event.action_id, "value" => event.value, "values" => json_safe(event.values), "user" => dump_author(event.user), "trigger_id" => event.trigger_id)
|
|
25
25
|
when :slash_command
|
|
26
26
|
data.merge!("command" => event.command, "text" => event.text, "user_id" => event.user_id, "trigger_id" => event.trigger_id)
|
|
27
|
+
when :installed, :uninstalled
|
|
28
|
+
data.merge!(
|
|
29
|
+
"installation_id" => event.installation_id,
|
|
30
|
+
"team_id" => event.team_id,
|
|
31
|
+
"tenant_id" => event.tenant_id,
|
|
32
|
+
"conversation_id" => event.conversation_id,
|
|
33
|
+
"action" => event.action,
|
|
34
|
+
"user" => dump_author(event.user)
|
|
35
|
+
)
|
|
27
36
|
end
|
|
28
37
|
data
|
|
29
38
|
end
|
|
@@ -55,10 +64,27 @@ module ChatSDK
|
|
|
55
64
|
when :reaction
|
|
56
65
|
Events::Reaction.new(emoji: data["emoji"], user_id: data["user_id"], message_id: data["message_id"], added: data["added"], **common)
|
|
57
66
|
when :action
|
|
58
|
-
Events::Action.new(action_id: data["action_id"], value: data["value"], user: load_author(data["user"]), trigger_id: data["trigger_id"], **common)
|
|
67
|
+
Events::Action.new(action_id: data["action_id"], value: data["value"], values: data["values"], user: load_author(data["user"]), trigger_id: data["trigger_id"], **common)
|
|
59
68
|
when :slash_command
|
|
60
69
|
common.delete(:thread_id)
|
|
61
70
|
Events::SlashCommand.new(command: data["command"], text: data["text"], user_id: data["user_id"], trigger_id: data["trigger_id"], **common)
|
|
71
|
+
when :installed, :uninstalled
|
|
72
|
+
common.delete(:thread_id)
|
|
73
|
+
common.delete(:channel_id)
|
|
74
|
+
event_class = if type == :installed
|
|
75
|
+
Events::Installed
|
|
76
|
+
else
|
|
77
|
+
Events::Uninstalled
|
|
78
|
+
end
|
|
79
|
+
event_class.new(
|
|
80
|
+
installation_id: data["installation_id"],
|
|
81
|
+
team_id: data["team_id"],
|
|
82
|
+
tenant_id: data["tenant_id"],
|
|
83
|
+
conversation_id: data["conversation_id"],
|
|
84
|
+
action: data["action"],
|
|
85
|
+
user: load_author(data["user"]),
|
|
86
|
+
**common
|
|
87
|
+
)
|
|
62
88
|
else
|
|
63
89
|
raise ArgumentError, "unsupported queued event type: #{type}"
|
|
64
90
|
end
|
|
@@ -93,7 +119,8 @@ module ChatSDK
|
|
|
93
119
|
"reply_to" => dump_message(message.reply_to),
|
|
94
120
|
"subject" => json_safe(message.subject),
|
|
95
121
|
"raw" => json_safe(message.raw),
|
|
96
|
-
"timestamp" => message.timestamp&.iso8601
|
|
122
|
+
"timestamp" => message.timestamp&.iso8601,
|
|
123
|
+
"is_mention" => message.is_mention
|
|
97
124
|
}
|
|
98
125
|
end
|
|
99
126
|
|
|
@@ -113,7 +140,8 @@ module ChatSDK
|
|
|
113
140
|
reply_to: load_message(data["reply_to"]),
|
|
114
141
|
subject: data["subject"],
|
|
115
142
|
raw: data["raw"],
|
|
116
|
-
timestamp: parse_time(data["timestamp"])
|
|
143
|
+
timestamp: parse_time(data["timestamp"]),
|
|
144
|
+
is_mention: data["is_mention"]
|
|
117
145
|
)
|
|
118
146
|
end
|
|
119
147
|
|
|
@@ -125,6 +153,7 @@ module ChatSDK
|
|
|
125
153
|
"name" => author.name,
|
|
126
154
|
"platform" => author.platform.to_s,
|
|
127
155
|
"bot" => author.bot?,
|
|
156
|
+
"me" => author.me?,
|
|
128
157
|
"system" => author.system?,
|
|
129
158
|
"locale" => author.locale,
|
|
130
159
|
"email" => author.email,
|
|
@@ -141,6 +170,7 @@ module ChatSDK
|
|
|
141
170
|
name: data["name"],
|
|
142
171
|
platform: data["platform"].to_sym,
|
|
143
172
|
bot: data["bot"],
|
|
173
|
+
me: data["me"],
|
|
144
174
|
system: data["system"],
|
|
145
175
|
locale: data["locale"],
|
|
146
176
|
email: data["email"],
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
module Events
|
|
5
5
|
class Action < Base
|
|
6
|
-
attr_reader :action_id, :value, :user, :thread_id, :channel_id, :trigger_id
|
|
6
|
+
attr_reader :action_id, :value, :values, :user, :thread_id, :channel_id, :trigger_id
|
|
7
7
|
attr_accessor :thread
|
|
8
8
|
|
|
9
|
-
def initialize(action_id:, thread_id:, channel_id:, value: nil, user: nil, trigger_id: nil, **kwargs)
|
|
9
|
+
def initialize(action_id:, thread_id:, channel_id:, value: nil, values: nil, user: nil, trigger_id: nil, **kwargs)
|
|
10
10
|
super(type: :action, **kwargs)
|
|
11
11
|
@action_id = action_id
|
|
12
12
|
@value = value
|
|
13
|
+
@values = values
|
|
13
14
|
@user = user
|
|
14
15
|
@thread_id = thread_id
|
|
15
16
|
@channel_id = channel_id
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ChatSDK
|
|
4
|
+
module Events
|
|
5
|
+
class Installed < Base
|
|
6
|
+
attr_reader :installation_id, :team_id, :tenant_id, :conversation_id, :action, :user
|
|
7
|
+
|
|
8
|
+
def initialize(conversation_id:, action:, installation_id: nil, team_id: nil, tenant_id: nil, user: nil, **kwargs)
|
|
9
|
+
super(type: :installed, **kwargs)
|
|
10
|
+
@installation_id = installation_id
|
|
11
|
+
@team_id = team_id
|
|
12
|
+
@tenant_id = tenant_id
|
|
13
|
+
@conversation_id = conversation_id
|
|
14
|
+
@action = action
|
|
15
|
+
@user = user
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ChatSDK
|
|
4
|
+
module Events
|
|
5
|
+
class Uninstalled < Base
|
|
6
|
+
attr_reader :installation_id, :team_id, :tenant_id, :conversation_id, :action, :user
|
|
7
|
+
|
|
8
|
+
def initialize(conversation_id:, action:, installation_id: nil, team_id: nil, tenant_id: nil, user: nil, **kwargs)
|
|
9
|
+
super(type: :uninstalled, **kwargs)
|
|
10
|
+
@installation_id = installation_id
|
|
11
|
+
@team_id = team_id
|
|
12
|
+
@tenant_id = tenant_id
|
|
13
|
+
@conversation_id = conversation_id
|
|
14
|
+
@action = action
|
|
15
|
+
@user = user
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ChatSDK
|
|
4
|
+
module Format
|
|
5
|
+
module Mentions
|
|
6
|
+
class << self
|
|
7
|
+
def mask_code_spans(text)
|
|
8
|
+
characters = text.to_s.chars
|
|
9
|
+
masked = characters.dup
|
|
10
|
+
opening = nil
|
|
11
|
+
opening_length = nil
|
|
12
|
+
fence = false
|
|
13
|
+
index = 0
|
|
14
|
+
|
|
15
|
+
while index < characters.length
|
|
16
|
+
if characters[index] == "`"
|
|
17
|
+
finish = index + 1
|
|
18
|
+
finish += 1 while finish < characters.length && characters[finish] == "`"
|
|
19
|
+
length = finish - index
|
|
20
|
+
|
|
21
|
+
if opening
|
|
22
|
+
closes = fence ? length >= 3 : length == opening_length
|
|
23
|
+
if closes
|
|
24
|
+
blank_span(masked, opening, finish)
|
|
25
|
+
opening = nil
|
|
26
|
+
opening_length = nil
|
|
27
|
+
fence = false
|
|
28
|
+
else
|
|
29
|
+
blank_span(masked, index, finish)
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
opening = index
|
|
33
|
+
opening_length = length
|
|
34
|
+
fence = length >= 3
|
|
35
|
+
blank_span(masked, index, finish)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
index = finish
|
|
39
|
+
else
|
|
40
|
+
if opening
|
|
41
|
+
masked[index] = " " unless characters[index] == "\n" || characters[index] == "\r"
|
|
42
|
+
end
|
|
43
|
+
index += 1
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
masked[opening..] = characters[opening..] if opening
|
|
48
|
+
masked.join
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def blank_span(characters, first, finish)
|
|
54
|
+
(first...finish).each do |index|
|
|
55
|
+
characters[index] = " " unless characters[index] == "\n" || characters[index] == "\r"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/chat_sdk/message.rb
CHANGED
|
@@ -4,9 +4,11 @@ module ChatSDK
|
|
|
4
4
|
class Message
|
|
5
5
|
attr_reader :id, :text, :author, :thread_id, :channel_id,
|
|
6
6
|
:platform, :attachments, :links, :reply_to, :subject, :raw, :timestamp
|
|
7
|
+
attr_accessor :is_mention
|
|
7
8
|
|
|
8
9
|
def initialize(id:, text:, author:, thread_id:, channel_id:, platform:,
|
|
9
|
-
attachments: [], links: [], reply_to: nil, subject: nil, raw: nil,
|
|
10
|
+
attachments: [], links: [], reply_to: nil, subject: nil, raw: nil,
|
|
11
|
+
timestamp: nil, is_mention: nil)
|
|
10
12
|
@id = id
|
|
11
13
|
@text = text
|
|
12
14
|
@author = author
|
|
@@ -19,6 +21,7 @@ module ChatSDK
|
|
|
19
21
|
@subject = subject
|
|
20
22
|
@raw = raw
|
|
21
23
|
@timestamp = timestamp
|
|
24
|
+
@is_mention = is_mention
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def ==(other)
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
class PostableMessage
|
|
5
|
-
attr_reader :text, :card, :attachments, :metadata
|
|
5
|
+
attr_reader :text, :card, :attachments, :metadata, :ai_generated
|
|
6
6
|
|
|
7
|
-
def initialize(text: nil, card: nil, attachments: [], metadata: {})
|
|
7
|
+
def initialize(text: nil, card: nil, attachments: [], metadata: {}, ai_generated: false)
|
|
8
8
|
raise ArgumentError, "text or card required" if text.nil? && card.nil?
|
|
9
9
|
@text = text
|
|
10
10
|
@card = card
|
|
11
11
|
@attachments = attachments
|
|
12
12
|
@metadata = metadata
|
|
13
|
+
@ai_generated = ai_generated
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def card?
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
module Streaming
|
|
5
5
|
class Stream
|
|
6
|
+
include ChatSDK::Adapter::Invocation
|
|
7
|
+
|
|
6
8
|
attr_reader :adapter, :channel_id, :thread_id, :update_interval
|
|
7
9
|
|
|
8
10
|
def initialize(adapter:, channel_id:, thread_id:, placeholder: nil, update_interval: 0.5)
|
|
@@ -50,10 +52,12 @@ module ChatSDK
|
|
|
50
52
|
return if @buffer.empty?
|
|
51
53
|
|
|
52
54
|
if @message_id && adapter.supports?(:edit_messages)
|
|
53
|
-
|
|
55
|
+
call_adapter(
|
|
56
|
+
:edit_message,
|
|
54
57
|
channel_id: channel_id,
|
|
55
58
|
message_id: @message_id,
|
|
56
|
-
message: PostableMessage.new(text: @buffer.dup)
|
|
59
|
+
message: PostableMessage.new(text: @buffer.dup),
|
|
60
|
+
thread_id: thread_id
|
|
57
61
|
)
|
|
58
62
|
elsif !@message_id
|
|
59
63
|
result = adapter.post_message(
|
|
@@ -50,7 +50,7 @@ module ChatSDK
|
|
|
50
50
|
)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
def edit_message(channel_id:, message_id:, message
|
|
53
|
+
def edit_message(channel_id:, message_id:, message:, **_options)
|
|
54
54
|
record = RecordedCall.new(:edit_message, channel_id: channel_id, message_id: message_id, message: message)
|
|
55
55
|
@edited_messages << record
|
|
56
56
|
record
|
|
@@ -68,7 +68,7 @@ module ChatSDK
|
|
|
68
68
|
record
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
def delete_message(channel_id:, message_id
|
|
71
|
+
def delete_message(channel_id:, message_id:, **_options)
|
|
72
72
|
record = RecordedCall.new(:delete_message, channel_id: channel_id, message_id: message_id)
|
|
73
73
|
@deleted_messages << record
|
|
74
74
|
record
|
|
@@ -86,13 +86,13 @@ module ChatSDK
|
|
|
86
86
|
record
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
def add_reaction(channel_id:, message_id:, emoji
|
|
89
|
+
def add_reaction(channel_id:, message_id:, emoji:, **_options)
|
|
90
90
|
record = RecordedCall.new(:add_reaction, channel_id: channel_id, message_id: message_id, emoji: emoji)
|
|
91
91
|
@reactions_added << record
|
|
92
92
|
record
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
-
def remove_reaction(channel_id:, message_id:, emoji
|
|
95
|
+
def remove_reaction(channel_id:, message_id:, emoji:, **_options)
|
|
96
96
|
record = RecordedCall.new(:remove_reaction, channel_id: channel_id, message_id: message_id, emoji: emoji)
|
|
97
97
|
@reactions_removed << record
|
|
98
98
|
record
|
|
@@ -122,8 +122,8 @@ module ChatSDK
|
|
|
122
122
|
record
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
-
def start_typing(channel_id:, thread_id: nil)
|
|
126
|
-
record = RecordedCall.new(:start_typing, channel_id: channel_id, thread_id: thread_id)
|
|
125
|
+
def start_typing(channel_id:, thread_id: nil, message_id: nil)
|
|
126
|
+
record = RecordedCall.new(:start_typing, channel_id: channel_id, thread_id: thread_id, message_id: message_id)
|
|
127
127
|
@typing_started << record
|
|
128
128
|
record
|
|
129
129
|
end
|
data/lib/chat_sdk/thread.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module ChatSDK
|
|
4
4
|
class Thread
|
|
5
|
+
include Adapter::Invocation
|
|
6
|
+
|
|
5
7
|
attr_reader :id, :channel_id, :adapter, :chat, :current_message
|
|
6
8
|
|
|
7
9
|
def initialize(id:, channel_id:, adapter:, chat:, current_message: nil)
|
|
@@ -51,6 +53,10 @@ module ChatSDK
|
|
|
51
53
|
adapter.mark_as_read(channel_id: channel_id, thread_id: id, message_id: message_id, message: target.is_a?(ChatSDK::Message) ? target : nil)
|
|
52
54
|
end
|
|
53
55
|
|
|
56
|
+
def start_typing
|
|
57
|
+
adapter.start_typing(channel_id: channel_id, thread_id: id, message_id: current_message&.id)
|
|
58
|
+
end
|
|
59
|
+
|
|
54
60
|
def post_ephemeral(content, user_id:)
|
|
55
61
|
message = PostableMessage.from(content)
|
|
56
62
|
adapter.post_ephemeral(channel_id: channel_id, user_id: user_id, message: message, thread_id: id)
|
|
@@ -69,19 +75,19 @@ module ChatSDK
|
|
|
69
75
|
|
|
70
76
|
def edit(message_id, content)
|
|
71
77
|
message = PostableMessage.from(content)
|
|
72
|
-
|
|
78
|
+
call_adapter(:edit_message, channel_id: channel_id, message_id: message_id, message: message, thread_id: id)
|
|
73
79
|
end
|
|
74
80
|
|
|
75
81
|
def delete(message_id)
|
|
76
|
-
|
|
82
|
+
call_adapter(:delete_message, channel_id: channel_id, message_id: message_id, thread_id: id)
|
|
77
83
|
end
|
|
78
84
|
|
|
79
85
|
def react(message_id, emoji)
|
|
80
|
-
|
|
86
|
+
call_adapter(:add_reaction, channel_id: channel_id, message_id: message_id, emoji: emoji, thread_id: id)
|
|
81
87
|
end
|
|
82
88
|
|
|
83
89
|
def unreact(message_id, emoji)
|
|
84
|
-
|
|
90
|
+
call_adapter(:remove_reaction, channel_id: channel_id, message_id: message_id, emoji: emoji, thread_id: id)
|
|
85
91
|
end
|
|
86
92
|
|
|
87
93
|
def post_ai_stream(enumerable, placeholder: "Thinking...")
|
data/lib/chat_sdk/version.rb
CHANGED
|
@@ -22,6 +22,9 @@ module ChatSDK
|
|
|
22
22
|
events = adapter.parse_events(request || env)
|
|
23
23
|
events.each { |event| chat.dispatch(event, adapter_name: adapter_name) }
|
|
24
24
|
|
|
25
|
+
response = adapter.webhook_response(events)
|
|
26
|
+
return response if response
|
|
27
|
+
|
|
25
28
|
[200, {"content-type" => "text/plain"}, [""]]
|
|
26
29
|
rescue ChatSDK::SignatureVerificationError => e
|
|
27
30
|
ChatSDK::Log.warn("Signature verification failed: #{e.message}")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: chat_sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Quentin Rousseau
|
|
@@ -48,6 +48,7 @@ files:
|
|
|
48
48
|
- lib/chat_sdk.rb
|
|
49
49
|
- lib/chat_sdk/adapter/base.rb
|
|
50
50
|
- lib/chat_sdk/adapter/capabilities.rb
|
|
51
|
+
- lib/chat_sdk/adapter/invocation.rb
|
|
51
52
|
- lib/chat_sdk/adapter/media_types.rb
|
|
52
53
|
- lib/chat_sdk/adapter/meta_verification.rb
|
|
53
54
|
- lib/chat_sdk/ai.rb
|
|
@@ -75,13 +76,16 @@ files:
|
|
|
75
76
|
- lib/chat_sdk/events/action.rb
|
|
76
77
|
- lib/chat_sdk/events/base.rb
|
|
77
78
|
- lib/chat_sdk/events/direct_message.rb
|
|
79
|
+
- lib/chat_sdk/events/installed.rb
|
|
78
80
|
- lib/chat_sdk/events/mention.rb
|
|
79
81
|
- lib/chat_sdk/events/message_deleted.rb
|
|
80
82
|
- lib/chat_sdk/events/message_updated.rb
|
|
81
83
|
- lib/chat_sdk/events/reaction.rb
|
|
82
84
|
- lib/chat_sdk/events/slash_command.rb
|
|
83
85
|
- lib/chat_sdk/events/subscribed_message.rb
|
|
86
|
+
- lib/chat_sdk/events/uninstalled.rb
|
|
84
87
|
- lib/chat_sdk/format/converter.rb
|
|
88
|
+
- lib/chat_sdk/format/mentions.rb
|
|
85
89
|
- lib/chat_sdk/history.rb
|
|
86
90
|
- lib/chat_sdk/instrumentation.rb
|
|
87
91
|
- lib/chat_sdk/log.rb
|