2024-06-08 22:40:37 +05:30
|
|
|
/**
|
|
|
|
|
* A set of functions handling reproduction of user input
|
|
|
|
|
* on the remote browser instance as well as the generation of workflow pairs.
|
|
|
|
|
* These functions are called by the client through socket communication.
|
|
|
|
|
*/
|
|
|
|
|
import { Socket } from 'socket.io';
|
|
|
|
|
|
|
|
|
|
import logger from "../logger";
|
|
|
|
|
import { Coordinates, ScrollDeltas, KeyboardInput } from '../types';
|
|
|
|
|
import { browserPool } from "../server";
|
|
|
|
|
import { WorkflowGenerator } from "../workflow-management/classes/Generator";
|
|
|
|
|
import { Page } from "playwright";
|
|
|
|
|
import { throttle } from "../../../src/helpers/inputHelpers";
|
|
|
|
|
import { CustomActions } from "../../../src/shared/types";
|
|
|
|
|
|
2024-06-08 22:50:26 +05:30
|
|
|
/**
|
|
|
|
|
* A wrapper function for handling user input.
|
|
|
|
|
* This function gets the active browser instance from the browser pool
|
|
|
|
|
* and passes necessary arguments to the appropriate handlers.
|
|
|
|
|
* e.g. {@link Generator}, {@link RemoteBrowser.currentPage}
|
|
|
|
|
*
|
|
|
|
|
* Also ignores any user input while interpretation is in progress.
|
|
|
|
|
*
|
|
|
|
|
* @param handleCallback The callback handler to be called
|
|
|
|
|
* @param args - arguments to be passed to the handler
|
|
|
|
|
* @category HelperFunctions
|
|
|
|
|
*/
|
2024-06-08 22:50:12 +05:30
|
|
|
const handleWrapper = async (
|
|
|
|
|
handleCallback: (
|
|
|
|
|
generator: WorkflowGenerator,
|
|
|
|
|
page: Page,
|
|
|
|
|
args?: any
|
|
|
|
|
) => Promise<void>,
|
|
|
|
|
args?: any
|
|
|
|
|
) => {
|
|
|
|
|
const id = browserPool.getActiveBrowserId();
|
|
|
|
|
if (id) {
|
|
|
|
|
const activeBrowser = browserPool.getRemoteBrowser(id);
|
|
|
|
|
if (activeBrowser?.interpreter.interpretationInProgress() && !activeBrowser.interpreter.interpretationIsPaused) {
|
|
|
|
|
logger.log('debug', `Ignoring input, while interpretation is in progress`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const currentPage = activeBrowser?.getCurrentPage();
|
|
|
|
|
if (currentPage && activeBrowser) {
|
|
|
|
|
if (args) {
|
|
|
|
|
await handleCallback(activeBrowser.generator, currentPage, args);
|
|
|
|
|
} else {
|
|
|
|
|
await handleCallback(activeBrowser.generator, currentPage);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logger.log('warn', `No active page for browser ${id}`);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
logger.log('warn', `No active browser for id ${id}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 22:51:51 +05:30
|
|
|
/**
|
|
|
|
|
* An interface for custom action description.
|
|
|
|
|
* @category Types
|
|
|
|
|
*/
|
2024-06-08 22:51:37 +05:30
|
|
|
interface CustomActionEventData {
|
|
|
|
|
action: CustomActions;
|
|
|
|
|
settings: any;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 22:53:09 +05:30
|
|
|
/**
|
|
|
|
|
* A wrapper function for handling custom actions.
|
|
|
|
|
* @param customActionEventData The custom action event data
|
|
|
|
|
* @category HelperFunctions
|
|
|
|
|
*/
|
2024-06-08 22:52:51 +05:30
|
|
|
const onGenerateAction = async (customActionEventData: CustomActionEventData) => {
|
|
|
|
|
logger.log('debug', `Generating ${customActionEventData.action} action emitted from client`);
|
|
|
|
|
await handleWrapper(handleGenerateAction, customActionEventData);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 22:54:29 +05:30
|
|
|
/**
|
|
|
|
|
* Handles the generation of a cutom action workflow pair.
|
|
|
|
|
* @param generator The workflow generator
|
|
|
|
|
* @param page The active page
|
|
|
|
|
* @param action The custom action
|
|
|
|
|
* @param settings The custom action settings
|
|
|
|
|
* @category BrowserManagement
|
|
|
|
|
*/
|
2024-06-08 22:54:15 +05:30
|
|
|
const handleGenerateAction =
|
|
|
|
|
async (generator: WorkflowGenerator, page: Page, {action, settings}: CustomActionEventData) => {
|
|
|
|
|
await generator.customAction(action, settings, page);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 22:55:12 +05:30
|
|
|
/**
|
|
|
|
|
* A wrapper function for handling mousedown event.
|
|
|
|
|
* @param coordinates - coordinates of the mouse click
|
|
|
|
|
* @category HelperFunctions
|
|
|
|
|
*/
|
|
|
|
|
const onMousedown = async (coordinates: Coordinates) => {
|
|
|
|
|
logger.log('debug', 'Handling mousedown event emitted from client');
|
|
|
|
|
await handleWrapper(handleMousedown, coordinates);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 22:56:55 +05:30
|
|
|
|
|
|
|
|
const handleMousedown = async (generator: WorkflowGenerator, page: Page, { x, y }: Coordinates) => {
|
|
|
|
|
await generator.onClick({ x, y }, page);
|
|
|
|
|
const previousUrl = page.url();
|
|
|
|
|
const tabsBeforeClick = page.context().pages().length;
|
|
|
|
|
await page.mouse.click(x, y);
|
2024-06-08 22:57:47 +05:30
|
|
|
// try if the click caused a navigation to a new url
|
|
|
|
|
try {
|
|
|
|
|
await page.waitForNavigation({ timeout: 2000 });
|
|
|
|
|
const currentUrl = page.url();
|
|
|
|
|
if (currentUrl !== previousUrl) {
|
|
|
|
|
generator.notifyUrlChange(currentUrl);
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
const {message} = e as Error;
|
|
|
|
|
} //ignore possible timeouts
|
|
|
|
|
|
2024-06-08 22:57:19 +05:30
|
|
|
// check if any new page was opened by the click
|
|
|
|
|
const tabsAfterClick = page.context().pages().length;
|
|
|
|
|
const numOfNewPages = tabsAfterClick - tabsBeforeClick;
|
|
|
|
|
if (numOfNewPages > 0) {
|
|
|
|
|
for (let i = 1; i <= numOfNewPages; i++) {
|
|
|
|
|
const newPage = page.context().pages()[tabsAfterClick - i];
|
|
|
|
|
if (newPage) {
|
|
|
|
|
generator.notifyOnNewTab(newPage, tabsAfterClick - i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-08 22:56:55 +05:30
|
|
|
logger.log('debug', `Clicked on position x:${x}, y:${y}`);
|
|
|
|
|
};
|
|
|
|
|
|