|
1 | 1 | import type { RouteContext } from "@emulators/core"; |
2 | | -import type { SlackChannel, SlackMessage, SlackUser } from "../entities.js"; |
| 2 | +import type { SlackChannel, SlackFile, SlackFileShare, SlackMessage, SlackUser } from "../entities.js"; |
3 | 3 | import { getSlackStore } from "../store.js"; |
4 | 4 | import { |
5 | 5 | formatSlackMessage, |
@@ -33,6 +33,35 @@ export function conversationsRoutes(ctx: RouteContext): void { |
33 | 33 | getChannelMemberKey(channel, user, userId) !== undefined; |
34 | 34 | const canReadConversation = (channel: SlackChannel, user: SlackUser | undefined, userId: string) => |
35 | 35 | !channel.is_private || isChannelMember(channel, user, userId); |
| 36 | + const visibleFileChannelIds = (file: SlackFile, authUser: { login: string }) => { |
| 37 | + const authSlackUser = getAuthSlackUser(authUser); |
| 38 | + const authUserId = authSlackUser?.user_id ?? authUser.login; |
| 39 | + return fileChannels(file).filter((channelId) => { |
| 40 | + const channel = ss().channels.findOneBy("channel_id", channelId); |
| 41 | + return channel ? canReadConversation(channel, authSlackUser, authUserId) : false; |
| 42 | + }); |
| 43 | + }; |
| 44 | + const visibleFileForAuth = (file: SlackFile, authUser: { login: string }): SlackFile => { |
| 45 | + const visibleIds = new Set(visibleFileChannelIds(file, authUser)); |
| 46 | + const publicShares = filterVisibleShares(file.shares.public, visibleIds); |
| 47 | + const privateShares = filterVisibleShares(file.shares.private, visibleIds); |
| 48 | + const shares: SlackFile["shares"] = {}; |
| 49 | + if (publicShares) shares.public = publicShares; |
| 50 | + if (privateShares) shares.private = privateShares; |
| 51 | + |
| 52 | + return { |
| 53 | + ...file, |
| 54 | + channels: file.channels.filter((channelId) => visibleIds.has(channelId)), |
| 55 | + groups: file.groups.filter((channelId) => visibleIds.has(channelId)), |
| 56 | + ims: file.ims.filter((channelId) => visibleIds.has(channelId)), |
| 57 | + shares, |
| 58 | + }; |
| 59 | + }; |
| 60 | + const formatSlackMessageForAuth = (msg: SlackMessage, authUser: { login: string }) => |
| 61 | + formatSlackMessage({ |
| 62 | + ...msg, |
| 63 | + ...(msg.files ? { files: msg.files.map((file) => visibleFileForAuth(file, authUser)) } : {}), |
| 64 | + }); |
36 | 65 | const dispatchConversationEvent = async (type: string, event: Record<string, unknown>) => { |
37 | 66 | await webhooks.dispatch( |
38 | 67 | type, |
@@ -428,7 +457,7 @@ export function conversationsRoutes(ctx: RouteContext): void { |
428 | 457 | const nextCursor = hasMore ? allMessages[startIndex + limit].ts : ""; |
429 | 458 |
|
430 | 459 | return slackOk(c, { |
431 | | - messages: page.map(formatSlackMessage), |
| 460 | + messages: page.map((message) => formatSlackMessageForAuth(message, authUser)), |
432 | 461 | has_more: hasMore, |
433 | 462 | response_metadata: { next_cursor: nextCursor }, |
434 | 463 | }); |
@@ -458,7 +487,7 @@ export function conversationsRoutes(ctx: RouteContext): void { |
458 | 487 | .sort((a, b) => (a.ts > b.ts ? 1 : -1)); |
459 | 488 |
|
460 | 489 | return slackOk(c, { |
461 | | - messages: allMessages.map(formatSlackMessage), |
| 490 | + messages: allMessages.map((message) => formatSlackMessageForAuth(message, authUser)), |
462 | 491 | has_more: false, |
463 | 492 | }); |
464 | 493 | }); |
@@ -920,6 +949,15 @@ function findNamedChannel(channels: SlackChannel[], name: string): SlackChannel |
920 | 949 | return channels.find((ch) => !ch.is_im && !ch.is_mpim && ch.name === name); |
921 | 950 | } |
922 | 951 |
|
| 952 | +function fileChannels(file: SlackFile): string[] { |
| 953 | + return [...file.channels, ...file.groups, ...file.ims]; |
| 954 | +} |
| 955 | + |
| 956 | +function filterVisibleShares(shares: Record<string, SlackFileShare[]> | undefined, visibleIds: Set<string>) { |
| 957 | + const entries = Object.entries(shares ?? {}).filter(([channelId]) => visibleIds.has(channelId)); |
| 958 | + return entries.length > 0 ? Object.fromEntries(entries) : undefined; |
| 959 | +} |
| 960 | + |
923 | 961 | function channelTypeLetter(ch: SlackChannel): "C" | "D" | "G" { |
924 | 962 | if (ch.is_im) return "D"; |
925 | 963 | if (ch.is_private || ch.is_mpim) return "G"; |
|
0 commit comments