Skip to content

Instantly share code, notes, and snippets.

@prescience-data
Last active September 8, 2023 00:38
Show Gist options
  • Save prescience-data/7345034897119256febccad5a8d8ca5e to your computer and use it in GitHub Desktop.
Save prescience-data/7345034897119256febccad5a8d8ca5e to your computer and use it in GitHub Desktop.
Demo of biometric typing skeleton logic for Playwright
import { chromium, Page } from "playwright-core"
/**
* Entrypoint.
*
* @remarks
* Demonstrates a safe typing algorithm that mimics human typing cadence.
*
* @public
*/
export async function main(): Promise<void> {
const browser = await chromium.launch({ headless: false })
const page = await browser.newPage()
await page.goto("https://www.google.com")
await typeInto(page, "input[name=q]", "Hello, World!")
}
/**
* Safely type into a text input.
*
* @param page - Current page instance.
* @param selector - DOM selector to type into.
* @param text - Text string to type.
*
* @internal
*/
async function typeInto(
page: Page,
selector: string,
text: string
): Promise<void> {
// Split input text into characters.
const chars = text.split("")
for (let i = 0; i < chars.length; i++) {
const currentKey = chars[i]
const previousKey = i > 0 ? chars[i - 1] : undefined
// Handle biometric cadence delay between keys.
if (previousKey) {
const cadenceDelay = rollCadenceDelay(previousKey, currentKey)
await page.waitForTimeout(cadenceDelay)
// Handle typing mistakes.
const typoKey = rollTypo(previousKey, currentKey)
if (typoKey) {
const typoPressDelay = rollPressDelay(typoKey)
const typoCadenceDelay = rollCadenceDelay(previousKey, "Backspace")
await page.keyboard.press(typoKey, { delay: typoPressDelay })
await page.waitForTimeout(typoCadenceDelay)
const backspacePressDelay = rollPressDelay("Backspace")
await page.keyboard.press("Backspace", { delay: backspacePressDelay })
}
}
// Handle key up down delay.
const pressDelay = rollPressDelay(chars[i])
await page.type(selector, chars[i], { delay: pressDelay })
}
}
/**
* Biometrics logic for calculating a keypress delay for a specific key.
*
* @param fromKey - Source key.
*
* @returns Delay in milliseconds.
*/
function rollPressDelay(fromKey: string): number {
// TODO: Add specific logic per key.
return rand(2, 5)
}
/**
* Biometrics logic for calculating a delay between keys.
*
* @param fromKey - Previous key.
* @param toKey - Current key.
*
* @returns Delay in milliseconds.
*/
function rollCadenceDelay(fromKey: string, toKey: string): number {
// TODO: Add specific logic per key transition.
return rand(6, 8)
}
/**
* Biometrics logic for calculating a chance of a typo between keys.
*
* @param fromKey - Previous key.
* @param toKey - Current key.
*
* @returns True if a typo should be made.
*/
function rollTypo(fromKey: string, toKey: string): string | undefined {
// TODO: Add specific logic per key transition.
if (chance(10)) {
// TODO: Pick a key most likely to be a typo based on vector.
return "abcdefghiklmnopqrstuvwxyz".split("")[rand(0, 25)]
}
return undefined
}
/**
* Generates a random number within range.
*
* @param min - Min value.
* @param max - Max value.
*
* @returns Random number.
*/
function rand(min: number, max?: number): number {
return Math.floor(Math.random() * ((max ?? min) - (max ? min : 0)) + (max ? min : 0))
}
/**
* Rolls a weighted chance.
*
* @param weight - Weight of chance.
*
* @returns True if chance is successful.
*/
function chance(weight: number = 50): boolean {
return rand(0, 100) <= weight
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment