slack-term 1.35.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ts/slack.ts CHANGED
@@ -47,7 +47,7 @@ async function call(token: string, method: string, init: RequestInit, cookie?: s
47
47
  if (err === "invalid_auth" && token.startsWith("xoxc-") && !cookie) {
48
48
  throw new Error(
49
49
  `Desktop app token (xoxc-) needs its session cookie to be accepted by the public Slack API.\n` +
50
- `Attach it: slack auth chrome (macOS) or slack auth firefox\n` +
50
+ `Attach it: slack auth firefox (Linux/macOS/Windows) or slack auth chrome (macOS/Linux)\n` +
51
51
  `Or replace the token with an xoxp- user token:\n` +
52
52
  ` slack auth login`,
53
53
  );
@@ -104,7 +104,7 @@ async function callSession(token: string, method: string, init: RequestInit, coo
104
104
  if ((err === "invalid_auth" || err === "not_authed") && !cookie) {
105
105
  throw new Error(
106
106
  `drafts.list also requires the xoxd session cookie.\n` +
107
- `Attach it with: slack auth chrome (macOS) or slack auth firefox`,
107
+ `Attach it with: slack auth firefox (Linux/macOS/Windows) or slack auth chrome (macOS/Linux)`,
108
108
  );
109
109
  }
110
110
  throw new Error(`Slack error on ${method}: ${err}`);
@@ -343,6 +343,10 @@ export async function send(
343
343
  // `:one:` — so a message whose body has to be read back verbatim (`ask`,
344
344
  // `poll`) must not carry them. Verified against a real workspace 2026-08-20.
345
345
  plain?: boolean,
346
+ /** Message metadata (`event_type`/`event_payload`) — invisible to readers.
347
+ * Dropped and retried once if Slack rejects it: attribution is bookkeeping,
348
+ * and must never be the reason a message was not sent. */
349
+ metadata?: MessageMetadata,
346
350
  ): Promise<string> {
347
351
  const body: Record<string, Json> = plain
348
352
  ? { channel, text }
@@ -351,10 +355,31 @@ export async function send(
351
355
  // "Also send to channel": broadcast a threaded reply back to the channel.
352
356
  // Only meaningful alongside thread_ts; Slack ignores it on top-level sends.
353
357
  if (replyBroadcast && threadTs !== undefined) body.reply_broadcast = true;
354
- const resp = (await post(token, "chat.postMessage", body, cookie)) as { ts?: string };
358
+ const resp = (await postWithMetadata(token, "chat.postMessage", body, metadata, cookie)) as { ts?: string };
355
359
  return resp.ts ?? "";
356
360
  }
357
361
 
362
+ export type MessageMetadata = { event_type: string; event_payload: Record<string, string | number> };
363
+
364
+ /** Slack error codes that mean "the metadata itself was refused". Only these
365
+ * trigger the retry without it — any other failure is the send's own and must
366
+ * surface, not be retried into a duplicate post. */
367
+ function isMetadataError(e: unknown): boolean {
368
+ return e instanceof Error && /: (invalid_metadata\w*|metadata_\w+)$/.test(e.message);
369
+ }
370
+
371
+ async function postWithMetadata(
372
+ token: string, method: string, body: Record<string, Json>, metadata: MessageMetadata | undefined, cookie?: string,
373
+ ): Promise<Json> {
374
+ if (!metadata) return post(token, method, body, cookie);
375
+ try {
376
+ return await post(token, method, { ...body, metadata: metadata as unknown as Json }, cookie);
377
+ } catch (e: unknown) {
378
+ if (!isMetadataError(e)) throw e;
379
+ return post(token, method, body, cookie);
380
+ }
381
+ }
382
+
358
383
  export async function scheduleMessage(
359
384
  token: string,
360
385
  channel: string,
@@ -417,11 +442,12 @@ export async function editMessage(
417
442
  cookie?: string,
418
443
  /** As in `send`: no blocks, so the stored text survives verbatim. */
419
444
  plain?: boolean,
445
+ metadata?: MessageMetadata,
420
446
  ): Promise<string> {
421
- const resp = (await post(token, "chat.update", plain
447
+ const resp = (await postWithMetadata(token, "chat.update", plain
422
448
  ? { channel, ts, text }
423
449
  : { channel, ts, text, blocks: [{ type: "markdown", text }] },
424
- cookie)) as { ts?: string };
450
+ metadata, cookie)) as { ts?: string };
425
451
  return resp.ts ?? ts;
426
452
  }
427
453