@urugus/slack-cli 0.2.3 → 0.2.5
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.
- package/.claude/settings.local.json +7 -1
- package/.github/workflows/pr-validation.yml +0 -10
- package/README.md +86 -19
- package/dist/commands/unread.d.ts.map +1 -1
- package/dist/commands/unread.js +12 -0
- package/dist/commands/unread.js.map +1 -1
- package/dist/types/commands.d.ts +1 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/utils/format-utils.d.ts.map +1 -1
- package/dist/utils/format-utils.js +2 -1
- package/dist/utils/format-utils.js.map +1 -1
- package/dist/utils/mention-utils.d.ts +17 -0
- package/dist/utils/mention-utils.d.ts.map +1 -0
- package/dist/utils/mention-utils.js +45 -0
- package/dist/utils/mention-utils.js.map +1 -0
- package/dist/utils/slack-api-client.d.ts +1 -0
- package/dist/utils/slack-api-client.d.ts.map +1 -1
- package/dist/utils/slack-api-client.js +3 -0
- package/dist/utils/slack-api-client.js.map +1 -1
- package/dist/utils/slack-operations/message-operations.d.ts +1 -0
- package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
- package/dist/utils/slack-operations/message-operations.js +9 -2
- package/dist/utils/slack-operations/message-operations.js.map +1 -1
- package/dist/utils/slack-patterns.d.ts +6 -0
- package/dist/utils/slack-patterns.d.ts.map +1 -0
- package/dist/utils/slack-patterns.js +11 -0
- package/dist/utils/slack-patterns.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/unread.ts +14 -0
- package/src/types/commands.ts +1 -0
- package/src/utils/format-utils.ts +3 -1
- package/src/utils/mention-utils.ts +47 -0
- package/src/utils/slack-api-client.ts +4 -0
- package/src/utils/slack-operations/message-operations.ts +10 -2
- package/src/utils/slack-patterns.ts +9 -0
- package/tests/commands/unread.test.ts +60 -0
- package/tests/index.test.ts +4 -4
- package/tests/utils/mention-utils.test.ts +100 -0
- package/tests/utils/slack-operations/message-operations.test.ts +126 -0
|
@@ -57,7 +57,13 @@
|
|
|
57
57
|
"Bash(npx tsx:*)",
|
|
58
58
|
"Bash(gh workflow view:*)",
|
|
59
59
|
"Bash(git tag:*)",
|
|
60
|
-
"Bash(./bin/dev:*)"
|
|
60
|
+
"Bash(./bin/dev:*)",
|
|
61
|
+
"Bash(git checkout:*)",
|
|
62
|
+
"Bash(gh pr create:*)",
|
|
63
|
+
"Bash(gh pr checks:*)",
|
|
64
|
+
"Bash(gh pr view:*)",
|
|
65
|
+
"Bash(gh pr merge:*)",
|
|
66
|
+
"Bash(git pull:*)"
|
|
61
67
|
],
|
|
62
68
|
"deny": []
|
|
63
69
|
}
|
|
@@ -28,16 +28,6 @@ jobs:
|
|
|
28
28
|
|
|
29
29
|
- name: Run tests with coverage
|
|
30
30
|
run: npm run test:coverage
|
|
31
|
-
|
|
32
|
-
- name: Comment PR with coverage
|
|
33
|
-
uses: ArtiomTr/jest-coverage-report-action@v2
|
|
34
|
-
if: github.event_name == 'pull_request'
|
|
35
|
-
with:
|
|
36
|
-
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
37
|
-
test-script: npm run test:coverage
|
|
38
|
-
skip-step: all
|
|
39
|
-
coverage-file: ./coverage/coverage-final.json
|
|
40
|
-
base-coverage-file: ./coverage/coverage-final.json
|
|
41
31
|
|
|
42
32
|
- name: Check build
|
|
43
33
|
run: npm run build
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install -g @urugus/slack-cli
|
|
|
13
13
|
You need to configure your Slack API token on first use:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
slack-cli config --token YOUR_SLACK_API_TOKEN
|
|
16
|
+
slack-cli config set --token YOUR_SLACK_API_TOKEN
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
@@ -70,16 +70,29 @@ slack-cli channels
|
|
|
70
70
|
slack-cli channels --profile work
|
|
71
71
|
|
|
72
72
|
# List public channels only
|
|
73
|
-
slack-cli channels --public
|
|
73
|
+
slack-cli channels --type public
|
|
74
74
|
|
|
75
75
|
# List private channels only
|
|
76
|
-
slack-cli channels --private
|
|
76
|
+
slack-cli channels --type private
|
|
77
|
+
|
|
78
|
+
# List all channel types including IMs and MPIMs
|
|
79
|
+
slack-cli channels --type all
|
|
80
|
+
|
|
81
|
+
# Include archived channels
|
|
82
|
+
slack-cli channels --include-archived
|
|
83
|
+
|
|
84
|
+
# Limit number of channels displayed
|
|
85
|
+
slack-cli channels --limit 20
|
|
86
|
+
|
|
87
|
+
# Output in different formats
|
|
88
|
+
slack-cli channels --format json
|
|
89
|
+
slack-cli channels --format simple
|
|
77
90
|
```
|
|
78
91
|
|
|
79
92
|
### View Message History
|
|
80
93
|
|
|
81
94
|
```bash
|
|
82
|
-
# Get latest 10 messages
|
|
95
|
+
# Get latest 10 messages (default)
|
|
83
96
|
slack-cli history -c general
|
|
84
97
|
|
|
85
98
|
# Specify number of messages
|
|
@@ -87,6 +100,9 @@ slack-cli history -c general -n 20
|
|
|
87
100
|
|
|
88
101
|
# Get messages since specific date
|
|
89
102
|
slack-cli history -c general --since "2024-01-01 00:00:00"
|
|
103
|
+
|
|
104
|
+
# Use specific profile
|
|
105
|
+
slack-cli history -c general --profile work
|
|
90
106
|
```
|
|
91
107
|
|
|
92
108
|
### Get Unread Messages
|
|
@@ -98,14 +114,21 @@ slack-cli unread
|
|
|
98
114
|
# Get unread messages from specific channel
|
|
99
115
|
slack-cli unread -c general
|
|
100
116
|
|
|
101
|
-
#
|
|
102
|
-
slack-cli unread --
|
|
117
|
+
# Show only unread counts (no message content)
|
|
118
|
+
slack-cli unread --count-only
|
|
103
119
|
|
|
104
120
|
# Mark messages as read after fetching
|
|
105
121
|
slack-cli unread --mark-read
|
|
106
122
|
|
|
107
|
-
#
|
|
108
|
-
slack-cli unread -c general
|
|
123
|
+
# Mark messages as read for specific channel
|
|
124
|
+
slack-cli unread -c general --mark-read
|
|
125
|
+
|
|
126
|
+
# Limit number of channels displayed
|
|
127
|
+
slack-cli unread --limit 10
|
|
128
|
+
|
|
129
|
+
# Output in different formats
|
|
130
|
+
slack-cli unread --format json
|
|
131
|
+
slack-cli unread --format simple
|
|
109
132
|
```
|
|
110
133
|
|
|
111
134
|
### Other Commands
|
|
@@ -126,23 +149,42 @@ slack-cli config set --token NEW_TOKEN
|
|
|
126
149
|
|
|
127
150
|
## Options
|
|
128
151
|
|
|
152
|
+
### Global Options
|
|
153
|
+
| Option | Short | Description |
|
|
154
|
+
|--------|-------|-------------|
|
|
155
|
+
| --profile | -p | Use specific workspace profile |
|
|
156
|
+
|
|
157
|
+
### send command
|
|
129
158
|
| Option | Short | Description |
|
|
130
159
|
|--------|-------|-------------|
|
|
131
|
-
| --channel | -c | Target channel name or ID |
|
|
160
|
+
| --channel | -c | Target channel name or ID (required) |
|
|
132
161
|
| --message | -m | Message to send |
|
|
133
162
|
| --file | -f | File containing message content |
|
|
134
|
-
| --token | -t | Slack API token (temporary override) |
|
|
135
|
-
| --profile | -p | Use specific workspace profile |
|
|
136
|
-
| --format | | Message format (text/markdown) |
|
|
137
|
-
| --verbose | -v | Show verbose output |
|
|
138
|
-
| --show-channel | | Display channel name with unread messages |
|
|
139
|
-
| --mark-read | | Mark messages as read after fetching |
|
|
140
163
|
|
|
141
|
-
|
|
164
|
+
### channels command
|
|
165
|
+
| Option | Short | Description |
|
|
166
|
+
|--------|-------|-------------|
|
|
167
|
+
| --type | | Channel type: public, private, im, mpim, all (default: public) |
|
|
168
|
+
| --include-archived | | Include archived channels |
|
|
169
|
+
| --format | | Output format: table, simple, json (default: table) |
|
|
170
|
+
| --limit | | Maximum number of channels to list (default: 100) |
|
|
171
|
+
|
|
172
|
+
### history command
|
|
173
|
+
| Option | Short | Description |
|
|
174
|
+
|--------|-------|-------------|
|
|
175
|
+
| --channel | -c | Target channel name or ID (required) |
|
|
176
|
+
| --number | -n | Number of messages to retrieve (default: 10) |
|
|
177
|
+
| --since | | Get messages since specific date (YYYY-MM-DD HH:MM:SS) |
|
|
178
|
+
|
|
179
|
+
### unread command
|
|
180
|
+
| Option | Short | Description |
|
|
181
|
+
|--------|-------|-------------|
|
|
182
|
+
| --channel | -c | Get unread for specific channel |
|
|
183
|
+
| --format | | Output format: table, simple, json (default: table) |
|
|
184
|
+
| --count-only | | Show only unread counts |
|
|
185
|
+
| --limit | | Maximum number of channels to display (default: 50) |
|
|
186
|
+
| --mark-read | | Mark messages as read after fetching |
|
|
142
187
|
|
|
143
|
-
- `SLACK_API_TOKEN`: Default API token (used when no profile is configured)
|
|
144
|
-
- `SLACK_DEFAULT_CHANNEL`: Default target channel
|
|
145
|
-
- `SLACK_DEFAULT_PROFILE`: Default profile to use
|
|
146
188
|
|
|
147
189
|
## Required Permissions
|
|
148
190
|
|
|
@@ -156,6 +198,31 @@ Your Slack API token needs the following scopes:
|
|
|
156
198
|
- `im:history` - Read direct message history
|
|
157
199
|
- `users:read` - Access user information for unread counts
|
|
158
200
|
|
|
201
|
+
## Advanced Features
|
|
202
|
+
|
|
203
|
+
### Rate Limiting
|
|
204
|
+
The CLI includes built-in rate limiting to handle Slack API limits:
|
|
205
|
+
- Concurrent requests: 3
|
|
206
|
+
- Automatic retry with exponential backoff (max 3 retries)
|
|
207
|
+
- Graceful error handling for rate limit errors
|
|
208
|
+
|
|
209
|
+
### Output Formats
|
|
210
|
+
Most commands support multiple output formats:
|
|
211
|
+
- `table` (default) - Human-readable table format
|
|
212
|
+
- `simple` - Simplified text output
|
|
213
|
+
- `json` - Machine-readable JSON format
|
|
214
|
+
|
|
215
|
+
### Markdown Support
|
|
216
|
+
Messages sent via the `send` command automatically support Slack's mrkdwn formatting:
|
|
217
|
+
- `*bold*` for bold text
|
|
218
|
+
- `_italic_` for italic text
|
|
219
|
+
- `~strikethrough~` for strikethrough
|
|
220
|
+
- `` `code` `` for inline code
|
|
221
|
+
- ` ```code blocks``` ` for multiline code
|
|
222
|
+
- Links are automatically hyperlinked
|
|
223
|
+
- User mentions: `<@USER_ID>`
|
|
224
|
+
- Channel mentions: `<#CHANNEL_ID>`
|
|
225
|
+
|
|
159
226
|
## License
|
|
160
227
|
|
|
161
228
|
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unread.d.ts","sourceRoot":"","sources":["../../src/commands/unread.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"unread.d.ts","sourceRoot":"","sources":["../../src/commands/unread.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiEpC,wBAAgB,kBAAkB,IAAI,OAAO,CA0B5C"}
|
package/dist/commands/unread.js
CHANGED
|
@@ -24,6 +24,10 @@ async function handleSpecificChannelUnread(client, options) {
|
|
|
24
24
|
countOnly: countOnly,
|
|
25
25
|
format: format,
|
|
26
26
|
});
|
|
27
|
+
if ((0, option_parsers_1.parseBoolean)(options.markRead)) {
|
|
28
|
+
await client.markAsRead(result.channel.id);
|
|
29
|
+
console.log(chalk_1.default.green(`✓ Marked messages in #${result.channel.name} as read`));
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
async function handleAllChannelsUnread(client, options) {
|
|
29
33
|
const channels = await client.listUnreadChannels();
|
|
@@ -38,6 +42,13 @@ async function handleAllChannelsUnread(client, options) {
|
|
|
38
42
|
const countOnly = (0, option_parsers_1.parseBoolean)(options.countOnly);
|
|
39
43
|
const formatter = (0, channel_formatters_1.createChannelFormatter)(format, countOnly);
|
|
40
44
|
formatter.format({ channels: displayChannels, countOnly: countOnly });
|
|
45
|
+
if ((0, option_parsers_1.parseBoolean)(options.markRead)) {
|
|
46
|
+
// Mark all unread channels as read
|
|
47
|
+
for (const channel of channels) {
|
|
48
|
+
await client.markAsRead(channel.id);
|
|
49
|
+
}
|
|
50
|
+
console.log(chalk_1.default.green('✓ Marked all messages as read'));
|
|
51
|
+
}
|
|
41
52
|
}
|
|
42
53
|
function setupUnreadCommand() {
|
|
43
54
|
const unreadCommand = new commander_1.Command('unread')
|
|
@@ -46,6 +57,7 @@ function setupUnreadCommand() {
|
|
|
46
57
|
.option('--format <format>', 'Output format: table, simple, json', 'table')
|
|
47
58
|
.option('--count-only', 'Show only unread counts', false)
|
|
48
59
|
.option('--limit <number>', 'Maximum number of channels to display', constants_1.DEFAULTS.UNREAD_DISPLAY_LIMIT.toString())
|
|
60
|
+
.option('--mark-read', 'Mark messages as read after fetching', false)
|
|
49
61
|
.option('--profile <profile>', 'Use specific workspace profile')
|
|
50
62
|
.action((0, command_wrapper_1.wrapCommand)(async (options) => {
|
|
51
63
|
const client = await (0, client_factory_1.createSlackClient)(options.profile);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unread.js","sourceRoot":"","sources":["../../src/commands/unread.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"unread.js","sourceRoot":"","sources":["../../src/commands/unread.ts"],"names":[],"mappings":"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"}
|
package/dist/types/commands.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/types/commands.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,CAAC;IACnD,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/types/commands.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,CAAC;IACnD,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-utils.d.ts","sourceRoot":"","sources":["../../src/utils/format-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format-utils.d.ts","sourceRoot":"","sources":["../../src/utils/format-utils.ts"],"names":[],"mappings":"AAEA,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAM7F"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.formatMessageWithMentions = formatMessageWithMentions;
|
|
4
|
+
const slack_patterns_1 = require("./slack-patterns");
|
|
4
5
|
function formatMessageWithMentions(message, users) {
|
|
5
6
|
// Replace <@USERID> mentions with @username
|
|
6
|
-
return message.replace(
|
|
7
|
+
return message.replace(slack_patterns_1.USER_MENTION_PATTERN, (match, userId) => {
|
|
7
8
|
const username = users.get(userId) || userId;
|
|
8
9
|
return `@${username}`;
|
|
9
10
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-utils.js","sourceRoot":"","sources":["../../src/utils/format-utils.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"format-utils.js","sourceRoot":"","sources":["../../src/utils/format-utils.ts"],"names":[],"mappings":";;AAEA,8DAMC;AARD,qDAAwD;AAExD,SAAgB,yBAAyB,CAAC,OAAe,EAAE,KAA0B;IACnF,4CAA4C;IAC5C,OAAO,OAAO,CAAC,OAAO,CAAC,qCAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAC7D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;QAC7C,OAAO,IAAI,QAAQ,EAAE,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts all user IDs from mentions in a text
|
|
3
|
+
* @param text - The text containing Slack mentions
|
|
4
|
+
* @returns Array of unique user IDs found in mentions
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractUserIdsFromMentions(text: string): string[];
|
|
7
|
+
/**
|
|
8
|
+
* Extracts all unique user IDs from an array of messages
|
|
9
|
+
* Includes both message authors and mentioned users
|
|
10
|
+
* @param messages - Array of messages to extract user IDs from
|
|
11
|
+
* @returns Array of unique user IDs
|
|
12
|
+
*/
|
|
13
|
+
export declare function extractAllUserIds(messages: Array<{
|
|
14
|
+
user?: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
}>): string[];
|
|
17
|
+
//# sourceMappingURL=mention-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-utils.d.ts","sourceRoot":"","sources":["../../src/utils/mention-utils.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAYjE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,EAAE,CAmB7F"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractUserIdsFromMentions = extractUserIdsFromMentions;
|
|
4
|
+
exports.extractAllUserIds = extractAllUserIds;
|
|
5
|
+
const slack_patterns_1 = require("./slack-patterns");
|
|
6
|
+
/**
|
|
7
|
+
* Extracts all user IDs from mentions in a text
|
|
8
|
+
* @param text - The text containing Slack mentions
|
|
9
|
+
* @returns Array of unique user IDs found in mentions
|
|
10
|
+
*/
|
|
11
|
+
function extractUserIdsFromMentions(text) {
|
|
12
|
+
const userIds = [];
|
|
13
|
+
const matches = text.matchAll(slack_patterns_1.USER_MENTION_PATTERN);
|
|
14
|
+
for (const match of matches) {
|
|
15
|
+
const userId = match[1];
|
|
16
|
+
if (userId) {
|
|
17
|
+
userIds.push(userId);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return userIds;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extracts all unique user IDs from an array of messages
|
|
24
|
+
* Includes both message authors and mentioned users
|
|
25
|
+
* @param messages - Array of messages to extract user IDs from
|
|
26
|
+
* @returns Array of unique user IDs
|
|
27
|
+
*/
|
|
28
|
+
function extractAllUserIds(messages) {
|
|
29
|
+
const userIds = new Set();
|
|
30
|
+
for (const message of messages) {
|
|
31
|
+
// Add message author
|
|
32
|
+
if (message.user) {
|
|
33
|
+
userIds.add(message.user);
|
|
34
|
+
}
|
|
35
|
+
// Add mentioned users
|
|
36
|
+
if (message.text) {
|
|
37
|
+
const mentionedIds = extractUserIdsFromMentions(message.text);
|
|
38
|
+
for (const id of mentionedIds) {
|
|
39
|
+
userIds.add(id);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return Array.from(userIds);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=mention-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mention-utils.js","sourceRoot":"","sources":["../../src/utils/mention-utils.ts"],"names":[],"mappings":";;AAOA,gEAYC;AAQD,8CAmBC;AA9CD,qDAAwD;AAExD;;;;GAIG;AACH,SAAgB,0BAA0B,CAAC,IAAY;IACrD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,qCAAoB,CAAC,CAAC;IAEpD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,QAAiD;IACjF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,qBAAqB;QACrB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAED,sBAAsB;QACtB,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,YAAY,GAAG,0BAA0B,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC9D,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -68,6 +68,7 @@ export declare class SlackApiClient {
|
|
|
68
68
|
getHistory(channel: string, options: HistoryOptions): Promise<HistoryResult>;
|
|
69
69
|
listUnreadChannels(): Promise<Channel[]>;
|
|
70
70
|
getChannelUnread(channelNameOrId: string): Promise<ChannelUnreadResult>;
|
|
71
|
+
markAsRead(channelId: string): Promise<void>;
|
|
71
72
|
}
|
|
72
73
|
export declare const slackApiClient: {
|
|
73
74
|
listChannels: (token: string, options: ListChannelsOptions) => Promise<Channel[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-api-client.d.ts","sourceRoot":"","sources":["../../src/utils/slack-api-client.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"slack-api-client.d.ts","sourceRoot":"","sources":["../../src/utils/slack-api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,9 @@ class SlackApiClient {
|
|
|
23
23
|
async getChannelUnread(channelNameOrId) {
|
|
24
24
|
return this.messageOps.getChannelUnread(channelNameOrId);
|
|
25
25
|
}
|
|
26
|
+
async markAsRead(channelId) {
|
|
27
|
+
return this.messageOps.markAsRead(channelId);
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
30
|
exports.SlackApiClient = SlackApiClient;
|
|
28
31
|
exports.slackApiClient = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-api-client.js","sourceRoot":"","sources":["../../src/utils/slack-api-client.ts"],"names":[],"mappings":";;;AACA,8EAA0E;AAC1E,8EAA0E;AAoE1E,MAAa,cAAc;IAIzB,YAAY,KAAa;QACvB,IAAI,CAAC,UAAU,GAAG,IAAI,sCAAiB,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,IAAI,sCAAiB,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,IAAY;QAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAA4B;QAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,OAAuB;QACvD,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,OAAO,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,eAAuB;QAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"slack-api-client.js","sourceRoot":"","sources":["../../src/utils/slack-api-client.ts"],"names":[],"mappings":";;;AACA,8EAA0E;AAC1E,8EAA0E;AAoE1E,MAAa,cAAc;IAIzB,YAAY,KAAa;QACvB,IAAI,CAAC,UAAU,GAAG,IAAI,sCAAiB,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,IAAI,sCAAiB,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,IAAY;QAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAA4B;QAC7C,OAAO,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,OAAuB;QACvD,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,OAAO,IAAI,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,eAAuB;QAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC;CACF;AAhCD,wCAgCC;AAEY,QAAA,cAAc,GAAG;IAC5B,YAAY,EAAE,KAAK,EAAE,KAAa,EAAE,OAA4B,EAAsB,EAAE;QACtF,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;CACF,CAAC"}
|
|
@@ -8,5 +8,6 @@ export declare class MessageOperations extends BaseSlackClient {
|
|
|
8
8
|
getHistory(channel: string, options: HistoryOptions): Promise<HistoryResult>;
|
|
9
9
|
getChannelUnread(channelNameOrId: string): Promise<ChannelUnreadResult>;
|
|
10
10
|
private fetchUserInfo;
|
|
11
|
+
markAsRead(channelId: string): Promise<void>;
|
|
11
12
|
}
|
|
12
13
|
//# sourceMappingURL=message-operations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-operations.d.ts","sourceRoot":"","sources":["../../../src/utils/slack-operations/message-operations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAAW,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"message-operations.d.ts","sourceRoot":"","sources":["../../../src/utils/slack-operations/message-operations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAAW,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAIlG,qBAAa,iBAAkB,SAAQ,eAAe;IACpD,OAAO,CAAC,UAAU,CAAoB;gBAE1B,KAAK,EAAE,MAAM;IAKnB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAO5E,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAyB5E,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;YAsC/D,aAAa;IAoBrB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMnD"}
|
|
@@ -5,6 +5,7 @@ const base_client_1 = require("./base-client");
|
|
|
5
5
|
const channel_resolver_1 = require("../channel-resolver");
|
|
6
6
|
const constants_1 = require("../constants");
|
|
7
7
|
const channel_operations_1 = require("./channel-operations");
|
|
8
|
+
const mention_utils_1 = require("../mention-utils");
|
|
8
9
|
class MessageOperations extends base_client_1.BaseSlackClient {
|
|
9
10
|
constructor(token) {
|
|
10
11
|
super(token);
|
|
@@ -29,8 +30,8 @@ class MessageOperations extends base_client_1.BaseSlackClient {
|
|
|
29
30
|
oldest: options.oldest,
|
|
30
31
|
});
|
|
31
32
|
const messages = response.messages;
|
|
32
|
-
//
|
|
33
|
-
const userIds =
|
|
33
|
+
// Extract all unique user IDs (authors and mentioned users)
|
|
34
|
+
const userIds = (0, mention_utils_1.extractAllUserIds)(messages);
|
|
34
35
|
const users = await this.fetchUserInfo(userIds);
|
|
35
36
|
return { messages, users };
|
|
36
37
|
}
|
|
@@ -87,6 +88,12 @@ class MessageOperations extends base_client_1.BaseSlackClient {
|
|
|
87
88
|
}
|
|
88
89
|
return users;
|
|
89
90
|
}
|
|
91
|
+
async markAsRead(channelId) {
|
|
92
|
+
await this.client.conversations.mark({
|
|
93
|
+
channel: channelId,
|
|
94
|
+
ts: Date.now() / 1000 + '',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
90
97
|
}
|
|
91
98
|
exports.MessageOperations = MessageOperations;
|
|
92
99
|
//# sourceMappingURL=message-operations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-operations.js","sourceRoot":"","sources":["../../../src/utils/slack-operations/message-operations.ts"],"names":[],"mappings":";;;AACA,+CAAgD;AAChD,0DAAsD;AACtD,4CAAwC;AAExC,6DAAyD;
|
|
1
|
+
{"version":3,"file":"message-operations.js","sourceRoot":"","sources":["../../../src/utils/slack-operations/message-operations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-patterns.d.ts","sourceRoot":"","sources":["../../src/utils/slack-patterns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,eAAO,MAAM,oBAAoB,QAAoB,CAAC;AAGtD,eAAO,MAAM,2BAA2B,QAAmB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Common regex patterns for Slack message parsing
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SINGLE_USER_MENTION_PATTERN = exports.USER_MENTION_PATTERN = void 0;
|
|
7
|
+
// Matches Slack user mentions in the format <@USERID>
|
|
8
|
+
exports.USER_MENTION_PATTERN = /<@([A-Z0-9]+)>/g;
|
|
9
|
+
// Matches a single user mention (non-global)
|
|
10
|
+
exports.SINGLE_USER_MENTION_PATTERN = /<@([A-Z0-9]+)>/;
|
|
11
|
+
//# sourceMappingURL=slack-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-patterns.js","sourceRoot":"","sources":["../../src/utils/slack-patterns.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,sDAAsD;AACzC,QAAA,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD,6CAA6C;AAChC,QAAA,2BAA2B,GAAG,gBAAgB,CAAC"}
|
package/package.json
CHANGED
package/src/commands/unread.ts
CHANGED
|
@@ -26,6 +26,11 @@ async function handleSpecificChannelUnread(
|
|
|
26
26
|
countOnly: countOnly,
|
|
27
27
|
format: format,
|
|
28
28
|
});
|
|
29
|
+
|
|
30
|
+
if (parseBoolean(options.markRead)) {
|
|
31
|
+
await client.markAsRead(result.channel.id);
|
|
32
|
+
console.log(chalk.green(`✓ Marked messages in #${result.channel.name} as read`));
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
async function handleAllChannelsUnread(
|
|
@@ -48,6 +53,14 @@ async function handleAllChannelsUnread(
|
|
|
48
53
|
|
|
49
54
|
const formatter = createChannelFormatter(format, countOnly);
|
|
50
55
|
formatter.format({ channels: displayChannels, countOnly: countOnly });
|
|
56
|
+
|
|
57
|
+
if (parseBoolean(options.markRead)) {
|
|
58
|
+
// Mark all unread channels as read
|
|
59
|
+
for (const channel of channels) {
|
|
60
|
+
await client.markAsRead(channel.id);
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk.green('✓ Marked all messages as read'));
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
export function setupUnreadCommand(): Command {
|
|
@@ -61,6 +74,7 @@ export function setupUnreadCommand(): Command {
|
|
|
61
74
|
'Maximum number of channels to display',
|
|
62
75
|
DEFAULTS.UNREAD_DISPLAY_LIMIT.toString()
|
|
63
76
|
)
|
|
77
|
+
.option('--mark-read', 'Mark messages as read after fetching', false)
|
|
64
78
|
.option('--profile <profile>', 'Use specific workspace profile')
|
|
65
79
|
.action(
|
|
66
80
|
wrapCommand(async (options: UnreadOptions) => {
|
package/src/types/commands.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { USER_MENTION_PATTERN } from './slack-patterns';
|
|
2
|
+
|
|
1
3
|
export function formatMessageWithMentions(message: string, users: Map<string, string>): string {
|
|
2
4
|
// Replace <@USERID> mentions with @username
|
|
3
|
-
return message.replace(
|
|
5
|
+
return message.replace(USER_MENTION_PATTERN, (match, userId) => {
|
|
4
6
|
const username = users.get(userId) || userId;
|
|
5
7
|
return `@${username}`;
|
|
6
8
|
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { USER_MENTION_PATTERN } from './slack-patterns';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts all user IDs from mentions in a text
|
|
5
|
+
* @param text - The text containing Slack mentions
|
|
6
|
+
* @returns Array of unique user IDs found in mentions
|
|
7
|
+
*/
|
|
8
|
+
export function extractUserIdsFromMentions(text: string): string[] {
|
|
9
|
+
const userIds: string[] = [];
|
|
10
|
+
const matches = text.matchAll(USER_MENTION_PATTERN);
|
|
11
|
+
|
|
12
|
+
for (const match of matches) {
|
|
13
|
+
const userId = match[1];
|
|
14
|
+
if (userId) {
|
|
15
|
+
userIds.push(userId);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return userIds;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Extracts all unique user IDs from an array of messages
|
|
24
|
+
* Includes both message authors and mentioned users
|
|
25
|
+
* @param messages - Array of messages to extract user IDs from
|
|
26
|
+
* @returns Array of unique user IDs
|
|
27
|
+
*/
|
|
28
|
+
export function extractAllUserIds(messages: Array<{ user?: string; text?: string }>): string[] {
|
|
29
|
+
const userIds = new Set<string>();
|
|
30
|
+
|
|
31
|
+
for (const message of messages) {
|
|
32
|
+
// Add message author
|
|
33
|
+
if (message.user) {
|
|
34
|
+
userIds.add(message.user);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Add mentioned users
|
|
38
|
+
if (message.text) {
|
|
39
|
+
const mentionedIds = extractUserIdsFromMentions(message.text);
|
|
40
|
+
for (const id of mentionedIds) {
|
|
41
|
+
userIds.add(id);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Array.from(userIds);
|
|
47
|
+
}
|
|
@@ -96,6 +96,10 @@ export class SlackApiClient {
|
|
|
96
96
|
async getChannelUnread(channelNameOrId: string): Promise<ChannelUnreadResult> {
|
|
97
97
|
return this.messageOps.getChannelUnread(channelNameOrId);
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
async markAsRead(channelId: string): Promise<void> {
|
|
101
|
+
return this.messageOps.markAsRead(channelId);
|
|
102
|
+
}
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
export const slackApiClient = {
|
|
@@ -4,6 +4,7 @@ import { channelResolver } from '../channel-resolver';
|
|
|
4
4
|
import { DEFAULTS } from '../constants';
|
|
5
5
|
import { Message, HistoryOptions, HistoryResult, ChannelUnreadResult } from '../slack-api-client';
|
|
6
6
|
import { ChannelOperations } from './channel-operations';
|
|
7
|
+
import { extractAllUserIds } from '../mention-utils';
|
|
7
8
|
|
|
8
9
|
export class MessageOperations extends BaseSlackClient {
|
|
9
10
|
private channelOps: ChannelOperations;
|
|
@@ -38,8 +39,8 @@ export class MessageOperations extends BaseSlackClient {
|
|
|
38
39
|
|
|
39
40
|
const messages = response.messages as Message[];
|
|
40
41
|
|
|
41
|
-
//
|
|
42
|
-
const userIds =
|
|
42
|
+
// Extract all unique user IDs (authors and mentioned users)
|
|
43
|
+
const userIds = extractAllUserIds(messages);
|
|
43
44
|
const users = await this.fetchUserInfo(userIds);
|
|
44
45
|
|
|
45
46
|
return { messages, users };
|
|
@@ -102,4 +103,11 @@ export class MessageOperations extends BaseSlackClient {
|
|
|
102
103
|
|
|
103
104
|
return users;
|
|
104
105
|
}
|
|
106
|
+
|
|
107
|
+
async markAsRead(channelId: string): Promise<void> {
|
|
108
|
+
await this.client.conversations.mark({
|
|
109
|
+
channel: channelId,
|
|
110
|
+
ts: Date.now() / 1000 + '',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
105
113
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common regex patterns for Slack message parsing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Matches Slack user mentions in the format <@USERID>
|
|
6
|
+
export const USER_MENTION_PATTERN = /<@([A-Z0-9]+)>/g;
|
|
7
|
+
|
|
8
|
+
// Matches a single user mention (non-global)
|
|
9
|
+
export const SINGLE_USER_MENTION_PATTERN = /<@([A-Z0-9]+)>/;
|
|
@@ -429,4 +429,64 @@ describe('unread command', () => {
|
|
|
429
429
|
expect(mockConsole.logSpy).toHaveBeenCalledWith(expect.stringContaining('transitioned ES-4359'));
|
|
430
430
|
});
|
|
431
431
|
});
|
|
432
|
+
|
|
433
|
+
describe('mark-read functionality', () => {
|
|
434
|
+
it('should mark messages as read when --mark-read is specified', async () => {
|
|
435
|
+
vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
|
|
436
|
+
token: 'test-token',
|
|
437
|
+
updatedAt: new Date().toISOString()
|
|
438
|
+
});
|
|
439
|
+
vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
|
|
440
|
+
mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
|
|
441
|
+
);
|
|
442
|
+
vi.mocked(mockSlackClient.markAsRead).mockResolvedValue(undefined);
|
|
443
|
+
|
|
444
|
+
await program.parseAsync(['node', 'slack-cli', 'unread', '--mark-read']);
|
|
445
|
+
|
|
446
|
+
expect(mockSlackClient.listUnreadChannels).toHaveBeenCalled();
|
|
447
|
+
expect(mockSlackClient.markAsRead).toHaveBeenCalledWith('C123');
|
|
448
|
+
expect(mockSlackClient.markAsRead).toHaveBeenCalledWith('C456');
|
|
449
|
+
expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.green('✓ Marked all messages as read'));
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('should mark messages as read for specific channel when --channel and --mark-read are specified', async () => {
|
|
453
|
+
vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
|
|
454
|
+
token: 'test-token',
|
|
455
|
+
updatedAt: new Date().toISOString()
|
|
456
|
+
});
|
|
457
|
+
vi.mocked(mockSlackClient.getChannelUnread).mockResolvedValue({
|
|
458
|
+
channel: mockChannelsWithUnread[0],
|
|
459
|
+
messages: mockUnreadMessages,
|
|
460
|
+
users: new Map([
|
|
461
|
+
['U123', 'john.doe'],
|
|
462
|
+
['U456', 'jane.smith']
|
|
463
|
+
])
|
|
464
|
+
});
|
|
465
|
+
vi.mocked(mockSlackClient.markAsRead).mockResolvedValue(undefined);
|
|
466
|
+
|
|
467
|
+
await program.parseAsync(['node', 'slack-cli', 'unread', '--channel', 'general', '--mark-read']);
|
|
468
|
+
|
|
469
|
+
expect(mockSlackClient.getChannelUnread).toHaveBeenCalledWith('general');
|
|
470
|
+
expect(mockSlackClient.markAsRead).toHaveBeenCalledWith('C123');
|
|
471
|
+
expect(mockConsole.logSpy).toHaveBeenCalledWith(chalk.green('✓ Marked messages in #general as read'));
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('should handle mark as read errors gracefully', async () => {
|
|
475
|
+
vi.mocked(mockConfigManager.getConfig).mockResolvedValue({
|
|
476
|
+
token: 'test-token',
|
|
477
|
+
updatedAt: new Date().toISOString()
|
|
478
|
+
});
|
|
479
|
+
vi.mocked(mockSlackClient.listUnreadChannels).mockResolvedValue(
|
|
480
|
+
mockChannelsWithUnread.filter(ch => (ch.unread_count_display || 0) > 0)
|
|
481
|
+
);
|
|
482
|
+
vi.mocked(mockSlackClient.markAsRead).mockRejectedValue(
|
|
483
|
+
new Error('channel_not_found')
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
await program.parseAsync(['node', 'slack-cli', 'unread', '--mark-read']);
|
|
487
|
+
|
|
488
|
+
expect(mockConsole.errorSpy).toHaveBeenCalledWith(expect.stringContaining('Error:'), expect.any(String));
|
|
489
|
+
expect(mockConsole.exitSpy).toHaveBeenCalledWith(1);
|
|
490
|
+
});
|
|
491
|
+
});
|
|
432
492
|
});
|
package/tests/index.test.ts
CHANGED
|
@@ -11,8 +11,8 @@ describe('slack-cli version', () => {
|
|
|
11
11
|
);
|
|
12
12
|
const expectedVersion = packageJson.version;
|
|
13
13
|
|
|
14
|
-
// Execute the CLI command to get version
|
|
15
|
-
const output = execSync('node
|
|
14
|
+
// Execute the CLI command to get version using ts-node
|
|
15
|
+
const output = execSync('npx ts-node src/index.ts --version', {
|
|
16
16
|
encoding: 'utf-8',
|
|
17
17
|
cwd: join(__dirname, '..')
|
|
18
18
|
}).trim();
|
|
@@ -28,8 +28,8 @@ describe('slack-cli version', () => {
|
|
|
28
28
|
);
|
|
29
29
|
const expectedVersion = packageJson.version;
|
|
30
30
|
|
|
31
|
-
// Execute the CLI command with -V flag
|
|
32
|
-
const output = execSync('node
|
|
31
|
+
// Execute the CLI command with -V flag using ts-node
|
|
32
|
+
const output = execSync('npx ts-node src/index.ts -V', {
|
|
33
33
|
encoding: 'utf-8',
|
|
34
34
|
cwd: join(__dirname, '..')
|
|
35
35
|
}).trim();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { extractUserIdsFromMentions, extractAllUserIds } from '../../src/utils/mention-utils';
|
|
3
|
+
|
|
4
|
+
describe('mention-utils', () => {
|
|
5
|
+
describe('extractUserIdsFromMentions', () => {
|
|
6
|
+
it('should extract single user ID from mention', () => {
|
|
7
|
+
const text = 'Hello <@U123456789>';
|
|
8
|
+
const userIds = extractUserIdsFromMentions(text);
|
|
9
|
+
expect(userIds).toEqual(['U123456789']);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should extract multiple user IDs from mentions', () => {
|
|
13
|
+
const text = 'Hey <@U123456789> and <@U987654321>, please check this';
|
|
14
|
+
const userIds = extractUserIdsFromMentions(text);
|
|
15
|
+
expect(userIds).toEqual(['U123456789', 'U987654321']);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should handle duplicate mentions', () => {
|
|
19
|
+
const text = '<@U123456789> mentioned <@U123456789> again';
|
|
20
|
+
const userIds = extractUserIdsFromMentions(text);
|
|
21
|
+
expect(userIds).toEqual(['U123456789', 'U123456789']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should return empty array for text without mentions', () => {
|
|
25
|
+
const text = 'No mentions here';
|
|
26
|
+
const userIds = extractUserIdsFromMentions(text);
|
|
27
|
+
expect(userIds).toEqual([]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle empty text', () => {
|
|
31
|
+
const userIds = extractUserIdsFromMentions('');
|
|
32
|
+
expect(userIds).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should ignore malformed mentions', () => {
|
|
36
|
+
const text = 'Invalid <@> mention and <@lowercase> mention';
|
|
37
|
+
const userIds = extractUserIdsFromMentions(text);
|
|
38
|
+
expect(userIds).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('extractAllUserIds', () => {
|
|
43
|
+
it('should extract user IDs from message authors only', () => {
|
|
44
|
+
const messages = [
|
|
45
|
+
{ user: 'U111111111', text: 'Hello world' },
|
|
46
|
+
{ user: 'U222222222', text: 'Hi there' },
|
|
47
|
+
];
|
|
48
|
+
const userIds = extractAllUserIds(messages);
|
|
49
|
+
expect(userIds).toEqual(['U111111111', 'U222222222']);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should extract user IDs from mentions only', () => {
|
|
53
|
+
const messages = [
|
|
54
|
+
{ text: 'Hello <@U333333333>' },
|
|
55
|
+
{ text: 'Hi <@U444444444>' },
|
|
56
|
+
];
|
|
57
|
+
const userIds = extractAllUserIds(messages);
|
|
58
|
+
expect(userIds).toEqual(['U333333333', 'U444444444']);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should extract both authors and mentioned users', () => {
|
|
62
|
+
const messages = [
|
|
63
|
+
{ user: 'U111111111', text: 'Hello <@U222222222>' },
|
|
64
|
+
{ user: 'U333333333', text: 'Hi <@U444444444> and <@U555555555>' },
|
|
65
|
+
];
|
|
66
|
+
const userIds = extractAllUserIds(messages);
|
|
67
|
+
expect(userIds.sort()).toEqual([
|
|
68
|
+
'U111111111',
|
|
69
|
+
'U222222222',
|
|
70
|
+
'U333333333',
|
|
71
|
+
'U444444444',
|
|
72
|
+
'U555555555',
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should remove duplicate user IDs', () => {
|
|
77
|
+
const messages = [
|
|
78
|
+
{ user: 'U111111111', text: 'Hello <@U111111111>' },
|
|
79
|
+
{ user: 'U111111111', text: 'Another message' },
|
|
80
|
+
];
|
|
81
|
+
const userIds = extractAllUserIds(messages);
|
|
82
|
+
expect(userIds).toEqual(['U111111111']);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should handle messages without user or text', () => {
|
|
86
|
+
const messages = [
|
|
87
|
+
{ user: 'U111111111' },
|
|
88
|
+
{ text: 'No user here' },
|
|
89
|
+
{},
|
|
90
|
+
];
|
|
91
|
+
const userIds = extractAllUserIds(messages);
|
|
92
|
+
expect(userIds).toEqual(['U111111111']);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should handle empty messages array', () => {
|
|
96
|
+
const userIds = extractAllUserIds([]);
|
|
97
|
+
expect(userIds).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { beforeEach, describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { MessageOperations } from '../../../src/utils/slack-operations/message-operations';
|
|
3
|
+
import { channelResolver } from '../../../src/utils/channel-resolver';
|
|
4
|
+
|
|
5
|
+
vi.mock('@slack/web-api', () => ({
|
|
6
|
+
WebClient: vi.fn().mockImplementation(() => ({
|
|
7
|
+
conversations: {
|
|
8
|
+
history: vi.fn(),
|
|
9
|
+
},
|
|
10
|
+
users: {
|
|
11
|
+
info: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
chat: {
|
|
14
|
+
postMessage: vi.fn(),
|
|
15
|
+
},
|
|
16
|
+
})),
|
|
17
|
+
LogLevel: {
|
|
18
|
+
ERROR: 'error',
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock('../../../src/utils/channel-resolver');
|
|
23
|
+
|
|
24
|
+
describe('MessageOperations', () => {
|
|
25
|
+
let messageOps: MessageOperations;
|
|
26
|
+
let mockClient: any;
|
|
27
|
+
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
vi.clearAllMocks();
|
|
30
|
+
messageOps = new MessageOperations('test-token');
|
|
31
|
+
mockClient = (messageOps as any).client;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('getHistory with mentions', () => {
|
|
35
|
+
it('should fetch user info for mentioned users in message text', async () => {
|
|
36
|
+
const mockMessages = [
|
|
37
|
+
{
|
|
38
|
+
type: 'message',
|
|
39
|
+
text: 'Hello <@U123456789> can you check this?',
|
|
40
|
+
user: 'U987654321',
|
|
41
|
+
ts: '1234567890.123456',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
type: 'message',
|
|
45
|
+
text: '<@U111111111> and <@U222222222> please review',
|
|
46
|
+
user: 'U333333333',
|
|
47
|
+
ts: '1234567891.123456',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const mockUsersInfo = {
|
|
52
|
+
U123456789: { ok: true, user: { name: 'john.doe' } },
|
|
53
|
+
U987654321: { ok: true, user: { name: 'jane.smith' } },
|
|
54
|
+
U111111111: { ok: true, user: { name: 'alice.brown' } },
|
|
55
|
+
U222222222: { ok: true, user: { name: 'bob.wilson' } },
|
|
56
|
+
U333333333: { ok: true, user: { name: 'charlie.davis' } },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
mockClient.conversations.history.mockResolvedValue({
|
|
60
|
+
ok: true,
|
|
61
|
+
messages: mockMessages,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
mockClient.users.info.mockImplementation(({ user }: { user: string }) => {
|
|
65
|
+
return Promise.resolve(mockUsersInfo[user] || { ok: false });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
vi.mocked(channelResolver.resolveChannelId).mockResolvedValue('C123456789');
|
|
69
|
+
|
|
70
|
+
const result = await messageOps.getHistory('test-channel', { limit: 10 });
|
|
71
|
+
|
|
72
|
+
// Verify all user IDs were fetched (both message authors and mentioned users)
|
|
73
|
+
expect(mockClient.users.info).toHaveBeenCalledTimes(5);
|
|
74
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U987654321' });
|
|
75
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U333333333' });
|
|
76
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U123456789' });
|
|
77
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U111111111' });
|
|
78
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U222222222' });
|
|
79
|
+
|
|
80
|
+
// Verify the returned users map contains all users
|
|
81
|
+
expect(result.users.get('U123456789')).toBe('john.doe');
|
|
82
|
+
expect(result.users.get('U987654321')).toBe('jane.smith');
|
|
83
|
+
expect(result.users.get('U111111111')).toBe('alice.brown');
|
|
84
|
+
expect(result.users.get('U222222222')).toBe('bob.wilson');
|
|
85
|
+
expect(result.users.get('U333333333')).toBe('charlie.davis');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should handle messages with own mentions correctly', async () => {
|
|
89
|
+
const mockMessages = [
|
|
90
|
+
{
|
|
91
|
+
type: 'message',
|
|
92
|
+
text: '<@U07L5D50RAL> please check this task',
|
|
93
|
+
user: 'U123456789',
|
|
94
|
+
ts: '1234567890.123456',
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const mockUsersInfo = {
|
|
99
|
+
U123456789: { ok: true, user: { name: 'john.doe' } },
|
|
100
|
+
U07L5D50RAL: { ok: true, user: { name: 'koguchi_s' } },
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
mockClient.conversations.history.mockResolvedValue({
|
|
104
|
+
ok: true,
|
|
105
|
+
messages: mockMessages,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
mockClient.users.info.mockImplementation(({ user }: { user: string }) => {
|
|
109
|
+
return Promise.resolve(mockUsersInfo[user] || { ok: false });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
vi.mocked(channelResolver.resolveChannelId).mockResolvedValue('C123456789');
|
|
113
|
+
|
|
114
|
+
const result = await messageOps.getHistory('test-channel', { limit: 10 });
|
|
115
|
+
|
|
116
|
+
// Verify both users were fetched
|
|
117
|
+
expect(mockClient.users.info).toHaveBeenCalledTimes(2);
|
|
118
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U123456789' });
|
|
119
|
+
expect(mockClient.users.info).toHaveBeenCalledWith({ user: 'U07L5D50RAL' });
|
|
120
|
+
|
|
121
|
+
// Verify the returned users map contains both users
|
|
122
|
+
expect(result.users.get('U123456789')).toBe('john.doe');
|
|
123
|
+
expect(result.users.get('U07L5D50RAL')).toBe('koguchi_s');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|