lex-slack 0.1.0 → 0.3.1

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.
data/lex-slack.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/extensions/slack/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'lex-slack'
7
+ spec.version = Legion::Extensions::Slack::VERSION
8
+ spec.authors = ['Esity']
9
+ spec.email = ['matthewdiverson@gmail.com']
10
+
11
+ spec.summary = 'LEX::Slack'
12
+ spec.description = 'Connects Legion to Slack via Web API and incoming webhooks'
13
+ spec.homepage = 'https://github.com/LegionIO/lex-slack'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.4'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/LegionIO/lex-slack'
19
+ spec.metadata['documentation_uri'] = 'https://github.com/LegionIO/lex-slack'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/LegionIO/lex-slack'
21
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/LegionIO/lex-slack/issues'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_dependency 'faraday', '>= 2.0'
29
+ spec.add_dependency 'legion-cache', '>= 1.3.11'
30
+ spec.add_dependency 'legion-crypt', '>= 1.4.9'
31
+ spec.add_dependency 'legion-data', '>= 1.4.17'
32
+ spec.add_dependency 'legion-json', '>= 1.2.1'
33
+ spec.add_dependency 'legion-logging', '>= 1.3.2'
34
+ spec.add_dependency 'legion-settings', '>= 1.3.14'
35
+ spec.add_dependency 'legion-transport', '>= 1.3.9'
36
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Actor
7
+ class MessagePoller
8
+ include Helpers::Client
9
+ include Legion::Logging::Helper
10
+
11
+ def initialize
12
+ @hwm = {}
13
+ end
14
+
15
+ def run
16
+ return unless enabled?
17
+ return if channels.empty?
18
+
19
+ token = settings.dig('lex-slack', 'token')
20
+ limit = settings.dig('lex-slack', 'poller', 'limit') || 50
21
+
22
+ channels.each do |channel_id|
23
+ poll_channel(channel_id, token: token, limit: limit)
24
+ end
25
+ end
26
+
27
+ def interval
28
+ settings.dig('lex-slack', 'poller', 'interval') || 30
29
+ end
30
+
31
+ private
32
+
33
+ def enabled?
34
+ settings.dig('lex-slack', 'poller', 'enabled') == true
35
+ end
36
+
37
+ def channels
38
+ settings.dig('lex-slack', 'poller', 'channels') || []
39
+ end
40
+
41
+ def poll_channel(channel_id, token:, limit:)
42
+ params = { channel: channel_id, limit: limit }
43
+ params[:oldest] = @hwm[channel_id] if @hwm[channel_id]
44
+
45
+ conn = api_connection(token: token)
46
+ resp = conn.get('conversations.history', params)
47
+ body = resp.body
48
+ return unless body['ok']
49
+
50
+ messages = body['messages'] || []
51
+ return if messages.empty?
52
+
53
+ @hwm[channel_id] = messages.map { |m| m['ts'] }.max
54
+ messages.each { |msg| publish_message(channel_id, msg) }
55
+ end
56
+
57
+ def publish_message(channel_id, message)
58
+ if defined?(Legion::Transport::Message)
59
+ Legion::Transport::Message.new(
60
+ function: 'handle_message',
61
+ namespace: 'Legion::Extensions::Slack',
62
+ args: { channel: channel_id, message: message }
63
+ )
64
+ else
65
+ log.info "Slack message in #{channel_id}: #{message['text']}"
66
+ end
67
+ rescue StandardError => e
68
+ log.warn "Failed to publish slack message: #{e.message}"
69
+ end
70
+
71
+ def settings
72
+ return Legion::Settings.to_h if defined?(Legion::Settings)
73
+
74
+ {}
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers/client'
4
+ require_relative 'runners/blocks'
5
+ require_relative 'runners/chat'
6
+ require_relative 'runners/conversations'
7
+ require_relative 'runners/users'
8
+ require_relative 'runners/reactions'
9
+ require_relative 'runners/files'
10
+ require_relative 'runners/pins'
11
+ require_relative 'runners/bookmarks'
12
+ require_relative 'runners/reminders'
13
+ require_relative 'runners/usergroups'
14
+ require_relative 'runners/views'
15
+ require_relative 'runners/search'
16
+
17
+ module Legion
18
+ module Extensions
19
+ module Slack
20
+ class Client
21
+ include Helpers::Client
22
+ include Runners::Blocks
23
+ include Runners::Chat
24
+ include Runners::Conversations
25
+ include Runners::Users
26
+ include Runners::Reactions
27
+ include Runners::Files
28
+ include Runners::Pins
29
+ include Runners::Bookmarks
30
+ include Runners::Reminders
31
+ include Runners::Usergroups
32
+ include Runners::Views
33
+ include Runners::Search
34
+
35
+ attr_reader :opts
36
+
37
+ def initialize(token: nil, webhook: nil, **extra)
38
+ @opts = { token: token, webhook: webhook, **extra }.compact
39
+ end
40
+
41
+ def api_connection(**override)
42
+ super(**@opts, **override)
43
+ end
44
+
45
+ def webhook_connection(**override)
46
+ super(**@opts, **override)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Slack
8
+ module Helpers
9
+ module Client
10
+ def api_connection(token: nil, base_url: 'https://slack.com/api/', **)
11
+ Faraday.new(url: base_url) do |conn|
12
+ conn.request :json
13
+ conn.response :json, content_type: /\bjson$/
14
+ conn.headers['Authorization'] = "Bearer #{token}" if token
15
+ conn.adapter Faraday.default_adapter
16
+ end
17
+ end
18
+
19
+ def webhook_connection(base_url: 'https://hooks.slack.com', **)
20
+ Faraday.new(url: base_url) do |conn|
21
+ conn.request :json
22
+ conn.response :json, content_type: /\bjson$/
23
+ conn.adapter Faraday.default_adapter
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Blocks
8
+ def mrkdwn(text)
9
+ { type: 'mrkdwn', text: text }
10
+ end
11
+
12
+ def plain_text(text, emoji: true)
13
+ { type: 'plain_text', text: text, emoji: emoji }
14
+ end
15
+
16
+ def option(text:, value:)
17
+ { text: plain_text(text), value: value }
18
+ end
19
+
20
+ def section(text:, accessory: nil, fields: nil)
21
+ block = { type: 'section', text: mrkdwn(text) }
22
+ block[:accessory] = accessory if accessory
23
+ block[:fields] = fields if fields
24
+ block
25
+ end
26
+
27
+ def divider
28
+ { type: 'divider' }
29
+ end
30
+
31
+ def header(text:)
32
+ { type: 'header', text: plain_text(text) }
33
+ end
34
+
35
+ def context(elements:)
36
+ { type: 'context', elements: elements }
37
+ end
38
+
39
+ def actions(elements:, block_id: nil)
40
+ block = { type: 'actions', elements: elements }
41
+ block[:block_id] = block_id if block_id
42
+ block
43
+ end
44
+
45
+ def image(image_url:, alt_text:, title: nil, block_id: nil)
46
+ block = { type: 'image', image_url: image_url, alt_text: alt_text }
47
+ block[:title] = plain_text(title) if title
48
+ block[:block_id] = block_id if block_id
49
+ block
50
+ end
51
+
52
+ def input(label:, element:, block_id: nil, optional: false, dispatch_action: false)
53
+ block = { type: 'input', label: plain_text(label), element: element,
54
+ optional: optional, dispatch_action: dispatch_action }
55
+ block[:block_id] = block_id if block_id
56
+ block
57
+ end
58
+
59
+ def file_block(external_id:, block_id: nil)
60
+ block = { type: 'file', external_id: external_id, source: 'remote' }
61
+ block[:block_id] = block_id if block_id
62
+ block
63
+ end
64
+
65
+ def button(text:, action_id:, value: nil, style: nil, url: nil)
66
+ el = { type: 'button', text: plain_text(text), action_id: action_id }
67
+ el[:value] = value if value
68
+ el[:style] = style if style
69
+ el[:url] = url if url
70
+ el
71
+ end
72
+
73
+ def overflow_menu(action_id:, options:)
74
+ { type: 'overflow', action_id: action_id, options: options }
75
+ end
76
+
77
+ def static_select(action_id:, placeholder:, options:)
78
+ { type: 'static_select', action_id: action_id,
79
+ placeholder: plain_text(placeholder), options: options }
80
+ end
81
+
82
+ def multi_static_select(action_id:, placeholder:, options:)
83
+ { type: 'multi_static_select', action_id: action_id,
84
+ placeholder: plain_text(placeholder), options: options }
85
+ end
86
+
87
+ def datepicker(action_id:, placeholder: nil, initial_date: nil)
88
+ el = { type: 'datepicker', action_id: action_id }
89
+ el[:placeholder] = plain_text(placeholder) if placeholder
90
+ el[:initial_date] = initial_date if initial_date
91
+ el
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Bookmarks
8
+ include Helpers::Client
9
+
10
+ def add_bookmark(channel:, title:, type:, token: nil, link: nil, emoji: nil, **)
11
+ payload = { channel_id: channel, title: title, type: type }
12
+ payload[:link] = link if link
13
+ payload[:emoji] = emoji if emoji
14
+ conn = api_connection(token: token, **)
15
+ resp = conn.post('bookmarks.add', payload)
16
+ { ok: resp.body['ok'], bookmark: resp.body['bookmark'] }
17
+ end
18
+
19
+ def edit_bookmark(channel:, bookmark_id:, token: nil, title: nil, link: nil, emoji: nil, **)
20
+ payload = { channel_id: channel, bookmark_id: bookmark_id }
21
+ payload[:title] = title if title
22
+ payload[:link] = link if link
23
+ payload[:emoji] = emoji if emoji
24
+ conn = api_connection(token: token, **)
25
+ resp = conn.post('bookmarks.edit', payload)
26
+ { ok: resp.body['ok'], bookmark: resp.body['bookmark'] }
27
+ end
28
+
29
+ def remove_bookmark(channel:, bookmark_id:, token: nil, **)
30
+ conn = api_connection(token: token, **)
31
+ resp = conn.post('bookmarks.remove', { channel_id: channel, bookmark_id: bookmark_id })
32
+ { ok: resp.body['ok'] }
33
+ end
34
+
35
+ def list_bookmarks(channel:, token: nil, **)
36
+ conn = api_connection(token: token, **)
37
+ resp = conn.get('bookmarks.list', { channel_id: channel })
38
+ { ok: resp.body['ok'], bookmarks: resp.body['bookmarks'] }
39
+ end
40
+
41
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Chat
8
+ include Helpers::Client
9
+
10
+ def post_message(channel:, text:, token: nil, blocks: nil, thread_ts: nil, unfurl_links: nil, **)
11
+ payload = { channel: channel, text: text }
12
+ payload[:blocks] = blocks if blocks
13
+ payload[:thread_ts] = thread_ts if thread_ts
14
+ payload[:unfurl_links] = unfurl_links unless unfurl_links.nil?
15
+ conn = api_connection(token: token, **)
16
+ resp = conn.post('chat.postMessage', payload)
17
+ { ok: resp.body['ok'], ts: resp.body['ts'], channel: resp.body['channel'],
18
+ message: resp.body['message'] }
19
+ end
20
+
21
+ def send_webhook(message:, webhook:, **)
22
+ conn = webhook_connection(**)
23
+ resp = conn.post(webhook, { text: message })
24
+ { success: resp.body.nil? || resp.body == 'ok' || resp.body == {}, body: resp.body }
25
+ end
26
+
27
+ def update_message(channel:, ts:, token: nil, text: nil, blocks: nil, **)
28
+ payload = { channel: channel, ts: ts }
29
+ payload[:text] = text if text
30
+ payload[:blocks] = blocks if blocks
31
+ conn = api_connection(token: token, **)
32
+ resp = conn.post('chat.update', payload)
33
+ { ok: resp.body['ok'], ts: resp.body['ts'], channel: resp.body['channel'] }
34
+ end
35
+
36
+ def delete_message(channel:, ts:, token: nil, **)
37
+ conn = api_connection(token: token, **)
38
+ resp = conn.post('chat.delete', { channel: channel, ts: ts })
39
+ { ok: resp.body['ok'] }
40
+ end
41
+
42
+ def schedule_message(channel:, text:, post_at:, token: nil, blocks: nil, **)
43
+ payload = { channel: channel, text: text, post_at: post_at }
44
+ payload[:blocks] = blocks if blocks
45
+ conn = api_connection(token: token, **)
46
+ resp = conn.post('chat.scheduleMessage', payload)
47
+ { ok: resp.body['ok'], scheduled_message_id: resp.body['scheduled_message_id'],
48
+ post_at: resp.body['post_at'] }
49
+ end
50
+
51
+ def delete_scheduled(channel:, scheduled_message_id:, token: nil, **)
52
+ conn = api_connection(token: token, **)
53
+ resp = conn.post('chat.deleteScheduledMessage',
54
+ { channel: channel, scheduled_message_id: scheduled_message_id })
55
+ { ok: resp.body['ok'] }
56
+ end
57
+
58
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Conversations
8
+ include Helpers::Client
9
+
10
+ def list_conversations(token: nil, types: nil, cursor: nil, limit: 200, **)
11
+ params = { limit: limit }
12
+ params[:types] = types if types
13
+ params[:cursor] = cursor if cursor
14
+ conn = api_connection(token: token, **)
15
+ resp = conn.get('conversations.list', params)
16
+ { ok: resp.body['ok'], channels: resp.body['channels'],
17
+ response_metadata: resp.body['response_metadata'] }
18
+ end
19
+
20
+ def conversation_info(channel:, token: nil, **)
21
+ conn = api_connection(token: token, **)
22
+ resp = conn.get('conversations.info', { channel: channel })
23
+ { ok: resp.body['ok'], channel: resp.body['channel'] }
24
+ end
25
+
26
+ def conversation_history(channel:, token: nil, cursor: nil, limit: 100, oldest: nil, latest: nil, **)
27
+ params = { channel: channel, limit: limit }
28
+ params[:cursor] = cursor if cursor
29
+ params[:oldest] = oldest if oldest
30
+ params[:latest] = latest if latest
31
+ conn = api_connection(token: token, **)
32
+ resp = conn.get('conversations.history', params)
33
+ { ok: resp.body['ok'], messages: resp.body['messages'],
34
+ has_more: resp.body['has_more'], response_metadata: resp.body['response_metadata'] }
35
+ end
36
+
37
+ def conversation_replies(channel:, ts:, token: nil, cursor: nil, limit: 100, **)
38
+ params = { channel: channel, ts: ts, limit: limit }
39
+ params[:cursor] = cursor if cursor
40
+ conn = api_connection(token: token, **)
41
+ resp = conn.get('conversations.replies', params)
42
+ { ok: resp.body['ok'], messages: resp.body['messages'], has_more: resp.body['has_more'] }
43
+ end
44
+
45
+ def conversation_members(channel:, token: nil, cursor: nil, limit: 100, **)
46
+ params = { channel: channel, limit: limit }
47
+ params[:cursor] = cursor if cursor
48
+ conn = api_connection(token: token, **)
49
+ resp = conn.get('conversations.members', params)
50
+ { ok: resp.body['ok'], members: resp.body['members'],
51
+ response_metadata: resp.body['response_metadata'] }
52
+ end
53
+
54
+ def join_conversation(channel:, token: nil, **)
55
+ conn = api_connection(token: token, **)
56
+ resp = conn.post('conversations.join', { channel: channel })
57
+ { ok: resp.body['ok'], channel: resp.body['channel'] }
58
+ end
59
+
60
+ def leave_conversation(channel:, token: nil, **)
61
+ conn = api_connection(token: token, **)
62
+ resp = conn.post('conversations.leave', { channel: channel })
63
+ { ok: resp.body['ok'] }
64
+ end
65
+
66
+ def invite_to_conversation(channel:, users:, token: nil, **)
67
+ conn = api_connection(token: token, **)
68
+ resp = conn.post('conversations.invite', { channel: channel, users: users })
69
+ { ok: resp.body['ok'], channel: resp.body['channel'] }
70
+ end
71
+
72
+ def open_conversation(token: nil, users: nil, channel: nil, **)
73
+ payload = {}
74
+ payload[:users] = users if users
75
+ payload[:channel] = channel if channel
76
+ conn = api_connection(token: token, **)
77
+ resp = conn.post('conversations.open', payload)
78
+ { ok: resp.body['ok'], channel: resp.body['channel'] }
79
+ end
80
+
81
+ def create_conversation(name:, token: nil, is_private: false, **)
82
+ conn = api_connection(token: token, **)
83
+ resp = conn.post('conversations.create', { name: name, is_private: is_private })
84
+ { ok: resp.body['ok'], channel: resp.body['channel'] }
85
+ end
86
+
87
+ def archive_conversation(channel:, token: nil, **)
88
+ conn = api_connection(token: token, **)
89
+ resp = conn.post('conversations.archive', { channel: channel })
90
+ { ok: resp.body['ok'] }
91
+ end
92
+
93
+ def set_conversation_topic(channel:, topic:, token: nil, **)
94
+ conn = api_connection(token: token, **)
95
+ resp = conn.post('conversations.setTopic', { channel: channel, topic: topic })
96
+ { ok: resp.body['ok'], topic: resp.body['topic'] }
97
+ end
98
+
99
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Files
8
+ include Helpers::Client
9
+
10
+ def upload_file(filename:, content:, token: nil, channel: nil, title: nil, **)
11
+ conn = api_connection(token: token, **)
12
+ url_resp = conn.get('files.getUploadURLExternal',
13
+ { filename: filename, length: content.bytesize })
14
+ upload_url = url_resp.body['upload_url']
15
+ file_id = url_resp.body['file_id']
16
+ Faraday.put(upload_url, content, { 'Content-Type' => 'application/octet-stream' })
17
+ complete_payload = { files: [{ id: file_id, title: title || filename }] }
18
+ complete_payload[:channel_id] = channel if channel
19
+ complete_resp = conn.post('files.completeUploadExternal', complete_payload)
20
+ { ok: complete_resp.body['ok'], files: complete_resp.body['files'] }
21
+ end
22
+
23
+ def list_files(token: nil, channel: nil, user: nil, types: nil, cursor: nil, limit: 100, **)
24
+ params = { limit: limit }
25
+ params[:channel] = channel if channel
26
+ params[:user] = user if user
27
+ params[:types] = types if types
28
+ params[:cursor] = cursor if cursor
29
+ conn = api_connection(token: token, **)
30
+ resp = conn.get('files.list', params)
31
+ { ok: resp.body['ok'], files: resp.body['files'] }
32
+ end
33
+
34
+ def file_info(file:, token: nil, **)
35
+ conn = api_connection(token: token, **)
36
+ resp = conn.get('files.info', { file: file })
37
+ { ok: resp.body['ok'], file: resp.body['file'] }
38
+ end
39
+
40
+ def delete_file(file:, token: nil, **)
41
+ conn = api_connection(token: token, **)
42
+ resp = conn.post('files.delete', { file: file })
43
+ { ok: resp.body['ok'] }
44
+ end
45
+
46
+ def share_file(file:, token: nil, **)
47
+ conn = api_connection(token: token, **)
48
+ resp = conn.post('files.sharedPublicURL', { file: file })
49
+ { ok: resp.body['ok'], file: resp.body['file'] }
50
+ end
51
+
52
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Pins
8
+ include Helpers::Client
9
+
10
+ def add_pin(channel:, timestamp:, token: nil, **)
11
+ conn = api_connection(token: token, **)
12
+ resp = conn.post('pins.add', { channel: channel, timestamp: timestamp })
13
+ { ok: resp.body['ok'] }
14
+ end
15
+
16
+ def remove_pin(channel:, timestamp:, token: nil, **)
17
+ conn = api_connection(token: token, **)
18
+ resp = conn.post('pins.remove', { channel: channel, timestamp: timestamp })
19
+ { ok: resp.body['ok'] }
20
+ end
21
+
22
+ def list_pins(channel:, token: nil, **)
23
+ conn = api_connection(token: token, **)
24
+ resp = conn.get('pins.list', { channel: channel })
25
+ { ok: resp.body['ok'], items: resp.body['items'] }
26
+ end
27
+
28
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Slack
6
+ module Runners
7
+ module Reactions
8
+ include Helpers::Client
9
+
10
+ def add_reaction(channel:, timestamp:, name:, token: nil, **)
11
+ conn = api_connection(token: token, **)
12
+ resp = conn.post('reactions.add', { channel: channel, timestamp: timestamp, name: name })
13
+ { ok: resp.body['ok'] }
14
+ end
15
+
16
+ def remove_reaction(channel:, timestamp:, name:, token: nil, **)
17
+ conn = api_connection(token: token, **)
18
+ resp = conn.post('reactions.remove', { channel: channel, timestamp: timestamp, name: name })
19
+ { ok: resp.body['ok'] }
20
+ end
21
+
22
+ def get_reactions(channel:, timestamp:, token: nil, **)
23
+ conn = api_connection(token: token, **)
24
+ resp = conn.get('reactions.get', { channel: channel, timestamp: timestamp })
25
+ { ok: resp.body['ok'], message: resp.body['message'] }
26
+ end
27
+
28
+ def list_reactions(token: nil, user: nil, cursor: nil, limit: 100, **)
29
+ params = { limit: limit }
30
+ params[:user] = user if user
31
+ params[:cursor] = cursor if cursor
32
+ conn = api_connection(token: token, **)
33
+ resp = conn.get('reactions.list', params)
34
+ { ok: resp.body['ok'], items: resp.body['items'],
35
+ response_metadata: resp.body['response_metadata'] }
36
+ end
37
+
38
+ include Legion::Extensions::Helpers::Lex if defined?(Legion::Extensions::Helpers::Lex)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end