-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
176 lines (153 loc) · 6.24 KB
/
index.js
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
176
console.log("Init starting");
// puppeteer is our headless chromium browser which unlike wget or curl will actually render the contents of the site and allow us to interact with it
console.log("Load puppeteer");
const puppeteer = require('puppeteer');
// the following are our twilio credentials (sign up for a free trial at https://www.twilio.com/referral/MyIhxE)
console.log("Load twilio");
const twilioConfig = require ('./twilio-settings');
// ifttt webhook integration
const IFTTT = require('node-ifttt-maker');
const iftttConfig = require('./ifttt-settings');
// load the config for the pages we're watching
console.log("Load page config");
const pages = require ('./pages');
// node schedule acts as a crontab, allowing us to set a schedule to run functions
console.log("Load job scheduler");
var schedule = require('node-schedule');
(async () => {
// set the schedule frequency in minutes
var scheduleFrequency = 60;
var twilioClient;
var iftttClient;
// the following args are required for running chromium-browser in WSL2, probably not needed if running under native windows command, linux bash, or mac terminal.
console.log("Init browser");
const browser = await puppeteer.launch({
args: ['--disable-gpu', '--single-process','--no-sandbox']
});
if(twilioConfig.enabled){
console.log("Enabling twilio");
twilioClient = require('twilio')(twilioConfig.accountSid, twilioConfig.authToken);
}
if(iftttConfig.enabled){
console.log("Enabling ifttt");
iftttClient = new IFTTT(iftttConfig.makerKey);
}
// basically our init script, schedule the job to run
// run the first batch immediatly
console.log("Init job");
startJobs(pages);
// schedule the recurring future jobs
var j = schedule.scheduleJob('*/' + scheduleFrequency + ' * * * *', () => {
startJobs(pages);
});
console.log("Next job runs at: " + j.nextInvocation());
// open a new page, set the user agent, and browse to a url
// returns Promise<ChromePage>
async function OpenPage(url)
{
var page;
return browser.newPage()
.then((thisPage) =>{
page = thisPage;
thisPage.setViewport({
width: 1920,
height: 1080,
deviceScaleFactor: 1,
})
})
.then(() => {
console.log("Got new page, set user agent");
return page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36');
})
.then(() => {
console.log("User agent set, go to URL: " + url);
return page.goto(url);
})
.then(() => {
console.log(url + " Loaded");
return page
});
}
// our async function to actually scrape the website and check stock
// return void
async function Scrape(page)
{
var chromePage;
// spawn a new page and get a URL
OpenPage(page.url) //returns a chromePage
.then(chromePageRes => {
// set the page to a local variable so we can access it later.
chromePage = chromePageRes;
// take the chromepage and check for the selector
return chromePage.waitFor(page.selector, {timeout: 20000});
})
.then(element => element.getProperty('textContent'), ()=> new MockJsHandle()) // if the selector exists, get the textContent property from it, error creates empty object
.then(textContent => textContent.jsonValue()) // take the textContent property and return the jsonvalue
.then(text => text.match(page.contains)) // take the json value and regex match it against the page contains value
.then(result => {
console.log(page.id + " result: " + result);
if(result){
// out of stock condition matched
console.log("Still out of stock");
return;
}else{
// no out of stock condition (result should be null);
SendNotification(page);
return chromePage.screenshot({path: encodeURI(page.id) + GetDate() + 'in-stock.png'});
}
}).then(() => chromePage.close())
.catch(err => {
console.log(err)
});
}
// send sms message
// returns void
async function SendNotification(page)
{
if(iftttConfig.enabled){
const params = {value1: page.id, value2: page.url};
iftttClient
.request({event: iftttConfig.eventName, params: params})
.then((response) => {
console.log("Sent event " + iftttConfig.eventName + " to IFTTT");
})
.catch((err) => {});
}
if(twilioConfig.enabled){
twilioClient.messages
.create({
body: page.notificationMsg,
from: twilioConfig.callFromNumber,
to: twilioConfig.callToNumbers[0]
})
.then(message => console.log("Text sent:" + message.sid));
}
}
// itterate through our pages and scrape for stock status
function startJobs(pages){
console.log("Jobs start");
for(i = 0; i < pages.length; i++){
console.log(pages[i].id + " is starting");
Scrape(pages[i]);
}
}
})();
// empty element to stand in for a proper element if none are found
function MockJsHandle() {
return {
jsonValue: function(){
return '';
}
}
}
function GetDate()
{
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
var yyyy = today.getFullYear();
var hh = String(today.getHours());
var ii = String(today.getMinutes());
today = yyyy + mm + dd + '-' + hh + ii;
return today;
}