slk 0.2.0 → 0.4.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 +39 -1
- data/README.md +30 -12
- data/bin/ci +15 -0
- data/bin/coverage +225 -0
- data/bin/test +7 -0
- data/lib/slk/api/search.rb +31 -0
- data/lib/slk/cli.rb +50 -3
- data/lib/slk/commands/base.rb +1 -0
- data/lib/slk/commands/catchup.rb +3 -2
- data/lib/slk/commands/config.rb +48 -45
- data/lib/slk/commands/help.rb +1 -0
- data/lib/slk/commands/messages.rb +59 -11
- data/lib/slk/commands/search.rb +223 -0
- data/lib/slk/commands/ssh_key_manager.rb +129 -0
- data/lib/slk/formatters/attachment_formatter.rb +16 -2
- data/lib/slk/formatters/mention_replacer.rb +13 -31
- data/lib/slk/formatters/message_formatter.rb +8 -15
- data/lib/slk/formatters/search_formatter.rb +75 -0
- data/lib/slk/models/search_result.rb +115 -0
- data/lib/slk/runner.rb +12 -0
- data/lib/slk/services/api_client.rb +60 -11
- data/lib/slk/services/cache_store.rb +55 -36
- data/lib/slk/services/encryption.rb +114 -11
- data/lib/slk/services/setup_wizard.rb +3 -3
- data/lib/slk/services/target_resolver.rb +27 -4
- data/lib/slk/services/token_loader.rb +83 -0
- data/lib/slk/services/token_saver.rb +87 -0
- data/lib/slk/services/token_store.rb +35 -65
- data/lib/slk/services/user_lookup.rb +117 -0
- data/lib/slk/support/date_parser.rb +64 -0
- data/lib/slk/support/platform.rb +34 -0
- data/lib/slk/support/xdg_paths.rb +27 -9
- data/lib/slk/version.rb +1 -1
- data/lib/slk.rb +8 -0
- metadata +14 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd09692101d4658e57208d39e81d770e6dc36d2218088194ef55ad5fad0adbd0
|
|
4
|
+
data.tar.gz: 88e03a0be601536ffb3fd8f1b986900f8f57144b03a52ec5fa24b0b5dbdfbc57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 399ca26d5d030704ad553e579fab75376db1578e0814f1ad8a9c2e4115c44e32c8173263fb39c8839eb7a0014ca1758d4e1aaa33b8d65353ba64e6fe83fa8efe
|
|
7
|
+
data.tar.gz: 64bfbe31c44af1961f43cd7f027bd352847af05434b957bbceb03e26c8bdf820ee805c1fb67d2adac80e18e7ddc286fd643572a17695209db1e3e7826c358baa
|
data/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## [0.4.0] - 2026-01-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Windows Support** - slk now runs on Windows
|
|
15
|
+
- Uses `%APPDATA%` and `%LOCALAPPDATA%` for config/cache directories
|
|
16
|
+
- Cross-platform command detection with `Open3.capture3`
|
|
17
|
+
- Proper NTFS permission handling (skips `chmod` on Windows)
|
|
18
|
+
- New `Support::Platform` module for OS-specific behavior
|
|
19
|
+
- CI testing on Windows (Ruby 3.2, 3.3, 3.4, 4.0)
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- New `UserLookup` service consolidates duplicate user name resolution logic
|
|
24
|
+
- Removed ~65 lines of duplicated code from `MentionReplacer` and `MessageFormatter`
|
|
25
|
+
|
|
26
|
+
## [0.3.0] - 2026-01-16
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- `-vv`/`--very-verbose` flag for detailed API debugging with timing and response bodies
|
|
31
|
+
- SSH key validation and token migration when keys change
|
|
32
|
+
- Public key validation (ensures it matches private key)
|
|
33
|
+
- `config unset` command for removing configuration values
|
|
34
|
+
- CI infrastructure with GitHub Actions (Ruby 3.2-4.0, macOS, Ubuntu)
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Improved error handling throughout with comprehensive tests
|
|
39
|
+
- Better SSH key error messages with public key prompting
|
|
40
|
+
- Cache user lookups to reduce API calls
|
|
41
|
+
- Improved rate limit error messages
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Test output no longer leaks to stdout
|
|
46
|
+
- All rubocop offenses resolved
|
|
11
47
|
|
|
12
48
|
## [0.2.0] - 2025-01-15
|
|
13
49
|
|
|
@@ -63,5 +99,7 @@ Initial release of the Ruby rewrite. Pure Ruby, no external dependencies.
|
|
|
63
99
|
- Pure Ruby stdlib - no gem dependencies
|
|
64
100
|
- Ruby 3.2+ with modern features (Data.define, pattern matching)
|
|
65
101
|
|
|
102
|
+
[0.4.0]: https://github.com/ericboehs/slk/releases/tag/v0.4.0
|
|
103
|
+
[0.3.0]: https://github.com/ericboehs/slk/releases/tag/v0.3.0
|
|
66
104
|
[0.2.0]: https://github.com/ericboehs/slk/releases/tag/v0.2.0
|
|
67
105
|
[0.1.0]: https://github.com/ericboehs/slk/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -12,6 +12,22 @@ gem install slk
|
|
|
12
12
|
|
|
13
13
|
Requires Ruby 3.2+.
|
|
14
14
|
|
|
15
|
+
### Windows
|
|
16
|
+
|
|
17
|
+
```powershell
|
|
18
|
+
# Install Ruby (if needed) via RubyInstaller or Chocolatey
|
|
19
|
+
winget install RubyInstallerTeam.Ruby.3.3
|
|
20
|
+
# or: choco install ruby
|
|
21
|
+
|
|
22
|
+
# Install slk
|
|
23
|
+
gem install slk
|
|
24
|
+
|
|
25
|
+
# (Optional) Install age for encrypted token storage
|
|
26
|
+
choco install age.portable
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Configuration is stored in `%APPDATA%\slk\` on Windows.
|
|
30
|
+
|
|
15
31
|
## Setup
|
|
16
32
|
|
|
17
33
|
Run the setup wizard:
|
|
@@ -56,10 +72,10 @@ slk dnd off # Disable DND
|
|
|
56
72
|
### Messages
|
|
57
73
|
|
|
58
74
|
```bash
|
|
59
|
-
slk messages
|
|
75
|
+
slk messages general # Read channel messages
|
|
60
76
|
slk messages @username # Read DM with user
|
|
61
|
-
slk messages
|
|
62
|
-
slk messages
|
|
77
|
+
slk messages general -n 50 # Show 50 messages
|
|
78
|
+
slk messages general --json # Output as JSON
|
|
63
79
|
```
|
|
64
80
|
|
|
65
81
|
### Activity
|
|
@@ -86,7 +102,7 @@ Use `--show-messages` (or `-m`) to preview the actual message content for each a
|
|
|
86
102
|
```bash
|
|
87
103
|
slk unread # Show unread counts
|
|
88
104
|
slk unread clear # Mark all as read
|
|
89
|
-
slk unread clear
|
|
105
|
+
slk unread clear general # Mark channel as read
|
|
90
106
|
```
|
|
91
107
|
|
|
92
108
|
### Catchup (Interactive Triage)
|
|
@@ -155,13 +171,13 @@ Tokens will be stored encrypted in `~/.config/slk/tokens.age`.
|
|
|
155
171
|
|
|
156
172
|
## Configuration
|
|
157
173
|
|
|
158
|
-
Files are stored in XDG-compliant locations:
|
|
174
|
+
Files are stored in XDG-compliant locations (or `%APPDATA%`/`%LOCALAPPDATA%` on Windows):
|
|
159
175
|
|
|
160
|
-
- **Config**: `~/.config/slk/`
|
|
176
|
+
- **Config**: `~/.config/slk/` (Windows: `%APPDATA%\slk\`)
|
|
161
177
|
- `config.json` - Settings
|
|
162
178
|
- `tokens.json` or `tokens.age` - Workspace tokens
|
|
163
179
|
- `presets.json` - Status presets
|
|
164
|
-
- **Cache**: `~/.cache/slk/`
|
|
180
|
+
- **Cache**: `~/.cache/slk/` (Windows: `%LOCALAPPDATA%\slk\`)
|
|
165
181
|
- `users-{workspace}.json` - User cache
|
|
166
182
|
- `channels-{workspace}.json` - Channel cache
|
|
167
183
|
|
|
@@ -177,13 +193,15 @@ ruby -Ilib bin/slk --version
|
|
|
177
193
|
|
|
178
194
|
# Run tests
|
|
179
195
|
rake test
|
|
196
|
+
```
|
|
180
197
|
|
|
181
|
-
|
|
182
|
-
gem build slk.gemspec
|
|
198
|
+
### Releasing
|
|
183
199
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
200
|
+
1. Update version in `lib/slk/version.rb`
|
|
201
|
+
2. Update `CHANGELOG.md` (move Unreleased to new version, add date)
|
|
202
|
+
3. Commit: `git commit -am "Release vX.Y.Z"`
|
|
203
|
+
4. Release to RubyGems: `rake release`
|
|
204
|
+
5. Create GitHub Release: `gh release create vX.Y.Z --generate-notes`
|
|
187
205
|
|
|
188
206
|
## License
|
|
189
207
|
|
data/bin/ci
ADDED
data/bin/coverage
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/inline'
|
|
5
|
+
|
|
6
|
+
gemfile do
|
|
7
|
+
source 'https://rubygems.org'
|
|
8
|
+
gem 'nokogiri'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Path to the SimpleCov HTML report
|
|
12
|
+
coverage_file = File.join(Dir.pwd, 'coverage', 'index.html')
|
|
13
|
+
|
|
14
|
+
unless File.exist?(coverage_file)
|
|
15
|
+
puts "No coverage report found at #{coverage_file}"
|
|
16
|
+
puts 'Run tests first: COVERAGE=1 bundle exec rake test'
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Parse the HTML file
|
|
21
|
+
doc = Nokogiri::HTML(File.read(coverage_file))
|
|
22
|
+
|
|
23
|
+
# Extract overall line coverage
|
|
24
|
+
line_percent = doc.css('.covered_percent span').first&.text&.strip
|
|
25
|
+
total_lines = doc.css('.t-line-summary b').first&.text&.strip
|
|
26
|
+
covered_lines = doc.css('.t-line-summary .green b').first&.text&.strip
|
|
27
|
+
missed_lines = doc.css('.t-line-summary .red b').first&.text&.strip
|
|
28
|
+
|
|
29
|
+
# Extract overall branch coverage (first t-branch-summary is the overall stats)
|
|
30
|
+
overall_branch_summary = doc.css('.t-branch-summary').first
|
|
31
|
+
if overall_branch_summary
|
|
32
|
+
branch_percent = overall_branch_summary.css('span').last.text.strip.gsub(/[()]/, '')
|
|
33
|
+
branch_summary_spans = overall_branch_summary.css('span b')
|
|
34
|
+
overall_total_branches = branch_summary_spans[0]&.text&.strip
|
|
35
|
+
overall_covered_branches = branch_summary_spans[1]&.text&.strip
|
|
36
|
+
overall_missed_branches = branch_summary_spans[2]&.text&.strip
|
|
37
|
+
else
|
|
38
|
+
# No branch coverage available
|
|
39
|
+
branch_percent = nil
|
|
40
|
+
overall_total_branches = nil
|
|
41
|
+
overall_covered_branches = nil
|
|
42
|
+
overall_missed_branches = nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Extract timestamp
|
|
46
|
+
timestamp = doc.css('.timestamp .timeago').first&.attr('title')
|
|
47
|
+
|
|
48
|
+
puts '## SimpleCov Coverage Report'
|
|
49
|
+
puts "Generated: #{timestamp}"
|
|
50
|
+
puts ''
|
|
51
|
+
|
|
52
|
+
puts "### Line Coverage: #{line_percent}"
|
|
53
|
+
puts " #{covered_lines}/#{total_lines} lines covered"
|
|
54
|
+
puts " #{missed_lines} lines missed"
|
|
55
|
+
puts ''
|
|
56
|
+
|
|
57
|
+
if branch_percent
|
|
58
|
+
puts "### Branch Coverage: #{branch_percent}"
|
|
59
|
+
puts " #{overall_covered_branches}/#{overall_total_branches} branches covered"
|
|
60
|
+
puts " #{overall_missed_branches} branches missed"
|
|
61
|
+
puts ''
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Show file-by-file breakdown if there are missed lines or branches
|
|
65
|
+
if missed_lines.to_i.positive? || overall_missed_branches&.to_i&.positive?
|
|
66
|
+
# First, collect all files with missing coverage
|
|
67
|
+
files_with_issues = []
|
|
68
|
+
files_shown = Set.new
|
|
69
|
+
|
|
70
|
+
doc.css('tbody .t-file').each do |row|
|
|
71
|
+
file_name = row.css('.t-file__name a').first&.text&.strip
|
|
72
|
+
line_coverage = row.css('.t-file__coverage').first&.text&.strip
|
|
73
|
+
branch_coverage = row.css('.t-file__branch-coverage').first&.text&.strip
|
|
74
|
+
|
|
75
|
+
# Only include files that aren't 100% covered and haven't been seen yet
|
|
76
|
+
should_skip = files_shown.include?(file_name) ||
|
|
77
|
+
(line_coverage == '100.00 %' && (!branch_coverage || branch_coverage == '100.00 %'))
|
|
78
|
+
next if should_skip
|
|
79
|
+
|
|
80
|
+
files_shown.add(file_name)
|
|
81
|
+
files_with_issues << row
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
total_files_with_issues = files_with_issues.length
|
|
85
|
+
files_to_show = files_with_issues.take(50)
|
|
86
|
+
|
|
87
|
+
puts '### Files with missing coverage:'
|
|
88
|
+
puts ''
|
|
89
|
+
if total_files_with_issues > 50
|
|
90
|
+
puts "Showing top 50 of #{total_files_with_issues} files with missing coverage:"
|
|
91
|
+
puts ''
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# rubocop:disable Metrics/BlockLength
|
|
95
|
+
files_to_show.each do |row|
|
|
96
|
+
file_name = row.css('.t-file__name a').first&.text&.strip
|
|
97
|
+
line_coverage = row.css('.t-file__coverage').first&.text&.strip
|
|
98
|
+
branch_coverage = row.css('.t-file__branch-coverage').first&.text&.strip
|
|
99
|
+
file_link = row.css('.t-file__name a').first&.attr('href')
|
|
100
|
+
|
|
101
|
+
# Extract detailed line information for this file
|
|
102
|
+
missed_lines = []
|
|
103
|
+
missed_branches = []
|
|
104
|
+
total_file_lines = 0
|
|
105
|
+
covered_file_lines = 0
|
|
106
|
+
|
|
107
|
+
if file_link
|
|
108
|
+
file_id = file_link.gsub('#', '')
|
|
109
|
+
file_section = doc.css("##{file_id}")
|
|
110
|
+
|
|
111
|
+
if file_section.any?
|
|
112
|
+
# Get the actual counts from SimpleCov's summary
|
|
113
|
+
line_summary = file_section.css('.t-line-summary')
|
|
114
|
+
if line_summary.any? # rubocop:disable Metrics/BlockNesting
|
|
115
|
+
summary_text = line_summary.text
|
|
116
|
+
# Extract numbers from text like "13 relevant lines. 12 lines covered and 1 lines missed."
|
|
117
|
+
total_file_lines = Regexp.last_match(1).to_i if summary_text.match(/(\d+)\s+relevant\s+lines/) # rubocop:disable Metrics/BlockNesting
|
|
118
|
+
covered_file_lines = Regexp.last_match(1).to_i if summary_text.match(/(\d+)\s+lines\s+covered/) # rubocop:disable Metrics/BlockNesting
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Find missed lines and branches
|
|
122
|
+
file_section.css('li').each do |line_item|
|
|
123
|
+
line_number = line_item.attr('data-linenumber')
|
|
124
|
+
line_class = line_item.attr('class')
|
|
125
|
+
|
|
126
|
+
if line_class&.include?('missed') && !line_class.include?('missed-branch') # rubocop:disable Metrics/BlockNesting
|
|
127
|
+
missed_lines << line_number
|
|
128
|
+
elsif line_class&.include?('missed-branch') # rubocop:disable Metrics/BlockNesting
|
|
129
|
+
missed_branches << line_number
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Format the line ranges more clearly
|
|
136
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
137
|
+
def format_line_ranges(lines)
|
|
138
|
+
return '' if lines.empty?
|
|
139
|
+
|
|
140
|
+
ranges = []
|
|
141
|
+
current_range = [lines.first.to_i]
|
|
142
|
+
|
|
143
|
+
lines.map(&:to_i).sort[1..]&.each do |line|
|
|
144
|
+
if line == current_range.last + 1
|
|
145
|
+
current_range << line
|
|
146
|
+
else
|
|
147
|
+
ranges << format_range(current_range)
|
|
148
|
+
current_range = [line]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
ranges << format_range(current_range)
|
|
152
|
+
|
|
153
|
+
"L#{ranges.join(', L')}"
|
|
154
|
+
end
|
|
155
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
156
|
+
|
|
157
|
+
def format_range(range)
|
|
158
|
+
if range.length == 1
|
|
159
|
+
range.first.to_s
|
|
160
|
+
else
|
|
161
|
+
"#{range.first}-#{range.last}"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Get branch counts from the file section
|
|
166
|
+
covered_branches = 0
|
|
167
|
+
total_branches = 0
|
|
168
|
+
|
|
169
|
+
if file_link
|
|
170
|
+
file_id = file_link.gsub('#', '')
|
|
171
|
+
file_section = doc.css("##{file_id}")
|
|
172
|
+
branch_summary = file_section.css('.t-branch-summary')
|
|
173
|
+
|
|
174
|
+
if branch_summary.any?
|
|
175
|
+
branch_spans = branch_summary.css('span b')
|
|
176
|
+
total_branches = branch_spans[0]&.text.to_i
|
|
177
|
+
covered_branches = branch_spans[1]&.text.to_i
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
branch_display = branch_coverage ? "Branch: #{branch_coverage}" : 'Branch: N/A'
|
|
182
|
+
puts " #{file_name} (Line: #{line_coverage}, #{branch_display}):"
|
|
183
|
+
|
|
184
|
+
line_info = "Lines: #{covered_file_lines}/#{total_file_lines}"
|
|
185
|
+
line_info += " (missed: #{format_line_ranges(missed_lines)})" unless missed_lines.empty?
|
|
186
|
+
puts " #{line_info}"
|
|
187
|
+
|
|
188
|
+
if total_branches.positive?
|
|
189
|
+
branch_info = "Branches: #{covered_branches}/#{total_branches}"
|
|
190
|
+
branch_info += " (missed: #{format_line_ranges(missed_branches)})" unless missed_branches.empty?
|
|
191
|
+
puts " #{branch_info}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
puts ''
|
|
195
|
+
end
|
|
196
|
+
# rubocop:enable Metrics/BlockLength
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Write to GitHub Actions job summary if available
|
|
200
|
+
if ENV['GITHUB_STEP_SUMMARY']
|
|
201
|
+
File.open(ENV['GITHUB_STEP_SUMMARY'], 'a') do |f|
|
|
202
|
+
f.puts ''
|
|
203
|
+
f.puts '## Test Coverage Report'
|
|
204
|
+
f.puts ''
|
|
205
|
+
f.puts "**Line Coverage:** #{line_percent} (#{covered_lines}/#{total_lines} lines)"
|
|
206
|
+
f.puts ''
|
|
207
|
+
|
|
208
|
+
if branch_percent
|
|
209
|
+
f.puts "**Branch Coverage:** #{branch_percent} (#{overall_covered_branches}/#{overall_total_branches} branches)"
|
|
210
|
+
f.puts ''
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Add visual indicators
|
|
214
|
+
line_pct_value = line_percent.gsub('%', '').to_f
|
|
215
|
+
branch_pct_value = branch_percent&.gsub('%', '')&.to_f
|
|
216
|
+
|
|
217
|
+
if line_pct_value >= 95 && (!branch_pct_value || branch_pct_value >= 95)
|
|
218
|
+
f.puts 'Coverage meets quality thresholds'
|
|
219
|
+
elsif line_pct_value >= 80 && (!branch_pct_value || branch_pct_value >= 80)
|
|
220
|
+
f.puts 'Coverage is acceptable but could be improved'
|
|
221
|
+
else
|
|
222
|
+
f.puts 'Coverage is below recommended thresholds'
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
data/bin/test
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Api
|
|
5
|
+
# Wrapper for Slack search.* API endpoints
|
|
6
|
+
# Note: search.messages requires user tokens (xoxc/xoxs), NOT bot tokens (xoxb)
|
|
7
|
+
class Search
|
|
8
|
+
def initialize(api_client, workspace)
|
|
9
|
+
@api = api_client
|
|
10
|
+
@workspace = workspace
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Search for messages across channels and DMs
|
|
14
|
+
# @param query [String] Search query with optional modifiers (in:, from:, before:, after:, etc.)
|
|
15
|
+
# @param count [Integer] Number of results per page (max 100)
|
|
16
|
+
# @param page [Integer] Page number (1-indexed)
|
|
17
|
+
# @param sort [String] Sort field: 'score' or 'timestamp'
|
|
18
|
+
# @param sort_dir [String] Sort direction: 'asc' or 'desc'
|
|
19
|
+
# @return [Hash] API response with messages.matches array
|
|
20
|
+
def messages(query:, count: 20, page: 1, sort: 'timestamp', sort_dir: 'desc')
|
|
21
|
+
@api.get(@workspace, 'search.messages', {
|
|
22
|
+
query: query,
|
|
23
|
+
count: [count, 100].min,
|
|
24
|
+
page: page,
|
|
25
|
+
sort: sort,
|
|
26
|
+
sort_dir: sort_dir
|
|
27
|
+
})
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/slk/cli.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Slk
|
|
|
13
13
|
'unread' => Commands::Unread,
|
|
14
14
|
'catchup' => Commands::Catchup,
|
|
15
15
|
'activity' => Commands::Activity,
|
|
16
|
+
'search' => Commands::Search,
|
|
16
17
|
'preset' => Commands::Preset,
|
|
17
18
|
'workspaces' => Commands::Workspaces,
|
|
18
19
|
'cache' => Commands::Cache,
|
|
@@ -115,10 +116,12 @@ module Slk
|
|
|
115
116
|
end
|
|
116
117
|
|
|
117
118
|
def build_runner(args)
|
|
118
|
-
verbose =
|
|
119
|
-
|
|
119
|
+
verbose = verbose_mode?(args)
|
|
120
|
+
very_verbose = args.include?('-vv') || args.include?('--very-verbose')
|
|
121
|
+
output = @output || Formatters::Output.new(verbose: verbose)
|
|
120
122
|
runner = Runner.new(output: output)
|
|
121
123
|
setup_verbose_logging(runner, output) if verbose
|
|
124
|
+
setup_very_verbose_logging(runner, output) if very_verbose
|
|
122
125
|
runner
|
|
123
126
|
end
|
|
124
127
|
|
|
@@ -128,6 +131,49 @@ module Slk
|
|
|
128
131
|
}
|
|
129
132
|
end
|
|
130
133
|
|
|
134
|
+
def setup_very_verbose_logging(runner, output)
|
|
135
|
+
setup_response_header_logging(runner, output)
|
|
136
|
+
setup_request_body_logging(runner, output)
|
|
137
|
+
setup_response_body_logging(runner, output)
|
|
138
|
+
setup_cache_logging(runner, output)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def setup_response_header_logging(runner, output)
|
|
142
|
+
runner.api_client.on_response = lambda { |method, code, headers|
|
|
143
|
+
next if headers.empty?
|
|
144
|
+
|
|
145
|
+
elapsed = headers.delete('elapsed_ms')
|
|
146
|
+
req_id = headers.delete('X-Slack-Req-Id')
|
|
147
|
+
rate_parts = headers.map { |k, v| "#{k.sub('X-RateLimit-', '')}=#{v}" }
|
|
148
|
+
|
|
149
|
+
parts = ["#{elapsed}ms"]
|
|
150
|
+
parts << "req=#{req_id}" if req_id
|
|
151
|
+
parts.concat(rate_parts)
|
|
152
|
+
output.debug(" #{method} #{code}: #{parts.join(', ')}")
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def setup_request_body_logging(runner, output)
|
|
157
|
+
runner.api_client.on_request_body = lambda { |method, body|
|
|
158
|
+
truncated = body.length > 500 ? "#{body[0..500]}..." : body
|
|
159
|
+
output.debug(" #{method} request: #{truncated}")
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def setup_response_body_logging(runner, output)
|
|
164
|
+
runner.api_client.on_response_body = lambda { |method, body|
|
|
165
|
+
truncated = body.length > 500 ? "#{body[0..500]}..." : body
|
|
166
|
+
output.debug(" #{method} response: #{truncated}")
|
|
167
|
+
}
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def setup_cache_logging(runner, output)
|
|
171
|
+
runner.cache_store.on_cache_access = lambda { |type, _workspace, key, hit, value|
|
|
172
|
+
status = hit ? "HIT (#{value})" : 'MISS'
|
|
173
|
+
output.debug(" [Cache] #{type} #{key}: #{status}")
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
|
|
131
177
|
def execute_command(command_class, args, runner)
|
|
132
178
|
command = command_class.new(args, runner: runner)
|
|
133
179
|
result = command.execute
|
|
@@ -136,7 +182,8 @@ module Slk
|
|
|
136
182
|
end
|
|
137
183
|
|
|
138
184
|
def verbose_mode?(args)
|
|
139
|
-
args.include?('-v') || args.include?('--verbose')
|
|
185
|
+
args.include?('-v') || args.include?('--verbose') ||
|
|
186
|
+
args.include?('-vv') || args.include?('--very-verbose')
|
|
140
187
|
end
|
|
141
188
|
|
|
142
189
|
def log_api_call_count(runner)
|
data/lib/slk/commands/base.rb
CHANGED
|
@@ -69,6 +69,7 @@ module Slk
|
|
|
69
69
|
when '--no-wrap' then @options[:width] = nil
|
|
70
70
|
when '--all' then @options[:all] = true
|
|
71
71
|
when '-v', '--verbose' then @options[:verbose] = true
|
|
72
|
+
when '-vv', '--very-verbose' then @options[:verbose] = @options[:very_verbose] = true
|
|
72
73
|
when '-q', '--quiet' then @options[:quiet] = true
|
|
73
74
|
when '--json' then @options[:json] = true
|
|
74
75
|
when '-h', '--help' then @options[:help] = true
|
data/lib/slk/commands/catchup.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative '../support/help_formatter'
|
|
4
|
+
require_relative '../support/platform'
|
|
4
5
|
|
|
5
6
|
module Slk
|
|
6
7
|
module Commands
|
|
@@ -293,7 +294,7 @@ module Slk
|
|
|
293
294
|
|
|
294
295
|
def open_channel_in_slack(workspace, channel_id)
|
|
295
296
|
team_id = runner.client_api(workspace.name).team_id
|
|
296
|
-
|
|
297
|
+
Support::Platform.open_url("slack://channel?team=#{team_id}&id=#{channel_id}")
|
|
297
298
|
success('Opened in Slack')
|
|
298
299
|
:next
|
|
299
300
|
end
|
|
@@ -402,7 +403,7 @@ module Slk
|
|
|
402
403
|
first = thread_mark_data.first
|
|
403
404
|
team_id = runner.client_api(workspace.name).team_id
|
|
404
405
|
url = "slack://channel?team=#{team_id}&id=#{first[:channel]}&thread_ts=#{first[:thread_ts]}"
|
|
405
|
-
|
|
406
|
+
Support::Platform.open_url(url)
|
|
406
407
|
success('Opened in Slack')
|
|
407
408
|
end
|
|
408
409
|
end
|