Spaces:
Runtime error
Runtime error
Commit ·
11b3fe5
1
Parent(s): 0fa57a6
Upload 3 files
Browse files- Dockerfile +50 -0
- api.py +78 -0
- recaptcha_base.py +50 -0
Dockerfile
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim-bullseye as builder
|
| 2 |
+
|
| 3 |
+
# Build dummy packages to skip installing them and their dependencies
|
| 4 |
+
RUN apt-get update \
|
| 5 |
+
&& apt-get install -y --no-install-recommends equivs \
|
| 6 |
+
&& equivs-control libgl1-mesa-dri \
|
| 7 |
+
&& printf 'Section: misc\nPriority: optional\nStandards-Version: 3.9.2\nPackage: libgl1-mesa-dri\nVersion: 99.0.0\nDescription: Dummy package for libgl1-mesa-dri\n' >> libgl1-mesa-dri \
|
| 8 |
+
&& equivs-build libgl1-mesa-dri \
|
| 9 |
+
&& mv libgl1-mesa-dri_*.deb /libgl1-mesa-dri.deb \
|
| 10 |
+
&& equivs-control adwaita-icon-theme \
|
| 11 |
+
&& printf 'Section: misc\nPriority: optional\nStandards-Version: 3.9.2\nPackage: adwaita-icon-theme\nVersion: 99.0.0\nDescription: Dummy package for adwaita-icon-theme\n' >> adwaita-icon-theme \
|
| 12 |
+
&& equivs-build adwaita-icon-theme \
|
| 13 |
+
&& mv adwaita-icon-theme_*.deb /adwaita-icon-theme.deb
|
| 14 |
+
|
| 15 |
+
FROM python:3.11-slim-bullseye
|
| 16 |
+
COPY --from=builder /*.deb /
|
| 17 |
+
WORKDIR /app
|
| 18 |
+
#RUN echo "deb http://deb.debian.org/debian/ unstable main contrib non-free" >> /etc/apt/sources.list
|
| 19 |
+
RUN apt update
|
| 20 |
+
RUN apt upgrade -y
|
| 21 |
+
RUN apt install -y python3 python3-pip libgl1-mesa-glx wget libglib2.0-dev sudo libpci-dev psmisc
|
| 22 |
+
RUN pip install playwright playwright_recaptcha requests loguru flask Flask-Limiter
|
| 23 |
+
RUN dpkg -i /libgl1-mesa-dri.deb \
|
| 24 |
+
&& dpkg -i /adwaita-icon-theme.deb \
|
| 25 |
+
# Install dependencies
|
| 26 |
+
&& apt-get update \
|
| 27 |
+
&& apt-get install -y --no-install-recommends xvfb dumb-init \
|
| 28 |
+
procps curl vim xauth \
|
| 29 |
+
# Remove temporary files and hardware decoding libraries
|
| 30 |
+
&& rm -rf /var/lib/apt/lists/* \
|
| 31 |
+
&& rm -f /usr/lib/x86_64-linux-gnu/libmfxhw* \
|
| 32 |
+
&& rm -f /usr/lib/x86_64-linux-gnu/mfx/* \
|
| 33 |
+
&& useradd --home-dir /app --shell /bin/sh foxer \
|
| 34 |
+
&& chown -R foxer:foxer .
|
| 35 |
+
|
| 36 |
+
RUN rm -rf /root/.cache
|
| 37 |
+
RUN chmod 777 -R "/usr/local/lib/python3.11/"
|
| 38 |
+
RUN chmod 777 -R "/app/"
|
| 39 |
+
RUN playwright install firefox --with-deps
|
| 40 |
+
|
| 41 |
+
USER foxer
|
| 42 |
+
RUN playwright install firefox
|
| 43 |
+
COPY recaptcha_base.py .
|
| 44 |
+
|
| 45 |
+
COPY api.py .
|
| 46 |
+
EXPOSE 8081
|
| 47 |
+
|
| 48 |
+
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
| 49 |
+
CMD ["/usr/local/bin/python","-u","/app/api.py"]
|
| 50 |
+
|
api.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import asyncio
|
| 3 |
+
from flask import Flask, jsonify, request, logging as flog
|
| 4 |
+
from flask_limiter.util import get_remote_address
|
| 5 |
+
import recaptcha_base
|
| 6 |
+
|
| 7 |
+
app = Flask(__name__)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def get_ipaddr():
|
| 11 |
+
if request.access_route:
|
| 12 |
+
print(request.access_route[0])
|
| 13 |
+
return request.access_route[0]
|
| 14 |
+
else:
|
| 15 |
+
return request.remote_addr or '127.0.0.1'
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
handler = flog.default_handler
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_token():
|
| 22 |
+
default_token = "init_token"
|
| 23 |
+
if os.path.exists("token"):
|
| 24 |
+
return open("token", "r").read().strip()
|
| 25 |
+
return default_token
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def check_request(required_data, data):
|
| 29 |
+
token = get_token()
|
| 30 |
+
if not data or any(key not in data for key in required_data):
|
| 31 |
+
print("Error:Invalid Request Data\n" + str(data))
|
| 32 |
+
return False
|
| 33 |
+
if data["token"] != token:
|
| 34 |
+
print("Error:Invalid Token\n" + str(data))
|
| 35 |
+
return False
|
| 36 |
+
return True
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@app.errorhandler(429)
|
| 40 |
+
def rate_limit_exceeded(e):
|
| 41 |
+
print(get_remote_address())
|
| 42 |
+
return jsonify(msg="Too many request"), 429
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@app.errorhandler(405)
|
| 46 |
+
def method_not_allowed(e):
|
| 47 |
+
print(get_remote_address())
|
| 48 |
+
return jsonify(msg="Unauthorized Request"), 405
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@app.route("/", methods=["GET"])
|
| 52 |
+
def index():
|
| 53 |
+
return jsonify(status_code=200, ip=get_ipaddr())
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
@app.route("/update/token", methods=["POST"])
|
| 57 |
+
def update_token():
|
| 58 |
+
require_data = ["token", "new_token"]
|
| 59 |
+
data = request.get_json(force=True, silent=True)
|
| 60 |
+
if not check_request(require_data, data):
|
| 61 |
+
return jsonify(msg="Unauthorized Request"), 403
|
| 62 |
+
token = open("token", "w+")
|
| 63 |
+
token.write(data["new_token"])
|
| 64 |
+
token.close()
|
| 65 |
+
return jsonify(msg="Token updated successfully", success=True)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
@app.route("/api/solve", methods=["POST"])
|
| 69 |
+
def solver_captcha():
|
| 70 |
+
require_data = ["token", "host", "site_key", "action"]
|
| 71 |
+
data = request.get_json(force=True, silent=True)
|
| 72 |
+
if not check_request(require_data, data):
|
| 73 |
+
return jsonify(msg="Unauthorized Request"), 403
|
| 74 |
+
resp = asyncio.run(recaptcha_base.get_token(data["host"], data["site_key"], data["action"]))
|
| 75 |
+
return resp
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
app.run(host="0.0.0.0", port=8081)
|
recaptcha_base.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from playwright.sync_api import sync_playwright
|
| 2 |
+
from playwright_recaptcha import recaptchav3
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
async def route_continuation(route, request, host, sitekey, action):
|
| 6 |
+
# 检查请求的URL,只拦截特定网站的请求
|
| 7 |
+
if request.url == f"https://{host}/":
|
| 8 |
+
print("start to solve")
|
| 9 |
+
# 修改DNS解析结果
|
| 10 |
+
await route.fulfill(status=200,
|
| 11 |
+
body="""
|
| 12 |
+
<!DOCTYPE html>
|
| 13 |
+
<html lang="en">
|
| 14 |
+
<head>
|
| 15 |
+
<meta charset="UTF-8">
|
| 16 |
+
<title>test</title>
|
| 17 |
+
<script src="https://www.recaptcha.net/recaptcha/api.js?render=%%%%%%%%%%%%%%%"></script>
|
| 18 |
+
</head>
|
| 19 |
+
<body>
|
| 20 |
+
<script>
|
| 21 |
+
grecaptcha.ready(function() {
|
| 22 |
+
grecaptcha.execute('%%%%%%%%%%%%%%%', {action: '$$$$$'}).then(function(token) {
|
| 23 |
+
console.log(token);
|
| 24 |
+
|
| 25 |
+
});
|
| 26 |
+
});
|
| 27 |
+
</script>
|
| 28 |
+
</body>
|
| 29 |
+
</html>
|
| 30 |
+
""".replace("%%%%%%%%%%%%%%%", sitekey).replace("$$$$$", action))
|
| 31 |
+
else:
|
| 32 |
+
await route.route.continue_()
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
async def get_token(host, sitekey, action):
|
| 36 |
+
async with sync_playwright() as p:
|
| 37 |
+
browser = await p.firefox.launch(headless=True)
|
| 38 |
+
try:
|
| 39 |
+
page = await browser.new_page()
|
| 40 |
+
await page.route('**/*', lambda route, request: route_continuation(route, request, host, sitekey, action))
|
| 41 |
+
await page.goto(f"https://{host}/")
|
| 42 |
+
async with recaptchav3.SyncSolver(page) as solver:
|
| 43 |
+
token = await solver.solve_recaptcha()
|
| 44 |
+
except Exception as e:
|
| 45 |
+
print(e)
|
| 46 |
+
token = str(e)
|
| 47 |
+
finally:
|
| 48 |
+
await page.close()
|
| 49 |
+
await browser.close()
|
| 50 |
+
return token
|