Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 8 additions & 18 deletions src/slack/search-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export async function searchMessagesInChannelsFallback(

const results: SearchCompactMessage[] = [];

for (const channelId of channelIds) {
channelLoop: for (const channelId of channelIds) {
let cursorLatest: string | undefined;
for (;;) {
const resp = await client.api("conversations.history", {
Expand All @@ -187,6 +187,7 @@ export async function searchMessagesInChannelsFallback(
break;
}

let reachedAfterBoundary = false;
for (const m of messages) {
const summary = messageSummaryFromApiMessage(channelId, m);

Expand All @@ -196,7 +197,7 @@ export async function searchMessagesInChannelsFallback(
continue;
}
if (afterSec !== null && tsNum < afterSec) {
cursorLatest = undefined;
reachedAfterBoundary = true;
break;
}
}
Expand Down Expand Up @@ -230,31 +231,20 @@ export async function searchMessagesInChannelsFallback(
matchedSummaries.push(summary);
results.push(toSearchCompactMessage(compact));
if (results.length >= input.limit) {
const referencedUserIds = collectReferencedUserIds(matchedSummaries, {
includeReactions: false,
});
const usersById = await resolveUsersById({
client,
workspaceUrl: input.workspace_url ?? "",
userIds: referencedUserIds,
forceRefresh: Boolean(input.refreshUsers),
});
return {
messages: results,
referenced_users: toReferencedUsers(referencedUserIds, usersById),
};
break channelLoop;
}
}

if (!cursorLatest) {
if (reachedAfterBoundary || resp.has_more === false) {
break;
}

const last = messages.at(-1);
cursorLatest = last ? getString(last.ts) : undefined;
if (!cursorLatest) {
const nextLatest = last ? getString(last.ts) : undefined;
if (!nextLatest || nextLatest === cursorLatest) {
break;
}
cursorLatest = nextLatest;
}
}

Expand Down
73 changes: 73 additions & 0 deletions test/search-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function createClient(calls: ApiCall[]) {

if (method === "conversations.history") {
return {
has_more: false,
messages: [
{
ts: "1.000001",
Expand Down Expand Up @@ -154,6 +155,78 @@ describe("search referenced users", () => {
expect(result.referenced_users).not.toHaveProperty("U44444444");
});

test("searchSlack scans older channel history pages", async () => {
const calls: ApiCall[] = [];
const client = {
api: async (method: string, params: Record<string, unknown>) => {
calls.push({ method, params });
if (method !== "conversations.history") {
throw new Error(`Unexpected API method: ${method}`);
}
if (params.latest === undefined) {
return {
has_more: true,
messages: [{ ts: "2.000002", text: "not a match", user: "U11111111" }],
};
}
if (params.latest === "2.000002") {
return {
has_more: false,
messages: [{ ts: "1.000001", text: "needle", user: "U22222222" }],
};
}
throw new Error(`Unexpected latest timestamp: ${String(params.latest)}`);
},
};

const result = await searchSlack({
client: client as never,
auth: { auth_type: "standard", token: "x" },
options: {
workspace_url: "https://workspace.slack.com",
query: "needle",
kind: "messages",
channels: ["C12345678"],
limit: 20,
max_content_chars: 4000,
content_type: "any",
download: false,
},
});

expect(result.messages).toHaveLength(1);
expect(result.messages?.[0]?.content).toBe("needle");
expect(
calls
.filter((call) => call.method === "conversations.history")
.map((call) => call.params.latest),
).toEqual([undefined, "2.000002"]);
});

test("searchSlack does not resolve users at the result limit without opt-in", async () => {
const calls: ApiCall[] = [];
const client = createClient(calls) as never;

const result = await searchSlack({
client,
auth: { auth_type: "standard", token: "x" },
options: {
workspace_url: "https://workspace.slack.com",
query: "hello",
kind: "messages",
channels: ["C12345678"],
limit: 1,
max_content_chars: 4000,
content_type: "any",
download: false,
},
});

expect(result.messages).toHaveLength(1);
expect(result.referenced_users).toBeUndefined();
expect(calls.filter((call) => call.method === "users.info")).toHaveLength(0);
});

test("search command forwards --refresh-users and bypasses cached user lookups", async () => {
const calls: ApiCall[] = [];
const ctx = createContext(calls);
Expand Down
Loading