Skip to content

Commit 22b729c

Browse files
committed
Add support for funcaptcha to callback sniffer
1 parent 581a1ae commit 22b729c

6 files changed

Lines changed: 142 additions & 26 deletions

File tree

examples/app_stat.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
from pprint import pprint
33
from python_anticaptcha import AnticaptchaClient, ImageToTextTask
44
from sys import argv
5+
56
api_key = environ["KEY"]
67

78
soft_id = argv[1]
89
mode = argv[2]
910

11+
1012
def process(soft_id, mode):
1113
client = AnticaptchaClient(api_key)
1214
pprint(client.getAppStats(soft_id, mode))
1315

16+
1417
if __name__ == "__main__":
1518
pprint(process(soft_id, mode))

examples/balance.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
from pprint import pprint
33
from python_anticaptcha import AnticaptchaClient, ImageToTextTask
44
from sys import argv
5+
56
api_key = environ["KEY"]
67

78

89
def process():
910
client = AnticaptchaClient(api_key)
1011
pprint(client.getBalance())
1112

13+
1214
if __name__ == "__main__":
1315
pprint(process())

examples/callback_sniffer.js

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,50 @@
1+
recaptchaCallback = typeof recaptchaCallback == 'undefined' ? [] : recaptchaCallback;
12
(function () {
2-
if (!window.grecaptcha) {
3+
if (typeof grecaptcha == 'undefined') {
34
console.log('Avoid wrapping before load grecaptcha');
45
return;
56
};
6-
if (window.grecaptcha.wrapped) {
7+
if (!typeof grecaptcha.recaptchaCallback == 'undefined') {
78
console.log('Avoid multiple wrapping of grecaptcha');
89
return;
910
};
1011
const x = grecaptcha.render;
12+
grecaptcha.recaptchaCallback = recaptchaCallback;
1113
grecaptcha.render = function () {
1214
console.log('grecaptcha arguments', arguments);
13-
const cb = arguments[1].callback;
1415
if (arguments[1] && arguments[1].callback) {
15-
window.recaptchaCallback = window.recaptchaCallback || [];
16-
window.recaptchaCallback.push(arguments[1].callback);
17-
console.log('recaptchaCallback', window.recaptchaCallback);
16+
const cb = arguments[1].callback;
17+
recaptchaCallback.push(cb);
18+
console.log('recaptchaCallback', cb);
1819
}
1920
return x(...arguments);
2021
};
21-
grecaptcha.wrapped = true;
22+
})();
23+
24+
funcaptchaCallback = typeof funcaptchaCallback == 'undefined' ? [] : funcaptchaCallback;
25+
(function () {
26+
if (typeof ArkoseEnforcement == 'undefined'){
27+
console.log('Avoid wrapping before load ArkoseEnforcement');
28+
return;
29+
};
30+
if (!typeof ArkoseEnforcement.funcaptchaCallback == 'undefined') {
31+
console.log('Avoid multiple wrapping of ArkoseEnforcement');
32+
return;
33+
};
34+
const extendFuncaptcha = function(cls) {
35+
function inner() {
36+
console.log('funcaptcha arguments', arguments);
37+
if (arguments[0] && arguments[0].callback) {
38+
const cb = arguments[0].callback;
39+
recaptchaCallback.push(cb);
40+
console.log('funcaptchaCallback', cb);
41+
}
42+
cls.apply(this, arguments)
43+
}
44+
inner.prototype = Object.create(cls.prototype);
45+
inner.funcaptchaCallback = recaptchaCallback;
46+
return inner;
47+
};
48+
ArkoseEnforcement = new extendFuncaptcha(FunCaptcha);
49+
FunCaptcha = new extendFuncaptcha(ArkoseEnforcement);
2250
})();
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import re
2+
import time
3+
from urllib.parse import urlparse, quote
4+
import os
5+
from os import environ
6+
import gzip
7+
8+
from python_anticaptcha import AnticaptchaClient, FunCaptchaProxylessTask
9+
from selenium.webdriver.common.by import By
10+
11+
api_key = environ["KEY"]
12+
site_key_pattern = 'public_key: "(.+?)",'
13+
url = "https://client-demo.arkoselabs.com/solo-animals"
14+
EXPECTED_RESULT = "Solved!"
15+
client = AnticaptchaClient(api_key)
16+
17+
DIR = os.path.dirname(os.path.abspath(__file__))
18+
19+
with open(os.path.join(DIR, "callback_sniffer.js"), "rb") as fp:
20+
wrapper_code = fp.read()
21+
22+
23+
def get_token(url, site_key):
24+
task = FunCaptchaProxylessTask(website_url=url, website_key=site_key)
25+
job = client.createTask(task)
26+
job.join(maximum_time=60 * 15)
27+
return job.get_token_response()
28+
29+
30+
def process(driver):
31+
driver.get(url)
32+
site_key = get_sitekey(driver)
33+
print("Found site-key", site_key)
34+
token = get_token(url, site_key)
35+
print("Found token", token)
36+
form_submit(driver, token)
37+
return driver.page_source
38+
39+
40+
def form_submit(driver, token):
41+
script = """
42+
document.getElementById('FunCaptcha-Token').value = decodeURIComponent('{0}');
43+
document.getElementById('verification-token').value = decodeURIComponent('{0}');
44+
document.getElementById('submit-btn').disabled = false;
45+
""".format(
46+
quote(token)
47+
)
48+
time.sleep(1)
49+
# as example call callback - not required in that example
50+
driver.execute_script("ArkoseEnforcement.funcaptchaCallback[0]('{}')".format(token))
51+
driver.find_element(By.ID, "submit-btn").click()
52+
time.sleep(1)
53+
54+
55+
def get_sitekey(driver):
56+
return re.search(site_key_pattern, driver.page_source).group(1)
57+
58+
59+
if __name__ == "__main__":
60+
from seleniumwire import webdriver # Import from seleniumwire
61+
62+
def custom(req, req_body, res, res_body):
63+
if not req.path:
64+
return
65+
if not "arkoselabs" in req.path:
66+
return
67+
if not res.headers.get("Content-Type", None) in [
68+
"text/javascript",
69+
"application/javascript",
70+
]:
71+
print(
72+
"Skip invalid content type",
73+
req.path,
74+
res.headers.get("Content-Type", None),
75+
)
76+
return
77+
if res.headers["Content-Encoding"] == "gzip":
78+
del res.headers["Content-Encoding"]
79+
res_body = gzip.decompress(res_body)
80+
return res_body + wrapper_code
81+
82+
driver = webdriver.Firefox(seleniumwire_options={"custom_response_handler": custom})
83+
84+
try:
85+
assert EXPECTED_RESULT in process(driver)
86+
finally:
87+
driver.close()

examples/recaptcha_selenium_callback.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
client = AnticaptchaClient(api_key)
1414

1515
DIR = os.path.dirname(os.path.abspath(__file__))
16-
wrapper_code = open(os.path.join(DIR, 'callback_sniffer.js'), 'rb').read()
16+
17+
with open(os.path.join(DIR, "callback_sniffer.js"), "rb") as fp:
18+
wrapper_code = fp.read()
19+
1720

1821
def get_token(url, site_key):
19-
task = NoCaptchaTaskProxylessTask(
20-
website_url=url, website_key=site_key
21-
)
22+
task = NoCaptchaTaskProxylessTask(website_url=url, website_key=site_key)
2223
job = client.createTask(task)
2324
job.join(maximum_time=60 * 15)
2425
return job.get_solution_response()
@@ -33,11 +34,12 @@ def process(driver):
3334
form_submit(driver, token)
3435
return driver.page_source
3536

37+
3638
def form_submit(driver, token):
3739
driver.execute_script(
3840
"document.getElementById('g-recaptcha-response').innerHTML='{}';".format(token)
3941
)
40-
driver.execute_script("window.recaptchaCallback[0]('{}')".format(token))
42+
driver.execute_script("grecaptcha.recaptchaCallback[0]('{}')".format(token))
4143
time.sleep(1)
4244

4345

@@ -49,20 +51,18 @@ def get_sitekey(driver):
4951
from seleniumwire import webdriver # Import from seleniumwire
5052

5153
def custom(req, req_body, res, res_body):
52-
if not req.path or not 'recaptcha' in req.path:
53-
return
54-
if not res.headers.get('Content-Type', None) == 'text/javascript':
54+
if not req.path or not "recaptcha" in req.path:
55+
return
56+
if not res.headers.get("Content-Type", None) == "text/javascript":
5557
return
56-
if res.headers['Content-Encoding'] == 'gzip':
57-
del res.headers['Content-Encoding']
58+
if res.headers["Content-Encoding"] == "gzip":
59+
del res.headers["Content-Encoding"]
5860
res_body = gzip.decompress(res_body)
5961
return res_body + wrapper_code
6062

61-
driver = webdriver.Firefox(seleniumwire_options={
62-
'custom_response_handler': custom
63-
})
63+
driver = webdriver.Firefox(seleniumwire_options={"custom_response_handler": custom})
6464

6565
try:
6666
assert EXPECTED_RESULT in process(driver)
6767
finally:
68-
driver.close()
68+
driver.close()

python_anticaptcha/base.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,7 @@ def getBalance(self):
192192
return response["balance"]
193193

194194
def getAppStats(self, soft_id, mode):
195-
request = {
196-
"clientKey": self.client_key,
197-
"softId": soft_id,
198-
"mode": mode
199-
}
195+
request = {"clientKey": self.client_key, "softId": soft_id, "mode": mode}
200196
response = self.session.post(
201197
urljoin(self.base_url, self.APP_STAT_URL), json=request
202198
).json()

0 commit comments

Comments
 (0)