|
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'); |
7 | 5 |
|
8 | 6 | var config = require('./run-headless.config.json');
|
9 | 7 |
|
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; |
16 | 49 |
|
17 |
| - called = true; |
18 |
| - res = fn.apply(this, arguments); |
| 50 | + var { root: { nodeId: documentId } } = await DOM.getDocument({ depth: 0 }); |
19 | 51 |
|
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); |
22 | 61 | }
|
23 | 62 |
|
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!'); |
40 | 78 | };
|
| 79 | +document.head.appendChild(script);` |
| 80 | + }); |
| 81 | +} |
| 82 | + |
| 83 | +// rando utils because fml |
41 | 84 |
|
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 | + }); |
49 | 96 | }
|
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 |
67 | 106 | }
|
68 | 107 |
|
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'); |
126 | 119 | }
|