slk 0.1.0 → 0.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/CHANGELOG.md +22 -1
- data/README.md +5 -5
- data/bin/slk +3 -3
- data/lib/{slack_cli → slk}/api/activity.rb +10 -11
- data/lib/{slack_cli → slk}/api/bots.rb +5 -4
- data/lib/slk/api/client.rb +51 -0
- data/lib/{slack_cli → slk}/api/conversations.rb +14 -13
- data/lib/slk/api/dnd.rb +41 -0
- data/lib/{slack_cli → slk}/api/emoji.rb +4 -3
- data/lib/{slack_cli → slk}/api/threads.rb +13 -12
- data/lib/{slack_cli → slk}/api/usergroups.rb +2 -1
- data/lib/slk/api/users.rb +105 -0
- data/lib/slk/cli.rb +157 -0
- data/lib/slk/commands/activity.rb +152 -0
- data/lib/{slack_cli → slk}/commands/base.rb +67 -41
- data/lib/slk/commands/cache.rb +141 -0
- data/lib/slk/commands/catchup.rb +411 -0
- data/lib/slk/commands/config.rb +114 -0
- data/lib/slk/commands/dnd.rb +172 -0
- data/lib/slk/commands/emoji.rb +352 -0
- data/lib/slk/commands/help.rb +97 -0
- data/lib/slk/commands/messages.rb +299 -0
- data/lib/slk/commands/presence.rb +109 -0
- data/lib/slk/commands/preset.rb +231 -0
- data/lib/slk/commands/status.rb +223 -0
- data/lib/slk/commands/thread.rb +72 -0
- data/lib/slk/commands/unread.rb +305 -0
- data/lib/slk/commands/workspaces.rb +168 -0
- data/lib/slk/formatters/activity_formatter.rb +148 -0
- data/lib/slk/formatters/attachment_formatter.rb +65 -0
- data/lib/slk/formatters/block_formatter.rb +57 -0
- data/lib/{slack_cli → slk}/formatters/duration_formatter.rb +6 -5
- data/lib/slk/formatters/emoji_replacer.rb +141 -0
- data/lib/slk/formatters/json_message_formatter.rb +95 -0
- data/lib/slk/formatters/mention_replacer.rb +158 -0
- data/lib/slk/formatters/message_formatter.rb +174 -0
- data/lib/{slack_cli → slk}/formatters/output.rb +7 -6
- data/lib/slk/formatters/reaction_formatter.rb +87 -0
- data/lib/{slack_cli → slk}/models/channel.rb +12 -10
- data/lib/slk/models/duration.rb +94 -0
- data/lib/slk/models/message.rb +242 -0
- data/lib/slk/models/preset.rb +78 -0
- data/lib/{slack_cli → slk}/models/reaction.rb +6 -6
- data/lib/{slack_cli → slk}/models/status.rb +6 -6
- data/lib/slk/models/user.rb +55 -0
- data/lib/slk/models/workspace.rb +54 -0
- data/lib/{slack_cli → slk}/runner.rb +22 -19
- data/lib/slk/services/activity_enricher.rb +124 -0
- data/lib/slk/services/api_client.rb +145 -0
- data/lib/{slack_cli → slk}/services/cache_store.rb +20 -17
- data/lib/{slack_cli → slk}/services/configuration.rb +9 -8
- data/lib/slk/services/emoji_downloader.rb +103 -0
- data/lib/slk/services/emoji_searcher.rb +72 -0
- data/lib/{slack_cli → slk}/services/encryption.rb +11 -14
- data/lib/slk/services/gemoji_sync.rb +97 -0
- data/lib/{slack_cli → slk}/services/preset_store.rb +34 -33
- data/lib/slk/services/reaction_enricher.rb +82 -0
- data/lib/slk/services/setup_wizard.rb +131 -0
- data/lib/slk/services/target_resolver.rb +108 -0
- data/lib/{slack_cli → slk}/services/token_store.rb +11 -10
- data/lib/slk/services/unread_marker.rb +101 -0
- data/lib/{slack_cli → slk}/support/error_logger.rb +2 -1
- data/lib/{slack_cli → slk}/support/help_formatter.rb +36 -44
- data/lib/{slack_cli → slk}/support/inline_images.rb +28 -19
- data/lib/slk/support/interactive_prompt.rb +29 -0
- data/lib/{slack_cli → slk}/support/slack_url_parser.rb +15 -17
- data/lib/slk/support/text_wrapper.rb +57 -0
- data/lib/slk/support/user_resolver.rb +141 -0
- data/lib/{slack_cli → slk}/support/xdg_paths.rb +6 -5
- data/lib/slk/version.rb +5 -0
- data/lib/slk.rb +112 -0
- metadata +80 -59
- data/lib/slack_cli/api/client.rb +0 -49
- data/lib/slack_cli/api/dnd.rb +0 -40
- data/lib/slack_cli/api/users.rb +0 -101
- data/lib/slack_cli/cli.rb +0 -118
- data/lib/slack_cli/commands/activity.rb +0 -292
- data/lib/slack_cli/commands/cache.rb +0 -116
- data/lib/slack_cli/commands/catchup.rb +0 -484
- data/lib/slack_cli/commands/config.rb +0 -159
- data/lib/slack_cli/commands/dnd.rb +0 -143
- data/lib/slack_cli/commands/emoji.rb +0 -412
- data/lib/slack_cli/commands/help.rb +0 -76
- data/lib/slack_cli/commands/messages.rb +0 -317
- data/lib/slack_cli/commands/presence.rb +0 -107
- data/lib/slack_cli/commands/preset.rb +0 -239
- data/lib/slack_cli/commands/status.rb +0 -194
- data/lib/slack_cli/commands/thread.rb +0 -62
- data/lib/slack_cli/commands/unread.rb +0 -312
- data/lib/slack_cli/commands/workspaces.rb +0 -151
- data/lib/slack_cli/formatters/emoji_replacer.rb +0 -143
- data/lib/slack_cli/formatters/mention_replacer.rb +0 -154
- data/lib/slack_cli/formatters/message_formatter.rb +0 -429
- data/lib/slack_cli/models/duration.rb +0 -85
- data/lib/slack_cli/models/message.rb +0 -217
- data/lib/slack_cli/models/preset.rb +0 -73
- data/lib/slack_cli/models/user.rb +0 -56
- data/lib/slack_cli/models/workspace.rb +0 -52
- data/lib/slack_cli/services/api_client.rb +0 -149
- data/lib/slack_cli/services/reaction_enricher.rb +0 -87
- data/lib/slack_cli/support/user_resolver.rb +0 -114
- data/lib/slack_cli/version.rb +0 -5
- data/lib/slack_cli.rb +0 -91
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../support/inline_images'
|
|
4
|
+
require_relative '../support/help_formatter'
|
|
5
|
+
|
|
6
|
+
module Slk
|
|
7
|
+
module Commands
|
|
8
|
+
# Downloads and manages workspace custom emoji
|
|
9
|
+
# rubocop:disable Metrics/ClassLength
|
|
10
|
+
class Emoji < Base
|
|
11
|
+
include Support::InlineImages
|
|
12
|
+
|
|
13
|
+
def execute
|
|
14
|
+
result = validate_options
|
|
15
|
+
return result if result
|
|
16
|
+
|
|
17
|
+
dispatch_action
|
|
18
|
+
rescue ApiError => e
|
|
19
|
+
error("Failed: #{e.message}")
|
|
20
|
+
1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def dispatch_action
|
|
26
|
+
case positional_args
|
|
27
|
+
in ['status' | 'list'] | [] then show_status
|
|
28
|
+
in ['sync-standard'] then sync_standard
|
|
29
|
+
in ['download', *rest] then download_emoji(rest.first)
|
|
30
|
+
in ['clear', *rest] then clear_emoji(rest.first)
|
|
31
|
+
in ['search', query, *_] then search_emoji(query)
|
|
32
|
+
in ['search'] then missing_search_query
|
|
33
|
+
else unknown_action
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def missing_search_query
|
|
38
|
+
error('Usage: slk emoji search <query>')
|
|
39
|
+
1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def unknown_action
|
|
43
|
+
error("Unknown action: #{positional_args.first}")
|
|
44
|
+
1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
protected
|
|
48
|
+
|
|
49
|
+
def default_options
|
|
50
|
+
super.merge(force: false)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def handle_option(arg, args, remaining)
|
|
54
|
+
case arg
|
|
55
|
+
when '-f', '--force'
|
|
56
|
+
@options[:force] = true
|
|
57
|
+
else
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def help_text
|
|
63
|
+
help = Support::HelpFormatter.new('slk emoji <action> [workspace]')
|
|
64
|
+
help.description('Manage emoji cache.')
|
|
65
|
+
add_actions_section(help)
|
|
66
|
+
add_options_section(help)
|
|
67
|
+
help.render
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def add_actions_section(help)
|
|
71
|
+
help.section('ACTIONS') do |s|
|
|
72
|
+
s.action('status', 'Show emoji cache status')
|
|
73
|
+
s.action('search <query>', 'Search emoji by name (all workspaces by default)')
|
|
74
|
+
s.action('sync-standard', 'Download standard emoji database (gemoji)')
|
|
75
|
+
s.action('download [ws]', 'Download workspace custom emoji')
|
|
76
|
+
s.action('clear [ws]', 'Clear emoji cache')
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def add_options_section(help)
|
|
81
|
+
help.section('OPTIONS') do |s|
|
|
82
|
+
s.option('-w, --workspace', 'Limit to specific workspace')
|
|
83
|
+
s.option('-f, --force', 'Skip confirmation for clear')
|
|
84
|
+
s.option('-q, --quiet', 'Suppress output')
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def show_status
|
|
91
|
+
paths = Support::XdgPaths.new
|
|
92
|
+
emoji_dir = config.emoji_dir || paths.cache_dir
|
|
93
|
+
|
|
94
|
+
show_standard_emoji_status(paths.cache_dir)
|
|
95
|
+
puts
|
|
96
|
+
show_workspace_emoji_status(emoji_dir)
|
|
97
|
+
|
|
98
|
+
0
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def show_standard_emoji_status(cache_dir)
|
|
102
|
+
gemoji_path = File.join(cache_dir, 'gemoji.json')
|
|
103
|
+
|
|
104
|
+
if File.exist?(gemoji_path)
|
|
105
|
+
display_gemoji_status(gemoji_path)
|
|
106
|
+
else
|
|
107
|
+
puts "Standard emoji database: #{output.yellow('not downloaded')}"
|
|
108
|
+
puts " Run 'slk emoji sync-standard' to download"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def display_gemoji_status(gemoji_path)
|
|
113
|
+
gemoji = JSON.parse(File.read(gemoji_path))
|
|
114
|
+
puts "Standard emoji database: #{gemoji.size} emojis"
|
|
115
|
+
rescue JSON::ParserError
|
|
116
|
+
puts "Standard emoji database: #{output.yellow('corrupted')}"
|
|
117
|
+
puts " Run 'slk emoji sync-standard' to re-download"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def show_workspace_emoji_status(emoji_dir)
|
|
121
|
+
puts "Workspace emojis: (#{emoji_dir})"
|
|
122
|
+
target_workspaces.each { |workspace| display_workspace_status(emoji_dir, workspace) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def display_workspace_status(emoji_dir, workspace)
|
|
126
|
+
workspace_dir = File.join(emoji_dir, workspace.name)
|
|
127
|
+
|
|
128
|
+
if Dir.exist?(workspace_dir)
|
|
129
|
+
display_workspace_emoji_count(workspace.name, workspace_dir)
|
|
130
|
+
else
|
|
131
|
+
puts " #{workspace.name}: #{output.yellow('not downloaded')}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def display_workspace_emoji_count(name, workspace_dir)
|
|
136
|
+
files = Dir.glob(File.join(workspace_dir, '*'))
|
|
137
|
+
count = files.count
|
|
138
|
+
size = files.sum { |f| safe_file_size(f) }
|
|
139
|
+
puts " #{name}: #{count} emojis (#{format_size(size)})"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def search_emoji(query)
|
|
143
|
+
searcher = build_emoji_searcher
|
|
144
|
+
workspaces = @options[:workspace] ? [runner.workspace(@options[:workspace])] : runner.all_workspaces
|
|
145
|
+
by_source = searcher.search(query, workspaces: workspaces)
|
|
146
|
+
|
|
147
|
+
if by_source.empty?
|
|
148
|
+
puts "No emoji matching '#{query}'"
|
|
149
|
+
else
|
|
150
|
+
display_search_results(by_source)
|
|
151
|
+
puts "Found #{by_source.values.flatten.size} emoji matching '#{query}'"
|
|
152
|
+
end
|
|
153
|
+
0
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def build_emoji_searcher
|
|
157
|
+
paths = Support::XdgPaths.new
|
|
158
|
+
emoji_dir = config.emoji_dir || paths.cache_dir
|
|
159
|
+
|
|
160
|
+
Services::EmojiSearcher.new(
|
|
161
|
+
cache_dir: paths.cache_dir,
|
|
162
|
+
emoji_dir: emoji_dir,
|
|
163
|
+
on_debug: ->(msg) { debug(msg) }
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def display_search_results(by_source)
|
|
168
|
+
by_source.each do |source, items|
|
|
169
|
+
puts output.bold(source == 'standard' ? 'Standard emoji:' : "#{source}:")
|
|
170
|
+
items.sort_by { |r| r[:name] }.each do |item|
|
|
171
|
+
display_emoji_item(item)
|
|
172
|
+
end
|
|
173
|
+
puts
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def display_emoji_item(item)
|
|
178
|
+
if item[:char]
|
|
179
|
+
puts "#{item[:char]} :#{item[:name]}:"
|
|
180
|
+
elsif item[:path]
|
|
181
|
+
puts ":#{item[:name]}:" unless print_inline_image_with_text(item[:path], ":#{item[:name]}:")
|
|
182
|
+
else
|
|
183
|
+
puts ":#{item[:name]}:"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def print_progress(current, total, downloaded, _skipped)
|
|
188
|
+
# Only update every 1% or when downloading (to show new count)
|
|
189
|
+
pct = ((current.to_f / total) * 100).round
|
|
190
|
+
@last_pct ||= -1
|
|
191
|
+
return if pct == @last_pct && downloaded == (@last_downloaded || 0)
|
|
192
|
+
|
|
193
|
+
@last_pct = pct
|
|
194
|
+
@last_downloaded = downloaded
|
|
195
|
+
|
|
196
|
+
bar_width = 20
|
|
197
|
+
filled = (pct * bar_width / 100).round
|
|
198
|
+
bar = ('=' * filled) + ('-' * (bar_width - filled))
|
|
199
|
+
print "\r [#{bar}] #{pct}% (#{current}/#{total}) +#{downloaded} new"
|
|
200
|
+
$stdout.flush
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def format_size(bytes)
|
|
204
|
+
if bytes >= 1024 * 1024
|
|
205
|
+
"#{(bytes / (1024.0 * 1024)).round}M"
|
|
206
|
+
elsif bytes >= 1024
|
|
207
|
+
"#{(bytes / 1024.0).round}K"
|
|
208
|
+
else
|
|
209
|
+
"#{bytes}B"
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Get file size, returning 0 if file doesn't exist or is inaccessible
|
|
214
|
+
def safe_file_size(path)
|
|
215
|
+
File.size(path)
|
|
216
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
217
|
+
0
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def sync_standard
|
|
221
|
+
paths = Support::XdgPaths.new
|
|
222
|
+
syncer = Services::GemojiSync.new(cache_dir: paths.cache_dir, on_progress: ->(msg) { puts msg })
|
|
223
|
+
result = syncer.sync
|
|
224
|
+
|
|
225
|
+
return sync_error(result[:error]) if result[:error]
|
|
226
|
+
|
|
227
|
+
success("Downloaded #{result[:count]} standard emoji mappings")
|
|
228
|
+
puts " Location: #{result[:path]}"
|
|
229
|
+
0
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def sync_error(message)
|
|
233
|
+
error(message)
|
|
234
|
+
1
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def download_emoji(workspace_name)
|
|
238
|
+
workspaces = workspace_name ? [runner.workspace(workspace_name)] : target_workspaces
|
|
239
|
+
downloader = build_emoji_downloader
|
|
240
|
+
|
|
241
|
+
workspaces.each { |workspace| download_workspace_emoji(workspace, downloader) }
|
|
242
|
+
0
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def build_emoji_downloader
|
|
246
|
+
paths = Support::XdgPaths.new
|
|
247
|
+
emoji_dir = config.emoji_dir || paths.cache_dir
|
|
248
|
+
|
|
249
|
+
Services::EmojiDownloader.new(
|
|
250
|
+
emoji_dir: emoji_dir,
|
|
251
|
+
on_progress: ->(current, total, downloaded, skipped) { print_progress(current, total, downloaded, skipped) },
|
|
252
|
+
on_debug: ->(msg) { debug(msg) }
|
|
253
|
+
)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def download_workspace_emoji(workspace, downloader)
|
|
257
|
+
puts "Fetching emoji list for #{workspace.name}..."
|
|
258
|
+
|
|
259
|
+
api = runner.emoji_api(workspace.name)
|
|
260
|
+
emoji_map = api.custom_emoji
|
|
261
|
+
stats = downloader.download(workspace.name, emoji_map)
|
|
262
|
+
|
|
263
|
+
display_download_results(workspace.name, stats)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def display_download_results(workspace_name, stats)
|
|
267
|
+
puts "\r#{' ' * 60}\r" # Clear progress line
|
|
268
|
+
success("Downloaded #{stats[:downloaded]} new emoji for #{workspace_name}")
|
|
269
|
+
return unless stats[:skipped].positive? || stats[:failed].positive?
|
|
270
|
+
|
|
271
|
+
puts " Skipped: #{stats[:skipped]} (already exist), #{stats[:aliases]} aliases, #{stats[:failed]} failed"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def clear_emoji(workspace_name)
|
|
275
|
+
paths = Support::XdgPaths.new
|
|
276
|
+
emoji_dir = config.emoji_dir || paths.cache_dir
|
|
277
|
+
|
|
278
|
+
to_clear = gather_dirs_to_clear(emoji_dir, workspace_name)
|
|
279
|
+
return 0 if to_clear.nil?
|
|
280
|
+
|
|
281
|
+
stats = display_clear_preview(to_clear)
|
|
282
|
+
return 0 unless confirm_clear?
|
|
283
|
+
|
|
284
|
+
perform_clear(to_clear, stats[:total_count])
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def gather_dirs_to_clear(emoji_dir, workspace_name)
|
|
288
|
+
workspace_name ? gather_single_workspace_dir(emoji_dir, workspace_name) : gather_all_workspace_dirs(emoji_dir)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def gather_single_workspace_dir(emoji_dir, workspace_name)
|
|
292
|
+
workspace_dir = File.join(emoji_dir, workspace_name)
|
|
293
|
+
return [{ name: workspace_name, dir: workspace_dir }] if Dir.exist?(workspace_dir)
|
|
294
|
+
|
|
295
|
+
puts "No emoji cache for #{workspace_name}"
|
|
296
|
+
nil
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def gather_all_workspace_dirs(emoji_dir)
|
|
300
|
+
dirs = target_workspaces.filter_map do |workspace|
|
|
301
|
+
workspace_dir = File.join(emoji_dir, workspace.name)
|
|
302
|
+
{ name: workspace.name, dir: workspace_dir } if Dir.exist?(workspace_dir)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
return dirs if dirs.any?
|
|
306
|
+
|
|
307
|
+
puts 'No emoji caches to clear'
|
|
308
|
+
nil
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def display_clear_preview(to_clear)
|
|
312
|
+
puts 'Will delete:'
|
|
313
|
+
totals = { count: 0, size: 0 }
|
|
314
|
+
|
|
315
|
+
to_clear.each { |entry| display_clear_entry(entry, totals) }
|
|
316
|
+
|
|
317
|
+
puts " Total: #{totals[:count]} files (#{format_size(totals[:size])})"
|
|
318
|
+
{ total_count: totals[:count], total_size: totals[:size] }
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def display_clear_entry(entry, totals)
|
|
322
|
+
files = Dir.glob(File.join(entry[:dir], '*'))
|
|
323
|
+
count = files.count
|
|
324
|
+
size = files.sum { |f| safe_file_size(f) }
|
|
325
|
+
totals[:count] += count
|
|
326
|
+
totals[:size] += size
|
|
327
|
+
puts " #{entry[:name]}: #{count} files (#{format_size(size)})"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def confirm_clear?
|
|
331
|
+
return true if @options[:force]
|
|
332
|
+
|
|
333
|
+
print "\nAre you sure? [y/N] "
|
|
334
|
+
response = $stdin.gets&.chomp&.downcase
|
|
335
|
+
return true if %w[y yes].include?(response)
|
|
336
|
+
|
|
337
|
+
puts 'Cancelled'
|
|
338
|
+
false
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def perform_clear(to_clear, total_count)
|
|
342
|
+
to_clear.each do |entry|
|
|
343
|
+
FileUtils.rm_rf(entry[:dir])
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
success("Cleared #{total_count} emoji files")
|
|
347
|
+
0
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
# rubocop:enable Metrics/ClassLength
|
|
351
|
+
end
|
|
352
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Commands
|
|
5
|
+
# Displays help information for commands
|
|
6
|
+
class Help < Base
|
|
7
|
+
def execute
|
|
8
|
+
topic = positional_args.first
|
|
9
|
+
|
|
10
|
+
if topic
|
|
11
|
+
show_command_help(topic)
|
|
12
|
+
else
|
|
13
|
+
show_general_help
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
0
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def show_general_help
|
|
22
|
+
puts build_header
|
|
23
|
+
puts build_commands_section
|
|
24
|
+
puts build_options_section
|
|
25
|
+
puts build_examples_section
|
|
26
|
+
puts "Run #{output.cyan('slk <command> --help')} for command-specific help."
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def build_header
|
|
30
|
+
<<~HEADER
|
|
31
|
+
#{output.bold('slk')} - Slack CLI v#{VERSION}
|
|
32
|
+
|
|
33
|
+
#{output.bold('USAGE:')}
|
|
34
|
+
slk <command> [options]
|
|
35
|
+
HEADER
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# rubocop:disable Metrics/AbcSize
|
|
39
|
+
def build_commands_section
|
|
40
|
+
<<~COMMANDS
|
|
41
|
+
#{output.bold('COMMANDS:')}
|
|
42
|
+
#{output.cyan('status')} Get or set your status
|
|
43
|
+
#{output.cyan('presence')} Get or set your presence (away/active)
|
|
44
|
+
#{output.cyan('dnd')} Manage Do Not Disturb
|
|
45
|
+
#{output.cyan('messages')} Read channel or DM messages
|
|
46
|
+
#{output.cyan('unread')} View and clear unread messages
|
|
47
|
+
#{output.cyan('preset')} Manage and apply status presets
|
|
48
|
+
#{output.cyan('workspaces')} Manage Slack workspaces
|
|
49
|
+
#{output.cyan('cache')} Manage user/channel cache
|
|
50
|
+
#{output.cyan('emoji')} Download workspace custom emoji
|
|
51
|
+
#{output.cyan('config')} Configuration and setup
|
|
52
|
+
COMMANDS
|
|
53
|
+
end
|
|
54
|
+
# rubocop:enable Metrics/AbcSize
|
|
55
|
+
|
|
56
|
+
def build_options_section
|
|
57
|
+
<<~OPTIONS
|
|
58
|
+
#{output.bold('GLOBAL OPTIONS:')}
|
|
59
|
+
-w, --workspace NAME Use specific workspace
|
|
60
|
+
--all Apply to all workspaces
|
|
61
|
+
-v, --verbose Show debug output
|
|
62
|
+
-q, --quiet Suppress output
|
|
63
|
+
--json Output as JSON (where supported)
|
|
64
|
+
-h, --help Show help
|
|
65
|
+
OPTIONS
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def build_examples_section
|
|
69
|
+
<<~EXAMPLES
|
|
70
|
+
#{output.bold('EXAMPLES:')}
|
|
71
|
+
slk status Show current status
|
|
72
|
+
slk status "Working" :laptop: Set status
|
|
73
|
+
slk status clear Clear status
|
|
74
|
+
slk dnd 1h Enable DND for 1 hour
|
|
75
|
+
slk messages #general Read channel messages
|
|
76
|
+
slk preset meeting Apply preset
|
|
77
|
+
EXAMPLES
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def show_command_help(topic)
|
|
81
|
+
command_class = CLI::COMMANDS[topic]
|
|
82
|
+
|
|
83
|
+
if command_class
|
|
84
|
+
# Create instance just to get help text
|
|
85
|
+
# Call --help directly since help_text is protected
|
|
86
|
+
runner_stub = Runner.new(output: output)
|
|
87
|
+
cmd = command_class.new(['--help'], runner: runner_stub)
|
|
88
|
+
cmd.execute
|
|
89
|
+
else
|
|
90
|
+
error("Unknown command: #{topic}")
|
|
91
|
+
puts
|
|
92
|
+
puts "Available commands: #{CLI::COMMANDS.keys.join(', ')}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|