slk 0.5.0 → 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/debug.rb +110 -0
- data/lib/slk/commands/org.rb +119 -0
- data/lib/slk/commands/who.rb +115 -0
- 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/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 +44 -1
- metadata +16 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Services
|
|
5
|
+
# Orchestrates the API calls needed to assemble a Models::Profile.
|
|
6
|
+
# Memoizes within the instance — one resolver per command run.
|
|
7
|
+
class ProfileResolver
|
|
8
|
+
SCHEMA_TTL = 86_400 # 24h
|
|
9
|
+
PROFILE_TTL = 3_600 # 1h
|
|
10
|
+
EMPTY_SCHEMA = { 'ok' => false, 'profile' => { 'fields' => [], 'sections' => [] } }.freeze
|
|
11
|
+
|
|
12
|
+
attr_accessor :refresh
|
|
13
|
+
|
|
14
|
+
def initialize(users_api:, team_api:, cache_store: nil, workspace_name: nil, on_debug: nil)
|
|
15
|
+
@users_api = users_api
|
|
16
|
+
@team_api = team_api
|
|
17
|
+
@cache_store = cache_store
|
|
18
|
+
@workspace_name = workspace_name
|
|
19
|
+
@on_debug = on_debug
|
|
20
|
+
@refresh = false
|
|
21
|
+
@profile_cache = {}
|
|
22
|
+
@home_team_names = {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Resolve a user ID to a Profile. Memoized per resolver instance.
|
|
26
|
+
def resolve(user_id)
|
|
27
|
+
return @profile_cache[user_id] if @profile_cache.key?(user_id)
|
|
28
|
+
|
|
29
|
+
@profile_cache[user_id] = build_profile(user_id)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Resolve a profile and one level of type:user custom fields, populating
|
|
33
|
+
# `resolved_users` so the formatter can render the People section.
|
|
34
|
+
def resolve_with_people(user_id)
|
|
35
|
+
profile = resolve(user_id)
|
|
36
|
+
return profile if profile.external?
|
|
37
|
+
|
|
38
|
+
profile.people_fields.flat_map(&:user_ids).uniq.each do |ref_id|
|
|
39
|
+
profile.resolved_users[ref_id] ||= resolve(ref_id)
|
|
40
|
+
end
|
|
41
|
+
profile
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Walks Supervisor (or first non-inverse type:user field) upward.
|
|
45
|
+
# Returns Array<Profile> from immediate supervisor to top, capped by depth.
|
|
46
|
+
def resolve_chain_up(user_id, depth: 5)
|
|
47
|
+
chain = []
|
|
48
|
+
seen = Set.new([user_id])
|
|
49
|
+
current = resolve(user_id)
|
|
50
|
+
depth.times { current = step_up(current, seen, chain) or break }
|
|
51
|
+
chain
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def step_up(current, seen, chain)
|
|
57
|
+
parent_id = current.supervisor_ids.first
|
|
58
|
+
return nil unless parent_id && !seen.include?(parent_id)
|
|
59
|
+
|
|
60
|
+
parent = resolve(parent_id)
|
|
61
|
+
chain << parent
|
|
62
|
+
seen << parent.user_id
|
|
63
|
+
parent
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def build_profile(user_id)
|
|
67
|
+
profile = ProfileBuilder.build(
|
|
68
|
+
profile_response: fetch_profile_response(user_id),
|
|
69
|
+
info_response: cache_or_fetch("ui_#{user_id}", ttl: PROFILE_TTL) { @users_api.info(user_id) },
|
|
70
|
+
schema_response: schema,
|
|
71
|
+
workspace_team_id: workspace_team_id
|
|
72
|
+
)
|
|
73
|
+
attach_extras(profile, user_id)
|
|
74
|
+
rescue ApiError => e
|
|
75
|
+
@on_debug&.call("Profile resolve failed for #{user_id}: #{e.message}")
|
|
76
|
+
raise
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Only swallow `user_not_found` (Slack Connect); other errors propagate.
|
|
80
|
+
def fetch_profile_response(user_id)
|
|
81
|
+
key = "up_#{user_id}"
|
|
82
|
+
cached = MetaCache.read(@cache_store, @workspace_name, key, ttl: PROFILE_TTL) unless @refresh
|
|
83
|
+
return cached if cached
|
|
84
|
+
|
|
85
|
+
response = @users_api.profile_for(user_id)
|
|
86
|
+
MetaCache.write(@cache_store, @workspace_name, key, response)
|
|
87
|
+
response
|
|
88
|
+
rescue ApiError => e
|
|
89
|
+
raise unless e.code == :user_not_found
|
|
90
|
+
|
|
91
|
+
@on_debug&.call("#{key}: #{e.message} (falling back to users.info)")
|
|
92
|
+
nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def attach_extras(profile, user_id)
|
|
96
|
+
profile = attach_home_team_name(profile)
|
|
97
|
+
presence = fetch_presence(user_id)
|
|
98
|
+
presence ? Models::Profile.new(**profile.to_h, presence: presence) : profile
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def fetch_presence(user_id)
|
|
102
|
+
@users_api.get_presence_for(user_id)&.dig('presence')
|
|
103
|
+
rescue ApiError => e
|
|
104
|
+
@on_debug&.call("get_presence_for(#{user_id}) failed: #{e.message}")
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def schema
|
|
109
|
+
@schema ||= cache_or_fetch('team_profile_schema', ttl: SCHEMA_TTL,
|
|
110
|
+
empty: EMPTY_SCHEMA) { @team_api.profile_schema }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def workspace_team_id
|
|
114
|
+
@workspace_team_id ||= cache_or_fetch('workspace_team_id') { @team_api.info.dig('team', 'id') }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def cache_or_fetch(key, ttl: nil, empty: nil, &)
|
|
118
|
+
MetaCache.fetch(@cache_store, @workspace_name, key, ttl: ttl, refresh: @refresh, &)
|
|
119
|
+
rescue ApiError => e
|
|
120
|
+
@on_debug&.call("#{key} fetch failed: #{e.message}")
|
|
121
|
+
empty
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def attach_home_team_name(profile)
|
|
125
|
+
return profile unless profile.external? && profile.team_id && (name = home_team_name(profile.team_id))
|
|
126
|
+
|
|
127
|
+
Models::Profile.new(**profile.to_h, home_team_name: name)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def home_team_name(team_id)
|
|
131
|
+
@home_team_names[team_id] ||= @team_api.info(team_id).dig('team', 'name')
|
|
132
|
+
rescue ApiError => e
|
|
133
|
+
@on_debug&.call("team.info(#{team_id}) failed: #{e.message}")
|
|
134
|
+
@home_team_names[team_id] = nil
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -52,6 +52,14 @@ module Slk
|
|
|
52
52
|
fetch_id_by_name(name)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
# @return [Array<Hash>] raw users.list-shaped hashes for all matches
|
|
56
|
+
def find_all_by_name(name)
|
|
57
|
+
UserMatcher.new(
|
|
58
|
+
api_client: @api, workspace: @workspace,
|
|
59
|
+
cache_store: @cache, on_debug: @on_debug
|
|
60
|
+
).find_all(name)
|
|
61
|
+
end
|
|
62
|
+
|
|
55
63
|
private
|
|
56
64
|
|
|
57
65
|
def fetch_and_cache_name(user_id)
|
|
@@ -88,11 +96,7 @@ module Slk
|
|
|
88
96
|
end
|
|
89
97
|
|
|
90
98
|
def fetch_id_by_name(name)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
users_api = Api::Users.new(@api, @workspace, on_debug: @on_debug)
|
|
94
|
-
users = users_api.list['members'] || []
|
|
95
|
-
user = find_user_by_name(users, name)
|
|
99
|
+
user = find_all_by_name(name).first
|
|
96
100
|
cache_user_from_api(user) if user
|
|
97
101
|
user&.dig('id')
|
|
98
102
|
rescue ApiError => e
|
|
@@ -100,14 +104,6 @@ module Slk
|
|
|
100
104
|
nil
|
|
101
105
|
end
|
|
102
106
|
|
|
103
|
-
def find_user_by_name(users, name)
|
|
104
|
-
users.find do |u|
|
|
105
|
-
u['name'] == name ||
|
|
106
|
-
u.dig('profile', 'display_name') == name ||
|
|
107
|
-
u.dig('profile', 'real_name') == name
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
107
|
def cache_user_from_api(user_data)
|
|
112
108
|
user = Models::User.from_api(user_data)
|
|
113
109
|
@cache.set_user(@workspace.name, user.id, user.best_name, persist: true)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Services
|
|
5
|
+
# Finds all users whose name/display/real/first+last matches a query
|
|
6
|
+
# (case-insensitive). Combines users.list with previously-resolved profiles
|
|
7
|
+
# cached locally — Slack Connect external users don't appear in users.list
|
|
8
|
+
# but do show up in the meta cache once users.info has been fetched.
|
|
9
|
+
class UserMatcher
|
|
10
|
+
def initialize(api_client:, workspace:, cache_store:, on_debug: nil)
|
|
11
|
+
@api = api_client
|
|
12
|
+
@workspace = workspace
|
|
13
|
+
@cache = cache_store
|
|
14
|
+
@on_debug = on_debug
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns users.list-shaped hashes (deduped by id). Raises ApiError on
|
|
18
|
+
# network/auth failures so callers don't conflate them with "no matches".
|
|
19
|
+
def find_all(name)
|
|
20
|
+
return [] if name.to_s.empty? || @api.nil?
|
|
21
|
+
|
|
22
|
+
target = name.downcase
|
|
23
|
+
candidates = list_members + cached_profile_users
|
|
24
|
+
unique_by_id(candidates.select { |u| matches?(u, target) })
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def matches?(user, target_lower)
|
|
28
|
+
name_candidates(user).any? { |c| c.downcase == target_lower }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def name_candidates(user)
|
|
32
|
+
profile = user['profile'] || {}
|
|
33
|
+
full = [profile['first_name'], profile['last_name']].compact.join(' ').strip
|
|
34
|
+
[user['name'], profile['display_name'], profile['real_name'], full]
|
|
35
|
+
.map(&:to_s).reject(&:empty?)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def list_members
|
|
41
|
+
Api::Users.new(@api, @workspace, on_debug: @on_debug).list['members'] || []
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Reshape cached `ui_<uid>` meta entries (raw users.info responses) into
|
|
45
|
+
# users.list-shaped hashes so the matcher can compare them uniformly.
|
|
46
|
+
def cached_profile_users
|
|
47
|
+
return [] unless @cache.respond_to?(:each_meta)
|
|
48
|
+
|
|
49
|
+
@cache.each_meta(@workspace.name).filter_map do |key, value|
|
|
50
|
+
next unless key.start_with?('ui_')
|
|
51
|
+
|
|
52
|
+
user = value.is_a?(Hash) ? value.dig('value', 'user') : nil
|
|
53
|
+
user if user.is_a?(Hash) && user['id']
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def unique_by_id(users)
|
|
58
|
+
seen = {}
|
|
59
|
+
users.each { |u| seen[u['id']] ||= u }
|
|
60
|
+
seen.values
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -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
|
|
@@ -56,7 +86,13 @@ module Slk
|
|
|
56
86
|
autoload :TargetResolver, 'slk/services/target_resolver'
|
|
57
87
|
autoload :SetupWizard, 'slk/services/setup_wizard'
|
|
58
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'
|
|
59
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'
|
|
60
96
|
end
|
|
61
97
|
|
|
62
98
|
# Output formatters for messages, durations, and emoji
|
|
@@ -75,6 +111,9 @@ module Slk
|
|
|
75
111
|
autoload :SearchFormatter, 'slk/formatters/search_formatter'
|
|
76
112
|
autoload :SavedItemFormatter, 'slk/formatters/saved_item_formatter'
|
|
77
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'
|
|
78
117
|
end
|
|
79
118
|
|
|
80
119
|
# CLI commands implementing user-facing functionality
|
|
@@ -96,6 +135,9 @@ module Slk
|
|
|
96
135
|
autoload :Config, 'slk/commands/config'
|
|
97
136
|
autoload :Help, 'slk/commands/help'
|
|
98
137
|
autoload :Later, 'slk/commands/later'
|
|
138
|
+
autoload :Debug, 'slk/commands/debug'
|
|
139
|
+
autoload :Who, 'slk/commands/who'
|
|
140
|
+
autoload :Org, 'slk/commands/org'
|
|
99
141
|
end
|
|
100
142
|
|
|
101
143
|
# Thin wrappers around Slack API endpoints
|
|
@@ -111,6 +153,7 @@ module Slk
|
|
|
111
153
|
autoload :Activity, 'slk/api/activity'
|
|
112
154
|
autoload :Search, 'slk/api/search'
|
|
113
155
|
autoload :Saved, 'slk/api/saved'
|
|
156
|
+
autoload :Team, 'slk/api/team'
|
|
114
157
|
end
|
|
115
158
|
|
|
116
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
|
|
@@ -91,7 +100,10 @@ files:
|
|
|
91
100
|
- lib/slk/services/file_downloader.rb
|
|
92
101
|
- lib/slk/services/gemoji_sync.rb
|
|
93
102
|
- lib/slk/services/message_resolver.rb
|
|
103
|
+
- lib/slk/services/meta_cache.rb
|
|
94
104
|
- lib/slk/services/preset_store.rb
|
|
105
|
+
- lib/slk/services/profile_builder.rb
|
|
106
|
+
- lib/slk/services/profile_resolver.rb
|
|
95
107
|
- lib/slk/services/reaction_enricher.rb
|
|
96
108
|
- lib/slk/services/setup_wizard.rb
|
|
97
109
|
- lib/slk/services/target_resolver.rb
|
|
@@ -100,6 +112,9 @@ files:
|
|
|
100
112
|
- lib/slk/services/token_store.rb
|
|
101
113
|
- lib/slk/services/unread_marker.rb
|
|
102
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
|
|
103
118
|
- lib/slk/support/date_parser.rb
|
|
104
119
|
- lib/slk/support/error_logger.rb
|
|
105
120
|
- lib/slk/support/help_formatter.rb
|