Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose detectors and separate detect and collect functions #144

Merged
merged 15 commits into from
Nov 5, 2023
Merged
10 changes: 5 additions & 5 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import {
State,
} from './types'

export function detect<T extends AbstractDetectorDict>(
components: ComponentDict<any>,
detectors: T,
): [DetectionDict<T>, BotDetectionResult] {
const detections = {} as DetectionDict<T>
export function detect<T extends ComponentDict, K extends AbstractDetectorDict<T>>(
components: T,
detectors: K,
): [DetectionDict<K>, BotDetectionResult] {
const detections = {} as DetectionDict<K>
let finalDetection: BotDetectionResult = {
bot: false,
}
Expand Down
3 changes: 2 additions & 1 deletion src/detector.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BotDetectionResult, BotDetectorInterface, ComponentDict, DetectionDict } from './types'
import { collect, detect } from './api'
import { detectors, sources } from '.'
import { detectors } from './detectors'
import { sources } from './sources'

/**
* Class representing a bot detector.
Expand Down
17 changes: 12 additions & 5 deletions src/detectors/eval_length.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import { BrowserEngineKind, BrowserKind, ComponentDict, DetectorResponse, State

export function detectEvalLengthInconsistency({
evalLength,
browser: { browserKind: browser, BrowserEngineKind: browserEngine },
browserKind,
browserEngineKind,
}: ComponentDict): DetectorResponse {
if (evalLength.state !== State.Success) return
if (
evalLength.state !== State.Success ||
browserKind.state !== State.Success ||
browserEngineKind.state !== State.Success
)
return

const length = evalLength.value
return (
(length === 37 && !arrayIncludes([BrowserEngineKind.Webkit, BrowserEngineKind.Gecko], browserEngine)) ||
(length === 39 && !arrayIncludes([BrowserKind.IE], browser)) ||
(length === 33 && !arrayIncludes([BrowserEngineKind.Chromium], browserEngine))
(length === 37 && !arrayIncludes([BrowserEngineKind.Webkit, BrowserEngineKind.Gecko], browserEngineKind.value)) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, remove .value from enums.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also @Le0Developer please, merge new changes from main

(length === 39 && !arrayIncludes([BrowserKind.IE], browserKind.value)) ||
(length === 33 && !arrayIncludes([BrowserEngineKind.Chromium], browserEngineKind.value))
)
}
4 changes: 2 additions & 2 deletions src/detectors/notification_permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { BotKind, BrowserKind, ComponentDict, DetectorResponse, State } from '..

export function detectNotificationPermissions({
notificationPermissions,
browser: { browserKind },
browserKind,
}: ComponentDict): DetectorResponse {
if (browserKind !== BrowserKind.Chrome) return false
if (browserKind.state !== State.Success || browserKind.value !== BrowserKind.Chrome) return false

if (notificationPermissions.state === State.Success && notificationPermissions.value) {
return BotKind.HeadlessChrome
Expand Down
21 changes: 17 additions & 4 deletions src/detectors/plugins_inconsistency.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { BotKind, BrowserKind, ComponentDict, DetectorResponse, State } from '../types'
import { BotKind, BrowserEngineKind, BrowserKind, ComponentDict, DetectorResponse, State } from '../types'

export function detectPluginsLengthInconsistency({
pluginsLength,
browser: { browserKind, isAndroid },
android,
browserKind,
browserEngineKind,
}: ComponentDict): DetectorResponse {
if (pluginsLength.state !== State.Success) return
if (browserKind !== BrowserKind.Chrome || isAndroid) return
if (
pluginsLength.state !== State.Success ||
android.state !== State.Success ||
browserKind.state !== State.Success ||
browserEngineKind.state !== State.Success
)
return
if (
browserKind.value !== BrowserKind.Chrome ||
android.value ||
browserEngineKind.value !== BrowserEngineKind.Chromium
)
return
if (pluginsLength.value === 0) return BotKind.HeadlessChrome
}
12 changes: 6 additions & 6 deletions src/detectors/product_sub.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BotKind, BrowserKind, ComponentDict, DetectorResponse, State } from '../types'

export function detectProductSub({ productSub, browser: { browserKind } }: ComponentDict): DetectorResponse {
if (productSub.state !== State.Success) return false
export function detectProductSub({ productSub, browserKind }: ComponentDict): DetectorResponse {
if (productSub.state !== State.Success || browserKind.state !== State.Success) return false
if (
(browserKind === BrowserKind.Chrome ||
browserKind === BrowserKind.Safari ||
browserKind === BrowserKind.Opera ||
browserKind === BrowserKind.WeChat) &&
(browserKind.value === BrowserKind.Chrome ||
browserKind.value === BrowserKind.Safari ||
browserKind.value === BrowserKind.Opera ||
browserKind.value === BrowserKind.WeChat) &&
productSub.value !== '20030107'
)
return BotKind.Unknown
Expand Down
6 changes: 3 additions & 3 deletions src/detectors/rtt.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BotKind, ComponentDict, DetectorResponse, State } from '../types'

export function detectRTT({ rtt, browser: { isAndroid } }: ComponentDict): DetectorResponse {
if (rtt.state !== State.Success) return
export function detectRTT({ rtt, android }: ComponentDict): DetectorResponse {
if (rtt.state !== State.Success || android.state !== State.Success) return
// Rtt is 0 on android webview
if (isAndroid) return
if (android.value) return
if (rtt.value === 0) return BotKind.HeadlessChrome
}
6 changes: 3 additions & 3 deletions src/detectors/window_size.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BotKind, ComponentDict, DetectorResponse, State } from '../types'

export function detectWindowSize({ windowSize, browser: { documentFocus } }: ComponentDict): DetectorResponse {
if (windowSize.state !== State.Success) return false
export function detectWindowSize({ windowSize, documentFocus }: ComponentDict): DetectorResponse {
if (windowSize.state !== State.Success || documentFocus.state !== State.Success) return false
const { outerWidth, outerHeight } = windowSize.value
// When a page is opened in a new tab without focusing it right away, the window outer size is 0x0
if (!documentFocus) return
if (!documentFocus.value) return
if (outerWidth === 0 && outerHeight === 0) return BotKind.HeadlessChrome
}
5 changes: 5 additions & 0 deletions src/sources/android.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { isAndroid } from '../utils/browser'

export default function collectIsAndroid(): boolean {
return isAndroid()
}
Finesse marked this conversation as resolved.
Show resolved Hide resolved
27 changes: 0 additions & 27 deletions src/sources/browser.test.ts
Le0Developer marked this conversation as resolved.
Outdated
Show resolved Hide resolved

This file was deleted.

18 changes: 0 additions & 18 deletions src/sources/browser.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/sources/browser_engine_kind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { BrowserEngineKind } from '../types'
import { getBrowserEngineKind } from '../utils/browser'

export default function collectBrowserEngineKind(): BrowserEngineKind {
return getBrowserEngineKind()
}
6 changes: 6 additions & 0 deletions src/sources/browser_kind.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { BrowserKind } from '../types'
import { getBrowserKind } from '../utils/browser'

export default function collectBrowserKind(): BrowserKind {
return getBrowserKind()
}
5 changes: 5 additions & 0 deletions src/sources/document_focus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { getDocumentFocus } from '../utils/browser'

export default function hasDocumentFocus(): boolean {
return getDocumentFocus()
}
10 changes: 8 additions & 2 deletions src/sources/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import collectIsAndroid from './android'
import getAppVersion from './app_version'
import getBrowserInformation from './browser'
import collectBrowserKind from './browser_kind'
import collectBrowserEngineKind from './browser_engine_kind'
import getDocumentElementKeys from './document_element_keys'
import hasDocumentFocus from './document_focus'
import getErrorTrace from './error_trace'
import getEvalLength from './eval_length'
import getFunctionBind from './function_bind'
Expand All @@ -20,9 +23,12 @@ import getWindowSize, { WindowSizePayload } from './window_size'
import checkDistinctiveProperties, { DistinctivePropertiesPayload } from './distinctive_properties'

export const sources = {
android: collectIsAndroid,
browserKind: collectBrowserKind,
browserEngineKind: collectBrowserEngineKind,
documentFocus: hasDocumentFocus,
userAgent: getUserAgent,
appVersion: getAppVersion,
browser: getBrowserInformation,
rtt: getRTT,
windowSize: getWindowSize,
pluginsLength: getPluginsLength,
Expand Down
9 changes: 6 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ export type DefaultDetectorDict = typeof detectors
*/
export type SourceResponse<T> = T extends (...args: any[]) => any ? Awaited<ReturnType<T>> : T

export type AbstractDetector = (...args: any[]) => DetectorResponse
export type AbstractDetector<T> = (components: T) => DetectorResponse

export type AbstractSourceDict = Record<string, SourceResponse<any>>

export type AbstractDetectorDict = Record<string, AbstractDetector>
export type AbstractDetectorDict<T> = Record<string, AbstractDetector<T>>

export type AbstractComponentDict = Record<string, Component<any>>

Expand All @@ -100,7 +100,10 @@ export type AbstractDetectionsDict = Record<string, BotDetectionResult>
/**
* Represents a dictionary of detectors detection.
*/
export type DetectionDict<T extends AbstractDetectorDict = DefaultDetectorDict> = Record<keyof T, BotDetectionResult>
export type DetectionDict<T extends AbstractDetectorDict<any> = DefaultDetectorDict> = Record<
keyof T,
BotDetectionResult
>

/**
* Dictionary of components.
Expand Down