slk 0.4.2 → 0.6.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/CHANGELOG.md +53 -0
- data/lib/slk/api/team.rb +24 -0
- data/lib/slk/api/users.rb +10 -0
- data/lib/slk/cli.rb +9 -1
- data/lib/slk/commands/base.rb +5 -2
- data/lib/slk/commands/debug.rb +110 -0
- data/lib/slk/commands/messages.rb +44 -6
- data/lib/slk/commands/org.rb +119 -0
- data/lib/slk/commands/thread.rb +11 -6
- data/lib/slk/commands/who.rb +115 -0
- data/lib/slk/formatters/attachment_formatter.rb +22 -7
- data/lib/slk/formatters/message_formatter.rb +13 -4
- data/lib/slk/formatters/output.rb +2 -0
- data/lib/slk/formatters/profile_field_renderer.rb +107 -0
- data/lib/slk/formatters/profile_formatter.rb +87 -0
- data/lib/slk/formatters/profile_rows.rb +72 -0
- data/lib/slk/models/profile.rb +71 -0
- data/lib/slk/models/profile_field.rb +29 -0
- data/lib/slk/runner.rb +17 -0
- data/lib/slk/services/api_client.rb +75 -10
- data/lib/slk/services/cache_store.rb +46 -2
- data/lib/slk/services/file_downloader.rb +180 -0
- data/lib/slk/services/meta_cache.rb +32 -0
- data/lib/slk/services/profile_builder.rb +129 -0
- data/lib/slk/services/profile_resolver.rb +138 -0
- data/lib/slk/services/user_lookup.rb +9 -13
- data/lib/slk/services/user_matcher.rb +64 -0
- data/lib/slk/services/user_picker.rb +68 -0
- data/lib/slk/services/who_target_resolver.rb +64 -0
- data/lib/slk/version.rb +1 -1
- data/lib/slk.rb +45 -1
- metadata +18 -2
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Services
|
|
5
|
+
# Disambiguates between multiple matching users. Prompts at a TTY; raises
|
|
6
|
+
# ApiError in non-interactive contexts so callers don't silently get the
|
|
7
|
+
# wrong user when name resolution is ambiguous.
|
|
8
|
+
class UserPicker
|
|
9
|
+
def initialize(stdin: $stdin, prompt_io: $stderr)
|
|
10
|
+
@stdin = stdin
|
|
11
|
+
@prompt_io = prompt_io
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def pick(matches)
|
|
15
|
+
return matches.first['id'] if matches.size == 1
|
|
16
|
+
|
|
17
|
+
unless interactive?
|
|
18
|
+
raise ApiError,
|
|
19
|
+
"Ambiguous match (#{matches.size} users): #{ids(matches).join(', ')}. " \
|
|
20
|
+
'Use --pick N or --all to disambiguate non-interactively.'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
list(matches)
|
|
24
|
+
matches[read_index(matches.size)]['id']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def interactive?
|
|
30
|
+
@stdin.respond_to?(:tty?) && @stdin.tty?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def list(matches)
|
|
34
|
+
@prompt_io.puts('Multiple users match — pick one:')
|
|
35
|
+
matches.each_with_index { |u, i| @prompt_io.puts(" [#{i + 1}] #{describe(u)}") }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def describe(user)
|
|
39
|
+
profile = user['profile'] || {}
|
|
40
|
+
name = profile['real_name'] || profile['display_name'] || user['name']
|
|
41
|
+
suffix = profile['title'].to_s.empty? ? '' : " — #{profile['title']}"
|
|
42
|
+
"#{name} (#{user['id']})#{suffix}#{flag_suffix(user)}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def flag_suffix(user)
|
|
46
|
+
flags = []
|
|
47
|
+
flags << 'deactivated' if user['deleted']
|
|
48
|
+
flags << 'bot' if user['is_bot']
|
|
49
|
+
flags.empty? ? '' : " [#{flags.join(', ')}]"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def ids(matches)
|
|
53
|
+
matches.map { |u| u['id'] }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def read_index(count)
|
|
57
|
+
loop do
|
|
58
|
+
@prompt_io.print("Choice [1-#{count}]: ")
|
|
59
|
+
choice = @stdin.gets&.strip
|
|
60
|
+
raise ApiError, 'No selection made' if choice.nil? || choice.empty?
|
|
61
|
+
|
|
62
|
+
n = Integer(choice, exception: false)
|
|
63
|
+
return n - 1 if n&.between?(1, count)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Services
|
|
5
|
+
# Resolves the positional target for `slk who` into one or more user_ids.
|
|
6
|
+
class WhoTargetResolver
|
|
7
|
+
def initialize(workspace:, cache_store:, api_client:, output:, options:)
|
|
8
|
+
@workspace = workspace
|
|
9
|
+
@cache_store = cache_store
|
|
10
|
+
@api_client = api_client
|
|
11
|
+
@output = output
|
|
12
|
+
@options = options
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def resolve(target)
|
|
16
|
+
return [self_user_id] if target.nil? || target == 'me'
|
|
17
|
+
return [target] if target.match?(/\A[UW][A-Z0-9]+\z/)
|
|
18
|
+
|
|
19
|
+
ids = resolve_by_name(target.delete_prefix('@'))
|
|
20
|
+
ids || (raise ApiError, "Could not resolve user: #{target}")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def resolve_by_name(name)
|
|
26
|
+
matches = lookup.find_all_by_name(name)
|
|
27
|
+
return select(matches) if matches.any?
|
|
28
|
+
|
|
29
|
+
cached = @cache_store.get_user_id_by_name(@workspace.name, name)
|
|
30
|
+
cached ? [cached] : nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def select(matches)
|
|
34
|
+
return matches.map { |u| u['id'] } if @options[:all]
|
|
35
|
+
return [pick_by_index(matches)] if @options[:pick]
|
|
36
|
+
|
|
37
|
+
[UserPicker.new.pick(matches)]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def pick_by_index(matches)
|
|
41
|
+
idx = @options[:pick]
|
|
42
|
+
raise ApiError, "--pick #{idx} out of range (got #{matches.size} matches)" unless idx&.between?(1, matches.size)
|
|
43
|
+
|
|
44
|
+
matches[idx - 1]['id']
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def lookup
|
|
48
|
+
UserLookup.new(
|
|
49
|
+
cache_store: @cache_store, workspace: @workspace,
|
|
50
|
+
api_client: @api_client, on_debug: ->(msg) { @output.debug(msg) }
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self_user_id
|
|
55
|
+
cached = @cache_store.get_meta(@workspace.name, 'self_user_id')
|
|
56
|
+
return cached if cached
|
|
57
|
+
|
|
58
|
+
user_id = Api::Client.new(@api_client, @workspace).auth_test['user_id']
|
|
59
|
+
@cache_store.set_meta(@workspace.name, 'self_user_id', user_id) if user_id
|
|
60
|
+
user_id
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/slk/version.rb
CHANGED
data/lib/slk.rb
CHANGED
|
@@ -11,7 +11,35 @@ require 'io/console'
|
|
|
11
11
|
# Slack CLI - A command-line interface for Slack
|
|
12
12
|
module Slk
|
|
13
13
|
class Error < StandardError; end
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
# Errors from any Slack-API-shaped failure: HTTP errors, network errors,
|
|
16
|
+
# logical Slack errors (user_not_found, missing_scope, etc.), JSON parse
|
|
17
|
+
# failures. The optional `code` symbol lets callers match specific cases
|
|
18
|
+
# (e.g. `e.code == :user_not_found`) without parsing message strings.
|
|
19
|
+
#
|
|
20
|
+
# Codes used:
|
|
21
|
+
# :network_error, :http_error, :unauthorized, :invalid_json,
|
|
22
|
+
# :ratelimited, plus any literal Slack `error` value (`:user_not_found`,
|
|
23
|
+
# `:missing_scope`, `:account_inactive`, etc.).
|
|
24
|
+
class ApiError < Error
|
|
25
|
+
attr_reader :code
|
|
26
|
+
|
|
27
|
+
def initialize(message, code: nil)
|
|
28
|
+
super(message)
|
|
29
|
+
@code = code
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Slack rate-limit error. Carries Retry-After in seconds when present.
|
|
34
|
+
class RateLimitError < ApiError
|
|
35
|
+
attr_reader :retry_after
|
|
36
|
+
|
|
37
|
+
def initialize(message, retry_after: nil)
|
|
38
|
+
super(message, code: :ratelimited)
|
|
39
|
+
@retry_after = retry_after
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
15
43
|
class ConfigError < Error; end
|
|
16
44
|
class EncryptionError < Error; end
|
|
17
45
|
class TokenStoreError < Error; end
|
|
@@ -34,6 +62,8 @@ module Slk
|
|
|
34
62
|
autoload :Preset, 'slk/models/preset'
|
|
35
63
|
autoload :SearchResult, 'slk/models/search_result'
|
|
36
64
|
autoload :SavedItem, 'slk/models/saved_item'
|
|
65
|
+
autoload :Profile, 'slk/models/profile'
|
|
66
|
+
autoload :ProfileField, 'slk/models/profile_field'
|
|
37
67
|
end
|
|
38
68
|
|
|
39
69
|
# Application services for configuration, caching, and API communication
|
|
@@ -49,13 +79,20 @@ module Slk
|
|
|
49
79
|
autoload :ReactionEnricher, 'slk/services/reaction_enricher'
|
|
50
80
|
autoload :GemojiSync, 'slk/services/gemoji_sync'
|
|
51
81
|
autoload :EmojiDownloader, 'slk/services/emoji_downloader'
|
|
82
|
+
autoload :FileDownloader, 'slk/services/file_downloader'
|
|
52
83
|
autoload :EmojiSearcher, 'slk/services/emoji_searcher'
|
|
53
84
|
autoload :ActivityEnricher, 'slk/services/activity_enricher'
|
|
54
85
|
autoload :UnreadMarker, 'slk/services/unread_marker'
|
|
55
86
|
autoload :TargetResolver, 'slk/services/target_resolver'
|
|
56
87
|
autoload :SetupWizard, 'slk/services/setup_wizard'
|
|
57
88
|
autoload :UserLookup, 'slk/services/user_lookup'
|
|
89
|
+
autoload :UserMatcher, 'slk/services/user_matcher'
|
|
90
|
+
autoload :UserPicker, 'slk/services/user_picker'
|
|
91
|
+
autoload :WhoTargetResolver, 'slk/services/who_target_resolver'
|
|
58
92
|
autoload :MessageResolver, 'slk/services/message_resolver'
|
|
93
|
+
autoload :ProfileBuilder, 'slk/services/profile_builder'
|
|
94
|
+
autoload :ProfileResolver, 'slk/services/profile_resolver'
|
|
95
|
+
autoload :MetaCache, 'slk/services/meta_cache'
|
|
59
96
|
end
|
|
60
97
|
|
|
61
98
|
# Output formatters for messages, durations, and emoji
|
|
@@ -74,6 +111,9 @@ module Slk
|
|
|
74
111
|
autoload :SearchFormatter, 'slk/formatters/search_formatter'
|
|
75
112
|
autoload :SavedItemFormatter, 'slk/formatters/saved_item_formatter'
|
|
76
113
|
autoload :TextProcessor, 'slk/formatters/text_processor'
|
|
114
|
+
autoload :ProfileFormatter, 'slk/formatters/profile_formatter'
|
|
115
|
+
autoload :ProfileFieldRenderer, 'slk/formatters/profile_field_renderer'
|
|
116
|
+
autoload :ProfileRows, 'slk/formatters/profile_rows'
|
|
77
117
|
end
|
|
78
118
|
|
|
79
119
|
# CLI commands implementing user-facing functionality
|
|
@@ -95,6 +135,9 @@ module Slk
|
|
|
95
135
|
autoload :Config, 'slk/commands/config'
|
|
96
136
|
autoload :Help, 'slk/commands/help'
|
|
97
137
|
autoload :Later, 'slk/commands/later'
|
|
138
|
+
autoload :Debug, 'slk/commands/debug'
|
|
139
|
+
autoload :Who, 'slk/commands/who'
|
|
140
|
+
autoload :Org, 'slk/commands/org'
|
|
98
141
|
end
|
|
99
142
|
|
|
100
143
|
# Thin wrappers around Slack API endpoints
|
|
@@ -110,6 +153,7 @@ module Slk
|
|
|
110
153
|
autoload :Activity, 'slk/api/activity'
|
|
111
154
|
autoload :Search, 'slk/api/search'
|
|
112
155
|
autoload :Saved, 'slk/api/saved'
|
|
156
|
+
autoload :Team, 'slk/api/team'
|
|
113
157
|
end
|
|
114
158
|
|
|
115
159
|
# Utility classes for paths, parsing, and helpers
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Boehs
|
|
@@ -34,6 +34,7 @@ files:
|
|
|
34
34
|
- lib/slk/api/emoji.rb
|
|
35
35
|
- lib/slk/api/saved.rb
|
|
36
36
|
- lib/slk/api/search.rb
|
|
37
|
+
- lib/slk/api/team.rb
|
|
37
38
|
- lib/slk/api/threads.rb
|
|
38
39
|
- lib/slk/api/usergroups.rb
|
|
39
40
|
- lib/slk/api/users.rb
|
|
@@ -43,11 +44,13 @@ files:
|
|
|
43
44
|
- lib/slk/commands/cache.rb
|
|
44
45
|
- lib/slk/commands/catchup.rb
|
|
45
46
|
- lib/slk/commands/config.rb
|
|
47
|
+
- lib/slk/commands/debug.rb
|
|
46
48
|
- lib/slk/commands/dnd.rb
|
|
47
49
|
- lib/slk/commands/emoji.rb
|
|
48
50
|
- lib/slk/commands/help.rb
|
|
49
51
|
- lib/slk/commands/later.rb
|
|
50
52
|
- lib/slk/commands/messages.rb
|
|
53
|
+
- lib/slk/commands/org.rb
|
|
51
54
|
- lib/slk/commands/presence.rb
|
|
52
55
|
- lib/slk/commands/preset.rb
|
|
53
56
|
- lib/slk/commands/search.rb
|
|
@@ -55,6 +58,7 @@ files:
|
|
|
55
58
|
- lib/slk/commands/status.rb
|
|
56
59
|
- lib/slk/commands/thread.rb
|
|
57
60
|
- lib/slk/commands/unread.rb
|
|
61
|
+
- lib/slk/commands/who.rb
|
|
58
62
|
- lib/slk/commands/workspaces.rb
|
|
59
63
|
- lib/slk/formatters/activity_formatter.rb
|
|
60
64
|
- lib/slk/formatters/attachment_formatter.rb
|
|
@@ -66,6 +70,9 @@ files:
|
|
|
66
70
|
- lib/slk/formatters/mention_replacer.rb
|
|
67
71
|
- lib/slk/formatters/message_formatter.rb
|
|
68
72
|
- lib/slk/formatters/output.rb
|
|
73
|
+
- lib/slk/formatters/profile_field_renderer.rb
|
|
74
|
+
- lib/slk/formatters/profile_formatter.rb
|
|
75
|
+
- lib/slk/formatters/profile_rows.rb
|
|
69
76
|
- lib/slk/formatters/reaction_formatter.rb
|
|
70
77
|
- lib/slk/formatters/saved_item_formatter.rb
|
|
71
78
|
- lib/slk/formatters/search_formatter.rb
|
|
@@ -74,6 +81,8 @@ files:
|
|
|
74
81
|
- lib/slk/models/duration.rb
|
|
75
82
|
- lib/slk/models/message.rb
|
|
76
83
|
- lib/slk/models/preset.rb
|
|
84
|
+
- lib/slk/models/profile.rb
|
|
85
|
+
- lib/slk/models/profile_field.rb
|
|
77
86
|
- lib/slk/models/reaction.rb
|
|
78
87
|
- lib/slk/models/saved_item.rb
|
|
79
88
|
- lib/slk/models/search_result.rb
|
|
@@ -88,9 +97,13 @@ files:
|
|
|
88
97
|
- lib/slk/services/emoji_downloader.rb
|
|
89
98
|
- lib/slk/services/emoji_searcher.rb
|
|
90
99
|
- lib/slk/services/encryption.rb
|
|
100
|
+
- lib/slk/services/file_downloader.rb
|
|
91
101
|
- lib/slk/services/gemoji_sync.rb
|
|
92
102
|
- lib/slk/services/message_resolver.rb
|
|
103
|
+
- lib/slk/services/meta_cache.rb
|
|
93
104
|
- lib/slk/services/preset_store.rb
|
|
105
|
+
- lib/slk/services/profile_builder.rb
|
|
106
|
+
- lib/slk/services/profile_resolver.rb
|
|
94
107
|
- lib/slk/services/reaction_enricher.rb
|
|
95
108
|
- lib/slk/services/setup_wizard.rb
|
|
96
109
|
- lib/slk/services/target_resolver.rb
|
|
@@ -99,6 +112,9 @@ files:
|
|
|
99
112
|
- lib/slk/services/token_store.rb
|
|
100
113
|
- lib/slk/services/unread_marker.rb
|
|
101
114
|
- lib/slk/services/user_lookup.rb
|
|
115
|
+
- lib/slk/services/user_matcher.rb
|
|
116
|
+
- lib/slk/services/user_picker.rb
|
|
117
|
+
- lib/slk/services/who_target_resolver.rb
|
|
102
118
|
- lib/slk/support/date_parser.rb
|
|
103
119
|
- lib/slk/support/error_logger.rb
|
|
104
120
|
- lib/slk/support/help_formatter.rb
|
|
@@ -132,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
132
148
|
- !ruby/object:Gem::Version
|
|
133
149
|
version: '0'
|
|
134
150
|
requirements: []
|
|
135
|
-
rubygems_version: 4.0.
|
|
151
|
+
rubygems_version: 4.0.10
|
|
136
152
|
specification_version: 4
|
|
137
153
|
summary: A command-line interface for Slack
|
|
138
154
|
test_files: []
|