slk 0.4.2 → 0.5.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/lib/slk/commands/base.rb +5 -2
- data/lib/slk/commands/messages.rb +44 -6
- data/lib/slk/commands/thread.rb +11 -6
- data/lib/slk/formatters/attachment_formatter.rb +22 -7
- data/lib/slk/formatters/message_formatter.rb +13 -4
- data/lib/slk/services/file_downloader.rb +180 -0
- data/lib/slk/version.rb +1 -1
- data/lib/slk.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54edc7e40c9bcf891e2d2727c4cf50c4f43de73a70051465da12f3a97bc5282b
|
|
4
|
+
data.tar.gz: 40bbb5862b982c4e0d87d2a47c18b91a2f0070cac9fe7ad15a68e109114bf977
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 96abfcac2a271dccbce0a7b8068d23cf70d62ff68466e2a915fd67474aa9a3b6f648dce14ebaa87718c069993886a309c86490ab865a060586789bf8f8775752
|
|
7
|
+
data.tar.gz: ece446478a9dfda1dec3fa5dad6feff79c5f7be24e89f9835d61a0f3684e3d7839a94c673859ac5678a1f0f0c8675d31a9673493e744efcdd67433acda1f8fda
|
data/lib/slk/commands/base.rb
CHANGED
|
@@ -32,7 +32,8 @@ module Slk
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def base_options
|
|
35
|
-
{ workspace: nil, all: false, verbose: false, quiet: false, json: false, markdown: false,
|
|
35
|
+
{ workspace: nil, all: false, verbose: false, quiet: false, json: false, markdown: false,
|
|
36
|
+
width: default_width, fetch_attachments: false }
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def formatting_options
|
|
@@ -79,6 +80,7 @@ module Slk
|
|
|
79
80
|
when '--no-names' then @options[:no_names] = true
|
|
80
81
|
when '--reaction-names' then @options[:reaction_names] = true
|
|
81
82
|
when '--reaction-timestamps' then @options[:reaction_timestamps] = true
|
|
83
|
+
when '--fetch-attachments' then @options[:fetch_attachments] = true
|
|
82
84
|
else handle_option(arg, args, remaining)
|
|
83
85
|
end
|
|
84
86
|
end
|
|
@@ -194,7 +196,8 @@ module Slk
|
|
|
194
196
|
no_names: @options[:no_names],
|
|
195
197
|
reaction_names: @options[:reaction_names],
|
|
196
198
|
reaction_timestamps: @options[:reaction_timestamps],
|
|
197
|
-
width: @options[:width]
|
|
199
|
+
width: @options[:width],
|
|
200
|
+
fetch_attachments: @options[:fetch_attachments]
|
|
198
201
|
}
|
|
199
202
|
end
|
|
200
203
|
end
|
|
@@ -124,6 +124,7 @@ module Slk
|
|
|
124
124
|
section.option('--no-names', 'Skip user name lookups (faster)')
|
|
125
125
|
section.option('--reaction-names', 'Show reactions with user names')
|
|
126
126
|
section.option('--reaction-timestamps', 'Show when each person reacted')
|
|
127
|
+
section.option('--fetch-attachments', 'Download files/images to local cache (~/.cache/slk/files/)')
|
|
127
128
|
section.option('--width N', 'Wrap text at N columns (default: 72 on TTY, no wrap otherwise)')
|
|
128
129
|
section.option('--no-wrap', 'Disable text wrapping')
|
|
129
130
|
end
|
|
@@ -258,15 +259,22 @@ module Slk
|
|
|
258
259
|
end
|
|
259
260
|
|
|
260
261
|
def display_messages(messages, workspace, channel_id)
|
|
261
|
-
|
|
262
|
-
opts = format_options.merge(channel_id: channel_id)
|
|
262
|
+
opts = build_display_options(messages, workspace, channel_id)
|
|
263
263
|
|
|
264
264
|
messages.each_with_index do |message, index|
|
|
265
|
-
display_single_message(
|
|
265
|
+
display_single_message(runner.message_formatter, message, workspace, opts)
|
|
266
266
|
puts if index < messages.length - 1
|
|
267
267
|
|
|
268
268
|
show_thread_replies(workspace, channel_id, message, opts) if should_show_thread?(message)
|
|
269
269
|
end
|
|
270
|
+
|
|
271
|
+
print_file_summary(messages) unless opts[:fetch_attachments]
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def build_display_options(messages, workspace, channel_id)
|
|
275
|
+
opts = format_options.merge(channel_id: channel_id)
|
|
276
|
+
opts[:file_paths] = fetch_attachment_files(messages, workspace) if opts[:fetch_attachments]
|
|
277
|
+
opts
|
|
270
278
|
end
|
|
271
279
|
|
|
272
280
|
def should_show_thread?(message)
|
|
@@ -281,12 +289,20 @@ module Slk
|
|
|
281
289
|
def show_thread_replies(workspace, channel_id, parent_message, opts)
|
|
282
290
|
api = runner.conversations_api(workspace.name)
|
|
283
291
|
replies = fetch_all_thread_replies(api, channel_id, parent_message.ts)
|
|
292
|
+
reply_messages = replies[1..].map { |r| Models::Message.from_api(r, channel_id: channel_id) }
|
|
284
293
|
|
|
285
|
-
|
|
294
|
+
download_reply_files(reply_messages, workspace, opts)
|
|
295
|
+
reply_messages.each { |reply| display_thread_reply_message(reply, workspace, opts) }
|
|
286
296
|
end
|
|
287
297
|
|
|
288
|
-
def
|
|
289
|
-
|
|
298
|
+
def download_reply_files(replies, workspace, opts)
|
|
299
|
+
return unless opts[:fetch_attachments]
|
|
300
|
+
|
|
301
|
+
new_paths = fetch_attachment_files(replies, workspace)
|
|
302
|
+
opts[:file_paths].merge!(new_paths)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def display_thread_reply_message(reply, workspace, opts)
|
|
290
306
|
formatted = runner.message_formatter.format(reply, workspace: workspace, options: opts)
|
|
291
307
|
|
|
292
308
|
lines = formatted.lines
|
|
@@ -331,6 +347,28 @@ module Slk
|
|
|
331
347
|
end
|
|
332
348
|
end
|
|
333
349
|
|
|
350
|
+
def print_file_summary(messages)
|
|
351
|
+
file_count = messages.sum { |m| m.files.size + downloadable_attachment_count(m) }
|
|
352
|
+
return if file_count.zero?
|
|
353
|
+
|
|
354
|
+
label = file_count == 1 ? '1 file' : "#{file_count} files"
|
|
355
|
+
puts
|
|
356
|
+
info("#{label} not downloaded. Use --fetch-attachments to download.")
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def downloadable_attachment_count(message)
|
|
360
|
+
message.attachments.count { |a| a['image_url'] || a['thumb_url'] }
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def fetch_attachment_files(messages, workspace)
|
|
364
|
+
paths = Support::XdgPaths.new
|
|
365
|
+
downloader = Services::FileDownloader.new(
|
|
366
|
+
cache_dir: paths.cache_dir,
|
|
367
|
+
on_debug: ->(msg) { debug(msg) }
|
|
368
|
+
)
|
|
369
|
+
downloader.download_message_files(messages, workspace)
|
|
370
|
+
end
|
|
371
|
+
|
|
334
372
|
def find_workspace_emoji(workspace_name, emoji_name)
|
|
335
373
|
return nil if emoji_name.empty?
|
|
336
374
|
|
data/lib/slk/commands/thread.rb
CHANGED
|
@@ -10,6 +10,16 @@ module Slk
|
|
|
10
10
|
result = validate_options
|
|
11
11
|
return result if result
|
|
12
12
|
|
|
13
|
+
resolve_and_display_thread
|
|
14
|
+
rescue ApiError => e
|
|
15
|
+
error("Failed to fetch messages: #{e.message}")
|
|
16
|
+
1
|
|
17
|
+
rescue ArgumentError => e
|
|
18
|
+
error(e.message)
|
|
19
|
+
1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def resolve_and_display_thread
|
|
13
23
|
target = positional_args.first
|
|
14
24
|
return usage_error unless target
|
|
15
25
|
|
|
@@ -18,12 +28,6 @@ module Slk
|
|
|
18
28
|
|
|
19
29
|
resolved = target_resolver.resolve(target, default_workspace: target_workspaces.first)
|
|
20
30
|
fetch_and_display_messages(resolved)
|
|
21
|
-
rescue ApiError => e
|
|
22
|
-
error("Failed to fetch messages: #{e.message}")
|
|
23
|
-
1
|
|
24
|
-
rescue ArgumentError => e
|
|
25
|
-
error(e.message)
|
|
26
|
-
1
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def fetch_and_display_messages(resolved)
|
|
@@ -75,6 +79,7 @@ module Slk
|
|
|
75
79
|
s.option('--no-emoji', 'Show :emoji: codes instead of unicode')
|
|
76
80
|
s.option('--no-reactions', 'Hide reactions')
|
|
77
81
|
s.option('--no-names', 'Skip user name lookups (faster)')
|
|
82
|
+
s.option('--fetch-attachments', 'Download files/images to local cache (~/.cache/slk/files/)')
|
|
78
83
|
s.option('--json', 'Output as JSON')
|
|
79
84
|
s.option('-v, --verbose', 'Show debug information')
|
|
80
85
|
end
|
|
@@ -9,17 +9,19 @@ module Slk
|
|
|
9
9
|
@text_processor = text_processor
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def format(attachments, lines, options)
|
|
12
|
+
def format(attachments, lines, options, message_ts: nil)
|
|
13
13
|
return if attachments.empty?
|
|
14
14
|
return if options[:no_attachments]
|
|
15
15
|
|
|
16
|
-
attachments.
|
|
16
|
+
attachments.each_with_index do |att, idx|
|
|
17
|
+
format_attachment(att, lines, options, message_ts: message_ts, index: idx)
|
|
18
|
+
end
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
private
|
|
20
22
|
|
|
21
23
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
22
|
-
def format_attachment(attachment, lines, options)
|
|
24
|
+
def format_attachment(attachment, lines, options, message_ts: nil, index: 0)
|
|
23
25
|
att_text = attachment['text'] || attachment['fallback']
|
|
24
26
|
image_url = attachment['image_url'] || attachment['thumb_url']
|
|
25
27
|
block_images = extract_block_images(attachment)
|
|
@@ -29,7 +31,7 @@ module Slk
|
|
|
29
31
|
lines << ''
|
|
30
32
|
format_author(attachment, lines)
|
|
31
33
|
format_text(att_text, lines, options) if att_text && block_images.empty?
|
|
32
|
-
format_image(attachment,
|
|
34
|
+
format_image(attachment, lines, options, message_ts: message_ts, index: index) if image_url
|
|
33
35
|
block_images.each { |img| lines << "> [Image: #{img}]" }
|
|
34
36
|
end
|
|
35
37
|
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
@@ -64,9 +66,22 @@ module Slk
|
|
|
64
66
|
Support::TextWrapper.wrap(text, width - 2, width - 2)
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
def format_image(attachment,
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
def format_image(attachment, lines, options, message_ts: nil, index: 0)
|
|
70
|
+
local_path = lookup_attachment_path(options, message_ts, index)
|
|
71
|
+
if local_path
|
|
72
|
+
lines << "> [Image: #{local_path}]"
|
|
73
|
+
else
|
|
74
|
+
image_url = attachment['image_url'] || attachment['thumb_url']
|
|
75
|
+
filename = attachment['title'] || extract_filename(image_url)
|
|
76
|
+
lines << "> [Image: #{filename}]"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def lookup_attachment_path(options, message_ts, index)
|
|
81
|
+
return nil unless message_ts
|
|
82
|
+
|
|
83
|
+
key = "att_#{message_ts}_#{index}"
|
|
84
|
+
options.dig(:file_paths, key)
|
|
70
85
|
end
|
|
71
86
|
|
|
72
87
|
def extract_filename(url)
|
|
@@ -85,8 +85,14 @@ module Slk
|
|
|
85
85
|
return '' unless message.files? && !options[:no_files]
|
|
86
86
|
|
|
87
87
|
first_file = message.files.first
|
|
88
|
-
|
|
89
|
-
@output.blue("[File: #{
|
|
88
|
+
file_label = file_display_label(first_file, options)
|
|
89
|
+
@output.blue("[File: #{file_label}]")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def file_display_label(file, options)
|
|
93
|
+
file_id = file['id']
|
|
94
|
+
local_path = options.dig(:file_paths, file_id) if file_id
|
|
95
|
+
local_path || file['name'] || 'file'
|
|
90
96
|
end
|
|
91
97
|
|
|
92
98
|
def build_output_lines(main_line, message, workspace, options, display_text)
|
|
@@ -96,7 +102,7 @@ module Slk
|
|
|
96
102
|
BlockFormatter.new(text_processor: text_processor)
|
|
97
103
|
.format(message.blocks, message.text, lines, options)
|
|
98
104
|
AttachmentFormatter.new(output: @output, text_processor: text_processor)
|
|
99
|
-
.format(message.attachments, lines, options)
|
|
105
|
+
.format(message.attachments, lines, options, message_ts: message.ts)
|
|
100
106
|
format_files(message, lines, options, skip_first: display_text.include?('[File:'))
|
|
101
107
|
format_reactions(message, lines, workspace, options)
|
|
102
108
|
format_thread_indicator(message, lines, options)
|
|
@@ -128,7 +134,10 @@ module Slk
|
|
|
128
134
|
return if options[:no_files]
|
|
129
135
|
|
|
130
136
|
files = files_to_display(message.files, skip_first)
|
|
131
|
-
files.each
|
|
137
|
+
files.each do |file|
|
|
138
|
+
label = file_display_label(file, options)
|
|
139
|
+
lines << @output.blue("[File: #{label}]")
|
|
140
|
+
end
|
|
132
141
|
end
|
|
133
142
|
|
|
134
143
|
def files_to_display(files, skip_first)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Services
|
|
5
|
+
# Downloads Slack message files and attachment images to XDG cache dir.
|
|
6
|
+
# Authed files (url_private_download) require workspace headers.
|
|
7
|
+
# Public attachment images (image_url) are fetched without auth.
|
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
|
9
|
+
class FileDownloader
|
|
10
|
+
NETWORK_ERRORS = [
|
|
11
|
+
SocketError,
|
|
12
|
+
Errno::ECONNREFUSED,
|
|
13
|
+
Errno::ETIMEDOUT,
|
|
14
|
+
Net::OpenTimeout,
|
|
15
|
+
Net::ReadTimeout,
|
|
16
|
+
URI::InvalidURIError,
|
|
17
|
+
OpenSSL::SSL::SSLError
|
|
18
|
+
].freeze
|
|
19
|
+
|
|
20
|
+
IMAGE_TYPES = %w[png jpg jpeg gif bmp webp svg].freeze
|
|
21
|
+
MAX_REDIRECTS = 3
|
|
22
|
+
|
|
23
|
+
def initialize(cache_dir:, on_debug: nil)
|
|
24
|
+
@cache_dir = cache_dir
|
|
25
|
+
@on_debug = on_debug
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Download all files from a list of messages, returning a hash of
|
|
29
|
+
# file_id => local_path for files and attachment index => local_path for attachments.
|
|
30
|
+
def download_message_files(messages, workspace)
|
|
31
|
+
files_dir = ensure_workspace_dir(workspace.name)
|
|
32
|
+
file_paths = {}
|
|
33
|
+
|
|
34
|
+
messages.each do |message|
|
|
35
|
+
download_files(message.files, files_dir, workspace, file_paths)
|
|
36
|
+
download_attachment_images(message.attachments, message.ts, files_dir, file_paths)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
file_paths
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def ensure_workspace_dir(workspace_name)
|
|
45
|
+
dir = File.join(@cache_dir, 'files', workspace_name)
|
|
46
|
+
FileUtils.mkdir_p(dir)
|
|
47
|
+
dir
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def download_files(files, dir, workspace, paths)
|
|
51
|
+
files.each do |file|
|
|
52
|
+
path = download_single_file(file, dir, workspace)
|
|
53
|
+
paths[file['id']] = path if path
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def download_single_file(file, dir, workspace)
|
|
58
|
+
file_id = file['id']
|
|
59
|
+
url = file['url_private_download']
|
|
60
|
+
return unless file_id && url
|
|
61
|
+
|
|
62
|
+
name = file['name'] || 'file'
|
|
63
|
+
local_path = File.join(dir, "#{file_id}_#{sanitize_filename(name)}")
|
|
64
|
+
|
|
65
|
+
return local_path if cached?(local_path, name)
|
|
66
|
+
|
|
67
|
+
download_authed(url, local_path, workspace) ? local_path : nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def download_attachment_images(attachments, message_ts, dir, paths)
|
|
71
|
+
attachments.each_with_index do |att, idx|
|
|
72
|
+
path = download_single_attachment(att, message_ts, idx, dir)
|
|
73
|
+
paths["att_#{message_ts}_#{idx}"] = path if path
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def download_single_attachment(att, message_ts, idx, dir)
|
|
78
|
+
url = att['image_url'] || att['thumb_url']
|
|
79
|
+
return unless url && downloadable_image_url?(url)
|
|
80
|
+
|
|
81
|
+
local_path = attachment_path(dir, message_ts, idx, url)
|
|
82
|
+
return local_path if cached?(local_path, 'attachment image')
|
|
83
|
+
|
|
84
|
+
download_public(url, local_path) ? local_path : nil
|
|
85
|
+
rescue URI::InvalidURIError
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def attachment_path(dir, message_ts, idx, url)
|
|
90
|
+
ext = File.extname(URI.parse(url).path)
|
|
91
|
+
ext = '.jpg' if ext.empty?
|
|
92
|
+
File.join(dir, "att_#{message_ts}_#{idx}#{ext}")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def cached?(local_path, label)
|
|
96
|
+
return false unless File.exist?(local_path)
|
|
97
|
+
|
|
98
|
+
@on_debug&.call("Skipping #{label} (cached)")
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def downloadable_image_url?(url)
|
|
103
|
+
ext = File.extname(URI.parse(url).path).delete('.').downcase
|
|
104
|
+
IMAGE_TYPES.include?(ext) || url.include?('/giphy') || url.include?('tenor.com')
|
|
105
|
+
rescue URI::InvalidURIError
|
|
106
|
+
false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def download_authed(url, filepath, workspace)
|
|
110
|
+
uri = URI.parse(url)
|
|
111
|
+
request = Net::HTTP::Get.new(uri)
|
|
112
|
+
apply_workspace_headers(request, workspace)
|
|
113
|
+
write_response(build_http_client(uri).request(request), filepath)
|
|
114
|
+
rescue *NETWORK_ERRORS, SystemCallError => e
|
|
115
|
+
@on_debug&.call("Failed to download file: #{e.message}")
|
|
116
|
+
false
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def apply_workspace_headers(request, workspace)
|
|
120
|
+
request['Authorization'] = workspace.headers['Authorization']
|
|
121
|
+
request['Cookie'] = workspace.headers['Cookie'] if workspace.headers['Cookie']
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def download_public(url, filepath)
|
|
125
|
+
response = fetch_with_redirect(url)
|
|
126
|
+
write_response(response, filepath)
|
|
127
|
+
rescue *NETWORK_ERRORS, SystemCallError => e
|
|
128
|
+
@on_debug&.call("Failed to download attachment image: #{e.message}")
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def fetch_with_redirect(url)
|
|
133
|
+
uri = URI.parse(url)
|
|
134
|
+
MAX_REDIRECTS.times do
|
|
135
|
+
response = build_http_client(uri).request(Net::HTTP::Get.new(uri))
|
|
136
|
+
return response unless response.is_a?(Net::HTTPRedirection) && response['location']
|
|
137
|
+
|
|
138
|
+
uri = resolve_redirect(uri, response['location'])
|
|
139
|
+
return response unless uri.host
|
|
140
|
+
end
|
|
141
|
+
# Exhausted redirects — return last response as-is
|
|
142
|
+
build_http_client(uri).request(Net::HTTP::Get.new(uri))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def resolve_redirect(original_uri, location)
|
|
146
|
+
parsed = URI.parse(location)
|
|
147
|
+
return parsed if parsed.host
|
|
148
|
+
|
|
149
|
+
# Relative redirect — resolve against original URI
|
|
150
|
+
URI.parse("#{original_uri.scheme}://#{original_uri.host}:#{original_uri.port}#{location}")
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def write_response(response, filepath) # rubocop:disable Naming/PredicateMethod
|
|
154
|
+
return false unless response.is_a?(Net::HTTPSuccess)
|
|
155
|
+
|
|
156
|
+
File.binwrite(filepath, response.body)
|
|
157
|
+
@on_debug&.call("Downloaded #{File.basename(filepath)} (#{response.body.bytesize} bytes)")
|
|
158
|
+
true
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def build_http_client(uri)
|
|
162
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
163
|
+
http.use_ssl = uri.scheme == 'https'
|
|
164
|
+
if http.use_ssl?
|
|
165
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
166
|
+
http.cert_store = OpenSSL::X509::Store.new
|
|
167
|
+
http.cert_store.set_default_paths
|
|
168
|
+
end
|
|
169
|
+
http.open_timeout = 10
|
|
170
|
+
http.read_timeout = 30
|
|
171
|
+
http
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def sanitize_filename(name)
|
|
175
|
+
name.gsub(%r{[/\\:*?"<>|]}, '_')
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
# rubocop:enable Metrics/ClassLength
|
|
179
|
+
end
|
|
180
|
+
end
|
data/lib/slk/version.rb
CHANGED
data/lib/slk.rb
CHANGED
|
@@ -49,6 +49,7 @@ module Slk
|
|
|
49
49
|
autoload :ReactionEnricher, 'slk/services/reaction_enricher'
|
|
50
50
|
autoload :GemojiSync, 'slk/services/gemoji_sync'
|
|
51
51
|
autoload :EmojiDownloader, 'slk/services/emoji_downloader'
|
|
52
|
+
autoload :FileDownloader, 'slk/services/file_downloader'
|
|
52
53
|
autoload :EmojiSearcher, 'slk/services/emoji_searcher'
|
|
53
54
|
autoload :ActivityEnricher, 'slk/services/activity_enricher'
|
|
54
55
|
autoload :UnreadMarker, 'slk/services/unread_marker'
|
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.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Boehs
|
|
@@ -88,6 +88,7 @@ files:
|
|
|
88
88
|
- lib/slk/services/emoji_downloader.rb
|
|
89
89
|
- lib/slk/services/emoji_searcher.rb
|
|
90
90
|
- lib/slk/services/encryption.rb
|
|
91
|
+
- lib/slk/services/file_downloader.rb
|
|
91
92
|
- lib/slk/services/gemoji_sync.rb
|
|
92
93
|
- lib/slk/services/message_resolver.rb
|
|
93
94
|
- lib/slk/services/preset_store.rb
|
|
@@ -132,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
132
133
|
- !ruby/object:Gem::Version
|
|
133
134
|
version: '0'
|
|
134
135
|
requirements: []
|
|
135
|
-
rubygems_version: 4.0.
|
|
136
|
+
rubygems_version: 4.0.10
|
|
136
137
|
specification_version: 4
|
|
137
138
|
summary: A command-line interface for Slack
|
|
138
139
|
test_files: []
|