Skip to content
This repository was archived by the owner on Jul 17, 2020. It is now read-only.

Commit 1fd7c52

Browse files
committedAug 12, 2017
Use headless chrome instead of nightmare
huzzah!
1 parent e0eced8 commit 1fd7c52

File tree

3 files changed

+107
-114
lines changed

3 files changed

+107
-114
lines changed
 

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "A chatbot for StackOverflow.",
55
"main": "run-headless.js",
66
"dependencies": {
7-
"nightmare": "^1.8.1"
7+
"chrome-remote-interface": "^0.24.3"
Has a conversation. Original line has a conversation.
88
},
99
"devDependencies": {
1010
"browserify": "^13.0.0",

‎pics/.gitkeep

Whitespace-only changes.

‎run-headless.js

+106-113
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,119 @@
1-
var Nightmare = require('nightmare'),
2-
readline = require('readline');
3-
4-
var hound = new Nightmare({
5-
cookiesFile: 'cookies.jar'
6-
});
1+
var cri = require('chrome-remote-interface');
2+
var fs = require('fs');
3+
var repl = require('readline');
4+
var util = require('util');
75

86
var config = require('./run-headless.config.json');
97

10-
function once(fn) {
11-
var called = false, res;
12-
return function () {
13-
if (called) {
14-
return res;
15-
}
8+
cri(async (client) => {
9+
try {
10+
await demLogics(client);
11+
}
12+
catch (e) {
13+
console.error(e);
14+
}
15+
finally {
16+
client.close();
17+
console.log('Have a nice day!');
18+
}
19+
}).on('error', console.error);
20+
21+
async function demLogics(client) {
22+
var { DOM, Page, Runtime } = client;
23+
24+
await Promise.all([
25+
DOM.enable(),
26+
Page.enable(),
27+
Runtime.enable()
28+
]);
29+
30+
await Page.navigate({ url: config.siteUrl + '/users/login/' });
31+
await Page.loadEventFired();
32+
await screenshot('pics/login-pre.png', client);
33+
34+
var url = await getUrl(client);
35+
if (!/login-add($|\?)/.test(url)) {
36+
console.log('Need to authenticate');
37+
await loginToSE(client);
38+
}
39+
else {
40+
console.log('Cool, already logged in');
41+
}
42+
43+
await injectBot(client);
44+
console.log('Injected bot');
45+
}
46+
47+
async function loginToSE(client) {
48+
var { DOM, Page } = client;
1649

17-
called = true;
18-
res = fn.apply(this, arguments);
50+
var { root: { nodeId: documentId } } = await DOM.getDocument({ depth: 0 });
1951

20-
return res;
21-
};
52+
await Promise.all([
53+
type('#login-form #email', config.email, client, documentId),
54+
type('#login-form #password', config.password, client, documentId)
55+
]);
56+
await screenshot('pics/login-filled.png', client);
57+
58+
await click('#login-form #submit-button', client);
59+
await Page.loadEventFired();
60+
await screenshot('pics/login-post.png', client);
2261
}
2362

24-
hound.drainQueue = function (cb) {
25-
var self = hound;
26-
27-
setTimeout(next, 0);
28-
function next(err) {
29-
var item = self.queue.shift();
30-
if (!item) {
31-
cb && cb(err, self);
32-
return;
33-
}
34-
35-
var method = item[0],
36-
args = item[1];
37-
args.push(once(next));
38-
method.apply(self, args);
39-
}
63+
async function injectBot(client) {
64+
var { Page, Runtime } = client;
65+
66+
await Page.navigate({ url: config.roomUrl });
67+
await Page.loadEventFired();
68+
await screenshot('pics/chat.png', client);
69+
70+
await Runtime.evaluate({
71+
expression: `
72+
var script = document.createElement('script');
73+
script.src = 'https://rawgit.com/Zirak/SO-ChatBot/master/master.js';
74+
script.onload = function() {
75+
bot.activateDevMode();
76+
console.log('Loaded bot');
77+
bot.adapter.out.add('I will derive!');
4078
};
79+
document.head.appendChild(script);`
80+
});
81+
}
82+
83+
// rando utils because fml
4184

42-
function seLogin () {
43-
hound
44-
.type('#se-login input[type="email"]', config.email)
45-
.type('#se-login input[type="password"]', config.password)
46-
.click('#se-login input[type="button"]')
47-
.wait()
48-
.screenshot('pics/login.png');
85+
async function type(selector, value, { DOM }, documentId) {
86+
var { nodeId } = await DOM.querySelector({
87+
selector,
88+
nodeId: documentId
89+
});
90+
91+
await DOM.setAttributeValue({
92+
nodeId,
93+
name: 'value',
94+
value
95+
});
4996
}
50-
function injectToChat (hound) {
51-
hound
52-
.goto(config.roomUrl)
53-
.wait()
54-
.screenshot('pics/chat.png')
55-
.evaluate(function () {
56-
var script = document.createElement('script');
57-
script.src = 'https://raw.github.com/Zirak/SO-ChatBot/master/master.js';
58-
script.onload = function() {
59-
bot.activateDevMode();
60-
console.log('Loaded bot');
61-
bot.adapter.out.add('I will derive!');
62-
};
63-
document.head.appendChild(script);
64-
}, function () {
65-
console.log('Injected chatbot.');
66-
});
97+
98+
async function click(selector, { Runtime }) {
99+
// as of yet, the only way to dispatch mouse events is via coordinates
100+
// that's rather lame so we do something lamer
101+
await Runtime.evaluate({
102+
expression: `document.querySelector('${selector}').click()`
103+
});
104+
// plz no kill me i have family, kidnap me and depand ransom
105+
// or just kill them
67106
}
68107

69-
hound
70-
.goto(config.siteUrl + '/users/login/')
71-
.screenshot('pics/pre-login.png')
72-
.wait(1000)
73-
.url(function (url) {
74-
if (!/login-add$/.test(url)) {
75-
console.log('Need to authenticate');
76-
hound.use(seLogin);
77-
}
78-
else {
79-
console.log('Cool, already logged in');
80-
}
81-
82-
hound.use(injectToChat);
83-
hound.drainQueue(function () {
84-
console.log('Should be done loading stuff.');
85-
hitTheRepl();
86-
});
87-
})
88-
.setup(function () {
89-
hound.drainQueue();
90-
});
91-
92-
function hitTheRepl() {
93-
var repl = readline.createInterface({
94-
input: process.stdin,
95-
output: process.stdout
96-
});
97-
98-
console.log('You are now in a REPL with the remote page. Have fun!');
99-
100-
hound.on('consoleMessage', function (msg) {
101-
console.log('<', msg);
102-
});
103-
hound.on('error', function (msg) {
104-
console.log('! ', msg);
105-
});
106-
107-
repl.on('line', function (data) {
108-
hound.evaluate(function (code) {
109-
try {
110-
return eval(code);
111-
}
112-
catch (e) {
113-
return e.message;
114-
}
115-
}, function (res) {
116-
console.log('$', res);
117-
repl.prompt();
118-
}, data).drainQueue();
119-
});
120-
repl.on('close', function () {
121-
console.log('Leaving the nightmare...');
122-
hound.teardownInstance();
123-
});
124-
125-
repl.prompt();
108+
async function getUrl({ Runtime }) {
109+
var { result: { value } } = await Runtime.evaluate({
110+
expression: 'location.href'
111+
});
112+
return value;
113+
}
114+
115+
async function screenshot(ssName, { Page }) {
116+
var ss = new Buffer((await Page.captureScreenshot()).data, 'base64');
117+
118+
return await util.promisify(fs.writeFile)(ssName, ss, 'base64');
126119
}

0 commit comments

Comments
 (0)
This repository has been archived.