Class: ChatSDK::Mattermost::Adapter

Inherits:
Adapter::Base
  • Object
show all
Defined in:
lib/chat_sdk/mattermost/adapter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url: nil, bot_token: nil, webhook_token: nil, bot_user_id: nil) ⇒ Adapter

Returns a new instance of Adapter.

Raises:

  • (ChatSDK::ConfigurationError)


14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/chat_sdk/mattermost/adapter.rb', line 14

def initialize(base_url: nil, bot_token: nil, webhook_token: nil, bot_user_id: nil)
  @base_url = base_url || ENV["MATTERMOST_BASE_URL"]
  @bot_token = bot_token || ENV["MATTERMOST_BOT_TOKEN"]
  @webhook_token = webhook_token || ENV["MATTERMOST_WEBHOOK_TOKEN"]
  @bot_user_id = bot_user_id || ENV["MATTERMOST_BOT_USER_ID"]

  raise ChatSDK::ConfigurationError, "Mattermost base_url required" unless @base_url
  raise ChatSDK::ConfigurationError, "Mattermost bot_token required" unless @bot_token

  @client = ApiClient.new(base_url: @base_url, bot_token: @bot_token)
  @renderer = AttachmentRenderer.new
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



12
13
14
# File 'lib/chat_sdk/mattermost/adapter.rb', line 12

def client
  @client
end

Instance Method Details

#add_reaction(channel_id:, message_id:, emoji:) ⇒ Object

Raises:

  • (ChatSDK::ConfigurationError)


134
135
136
137
138
139
140
141
142
143
# File 'lib/chat_sdk/mattermost/adapter.rb', line 134

def add_reaction(channel_id:, message_id:, emoji:)
  require_capability!(:reactions)
  raise ChatSDK::ConfigurationError, "bot_user_id required for reactions" unless @bot_user_id

  @client.add_reaction(
    user_id: @bot_user_id,
    post_id: message_id,
    emoji_name: emoji
  )
end

#delete_message(channel_id:, message_id:) ⇒ Object



104
105
106
107
# File 'lib/chat_sdk/mattermost/adapter.rb', line 104

def delete_message(channel_id:, message_id:)
  require_capability!(:delete_messages)
  @client.delete_post(post_id: message_id)
end

#edit_message(channel_id:, message_id:, message:) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/chat_sdk/mattermost/adapter.rb', line 87

def edit_message(channel_id:, message_id:, message:)
  require_capability!(:edit_messages)
  msg = ChatSDK::PostableMessage.from(message)

  props = nil
  if msg.card?
    attachments = @renderer.render(msg.card)
    props = {"attachments" => attachments}
  end

  @client.update_post(
    post_id: message_id,
    message: msg.text || msg.card&.fallback_text || "",
    props: props
  )
end

#fetch_messages(channel_id:, thread_id: nil, cursor: nil, limit: 50) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/chat_sdk/mattermost/adapter.rb', line 177

def fetch_messages(channel_id:, thread_id: nil, cursor: nil, limit: 50)
  require_capability!(:message_history)

  if thread_id
    result = @client.get_post_thread(post_id: thread_id)
  else
    page = cursor&.to_i || 0
    result = @client.get_channel_posts(channel_id: channel_id, page: page, per_page: limit)
  end

  order = result["order"] || []
  posts = result["posts"] || {}

  messages = order.map do |post_id|
    post = posts[post_id]
    next unless post

    ChatSDK::Message.new(
      id: post["id"],
      text: post["message"] || "",
      author: ChatSDK::Author.new(
        id: post["user_id"] || "unknown",
        name: post["user_id"] || "unknown",
        platform: :mattermost
      ),
      thread_id: post["root_id"].to_s.empty? ? post["id"] : post["root_id"],
      channel_id: post["channel_id"],
      platform: :mattermost,
      raw: post
    )
  end.compact

  next_cursor = thread_id ? nil : ((cursor&.to_i || 0) + 1).to_s
  [messages, next_cursor]
end

#get_user(user_id) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/chat_sdk/mattermost/adapter.rb', line 156

def get_user(user_id)
  data = @client.get_user(user_id)
  return nil unless data && data["id"]

  ChatSDK::Author.new(
    id: data["id"],
    name: data["username"],
    platform: :mattermost,
    bot: data["is_bot"] || false,
    raw: data
  )
end

#mention(user_id) ⇒ Object



222
223
224
# File 'lib/chat_sdk/mattermost/adapter.rb', line 222

def mention(user_id)
  "@#{user_id}"
end

#nameObject



27
28
29
# File 'lib/chat_sdk/mattermost/adapter.rb', line 27

def name
  :mattermost
end

#open_dm(user_id) ⇒ Object

Raises:

  • (ChatSDK::ConfigurationError)


169
170
171
172
173
174
175
# File 'lib/chat_sdk/mattermost/adapter.rb', line 169

def open_dm(user_id)
  require_capability!(:direct_messages)
  raise ChatSDK::ConfigurationError, "bot_user_id required for DMs" unless @bot_user_id

  result = @client.create_direct_channel([@bot_user_id, user_id])
  result["id"]
end

#open_modal(trigger_id:, modal:) ⇒ Object



213
214
215
# File 'lib/chat_sdk/mattermost/adapter.rb', line 213

def open_modal(trigger_id:, modal:)
  super # raises NotSupportedError
end

#parse_events(rack_request) ⇒ Object



52
53
54
55
56
57
# File 'lib/chat_sdk/mattermost/adapter.rb', line 52

def parse_events(rack_request)
  payload = read_json_body(rack_request)
  EventParser.parse(payload, bot_user_id: @bot_user_id)
rescue JSON::ParserError
  []
end

#post_ephemeral(channel_id:, user_id:, message:, thread_id: nil) ⇒ Object



109
110
111
112
113
114
115
116
117
# File 'lib/chat_sdk/mattermost/adapter.rb', line 109

def post_ephemeral(channel_id:, user_id:, message:, thread_id: nil)
  require_capability!(:ephemeral_messages)
  msg = ChatSDK::PostableMessage.from(message)
  @client.create_ephemeral_post(
    user_id: user_id,
    channel_id: channel_id,
    message: msg.text || msg.card&.fallback_text || ""
  )
end

#post_message(channel_id:, message:, thread_id: nil) ⇒ Object

Outbound



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/chat_sdk/mattermost/adapter.rb', line 60

def post_message(channel_id:, message:, thread_id: nil)
  msg = ChatSDK::PostableMessage.from(message)

  props = nil
  if msg.card?
    attachments = @renderer.render(msg.card)
    props = {"attachments" => attachments}
  end

  result = @client.create_post(
    channel_id: channel_id,
    message: msg.text || msg.card&.fallback_text || "",
    root_id: thread_id,
    props: props
  )

  ChatSDK::Message.new(
    id: result["id"],
    text: msg.text || "",
    author: ChatSDK::Author.new(id: @bot_user_id || "bot", name: "bot", platform: :mattermost, bot: true),
    thread_id: thread_id || result["id"],
    channel_id: channel_id,
    platform: :mattermost,
    raw: result
  )
end

#remove_reaction(channel_id:, message_id:, emoji:) ⇒ Object

Raises:

  • (ChatSDK::ConfigurationError)


145
146
147
148
149
150
151
152
153
154
# File 'lib/chat_sdk/mattermost/adapter.rb', line 145

def remove_reaction(channel_id:, message_id:, emoji:)
  require_capability!(:reactions)
  raise ChatSDK::ConfigurationError, "bot_user_id required for reactions" unless @bot_user_id

  @client.remove_reaction(
    user_id: @bot_user_id,
    post_id: message_id,
    emoji_name: emoji
  )
end

#render(postable_message) ⇒ Object



226
227
228
229
230
231
232
233
# File 'lib/chat_sdk/mattermost/adapter.rb', line 226

def render(postable_message)
  msg = ChatSDK::PostableMessage.from(postable_message)
  if msg.card?
    @renderer.render(msg.card)
  else
    msg.text
  end
end

#start_typing(channel_id:, thread_id: nil) ⇒ Object



217
218
219
220
# File 'lib/chat_sdk/mattermost/adapter.rb', line 217

def start_typing(channel_id:, thread_id: nil)
  require_capability!(:typing_indicator)
  @client.send_typing(channel_id: channel_id)
end

#upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/chat_sdk/mattermost/adapter.rb', line 119

def upload_file(channel_id:, io:, filename:, thread_id: nil, comment: nil)
  require_capability!(:file_uploads)

  file_result = @client.upload_file(channel_id: channel_id, io: io, filename: filename)
  file_ids = file_result.dig("file_infos")&.map { |fi| fi["id"] } || []

  @client.create_post(
    channel_id: channel_id,
    message: comment || "",
    root_id: thread_id,
    props: nil,
    file_ids: file_ids
  )
end

#verify_request!(rack_request) ⇒ Object

Inbound



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/chat_sdk/mattermost/adapter.rb', line 32

def verify_request!(rack_request)
  body = rack_request.body.read
  rack_request.body.rewind

  payload = begin
    JSON.parse(body)
  rescue JSON::ParserError
    raise ChatSDK::SignatureVerificationError, "Invalid JSON payload"
  end

  return true unless @webhook_token

  token = payload["token"]
  unless token && Rack::Utils.secure_compare(token, @webhook_token)
    raise ChatSDK::SignatureVerificationError, "Invalid webhook token"
  end

  true
end