@executor-js/emulate 0.6.0 → 0.7.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.
@@ -189,10 +189,10 @@ function formatSlackFile(file) {
189
189
  ...file.thread_ts ? { thread_ts: file.thread_ts } : {}
190
190
  };
191
191
  }
192
- function formatSlackPermalink(baseUrl, channel, msg) {
193
- const permalink = `${baseUrl.replace(/\/$/, "")}/archives/${channel}/p${msg.ts.replace(".", "")}`;
192
+ function formatSlackPermalink(baseUrl, channel2, msg) {
193
+ const permalink = `${baseUrl.replace(/\/$/, "")}/archives/${channel2}/p${msg.ts.replace(".", "")}`;
194
194
  if (!msg.thread_ts || msg.thread_ts === msg.ts) return permalink;
195
- const params = new URLSearchParams({ thread_ts: msg.thread_ts, cid: channel });
195
+ const params = new URLSearchParams({ thread_ts: msg.thread_ts, cid: channel2 });
196
196
  return `${permalink}?${params.toString()}`;
197
197
  }
198
198
  function formatSlackScheduledMessage(msg) {
@@ -398,22 +398,22 @@ function authRoutes(ctx) {
398
398
  function chatRoutes(ctx) {
399
399
  const { app, store, webhooks, baseUrl } = ctx;
400
400
  const ss = () => getSlackStore(store);
401
- const findChannel = (channel) => ss().channels.findOneBy("channel_id", channel) ?? ss().channels.all().find((ch) => !ch.is_im && !ch.is_mpim && ch.name === channel);
401
+ const findChannel = (channel2) => ss().channels.findOneBy("channel_id", channel2) ?? ss().channels.all().find((ch) => !ch.is_im && !ch.is_mpim && ch.name === channel2);
402
402
  const getAuthSlackUser = (authUser) => ss().users.findOneBy("user_id", authUser.login) ?? ss().users.findOneBy("name", authUser.login);
403
403
  const getAuthUserId = (authUser) => getAuthSlackUser(authUser)?.user_id ?? authUser.login;
404
- const isAuthChannelMember = (channel, authUser) => {
404
+ const isAuthChannelMember = (channel2, authUser) => {
405
405
  const user = getAuthSlackUser(authUser);
406
406
  const userId = user?.user_id ?? authUser.login;
407
- return channel.members.includes(userId) || (user ? channel.members.includes(user.name) : false);
407
+ return channel2.members.includes(userId) || (user ? channel2.members.includes(user.name) : false);
408
408
  };
409
- const canAccessConversation = (channel, authUser) => !channel.is_private || isAuthChannelMember(channel, authUser);
409
+ const canAccessConversation = (channel2, authUser) => !channel2.is_private || isAuthChannelMember(channel2, authUser);
410
410
  const isAuthoredByUser = (msg, authUser) => {
411
411
  const user = getAuthSlackUser(authUser);
412
412
  return msg.user === authUser.login || msg.user === user?.user_id || msg.user === user?.name;
413
413
  };
414
- const isChannelMember = (channel, user) => channel.members.includes(user.user_id) || channel.members.includes(user.name);
415
- const deletePinsForMessage = (channel, ts) => {
416
- for (const pin of ss().pins.findBy("message_ts", ts).filter((pin2) => pin2.channel_id === channel)) {
414
+ const isChannelMember = (channel2, user) => channel2.members.includes(user.user_id) || channel2.members.includes(user.name);
415
+ const deletePinsForMessage = (channel2, ts) => {
416
+ for (const pin of ss().pins.findBy("message_ts", ts).filter((pin2) => pin2.channel_id === channel2)) {
417
417
  ss().pins.delete(pin.id);
418
418
  }
419
419
  };
@@ -471,21 +471,21 @@ function chatRoutes(ctx) {
471
471
  await dispatchConversationEvent("im_open", { channel: created.channel_id });
472
472
  return created;
473
473
  };
474
- const findWritableConversation = async (authUser, channel) => findChannel(channel) ?? await findOrCreateDirectMessage(authUser, channel);
474
+ const findWritableConversation = async (authUser, channel2) => findChannel(channel2) ?? await findOrCreateDirectMessage(authUser, channel2);
475
475
  app.post("/api/chat.postMessage", async (c) => {
476
476
  const authUser = c.get("authUser");
477
477
  if (!authUser) return slackError(c, "not_authed");
478
478
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
479
479
  if (scopeError) return scopeError;
480
480
  const body = await parseSlackBody(c);
481
- const channel = typeof body.channel === "string" ? body.channel : "";
481
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
482
482
  const text = typeof body.text === "string" ? body.text : "";
483
483
  const thread_ts = typeof body.thread_ts === "string" ? body.thread_ts : void 0;
484
484
  const richMessage = parseSlackRichMessageFields(body);
485
485
  if (richMessage.error) return slackError(c, richMessage.error);
486
- if (!channel) return slackError(c, "channel_not_found");
486
+ if (!channel2) return slackError(c, "channel_not_found");
487
487
  if (!hasSlackMessageContent(text, richMessage.fields)) return slackError(c, "no_text");
488
- const ch = await findWritableConversation(authUser, channel);
488
+ const ch = await findWritableConversation(authUser, channel2);
489
489
  if (!ch) return slackError(c, "channel_not_found");
490
490
  if (ch.is_archived) return slackError(c, "is_archived");
491
491
  if (!canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
@@ -538,16 +538,16 @@ function chatRoutes(ctx) {
538
538
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
539
539
  if (scopeError) return scopeError;
540
540
  const body = await parseSlackBody(c);
541
- const channel = typeof body.channel === "string" ? body.channel : "";
541
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
542
542
  const user = typeof body.user === "string" ? body.user : "";
543
543
  const text = typeof body.text === "string" ? body.text : "";
544
544
  const thread_ts = typeof body.thread_ts === "string" ? body.thread_ts : void 0;
545
545
  const richMessage = parseSlackRichMessageFields(body);
546
546
  if (richMessage.error) return slackError(c, richMessage.error);
547
- if (!channel) return slackError(c, "channel_not_found");
547
+ if (!channel2) return slackError(c, "channel_not_found");
548
548
  if (!user) return slackError(c, "user_not_found");
549
549
  if (!hasSlackMessageContent(text, richMessage.fields)) return slackError(c, "no_text");
550
- const ch = findChannel(channel);
550
+ const ch = findChannel(channel2);
551
551
  if (!ch) return slackError(c, "channel_not_found");
552
552
  if (ch.is_archived) return slackError(c, "is_archived");
553
553
  if (!canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
@@ -577,16 +577,16 @@ function chatRoutes(ctx) {
577
577
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
578
578
  if (scopeError) return scopeError;
579
579
  const body = await parseSlackBody(c);
580
- const channel = typeof body.channel === "string" ? body.channel : "";
580
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
581
581
  const ts = typeof body.ts === "string" ? body.ts : "";
582
582
  const hasText = typeof body.text === "string";
583
583
  const text = hasText ? body.text : "";
584
584
  const richMessage = parseSlackRichMessageFields(body);
585
585
  if (richMessage.error) return slackError(c, richMessage.error);
586
- if (!channel || !ts) return slackError(c, "message_not_found");
587
- const ch = ss().channels.findOneBy("channel_id", channel);
586
+ if (!channel2 || !ts) return slackError(c, "message_not_found");
587
+ const ch = ss().channels.findOneBy("channel_id", channel2);
588
588
  if (ch && !canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
589
- const msg = ss().messages.all().find((m) => m.ts === ts && m.channel_id === channel);
589
+ const msg = ss().messages.all().find((m) => m.ts === ts && m.channel_id === channel2);
590
590
  if (!msg) return slackError(c, "message_not_found");
591
591
  if (!isAuthoredByUser(msg, authUser)) return slackError(c, "cant_update_message");
592
592
  const updates = { ...richMessage.fields };
@@ -613,7 +613,7 @@ function chatRoutes(ctx) {
613
613
  type: "message",
614
614
  subtype: "message_changed",
615
615
  hidden: true,
616
- channel,
616
+ channel: channel2,
617
617
  ts: eventTs,
618
618
  event_ts: eventTs,
619
619
  message: formatSlackMessage(updated),
@@ -623,7 +623,7 @@ function chatRoutes(ctx) {
623
623
  "slack"
624
624
  );
625
625
  return slackOk(c, {
626
- channel,
626
+ channel: channel2,
627
627
  ts,
628
628
  text: updated.text,
629
629
  message: formatSlackMessage(updated)
@@ -635,16 +635,16 @@ function chatRoutes(ctx) {
635
635
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
636
636
  if (scopeError) return scopeError;
637
637
  const body = await parseSlackBody(c);
638
- const channel = typeof body.channel === "string" ? body.channel : "";
638
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
639
639
  const ts = typeof body.ts === "string" ? body.ts : "";
640
- if (!channel || !ts) return slackError(c, "message_not_found");
641
- const ch = ss().channels.findOneBy("channel_id", channel);
640
+ if (!channel2 || !ts) return slackError(c, "message_not_found");
641
+ const ch = ss().channels.findOneBy("channel_id", channel2);
642
642
  if (ch && !canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
643
- const msg = ss().messages.all().find((m) => m.ts === ts && m.channel_id === channel);
643
+ const msg = ss().messages.all().find((m) => m.ts === ts && m.channel_id === channel2);
644
644
  if (!msg) return slackError(c, "message_not_found");
645
645
  if (!isAuthoredByUser(msg, authUser)) return slackError(c, "cant_delete_message");
646
646
  ss().messages.delete(msg.id);
647
- deletePinsForMessage(channel, ts);
647
+ deletePinsForMessage(channel2, ts);
648
648
  const eventTs = generateTs();
649
649
  await webhooks.dispatch(
650
650
  "message",
@@ -655,7 +655,7 @@ function chatRoutes(ctx) {
655
655
  type: "message",
656
656
  subtype: "message_deleted",
657
657
  hidden: true,
658
- channel,
658
+ channel: channel2,
659
659
  ts: eventTs,
660
660
  event_ts: eventTs,
661
661
  deleted_ts: ts,
@@ -664,23 +664,23 @@ function chatRoutes(ctx) {
664
664
  },
665
665
  "slack"
666
666
  );
667
- return slackOk(c, { channel, ts });
667
+ return slackOk(c, { channel: channel2, ts });
668
668
  });
669
669
  async function getPermalink(c) {
670
670
  const authUser = c.get("authUser");
671
671
  if (!authUser) return slackError(c, "not_authed");
672
672
  const body = c.req.method === "GET" ? {} : await parseSlackBody(c);
673
- const channel = typeof body.channel === "string" ? body.channel : c.req.query("channel") ?? "";
673
+ const channel2 = typeof body.channel === "string" ? body.channel : c.req.query("channel") ?? "";
674
674
  const messageTs = typeof body.message_ts === "string" ? body.message_ts : c.req.query("message_ts") ?? "";
675
- if (!channel) return slackError(c, "channel_not_found");
675
+ if (!channel2) return slackError(c, "channel_not_found");
676
676
  if (!messageTs) return slackError(c, "message_not_found");
677
- const ch = ss().channels.findOneBy("channel_id", channel);
677
+ const ch = ss().channels.findOneBy("channel_id", channel2);
678
678
  if (!ch) return slackError(c, "channel_not_found");
679
679
  if (!canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
680
- const msg = ss().messages.all().find((m) => m.ts === messageTs && m.channel_id === channel);
680
+ const msg = ss().messages.all().find((m) => m.ts === messageTs && m.channel_id === channel2);
681
681
  if (!msg) return slackError(c, "message_not_found");
682
682
  return slackOk(c, {
683
- channel,
683
+ channel: channel2,
684
684
  permalink: formatSlackPermalink(baseUrl, ch.channel_id, msg)
685
685
  });
686
686
  }
@@ -692,20 +692,20 @@ function chatRoutes(ctx) {
692
692
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
693
693
  if (scopeError) return scopeError;
694
694
  const body = await parseSlackBody(c);
695
- const channel = typeof body.channel === "string" ? body.channel : "";
695
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
696
696
  const text = typeof body.text === "string" ? body.text : "";
697
697
  const postAt = Number(body.post_at);
698
698
  const thread_ts = typeof body.thread_ts === "string" ? body.thread_ts : void 0;
699
699
  const richMessage = parseSlackRichMessageFields(body);
700
700
  if (richMessage.error) return slackError(c, richMessage.error);
701
- if (!channel) return slackError(c, "channel_not_found");
701
+ if (!channel2) return slackError(c, "channel_not_found");
702
702
  if (!hasSlackMessageContent(text, richMessage.fields)) return slackError(c, "no_text");
703
703
  if (!Number.isFinite(postAt) || postAt <= 0) return slackError(c, "invalid_time");
704
704
  const now = Math.floor(Date.now() / 1e3);
705
705
  const postAtSeconds = Math.floor(postAt);
706
706
  if (postAtSeconds <= now) return slackError(c, "time_in_past");
707
707
  if (postAtSeconds > now + 120 * 24 * 60 * 60) return slackError(c, "time_too_far");
708
- const ch = findChannel(channel);
708
+ const ch = findChannel(channel2);
709
709
  if (!ch) return slackError(c, "channel_not_found");
710
710
  if (ch.is_archived) return slackError(c, "is_archived");
711
711
  if (!canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
@@ -735,11 +735,11 @@ function chatRoutes(ctx) {
735
735
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
736
736
  if (scopeError) return scopeError;
737
737
  const body = await parseSlackBody(c);
738
- const channel = typeof body.channel === "string" ? body.channel : "";
738
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
739
739
  const scheduledMessageId = typeof body.scheduled_message_id === "string" ? body.scheduled_message_id : "";
740
- if (!channel) return slackError(c, "channel_not_found");
740
+ if (!channel2) return slackError(c, "channel_not_found");
741
741
  if (!scheduledMessageId) return slackError(c, "invalid_scheduled_message_id");
742
- const ch = findChannel(channel);
742
+ const ch = findChannel(channel2);
743
743
  if (!ch) return slackError(c, "channel_not_found");
744
744
  if (!canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
745
745
  const scheduled = ss().scheduledMessages.all().find((m) => m.channel_id === ch.channel_id && m.scheduled_message_id === scheduledMessageId);
@@ -754,8 +754,8 @@ function chatRoutes(ctx) {
754
754
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
755
755
  if (scopeError) return scopeError;
756
756
  const body = await parseSlackBody(c);
757
- const channel = typeof body.channel === "string" ? body.channel : "";
758
- const cursor = typeof body.cursor === "string" ? body.cursor : "";
757
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
758
+ const cursor2 = typeof body.cursor === "string" ? body.cursor : "";
759
759
  const requestedLimit = body.limit === void 0 ? 100 : Number(body.limit);
760
760
  const oldest = body.oldest === void 0 ? void 0 : Number(body.oldest);
761
761
  const latest = body.latest === void 0 ? void 0 : Number(body.latest);
@@ -768,22 +768,22 @@ function chatRoutes(ctx) {
768
768
  if (oldest !== void 0 && latest !== void 0 && oldest > latest) {
769
769
  return slackError(c, "invalid_arguments");
770
770
  }
771
- const limit = Math.min(Math.floor(requestedLimit), 1e3);
772
- const ch = channel ? findChannel(channel) : void 0;
773
- if (channel && !ch) return slackError(c, "channel_not_found");
771
+ const limit2 = Math.min(Math.floor(requestedLimit), 1e3);
772
+ const ch = channel2 ? findChannel(channel2) : void 0;
773
+ if (channel2 && !ch) return slackError(c, "channel_not_found");
774
774
  if (ch && !canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
775
775
  const allScheduled = ss().scheduledMessages.all().filter((msg) => isAuthoredByUser(msg, authUser)).filter((msg) => !ch || msg.channel_id === ch.channel_id).filter((msg) => {
776
776
  const messageChannel = ss().channels.findOneBy("channel_id", msg.channel_id);
777
777
  return messageChannel ? canAccessConversation(messageChannel, authUser) : false;
778
778
  }).filter((msg) => oldest === void 0 || msg.post_at >= oldest).filter((msg) => latest === void 0 || msg.post_at <= latest).sort((a, b) => a.post_at - b.post_at || a.scheduled_message_id.localeCompare(b.scheduled_message_id));
779
779
  let startIndex = 0;
780
- if (cursor) {
781
- const idx = allScheduled.findIndex((msg) => msg.scheduled_message_id === cursor);
780
+ if (cursor2) {
781
+ const idx = allScheduled.findIndex((msg) => msg.scheduled_message_id === cursor2);
782
782
  if (idx < 0) return slackError(c, "invalid_cursor");
783
783
  if (idx >= 0) startIndex = idx;
784
784
  }
785
- const page = allScheduled.slice(startIndex, startIndex + limit);
786
- const nextCursor = startIndex + limit < allScheduled.length ? allScheduled[startIndex + limit].scheduled_message_id : "";
785
+ const page = allScheduled.slice(startIndex, startIndex + limit2);
786
+ const nextCursor = startIndex + limit2 < allScheduled.length ? allScheduled[startIndex + limit2].scheduled_message_id : "";
787
787
  return slackOk(c, {
788
788
  scheduled_messages: page.map(formatSlackScheduledMessageListItem),
789
789
  response_metadata: { next_cursor: nextCursor }
@@ -795,10 +795,10 @@ function chatRoutes(ctx) {
795
795
  const scopeError = requireSlackScopes(c, store, ["chat:write"]);
796
796
  if (scopeError) return scopeError;
797
797
  const body = await parseSlackBody(c);
798
- const channel = typeof body.channel === "string" ? body.channel : "";
798
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
799
799
  const text = typeof body.text === "string" ? body.text : "";
800
- if (!channel) return slackError(c, "channel_not_found");
801
- const ch = findChannel(channel);
800
+ if (!channel2) return slackError(c, "channel_not_found");
801
+ const ch = findChannel(channel2);
802
802
  if (!ch) return slackError(c, "channel_not_found");
803
803
  if (ch.is_archived) return slackError(c, "is_archived");
804
804
  if (!canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
@@ -846,18 +846,18 @@ function conversationsRoutes(ctx) {
846
846
  const getAuthSlackUser = (authUser) => ss().users.findOneBy("user_id", authUser.login) ?? ss().users.findOneBy("name", authUser.login);
847
847
  const getAuthUserId = (authUser) => getAuthSlackUser(authUser)?.user_id ?? authUser.login;
848
848
  const memberAliases = (user, userId) => new Set([userId, user?.name].filter((value) => Boolean(value)));
849
- const getChannelMemberKey = (channel, user, userId) => {
849
+ const getChannelMemberKey = (channel2, user, userId) => {
850
850
  const aliases = memberAliases(user, userId);
851
- return channel.members.find((member) => aliases.has(member));
851
+ return channel2.members.find((member) => aliases.has(member));
852
852
  };
853
- const isChannelMember = (channel, user, userId) => getChannelMemberKey(channel, user, userId) !== void 0;
854
- const canReadConversation = (channel, user, userId) => !channel.is_private || isChannelMember(channel, user, userId);
853
+ const isChannelMember = (channel2, user, userId) => getChannelMemberKey(channel2, user, userId) !== void 0;
854
+ const canReadConversation = (channel2, user, userId) => !channel2.is_private || isChannelMember(channel2, user, userId);
855
855
  const visibleFileChannelIds = (file, authUser) => {
856
856
  const authSlackUser = getAuthSlackUser(authUser);
857
857
  const authUserId = authSlackUser?.user_id ?? authUser.login;
858
858
  return fileChannels(file).filter((channelId) => {
859
- const channel = ss().channels.findOneBy("channel_id", channelId);
860
- return channel ? canReadConversation(channel, authSlackUser, authUserId) : false;
859
+ const channel2 = ss().channels.findOneBy("channel_id", channelId);
860
+ return channel2 ? canReadConversation(channel2, authSlackUser, authUserId) : false;
861
861
  });
862
862
  };
863
863
  const visibleFileForAuth = (file, authUser) => {
@@ -892,10 +892,10 @@ function conversationsRoutes(ctx) {
892
892
  "slack"
893
893
  );
894
894
  };
895
- const insertAndDispatchMessageEvent = async (channel, user, message) => {
895
+ const insertAndDispatchMessageEvent = async (channel2, user, message) => {
896
896
  const msg = ss().messages.insert({
897
897
  ts: generateTs(),
898
- channel_id: channel.channel_id,
898
+ channel_id: channel2.channel_id,
899
899
  user,
900
900
  type: "message",
901
901
  ...message,
@@ -910,7 +910,7 @@ function conversationsRoutes(ctx) {
910
910
  type: "event_callback",
911
911
  event: {
912
912
  ...formatSlackMessage(msg),
913
- channel: channel.channel_id,
913
+ channel: channel2.channel_id,
914
914
  event_ts: msg.ts
915
915
  }
916
916
  },
@@ -918,29 +918,29 @@ function conversationsRoutes(ctx) {
918
918
  );
919
919
  return msg;
920
920
  };
921
- const dispatchMemberJoined = async (channel, user, inviter) => {
921
+ const dispatchMemberJoined = async (channel2, user, inviter) => {
922
922
  await dispatchConversationEvent("member_joined_channel", {
923
923
  user,
924
- channel: channel.channel_id,
925
- channel_type: channelTypeLetter(channel),
926
- team: channel.team_id,
924
+ channel: channel2.channel_id,
925
+ channel_type: channelTypeLetter(channel2),
926
+ team: channel2.team_id,
927
927
  ...inviter ? { inviter } : {}
928
928
  });
929
929
  };
930
- const dispatchMemberLeft = async (channel, user) => {
930
+ const dispatchMemberLeft = async (channel2, user) => {
931
931
  await dispatchConversationEvent("member_left_channel", {
932
932
  user,
933
- channel: channel.channel_id,
934
- channel_type: channelTypeLetter(channel),
935
- team: channel.team_id
933
+ channel: channel2.channel_id,
934
+ channel_type: channelTypeLetter(channel2),
935
+ team: channel2.team_id
936
936
  });
937
937
  };
938
938
  app.post("/api/conversations.list", async (c) => {
939
939
  const authUser = c.get("authUser");
940
940
  if (!authUser) return slackError(c, "not_authed");
941
941
  const body = await parseSlackBody(c);
942
- const limit = Math.min(Number(body.limit) || 100, 1e3);
943
- const cursor = typeof body.cursor === "string" ? body.cursor : "";
942
+ const limit2 = Math.min(Number(body.limit) || 100, 1e3);
943
+ const cursor2 = typeof body.cursor === "string" ? body.cursor : "";
944
944
  const excludeArchived = isTruthySlackBoolean(body.exclude_archived);
945
945
  const types = parseConversationTypes(body.types);
946
946
  const scopeError = requireSlackScopes(c, store, readScopesForConversationTypes(types));
@@ -949,12 +949,12 @@ function conversationsRoutes(ctx) {
949
949
  const authUserId = getAuthUserId(authUser);
950
950
  const allChannels = ss().channels.all().filter((ch) => matchesConversationTypes(ch, types)).filter((ch) => canReadConversation(ch, authSlackUser, authUserId)).filter((ch) => !excludeArchived || !ch.is_archived);
951
951
  let startIndex = 0;
952
- if (cursor) {
953
- const idx = allChannels.findIndex((ch) => ch.channel_id === cursor);
952
+ if (cursor2) {
953
+ const idx = allChannels.findIndex((ch) => ch.channel_id === cursor2);
954
954
  if (idx >= 0) startIndex = idx;
955
955
  }
956
- const page = allChannels.slice(startIndex, startIndex + limit);
957
- const nextCursor = startIndex + limit < allChannels.length ? allChannels[startIndex + limit].channel_id : "";
956
+ const page = allChannels.slice(startIndex, startIndex + limit2);
957
+ const nextCursor = startIndex + limit2 < allChannels.length ? allChannels[startIndex + limit2].channel_id : "";
958
958
  return slackOk(c, {
959
959
  channels: page.map((ch) => formatChannel(ch, authUserId, authSlackUser?.name)),
960
960
  response_metadata: { next_cursor: nextCursor }
@@ -964,8 +964,8 @@ function conversationsRoutes(ctx) {
964
964
  const authUser = c.get("authUser");
965
965
  if (!authUser) return slackError(c, "not_authed");
966
966
  const body = await parseSlackBody(c);
967
- const channel = typeof body.channel === "string" ? body.channel : "";
968
- const ch = ss().channels.findOneBy("channel_id", channel);
967
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
968
+ const ch = ss().channels.findOneBy("channel_id", channel2);
969
969
  if (!ch) return slackError(c, "channel_not_found");
970
970
  const scopeError = requireSlackScopes(c, store, [slackConversationReadScope(ch)]);
971
971
  if (scopeError) return scopeError;
@@ -1013,9 +1013,9 @@ function conversationsRoutes(ctx) {
1013
1013
  const authUser = c.get("authUser");
1014
1014
  if (!authUser) return slackError(c, "not_authed");
1015
1015
  const body = await parseSlackBody(c);
1016
- const channel = typeof body.channel === "string" ? body.channel : "";
1017
- if (!channel) return slackError(c, "channel_not_found");
1018
- const ch = ss().channels.findOneBy("channel_id", channel);
1016
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1017
+ if (!channel2) return slackError(c, "channel_not_found");
1018
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1019
1019
  if (!ch) return slackError(c, "channel_not_found");
1020
1020
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1021
1021
  if (scopeError) return scopeError;
@@ -1040,9 +1040,9 @@ function conversationsRoutes(ctx) {
1040
1040
  const authUser = c.get("authUser");
1041
1041
  if (!authUser) return slackError(c, "not_authed");
1042
1042
  const body = await parseSlackBody(c);
1043
- const channel = typeof body.channel === "string" ? body.channel : "";
1044
- if (!channel) return slackError(c, "channel_not_found");
1045
- const ch = ss().channels.findOneBy("channel_id", channel);
1043
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1044
+ if (!channel2) return slackError(c, "channel_not_found");
1045
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1046
1046
  if (!ch) return slackError(c, "channel_not_found");
1047
1047
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1048
1048
  if (scopeError) return scopeError;
@@ -1072,12 +1072,12 @@ function conversationsRoutes(ctx) {
1072
1072
  const authUser = c.get("authUser");
1073
1073
  if (!authUser) return slackError(c, "not_authed");
1074
1074
  const body = await parseSlackBody(c);
1075
- const channel = typeof body.channel === "string" ? body.channel : "";
1075
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1076
1076
  const name = normalizeChannelName(typeof body.name === "string" ? body.name : "");
1077
- if (!channel) return slackError(c, "channel_not_found");
1077
+ if (!channel2) return slackError(c, "channel_not_found");
1078
1078
  const nameError = validateChannelName(name);
1079
1079
  if (nameError) return slackError(c, nameError);
1080
- const ch = ss().channels.findOneBy("channel_id", channel);
1080
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1081
1081
  if (!ch) return slackError(c, "channel_not_found");
1082
1082
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1083
1083
  if (scopeError) return scopeError;
@@ -1113,12 +1113,12 @@ function conversationsRoutes(ctx) {
1113
1113
  const authUser = c.get("authUser");
1114
1114
  if (!authUser) return slackError(c, "not_authed");
1115
1115
  const body = await parseSlackBody(c);
1116
- const channel = typeof body.channel === "string" ? body.channel : "";
1116
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1117
1117
  const topic = typeof body.topic === "string" ? body.topic : void 0;
1118
- if (!channel) return slackError(c, "channel_not_found");
1118
+ if (!channel2) return slackError(c, "channel_not_found");
1119
1119
  if (topic === void 0) return slackError(c, "invalid_arguments");
1120
1120
  if (topic.length > 250) return slackError(c, "too_long");
1121
- const ch = ss().channels.findOneBy("channel_id", channel);
1121
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1122
1122
  if (!ch) return slackError(c, "channel_not_found");
1123
1123
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1124
1124
  if (scopeError) return scopeError;
@@ -1142,12 +1142,12 @@ function conversationsRoutes(ctx) {
1142
1142
  const authUser = c.get("authUser");
1143
1143
  if (!authUser) return slackError(c, "not_authed");
1144
1144
  const body = await parseSlackBody(c);
1145
- const channel = typeof body.channel === "string" ? body.channel : "";
1145
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1146
1146
  const purpose = typeof body.purpose === "string" ? body.purpose : void 0;
1147
- if (!channel) return slackError(c, "channel_not_found");
1147
+ if (!channel2) return slackError(c, "channel_not_found");
1148
1148
  if (purpose === void 0) return slackError(c, "invalid_arguments");
1149
1149
  if (purpose.length > 250) return slackError(c, "too_long");
1150
- const ch = ss().channels.findOneBy("channel_id", channel);
1150
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1151
1151
  if (!ch) return slackError(c, "channel_not_found");
1152
1152
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1153
1153
  if (scopeError) return scopeError;
@@ -1171,26 +1171,26 @@ function conversationsRoutes(ctx) {
1171
1171
  const authUser = c.get("authUser");
1172
1172
  if (!authUser) return slackError(c, "not_authed");
1173
1173
  const body = await parseSlackBody(c);
1174
- const channel = typeof body.channel === "string" ? body.channel : "";
1175
- const limit = Math.min(Number(body.limit) || 100, 1e3);
1176
- const cursor = typeof body.cursor === "string" ? body.cursor : "";
1177
- if (!channel) return slackError(c, "channel_not_found");
1178
- const ch = ss().channels.findOneBy("channel_id", channel);
1174
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1175
+ const limit2 = Math.min(Number(body.limit) || 100, 1e3);
1176
+ const cursor2 = typeof body.cursor === "string" ? body.cursor : "";
1177
+ if (!channel2) return slackError(c, "channel_not_found");
1178
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1179
1179
  if (!ch) return slackError(c, "channel_not_found");
1180
1180
  const scopeError = requireSlackScopes(c, store, [slackConversationHistoryScope(ch)]);
1181
1181
  if (scopeError) return scopeError;
1182
1182
  const authSlackUser = getAuthSlackUser(authUser);
1183
1183
  const authUserId = getAuthUserId(authUser);
1184
1184
  if (!canReadConversation(ch, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
1185
- const allMessages = ss().messages.findBy("channel_id", channel).filter((m) => !m.thread_ts || m.thread_ts === m.ts).sort((a, b) => b.ts > a.ts ? 1 : -1);
1185
+ const allMessages = ss().messages.findBy("channel_id", channel2).filter((m) => !m.thread_ts || m.thread_ts === m.ts).sort((a, b) => b.ts > a.ts ? 1 : -1);
1186
1186
  let startIndex = 0;
1187
- if (cursor) {
1188
- const idx = allMessages.findIndex((m) => m.ts === cursor);
1187
+ if (cursor2) {
1188
+ const idx = allMessages.findIndex((m) => m.ts === cursor2);
1189
1189
  if (idx >= 0) startIndex = idx;
1190
1190
  }
1191
- const page = allMessages.slice(startIndex, startIndex + limit);
1192
- const hasMore = startIndex + limit < allMessages.length;
1193
- const nextCursor = hasMore ? allMessages[startIndex + limit].ts : "";
1191
+ const page = allMessages.slice(startIndex, startIndex + limit2);
1192
+ const hasMore = startIndex + limit2 < allMessages.length;
1193
+ const nextCursor = hasMore ? allMessages[startIndex + limit2].ts : "";
1194
1194
  return slackOk(c, {
1195
1195
  messages: page.map((message) => formatSlackMessageForAuth(message, authUser)),
1196
1196
  has_more: hasMore,
@@ -1201,17 +1201,17 @@ function conversationsRoutes(ctx) {
1201
1201
  const authUser = c.get("authUser");
1202
1202
  if (!authUser) return slackError(c, "not_authed");
1203
1203
  const body = await parseSlackBody(c);
1204
- const channel = typeof body.channel === "string" ? body.channel : "";
1204
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1205
1205
  const ts = typeof body.ts === "string" ? body.ts : "";
1206
- if (!channel || !ts) return slackError(c, "channel_not_found");
1207
- const ch = ss().channels.findOneBy("channel_id", channel);
1206
+ if (!channel2 || !ts) return slackError(c, "channel_not_found");
1207
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1208
1208
  if (!ch) return slackError(c, "channel_not_found");
1209
1209
  const scopeError = requireSlackScopes(c, store, [slackConversationHistoryScope(ch)]);
1210
1210
  if (scopeError) return scopeError;
1211
1211
  const authSlackUser = getAuthSlackUser(authUser);
1212
1212
  const authUserId = getAuthUserId(authUser);
1213
1213
  if (!canReadConversation(ch, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
1214
- const allMessages = ss().messages.findBy("channel_id", channel).filter((m) => m.ts === ts || m.thread_ts === ts).sort((a, b) => a.ts > b.ts ? 1 : -1);
1214
+ const allMessages = ss().messages.findBy("channel_id", channel2).filter((m) => m.ts === ts || m.thread_ts === ts).sort((a, b) => a.ts > b.ts ? 1 : -1);
1215
1215
  return slackOk(c, {
1216
1216
  messages: allMessages.map((message) => formatSlackMessageForAuth(message, authUser)),
1217
1217
  has_more: false
@@ -1221,8 +1221,8 @@ function conversationsRoutes(ctx) {
1221
1221
  const authUser = c.get("authUser");
1222
1222
  if (!authUser) return slackError(c, "not_authed");
1223
1223
  const body = await parseSlackBody(c);
1224
- const channel = typeof body.channel === "string" ? body.channel : "";
1225
- const ch = ss().channels.findOneBy("channel_id", channel);
1224
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1225
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1226
1226
  if (!ch) return slackError(c, "channel_not_found");
1227
1227
  const scopeError = requireSlackScopes(c, store, [slackConversationJoinScope(ch)]);
1228
1228
  if (scopeError) return scopeError;
@@ -1241,15 +1241,15 @@ function conversationsRoutes(ctx) {
1241
1241
  });
1242
1242
  await dispatchMemberJoined(updated2, authUserId);
1243
1243
  }
1244
- const updated = ss().channels.findOneBy("channel_id", channel);
1244
+ const updated = ss().channels.findOneBy("channel_id", channel2);
1245
1245
  return slackOk(c, { channel: formatChannel(updated, authUserId, authSlackUser?.name) });
1246
1246
  });
1247
1247
  app.post("/api/conversations.leave", async (c) => {
1248
1248
  const authUser = c.get("authUser");
1249
1249
  if (!authUser) return slackError(c, "not_authed");
1250
1250
  const body = await parseSlackBody(c);
1251
- const channel = typeof body.channel === "string" ? body.channel : "";
1252
- const ch = ss().channels.findOneBy("channel_id", channel);
1251
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1252
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1253
1253
  if (!ch) return slackError(c, "channel_not_found");
1254
1254
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1255
1255
  if (scopeError) return scopeError;
@@ -1273,12 +1273,12 @@ function conversationsRoutes(ctx) {
1273
1273
  const authUser = c.get("authUser");
1274
1274
  if (!authUser) return slackError(c, "not_authed");
1275
1275
  const body = await parseSlackBody(c);
1276
- const channel = typeof body.channel === "string" ? body.channel : "";
1276
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1277
1277
  const users = parseUserList(body.users);
1278
- if (!channel) return slackError(c, "channel_not_found");
1278
+ if (!channel2) return slackError(c, "channel_not_found");
1279
1279
  if (users.length === 0) return slackError(c, "user_not_found");
1280
1280
  if (users.length > 100) return slackError(c, "too_many_users");
1281
- const ch = ss().channels.findOneBy("channel_id", channel);
1281
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1282
1282
  if (!ch) return slackError(c, "channel_not_found");
1283
1283
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1284
1284
  if (scopeError) return scopeError;
@@ -1318,11 +1318,11 @@ function conversationsRoutes(ctx) {
1318
1318
  const authUser = c.get("authUser");
1319
1319
  if (!authUser) return slackError(c, "not_authed");
1320
1320
  const body = await parseSlackBody(c);
1321
- const channel = typeof body.channel === "string" ? body.channel : "";
1321
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1322
1322
  const user = typeof body.user === "string" ? body.user : "";
1323
- if (!channel) return slackError(c, "channel_not_found");
1323
+ if (!channel2) return slackError(c, "channel_not_found");
1324
1324
  if (!user) return slackError(c, "user_not_found");
1325
- const ch = ss().channels.findOneBy("channel_id", channel);
1325
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1326
1326
  if (!ch) return slackError(c, "channel_not_found");
1327
1327
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1328
1328
  if (scopeError) return scopeError;
@@ -1350,14 +1350,14 @@ function conversationsRoutes(ctx) {
1350
1350
  const authUser = c.get("authUser");
1351
1351
  if (!authUser) return slackError(c, "not_authed");
1352
1352
  const body = await parseSlackBody(c);
1353
- const channel = typeof body.channel === "string" ? body.channel : "";
1353
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1354
1354
  const users = parseUserList(body.users);
1355
1355
  const returnIm = isTruthySlackBoolean(body.return_im);
1356
1356
  const preventCreation = isTruthySlackBoolean(body.prevent_creation);
1357
1357
  const authUserId = getAuthUserId(authUser);
1358
1358
  const authSlackUser = getAuthSlackUser(authUser);
1359
- if (channel) {
1360
- const existing2 = ss().channels.findOneBy("channel_id", channel);
1359
+ if (channel2) {
1360
+ const existing2 = ss().channels.findOneBy("channel_id", channel2);
1361
1361
  if (!existing2 || !existing2.is_im && !existing2.is_mpim) return slackError(c, "channel_not_found");
1362
1362
  const scopeError2 = requireSlackScopes(c, store, [slackConversationWriteScope(existing2)]);
1363
1363
  if (scopeError2) return scopeError2;
@@ -1427,9 +1427,9 @@ function conversationsRoutes(ctx) {
1427
1427
  const authUser = c.get("authUser");
1428
1428
  if (!authUser) return slackError(c, "not_authed");
1429
1429
  const body = await parseSlackBody(c);
1430
- const channel = typeof body.channel === "string" ? body.channel : "";
1431
- if (!channel) return slackError(c, "channel_not_found");
1432
- const ch = ss().channels.findOneBy("channel_id", channel);
1430
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1431
+ if (!channel2) return slackError(c, "channel_not_found");
1432
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1433
1433
  if (!ch || !ch.is_im && !ch.is_mpim) return slackError(c, "channel_not_found");
1434
1434
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1435
1435
  if (scopeError) return scopeError;
@@ -1447,11 +1447,11 @@ function conversationsRoutes(ctx) {
1447
1447
  const authUser = c.get("authUser");
1448
1448
  if (!authUser) return slackError(c, "not_authed");
1449
1449
  const body = await parseSlackBody(c);
1450
- const channel = typeof body.channel === "string" ? body.channel : "";
1450
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1451
1451
  const ts = typeof body.ts === "string" ? body.ts : "";
1452
- if (!channel) return slackError(c, "channel_not_found");
1452
+ if (!channel2) return slackError(c, "channel_not_found");
1453
1453
  if (!ts) return slackError(c, "invalid_ts");
1454
- const ch = ss().channels.findOneBy("channel_id", channel);
1454
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1455
1455
  if (!ch) return slackError(c, "channel_not_found");
1456
1456
  const scopeError = requireSlackScopes(c, store, [slackConversationWriteScope(ch)]);
1457
1457
  if (scopeError) return scopeError;
@@ -1468,8 +1468,8 @@ function conversationsRoutes(ctx) {
1468
1468
  const authUser = c.get("authUser");
1469
1469
  if (!authUser) return slackError(c, "not_authed");
1470
1470
  const body = await parseSlackBody(c);
1471
- const channel = typeof body.channel === "string" ? body.channel : "";
1472
- const ch = ss().channels.findOneBy("channel_id", channel);
1471
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1472
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1473
1473
  if (!ch) return slackError(c, "channel_not_found");
1474
1474
  const scopeError = requireSlackScopes(c, store, [slackConversationReadScope(ch)]);
1475
1475
  if (scopeError) return scopeError;
@@ -1616,16 +1616,16 @@ function usersRoutes(ctx) {
1616
1616
  const scopeError = requireSlackScopes(c, store, ["users:read"]);
1617
1617
  if (scopeError) return scopeError;
1618
1618
  const body = await parseSlackBody(c);
1619
- const limit = Math.min(Number(body.limit) || 100, 1e3);
1620
- const cursor = typeof body.cursor === "string" ? body.cursor : "";
1619
+ const limit2 = Math.min(Number(body.limit) || 100, 1e3);
1620
+ const cursor2 = typeof body.cursor === "string" ? body.cursor : "";
1621
1621
  const allUsers = ss().users.all().filter((u) => !u.deleted);
1622
1622
  let startIndex = 0;
1623
- if (cursor) {
1624
- const idx = allUsers.findIndex((u) => u.user_id === cursor);
1623
+ if (cursor2) {
1624
+ const idx = allUsers.findIndex((u) => u.user_id === cursor2);
1625
1625
  if (idx >= 0) startIndex = idx;
1626
1626
  }
1627
- const page = allUsers.slice(startIndex, startIndex + limit);
1628
- const nextCursor = startIndex + limit < allUsers.length ? allUsers[startIndex + limit].user_id : "";
1627
+ const page = allUsers.slice(startIndex, startIndex + limit2);
1628
+ const nextCursor = startIndex + limit2 < allUsers.length ? allUsers[startIndex + limit2].user_id : "";
1629
1629
  return slackOk(c, {
1630
1630
  members: page.map((user) => formatUser(user, canExposeEmail(c))),
1631
1631
  response_metadata: { next_cursor: nextCursor }
@@ -1861,25 +1861,25 @@ function reactionsRoutes(ctx) {
1861
1861
  const user = getAuthSlackUser(authUser);
1862
1862
  return new Set([authUser.login, user?.user_id, user?.name].filter((value) => Boolean(value)));
1863
1863
  };
1864
- const isAuthChannelMember = (channel, authUser) => {
1864
+ const isAuthChannelMember = (channel2, authUser) => {
1865
1865
  const user = getAuthSlackUser(authUser);
1866
1866
  const userId = user?.user_id ?? authUser.login;
1867
- return channel.members.includes(userId) || (user ? channel.members.includes(user.name) : false);
1867
+ return channel2.members.includes(userId) || (user ? channel2.members.includes(user.name) : false);
1868
1868
  };
1869
- const canAccessConversation = (channel, authUser) => !channel.is_private || isAuthChannelMember(channel, authUser);
1869
+ const canAccessConversation = (channel2, authUser) => !channel2.is_private || isAuthChannelMember(channel2, authUser);
1870
1870
  app.post("/api/reactions.add", async (c) => {
1871
1871
  const authUser = c.get("authUser");
1872
1872
  if (!authUser) return slackError(c, "not_authed");
1873
1873
  const scopeError = requireSlackScopes(c, store, ["reactions:write"]);
1874
1874
  if (scopeError) return scopeError;
1875
1875
  const body = await parseSlackBody(c);
1876
- const channel = typeof body.channel === "string" ? body.channel : "";
1876
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1877
1877
  const timestamp = typeof body.timestamp === "string" ? body.timestamp : "";
1878
1878
  const name = typeof body.name === "string" ? body.name : "";
1879
1879
  if (!name) return slackError(c, "invalid_name");
1880
- const ch = ss().channels.findOneBy("channel_id", channel);
1880
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1881
1881
  if (ch && !canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
1882
- const msg = ss().messages.all().find((m) => m.ts === timestamp && m.channel_id === channel);
1882
+ const msg = ss().messages.all().find((m) => m.ts === timestamp && m.channel_id === channel2);
1883
1883
  if (!msg) return slackError(c, "message_not_found");
1884
1884
  const reactions = [...msg.reactions];
1885
1885
  const existing = reactions.find((r) => r.name === name);
@@ -1904,7 +1904,7 @@ function reactionsRoutes(ctx) {
1904
1904
  type: "reaction_added",
1905
1905
  user: authUserId,
1906
1906
  reaction: name,
1907
- item: { type: "message", channel, ts: timestamp }
1907
+ item: { type: "message", channel: channel2, ts: timestamp }
1908
1908
  }
1909
1909
  },
1910
1910
  "slack"
@@ -1917,13 +1917,13 @@ function reactionsRoutes(ctx) {
1917
1917
  const scopeError = requireSlackScopes(c, store, ["reactions:write"]);
1918
1918
  if (scopeError) return scopeError;
1919
1919
  const body = await parseSlackBody(c);
1920
- const channel = typeof body.channel === "string" ? body.channel : "";
1920
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1921
1921
  const timestamp = typeof body.timestamp === "string" ? body.timestamp : "";
1922
1922
  const name = typeof body.name === "string" ? body.name : "";
1923
1923
  if (!name) return slackError(c, "invalid_name");
1924
- const ch = ss().channels.findOneBy("channel_id", channel);
1924
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1925
1925
  if (ch && !canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
1926
- const msg = ss().messages.all().find((m) => m.ts === timestamp && m.channel_id === channel);
1926
+ const msg = ss().messages.all().find((m) => m.ts === timestamp && m.channel_id === channel2);
1927
1927
  if (!msg) return slackError(c, "message_not_found");
1928
1928
  const reactions = [...msg.reactions];
1929
1929
  const existing = reactions.find((r) => r.name === name);
@@ -1945,7 +1945,7 @@ function reactionsRoutes(ctx) {
1945
1945
  type: "reaction_removed",
1946
1946
  user: authUserId,
1947
1947
  reaction: name,
1948
- item: { type: "message", channel, ts: timestamp }
1948
+ item: { type: "message", channel: channel2, ts: timestamp }
1949
1949
  }
1950
1950
  },
1951
1951
  "slack"
@@ -1958,11 +1958,11 @@ function reactionsRoutes(ctx) {
1958
1958
  const scopeError = requireSlackScopes(c, store, ["reactions:read"]);
1959
1959
  if (scopeError) return scopeError;
1960
1960
  const body = await parseSlackBody(c);
1961
- const channel = typeof body.channel === "string" ? body.channel : "";
1961
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
1962
1962
  const timestamp = typeof body.timestamp === "string" ? body.timestamp : "";
1963
- const ch = ss().channels.findOneBy("channel_id", channel);
1963
+ const ch = ss().channels.findOneBy("channel_id", channel2);
1964
1964
  if (ch && !canAccessConversation(ch, authUser)) return slackError(c, "not_in_channel");
1965
- const msg = ss().messages.all().find((m) => m.ts === timestamp && m.channel_id === channel);
1965
+ const msg = ss().messages.all().find((m) => m.ts === timestamp && m.channel_id === channel2);
1966
1966
  if (!msg) return slackError(c, "message_not_found");
1967
1967
  return slackOk(c, {
1968
1968
  type: "message",
@@ -2727,7 +2727,7 @@ function slugifyBotName(value) {
2727
2727
  function webhookRoutes(ctx) {
2728
2728
  const { app, store, webhooks } = ctx;
2729
2729
  const ss = () => getSlackStore(store);
2730
- const findChannel = (channel) => ss().channels.findOneBy("channel_id", channel) ?? ss().channels.all().find((ch) => !ch.is_im && !ch.is_mpim && ch.name === channel);
2730
+ const findChannel = (channel2) => ss().channels.findOneBy("channel_id", channel2) ?? ss().channels.all().find((ch) => !ch.is_im && !ch.is_mpim && ch.name === channel2);
2731
2731
  app.post("/services/:teamId/:botId/:token", async (c) => {
2732
2732
  const contentType = c.req.header("Content-Type") ?? "";
2733
2733
  const rawText = await c.req.text();
@@ -2813,14 +2813,14 @@ function filesRoutes(ctx) {
2813
2813
  const serviceBaseUrl = baseUrl.replace(/\/$/, "");
2814
2814
  const getAuthSlackUser = (authUser) => ss().users.findOneBy("user_id", authUser.login) ?? ss().users.findOneBy("name", authUser.login);
2815
2815
  const getAuthUserId = (authUser) => getAuthSlackUser(authUser)?.user_id ?? authUser.login;
2816
- const isChannelMember = (channel, user, userId) => channel.members.includes(userId) || (user ? channel.members.includes(user.name) : false);
2817
- const canReadConversation = (channel, user, userId) => !channel.is_private || isChannelMember(channel, user, userId);
2816
+ const isChannelMember = (channel2, user, userId) => channel2.members.includes(userId) || (user ? channel2.members.includes(user.name) : false);
2817
+ const canReadConversation = (channel2, user, userId) => !channel2.is_private || isChannelMember(channel2, user, userId);
2818
2818
  const visibleFileChannelIds = (file, authUser) => {
2819
2819
  const authSlackUser = getAuthSlackUser(authUser);
2820
2820
  const authUserId = authSlackUser?.user_id ?? authUser.login;
2821
2821
  return fileChannels2(file).filter((channelId) => {
2822
- const channel = ss().channels.findOneBy("channel_id", channelId);
2823
- return channel ? canReadConversation(channel, authSlackUser, authUserId) : false;
2822
+ const channel2 = ss().channels.findOneBy("channel_id", channelId);
2823
+ return channel2 ? canReadConversation(channel2, authSlackUser, authUserId) : false;
2824
2824
  });
2825
2825
  };
2826
2826
  const canAccessFile = (file, authUser) => {
@@ -2852,7 +2852,7 @@ function filesRoutes(ctx) {
2852
2852
  const authUserId = authSlackUser?.user_id ?? authUser.login;
2853
2853
  return file.user === authUserId || authSlackUser?.is_admin === true;
2854
2854
  };
2855
- const findChannel = (channel) => ss().channels.findOneBy("channel_id", channel) ?? ss().channels.all().find((ch) => !ch.is_im && !ch.is_mpim && ch.name === channel);
2855
+ const findChannel = (channel2) => ss().channels.findOneBy("channel_id", channel2) ?? ss().channels.all().find((ch) => !ch.is_im && !ch.is_mpim && ch.name === channel2);
2856
2856
  const findDirectMessage = (authUserId, userId) => {
2857
2857
  const members = [authUserId, userId].sort();
2858
2858
  return ss().channels.all().find(
@@ -2888,11 +2888,11 @@ function filesRoutes(ctx) {
2888
2888
  last_read: {}
2889
2889
  });
2890
2890
  };
2891
- const resolveShareTarget = (authUser, channel) => {
2892
- const existingChannel = findChannel(channel);
2891
+ const resolveShareTarget = (authUser, channel2) => {
2892
+ const existingChannel = findChannel(channel2);
2893
2893
  if (existingChannel) return { key: existingChannel.channel_id, channel: existingChannel };
2894
- if (!channel.startsWith("U")) return void 0;
2895
- const targetUser = ss().users.findOneBy("user_id", channel);
2894
+ if (!channel2.startsWith("U")) return void 0;
2895
+ const targetUser = ss().users.findOneBy("user_id", channel2);
2896
2896
  if (!targetUser || targetUser.deleted) return void 0;
2897
2897
  const authUserId = getAuthUserId(authUser);
2898
2898
  if (targetUser.user_id === authUserId) return void 0;
@@ -2982,9 +2982,9 @@ function filesRoutes(ctx) {
2982
2982
  }
2983
2983
  const targets = [];
2984
2984
  for (const target of targetRefs) {
2985
- const channel = target.channel ?? findOrCreateDirectMessage(authUser, target.directUserId ?? "");
2986
- if (!channel) return slackError(c, "channel_not_found");
2987
- targets.push(channel);
2985
+ const channel2 = target.channel ?? findOrCreateDirectMessage(authUser, target.directUserId ?? "");
2986
+ if (!channel2) return slackError(c, "channel_not_found");
2987
+ targets.push(channel2);
2988
2988
  }
2989
2989
  const completedFiles = [];
2990
2990
  for (let index = 0; index < requestedFiles.length; index++) {
@@ -3007,10 +3007,10 @@ function filesRoutes(ctx) {
3007
3007
  return slackOk(c, { files: sharedFiles.map((file) => formatSlackFileForAuth(file, authUser)) });
3008
3008
  async function shareFiles(channels, files) {
3009
3009
  const updatedFiles = [...files];
3010
- for (const channel of channels) {
3010
+ for (const channel2 of channels) {
3011
3011
  const msg = ss().messages.insert({
3012
3012
  ts: generateTs(),
3013
- channel_id: channel.channel_id,
3013
+ channel_id: channel2.channel_id,
3014
3014
  user: authUserId,
3015
3015
  text: initialComment,
3016
3016
  type: "message",
@@ -3023,12 +3023,12 @@ function filesRoutes(ctx) {
3023
3023
  reply_users: [],
3024
3024
  reactions: []
3025
3025
  });
3026
- updateParentThread(channel.channel_id, threadTs, authUserId);
3026
+ updateParentThread(channel2.channel_id, threadTs, authUserId);
3027
3027
  const messageFiles = [];
3028
3028
  for (const file of updatedFiles) {
3029
- const shared = updateFileShare(file, channel, msg, authUserId);
3029
+ const shared = updateFileShare(file, channel2, msg, authUserId);
3030
3030
  messageFiles.push(shared);
3031
- await dispatchFileEvent(webhooks, "file_shared", shared, { channel_id: channel.channel_id });
3031
+ await dispatchFileEvent(webhooks, "file_shared", shared, { channel_id: channel2.channel_id });
3032
3032
  }
3033
3033
  const updatedMessage = ss().messages.update(msg.id, { files: messageFiles });
3034
3034
  await webhooks.dispatch(
@@ -3040,7 +3040,7 @@ function filesRoutes(ctx) {
3040
3040
  ...formatSlackMessage(updatedMessage),
3041
3041
  type: "message",
3042
3042
  subtype: "file_share",
3043
- channel: channel.channel_id
3043
+ channel: channel2.channel_id
3044
3044
  }
3045
3045
  },
3046
3046
  "slack"
@@ -3074,14 +3074,14 @@ function filesRoutes(ctx) {
3074
3074
  const scopeError = requireSlackScopes(c, store, ["files:read"]);
3075
3075
  if (scopeError) return scopeError;
3076
3076
  const body = await parseSlackRequest2(c);
3077
- const channel = typeof body.channel === "string" ? body.channel : "";
3077
+ const channel2 = typeof body.channel === "string" ? body.channel : "";
3078
3078
  const user = typeof body.user === "string" ? body.user : "";
3079
3079
  const types = typeof body.types === "string" ? body.types : "all";
3080
3080
  const tsFrom = body.ts_from === void 0 ? void 0 : Number(body.ts_from);
3081
3081
  const tsTo = body.ts_to === void 0 ? void 0 : Number(body.ts_to);
3082
3082
  const page = Math.max(1, Math.floor(Number(body.page) || 1));
3083
3083
  const count = Math.min(Math.max(1, Math.floor(Number(body.count) || 100)), 1e3);
3084
- const files = ss().files.all().filter((file) => !file.deleted).filter((file) => canAccessFile(file, authUser)).filter((file) => !channel || canAccessFileInChannel(file, authUser, channel)).filter((file) => !user || file.user === user).filter((file) => tsFrom === void 0 || file.created >= tsFrom).filter((file) => tsTo === void 0 || file.created <= tsTo).filter((file) => matchesFileTypes(file, types)).sort((a, b) => b.created - a.created || b.file_id.localeCompare(a.file_id));
3084
+ const files = ss().files.all().filter((file) => !file.deleted).filter((file) => canAccessFile(file, authUser)).filter((file) => !channel2 || canAccessFileInChannel(file, authUser, channel2)).filter((file) => !user || file.user === user).filter((file) => tsFrom === void 0 || file.created >= tsFrom).filter((file) => tsTo === void 0 || file.created <= tsTo).filter((file) => matchesFileTypes(file, types)).sort((a, b) => b.created - a.created || b.file_id.localeCompare(a.file_id));
3085
3085
  const start = (page - 1) * count;
3086
3086
  const paged = files.slice(start, start + count);
3087
3087
  return slackOk(c, {
@@ -3148,11 +3148,11 @@ function filesRoutes(ctx) {
3148
3148
  reply_users: replyUsers
3149
3149
  });
3150
3150
  }
3151
- function updateFileShare(file, channel, msg, userId) {
3151
+ function updateFileShare(file, channel2, msg, userId) {
3152
3152
  const share = {
3153
3153
  ts: msg.ts,
3154
- channel_name: channel.name,
3155
- team_id: channel.team_id,
3154
+ channel_name: channel2.name,
3155
+ team_id: channel2.team_id,
3156
3156
  share_user_id: userId,
3157
3157
  source: "UPLOAD",
3158
3158
  thread_ts: msg.thread_ts,
@@ -3161,15 +3161,15 @@ function filesRoutes(ctx) {
3161
3161
  reply_users_count: 0,
3162
3162
  is_silent_share: false
3163
3163
  };
3164
- const shareBucket = channel.is_private ? "private" : "public";
3164
+ const shareBucket = channel2.is_private ? "private" : "public";
3165
3165
  const shares = {
3166
3166
  ...file.shares,
3167
3167
  [shareBucket]: {
3168
3168
  ...file.shares[shareBucket] ?? {},
3169
- [channel.channel_id]: [...file.shares[shareBucket]?.[channel.channel_id] ?? [], share]
3169
+ [channel2.channel_id]: [...file.shares[shareBucket]?.[channel2.channel_id] ?? [], share]
3170
3170
  }
3171
3171
  };
3172
- const channelFields = nextFileChannelFields(file, channel);
3172
+ const channelFields = nextFileChannelFields(file, channel2);
3173
3173
  return ss().files.update(file.id, {
3174
3174
  ...channelFields,
3175
3175
  shares,
@@ -3240,7 +3240,7 @@ function parseDestinationChannels(channelId, channels) {
3240
3240
  const values = [];
3241
3241
  if (typeof channelId === "string" && channelId.trim()) values.push(channelId.trim());
3242
3242
  if (typeof channels === "string" && channels.trim()) {
3243
- values.push(...channels.split(",").map((channel) => channel.trim()));
3243
+ values.push(...channels.split(",").map((channel2) => channel2.trim()));
3244
3244
  }
3245
3245
  return [...new Set(values.filter(Boolean))];
3246
3246
  }
@@ -3298,13 +3298,13 @@ function buildSlackFile(session, options) {
3298
3298
  content_base64: session.content_base64
3299
3299
  };
3300
3300
  }
3301
- function nextFileChannelFields(file, channel) {
3301
+ function nextFileChannelFields(file, channel2) {
3302
3302
  const channels = new Set(file.channels);
3303
3303
  const groups = new Set(file.groups);
3304
3304
  const ims = new Set(file.ims);
3305
- if (channel.is_im || channel.is_mpim) ims.add(channel.channel_id);
3306
- else if (channel.is_private) groups.add(channel.channel_id);
3307
- else channels.add(channel.channel_id);
3305
+ if (channel2.is_im || channel2.is_mpim) ims.add(channel2.channel_id);
3306
+ else if (channel2.is_private) groups.add(channel2.channel_id);
3307
+ else channels.add(channel2.channel_id);
3308
3308
  return { channels: [...channels], groups: [...groups], ims: [...ims] };
3309
3309
  }
3310
3310
  function fileChannels2(file) {
@@ -3381,8 +3381,8 @@ function pinsRoutes(ctx) {
3381
3381
  const ss = () => getSlackStore(store);
3382
3382
  const getAuthSlackUser = (authUser) => ss().users.findOneBy("user_id", authUser.login) ?? ss().users.findOneBy("name", authUser.login);
3383
3383
  const getAuthUserId = (authUser) => getAuthSlackUser(authUser)?.user_id ?? authUser.login;
3384
- const isChannelMember = (channel, user, userId) => channel.members.includes(userId) || (user ? channel.members.includes(user.name) : false);
3385
- const canReadConversation = (channel, user, userId) => !channel.is_private || isChannelMember(channel, user, userId);
3384
+ const isChannelMember = (channel2, user, userId) => channel2.members.includes(userId) || (user ? channel2.members.includes(user.name) : false);
3385
+ const canReadConversation = (channel2, user, userId) => !channel2.is_private || isChannelMember(channel2, user, userId);
3386
3386
  const findPinnedMessage = (channelId, timestamp) => ss().messages.all().find((message) => message.channel_id === channelId && message.ts === timestamp);
3387
3387
  const findPin = (channelId, timestamp) => ss().pins.all().find((pin) => pin.channel_id === channelId && pin.message_ts === timestamp);
3388
3388
  app.post("/api/pins.add", async (c) => {
@@ -3396,26 +3396,26 @@ function pinsRoutes(ctx) {
3396
3396
  if (!channelId) return slackError(c, "channel_not_found");
3397
3397
  if (!timestamp) return slackError(c, "no_item_specified");
3398
3398
  if (!isSlackTimestamp(timestamp)) return slackError(c, "bad_timestamp");
3399
- const channel = ss().channels.findOneBy("channel_id", channelId);
3400
- if (!channel) return slackError(c, "channel_not_found");
3401
- if (channel.is_archived) return slackError(c, "is_archived");
3399
+ const channel2 = ss().channels.findOneBy("channel_id", channelId);
3400
+ if (!channel2) return slackError(c, "channel_not_found");
3401
+ if (channel2.is_archived) return slackError(c, "is_archived");
3402
3402
  const authSlackUser = getAuthSlackUser(authUser);
3403
3403
  const authUserId = getAuthUserId(authUser);
3404
- if (!isChannelMember(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3405
- const message = findPinnedMessage(channel.channel_id, timestamp);
3404
+ if (!isChannelMember(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3405
+ const message = findPinnedMessage(channel2.channel_id, timestamp);
3406
3406
  if (!message) return slackError(c, "message_not_found");
3407
- if (findPin(channel.channel_id, timestamp)) return slackError(c, "already_pinned");
3407
+ if (findPin(channel2.channel_id, timestamp)) return slackError(c, "already_pinned");
3408
3408
  const pin = ss().pins.insert({
3409
3409
  pin_id: generateSlackId("P"),
3410
- team_id: channel.team_id,
3411
- channel_id: channel.channel_id,
3410
+ team_id: channel2.team_id,
3411
+ channel_id: channel2.channel_id,
3412
3412
  message_ts: timestamp,
3413
3413
  created: Math.floor(Date.now() / 1e3),
3414
3414
  created_by: authUserId
3415
3415
  });
3416
3416
  await dispatchPinEvent("pin_added", {
3417
3417
  user: authUserId,
3418
- channel_id: channel.channel_id,
3418
+ channel_id: channel2.channel_id,
3419
3419
  item: formatPinItem(pin, message),
3420
3420
  event_ts: generateTs()
3421
3421
  });
@@ -3429,12 +3429,12 @@ function pinsRoutes(ctx) {
3429
3429
  const body = await parseSlackRequest3(c);
3430
3430
  const channelId = typeof body.channel === "string" ? body.channel : "";
3431
3431
  if (!channelId) return slackError(c, "channel_not_found");
3432
- const channel = ss().channels.findOneBy("channel_id", channelId);
3433
- if (!channel) return slackError(c, "channel_not_found");
3432
+ const channel2 = ss().channels.findOneBy("channel_id", channelId);
3433
+ if (!channel2) return slackError(c, "channel_not_found");
3434
3434
  const authSlackUser = getAuthSlackUser(authUser);
3435
3435
  const authUserId = getAuthUserId(authUser);
3436
- if (!canReadConversation(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3437
- const items = ss().pins.findBy("channel_id", channel.channel_id).sort((a, b) => b.created - a.created).flatMap((pin) => {
3436
+ if (!canReadConversation(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3437
+ const items = ss().pins.findBy("channel_id", channel2.channel_id).sort((a, b) => b.created - a.created).flatMap((pin) => {
3438
3438
  const message = findPinnedMessage(pin.channel_id, pin.message_ts);
3439
3439
  return message ? [formatPinItem(pin, message)] : [];
3440
3440
  });
@@ -3453,21 +3453,21 @@ function pinsRoutes(ctx) {
3453
3453
  if (!channelId) return slackError(c, "channel_not_found");
3454
3454
  if (!timestamp) return slackError(c, "no_item_specified");
3455
3455
  if (!isSlackTimestamp(timestamp)) return slackError(c, "bad_timestamp");
3456
- const channel = ss().channels.findOneBy("channel_id", channelId);
3457
- if (!channel) return slackError(c, "channel_not_found");
3458
- if (channel.is_archived) return slackError(c, "is_archived");
3456
+ const channel2 = ss().channels.findOneBy("channel_id", channelId);
3457
+ if (!channel2) return slackError(c, "channel_not_found");
3458
+ if (channel2.is_archived) return slackError(c, "is_archived");
3459
3459
  const authSlackUser = getAuthSlackUser(authUser);
3460
3460
  const authUserId = getAuthUserId(authUser);
3461
- if (!isChannelMember(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3462
- const pin = findPin(channel.channel_id, timestamp);
3463
- const message = findPinnedMessage(channel.channel_id, timestamp);
3461
+ if (!isChannelMember(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3462
+ const pin = findPin(channel2.channel_id, timestamp);
3463
+ const message = findPinnedMessage(channel2.channel_id, timestamp);
3464
3464
  if (!pin) return slackError(c, "no_pin");
3465
3465
  ss().pins.delete(pin.id);
3466
3466
  if (!message) return slackOk(c, {});
3467
- const hasPins = ss().pins.findBy("channel_id", channel.channel_id).length > 0;
3467
+ const hasPins = ss().pins.findBy("channel_id", channel2.channel_id).length > 0;
3468
3468
  await dispatchPinEvent("pin_removed", {
3469
3469
  user: authUserId,
3470
- channel_id: channel.channel_id,
3470
+ channel_id: channel2.channel_id,
3471
3471
  item: formatPinItem(pin, message),
3472
3472
  has_pins: hasPins,
3473
3473
  event_ts: generateTs()
@@ -3513,8 +3513,8 @@ function bookmarksRoutes(ctx) {
3513
3513
  const ss = () => getSlackStore(store);
3514
3514
  const getAuthSlackUser = (authUser) => ss().users.findOneBy("user_id", authUser.login) ?? ss().users.findOneBy("name", authUser.login);
3515
3515
  const getAuthUserId = (authUser) => getAuthSlackUser(authUser)?.user_id ?? authUser.login;
3516
- const isChannelMember = (channel, user, userId) => channel.members.includes(userId) || (user ? channel.members.includes(user.name) : false);
3517
- const canReadConversation = (channel, user, userId) => !channel.is_private || isChannelMember(channel, user, userId);
3516
+ const isChannelMember = (channel2, user, userId) => channel2.members.includes(userId) || (user ? channel2.members.includes(user.name) : false);
3517
+ const canReadConversation = (channel2, user, userId) => !channel2.is_private || isChannelMember(channel2, user, userId);
3518
3518
  app.post("/api/bookmarks.add", async (c) => {
3519
3519
  const authUser = c.get("authUser");
3520
3520
  if (!authUser) return slackError(c, "not_authed");
@@ -3522,27 +3522,27 @@ function bookmarksRoutes(ctx) {
3522
3522
  if (scopeError) return scopeError;
3523
3523
  const body = await parseSlackBody(c);
3524
3524
  const channelId = stringField(body.channel_id) || stringField(body.channel);
3525
- const channel = findBookmarkChannel(channelId);
3526
- if (!channel) return slackError(c, "channel_not_found");
3527
- if (channel.is_archived) return slackError(c, "is_archived");
3525
+ const channel2 = findBookmarkChannel(channelId);
3526
+ if (!channel2) return slackError(c, "channel_not_found");
3527
+ if (channel2.is_archived) return slackError(c, "is_archived");
3528
3528
  const authSlackUser = getAuthSlackUser(authUser);
3529
3529
  const authUserId = getAuthUserId(authUser);
3530
- if (!isChannelMember(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3530
+ if (!isChannelMember(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3531
3531
  const title = stringField(body.title).trim();
3532
3532
  const type = stringField(body.type);
3533
3533
  const link = stringField(body.link) || stringField(body.url);
3534
3534
  if (type !== "link") return slackError(c, "invalid_bookmark_type");
3535
3535
  if (!title || !link) return slackError(c, "invalid_arguments");
3536
3536
  if (!isValidBookmarkLink(link)) return slackError(c, "invalid_link");
3537
- if (ss().bookmarks.findBy("channel_id", channel.channel_id).length >= 100) {
3537
+ if (ss().bookmarks.findBy("channel_id", channel2.channel_id).length >= 100) {
3538
3538
  return slackError(c, "too_many_bookmarks");
3539
3539
  }
3540
3540
  const now = Math.floor(Date.now() / 1e3);
3541
3541
  const team = ss().teams.all()[0];
3542
3542
  const bookmark = ss().bookmarks.insert({
3543
3543
  bookmark_id: generateSlackId("Bk"),
3544
- team_id: team?.team_id ?? channel.team_id,
3545
- channel_id: channel.channel_id,
3544
+ team_id: team?.team_id ?? channel2.team_id,
3545
+ channel_id: channel2.channel_id,
3546
3546
  title,
3547
3547
  type: "link",
3548
3548
  link,
@@ -3551,9 +3551,9 @@ function bookmarksRoutes(ctx) {
3551
3551
  entity_id: null,
3552
3552
  date_created: now,
3553
3553
  date_updated: 0,
3554
- rank: bookmarkRank(channel.channel_id),
3554
+ rank: bookmarkRank(channel2.channel_id),
3555
3555
  last_updated_by_user_id: authUserId,
3556
- last_updated_by_team_id: team?.team_id ?? channel.team_id,
3556
+ last_updated_by_team_id: team?.team_id ?? channel2.team_id,
3557
3557
  shortcut_id: null,
3558
3558
  app_id: null,
3559
3559
  ...accessLevel(body.access_level) ? { access_level: accessLevel(body.access_level) } : {},
@@ -3569,13 +3569,13 @@ function bookmarksRoutes(ctx) {
3569
3569
  const body = await parseSlackBody(c);
3570
3570
  const channelId = stringField(body.channel_id) || stringField(body.channel);
3571
3571
  const bookmarkId = stringField(body.bookmark_id);
3572
- const channel = findBookmarkChannel(channelId);
3573
- if (!channel) return slackError(c, "channel_not_found");
3574
- if (channel.is_archived) return slackError(c, "is_archived");
3572
+ const channel2 = findBookmarkChannel(channelId);
3573
+ if (!channel2) return slackError(c, "channel_not_found");
3574
+ if (channel2.is_archived) return slackError(c, "is_archived");
3575
3575
  const authSlackUser = getAuthSlackUser(authUser);
3576
3576
  const authUserId = getAuthUserId(authUser);
3577
- if (!isChannelMember(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3578
- const bookmark = findBookmark(channel.channel_id, bookmarkId);
3577
+ if (!isChannelMember(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3578
+ const bookmark = findBookmark(channel2.channel_id, bookmarkId);
3579
3579
  if (!bookmark) return slackError(c, "not_found");
3580
3580
  const updates = {
3581
3581
  date_updated: Math.floor(Date.now() / 1e3),
@@ -3601,12 +3601,12 @@ function bookmarksRoutes(ctx) {
3601
3601
  if (scopeError) return scopeError;
3602
3602
  const body = await parseSlackBody(c);
3603
3603
  const channelId = stringField(body.channel_id) || stringField(body.channel);
3604
- const channel = findBookmarkChannel(channelId);
3605
- if (!channel) return slackError(c, "channel_not_found");
3604
+ const channel2 = findBookmarkChannel(channelId);
3605
+ if (!channel2) return slackError(c, "channel_not_found");
3606
3606
  const authSlackUser = getAuthSlackUser(authUser);
3607
3607
  const authUserId = getAuthUserId(authUser);
3608
- if (!canReadConversation(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3609
- const bookmarks = ss().bookmarks.findBy("channel_id", channel.channel_id).sort(compareSlackBookmarks).map(formatBookmark);
3608
+ if (!canReadConversation(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3609
+ const bookmarks = ss().bookmarks.findBy("channel_id", channel2.channel_id).sort(compareSlackBookmarks).map(formatBookmark);
3610
3610
  return slackOk(c, { bookmarks });
3611
3611
  });
3612
3612
  app.post("/api/bookmarks.remove", async (c) => {
@@ -3617,13 +3617,13 @@ function bookmarksRoutes(ctx) {
3617
3617
  const body = await parseSlackBody(c);
3618
3618
  const channelId = stringField(body.channel_id) || stringField(body.channel);
3619
3619
  const bookmarkId = stringField(body.bookmark_id);
3620
- const channel = findBookmarkChannel(channelId);
3621
- if (!channel) return slackError(c, "channel_not_found");
3622
- if (channel.is_archived) return slackError(c, "is_archived");
3620
+ const channel2 = findBookmarkChannel(channelId);
3621
+ if (!channel2) return slackError(c, "channel_not_found");
3622
+ if (channel2.is_archived) return slackError(c, "is_archived");
3623
3623
  const authSlackUser = getAuthSlackUser(authUser);
3624
3624
  const authUserId = getAuthUserId(authUser);
3625
- if (!isChannelMember(channel, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3626
- const bookmark = findBookmark(channel.channel_id, bookmarkId);
3625
+ if (!isChannelMember(channel2, authSlackUser, authUserId)) return slackError(c, "not_in_channel");
3626
+ const bookmark = findBookmark(channel2.channel_id, bookmarkId);
3627
3627
  if (!bookmark) return slackError(c, "not_found");
3628
3628
  ss().bookmarks.delete(bookmark.id);
3629
3629
  return slackOk(c, {});
@@ -4164,8 +4164,8 @@ function inspectorRoutes(ctx) {
4164
4164
  }
4165
4165
  function renderChannelsView(users) {
4166
4166
  const channels = sortedChannels(ss().channels.all());
4167
- const conversations = channels.filter((channel) => !channel.is_im && !channel.is_mpim);
4168
- const dms = channels.filter((channel) => channel.is_im || channel.is_mpim);
4167
+ const conversations = channels.filter((channel2) => !channel2.is_im && !channel2.is_mpim);
4168
+ const dms = channels.filter((channel2) => channel2.is_im || channel2.is_mpim);
4169
4169
  return [
4170
4170
  renderSection(
4171
4171
  "Channels",
@@ -4463,6 +4463,616 @@ function renderDeliveriesTable(deliveries, subscriptions, empty) {
4463
4463
  empty
4464
4464
  );
4465
4465
  }
4466
+ function openapiRoutes({ app, baseUrl }) {
4467
+ app.get("/openapi.json", (c) => c.json(buildSpec(baseUrl)));
4468
+ }
4469
+ var ok = (description) => ({
4470
+ description,
4471
+ content: {
4472
+ "application/json": {
4473
+ schema: { type: "object", properties: { ok: { type: "boolean" } }, required: ["ok"] }
4474
+ }
4475
+ }
4476
+ });
4477
+ var slackBody = (properties, required, description) => {
4478
+ const schema = { type: "object", properties, required: [...required] };
4479
+ return {
4480
+ required: required.length > 0,
4481
+ description,
4482
+ content: {
4483
+ "application/x-www-form-urlencoded": { schema },
4484
+ "application/json": { schema }
4485
+ }
4486
+ };
4487
+ };
4488
+ var channel = { type: "string", description: "Channel ID (or name for chat methods)." };
4489
+ var cursor = { type: "string", description: "Pagination cursor from response_metadata.next_cursor." };
4490
+ var limit = { type: "integer", description: "Page size, capped at 1000." };
4491
+ var jsonArray = (description) => ({
4492
+ type: ["array", "string"],
4493
+ items: { type: "object" },
4494
+ description: `${description} (array, or JSON-encoded string in form bodies).`
4495
+ });
4496
+ var richMessageFields = {
4497
+ blocks: jsonArray("Layout blocks"),
4498
+ attachments: jsonArray("Legacy attachments")
4499
+ };
4500
+ function buildSpec(baseUrl) {
4501
+ return {
4502
+ openapi: "3.1.0",
4503
+ info: {
4504
+ title: "Slack Web API (Emulated)",
4505
+ version: "1.0.0",
4506
+ description: "Emulated subset of the Slack Web API. Authenticate with a bearer bot token (mint one at POST /_emulate/credentials, or use a seeded token). Methods return 200 with { ok: false, error } on failure."
4507
+ },
4508
+ servers: [{ url: baseUrl }],
4509
+ components: {
4510
+ securitySchemes: {
4511
+ bearerAuth: {
4512
+ type: "http",
4513
+ scheme: "bearer",
4514
+ description: "Slack token, sent as `Authorization: Bearer xoxb-\u2026`."
4515
+ }
4516
+ }
4517
+ },
4518
+ security: [{ bearerAuth: [] }],
4519
+ paths: {
4520
+ "/api/auth.test": {
4521
+ post: {
4522
+ operationId: "auth.test",
4523
+ tags: ["auth"],
4524
+ summary: "Check the token and return user/team identity",
4525
+ responses: { "200": ok("Identity for the authed token.") }
4526
+ }
4527
+ },
4528
+ "/api/chat.postMessage": {
4529
+ post: {
4530
+ operationId: "chat.postMessage",
4531
+ tags: ["chat"],
4532
+ summary: "Send a message to a channel",
4533
+ requestBody: slackBody(
4534
+ {
4535
+ channel,
4536
+ text: { type: "string" },
4537
+ thread_ts: { type: "string", description: "Parent message ts to reply in a thread." },
4538
+ ...richMessageFields,
4539
+ metadata: { type: ["object", "string"] },
4540
+ parse: { type: "string" },
4541
+ username: { type: "string" },
4542
+ icon_url: { type: "string" },
4543
+ icon_emoji: { type: "string" }
4544
+ },
4545
+ ["channel"],
4546
+ "The message to post. Requires text, blocks, or attachments."
4547
+ ),
4548
+ responses: { "200": ok("The posted message with channel and ts.") }
4549
+ }
4550
+ },
4551
+ "/api/chat.postEphemeral": {
4552
+ post: {
4553
+ operationId: "chat.postEphemeral",
4554
+ tags: ["chat"],
4555
+ summary: "Send an ephemeral message to a user in a channel",
4556
+ requestBody: slackBody(
4557
+ {
4558
+ channel,
4559
+ user: { type: "string", description: "User ID who sees the message." },
4560
+ text: { type: "string" },
4561
+ thread_ts: { type: "string" },
4562
+ ...richMessageFields
4563
+ },
4564
+ ["channel", "user"],
4565
+ "The ephemeral message. Requires text, blocks, or attachments."
4566
+ ),
4567
+ responses: { "200": ok("The ephemeral message_ts.") }
4568
+ }
4569
+ },
4570
+ "/api/chat.update": {
4571
+ post: {
4572
+ operationId: "chat.update",
4573
+ tags: ["chat"],
4574
+ summary: "Update an existing message",
4575
+ requestBody: slackBody(
4576
+ {
4577
+ channel,
4578
+ ts: { type: "string", description: "Timestamp of the message to update." },
4579
+ text: { type: "string" },
4580
+ ...richMessageFields
4581
+ },
4582
+ ["channel", "ts"],
4583
+ "The updated content."
4584
+ ),
4585
+ responses: { "200": ok("The updated message.") }
4586
+ }
4587
+ },
4588
+ "/api/chat.delete": {
4589
+ post: {
4590
+ operationId: "chat.delete",
4591
+ tags: ["chat"],
4592
+ summary: "Delete a message",
4593
+ requestBody: slackBody({ channel, ts: { type: "string" } }, ["channel", "ts"], "The message to delete."),
4594
+ responses: { "200": ok("Deletion confirmation.") }
4595
+ }
4596
+ },
4597
+ "/api/chat.getPermalink": {
4598
+ post: {
4599
+ operationId: "chat.getPermalink",
4600
+ tags: ["chat"],
4601
+ summary: "Get a permalink for a message",
4602
+ requestBody: slackBody(
4603
+ { channel, message_ts: { type: "string" } },
4604
+ ["channel", "message_ts"],
4605
+ "The message to link to."
4606
+ ),
4607
+ responses: { "200": ok("The permalink.") }
4608
+ }
4609
+ },
4610
+ "/api/chat.scheduleMessage": {
4611
+ post: {
4612
+ operationId: "chat.scheduleMessage",
4613
+ tags: ["chat"],
4614
+ summary: "Schedule a message for later delivery",
4615
+ requestBody: slackBody(
4616
+ {
4617
+ channel,
4618
+ text: { type: "string" },
4619
+ post_at: { type: "integer", description: "Unix timestamp (seconds) to post at." },
4620
+ thread_ts: { type: "string" },
4621
+ ...richMessageFields
4622
+ },
4623
+ ["channel", "post_at"],
4624
+ "The message to schedule. Requires text, blocks, or attachments."
4625
+ ),
4626
+ responses: { "200": ok("The scheduled_message_id and post_at.") }
4627
+ }
4628
+ },
4629
+ "/api/conversations.list": {
4630
+ post: {
4631
+ operationId: "conversations.list",
4632
+ tags: ["conversations"],
4633
+ summary: "List conversations",
4634
+ requestBody: slackBody(
4635
+ {
4636
+ types: {
4637
+ type: "string",
4638
+ description: "Comma-separated: public_channel, private_channel, im, mpim. Defaults to public_channel."
4639
+ },
4640
+ exclude_archived: { type: ["boolean", "string"] },
4641
+ limit,
4642
+ cursor
4643
+ },
4644
+ [],
4645
+ "Listing filters."
4646
+ ),
4647
+ responses: { "200": ok("Channel list with response_metadata.next_cursor.") }
4648
+ }
4649
+ },
4650
+ "/api/conversations.info": {
4651
+ post: {
4652
+ operationId: "conversations.info",
4653
+ tags: ["conversations"],
4654
+ summary: "Get a conversation's details",
4655
+ requestBody: slackBody({ channel }, ["channel"], "The conversation to look up."),
4656
+ responses: { "200": ok("The channel object.") }
4657
+ }
4658
+ },
4659
+ "/api/conversations.create": {
4660
+ post: {
4661
+ operationId: "conversations.create",
4662
+ tags: ["conversations"],
4663
+ summary: "Create a channel",
4664
+ requestBody: slackBody(
4665
+ {
4666
+ name: { type: "string", description: "Channel name (lowercase letters, digits, - and _)." },
4667
+ is_private: { type: ["boolean", "string"] }
4668
+ },
4669
+ ["name"],
4670
+ "The channel to create."
4671
+ ),
4672
+ responses: { "200": ok("The created channel.") }
4673
+ }
4674
+ },
4675
+ "/api/conversations.history": {
4676
+ post: {
4677
+ operationId: "conversations.history",
4678
+ tags: ["conversations"],
4679
+ summary: "Fetch a conversation's message history",
4680
+ requestBody: slackBody({ channel, limit, cursor }, ["channel"], "The conversation to read."),
4681
+ responses: { "200": ok("Messages, has_more, and response_metadata.next_cursor.") }
4682
+ }
4683
+ },
4684
+ "/api/conversations.replies": {
4685
+ post: {
4686
+ operationId: "conversations.replies",
4687
+ tags: ["conversations"],
4688
+ summary: "Fetch a thread of messages",
4689
+ requestBody: slackBody(
4690
+ { channel, ts: { type: "string", description: "Parent message ts." } },
4691
+ ["channel", "ts"],
4692
+ "The thread to read."
4693
+ ),
4694
+ responses: { "200": ok("The thread messages.") }
4695
+ }
4696
+ },
4697
+ "/api/conversations.join": {
4698
+ post: {
4699
+ operationId: "conversations.join",
4700
+ tags: ["conversations"],
4701
+ summary: "Join a channel",
4702
+ requestBody: slackBody({ channel }, ["channel"], "The channel to join."),
4703
+ responses: { "200": ok("The joined channel.") }
4704
+ }
4705
+ },
4706
+ "/api/conversations.invite": {
4707
+ post: {
4708
+ operationId: "conversations.invite",
4709
+ tags: ["conversations"],
4710
+ summary: "Invite users to a channel",
4711
+ requestBody: slackBody(
4712
+ {
4713
+ channel,
4714
+ users: { type: "string", description: "Comma-separated user IDs (up to 100)." }
4715
+ },
4716
+ ["channel", "users"],
4717
+ "The users to invite."
4718
+ ),
4719
+ responses: { "200": ok("The updated channel.") }
4720
+ }
4721
+ },
4722
+ "/api/conversations.members": {
4723
+ post: {
4724
+ operationId: "conversations.members",
4725
+ tags: ["conversations"],
4726
+ summary: "List members of a conversation",
4727
+ requestBody: slackBody({ channel }, ["channel"], "The conversation to list."),
4728
+ responses: { "200": ok("Member user IDs.") }
4729
+ }
4730
+ },
4731
+ "/api/users.list": {
4732
+ post: {
4733
+ operationId: "users.list",
4734
+ tags: ["users"],
4735
+ summary: "List workspace users",
4736
+ requestBody: slackBody({ limit, cursor }, [], "Pagination options."),
4737
+ responses: { "200": ok("Member list with response_metadata.next_cursor.") }
4738
+ }
4739
+ },
4740
+ "/api/users.info": {
4741
+ post: {
4742
+ operationId: "users.info",
4743
+ tags: ["users"],
4744
+ summary: "Get a user's details",
4745
+ requestBody: slackBody({ user: { type: "string" } }, ["user"], "The user to look up."),
4746
+ responses: { "200": ok("The user object.") }
4747
+ }
4748
+ },
4749
+ "/api/users.lookupByEmail": {
4750
+ post: {
4751
+ operationId: "users.lookupByEmail",
4752
+ tags: ["users"],
4753
+ summary: "Find a user by email",
4754
+ requestBody: slackBody({ email: { type: "string" } }, ["email"], "The email to look up."),
4755
+ responses: { "200": ok("The matching user.") }
4756
+ }
4757
+ },
4758
+ "/api/users.profile.get": {
4759
+ post: {
4760
+ operationId: "users.profile.get",
4761
+ tags: ["users"],
4762
+ summary: "Get a user's profile",
4763
+ requestBody: slackBody(
4764
+ { user: { type: "string", description: "Defaults to the authed user." } },
4765
+ [],
4766
+ "The user whose profile to read."
4767
+ ),
4768
+ responses: { "200": ok("The profile.") }
4769
+ }
4770
+ },
4771
+ "/api/users.profile.set": {
4772
+ post: {
4773
+ operationId: "users.profile.set",
4774
+ tags: ["users"],
4775
+ summary: "Set a user's profile fields",
4776
+ requestBody: slackBody(
4777
+ {
4778
+ user: { type: "string", description: "Defaults to the authed user." },
4779
+ profile: {
4780
+ type: ["object", "string"],
4781
+ description: "Profile fields to merge (object, or JSON-encoded string in form bodies)."
4782
+ },
4783
+ name: { type: "string", description: "Single field name (alternative to profile)." },
4784
+ value: { type: "string", description: "Single field value (used with name)." }
4785
+ },
4786
+ [],
4787
+ "Either a profile object or a name/value pair."
4788
+ ),
4789
+ responses: { "200": ok("The updated profile.") }
4790
+ }
4791
+ },
4792
+ "/api/users.getPresence": {
4793
+ post: {
4794
+ operationId: "users.getPresence",
4795
+ tags: ["users"],
4796
+ summary: "Get a user's presence",
4797
+ requestBody: slackBody(
4798
+ { user: { type: "string", description: "Defaults to the authed user." } },
4799
+ [],
4800
+ "The user whose presence to read."
4801
+ ),
4802
+ responses: { "200": ok("The presence state.") }
4803
+ }
4804
+ },
4805
+ "/api/users.setPresence": {
4806
+ post: {
4807
+ operationId: "users.setPresence",
4808
+ tags: ["users"],
4809
+ summary: "Set the authed user's presence",
4810
+ requestBody: slackBody(
4811
+ { presence: { type: "string", enum: ["auto", "away"] } },
4812
+ ["presence"],
4813
+ "The manual presence to set."
4814
+ ),
4815
+ responses: { "200": ok("Confirmation.") }
4816
+ }
4817
+ },
4818
+ "/api/reactions.add": {
4819
+ post: {
4820
+ operationId: "reactions.add",
4821
+ tags: ["reactions"],
4822
+ summary: "Add a reaction to a message",
4823
+ requestBody: slackBody(
4824
+ {
4825
+ channel,
4826
+ timestamp: { type: "string", description: "Message ts to react to." },
4827
+ name: { type: "string", description: "Emoji name without colons." }
4828
+ },
4829
+ ["channel", "timestamp", "name"],
4830
+ "The reaction to add."
4831
+ ),
4832
+ responses: { "200": ok("Confirmation.") }
4833
+ }
4834
+ },
4835
+ "/api/reactions.remove": {
4836
+ post: {
4837
+ operationId: "reactions.remove",
4838
+ tags: ["reactions"],
4839
+ summary: "Remove a reaction from a message",
4840
+ requestBody: slackBody(
4841
+ {
4842
+ channel,
4843
+ timestamp: { type: "string" },
4844
+ name: { type: "string", description: "Emoji name without colons." }
4845
+ },
4846
+ ["channel", "timestamp", "name"],
4847
+ "The reaction to remove."
4848
+ ),
4849
+ responses: { "200": ok("Confirmation.") }
4850
+ }
4851
+ },
4852
+ "/api/reactions.get": {
4853
+ post: {
4854
+ operationId: "reactions.get",
4855
+ tags: ["reactions"],
4856
+ summary: "Get reactions for a message",
4857
+ requestBody: slackBody(
4858
+ { channel, timestamp: { type: "string" } },
4859
+ ["channel", "timestamp"],
4860
+ "The message to inspect."
4861
+ ),
4862
+ responses: { "200": ok("The message with its reactions.") }
4863
+ }
4864
+ },
4865
+ "/api/pins.add": {
4866
+ post: {
4867
+ operationId: "pins.add",
4868
+ tags: ["pins"],
4869
+ summary: "Pin a message to a channel",
4870
+ requestBody: slackBody(
4871
+ { channel, timestamp: { type: "string", description: "Message ts to pin." } },
4872
+ ["channel", "timestamp"],
4873
+ "The message to pin."
4874
+ ),
4875
+ responses: { "200": ok("Confirmation.") }
4876
+ }
4877
+ },
4878
+ "/api/pins.list": {
4879
+ post: {
4880
+ operationId: "pins.list",
4881
+ tags: ["pins"],
4882
+ summary: "List pinned items in a channel",
4883
+ requestBody: slackBody({ channel }, ["channel"], "The channel to list."),
4884
+ responses: { "200": ok("Pinned items.") }
4885
+ }
4886
+ },
4887
+ "/api/pins.remove": {
4888
+ post: {
4889
+ operationId: "pins.remove",
4890
+ tags: ["pins"],
4891
+ summary: "Unpin a message from a channel",
4892
+ requestBody: slackBody(
4893
+ { channel, timestamp: { type: "string" } },
4894
+ ["channel", "timestamp"],
4895
+ "The message to unpin."
4896
+ ),
4897
+ responses: { "200": ok("Confirmation.") }
4898
+ }
4899
+ },
4900
+ "/api/bookmarks.add": {
4901
+ post: {
4902
+ operationId: "bookmarks.add",
4903
+ tags: ["bookmarks"],
4904
+ summary: "Add a bookmark to a channel",
4905
+ requestBody: slackBody(
4906
+ {
4907
+ channel_id: { type: "string" },
4908
+ title: { type: "string" },
4909
+ type: { type: "string", enum: ["link"] },
4910
+ link: { type: "string", description: "http(s) URL to bookmark." },
4911
+ emoji: { type: "string" },
4912
+ access_level: { type: "string", enum: ["read", "write"] },
4913
+ parent_id: { type: "string" }
4914
+ },
4915
+ ["channel_id", "title", "type", "link"],
4916
+ "The bookmark to add."
4917
+ ),
4918
+ responses: { "200": ok("The created bookmark.") }
4919
+ }
4920
+ },
4921
+ "/api/bookmarks.list": {
4922
+ post: {
4923
+ operationId: "bookmarks.list",
4924
+ tags: ["bookmarks"],
4925
+ summary: "List bookmarks in a channel",
4926
+ requestBody: slackBody({ channel_id: { type: "string" } }, ["channel_id"], "The channel to list."),
4927
+ responses: { "200": ok("Bookmark list.") }
4928
+ }
4929
+ },
4930
+ "/api/files.getUploadURLExternal": {
4931
+ post: {
4932
+ operationId: "files.getUploadURLExternal",
4933
+ tags: ["files"],
4934
+ summary: "Get an upload URL for a file",
4935
+ requestBody: slackBody(
4936
+ {
4937
+ filename: { type: "string" },
4938
+ length: { type: "integer", description: "File size in bytes." },
4939
+ alt_text: { type: "string" },
4940
+ snippet_type: { type: "string" }
4941
+ },
4942
+ ["filename", "length"],
4943
+ "The file to stage. POST the raw bytes to the returned upload_url, then call files.completeUploadExternal."
4944
+ ),
4945
+ responses: { "200": ok("The upload_url and file_id.") }
4946
+ }
4947
+ },
4948
+ "/api/files.completeUploadExternal": {
4949
+ post: {
4950
+ operationId: "files.completeUploadExternal",
4951
+ tags: ["files"],
4952
+ summary: "Finalize staged uploads and share them",
4953
+ requestBody: slackBody(
4954
+ {
4955
+ files: {
4956
+ type: ["array", "string"],
4957
+ items: {
4958
+ type: "object",
4959
+ properties: { id: { type: "string" }, title: { type: "string" } },
4960
+ required: ["id"]
4961
+ },
4962
+ description: "Staged files to complete (array, or JSON-encoded string in form bodies)."
4963
+ },
4964
+ channel_id: { type: "string", description: "Channel or user ID to share into." },
4965
+ channels: { type: "string", description: "Comma-separated channel IDs to share into." },
4966
+ initial_comment: { type: "string" },
4967
+ thread_ts: { type: "string" },
4968
+ blocks: jsonArray("Layout blocks for the share message")
4969
+ },
4970
+ ["files"],
4971
+ "The staged uploads to finalize."
4972
+ ),
4973
+ responses: { "200": ok("The completed file objects.") }
4974
+ }
4975
+ },
4976
+ "/api/files.list": {
4977
+ post: {
4978
+ operationId: "files.list",
4979
+ tags: ["files"],
4980
+ summary: "List files",
4981
+ requestBody: slackBody(
4982
+ {
4983
+ channel: { type: "string", description: "Filter to files visible in this channel." },
4984
+ user: { type: "string", description: "Filter to files uploaded by this user." },
4985
+ types: { type: "string", description: "Comma-separated file types. Defaults to all." },
4986
+ ts_from: { type: ["number", "string"] },
4987
+ ts_to: { type: ["number", "string"] },
4988
+ page: { type: "integer" },
4989
+ count: { type: "integer", description: "Page size, capped at 1000." }
4990
+ },
4991
+ [],
4992
+ "Listing filters."
4993
+ ),
4994
+ responses: { "200": ok("Files with paging info.") }
4995
+ }
4996
+ },
4997
+ "/api/views.publish": {
4998
+ post: {
4999
+ operationId: "views.publish",
5000
+ tags: ["views"],
5001
+ summary: "Publish a user's App Home view",
5002
+ requestBody: slackBody(
5003
+ {
5004
+ user_id: { type: "string" },
5005
+ view: {
5006
+ type: ["object", "string"],
5007
+ description: "Home view payload (object, or JSON-encoded string in form bodies)."
5008
+ },
5009
+ hash: { type: "string", description: "Expected current view hash for conflict detection." }
5010
+ },
5011
+ ["user_id", "view"],
5012
+ "The home view to publish."
5013
+ ),
5014
+ responses: { "200": ok("The published view.") }
5015
+ }
5016
+ },
5017
+ "/api/views.open": {
5018
+ post: {
5019
+ operationId: "views.open",
5020
+ tags: ["views"],
5021
+ summary: "Open a modal view",
5022
+ requestBody: slackBody(
5023
+ {
5024
+ trigger_id: { type: "string", description: "Short-lived trigger from an interaction." },
5025
+ interactivity_pointer: { type: "string", description: "Alternative to trigger_id." },
5026
+ view: {
5027
+ type: ["object", "string"],
5028
+ description: "Modal view payload (object, or JSON-encoded string in form bodies)."
5029
+ }
5030
+ },
5031
+ ["view"],
5032
+ "The modal to open. Requires trigger_id or interactivity_pointer."
5033
+ ),
5034
+ responses: { "200": ok("The opened view.") }
5035
+ }
5036
+ },
5037
+ "/api/team.info": {
5038
+ post: {
5039
+ operationId: "team.info",
5040
+ tags: ["team"],
5041
+ summary: "Get workspace info",
5042
+ responses: { "200": ok("The team object.") }
5043
+ }
5044
+ },
5045
+ "/api/bots.info": {
5046
+ post: {
5047
+ operationId: "bots.info",
5048
+ tags: ["team"],
5049
+ summary: "Get a bot's details",
5050
+ requestBody: slackBody({ bot: { type: "string", description: "Bot ID." } }, ["bot"], "The bot to look up."),
5051
+ responses: { "200": ok("The bot object.") }
5052
+ }
5053
+ },
5054
+ "/api/oauth.v2.access": {
5055
+ post: {
5056
+ operationId: "oauth.v2.access",
5057
+ tags: ["oauth"],
5058
+ summary: "Exchange an OAuth v2 authorization code for tokens",
5059
+ security: [],
5060
+ requestBody: slackBody(
5061
+ {
5062
+ code: { type: "string" },
5063
+ client_id: { type: "string", description: "May also be sent via HTTP Basic auth." },
5064
+ client_secret: { type: "string", description: "May also be sent via HTTP Basic auth." },
5065
+ redirect_uri: { type: "string" }
5066
+ },
5067
+ ["code"],
5068
+ "The authorization code exchange."
5069
+ ),
5070
+ responses: { "200": ok("Bot and user access tokens.") }
5071
+ }
5072
+ }
5073
+ }
5074
+ };
5075
+ }
4466
5076
  var manifest = {
4467
5077
  id: "slack",
4468
5078
  name: "Slack",
@@ -4480,9 +5090,10 @@ var manifest = {
4480
5090
  ],
4481
5091
  specs: [
4482
5092
  {
4483
- kind: "manual",
5093
+ kind: "openapi",
4484
5094
  title: "Slack Web API subset",
4485
5095
  coverage: "hand-authored",
5096
+ url: "/openapi.json",
4486
5097
  notes: "The Web API is invoked over POST with form-encoded or JSON bodies and returns { ok: boolean, ... }.",
4487
5098
  operations: [
4488
5099
  { operationId: "auth.test", method: "POST", path: "/api/auth.test", status: "hand-authored" },
@@ -4972,6 +5583,7 @@ var slackPlugin = {
4972
5583
  bookmarksRoutes(ctx);
4973
5584
  viewsRoutes(ctx);
4974
5585
  inspectorRoutes(ctx);
5586
+ openapiRoutes(ctx);
4975
5587
  },
4976
5588
  seed(store, baseUrl) {
4977
5589
  seedDefaults(store, baseUrl);
@@ -5106,4 +5718,4 @@ export {
5106
5718
  seedFromConfig,
5107
5719
  slackPlugin
5108
5720
  };
5109
- //# sourceMappingURL=dist-YPRJYQHW.js.map
5721
+ //# sourceMappingURL=dist-FE2JWST3.js.map