mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(google-meet): force English Meet UI via hl=en so automation works on any locale (#89671)
Summary: - The branch adds `forceMeetEnglishUi()` for Google Meet URLs, applies it to join/create browser opens, canonicalizes browser-created meeting links, and updates focused Google Meet tests. - PR surface: Source +18, Tests +19. Total +37 across 6 files. - Reproducibility: yes. Current main opens raw Meet URLs while the browser automation matches English-only lab ... R body provides after-fix CDP output showing non-English pages render expected English labels with `hl=en`. Automerge notes: - PR branch already contained follow-up commit before automerge: chore(tooling): add unused agent-cache-store files to knip optional a… - PR branch already contained follow-up commit before automerge: fix(google-meet): repair non-Latin mic regex matching boundary - PR branch already contained follow-up commit before automerge: revert(tooling): preserve current main deadcode allowlist guard - PR branch already contained follow-up commit before automerge: revert(tooling): perfectly match origin/main deadcode list - PR branch already contained follow-up commit before automerge: revert: match origin/main deadcode list exactly Validation: - ClawSweeper review passed for head880a41b6f0. - Required merge gates passed before the squash merge. Prepared head SHA:880a41b6f0Review: https://github.com/openclaw/openclaw/pull/89671#issuecomment-4608725456 Co-authored-by: Chen Chia Yang <unayung@gmail.com> Approved-by: hxy91819
This commit is contained in:
@@ -304,7 +304,7 @@ describe("google-meet create flow", () => {
|
||||
return false;
|
||||
}
|
||||
const body = proxy.body as Record<string, unknown>;
|
||||
return proxy.path === "/tabs/open" && body.url === "https://meet.google.com/new";
|
||||
return proxy.path === "/tabs/open" && body.url === "https://meet.google.com/new?hl=en";
|
||||
});
|
||||
});
|
||||
|
||||
@@ -426,7 +426,9 @@ describe("google-meet create flow", () => {
|
||||
payload: {
|
||||
result: {
|
||||
targetId:
|
||||
proxy.body?.url === "https://meet.google.com/new" ? "create-tab" : "join-tab",
|
||||
proxy.body?.url === "https://meet.google.com/new?hl=en"
|
||||
? "create-tab"
|
||||
: "join-tab",
|
||||
title: "Meet",
|
||||
url: proxy.body?.url,
|
||||
},
|
||||
|
||||
@@ -2294,7 +2294,7 @@ describe("google-meet plugin", () => {
|
||||
method: "POST",
|
||||
path: "/tabs/open",
|
||||
timeoutMs: 30000,
|
||||
body: { url: "https://meet.google.com/abc-defg-hij" },
|
||||
body: { url: "https://meet.google.com/abc-defg-hij?hl=en" },
|
||||
});
|
||||
expect(openCall[3]).toEqual({ progress: false });
|
||||
expect(
|
||||
@@ -3105,7 +3105,7 @@ describe("google-meet plugin", () => {
|
||||
method: "POST",
|
||||
path: "/tabs/open",
|
||||
timeoutMs: 30000,
|
||||
body: { url: "https://meet.google.com/abc-defg-hij" },
|
||||
body: { url: "https://meet.google.com/abc-defg-hij?hl=en" },
|
||||
});
|
||||
const startCall = nodesInvoke.mock.calls.find(([rawCall]) => {
|
||||
const call = requireRecord(rawCall, "node invoke");
|
||||
@@ -4105,7 +4105,7 @@ describe("google-meet plugin", () => {
|
||||
const request = requireRecord(params, "local browser open request");
|
||||
expect(request.method).toBe("POST");
|
||||
expect(request.path).toBe("/tabs/open");
|
||||
expect(request.body).toStrictEqual({ url: "https://meet.google.com/abc-defg-hij" });
|
||||
expect(request.body).toStrictEqual({ url: "https://meet.google.com/abc-defg-hij?hl=en" });
|
||||
expect(extra).toStrictEqual({ progress: false });
|
||||
} finally {
|
||||
Object.defineProperty(process, "platform", { value: originalPlatform });
|
||||
|
||||
@@ -2,7 +2,24 @@
|
||||
import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime";
|
||||
import type { PluginRuntime } from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { callBrowserProxyOnNode } from "./chrome-browser-proxy.js";
|
||||
import { callBrowserProxyOnNode, forceMeetEnglishUi } from "./chrome-browser-proxy.js";
|
||||
|
||||
describe("forceMeetEnglishUi", () => {
|
||||
it("pins hl=en on Meet URLs", () => {
|
||||
expect(forceMeetEnglishUi("https://meet.google.com/abc-defg-hij")).toBe(
|
||||
"https://meet.google.com/abc-defg-hij?hl=en",
|
||||
);
|
||||
expect(forceMeetEnglishUi("https://meet.google.com/new")).toBe(
|
||||
"https://meet.google.com/new?hl=en",
|
||||
);
|
||||
});
|
||||
|
||||
it("overrides an existing hl and keeps other params", () => {
|
||||
expect(forceMeetEnglishUi("https://meet.google.com/abc-defg-hij?hl=zh-TW&authuser=1")).toBe(
|
||||
"https://meet.google.com/abc-defg-hij?hl=en&authuser=1",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Google Meet Chrome browser proxy", () => {
|
||||
it("reports malformed node proxy payloadJSON with an owned error", async () => {
|
||||
|
||||
@@ -12,6 +12,19 @@ export type BrowserTab = {
|
||||
url?: string;
|
||||
};
|
||||
|
||||
// Meet automation scripts match English UI labels ("Join now", "Turn off microphone").
|
||||
// hl=en pins the Meet page language regardless of account/browser locale; without it,
|
||||
// non-English profiles render localized labels and every DOM matcher goes blind.
|
||||
export function forceMeetEnglishUi(url: string): string {
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
parsed.searchParams.set("hl", "en");
|
||||
return parsed.toString();
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeMeetUrlForReuse(url: string | undefined): string | undefined {
|
||||
if (!url) {
|
||||
return undefined;
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { GoogleMeetConfig } from "../config.js";
|
||||
import {
|
||||
asBrowserTabs,
|
||||
callBrowserProxyOnNode,
|
||||
forceMeetEnglishUi,
|
||||
readBrowserTab,
|
||||
resolveChromeNode,
|
||||
type BrowserTab,
|
||||
@@ -199,7 +200,9 @@ export const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
||||
}
|
||||
const href = current();
|
||||
if (meetUrlPattern.test(href)) {
|
||||
return { meetingUri: href, browserUrl: href, browserTitle: document.title, notes };
|
||||
// The /new redirect keeps the hl=en param we open with; strip query/hash so the
|
||||
// meeting link handed to users stays canonical instead of forcing English on them.
|
||||
return { meetingUri: href.split(/[?#]/)[0], browserUrl: href, browserTitle: document.title, notes };
|
||||
}
|
||||
const pageText = text(document.body);
|
||||
if (clickButton(/\\buse microphone\\b/i, "Accepted Meet microphone prompt with browser automation.")) {
|
||||
@@ -288,7 +291,7 @@ export async function createMeetWithBrowserProxyOnNode(params: {
|
||||
nodeId,
|
||||
method: "POST",
|
||||
path: "/tabs/open",
|
||||
body: { url: GOOGLE_MEET_NEW_URL },
|
||||
body: { url: forceMeetEnglishUi(GOOGLE_MEET_NEW_URL) },
|
||||
timeoutMs: stepTimeoutMs,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
import {
|
||||
asBrowserTabs,
|
||||
callBrowserProxyOnNode,
|
||||
forceMeetEnglishUi,
|
||||
isSameMeetUrlForReuse,
|
||||
normalizeMeetUrlForReuse,
|
||||
readBrowserTab,
|
||||
@@ -433,7 +434,7 @@ function meetStatusScript(params: {
|
||||
notes.push("Muted Meet microphone for observe-only mode.");
|
||||
}
|
||||
const join = !readOnly && ${JSON.stringify(params.autoJoin)}
|
||||
? findButton(/join now|ask to join/i)
|
||||
? findButton(/join now|ask to join|join here too/i)
|
||||
: null;
|
||||
if (join) join.click();
|
||||
const microphoneChoice = findButton(/\\buse microphone\\b/i);
|
||||
@@ -690,7 +691,7 @@ async function openMeetWithBrowserRequest(params: {
|
||||
await params.callBrowser({
|
||||
method: "POST",
|
||||
path: "/tabs/open",
|
||||
body: { url: params.url },
|
||||
body: { url: forceMeetEnglishUi(params.url) },
|
||||
timeoutMs,
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user