Path: blob/main/plugins/default-browser-emulator/lib/helpers/selectUserAgentOption.ts
1030 views
import { UAParser } from 'ua-parser-js';1import { pickRandom } from '@secret-agent/commons/utils';2import IUserAgentOption, { IVersion } from '@secret-agent/interfaces/IUserAgentOption';3import { IDataUserAgentOption, IDataUserAgentOptions } from '../../interfaces/IBrowserData';4import { latestChromeBrowserVersion } from '../../index';56const compareVersions = require('compare-versions');78export default function selectUserAgentOption(9userAgentSelector: string,10dataUserAgentOptions: IDataUserAgentOptions,11): IUserAgentOption {12userAgentSelector = userAgentSelector?.trim();13if (userAgentSelector === 'chrome-latest') userAgentSelector = '';1415if (!userAgentSelector) {16const filteredOptions = dataUserAgentOptions.filter(x => {17return (18x.browserName === 'chrome' &&19x.browserVersion.major === latestChromeBrowserVersion.major &&20x.browserVersion.minor === latestChromeBrowserVersion.minor21);22});23return pickRandomUserAgentOption(filteredOptions);24}2526if (userAgentSelector.startsWith('~')) {27return findUserAgentOption(userAgentSelector, dataUserAgentOptions);28}2930return createUserAgentOption(userAgentSelector);31}3233function pickRandomUserAgentOption(dataUserAgentOptions: IDataUserAgentOptions) {34const dataUserAgentOption = pickRandom<IDataUserAgentOption>(dataUserAgentOptions);35return convertToUserAgentOption(dataUserAgentOption);36}3738function findUserAgentOption(39userAgentSelector: string,40dataUserAgentOptions: IDataUserAgentOptions,41) {42const selectors = extractUserAgentSelectors(userAgentSelector);4344const filteredOptions = dataUserAgentOptions.filter(userAgentOption => {45return isSelectorMatch(userAgentOption, selectors);46});4748if (!filteredOptions.length) {49throw new Error(`No installed UserAgent Emulators match your criteria (${userAgentSelector})`);50}5152const dataUserAgentOption = pickRandom<IDataUserAgentOption>(filteredOptions);53return convertToUserAgentOption(dataUserAgentOption);54}5556function convertToUserAgentOption(dataUserAgentOption: IDataUserAgentOption) {57return {58...dataUserAgentOption,59strings: undefined,60string: pickRandom(dataUserAgentOption.strings),61} as IUserAgentOption;62}6364function isSelectorMatch(userAgentOption: IDataUserAgentOption, selectors: ISelector[]) {65if (!selectors.length) return true;6667const browserVersion = convertToSemVer(userAgentOption.browserVersion);68const operatingSystemVersion = convertToSemVer(userAgentOption.operatingSystemVersion);6970for (const { name, matches } of selectors) {71let version: string;72if (name === userAgentOption.browserName) {73version = browserVersion;74} else if (name === userAgentOption.operatingSystemName) {75version = operatingSystemVersion;76} else {77return false;78}7980for (const match of matches) {81if (match.version === '*.*.*') continue;82const isValid = compareVersions.compare(version, match.version, match.operator);8384// must match every selector85if (!isValid) return false;86}87}88return true;89}9091interface ISelectorMatch {92operator: string;93version: string;94}9596interface ISelector {97name: string;98matches: ISelectorMatch[];99}100101function convertToSemVer(version: IVersion) {102return [version.major, version.minor, version.patch].filter(x => x).join('.');103}104105function extractUserAgentSelectors(userAgentSelector: string): ISelector[] {106const selectorByName: { [name: string]: ISelector } = {};107const parts = userAgentSelector108.substr(1)109.toLowerCase()110.split('&')111.map(x => x.trim());112for (const part of parts) {113const matches = part.match(/^([a-z\s-]+)([\s><=]+)?([0-9.x*]+)?/);114if (!matches?.length) continue;115const [rawName, rawOperator, rawVersion] = matches.slice(1);116const name = cleanupName(rawName);117const operator = cleanupOperator(rawOperator);118let version = cleanupVersion(rawVersion);119selectorByName[name] = selectorByName[name] || { name, matches: [] };120121if (operator === '=') {122const versionParts = version.split('.');123const missingParts = 3 - versionParts.length;124for (let i = 0; i < missingParts; i += 1) {125versionParts.push('*');126}127version = versionParts.join('.');128}129if (version) selectorByName[name].matches.push({ operator, version });130}131132return Object.values(selectorByName);133}134135function cleanupName(name: string) {136name = name.trim();137if (name.startsWith('chrome')) return 'chrome';138if (name.startsWith('firefox')) return 'firefox';139if (name.startsWith('safari')) return 'safari';140if (name.startsWith('mac')) return 'mac-os';141if (name.startsWith('win')) return 'windows';142if (name.startsWith('linux')) return 'linux';143return name.split(' ')[0];144}145146function cleanupOperator(operator: string) {147if (!operator) return '=';148return operator.replace(/[^<>=]+/g, '');149}150151function cleanupVersion(version: string) {152if (!version) return '*';153return version.trim().replace(/[^0-9x*]+/g, '.');154}155156function createUserAgentOption(userAgentString: string): IUserAgentOption {157const uaParser = new UAParser(userAgentString);158const uaBrowser = uaParser.getBrowser();159const uaOs = uaParser.getOS();160161const [browserVersionMajor, browserVersionMinor, browserVersionPatch] = uaBrowser.version162.split('.')163.map(x => Number(x));164const browserName = (uaBrowser.name || '').toLowerCase().replace(' ', '-');165166let [osVersionMajor, osVersionMinor] = uaOs.version.split('.').map(x => Number(x));167const operatingSystemName = (uaOs.name || '').toLowerCase().replace(' ', '-');168if (osVersionMajor === 10 && osVersionMinor === 16) {169osVersionMajor = 11;170osVersionMinor = undefined;171}172173return {174browserName,175browserVersion: {176major: browserVersionMajor ? String(browserVersionMajor) : '1',177minor: browserVersionMinor ? String(browserVersionMinor) : '0',178patch: browserVersionPatch ? String(browserVersionPatch) : undefined,179},180operatingSystemName,181operatingSystemPlatform: '',182operatingSystemVersion: {183major: osVersionMajor ? String(osVersionMajor) : undefined,184minor: osVersionMinor ? String(osVersionMinor) : undefined,185},186string: userAgentString,187};188}189190191