-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
includes-labels.ts
109 lines (88 loc) 路 2.9 KB
/
includes-labels.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import {Element, LabelQuery, Pricing} from './model';
import {Page} from 'puppeteer';
import {logger} from '../logger';
export type Selector = {
requireVisible: boolean;
selector: string;
type: 'innerHTML' | 'outerHTML' | 'textContent';
};
function isElementArray(query: LabelQuery): query is Element[] {
return Array.isArray(query) && query.length > 0 && typeof query[0] === 'object';
}
function getQueryAsElementArray(query: LabelQuery, defaultContainer: string): Array<Required<Element>> {
if (isElementArray(query)) {
return query.map(x => ({
container: x.container ?? defaultContainer,
text: x.text
}));
}
if (Array.isArray(query)) {
return [{
container: defaultContainer,
text: query
}];
}
return [{
container: query.container ?? defaultContainer,
text: query.text
}];
}
export async function pageIncludesLabels(page: Page, query: LabelQuery, options: Selector) {
const elementQueries = getQueryAsElementArray(query, options.selector);
const resolved = await Promise.all(elementQueries.map(async query => {
const selector = {...options, selector: query.container};
const contents = await extractPageContents(page, selector) ?? '';
if (!contents) {
return false;
}
logger.debug(contents);
return includesLabels(contents, query.text);
}));
return resolved.includes(true);
}
export async function extractPageContents(page: Page, selector: Selector): Promise<string | null> {
return page.evaluate((options: Selector) => {
// eslint-disable-next-line no-undef
const element: globalThis.HTMLElement | null = document.querySelector(options.selector);
if (!element) {
return null;
}
if (options.requireVisible && !(element.offsetWidth > 0 && element.offsetHeight > 0)) {
return null;
}
switch (options.type) {
case 'innerHTML':
return element.innerHTML;
case 'outerHTML':
return element.outerHTML;
case 'textContent':
return element.textContent;
default:
return 'Error: selector.type is unknown';
}
}, selector);
}
/**
* Checks if DOM has any related text.
*
* @param domText Complete DOM of website.
* @param searchLabels Search labels for a match.
*/
export function includesLabels(domText: string, searchLabels: string[]): boolean {
const domTextLowerCase = domText.toLowerCase();
return searchLabels.some(label => domTextLowerCase.includes(label.toLowerCase()));
}
export async function cardPrice(page: Page, query: Pricing, max: number, options: Selector) {
if (!max) {
return null;
}
const selector = {...options, selector: query.container};
const cardPrice = await extractPageContents(page, selector);
if (cardPrice) {
const priceSeperator = query.euroFormat ? /\./g : /,/g;
const cardpriceNumber = Number.parseFloat(cardPrice.replace(priceSeperator, '').match(/\d+/g)!.join('.'));
logger.debug(`Raw card price: ${cardPrice} | Limit: ${max}`);
return cardpriceNumber > max ? cardpriceNumber : null;
}
return null;
}