@urugus/slack-cli 0.2.9 → 0.2.11
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 +60 -14
- package/README.md +71 -28
- package/dist/commands/scheduled.d.ts +3 -0
- package/dist/commands/scheduled.d.ts.map +1 -0
- package/dist/commands/scheduled.js +55 -0
- package/dist/commands/scheduled.js.map +1 -0
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +16 -2
- package/dist/commands/send.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/types/commands.d.ts +8 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/utils/channel-resolver.d.ts.map +1 -1
- package/dist/utils/channel-resolver.js +1 -3
- package/dist/utils/channel-resolver.js.map +1 -1
- package/dist/utils/config.d.ts +10 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +94 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/constants.d.ts +5 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/formatters/output-formatter.d.ts +7 -0
- package/dist/utils/formatters/output-formatter.d.ts.map +1 -0
- package/dist/utils/formatters/output-formatter.js +7 -0
- package/dist/utils/formatters/output-formatter.js.map +1 -0
- package/dist/utils/profile-config-refactored.d.ts +20 -0
- package/dist/utils/profile-config-refactored.d.ts.map +1 -0
- package/dist/utils/profile-config-refactored.js +174 -0
- package/dist/utils/profile-config-refactored.js.map +1 -0
- package/dist/utils/schedule-utils.d.ts +3 -0
- package/dist/utils/schedule-utils.d.ts.map +1 -0
- package/dist/utils/schedule-utils.js +34 -0
- package/dist/utils/schedule-utils.js.map +1 -0
- package/dist/utils/slack-api-client.d.ts +10 -1
- package/dist/utils/slack-api-client.d.ts.map +1 -1
- package/dist/utils/slack-api-client.js +6 -0
- package/dist/utils/slack-api-client.js.map +1 -1
- package/dist/utils/slack-operations/message-operations.d.ts +4 -2
- package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
- package/dist/utils/slack-operations/message-operations.js +28 -0
- package/dist/utils/slack-operations/message-operations.js.map +1 -1
- package/dist/utils/validators.d.ts +4 -0
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js +31 -0
- package/dist/utils/validators.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/scheduled.ts +71 -0
- package/src/commands/send.ts +21 -3
- package/src/index.ts +2 -0
- package/src/types/commands.ts +9 -0
- package/src/utils/channel-resolver.ts +1 -5
- package/src/utils/constants.ts +7 -0
- package/src/utils/schedule-utils.ts +41 -0
- package/src/utils/slack-api-client.ts +22 -1
- package/src/utils/slack-operations/message-operations.ts +55 -2
- package/src/utils/validators.ts +38 -0
- package/tests/commands/scheduled.test.ts +131 -0
- package/tests/commands/send.test.ts +235 -44
- package/tests/utils/channel-resolver.test.ts +25 -21
- package/tests/utils/schedule-utils.test.ts +63 -0
- package/tests/utils/slack-api-client.test.ts +81 -46
- package/tests/utils/slack-operations/message-operations.test.ts +38 -1
|
@@ -12,15 +12,19 @@ describe('SlackApiClient', () => {
|
|
|
12
12
|
vi.clearAllMocks();
|
|
13
13
|
mockWebClient = {
|
|
14
14
|
chat: {
|
|
15
|
-
postMessage: vi.fn()
|
|
15
|
+
postMessage: vi.fn(),
|
|
16
|
+
scheduleMessage: vi.fn(),
|
|
17
|
+
scheduledMessages: {
|
|
18
|
+
list: vi.fn(),
|
|
19
|
+
},
|
|
16
20
|
},
|
|
17
21
|
conversations: {
|
|
18
22
|
list: vi.fn(),
|
|
19
|
-
info: vi.fn()
|
|
23
|
+
info: vi.fn(),
|
|
20
24
|
},
|
|
21
25
|
users: {
|
|
22
|
-
conversations: vi.fn()
|
|
23
|
-
}
|
|
26
|
+
conversations: vi.fn(),
|
|
27
|
+
},
|
|
24
28
|
};
|
|
25
29
|
vi.mocked(WebClient).mockReturnValue(mockWebClient);
|
|
26
30
|
client = new SlackApiClient('test-token');
|
|
@@ -46,7 +50,7 @@ describe('SlackApiClient', () => {
|
|
|
46
50
|
|
|
47
51
|
expect(mockWebClient.chat.postMessage).toHaveBeenCalledWith({
|
|
48
52
|
channel: 'general',
|
|
49
|
-
text: 'Hello, World!'
|
|
53
|
+
text: 'Hello, World!',
|
|
50
54
|
});
|
|
51
55
|
expect(result).toEqual(mockResponse);
|
|
52
56
|
});
|
|
@@ -59,7 +63,7 @@ describe('SlackApiClient', () => {
|
|
|
59
63
|
|
|
60
64
|
expect(mockWebClient.chat.postMessage).toHaveBeenCalledWith({
|
|
61
65
|
channel: 'C1234567890',
|
|
62
|
-
text: 'Hello!'
|
|
66
|
+
text: 'Hello!',
|
|
63
67
|
});
|
|
64
68
|
});
|
|
65
69
|
|
|
@@ -72,7 +76,7 @@ describe('SlackApiClient', () => {
|
|
|
72
76
|
|
|
73
77
|
expect(mockWebClient.chat.postMessage).toHaveBeenCalledWith({
|
|
74
78
|
channel: 'general',
|
|
75
|
-
text: multiLineMessage
|
|
79
|
+
text: multiLineMessage,
|
|
76
80
|
});
|
|
77
81
|
});
|
|
78
82
|
|
|
@@ -84,6 +88,39 @@ describe('SlackApiClient', () => {
|
|
|
84
88
|
});
|
|
85
89
|
});
|
|
86
90
|
|
|
91
|
+
describe('scheduleMessage', () => {
|
|
92
|
+
it('should schedule message to channel', async () => {
|
|
93
|
+
const mockResponse = { ok: true, scheduled_message_id: 'Q123', post_at: 1770855000 };
|
|
94
|
+
vi.mocked(mockWebClient.chat.scheduleMessage).mockResolvedValue(mockResponse as any);
|
|
95
|
+
|
|
96
|
+
const result = await client.scheduleMessage('general', 'Hello, future!', 1770855000);
|
|
97
|
+
|
|
98
|
+
expect(mockWebClient.chat.scheduleMessage).toHaveBeenCalledWith({
|
|
99
|
+
channel: 'general',
|
|
100
|
+
text: 'Hello, future!',
|
|
101
|
+
post_at: 1770855000,
|
|
102
|
+
});
|
|
103
|
+
expect(result).toEqual(mockResponse);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('listScheduledMessages', () => {
|
|
108
|
+
it('should list scheduled messages', async () => {
|
|
109
|
+
const mockResponse = {
|
|
110
|
+
ok: true,
|
|
111
|
+
scheduled_messages: [
|
|
112
|
+
{ id: 'Q123', channel_id: 'C123', post_at: 1770855000, date_created: 1770854400 },
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
vi.mocked(mockWebClient.chat.scheduledMessages.list).mockResolvedValue(mockResponse as any);
|
|
116
|
+
|
|
117
|
+
const result = await client.listScheduledMessages();
|
|
118
|
+
|
|
119
|
+
expect(mockWebClient.chat.scheduledMessages.list).toHaveBeenCalledWith({ limit: 50 });
|
|
120
|
+
expect(result).toEqual(mockResponse.scheduled_messages);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
87
124
|
describe('listChannels', () => {
|
|
88
125
|
it('should list channels with default options', async () => {
|
|
89
126
|
const mockChannels = [
|
|
@@ -94,24 +131,24 @@ describe('SlackApiClient', () => {
|
|
|
94
131
|
is_private: false,
|
|
95
132
|
num_members: 150,
|
|
96
133
|
created: 1579075200,
|
|
97
|
-
purpose: { value: 'Company announcements' }
|
|
98
|
-
}
|
|
134
|
+
purpose: { value: 'Company announcements' },
|
|
135
|
+
},
|
|
99
136
|
];
|
|
100
137
|
vi.mocked(mockWebClient.conversations.list).mockResolvedValue({
|
|
101
138
|
ok: true,
|
|
102
|
-
channels: mockChannels
|
|
139
|
+
channels: mockChannels,
|
|
103
140
|
} as any);
|
|
104
141
|
|
|
105
142
|
const result = await client.listChannels({
|
|
106
143
|
types: 'public_channel',
|
|
107
144
|
exclude_archived: true,
|
|
108
|
-
limit: 100
|
|
145
|
+
limit: 100,
|
|
109
146
|
});
|
|
110
147
|
|
|
111
148
|
expect(mockWebClient.conversations.list).toHaveBeenCalledWith({
|
|
112
149
|
types: 'public_channel',
|
|
113
150
|
exclude_archived: true,
|
|
114
|
-
limit: 100
|
|
151
|
+
limit: 100,
|
|
115
152
|
});
|
|
116
153
|
expect(result).toEqual(mockChannels);
|
|
117
154
|
});
|
|
@@ -125,24 +162,24 @@ describe('SlackApiClient', () => {
|
|
|
125
162
|
is_private: true,
|
|
126
163
|
num_members: 10,
|
|
127
164
|
created: 1579075200,
|
|
128
|
-
purpose: { value: 'Private discussions' }
|
|
129
|
-
}
|
|
165
|
+
purpose: { value: 'Private discussions' },
|
|
166
|
+
},
|
|
130
167
|
];
|
|
131
168
|
vi.mocked(mockWebClient.conversations.list).mockResolvedValue({
|
|
132
169
|
ok: true,
|
|
133
|
-
channels: mockChannels
|
|
170
|
+
channels: mockChannels,
|
|
134
171
|
} as any);
|
|
135
172
|
|
|
136
173
|
const result = await client.listChannels({
|
|
137
174
|
types: 'private_channel',
|
|
138
175
|
exclude_archived: true,
|
|
139
|
-
limit: 50
|
|
176
|
+
limit: 50,
|
|
140
177
|
});
|
|
141
178
|
|
|
142
179
|
expect(mockWebClient.conversations.list).toHaveBeenCalledWith({
|
|
143
180
|
types: 'private_channel',
|
|
144
181
|
exclude_archived: true,
|
|
145
|
-
limit: 50
|
|
182
|
+
limit: 50,
|
|
146
183
|
});
|
|
147
184
|
expect(result).toEqual(mockChannels);
|
|
148
185
|
});
|
|
@@ -150,30 +187,32 @@ describe('SlackApiClient', () => {
|
|
|
150
187
|
it('should handle multiple channel types', async () => {
|
|
151
188
|
vi.mocked(mockWebClient.conversations.list).mockResolvedValue({
|
|
152
189
|
ok: true,
|
|
153
|
-
channels: []
|
|
190
|
+
channels: [],
|
|
154
191
|
} as any);
|
|
155
192
|
|
|
156
193
|
await client.listChannels({
|
|
157
194
|
types: 'public_channel,private_channel,im',
|
|
158
195
|
exclude_archived: false,
|
|
159
|
-
limit: 200
|
|
196
|
+
limit: 200,
|
|
160
197
|
});
|
|
161
198
|
|
|
162
199
|
expect(mockWebClient.conversations.list).toHaveBeenCalledWith({
|
|
163
200
|
types: 'public_channel,private_channel,im',
|
|
164
201
|
exclude_archived: false,
|
|
165
|
-
limit: 200
|
|
202
|
+
limit: 200,
|
|
166
203
|
});
|
|
167
204
|
});
|
|
168
205
|
|
|
169
206
|
it('should handle API errors', async () => {
|
|
170
207
|
vi.mocked(mockWebClient.conversations.list).mockRejectedValue(new Error('invalid_auth'));
|
|
171
208
|
|
|
172
|
-
await expect(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
209
|
+
await expect(
|
|
210
|
+
client.listChannels({
|
|
211
|
+
types: 'public_channel',
|
|
212
|
+
exclude_archived: true,
|
|
213
|
+
limit: 100,
|
|
214
|
+
})
|
|
215
|
+
).rejects.toThrow('invalid_auth');
|
|
177
216
|
});
|
|
178
217
|
|
|
179
218
|
it('should handle pagination when listing channels', async () => {
|
|
@@ -182,11 +221,11 @@ describe('SlackApiClient', () => {
|
|
|
182
221
|
ok: true,
|
|
183
222
|
channels: [
|
|
184
223
|
{ id: 'C001', name: 'channel1', is_private: false },
|
|
185
|
-
{ id: 'C002', name: 'channel2', is_private: false }
|
|
224
|
+
{ id: 'C002', name: 'channel2', is_private: false },
|
|
186
225
|
],
|
|
187
226
|
response_metadata: {
|
|
188
|
-
next_cursor: 'cursor123'
|
|
189
|
-
}
|
|
227
|
+
next_cursor: 'cursor123',
|
|
228
|
+
},
|
|
190
229
|
} as any);
|
|
191
230
|
|
|
192
231
|
// Second page
|
|
@@ -194,28 +233,26 @@ describe('SlackApiClient', () => {
|
|
|
194
233
|
ok: true,
|
|
195
234
|
channels: [
|
|
196
235
|
{ id: 'C003', name: 'channel3', is_private: false },
|
|
197
|
-
{ id: 'C004', name: 'channel4', is_private: false }
|
|
236
|
+
{ id: 'C004', name: 'channel4', is_private: false },
|
|
198
237
|
],
|
|
199
238
|
response_metadata: {
|
|
200
|
-
next_cursor: 'cursor456'
|
|
201
|
-
}
|
|
239
|
+
next_cursor: 'cursor456',
|
|
240
|
+
},
|
|
202
241
|
} as any);
|
|
203
242
|
|
|
204
243
|
// Third page (last page)
|
|
205
244
|
vi.mocked(mockWebClient.conversations.list).mockResolvedValueOnce({
|
|
206
245
|
ok: true,
|
|
207
|
-
channels: [
|
|
208
|
-
{ id: 'C005', name: 'channel5', is_private: false }
|
|
209
|
-
],
|
|
246
|
+
channels: [{ id: 'C005', name: 'channel5', is_private: false }],
|
|
210
247
|
response_metadata: {
|
|
211
|
-
next_cursor: ''
|
|
212
|
-
}
|
|
248
|
+
next_cursor: '',
|
|
249
|
+
},
|
|
213
250
|
} as any);
|
|
214
251
|
|
|
215
252
|
const result = await client.listChannels({
|
|
216
253
|
types: 'public_channel',
|
|
217
254
|
exclude_archived: true,
|
|
218
|
-
limit: 2
|
|
255
|
+
limit: 2,
|
|
219
256
|
});
|
|
220
257
|
|
|
221
258
|
// Should have called the API 3 times
|
|
@@ -226,7 +263,7 @@ describe('SlackApiClient', () => {
|
|
|
226
263
|
types: 'public_channel',
|
|
227
264
|
exclude_archived: true,
|
|
228
265
|
limit: 2,
|
|
229
|
-
cursor: undefined
|
|
266
|
+
cursor: undefined,
|
|
230
267
|
});
|
|
231
268
|
|
|
232
269
|
// Second call
|
|
@@ -234,7 +271,7 @@ describe('SlackApiClient', () => {
|
|
|
234
271
|
types: 'public_channel',
|
|
235
272
|
exclude_archived: true,
|
|
236
273
|
limit: 2,
|
|
237
|
-
cursor: 'cursor123'
|
|
274
|
+
cursor: 'cursor123',
|
|
238
275
|
});
|
|
239
276
|
|
|
240
277
|
// Third call
|
|
@@ -242,7 +279,7 @@ describe('SlackApiClient', () => {
|
|
|
242
279
|
types: 'public_channel',
|
|
243
280
|
exclude_archived: true,
|
|
244
281
|
limit: 2,
|
|
245
|
-
cursor: 'cursor456'
|
|
282
|
+
cursor: 'cursor456',
|
|
246
283
|
});
|
|
247
284
|
|
|
248
285
|
// Should return all channels
|
|
@@ -257,22 +294,20 @@ describe('SlackApiClient', () => {
|
|
|
257
294
|
it('should handle empty cursor in pagination', async () => {
|
|
258
295
|
vi.mocked(mockWebClient.conversations.list).mockResolvedValueOnce({
|
|
259
296
|
ok: true,
|
|
260
|
-
channels: [
|
|
261
|
-
{ id: 'C001', name: 'channel1', is_private: false }
|
|
262
|
-
],
|
|
297
|
+
channels: [{ id: 'C001', name: 'channel1', is_private: false }],
|
|
263
298
|
response_metadata: {
|
|
264
299
|
// No next_cursor field
|
|
265
|
-
}
|
|
300
|
+
},
|
|
266
301
|
} as any);
|
|
267
302
|
|
|
268
303
|
const result = await client.listChannels({
|
|
269
304
|
types: 'public_channel',
|
|
270
305
|
exclude_archived: true,
|
|
271
|
-
limit: 100
|
|
306
|
+
limit: 100,
|
|
272
307
|
});
|
|
273
308
|
|
|
274
309
|
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(1);
|
|
275
310
|
expect(result).toHaveLength(1);
|
|
276
311
|
});
|
|
277
312
|
});
|
|
278
|
-
});
|
|
313
|
+
});
|
|
@@ -12,6 +12,10 @@ vi.mock('@slack/web-api', () => ({
|
|
|
12
12
|
},
|
|
13
13
|
chat: {
|
|
14
14
|
postMessage: vi.fn(),
|
|
15
|
+
scheduleMessage: vi.fn(),
|
|
16
|
+
scheduledMessages: {
|
|
17
|
+
list: vi.fn(),
|
|
18
|
+
},
|
|
15
19
|
},
|
|
16
20
|
})),
|
|
17
21
|
LogLevel: {
|
|
@@ -31,6 +35,39 @@ describe('MessageOperations', () => {
|
|
|
31
35
|
mockClient = (messageOps as any).client;
|
|
32
36
|
});
|
|
33
37
|
|
|
38
|
+
describe('scheduleMessage', () => {
|
|
39
|
+
it('should call chat.scheduleMessage with post_at', async () => {
|
|
40
|
+
mockClient.chat.scheduleMessage.mockResolvedValue({
|
|
41
|
+
ok: true,
|
|
42
|
+
scheduled_message_id: 'Q123',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await messageOps.scheduleMessage('C1234567890', 'Hello', 1770855000);
|
|
46
|
+
|
|
47
|
+
expect(mockClient.chat.scheduleMessage).toHaveBeenCalledWith({
|
|
48
|
+
channel: 'C1234567890',
|
|
49
|
+
text: 'Hello',
|
|
50
|
+
post_at: 1770855000,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('listScheduledMessages', () => {
|
|
56
|
+
it('should call chat.scheduledMessages.list', async () => {
|
|
57
|
+
mockClient.chat.scheduledMessages.list.mockResolvedValue({
|
|
58
|
+
ok: true,
|
|
59
|
+
scheduled_messages: [
|
|
60
|
+
{ id: 'Q123', channel_id: 'C123', post_at: 1770855000, date_created: 1770854400 },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const result = await messageOps.listScheduledMessages(undefined, 20);
|
|
65
|
+
|
|
66
|
+
expect(mockClient.chat.scheduledMessages.list).toHaveBeenCalledWith({ limit: 20 });
|
|
67
|
+
expect(result).toHaveLength(1);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
34
71
|
describe('getHistory with mentions', () => {
|
|
35
72
|
it('should fetch user info for mentioned users in message text', async () => {
|
|
36
73
|
const mockMessages = [
|
|
@@ -123,4 +160,4 @@ describe('MessageOperations', () => {
|
|
|
123
160
|
expect(result.users.get('U07L5D50RAL')).toBe('koguchi_s');
|
|
124
161
|
});
|
|
125
162
|
});
|
|
126
|
-
});
|
|
163
|
+
});
|