diff --git a/README.md b/README.md
index a141609d..d3276d59 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,9 @@ you decide what they may do.
- **Let them hand work to each other.** Bots send messages and tasks to each other. Every message is
saved before it is sent and retried until it arrives.
- **Watch them work.** See every page a bot opens in its browser, live, and take control to sign in
- for it. Watch the screens it designs take shape while it writes them. Open the files it made.
+ for it. Sites that refuse to sign in there, like Google, take one click more: sign in in a real
+ window of the bot's browser, close it, and the bot keeps the login. Watch the screens it designs
+ take shape while it writes them. Open the files it made.
- **Schedule routines.** Weekday mornings, every two hours or any cron schedule, with missed runs
and overlaps handled.
- **Leave them running.** Bots keep working after you close Botloft. They start when you sign in,
diff --git a/app/src/features/browser/HandsBars.tsx b/app/src/features/browser/HandsBars.tsx
index 768342f2..9da03be9 100644
--- a/app/src/features/browser/HandsBars.tsx
+++ b/app/src/features/browser/HandsBars.tsx
@@ -30,6 +30,7 @@ export function AskCallout({ bot, task, hands }: { bot: Bot; task: string; hands
{t.window.hint}
); @@ -83,6 +84,7 @@ export function HeldBar({ {focused ? t.hands.typing : t.hands.clickToType} +{t.window.hint}
); } diff --git a/app/src/features/browser/window.test.tsx b/app/src/features/browser/window.test.tsx index c63955e6..19488414 100644 --- a/app/src/features/browser/window.test.tsx +++ b/app/src/features/browser/window.test.tsx @@ -45,6 +45,14 @@ describe("the browser in a window of its own", () => { await waitFor(() => expect(within(panel()).queryByText(/keeps the login/)).toBeNull()); }); + test("is suggested while the owner is in control, where a sign-in may fail", async () => { + await openScout(); + fireEvent.click(within(panel()).getByRole("button", { name: "Take control" })); + expect(await within(panel()).findByText("You are in control")).toBeDefined(); + expect(within(panel()).getByText(/won't let you sign in here\?/)).toBeDefined(); + expect(within(panel()).getByRole("button", { name: "Sign in in a window" })).toBeDefined(); + }); + test("is offered when the bot asks for a hand", async () => { const { fake, scout } = await openScout(); act(() => { @@ -52,6 +60,9 @@ describe("the browser in a window of its own", () => { }); const asking = await within(panel()).findByText("Sign in to your Google account"); expect(asking).toBeDefined(); + const hint = /won't let you sign in here\? Use Sign in in a window/; + expect(within(panel()).getByText(hint)).toBeDefined(); + expect(screen.getByText(/won't let you sign in there, use Sign in in a window/)).toBeDefined(); fireEvent.click(within(panel()).getByRole("button", { name: "Sign in in a window" })); expect(await within(panel()).findByText(/keeps the login/)).toBeDefined(); diff --git a/app/src/i18n/en/browser.ts b/app/src/i18n/en/browser.ts index 33688120..1a21372b 100644 --- a/app/src/i18n/en/browser.ts +++ b/app/src/i18n/en/browser.ts @@ -87,11 +87,12 @@ export const browser = { title: "Open in a window", body: (bot: string) => `Sign in in the window that opened, then close it. ${bot} waits meanwhile and keeps the login.`, + hint: "Site won't let you sign in here? Use Sign in in a window, and close the window when you're done.", }, help: { needs: (bot: string) => `${bot} needs you in the browser`, asks: (bot: string, task: string) => `${bot} asks: ${task}`, - why: "Take control, do it on the page yourself and give it back. The bot doesn't see what you type in password fields.", + why: "Take control, do it on the page yourself and give it back. If the site won't let you sign in there, use Sign in in a window in the browser panel. The bot doesn't see what you type in password fields.", take: "Take the browser", done: "Done", wontDo: "I won't do it", diff --git a/app/src/i18n/es/browser.ts b/app/src/i18n/es/browser.ts index cca1a261..e519b773 100644 --- a/app/src/i18n/es/browser.ts +++ b/app/src/i18n/es/browser.ts @@ -87,11 +87,12 @@ export const browser: Messages["browser"] = { title: "Abierto en una ventana", body: (bot) => `Inicia sesión en la ventana que se abrió y luego ciérrala. ${bot} espera mientras tanto y conserva la sesión.`, + hint: "¿El sitio no te deja iniciar sesión aquí? Usa Iniciar sesión en una ventana y ciérrala al terminar.", }, help: { needs: (bot) => `${bot} te necesita en el navegador`, asks: (bot, task) => `${bot} pide: ${task}`, - why: "Toma el control, hazlo en la página y devuélvelo. El bot no ve lo que escribes en campos de contraseña.", + why: "Toma el control, hazlo en la página y devuélvelo. Si el sitio no te deja iniciar sesión ahí, usa Iniciar sesión en una ventana en el panel del navegador. El bot no ve lo que escribes en campos de contraseña.", take: "Tomar el navegador", done: "Listo", wontDo: "No lo haré", diff --git a/app/src/i18n/pt-BR/browser.ts b/app/src/i18n/pt-BR/browser.ts index fe7ef25c..18bfb4d6 100644 --- a/app/src/i18n/pt-BR/browser.ts +++ b/app/src/i18n/pt-BR/browser.ts @@ -89,11 +89,12 @@ export const browser: Messages["browser"] = { title: "Aberto numa janela", body: (bot) => `Entre na conta na janela que abriu e depois feche-a. ${bot} espera enquanto isso e fica com o login.`, + hint: "O site não deixa entrar aqui? Use Entrar numa janela e feche a janela quando terminar.", }, help: { needs: (bot) => `${bot} precisa de você no navegador`, asks: (bot, task) => `${bot} pede: ${task}`, - why: "Assuma o controle, faça isso na página e devolva. O bot não vê o que você digita em campos de senha.", + why: "Assuma o controle, faça isso na página e devolva. Se o site não deixar entrar por ali, use Entrar numa janela no painel do navegador. O bot não vê o que você digita em campos de senha.", take: "Assumir o navegador", done: "Pronto", wontDo: "Não vou fazer", diff --git a/crates/botloftd/src/browser/agent.rs b/crates/botloftd/src/browser/agent.rs index b78e5801..b7fafa10 100644 --- a/crates/botloftd/src/browser/agent.rs +++ b/crates/botloftd/src/browser/agent.rs @@ -19,8 +19,10 @@ const SECURE_PAGE: &str = "chrome://version"; const READ_METADATA: &str = "navigator.userAgentData ? navigator.userAgentData\ .getHighEntropyValues(['architecture', 'bitness', 'fullVersionList', 'model', \ 'platformVersion', 'wow64']) : null"; -/// Tries while the page loads, 50 ms apart. -const TRIES: usize = 40; +/// Tries while the page loads, 50 ms apart: up to 10 s. Usually under +/// 100 ms, but a busy CI runner took over 2 s, and without the data the +/// brands go empty. +const TRIES: usize = 200; /// The params of `Emulation.setUserAgentOverride` for every tab. Run before /// anything else reads `events`: the page it reads from opens and closes diff --git a/crates/botloftd/src/tools/browser_catalog.rs b/crates/botloftd/src/tools/browser_catalog.rs index 3c5edd9b..511c45bf 100644 --- a/crates/botloftd/src/tools/browser_catalog.rs +++ b/crates/botloftd/src/tools/browser_catalog.rs @@ -157,8 +157,10 @@ pub(super) fn tools() -> Vec