mirror of
https://github.com/ThePhaseless/Byparr.git
synced 2025-03-15 09:50:20 +08:00
commit
0fa7209311
@ -1,8 +1,27 @@
|
|||||||
FROM mcr.microsoft.com/devcontainers/python:1-3.12-bullseye
|
FROM python:3.12
|
||||||
|
|
||||||
|
# Inspired by https://github.com/Hudrolax/uc-docker-alpine/
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apt update && apt upgrade -y && apt install -y\
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
gnupg \
|
||||||
|
bash \
|
||||||
|
stow
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apt install -y \
|
||||||
|
xvfb \
|
||||||
|
x11vnc \
|
||||||
|
fluxbox \
|
||||||
|
xterm \
|
||||||
|
git \
|
||||||
|
ca-certificates \
|
||||||
|
pipx \
|
||||||
|
chromium
|
||||||
|
|
||||||
USER vscode
|
|
||||||
RUN pipx install poetry
|
RUN pipx install poetry
|
||||||
RUN poetry config virtualenvs.in-project true
|
ENV DISPLAY=:0
|
||||||
RUN sudo apt update && sudo apt upgrade -y
|
# RUN poetry config virtualenvs.in-project true
|
||||||
RUN sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
|
||||||
RUN sudo apt install -y ./google-chrome-stable_current_amd64.deb
|
|
@ -6,7 +6,12 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"dockerfile": "Dockerfile"
|
"dockerfile": "Dockerfile"
|
||||||
},
|
},
|
||||||
"runArgs": ["-p", "8181:8191"],
|
"runArgs": [
|
||||||
|
"-p",
|
||||||
|
"8181:8191",
|
||||||
|
"--cap-add",
|
||||||
|
"SYS_ADMIN"
|
||||||
|
],
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
@ -19,19 +24,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"postStartCommand": "poetry install",
|
"postStartCommand": "./entrypoint.sh",
|
||||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
// "features": {},
|
// "features": {},
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
"forwardPorts": [6080],
|
"forwardPorts": [
|
||||||
"features": {
|
5900
|
||||||
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
|
]
|
||||||
"ghcr.io/devcontainers/features/desktop-lite:1": {}
|
|
||||||
}
|
|
||||||
// Use 'postCreateCommand' to run commands after the container is created.
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
||||||
// Configure tool-specific properties.
|
// Configure tool-specific properties.
|
||||||
// "customizations": {},
|
// "customizations": {},
|
||||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
// "remoteUser": "root"
|
// "remoteUser": "root"
|
||||||
}
|
}
|
15
.github/workflows/docker-publish.yml
vendored
15
.github/workflows/docker-publish.yml
vendored
@ -31,6 +31,13 @@ jobs:
|
|||||||
# with sigstore/fulcio when running outside of PRs.
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
id-token: write
|
id-token: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -81,13 +88,19 @@ jobs:
|
|||||||
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
|
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Upload error logs
|
||||||
|
if: ${{ failure() }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: error-screenshots
|
||||||
|
path: ./errors/*
|
||||||
|
|
||||||
# Sign the resulting Docker image digest except on PRs.
|
# Sign the resulting Docker image digest except on PRs.
|
||||||
# This will only write to the public Rekor transparency log when the Docker
|
# This will only write to the public Rekor transparency log when the Docker
|
||||||
# repository is public to avoid leaking data. If you would like to publish
|
# repository is public to avoid leaking data. If you would like to publish
|
||||||
|
52
Dockerfile
52
Dockerfile
@ -1,4 +1,37 @@
|
|||||||
FROM python:3.12
|
FROM python:3.12-alpine
|
||||||
|
|
||||||
|
# Inspired by https://github.com/Hudrolax/uc-docker-alpine/
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apk update && apk upgrade && \
|
||||||
|
apk add --no-cache --virtual .build-deps \
|
||||||
|
alpine-sdk \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
unzip \
|
||||||
|
gnupg
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
xvfb \
|
||||||
|
x11vnc \
|
||||||
|
fluxbox \
|
||||||
|
xterm \
|
||||||
|
libffi-dev \
|
||||||
|
openssl-dev \
|
||||||
|
zlib-dev \
|
||||||
|
bzip2-dev \
|
||||||
|
readline-dev \
|
||||||
|
git \
|
||||||
|
nss \
|
||||||
|
freetype \
|
||||||
|
freetype-dev \
|
||||||
|
harfbuzz \
|
||||||
|
ca-certificates \
|
||||||
|
ttf-freefont \
|
||||||
|
pipx \
|
||||||
|
chromium \
|
||||||
|
chromium-chromedriver
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 8191
|
EXPOSE 8191
|
||||||
@ -11,26 +44,15 @@ ENV \
|
|||||||
PYTHONDONTWRITEBYTECODE=1 \
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
# do not ask any interactive question
|
# do not ask any interactive question
|
||||||
POETRY_NO_INTERACTION=1 \
|
POETRY_NO_INTERACTION=1 \
|
||||||
POETRY_VIRTUALENVS_IN_PROJECT=true
|
POETRY_VIRTUALENVS_IN_PROJECT=true \
|
||||||
|
DISPLAY=:0
|
||||||
|
|
||||||
RUN apt update && apt upgrade -y
|
|
||||||
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
|
||||||
RUN apt install -y --no-install-recommends --no-install-suggests ./google-chrome-stable_current_amd64.deb && rm ./google-chrome-stable_current_amd64.deb
|
|
||||||
|
|
||||||
RUN apt install pipx -y
|
|
||||||
RUN pipx ensurepath
|
|
||||||
RUN pipx install poetry
|
RUN pipx install poetry
|
||||||
ENV PATH="/root/.local/bin:$PATH"
|
ENV PATH="/root/.local/bin:$PATH"
|
||||||
COPY pyproject.toml poetry.lock ./
|
COPY pyproject.toml poetry.lock ./
|
||||||
RUN poetry install
|
RUN poetry install
|
||||||
|
|
||||||
ENV INSTALL_NOVNC=false
|
|
||||||
COPY novnc.sh .
|
|
||||||
RUN ./novnc.sh
|
|
||||||
ENV DISPLAY=:1.0
|
|
||||||
|
|
||||||
COPY fix_nodriver.py ./
|
COPY fix_nodriver.py ./
|
||||||
RUN . /app/.venv/bin/activate && python fix_nodriver.py
|
RUN . /app/.venv/bin/activate && python fix_nodriver.py
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN /usr/local/share/desktop-init.sh && poetry run pytest
|
CMD ["./entrypoint.sh", "&&", ".", "/app/.venv/bin/activate", "&&", "python", "main.py"]
|
||||||
CMD /usr/local/share/desktop-init.sh && . /app/.venv/bin/activate && python main.py
|
|
13
README.md
13
README.md
@ -4,7 +4,6 @@ An alternative to [FlareSolverr](https://github.com/FlareSolverr/FlareSolverr) a
|
|||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Currenly, due to [bug in nodriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver/issues/1954), if you want to run this project ouside of prebuild container, you have to run `python fix_nodriver.py` after creating venv to patch the library.
|
> Currenly, due to [bug in nodriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver/issues/1954), if you want to run this project ouside of prebuild container, you have to run `python fix_nodriver.py` after creating venv to patch the library.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Thanks to FastAPI implementation, now you can also see the API documentation at `/docs` or `/` (redirect to `/docs`) endpoints.
|
> Thanks to FastAPI implementation, now you can also see the API documentation at `/docs` or `/` (redirect to `/docs`) endpoints.
|
||||||
|
|
||||||
@ -15,14 +14,16 @@ Long story short, I created it in like 3 days, so if you get any bugs/hangs etc.
|
|||||||
I focus maily on Cloudflare, which is tested daily, any other anti-bot challenges should pass out of the box, but if any issues, please report these providers with an example website ❤️
|
I focus maily on Cloudflare, which is tested daily, any other anti-bot challenges should pass out of the box, but if any issues, please report these providers with an example website ❤️
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
1. Clone repo to the host that has the container has issues on.
|
1. Clone repo to the host that has the container has issues on.
|
||||||
2. Using vscode and `SSH extention`, connect to the host and open repo in it.
|
2. Using vscode and `SSH extention`, connect to the host and open repo in it.
|
||||||
3. Download `devcontainers` extention and reopen repo in container (with `CTRL + SHIFT + P` -> `Reopen in devcontainer`)
|
3. Download `devcontainers` extention and reopen repo in container (with `CTRL + SHIFT + P` -> `Reopen in devcontainer`)
|
||||||
4. Open forwarded port from `Ports` tab in your browser to see emulated display
|
4. Write down the port in `Ports` tab in vscode
|
||||||
5. Check if `chrome` works by running in VNCs terminal command `chrome --no-sandbox`
|
5. Open <https://novnc.com/noVNC/vnc.html> in your pc's browser and using settings on left under websocket, set host to `localhost` nad port to the port you wrote down
|
||||||
6. If chrome works, run API by pressing F5 in vscode
|
6. Check if `chromium` works by running in VNCs terminal command `chromium --no-sandbox`
|
||||||
7. In Prowlarr (or target client) change port byparrs port to `8181` instead of `8191` (Port opened by and pointing to devcontainer)
|
7. If chrome works, run API by pressing F5 in vscode
|
||||||
8. Check if everything works by testing byparr and observing VNC in browser
|
8. In Prowlarr (or target client) change port byparr's port to `8181` instead of `8191` (Port opened by and pointing to devcontainer)
|
||||||
|
9. Check if everything works by testing byparr and observing VNC in browser
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
15
entrypoint.sh
Executable file
15
entrypoint.sh
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
rm -f /tmp/.X0-lock
|
||||||
|
|
||||||
|
# Run Xvfb on dispaly 0.
|
||||||
|
Xvfb :0 -screen 0 1280x720x16 &
|
||||||
|
|
||||||
|
# Run fluxbox windows manager on display 0.
|
||||||
|
fluxbox -display :0 &
|
||||||
|
|
||||||
|
# Run x11vnc on display 0
|
||||||
|
x11vnc -display :0 -forever -ncache 10 &
|
||||||
|
|
||||||
|
# Add delay
|
||||||
|
sleep 5
|
@ -5,11 +5,13 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from platform import python_version
|
||||||
|
|
||||||
env_path = os.getenv("VIRTUAL_ENV")
|
env_path = os.getenv("VIRTUAL_ENV")
|
||||||
if env_path is None:
|
if env_path is None:
|
||||||
env_path = Path(os.__file__).parent.parent.parent.as_posix()
|
env_path = Path(os.__file__).parent.parent.parent.as_posix()
|
||||||
nodriver_path = Path(env_path + "/lib/python3.12/site-packages/nodriver/cdp/network.py")
|
python_version = python_version().split(".")[0:2]
|
||||||
|
nodriver_path = Path(env_path + f"/lib/python{'.'.join(python_version)}/site-packages/nodriver/cdp/network.py")
|
||||||
if not nodriver_path.exists():
|
if not nodriver_path.exists():
|
||||||
msg = f"{nodriver_path} not found"
|
msg = f"{nodriver_path} not found"
|
||||||
raise FileNotFoundError(msg)
|
raise FileNotFoundError(msg)
|
||||||
|
10
main.py
10
main.py
@ -6,7 +6,7 @@ import time
|
|||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
import uvicorn.config
|
import uvicorn.config
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, HTTPException
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
|
|
||||||
from src.models.requests import LinkRequest, LinkResponse
|
from src.models.requests import LinkRequest, LinkResponse
|
||||||
@ -36,8 +36,12 @@ async def read_item(request: LinkRequest):
|
|||||||
timeout = request.maxTimeout
|
timeout = request.maxTimeout
|
||||||
if timeout == 0:
|
if timeout == 0:
|
||||||
timeout = None
|
timeout = None
|
||||||
|
try:
|
||||||
challenged = await asyncio.wait_for(bypass_cloudflare(page), timeout=timeout)
|
challenged = await asyncio.wait_for(bypass_cloudflare(page), timeout=timeout)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(await page.get_content())
|
||||||
|
logger.fatal("Element is a string, please report this to Byparr dev")
|
||||||
|
raise HTTPException(detail="Couldn't bypass", status_code=408) from e
|
||||||
|
|
||||||
logger.info(f"Got webpage: {request.url}")
|
logger.info(f"Got webpage: {request.url}")
|
||||||
|
|
||||||
|
429
novnc.sh
429
novnc.sh
@ -1,429 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#-------------------------------------------------------------------------------------------------------------
|
|
||||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
|
|
||||||
#-------------------------------------------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/desktop-lite.md
|
|
||||||
# Maintainer: The VS Code and Codespaces Teams
|
|
||||||
|
|
||||||
NOVNC_VERSION="${NOVNCVERSION:-"1.2.0"}" # TODO: Add in a 'latest' auto-detect and swap name to 'version'
|
|
||||||
VNC_PASSWORD=${PASSWORD:-"vscode"}
|
|
||||||
if [ "$VNC_PASSWORD" = "noPassword" ]; then
|
|
||||||
unset VNC_PASSWORD
|
|
||||||
fi
|
|
||||||
NOVNC_PORT="${WEBPORT:-6080}"
|
|
||||||
VNC_PORT="${VNCPORT:-5901}"
|
|
||||||
|
|
||||||
INSTALL_NOVNC="${INSTALL_NOVNC:-"true"}"
|
|
||||||
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
|
|
||||||
|
|
||||||
WEBSOCKETIFY_VERSION=0.10.0
|
|
||||||
|
|
||||||
package_list="
|
|
||||||
tigervnc-standalone-server \
|
|
||||||
tigervnc-common \
|
|
||||||
fluxbox \
|
|
||||||
dbus-x11 \
|
|
||||||
x11-utils \
|
|
||||||
x11-xserver-utils \
|
|
||||||
xdg-utils \
|
|
||||||
fbautostart \
|
|
||||||
at-spi2-core \
|
|
||||||
eterm \
|
|
||||||
gnome-keyring \
|
|
||||||
libx11-dev \
|
|
||||||
libxkbfile-dev \
|
|
||||||
libsecret-1-dev \
|
|
||||||
libgbm-dev \
|
|
||||||
libnotify4 \
|
|
||||||
libnss3 \
|
|
||||||
libxss1 \
|
|
||||||
fonts-wqy-microhei \
|
|
||||||
fonts-droid-fallback \
|
|
||||||
htop \
|
|
||||||
ncdu \
|
|
||||||
curl \
|
|
||||||
ca-certificates\
|
|
||||||
unzip \
|
|
||||||
nano \
|
|
||||||
locales"
|
|
||||||
|
|
||||||
# Packages to attempt to install if essential tools are missing (ie: vncpasswd).
|
|
||||||
# This is useful, at least, for Ubuntu 22.04 (jammy)
|
|
||||||
package_list_additional="
|
|
||||||
tigervnc-tools"
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
|
||||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Determine the appropriate non-root user
|
|
||||||
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
|
|
||||||
USERNAME=""
|
|
||||||
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
|
|
||||||
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
|
|
||||||
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
|
|
||||||
USERNAME=${CURRENT_USER}
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [ "${USERNAME}" = "" ]; then
|
|
||||||
USERNAME=root
|
|
||||||
fi
|
|
||||||
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
|
|
||||||
USERNAME=root
|
|
||||||
fi
|
|
||||||
# Add default Fluxbox config files if none are already present
|
|
||||||
fluxbox_apps="$(cat \
|
|
||||||
<< 'EOF'
|
|
||||||
[transient] (role=GtkFileChooserDialog)
|
|
||||||
[Dimensions] {70% 70%}
|
|
||||||
[Position] (CENTER) {0 0}
|
|
||||||
[end]
|
|
||||||
EOF
|
|
||||||
)"
|
|
||||||
|
|
||||||
fluxbox_init="$(cat \
|
|
||||||
<< 'EOF'
|
|
||||||
session.configVersion: 13
|
|
||||||
session.menuFile: ~/.fluxbox/menu
|
|
||||||
session.keyFile: ~/.fluxbox/keys
|
|
||||||
session.styleFile: /usr/share/fluxbox/styles/qnx-photon
|
|
||||||
session.screen0.workspaces: 1
|
|
||||||
session.screen0.workspacewarping: false
|
|
||||||
session.screen0.toolbar.widthPercent: 100
|
|
||||||
session.screen0.strftimeFormat: %a %l:%M %p
|
|
||||||
session.screen0.toolbar.tools: RootMenu, clock, iconbar, systemtray
|
|
||||||
session.screen0.workspaceNames: One,
|
|
||||||
EOF
|
|
||||||
)"
|
|
||||||
|
|
||||||
fluxbox_menu="$(cat \
|
|
||||||
<< 'EOF'
|
|
||||||
[begin] ( Application Menu )
|
|
||||||
[exec] (File Manager) { nautilus ~ } <>
|
|
||||||
[exec] (Text Editor) { mousepad } <>
|
|
||||||
[exec] (Terminal) { tilix -w ~ -e $(readlink -f /proc/$$/exe) -il } <>
|
|
||||||
[exec] (Web Browser) { x-www-browser --disable-dev-shm-usage } <>
|
|
||||||
[submenu] (System) {}
|
|
||||||
[exec] (Set Resolution) { tilix -t "Set Resolution" -e bash /usr/local/bin/set-resolution } <>
|
|
||||||
[exec] (Edit Application Menu) { mousepad ~/.fluxbox/menu } <>
|
|
||||||
[exec] (Passwords and Keys) { seahorse } <>
|
|
||||||
[exec] (Top Processes) { tilix -t "Top" -e htop } <>
|
|
||||||
[exec] (Disk Utilization) { tilix -t "Disk Utilization" -e ncdu / } <>
|
|
||||||
[exec] (Editres) {editres} <>
|
|
||||||
[exec] (Xfontsel) {xfontsel} <>
|
|
||||||
[exec] (Xkill) {xkill} <>
|
|
||||||
[exec] (Xrefresh) {xrefresh} <>
|
|
||||||
[end]
|
|
||||||
[config] (Configuration)
|
|
||||||
[workspaces] (Workspaces)
|
|
||||||
[end]
|
|
||||||
EOF
|
|
||||||
)"
|
|
||||||
|
|
||||||
# Copy config files if the don't already exist
|
|
||||||
copy_fluxbox_config() {
|
|
||||||
local target_dir="$1"
|
|
||||||
mkdir -p "${target_dir}/.fluxbox"
|
|
||||||
touch "${target_dir}/.Xmodmap"
|
|
||||||
if [ ! -e "${target_dir}/.fluxbox/apps" ]; then
|
|
||||||
echo "${fluxbox_apps}" > "${target_dir}/.fluxbox/apps"
|
|
||||||
fi
|
|
||||||
if [ ! -e "${target_dir}/.fluxbox/init" ]; then
|
|
||||||
echo "${fluxbox_init}" > "${target_dir}/.fluxbox/init"
|
|
||||||
fi
|
|
||||||
if [ ! -e "${target_dir}/.fluxbox/menu" ]; then
|
|
||||||
echo "${fluxbox_menu}" > "${target_dir}/.fluxbox/menu"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
apt_get_update()
|
|
||||||
{
|
|
||||||
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
|
|
||||||
echo "Running apt-get update..."
|
|
||||||
apt-get update -y
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Checks if packages are installed and installs them if not
|
|
||||||
check_packages() {
|
|
||||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
|
||||||
apt_get_update
|
|
||||||
apt-get -y install --no-install-recommends "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
##########################
|
|
||||||
# Install starts here #
|
|
||||||
##########################
|
|
||||||
|
|
||||||
# Ensure apt is in non-interactive to avoid prompts
|
|
||||||
export DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
apt_get_update
|
|
||||||
|
|
||||||
# On older Ubuntu, Tilix is in a PPA. on Debian stretch its in backports.
|
|
||||||
# if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
|
|
||||||
# . /etc/os-release
|
|
||||||
# if [ "${ID}" = "ubuntu" ]; then
|
|
||||||
# check_packages apt-transport-https software-properties-common
|
|
||||||
# add-apt-repository -y ppa:webupd8team/terminix
|
|
||||||
# elif [ "${VERSION_CODENAME}" = "stretch" ]; then
|
|
||||||
# echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list
|
|
||||||
# fi
|
|
||||||
# apt-get update
|
|
||||||
# if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
|
|
||||||
# echo "(!) WARNING: Tilix not available on ${ID} ${VERSION_CODENAME} architecture $(uname -m). Skipping."
|
|
||||||
# else
|
|
||||||
# package_list="${package_list} tilix"
|
|
||||||
# fi
|
|
||||||
# else
|
|
||||||
# package_list="${package_list} tilix"
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# Install X11, fluxbox and VS Code dependencies
|
|
||||||
check_packages ${package_list}
|
|
||||||
|
|
||||||
# if Ubuntu-24.04, noble(numbat) found, then will install libasound2-dev instead of libasound2.
|
|
||||||
# this change is temporary, https://packages.ubuntu.com/noble/libasound2 will switch to libasound2 once it is available for Ubuntu-24.04, noble(numbat)
|
|
||||||
# . /etc/os-release
|
|
||||||
# if [ "${ID}" = "ubuntu" ] && [ "${VERSION_CODENAME}" = "noble" ]; then
|
|
||||||
# echo "Ubuntu 24.04, Noble(Numbat) detected. Installing libasound2-dev package..."
|
|
||||||
# check_packages "libasound2-dev"
|
|
||||||
# else
|
|
||||||
# check_packages "libasound2"
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# On newer versions of Ubuntu (22.04),
|
|
||||||
# we need an additional package that isn't provided in earlier versions
|
|
||||||
if ! type vncpasswd > /dev/null 2>&1; then
|
|
||||||
check_packages ${package_list_additional}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install Emoji font if available in distro - Available in Debian 10+, Ubuntu 18.04+
|
|
||||||
# if dpkg-query -W fonts-noto-color-emoji > /dev/null 2>&1 && ! dpkg -s fonts-noto-color-emoji > /dev/null 2>&1; then
|
|
||||||
# apt-get -y install --no-install-recommends fonts-noto-color-emoji
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# Check at least one locale exists
|
|
||||||
if ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
|
|
||||||
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
|
|
||||||
locale-gen
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install the Cascadia Code fonts - https://github.com/microsoft/cascadia-code
|
|
||||||
# if [ ! -d "/usr/share/fonts/truetype/cascadia" ]; then
|
|
||||||
# curl -sSL https://github.com/microsoft/cascadia-code/releases/download/v2008.25/CascadiaCode-2008.25.zip -o /tmp/cascadia-fonts.zip
|
|
||||||
# unzip /tmp/cascadia-fonts.zip -d /tmp/cascadia-fonts
|
|
||||||
# mkdir -p /usr/share/fonts/truetype/cascadia
|
|
||||||
# mv /tmp/cascadia-fonts/ttf/* /usr/share/fonts/truetype/cascadia/
|
|
||||||
# rm -rf /tmp/cascadia-fonts.zip /tmp/cascadia-fonts
|
|
||||||
# fi
|
|
||||||
|
|
||||||
# Install noVNC
|
|
||||||
if [ "${INSTALL_NOVNC}" = "true" ] && [ ! -d "/usr/local/novnc" ]; then
|
|
||||||
mkdir -p /usr/local/novnc
|
|
||||||
curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip
|
|
||||||
unzip /tmp/novnc-install.zip -d /usr/local/novnc
|
|
||||||
cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html
|
|
||||||
curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip
|
|
||||||
unzip /tmp/websockify-install.zip -d /usr/local/novnc
|
|
||||||
ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify
|
|
||||||
rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip
|
|
||||||
|
|
||||||
# Install noVNC dependencies and use them.
|
|
||||||
check_packages python3-minimal python3-numpy
|
|
||||||
sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set up folders for scripts and init files
|
|
||||||
mkdir -p /var/run/dbus /usr/local/etc/vscode-dev-containers/
|
|
||||||
|
|
||||||
# Script to change resolution of desktop
|
|
||||||
cat << EOF > /usr/local/bin/set-resolution
|
|
||||||
#!/bin/bash
|
|
||||||
RESOLUTION=\${1:-\${VNC_RESOLUTION:-1920x1080}}
|
|
||||||
DPI=\${2:-\${VNC_DPI:-96}}
|
|
||||||
IGNORE_ERROR=\${3:-"false"}
|
|
||||||
if [ -z "\$1" ]; then
|
|
||||||
echo -e "**Current Settings **\n"
|
|
||||||
xrandr
|
|
||||||
echo -n -e "\nEnter new resolution (WIDTHxHEIGHT, blank for \${RESOLUTION}, Ctrl+C to abort).\n> "
|
|
||||||
read NEW_RES
|
|
||||||
if [ "\${NEW_RES}" != "" ]; then
|
|
||||||
RESOLUTION=\${NEW_RES}
|
|
||||||
fi
|
|
||||||
if ! echo "\${RESOLUTION}" | grep -E '[0-9]+x[0-9]+' > /dev/null; then
|
|
||||||
echo -e "\nInvalid resolution format!\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ -z "\$2" ]; then
|
|
||||||
echo -n -e "\nEnter new DPI (blank for \${DPI}, Ctrl+C to abort).\n> "
|
|
||||||
read NEW_DPI
|
|
||||||
if [ "\${NEW_DPI}" != "" ]; then
|
|
||||||
DPI=\${NEW_DPI}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
xrandr --fb \${RESOLUTION} --dpi \${DPI} > /dev/null 2>&1
|
|
||||||
|
|
||||||
if [ \$? -ne 0 ] && [ "\${IGNORE_ERROR}" != "true" ]; then
|
|
||||||
echo -e "\nFAILED TO SET RESOLUTION!\n"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "\nSuccess!\n"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Container ENTRYPOINT script
|
|
||||||
cat << EOF > /usr/local/share/desktop-init.sh
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
user_name="${USERNAME}"
|
|
||||||
group_name="$(id -gn ${USERNAME})"
|
|
||||||
LOG=/tmp/container-init.log
|
|
||||||
|
|
||||||
export DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-"autolaunch:"}"
|
|
||||||
export DISPLAY="${DISPLAY:-:1}"
|
|
||||||
export VNC_RESOLUTION="${VNC_RESOLUTION:-1440x768x16}"
|
|
||||||
export LANG="${LANG:-"en_US.UTF-8"}"
|
|
||||||
export LANGUAGE="${LANGUAGE:-"en_US.UTF-8"}"
|
|
||||||
|
|
||||||
# Execute the command it not already running
|
|
||||||
startInBackgroundIfNotRunning()
|
|
||||||
{
|
|
||||||
log "Starting \$1."
|
|
||||||
echo -e "\n** \$(date) **" | sudoIf tee -a /tmp/\$1.log > /dev/null
|
|
||||||
if ! pgrep -x \$1 > /dev/null; then
|
|
||||||
keepRunningInBackground "\$@"
|
|
||||||
while ! pgrep -x \$1 > /dev/null; do
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
log "\$1 started."
|
|
||||||
else
|
|
||||||
echo "\$1 is already running." | sudoIf tee -a /tmp/\$1.log > /dev/null
|
|
||||||
log "\$1 is already running."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Keep command running in background
|
|
||||||
keepRunningInBackground()
|
|
||||||
{
|
|
||||||
(\$2 bash -c "while :; do echo [\\\$(date)] Process started.; \$3; echo [\\\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/\$1.log > /dev/null & echo "\$!" | sudoIf tee /tmp/\$1.pid > /dev/null)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Use sudo to run as root when required
|
|
||||||
sudoIf()
|
|
||||||
{
|
|
||||||
if [ "\$(id -u)" -ne 0 ]; then
|
|
||||||
sudo "\$@"
|
|
||||||
else
|
|
||||||
"\$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Use sudo to run as non-root user if not already running
|
|
||||||
sudoUserIf()
|
|
||||||
{
|
|
||||||
if [ "\$(id -u)" -eq 0 ] && [ "\${user_name}" != "root" ]; then
|
|
||||||
sudo -u \${user_name} "\$@"
|
|
||||||
else
|
|
||||||
"\$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Log messages
|
|
||||||
log()
|
|
||||||
{
|
|
||||||
echo -e "[\$(date)] \$@" | sudoIf tee -a \$LOG > /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
log "** SCRIPT START **"
|
|
||||||
|
|
||||||
# Start dbus.
|
|
||||||
log 'Running "/etc/init.d/dbus start".'
|
|
||||||
if [ -f "/var/run/dbus/pid" ] && ! pgrep -x dbus-daemon > /dev/null; then
|
|
||||||
sudoIf rm -f /var/run/dbus/pid
|
|
||||||
fi
|
|
||||||
sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null
|
|
||||||
while ! pgrep -x dbus-daemon > /dev/null; do
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Startup tigervnc server and fluxbox
|
|
||||||
sudoIf rm -rf /tmp/.X11-unix /tmp/.X*-lock
|
|
||||||
mkdir -p /tmp/.X11-unix
|
|
||||||
sudoIf chmod 1777 /tmp/.X11-unix
|
|
||||||
sudoIf chown root:\${group_name} /tmp/.X11-unix
|
|
||||||
if [ "\$(echo "\${VNC_RESOLUTION}" | tr -cd 'x' | wc -c)" = "1" ]; then VNC_RESOLUTION=\${VNC_RESOLUTION}x16; fi
|
|
||||||
screen_geometry="\${VNC_RESOLUTION%*x*}"
|
|
||||||
screen_depth="\${VNC_RESOLUTION##*x}"
|
|
||||||
|
|
||||||
# Check if VNC_PASSWORD is set and use the appropriate command
|
|
||||||
common_options="tigervncserver \${DISPLAY} -geometry \${screen_geometry} -depth \${screen_depth} -rfbport ${VNC_PORT} -dpi \${VNC_DPI:-96} -localhost -desktop fluxbox -fg"
|
|
||||||
|
|
||||||
if [ -n "\${VNC_PASSWORD+x}" ]; then
|
|
||||||
startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "\${common_options} -passwd /usr/local/etc/vscode-dev-containers/vnc-passwd"
|
|
||||||
else
|
|
||||||
startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "\${common_options} -SecurityTypes None"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Spin up noVNC if installed and not running.
|
|
||||||
if [ -d "/usr/local/novnc" ] && [ "\$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then
|
|
||||||
keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT} --vnc localhost:${VNC_PORT}"
|
|
||||||
log "noVNC started."
|
|
||||||
else
|
|
||||||
log "noVNC is already running or not installed."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run whatever was passed in
|
|
||||||
log "Executing \"\$@\"."
|
|
||||||
exec "\$@"
|
|
||||||
log "** SCRIPT EXIT **"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ -n "${VNC_PASSWORD+x}" ]; then
|
|
||||||
echo "${VNC_PASSWORD}" | vncpasswd -f > /usr/local/etc/vscode-dev-containers/vnc-passwd
|
|
||||||
fi
|
|
||||||
chmod +x /usr/local/share/desktop-init.sh /usr/local/bin/set-resolution
|
|
||||||
|
|
||||||
# Set up fluxbox config
|
|
||||||
copy_fluxbox_config "/root"
|
|
||||||
if [ "${USERNAME}" != "root" ]; then
|
|
||||||
copy_fluxbox_config "/home/${USERNAME}"
|
|
||||||
chown -R ${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Determine the message based on whether VNC_PASSWORD is set
|
|
||||||
if [ -n "${VNC_PASSWORD+x}" ]; then
|
|
||||||
PASSWORD_MESSAGE="In both cases, use the password \"${VNC_PASSWORD}\" when connecting"
|
|
||||||
else
|
|
||||||
PASSWORD_MESSAGE="In both cases, no password is required."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Display the message
|
|
||||||
cat << EOF
|
|
||||||
|
|
||||||
|
|
||||||
You now have a working desktop! Connect to in one of the following ways:
|
|
||||||
|
|
||||||
- Forward port ${NOVNC_PORT} and use a web browser to start the noVNC client (recommended)
|
|
||||||
- Forward port ${VNC_PORT} using VS Code client and connect using a VNC Viewer
|
|
||||||
|
|
||||||
${PASSWORD_MESSAGE}
|
|
||||||
|
|
||||||
(*) Done!
|
|
||||||
|
|
||||||
EOF
|
|
912
poetry.lock
generated
912
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -48,3 +48,6 @@ ignore = [
|
|||||||
]
|
]
|
||||||
select = ["ALL"]
|
select = ["ALL"]
|
||||||
extend-safe-fixes = ["D415"]
|
extend-safe-fixes = ["D415"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
asyncio_default_fixture_loop_scope = "function"
|
||||||
|
3
pytest.sh
Executable file
3
pytest.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
if [ $(arch) = "x86_64" ]; then
|
||||||
|
./entrypoint.sh && . ./.venv/bin/activate && poetry run pytest
|
||||||
|
fi
|
@ -23,8 +23,9 @@ async def new_browser():
|
|||||||
Any exceptions that may occur during the creation of the browser instance.
|
Any exceptions that may occur during the creation of the browser instance.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
config: webdriver.Config = webdriver.Config()
|
config: webdriver.Config = webdriver.Config(
|
||||||
config.sandbox = False
|
browser_executable_path="/usr/bin/chromium", sandbox=True
|
||||||
|
)
|
||||||
config.add_argument(f"--load-extension={','.join(downloaded_extentions)}")
|
config.add_argument(f"--load-extension={','.join(downloaded_extentions)}")
|
||||||
|
|
||||||
return await webdriver.start(config=config)
|
return await webdriver.start(config=config)
|
||||||
@ -65,24 +66,26 @@ async def bypass_cloudflare(page: webdriver.Tab):
|
|||||||
if not challenged:
|
if not challenged:
|
||||||
logger.info("Found challenge")
|
logger.info("Found challenge")
|
||||||
challenged = True
|
challenged = True
|
||||||
|
|
||||||
|
loaded = False
|
||||||
try:
|
try:
|
||||||
elem = await page.find(
|
elem = await page.find("lds-ring", timeout=3)
|
||||||
"Verify you are human by completing the action below.",
|
parent = elem.parent
|
||||||
timeout=3,
|
if not isinstance(parent, Element) or parent.attributes is None:
|
||||||
)
|
continue
|
||||||
# If challenge solves by itself
|
for attr in parent.attributes:
|
||||||
|
if attr == "display: none; visibility: hidden;":
|
||||||
|
loaded = True
|
||||||
|
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
if page.target.title not in CHALLENGE_TITLES:
|
logger.debug("Challenge loaded")
|
||||||
return challenged
|
else:
|
||||||
|
if not loaded:
|
||||||
if elem is None:
|
logger.debug("Challenge still loading")
|
||||||
logger.debug("Couldn't find the title, trying other method...")
|
continue
|
||||||
continue
|
|
||||||
|
|
||||||
if not isinstance(elem, Element):
|
|
||||||
logger.fatal("Element is a string, please report this to Byparr dev")
|
|
||||||
raise InvalidElementError
|
|
||||||
|
|
||||||
|
await page
|
||||||
|
logger.debug("Couldn't find the title, trying other method...")
|
||||||
elem = await page.find("input")
|
elem = await page.find("input")
|
||||||
elem = elem.parent
|
elem = elem.parent
|
||||||
# Get the element containing the shadow root
|
# Get the element containing the shadow root
|
||||||
@ -92,10 +95,12 @@ async def bypass_cloudflare(page: webdriver.Tab):
|
|||||||
if isinstance(inner_elem, Element):
|
if isinstance(inner_elem, Element):
|
||||||
logger.debug("Clicking element")
|
logger.debug("Clicking element")
|
||||||
await inner_elem.mouse_click()
|
await inner_elem.mouse_click()
|
||||||
|
await asyncio.sleep(3)
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"Element is a string, please report this to Byparr dev"
|
"Element is a string, please report this to Byparr dev"
|
||||||
) # I really hope this never happens
|
) # I really hope this never happens
|
||||||
|
logger.warning(inner_elem)
|
||||||
else:
|
else:
|
||||||
logger.warning("Coulnd't find checkbox, trying again...")
|
logger.warning("Coulnd't find checkbox, trying again...")
|
||||||
|
|
||||||
|
@ -77,7 +77,12 @@ def download_extentions():
|
|||||||
logger.error(f"Error downloading {extention_name}, using local copy")
|
logger.error(f"Error downloading {extention_name}, using local copy")
|
||||||
downloaded_extentions.append(path.as_posix())
|
downloaded_extentions.append(path.as_posix())
|
||||||
continue
|
continue
|
||||||
zip_file = requests.get(extention.browser_download_url, timeout=10)
|
try:
|
||||||
|
zip_file = requests.get(extention.browser_download_url, timeout=10)
|
||||||
|
except UnboundLocalError as e:
|
||||||
|
logger.error(f"Error downloading {extention_name}, skipping")
|
||||||
|
logger.error(e)
|
||||||
|
continue
|
||||||
Path(EXTENTIONS_PATH).mkdir(exist_ok=True)
|
Path(EXTENTIONS_PATH).mkdir(exist_ok=True)
|
||||||
with ZipFile(io.BytesIO(zip_file.content)) as zip_obj:
|
with ZipFile(io.BytesIO(zip_file.content)) as zip_obj:
|
||||||
zip_obj.extractall(f"{EXTENTIONS_PATH}/{extention_name}")
|
zip_obj.extractall(f"{EXTENTIONS_PATH}/{extention_name}")
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from starlette.testclient import TestClient
|
from starlette.testclient import TestClient
|
||||||
@ -11,17 +12,20 @@ client = TestClient(app)
|
|||||||
test_websites = [
|
test_websites = [
|
||||||
"https://ext.to/",
|
"https://ext.to/",
|
||||||
"https://btmet.com/",
|
"https://btmet.com/",
|
||||||
# "https://extratorrent.st/", # github is blocking these
|
"https://extratorrent.st/", # github is blocking these
|
||||||
# "https://idope.se/", # github is blocking these
|
"https://idope.se/", # github is blocking these
|
||||||
|
"https://www.ygg.re/",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("website", test_websites)
|
@pytest.mark.parametrize("website", test_websites)
|
||||||
def test_bypass(website: str):
|
def test_bypass(website: str):
|
||||||
|
sleep(3)
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/v1",
|
"/v1",
|
||||||
json=LinkRequest(
|
json=LinkRequest(url=website, maxTimeout=30, cmd="request.get").model_dump(),
|
||||||
url=website, maxTimeout=60 * len(test_websites), cmd="request.get"
|
|
||||||
).model_dump(),
|
|
||||||
)
|
)
|
||||||
|
if response.status_code == HTTPStatus.TOO_MANY_REQUESTS:
|
||||||
|
# if rate limited
|
||||||
|
assert True
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == HTTPStatus.OK
|
||||||
|
Loading…
x
Reference in New Issue
Block a user