slack-term 1.23.0 → 1.23.1
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/SKILL.md +5 -0
- package/dist/cli.js +77 -73
- package/package.json +4 -2
- package/ts/cli.ts +152 -58
- package/ts/slack.ts +71 -44
package/ts/slack.ts
CHANGED
|
@@ -40,10 +40,13 @@ async function call(token: string, method: string, init: RequestInit, cookie?: s
|
|
|
40
40
|
if (body.ok !== true) {
|
|
41
41
|
const err = body.error ?? "unknown";
|
|
42
42
|
if (err === "ratelimited") throw new RateLimitError(60);
|
|
43
|
+
// A desktop session token (xoxc-) is only accepted by the public Web API when
|
|
44
|
+
// paired with its xoxd session cookie — without one, point at the xoxp fallback.
|
|
43
45
|
if (err === "invalid_auth" && token.startsWith("xoxc-") && !cookie) {
|
|
44
46
|
throw new Error(
|
|
45
|
-
`Desktop app token (xoxc-)
|
|
46
|
-
`
|
|
47
|
+
`Desktop app token (xoxc-) needs its session cookie to be accepted by the public Slack API.\n` +
|
|
48
|
+
`Attach it: slack auth chrome (macOS) or slack auth firefox\n` +
|
|
49
|
+
`Or replace the token with an xoxp- user token:\n` +
|
|
47
50
|
` slack auth token`,
|
|
48
51
|
);
|
|
49
52
|
}
|
|
@@ -119,16 +122,19 @@ function get(token: string, method: string, params: Record<string, string>, cook
|
|
|
119
122
|
return call(token, `${method}?${qs}`, { method: "GET" }, cookie);
|
|
120
123
|
}
|
|
121
124
|
|
|
122
|
-
function post(token: string, method: string, body: Record<string, Json
|
|
125
|
+
function post(token: string, method: string, body: Record<string, Json>, cookie?: string): Promise<Json> {
|
|
123
126
|
return call(token, method, {
|
|
124
127
|
method: "POST",
|
|
125
128
|
headers: { "Content-Type": "application/json" },
|
|
126
129
|
body: JSON.stringify(body),
|
|
127
|
-
});
|
|
130
|
+
}, cookie);
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
export async function authTest(
|
|
131
|
-
|
|
133
|
+
export async function authTest(
|
|
134
|
+
token: string,
|
|
135
|
+
cookie?: string,
|
|
136
|
+
): Promise<{ team: string; teamId: string; url: string; user: string; userId: string }> {
|
|
137
|
+
const resp = (await get(token, "auth.test", {}, cookie)) as {
|
|
132
138
|
team?: string; team_id?: string; url?: string; user?: string; user_id?: string;
|
|
133
139
|
};
|
|
134
140
|
return {
|
|
@@ -143,13 +149,12 @@ export async function authTest(token: string): Promise<{ team: string; teamId: s
|
|
|
143
149
|
// auth.test plus the token's granted scopes. Slack returns the granted bot/user
|
|
144
150
|
// scopes in the `X-OAuth-Scopes` response header on every Web API call, which is
|
|
145
151
|
// the only way to learn what a token can do without probing each endpoint.
|
|
146
|
-
export async function authScopes(token: string): Promise<{
|
|
152
|
+
export async function authScopes(token: string, cookie?: string): Promise<{
|
|
147
153
|
userId: string; botId: string; team: string; url: string; scopes: string[];
|
|
148
154
|
}> {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
});
|
|
155
|
+
const headers: Record<string, string> = { Authorization: `Bearer ${token}` };
|
|
156
|
+
if (cookie) headers["Cookie"] = `d=${cookie}`;
|
|
157
|
+
const res = await fetch(`${base()}/auth.test`, { method: "GET", headers });
|
|
153
158
|
if (res.status === 429) {
|
|
154
159
|
const retryAfter = parseInt(res.headers.get("retry-after") ?? "60", 10);
|
|
155
160
|
throw new RateLimitError(isNaN(retryAfter) ? 60 : retryAfter);
|
|
@@ -170,18 +175,25 @@ export async function authScopes(token: string): Promise<{
|
|
|
170
175
|
|
|
171
176
|
// Resolve a bot's app_id (and bot user id) from its bot_id — needed to build the
|
|
172
177
|
// app-settings deep link in messaging diagnostics.
|
|
173
|
-
export async function botsInfo(token: string, botId: string): Promise<{ appId: string; userId: string }> {
|
|
174
|
-
const resp = (await get(token, "bots.info", { bot: botId })) as {
|
|
178
|
+
export async function botsInfo(token: string, botId: string, cookie?: string): Promise<{ appId: string; userId: string }> {
|
|
179
|
+
const resp = (await get(token, "bots.info", { bot: botId }, cookie)) as {
|
|
175
180
|
bot?: { app_id?: string; user_id?: string };
|
|
176
181
|
};
|
|
177
182
|
return { appId: resp.bot?.app_id ?? "", userId: resp.bot?.user_id ?? "" };
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
export async function history(
|
|
185
|
+
export async function history(
|
|
186
|
+
token: string,
|
|
187
|
+
channel: string,
|
|
188
|
+
limit = 20,
|
|
189
|
+
oldest?: string,
|
|
190
|
+
cursor?: string,
|
|
191
|
+
cookie?: string,
|
|
192
|
+
): Promise<Json> {
|
|
181
193
|
const params: Record<string, string> = { channel, limit: String(limit) };
|
|
182
194
|
if (oldest !== undefined) params.oldest = oldest;
|
|
183
195
|
if (cursor !== undefined) params.cursor = cursor;
|
|
184
|
-
return get(token, "conversations.history", params);
|
|
196
|
+
return get(token, "conversations.history", params, cookie);
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
export async function replies(
|
|
@@ -189,8 +201,9 @@ export async function replies(
|
|
|
189
201
|
channel: string,
|
|
190
202
|
ts: string,
|
|
191
203
|
limit = 50,
|
|
204
|
+
cookie?: string,
|
|
192
205
|
): Promise<Json> {
|
|
193
|
-
return get(token, "conversations.replies", { channel, ts, limit: String(limit) });
|
|
206
|
+
return get(token, "conversations.replies", { channel, ts, limit: String(limit) }, cookie);
|
|
194
207
|
}
|
|
195
208
|
|
|
196
209
|
// Metadata for a single uploaded file (files.info). Carries url_private_download,
|
|
@@ -205,6 +218,7 @@ export async function searchPage(
|
|
|
205
218
|
query: string,
|
|
206
219
|
count: number,
|
|
207
220
|
page: number,
|
|
221
|
+
cookie?: string,
|
|
208
222
|
): Promise<Json> {
|
|
209
223
|
return get(token, "search.messages", {
|
|
210
224
|
query,
|
|
@@ -212,20 +226,20 @@ export async function searchPage(
|
|
|
212
226
|
sort_dir: "desc",
|
|
213
227
|
count: String(Math.min(Math.max(count, 1), 100)),
|
|
214
228
|
page: String(Math.max(page, 1)),
|
|
215
|
-
});
|
|
229
|
+
}, cookie);
|
|
216
230
|
}
|
|
217
231
|
|
|
218
|
-
export async function search(token: string, query: string): Promise<Json> {
|
|
219
|
-
return searchPage(token, query, 100, 1);
|
|
232
|
+
export async function search(token: string, query: string, cookie?: string): Promise<Json> {
|
|
233
|
+
return searchPage(token, query, 100, 1, cookie);
|
|
220
234
|
}
|
|
221
235
|
|
|
222
|
-
export async function searchAll(token: string, query: string, max: number): Promise<Json> {
|
|
236
|
+
export async function searchAll(token: string, query: string, max: number, cookie?: string): Promise<Json> {
|
|
223
237
|
const perPage = 100;
|
|
224
238
|
let page = 1;
|
|
225
239
|
const all: Json[] = [];
|
|
226
240
|
let last: Json = { ok: true, messages: {} };
|
|
227
241
|
while (true) {
|
|
228
|
-
const resp = await searchPage(token, query, perPage, page);
|
|
242
|
+
const resp = await searchPage(token, query, perPage, page, cookie);
|
|
229
243
|
const matches = getPath(resp, ["messages", "matches"]);
|
|
230
244
|
const arr = Array.isArray(matches) ? matches : [];
|
|
231
245
|
all.push(...arr);
|
|
@@ -246,6 +260,7 @@ export async function send(
|
|
|
246
260
|
text: string,
|
|
247
261
|
threadTs?: string,
|
|
248
262
|
replyBroadcast?: boolean,
|
|
263
|
+
cookie?: string,
|
|
249
264
|
): Promise<string> {
|
|
250
265
|
const body: Record<string, Json> = {
|
|
251
266
|
channel,
|
|
@@ -256,7 +271,7 @@ export async function send(
|
|
|
256
271
|
// "Also send to channel": broadcast a threaded reply back to the channel.
|
|
257
272
|
// Only meaningful alongside thread_ts; Slack ignores it on top-level sends.
|
|
258
273
|
if (replyBroadcast && threadTs !== undefined) body.reply_broadcast = true;
|
|
259
|
-
const resp = (await post(token, "chat.postMessage", body)) as { ts?: string };
|
|
274
|
+
const resp = (await post(token, "chat.postMessage", body, cookie)) as { ts?: string };
|
|
260
275
|
return resp.ts ?? "";
|
|
261
276
|
}
|
|
262
277
|
|
|
@@ -266,6 +281,7 @@ export async function scheduleMessage(
|
|
|
266
281
|
text: string,
|
|
267
282
|
postAt: number,
|
|
268
283
|
threadTs?: string,
|
|
284
|
+
cookie?: string,
|
|
269
285
|
): Promise<string> {
|
|
270
286
|
const body: Record<string, Json> = {
|
|
271
287
|
channel,
|
|
@@ -274,39 +290,42 @@ export async function scheduleMessage(
|
|
|
274
290
|
blocks: [{ type: "markdown", text }],
|
|
275
291
|
};
|
|
276
292
|
if (threadTs !== undefined) body.thread_ts = threadTs;
|
|
277
|
-
const resp = (await post(token, "chat.scheduleMessage", body)) as { scheduled_message_id?: string };
|
|
293
|
+
const resp = (await post(token, "chat.scheduleMessage", body, cookie)) as { scheduled_message_id?: string };
|
|
278
294
|
return resp.scheduled_message_id ?? "";
|
|
279
295
|
}
|
|
280
296
|
|
|
281
297
|
export async function listScheduledMessages(
|
|
282
298
|
token: string,
|
|
283
299
|
channel?: string,
|
|
300
|
+
cookie?: string,
|
|
284
301
|
): Promise<Json> {
|
|
285
302
|
const params: Record<string, string> = {};
|
|
286
303
|
if (channel) params.channel = channel;
|
|
287
|
-
return get(token, "chat.scheduledMessages.list", params);
|
|
304
|
+
return get(token, "chat.scheduledMessages.list", params, cookie);
|
|
288
305
|
}
|
|
289
306
|
|
|
290
307
|
export async function deleteScheduledMessage(
|
|
291
308
|
token: string,
|
|
292
309
|
channel: string,
|
|
293
310
|
scheduledMessageId: string,
|
|
311
|
+
cookie?: string,
|
|
294
312
|
): Promise<void> {
|
|
295
313
|
await post(token, "chat.deleteScheduledMessage", {
|
|
296
314
|
channel,
|
|
297
315
|
scheduled_message_id: scheduledMessageId,
|
|
298
|
-
});
|
|
316
|
+
}, cookie);
|
|
299
317
|
}
|
|
300
318
|
|
|
301
319
|
export async function getPermalink(
|
|
302
320
|
token: string,
|
|
303
321
|
channel: string,
|
|
304
322
|
messageTs: string,
|
|
323
|
+
cookie?: string,
|
|
305
324
|
): Promise<string> {
|
|
306
325
|
const resp = (await get(token, "chat.getPermalink", {
|
|
307
326
|
channel,
|
|
308
327
|
message_ts: messageTs,
|
|
309
|
-
})) as { permalink?: string };
|
|
328
|
+
}, cookie)) as { permalink?: string };
|
|
310
329
|
return resp.permalink ?? "";
|
|
311
330
|
}
|
|
312
331
|
|
|
@@ -315,13 +334,14 @@ export async function editMessage(
|
|
|
315
334
|
channel: string,
|
|
316
335
|
ts: string,
|
|
317
336
|
text: string,
|
|
337
|
+
cookie?: string,
|
|
318
338
|
): Promise<string> {
|
|
319
339
|
const resp = (await post(token, "chat.update", {
|
|
320
340
|
channel,
|
|
321
341
|
ts,
|
|
322
342
|
text,
|
|
323
343
|
blocks: [{ type: "markdown", text }],
|
|
324
|
-
})) as { ts?: string };
|
|
344
|
+
}, cookie)) as { ts?: string };
|
|
325
345
|
return resp.ts ?? ts;
|
|
326
346
|
}
|
|
327
347
|
|
|
@@ -329,8 +349,9 @@ export async function deleteMessage(
|
|
|
329
349
|
token: string,
|
|
330
350
|
channel: string,
|
|
331
351
|
ts: string,
|
|
352
|
+
cookie?: string,
|
|
332
353
|
): Promise<void> {
|
|
333
|
-
await post(token, "chat.delete", { channel, ts });
|
|
354
|
+
await post(token, "chat.delete", { channel, ts }, cookie);
|
|
334
355
|
}
|
|
335
356
|
|
|
336
357
|
// Add or remove an emoji reaction on a message. `name` is the emoji shortcode
|
|
@@ -342,8 +363,9 @@ export async function reactionAdd(
|
|
|
342
363
|
channel: string,
|
|
343
364
|
ts: string,
|
|
344
365
|
name: string,
|
|
366
|
+
cookie?: string,
|
|
345
367
|
): Promise<void> {
|
|
346
|
-
await post(token, "reactions.add", { channel, timestamp: ts, name });
|
|
368
|
+
await post(token, "reactions.add", { channel, timestamp: ts, name }, cookie);
|
|
347
369
|
}
|
|
348
370
|
|
|
349
371
|
export async function reactionRemove(
|
|
@@ -351,8 +373,9 @@ export async function reactionRemove(
|
|
|
351
373
|
channel: string,
|
|
352
374
|
ts: string,
|
|
353
375
|
name: string,
|
|
376
|
+
cookie?: string,
|
|
354
377
|
): Promise<void> {
|
|
355
|
-
await post(token, "reactions.remove", { channel, timestamp: ts, name });
|
|
378
|
+
await post(token, "reactions.remove", { channel, timestamp: ts, name }, cookie);
|
|
356
379
|
}
|
|
357
380
|
|
|
358
381
|
// Create a public (or private) channel via conversations.create. Slack
|
|
@@ -363,11 +386,12 @@ export async function createChannel(
|
|
|
363
386
|
token: string,
|
|
364
387
|
name: string,
|
|
365
388
|
isPrivate = false,
|
|
389
|
+
cookie?: string,
|
|
366
390
|
): Promise<{ id: string; name: string }> {
|
|
367
391
|
const resp = (await post(token, "conversations.create", {
|
|
368
392
|
name,
|
|
369
393
|
is_private: isPrivate,
|
|
370
|
-
})) as { channel?: { id?: string; name?: string } };
|
|
394
|
+
}, cookie)) as { channel?: { id?: string; name?: string } };
|
|
371
395
|
return { id: resp.channel?.id ?? "", name: resp.channel?.name ?? name };
|
|
372
396
|
}
|
|
373
397
|
|
|
@@ -376,17 +400,18 @@ export async function inviteToChannel(
|
|
|
376
400
|
token: string,
|
|
377
401
|
channel: string,
|
|
378
402
|
userIds: string[],
|
|
403
|
+
cookie?: string,
|
|
379
404
|
): Promise<void> {
|
|
380
|
-
await post(token, "conversations.invite", { channel, users: userIds.join(",") });
|
|
405
|
+
await post(token, "conversations.invite", { channel, users: userIds.join(",") }, cookie);
|
|
381
406
|
}
|
|
382
407
|
|
|
383
|
-
export async function listConversations(token: string): Promise<Json> {
|
|
408
|
+
export async function listConversations(token: string, cookie?: string): Promise<Json> {
|
|
384
409
|
const allChannels: Json[] = [];
|
|
385
410
|
let cursor = "";
|
|
386
411
|
while (true) {
|
|
387
412
|
const params: Record<string, string> = { limit: "200", types: "public_channel,private_channel,im,mpim" };
|
|
388
413
|
if (cursor) params.cursor = cursor;
|
|
389
|
-
const resp = (await get(token, "conversations.list", params)) as {
|
|
414
|
+
const resp = (await get(token, "conversations.list", params, cookie)) as {
|
|
390
415
|
channels?: Json[];
|
|
391
416
|
response_metadata?: { next_cursor?: string };
|
|
392
417
|
};
|
|
@@ -397,8 +422,8 @@ export async function listConversations(token: string): Promise<Json> {
|
|
|
397
422
|
return { channels: allChannels };
|
|
398
423
|
}
|
|
399
424
|
|
|
400
|
-
export async function openDm(token: string, userId: string): Promise<string> {
|
|
401
|
-
const resp = (await post(token, "conversations.open", { users: userId })) as {
|
|
425
|
+
export async function openDm(token: string, userId: string, cookie?: string): Promise<string> {
|
|
426
|
+
const resp = (await post(token, "conversations.open", { users: userId }, cookie)) as {
|
|
402
427
|
channel?: { id?: string };
|
|
403
428
|
};
|
|
404
429
|
const id = resp.channel?.id;
|
|
@@ -501,7 +526,7 @@ export async function resolveChannel(token: string, ref: string, cookie?: string
|
|
|
501
526
|
};
|
|
502
527
|
const dm = (dmResp.channels ?? []).find((ch) => ch.user === userId);
|
|
503
528
|
if (dm?.id) return String(dm.id);
|
|
504
|
-
return openDm(token, userId);
|
|
529
|
+
return openDm(token, userId, cookie);
|
|
505
530
|
}
|
|
506
531
|
|
|
507
532
|
// Find user ID first via users.list (batch), then locate existing DM.
|
|
@@ -577,9 +602,10 @@ export async function resolveChannel(token: string, ref: string, cookie?: string
|
|
|
577
602
|
export async function userInfoPair(
|
|
578
603
|
token: string,
|
|
579
604
|
userId: string,
|
|
605
|
+
cookie?: string,
|
|
580
606
|
): Promise<[string, string]> {
|
|
581
607
|
try {
|
|
582
|
-
const resp = (await get(token, "users.info", { user: userId })) as {
|
|
608
|
+
const resp = (await get(token, "users.info", { user: userId }, cookie)) as {
|
|
583
609
|
user?: { profile?: { display_name?: string }; real_name?: string; name?: string };
|
|
584
610
|
};
|
|
585
611
|
const display = resp.user?.profile?.display_name;
|
|
@@ -594,9 +620,9 @@ export async function userInfoPair(
|
|
|
594
620
|
}
|
|
595
621
|
}
|
|
596
622
|
|
|
597
|
-
export async function userName(token: string, userId: string): Promise<string> {
|
|
623
|
+
export async function userName(token: string, userId: string, cookie?: string): Promise<string> {
|
|
598
624
|
try {
|
|
599
|
-
const resp = (await get(token, "users.info", { user: userId })) as {
|
|
625
|
+
const resp = (await get(token, "users.info", { user: userId }, cookie)) as {
|
|
600
626
|
user?: { profile?: { display_name?: string }; real_name?: string; name?: string };
|
|
601
627
|
};
|
|
602
628
|
const display = resp.user?.profile?.display_name;
|
|
@@ -700,8 +726,8 @@ export async function userInfo(token: string, userId: string, cookie?: string):
|
|
|
700
726
|
return get(token, "users.info", { user: userId }, cookie);
|
|
701
727
|
}
|
|
702
728
|
|
|
703
|
-
export async function conversationInfo(token: string, channelId: string): Promise<Json> {
|
|
704
|
-
return get(token, "conversations.info", { channel: channelId });
|
|
729
|
+
export async function conversationInfo(token: string, channelId: string, cookie?: string): Promise<Json> {
|
|
730
|
+
return get(token, "conversations.info", { channel: channelId }, cookie);
|
|
705
731
|
}
|
|
706
732
|
|
|
707
733
|
export async function deleteDraft(token: string, draftId: string, cookie?: string): Promise<Json> {
|
|
@@ -724,6 +750,7 @@ export async function uploadFile(
|
|
|
724
750
|
channel: string,
|
|
725
751
|
filePath: string,
|
|
726
752
|
opts: { title?: string; threadTs?: string; initialComment?: string } = {},
|
|
753
|
+
cookie?: string,
|
|
727
754
|
): Promise<{ fileId: string; permalink?: string }> {
|
|
728
755
|
const { statSync, readFileSync } = await import("node:fs");
|
|
729
756
|
const { basename } = await import("node:path");
|
|
@@ -735,7 +762,7 @@ export async function uploadFile(
|
|
|
735
762
|
const urlResp = (await get(token, "files.getUploadURLExternal", {
|
|
736
763
|
filename,
|
|
737
764
|
length: String(stat.size),
|
|
738
|
-
})) as { upload_url?: string; file_id?: string };
|
|
765
|
+
}, cookie)) as { upload_url?: string; file_id?: string };
|
|
739
766
|
|
|
740
767
|
const uploadUrl = urlResp.upload_url;
|
|
741
768
|
const fileId = urlResp.file_id;
|
|
@@ -758,7 +785,7 @@ export async function uploadFile(
|
|
|
758
785
|
if (opts.threadTs) completeBody.thread_ts = opts.threadTs;
|
|
759
786
|
if (opts.initialComment) completeBody.initial_comment = opts.initialComment;
|
|
760
787
|
|
|
761
|
-
const completeResp = (await post(token, "files.completeUploadExternal", completeBody)) as {
|
|
788
|
+
const completeResp = (await post(token, "files.completeUploadExternal", completeBody, cookie)) as {
|
|
762
789
|
files?: Array<{ permalink?: string }>;
|
|
763
790
|
};
|
|
764
791
|
|