-
Notifications
You must be signed in to change notification settings - Fork 4
/
utils.ts
175 lines (161 loc) · 6.23 KB
/
utils.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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//deno-lint-ignore-file no-explicit-any
import {fromUnixTime, format} from 'https://raw.githubusercontent.com/pr0ggy/deno-date-fns/deno_bundle_fixes/index.js'
import {renderFile} from 'mustache_ts'
import type {Document} from 'deno_dom'
export const getQuery = (doc: Document): string => doc.querySelector('title')!.textContent.split(' - ')[0]
export function getBigFatJS(doc: Document): string {
let bigFatJS = ''
doc.querySelectorAll('script').forEach(el => el.textContent.includes('(function(){var u=') ? bigFatJS = el.textContent : null)
return bigFatJS
}
export function getRdmUA() {
const rdmStr = () => Date.now().toString(36) + Math.random().toString(36).substring(2)
const OSs = ['Windows NT;', 'Macintosh', 'X11; Linux x86_64', 'X11; Linux i686', 'X11; CrOS i686', 'X11; OpenBSD i386', 'X11; NetBSD']
return `Mozilla/5.0 (${OSs[~~(Math.random() * OSs.length)]} ${rdmStr()}) AppleWebKit/${rdmStr()} (KHTML, like Gecko) Chrome/${Math.floor(Math.random() * (500 - 50)) + 49} Safari/${rdmStr()} OPR/${rdmStr()}`
}
export function fetchURL(URL: string) {
return fetch(URL, {
headers: {
'user-agent': getRdmUA(),
'Origin': 'https://google.com'
}
})
}
export async function getFinanceData(period: string, id: string) {
let interval = ''
if (period === '1d') interval = '300'
else if (period === '5d') interval = '1800'
else if (period === ('5Y' || '40Y')) interval = '604800'
else interval = '86400'
const URL = 'https://www.google.com/async/finance_wholepage_chart?async=mid_list:/'
+ id
+ ',period:'
+ period
+ ',interval:'
+ interval
+ ',_fmt:pc'
return await fetchURL(URL)
.then(res => res.text())
.then(res => {
//console.log('---------------------------------------------------------')
//console.log(JSON.parse(res.substring(res.split('[[["')[0].length))[0][0][1])
const out: Record<string, (string | number)[]> = {
values: [],
labels: []
}
JSON.parse(JSON.parse(res.substring(res.split('[[["')[0].length))[0][0][1])[0][3][0][0][0][0].forEach((el: any) => {
out.values.push(el[2][0][0])
out.labels.push(format(fromUnixTime(el[5] * 60), 'dd/MM/yyyy HH:mm', {})) //TODO: better tooltip (relative from today for short period, no year for current year)
})
return out
})
}
export async function rendPage(page: string, data: Record<string, any>, lang: string): Promise<string> {
return await renderFile(Deno.cwd() + '/templates/base.hbs', {
page: await renderFile(Deno.cwd() + '/pages/' + page + '.hbs', data),
lang: lang || 'en',
style: page
})
}
export async function rendMenu(doc: Document, imagesTab = false): Promise<Record<string, string>> {
const data: any = {
shownMenu: [],
hiddenMenu: []
}
if (imagesTab) {
doc.querySelectorAll('.m3kSL').forEach((el, i) => {
const rawID = el.parentElement!.getAttribute('href')!
let ID = ''
if (rawID == null) ID = 'images'
else if (rawID.includes('//maps')) ID = 'maps'
else if (i === 0) ID = 'all'
else ID = {
'vid': 'videos',
'nws': 'news',
'shop': 'shopping',
'bks': 'books',
'flm': 'flights',
'fin': 'finance'
}[rawID.split('tbm=')[1].split('&')[0]]!
if (i < 5) data.shownMenu.push({
id: ID,
value: el.parentElement!.textContent
})
else data.hiddenMenu.push({
id: ID,
value: el.parentElement!.textContent
})
})
} else {
const raw = getBigFatJS(doc).split('var m=[')[1].split(';')[0]
const menuIDs = ['WEB', 'IMAGES', 'VIDEOS', 'NEWS', 'SHOPPING', 'BOOKS', 'MAPS', 'FLIGHTS', 'FINANCE']
const baseMenu: any[] = []
menuIDs.forEach((_, i) => baseMenu.push([
raw.indexOf(menuIDs[i]),
raw.split(menuIDs[i])[0].split('\\x22')[raw.split(menuIDs[i])[0].split('\\x22').length - 3].split('\\x22')[0],
menuIDs[i] !== 'WEB' ? menuIDs[i].toLowerCase() : 'all'
]))
baseMenu.sort((a, b) => a[0] > b[0] ? 1 : -1)
baseMenu.forEach((el, i) =>
i < 5 ?
data.shownMenu.push({
id: el[2],
value: el[1]
})
: data.hiddenMenu.push({
id: el[2],
value: el[1]
}))
}
return {
menu: await renderFile(Deno.cwd() + '/templates/menu.hbs', data)
}
}
export async function rendSearch(doc?: Document): Promise<Record<string, string>> {
return {
search: await renderFile(Deno.cwd() + '/templates/search.hbs', doc ? {query: getQuery(doc!)} : {})
}
}
export async function rendNavigation(): Promise<Record<string, string>> {
return {
navigation: await renderFile(Deno.cwd() + '/templates/navigation.hbs', {})
}
}
export async function rendFinance(doc: Document): Promise<Record<string, string>> {
const data: any = {}
if (doc.querySelector('#knowledge-finance-wholepage__entity-summary')) {
data.hasFinance = true
data.finance = {
ID: getBigFatJS(doc).split("'[[\\x22/")[1].split('\\')[0],
positive: false,
data: '',
legend: {
price: '',
currency: '',
priceEvolution: '',
btns: [],
exchange: ''
},
details: []
}
if (doc.querySelector('.fw-price-up')) data.finance.positive = true //positive
await getFinanceData('1d', data.finance.ID).then(res => data.finance.data = JSON.stringify(res)) //data
data.finance.legend.price = doc.querySelector('.NprOob')!.textContent //price
data.finance.legend.currency = doc.querySelector('.knFDje')!.textContent //currency
data.finance.legend.priceEvolution = //price evolution
doc.querySelector('.WlRRw')!.children[0].textContent
+ doc.querySelector('.jBBUv')!.textContent
+ (data.finance.positive ? ' ▲ ' : ' ▼ ')
+ doc.querySelector('.jdUcZd')!.children[0].textContent
doc.querySelectorAll('.qUjgX').forEach(el => data.finance.legend.btns.push(el.textContent)) //buttons
data.finance.legend.exchange = doc.querySelector('.HfMth')!.textContent //exchange
doc.querySelectorAll('.JgXcPd').forEach(el => data.finance.details.push({ //details
title: el.textContent,
content: el.parentElement!.children[1].textContent
}))
}
//TODO: implement "Previous close"
return {
finance: await renderFile(Deno.cwd() + '/instantAnswers/finance.hbs', data)
}
}