Skip to content
Merged
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
49 changes: 19 additions & 30 deletions packages/core-plugins/src/plugins/gemini/parser.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { createAppError } from "@ctxport/core-schema";
import type { GeminiRuntimeParams } from "./types";

const BATCH_EXECUTE_ENDPOINT =
"https://gemini.google.com/_/BardChatUi/data/batchexecute";
const BATCH_EXECUTE_BASE = "https://gemini.google.com";

const GEMINI_IMAGE_URL_PATTERN =
/^https:\/\/lh3\.googleusercontent\.com\/gg(?:-dl)?\//;
Expand All @@ -12,12 +11,13 @@ const GEMINI_IMAGE_URL_PATTERN =
export async function fetchConversationPayload(
conversationId: string,
runtimeParams: GeminiRuntimeParams,
pathPrefix = "",
): Promise<unknown> {
const rpcId = "hNvQHb";

const query = new URLSearchParams({
rpcids: rpcId,
"source-path": `/app/${conversationId}`,
"source-path": `${pathPrefix}/app/${conversationId}`,
bl: runtimeParams.bl,
"f.sid": runtimeParams.fSid,
hl: runtimeParams.hl,
Expand All @@ -29,16 +29,7 @@ export async function fetchConversationPayload(
[
[
rpcId,
JSON.stringify([
`c_${conversationId}`,
100,
null,
1,
[0],
[4],
null,
1,
]),
JSON.stringify([`c_${conversationId}`, 10, null, 1, [0], [4], null, 1]),
null,
"generic",
],
Expand All @@ -49,24 +40,22 @@ export async function fetchConversationPayload(
body.set("at", runtimeParams.at);
}

const response = await fetch(
`${BATCH_EXECUTE_ENDPOINT}?${query.toString()}`,
{
method: "POST",
mode: "cors",
credentials: "include",
cache: "no-store",
referrer: `https://gemini.google.com/app/${conversationId}`,
referrerPolicy: "strict-origin-when-cross-origin",
headers: {
Accept: "*/*",
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
Origin: "https://gemini.google.com",
"X-Same-Domain": "1",
},
body: body.toString(),
const endpoint = `${BATCH_EXECUTE_BASE}${pathPrefix}/_/BardChatUi/data/batchexecute`;
const response = await fetch(`${endpoint}?${query.toString()}`, {
method: "POST",
mode: "cors",
credentials: "include",
cache: "no-store",
referrer: `https://gemini.google.com${pathPrefix}/app/${conversationId}`,
referrerPolicy: "strict-origin-when-cross-origin",
headers: {
Accept: "*/*",
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
Origin: "https://gemini.google.com",
"X-Same-Domain": "1",
},
);
body: body.toString(),
});

if (!response.ok) {
throw createAppError(
Expand Down
11 changes: 10 additions & 1 deletion packages/core-plugins/src/plugins/gemini/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,25 @@ export const geminiPlugin: Plugin = {
throw createAppError("E-PARSE-001", "Not a Gemini conversation page");
}

const pathPrefix = extractPathPrefix(ctx.url);
const runtimeParams = await resolveRuntimeParams(ctx.document);
const payload = await fetchConversationPayload(
conversationId,
runtimeParams,
pathPrefix,
);
return buildContentBundle(payload, ctx.url);
},

async fetchById(conversationId: string): Promise<ContentBundle> {
const pathPrefix = extractPathPrefix(document.location.href);
const runtimeParams = await resolveRuntimeParams(document);
const payload = await fetchConversationPayload(
conversationId,
runtimeParams,
pathPrefix,
);
const url = `https://gemini.google.com/app/${conversationId}`;
const url = `https://gemini.google.com${pathPrefix}/app/${conversationId}`;
return buildContentBundle(payload, url);
},

Expand Down Expand Up @@ -81,6 +85,11 @@ function extractConversationId(url: string): string | null {
return match?.[1] ?? null;
}

function extractPathPrefix(url: string): string {
const match = /^https?:\/\/gemini\.google\.com\/(u\/\d+)\//.exec(url);
return match ? `/${match[1]}` : "";
}

async function resolveRuntimeParams(
doc: Document,
): Promise<GeminiRuntimeParams> {
Expand Down
Loading