@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.
Files changed (65) hide show
  1. package/.claude/settings.local.json +60 -14
  2. package/README.md +71 -28
  3. package/dist/commands/scheduled.d.ts +3 -0
  4. package/dist/commands/scheduled.d.ts.map +1 -0
  5. package/dist/commands/scheduled.js +55 -0
  6. package/dist/commands/scheduled.js.map +1 -0
  7. package/dist/commands/send.d.ts.map +1 -1
  8. package/dist/commands/send.js +16 -2
  9. package/dist/commands/send.js.map +1 -1
  10. package/dist/index.js +2 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/types/commands.d.ts +8 -0
  13. package/dist/types/commands.d.ts.map +1 -1
  14. package/dist/utils/channel-resolver.d.ts.map +1 -1
  15. package/dist/utils/channel-resolver.js +1 -3
  16. package/dist/utils/channel-resolver.js.map +1 -1
  17. package/dist/utils/config.d.ts +10 -0
  18. package/dist/utils/config.d.ts.map +1 -0
  19. package/dist/utils/config.js +94 -0
  20. package/dist/utils/config.js.map +1 -0
  21. package/dist/utils/constants.d.ts +5 -0
  22. package/dist/utils/constants.d.ts.map +1 -1
  23. package/dist/utils/constants.js +5 -0
  24. package/dist/utils/constants.js.map +1 -1
  25. package/dist/utils/formatters/output-formatter.d.ts +7 -0
  26. package/dist/utils/formatters/output-formatter.d.ts.map +1 -0
  27. package/dist/utils/formatters/output-formatter.js +7 -0
  28. package/dist/utils/formatters/output-formatter.js.map +1 -0
  29. package/dist/utils/profile-config-refactored.d.ts +20 -0
  30. package/dist/utils/profile-config-refactored.d.ts.map +1 -0
  31. package/dist/utils/profile-config-refactored.js +174 -0
  32. package/dist/utils/profile-config-refactored.js.map +1 -0
  33. package/dist/utils/schedule-utils.d.ts +3 -0
  34. package/dist/utils/schedule-utils.d.ts.map +1 -0
  35. package/dist/utils/schedule-utils.js +34 -0
  36. package/dist/utils/schedule-utils.js.map +1 -0
  37. package/dist/utils/slack-api-client.d.ts +10 -1
  38. package/dist/utils/slack-api-client.d.ts.map +1 -1
  39. package/dist/utils/slack-api-client.js +6 -0
  40. package/dist/utils/slack-api-client.js.map +1 -1
  41. package/dist/utils/slack-operations/message-operations.d.ts +4 -2
  42. package/dist/utils/slack-operations/message-operations.d.ts.map +1 -1
  43. package/dist/utils/slack-operations/message-operations.js +28 -0
  44. package/dist/utils/slack-operations/message-operations.js.map +1 -1
  45. package/dist/utils/validators.d.ts +4 -0
  46. package/dist/utils/validators.d.ts.map +1 -1
  47. package/dist/utils/validators.js +31 -0
  48. package/dist/utils/validators.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/commands/scheduled.ts +71 -0
  51. package/src/commands/send.ts +21 -3
  52. package/src/index.ts +2 -0
  53. package/src/types/commands.ts +9 -0
  54. package/src/utils/channel-resolver.ts +1 -5
  55. package/src/utils/constants.ts +7 -0
  56. package/src/utils/schedule-utils.ts +41 -0
  57. package/src/utils/slack-api-client.ts +22 -1
  58. package/src/utils/slack-operations/message-operations.ts +55 -2
  59. package/src/utils/validators.ts +38 -0
  60. package/tests/commands/scheduled.test.ts +131 -0
  61. package/tests/commands/send.test.ts +235 -44
  62. package/tests/utils/channel-resolver.test.ts +25 -21
  63. package/tests/utils/schedule-utils.test.ts +63 -0
  64. package/tests/utils/slack-api-client.test.ts +81 -46
  65. 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(client.listChannels({
173
- types: 'public_channel',
174
- exclude_archived: true,
175
- limit: 100
176
- })).rejects.toThrow('invalid_auth');
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
+ });