This repository has been archived by the owner on Apr 22, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
/
captcha-harvester.js
103 lines (87 loc) · 2.74 KB
/
captcha-harvester.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
const http = require('http')
const net = require('net')
const fs = require('fs')
const ChromeLauncher = require('chrome-launcher')
const log = require('./logging')
class CaptchaHarvester {
/**
* Creates a new instance of the harvester
* @param url {URL} The website Url
* @param sitekey {string} The website sitekey
* @param userAgent {string} The user agent
* @param port {int} The response server port
*/
constructor(url, sitekey, userAgent, port = 7777) {
this._url = url
this._sitekey = sitekey
this._port = port
this._userAgent = userAgent
this._server = http.createServer(this.handleRequest.bind(this))
.on('connect', this.handleConnect.bind(this))
.on('error', (err) => log.error('Failed handling request.', err))
.listen(port)
this._result = false
}
handleConnect(req, clientSocket, head) {
clientSocket.on('error', () => clientSocket.end())
const {port, hostname} = new URL(`http://${req.url}`)
const serverSocket = net.connect(port || 80, hostname, () => {
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n')
serverSocket.write(head)
serverSocket.pipe(clientSocket)
clientSocket.pipe(serverSocket)
}).on('error', (err) => {
log.error('Failed proxying HTTPS request.', err)
clientSocket.end()
})
}
handleRequest(req, res) {
req.on('error', () => {
res.statusCode = 400
res.end()
})
res.on('error', (err) => {
log.error('Failed sending response.', err)
})
const reqUrl = new URL(req.url)
if (reqUrl.hostname === this._url.hostname && reqUrl.pathname === '/') {
let htmlBody = fs.readFileSync('./harvester-body.html').toString()
htmlBody = htmlBody.replace('{{SITEKEY}}', this._sitekey)
res.setHeader('Content-Type', 'text/html')
res.writeHead(200)
res.end(htmlBody)
} else if (reqUrl.hostname === 'captcha-result' && req.method === 'POST') {
let token = ''
req.on('data', (data) => token += data)
req.on('end', () => {
this._result = token
res.statusCode = 200
res.end()
})
} else {
res.statusCode = 404
res.end()
}
}
/**
* Solves the captcha by opening a Chrome instance.
* @returns {Promise<string>} The promise resolving to the hCaptcha result
*/
async solveCaptcha() {
if (!this._server)
throw new Error('Cannot reuse instance of CaptchaHarvester.')
const chrome = await ChromeLauncher.launch({
startingUrl: this._url.toString().replace('https:', 'http:'),
chromeFlags: [`--proxy-server=http://127.0.0.1:${this._port}`]
})
log.info('Please solve the captcha in the browser window.')
while (!this._result) {
await new Promise((resolve) => setTimeout(resolve, 100))
}
await chrome.kill()
this._server.close()
this._server = null
return this._result
}
}
module.exports = CaptchaHarvester