From 1efbf5e1cd96791e7084b780f8bf921aad1b24be Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 23 Jan 2024 19:01:53 +0800 Subject: [PATCH 01/26] :art: 3.6.6: Optimized code. --- utils/backend.py | 77 ++++++++++++++++-------------------------------- utils/cleaner.py | 2 +- 2 files changed, 26 insertions(+), 53 deletions(-) diff --git a/utils/backend.py b/utils/backend.py index e4d0209d..15e84dd0 100644 --- a/utils/backend.py +++ b/utils/backend.py @@ -291,52 +291,31 @@ async def speed_start( workers: int = 0, ) -> tuple: st = Speedtest() - try: - from ftclib import speed_test - except ImportError: - speed_test = None - if speed_test is not None: - pass - # workers = workers if workers else st.thread - # print("rust黑科技,启动!") - # - # res = await speed_test(proxy_host, proxy_port, st.speedurl, workers) - # spmean = sum(res) / len(res) if res else 0 - # spmax = max(res) if res else 0 - # if spmean > spmax: - # spmean, spmax = spmax, spmean - # return ( - # spmean, - # spmax, - # res, - # 10, - # ) - else: - download_semaphore = asyncio.Semaphore(workers if workers else Speedtest().thread) - async with download_semaphore: - urls = st.speedurl - # logger.debug(f"Url: {url}") - thread = workers if workers else st.thread - logger.info(f"Running st_async, workers: {thread}.") - tasks = [ - asyncio.create_task(SpeedCore.fetch(st, urls, proxy_host, proxy_port, buffer)) - for _ in range(thread) - ] - await asyncio.wait(tasks) - st.show_progress_full() - spmean = st.total_red / st.time_used if st.time_used else 0 - spmax = st.max_speed - if spmean > spmax: - spmean, spmax = spmax, spmean - if st.time_used: - return ( - spmean, - spmax, - st.speed_list[1:], - st.total_red, - ) - - return 0, 0, [], 0 + download_semaphore = asyncio.Semaphore(workers if workers else Speedtest().thread) + async with download_semaphore: + urls = st.speedurl + # logger.debug(f"Url: {url}") + thread = workers if workers else st.thread + logger.info(f"Running st_async, workers: {thread}.") + tasks = [ + asyncio.create_task(SpeedCore.fetch(st, urls, proxy_host, proxy_port, buffer)) + for _ in range(thread) + ] + await asyncio.wait(tasks) + st.show_progress_full() + spmean = st.total_red / st.time_used if st.time_used else 0 + spmax = st.max_speed + if spmean > spmax: + spmean, spmax = spmax, spmean + if st.time_used: + return ( + spmean, + spmax, + st.speed_list[1:], + st.total_red, + ) + + return 0, 0, [], 0 # 以下为 另一部分 async def batch_speed(self, nodelist: list, port: int = 11220, proxy_obj: Union[proxy.FullTCore] = None, @@ -796,12 +775,6 @@ async def topo(self): return info, hosts, cl async def batch_topo(self, nodename: list, pool: dict, proxy_obj: Union[proxy.FullTCore] = None): - # 获取位置 - try: - from utils.geo import get_region - except ImportError: - pass - resdata = [] ipstackes = [] progress = 0 diff --git a/utils/cleaner.py b/utils/cleaner.py index a143c914..645958f3 100644 --- a/utils/cleaner.py +++ b/utils/cleaner.py @@ -1,7 +1,7 @@ import asyncio import importlib import os -import pathlib + from copy import deepcopy from typing import Union, List import socket From 274f8683d450e5a33e4ab626c4ace36f32f3fbab Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:56:15 +0800 Subject: [PATCH 02/26] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=84=9A=E6=9C=AC?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=86=E6=94=AF=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/update.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docker/update.sh b/docker/update.sh index ad1bb4ab..9cd9c108 100644 --- a/docker/update.sh +++ b/docker/update.sh @@ -1,13 +1,31 @@ #!/bin/bash . /etc/profile +git_branch=$(git --git-dir='/app/.git' --work-tree='/app' rev-parse --abbrev-ref HEAD) git_version=$(git --git-dir='/app/.git' --work-tree='/app' rev-parse HEAD) -last_version=$(curl -Ls "https://api.github.com/repos/AirportR/FullTclash/commits/dev" | jq .sha | sed -E 's/.*"([^"]+)".*/\1/') + +if [[ master == "$git_branch" ]]; then + echo -e "当前分支为 $git_branch" + echo -e "当前commits位于 $git_version" + last_version=$(curl -Ls "https://api.github.com/repos/AirportR/FullTclash/commits/master" | jq .sha | sed -E 's/.*"([^"]+)".*/\1/') +elif [[ dev == "$git_branch" ]]; then + echo -e "当前分支为 $git_branch" + echo -e "当前commits位于 $git_version" + last_version=$(curl -Ls "https://api.github.com/repos/AirportR/FullTclash/commits/dev" | jq .sha | sed -E 's/.*"([^"]+)".*/\1/') +elif [[ backend == "$git_branch" ]]; then + echo -e "当前分支为 $git_branch" + echo -e "当前commits位于 $git_version" + last_version=$(curl -Ls "https://api.github.com/repos/AirportR/FullTclash/commits/backend" | jq .sha | sed -E 's/.*"([^"]+)".*/\1/') +else + echo -e "暂不支持此分支" + exit 1 +fi update() { git --git-dir='/app/.git' --work-tree='/app' fetch --all - git --git-dir='/app/.git' --work-tree='/app' reset --hard origin/dev + git --git-dir='/app/.git' --work-tree='/app' reset --hard origin/$git_branch git --git-dir='/app/.git' --work-tree='/app' pull + git_version=$(git --git-dir='/app/.git' --work-tree='/app' rev-parse HEAD) } if [[ $last_version == "$git_version" ]]; then @@ -15,5 +33,6 @@ if [[ $last_version == "$git_version" ]]; then else echo -e "检查到新版本,正在更新" update + echo -e "更新完成,当前commits位于 $git_version" /opt/venv/bin/supervisorctl restart fulltclash fi \ No newline at end of file From ea1d81d1dc42e0b56a8905916ac56913c092f4bc Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:34:54 +0800 Subject: [PATCH 03/26] =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E5=88=86=E6=94=AFDocker=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-publish.yml | 37 +++++++++++++++++++++++----- docker/Dockerfile | 4 ++- docker/Dockerfile.alpine | 4 ++- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 0faaae40..17edc000 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,7 +1,11 @@ name: ci on: + workflow_dispatch: push: + # run only against tags + tags: + - 'v*' paths-ignore: - "resources/**" - "README.md" @@ -20,6 +24,11 @@ jobs: uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: @@ -29,10 +38,12 @@ jobs: - name: Set Environment Variables run: | IMG=fulltclash - OWNER="$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')" + # OWNER="$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')" echo "IMG=${IMG}" >> $GITHUB_ENV - echo "IMAGE=ghcr.io/${OWNER}/${IMG}" >> $GITHUB_ENV - - name: Build and push + # echo "IMAGE=ghcr.io/${OWNER}/${IMG}" >> $GITHUB_ENV + echo "IMAGE=${{ secrets.DOCKERHUB_USERNAME }}/${IMG}" >> $GITHUB_ENV + - name: Build Latest Image + if: ${{ github.ref_name == 'dev' }} uses: docker/build-push-action@v5 with: platforms: linux/amd64,linux/arm64 @@ -41,8 +52,20 @@ jobs: push: true tags: | ${{ env.IMAGE }}:latest - ${{ env.IMAGE }}:dev - - name: Build alpine Docker + build-args: | + GIT_Branch=${{ github.ref_name }} + - name: Build Debian Image + uses: docker/build-push-action@v5 + with: + platforms: linux/amd64,linux/arm64 + context: . + file: ./docker/Dockerfile + push: true + tags: | + ${{ env.IMAGE }}:debian-${{ github.ref_name }} + build-args: | + GIT_Branch=${{ github.ref_name }} + - name: Build Alpine Image uses: docker/build-push-action@v5 with: platforms: linux/amd64,linux/arm64 @@ -50,4 +73,6 @@ jobs: file: ./docker/Dockerfile.alpine push: true tags: | - ${{ env.IMAGE }}:alpine \ No newline at end of file + ${{ env.IMAGE }}:alpine-${{ github.ref_name }} + build-args: | + GIT_Branch=${{ github.ref_name }} \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index ca33048e..3c6b2c5e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,6 +13,8 @@ RUN pip3 install --no-cache-dir -r requirements.txt && \ FROM python:3.11.7-slim-bookworm +ARG GIT_Branch +ENV GIT_Branch=$GIT_Branch ENV TZ=Asia/Shanghai ENV admin=12345678 ENV api_id=123456 @@ -30,7 +32,7 @@ WORKDIR /app RUN apt-get update && \ apt-get install --no-install-recommends -y \ git tzdata curl wget jq bash nano cron && \ - git clone -b dev --single-branch --depth=1 https://github.com/AirportR/FullTclash.git /app && \ + git clone -b $GIT_Branch --single-branch --depth=1 https://github.com/AirportR/FullTclash.git /app && \ git clone --single-branch --depth=1 https://github.com/twitter/twemoji.git /app/resources/emoji/twemoji && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone && \ diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 40bcf2ba..99730c48 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -12,6 +12,8 @@ RUN pip3 install --no-cache-dir -r requirements.txt && \ FROM python:3.11.7-alpine3.19 +ARG GIT_Branch +ENV GIT_Branch=$GIT_Branch ENV TZ=Asia/Shanghai ENV admin=12345678 ENV api_id=123456 @@ -28,7 +30,7 @@ WORKDIR /app RUN apk add --no-cache \ git tzdata curl jq wget bash nano && \ - git clone -b dev --single-branch --depth=1 https://github.com/AirportR/FullTclash.git /app && \ + git clone -b $GIT_Branch --single-branch --depth=1 https://github.com/AirportR/FullTclash.git /app && \ git clone --single-branch --depth=1 https://github.com/twitter/twemoji.git /app/resources/emoji/twemoji && \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone && \ From bd9f7cbe69d933326aecda590af27674948724b5 Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 19:24:46 +0800 Subject: [PATCH 04/26] fix docker-publish.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改docker镜像上传到dockerhub --- .github/workflows/docker-publish.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 17edc000..738a909a 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,11 +1,7 @@ name: ci on: - workflow_dispatch: push: - # run only against tags - tags: - - 'v*' paths-ignore: - "resources/**" - "README.md" @@ -13,6 +9,8 @@ on: - ".github/ISSUE_TEMPLATE/**" branches: - "master" + - "dev" + - "backend" jobs: docker: @@ -29,18 +27,10 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - name: Set Environment Variables run: | IMG=fulltclash - # OWNER="$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')" echo "IMG=${IMG}" >> $GITHUB_ENV - # echo "IMAGE=ghcr.io/${OWNER}/${IMG}" >> $GITHUB_ENV echo "IMAGE=${{ secrets.DOCKERHUB_USERNAME }}/${IMG}" >> $GITHUB_ENV - name: Build Latest Image if: ${{ github.ref_name == 'dev' }} @@ -75,4 +65,4 @@ jobs: tags: | ${{ env.IMAGE }}:alpine-${{ github.ref_name }} build-args: | - GIT_Branch=${{ github.ref_name }} \ No newline at end of file + GIT_Branch=${{ github.ref_name }} From 4b77e17487255d5676b59fa433c148f4f2c113eb Mon Sep 17 00:00:00 2001 From: AirportR Date: Sun, 4 Feb 2024 20:03:01 +0800 Subject: [PATCH 05/26] :memo: 3.6.7: Update color.yaml --- resources/color.yaml | 76 +++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/resources/color.yaml b/resources/color.yaml index 7b9a1c0e..140af8f2 100644 --- a/resources/color.yaml +++ b/resources/color.yaml @@ -1,46 +1,50 @@ #Group1 中国传统色 蓝色系 -#color: -# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 -# - { label: 0, name: '云水', value: '#B4C3D0' } -# - { label: 1, name: '晴山', value: '#91AEC4' } -# - { label: 5, name: '秋波', value: '#8BB8CB' } -# - { label: 10, name: '天韵', value: '#5796B3' } -# - { label: 20, name: '鸢尾', value: '#1988AE' } -# - { label: 60, name: '甸子', value: '#238BBC' } -# - { label: 100, name: '景泰', value: '#2A73AC' } +#image: +# color: +# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 +# - { label: 0, name: '云水', value: '#B4C3D0' } +# - { label: 1, name: '晴山', value: '#91AEC4' } +# - { label: 5, name: '秋波', value: '#8BB8CB' } +# - { label: 10, name: '天韵', value: '#5796B3' } +# - { label: 20, name: '鸢尾', value: '#1988AE' } +# - { label: 60, name: '甸子', value: '#238BBC' } +# - { label: 100, name: '景泰', value: '#2A73AC' } #Group2 憨批作者乱配版 -#color: -# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 -# - { label: 0, name: '山矾', value: '#f5f3f2' } -# - { label: 1, name: '葭灰', value: '#beb1aa' } -# - { label: 5, name: '桃夭', value: '#f6bec8' } -# - { label: 10, name: '长春', value: '#dc6b82' } -# - { label: 20, name: '牙绯', value: '#c35c5d' } -# - { label: 60, name: '东方既白', value: '#8ba3c7' } -# - { label: 100, name: '丹雘', value: '#c8161d' } +#image: +# color: +# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 +# - { label: 0, name: '山矾', value: '#f5f3f2' } +# - { label: 1, name: '葭灰', value: '#beb1aa' } +# - { label: 5, name: '桃夭', value: '#f6bec8' } +# - { label: 10, name: '长春', value: '#dc6b82' } +# - { label: 20, name: '牙绯', value: '#c35c5d' } +# - { label: 60, name: '东方既白', value: '#8ba3c7' } +# - { label: 100, name: '丹雘', value: '#c8161d' } #Group3 SSRSpeedN origin彩虹 -#color: -# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 -# - { label: 4, name: '绿', value: '#66ff66' } -# - { label: 8, name: '黄', value: '#ffff66' } -# - { label: 16, name: '橙', value: '#ffb266' } -# - { label: 24, name: '红', value: '#ff6666' } -# - { label: 32, name: '紫', value: '#e28cff' } -# - { label: 40, name: '蓝', value: '#66ccff' } -# - { label: 50, name: '深蓝', value: '#6666ff' } +#image: +# color: +# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 +# - { label: 4, name: '绿', value: '#66ff66' } +# - { label: 8, name: '黄', value: '#ffff66' } +# - { label: 16, name: '橙', value: '#ffb266' } +# - { label: 24, name: '红', value: '#ff6666' } +# - { label: 32, name: '紫', value: '#e28cff' } +# - { label: 40, name: '蓝', value: '#66ccff' } +# - { label: 50, name: '深蓝', value: '#6666ff' } #Group3 SSRSpeedN poor -#color: -# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 -# - { label: 4, name: '黄', value: '#ffd700' } -# - { label: 8, name: '橙', value: '#ffb201' } -# - { label: 16, name: '桃红', value: '#fc6972' } -# - { label: 24, name: '紫1', value: '#e982d9' } -# - { label: 32, name: '紫2', value: '#c26cff' } -# - { label: 40, name: '天蓝', value: '#66c0ff' } -# - { label: 50, name: '蓝', value: '#666fff' } +#image: +# color: +# speed: # 刚刚好有七个颜色,多一个少一个可能会有奇怪的效果。注意缩进。 +# - { label: 4, name: '黄', value: '#ffd700' } +# - { label: 8, name: '橙', value: '#ffb201' } +# - { label: 16, name: '桃红', value: '#fc6972' } +# - { label: 24, name: '紫1', value: '#e982d9' } +# - { label: 32, name: '紫2', value: '#c26cff' } +# - { label: 40, name: '天蓝', value: '#66c0ff' } +# - { label: 50, name: '蓝', value: '#666fff' } #Group4 杰尼龟测速频道miaoko配色 #color: From 7a1e724da0489dc16a0abd79a9b318d681e6e12d Mon Sep 17 00:00:00 2001 From: AirportR Date: Sun, 4 Feb 2024 20:03:24 +0800 Subject: [PATCH 06/26] :art: 3.6.7: Update version. --- utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/__init__.py b/utils/__init__.py index 08151c8c..8e6aa57f 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,4 +1,4 @@ from utils.cron import * -__version__ = "3.6.6" # 项目版本号 +__version__ = "3.6.7" # 项目版本号 __all__ = ["cron_delete_message", "cron_edit_message", "message_delete_queue", "message_edit_queue", "__version__"] From 24a387131894f691ab1aabdb1478e467bc158ffe Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:12:46 +0800 Subject: [PATCH 07/26] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3Docke?= =?UTF-8?q?r=E9=95=9C=E5=83=8F=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/README.md | 55 ++++++++++++++++++++++++++++----------- docker/docker-compose.yml | 2 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/docker/README.md b/docker/README.md index 55233afb..c2a4d080 100644 --- a/docker/README.md +++ b/docker/README.md @@ -3,19 +3,25 @@ > 这能让你在Windows、Mac、Linux、openwrt、Nas几乎任何支持Docker(目前仅Amd64和Arm64)的环境下使用此项目! ## 创建配置文件 -新建配置文件保存目录`mkdir /etc/FullTclash` + +新建配置文件保存目录`mkdir /etc/fulltclash` 下载并编辑配置文件 + +```bash +wget -O /etc/fulltclash/config.yaml https://raw.githubusercontent.com/AirportR/FullTclash/dev/resources/config.yaml.example ``` -wget -O /etc/FullTclash/config.yaml https://raw.githubusercontent.com/AirportR/FullTclash/dev/resources/config.yaml.example -``` + 修改 config.yaml (path是必须修改的配置,不能使用默认的) -``` + +```bash clash: path: './bin/fulltclash-origin' branch: origin ``` + 或者 [Meta内核](https://github.com/AirportR/FullTCore/tree/meta) -``` + +```bash clash: path: './bin/fulltclash-meta' branch: meta @@ -23,15 +29,19 @@ clash: ## 部署 - ### 拉取镜像 -> 镜像可选dev或者alpine,dev是通过Debian构建的 - ```bash -docker pull ghcr.io/airportr/fulltclash:dev +docker pull airportr/fulltclash:latest ``` +镜像还可以选择以下标签,其中 `latest` 标签是基于 `debian` `dev分支`构建 + +- debian-dev +- alpine-dev +- debian-master +- debian-master + ### docker #### 快速启动 @@ -54,7 +64,7 @@ docker run -idt \ -e http_proxy=127.0.0.1:7890 \ --network=host \ --restart always \ - ghcr.io/airportr/fulltclash:dev + airportr/fulltclash:latest ``` #### 挂载配置文件启动(推荐) @@ -65,7 +75,7 @@ docker run -itd \ --network=host \ --restart=always \ -v /etc/fulltclash/config.yaml:/app/resources/config.yaml \ - ghcr.io/airportr/fulltclash:dev + airportr/fulltclash:latest ``` ### docker-compose(推荐) @@ -83,14 +93,29 @@ docker-compose down ``` 查看日志 -``` + +```bash docker exec -it fulltclash tail -f /var/log/fulltclash.log ``` + 更新版本 -``` + +```bash docker exec -it fulltclash bash /app/docker/update.sh ``` + 重启程序 -``` + +```bash docker exec -it fulltclash supervisorctl restart fulltclash -``` \ No newline at end of file +``` + +进入容器 + +```bash +docker exec -it fulltclash bash +``` + +退出容器 + +`root@deeb9eaf66aa:/app# exit` diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ea162138..ae7ad7a1 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.9' services: fulltclash: - image: ghcr.io/airportr/fulltclash:dev + image: airportr/fulltclash:latest restart: always network_mode: host container_name: fulltclash From 26bade55f1895cdbdaed9b5ff29bb3f25c83fda7 Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:52:09 +0800 Subject: [PATCH 08/26] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=EF=BC=8C=E5=8A=A0=E5=BF=AB?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_meta_image.yml | 43 ++++++++++++++++++++++++++ .github/workflows/docker-publish.yml | 1 - docker/Dockerfile | 13 +------- docker/Dockerfile.alpine | 12 +------ docker/Metafile | 12 +++++++ docker/Metafile.alpine | 11 +++++++ 6 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/build_meta_image.yml create mode 100644 docker/Metafile create mode 100644 docker/Metafile.alpine diff --git a/.github/workflows/build_meta_image.yml b/.github/workflows/build_meta_image.yml new file mode 100644 index 00000000..0293b002 --- /dev/null +++ b/.github/workflows/build_meta_image.yml @@ -0,0 +1,43 @@ +name: build_meta_image + +on: + workflow_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set Environment Variables + run: | + IMG=fulltclash + echo "IMG=${IMG}" >> $GITHUB_ENV + echo "IMAGE=${{ secrets.DOCKERHUB_USERNAME }}/${IMG}" >> $GITHUB_ENV + - name: Build Debian Image + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Metafile + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ${{ env.IMAGE }}:debian + - name: Build Alpine Image + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Metafile.alpine + platforms: linux/amd64,linux/arm64 + push: true + tags: | + ${{ env.IMAGE }}:alpine \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 738a909a..00f26825 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -10,7 +10,6 @@ on: branches: - "master" - "dev" - - "backend" jobs: docker: diff --git a/docker/Dockerfile b/docker/Dockerfile index 3c6b2c5e..c66b93f3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,15 +1,4 @@ -FROM python:3.11.7-slim-bookworm AS compile-image - -RUN apt-get update && \ - apt-get install --no-install-recommends -y \ - gcc g++ make ca-certificates - -RUN python -m venv /opt/venv - -ENV PATH="/opt/venv/bin:$PATH" -ADD https://raw.githubusercontent.com/AirportR/FullTclash/dev/requirements.txt . -RUN pip3 install --no-cache-dir -r requirements.txt && \ - pip3 install --no-cache-dir supervisor +FROM airportr/fulltclash:debian AS compile-image FROM python:3.11.7-slim-bookworm diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 99730c48..c675206d 100644 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -1,14 +1,4 @@ -FROM python:3.11.7-alpine3.19 AS compile-image - -RUN apk add --no-cache \ - gcc g++ make libffi-dev libstdc++ gcompat libgcc build-base py3-pybind11-dev abseil-cpp-dev re2-dev ca-certificates - -RUN python -m venv /opt/venv - -ENV PATH="/opt/venv/bin:$PATH" -ADD https://raw.githubusercontent.com/AirportR/FullTclash/dev/requirements.txt . -RUN pip3 install --no-cache-dir -r requirements.txt && \ - pip3 install --no-cache-dir supervisor +FROM airportr/fulltclash:alpine AS compile-image FROM python:3.11.7-alpine3.19 diff --git a/docker/Metafile b/docker/Metafile new file mode 100644 index 00000000..980cc531 --- /dev/null +++ b/docker/Metafile @@ -0,0 +1,12 @@ +FROM python:3.11.7-slim-bookworm AS compile-image + +RUN apt-get update && \ + apt-get install --no-install-recommends -y \ + gcc g++ make ca-certificates + +RUN python -m venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" +ADD https://raw.githubusercontent.com/AirportR/FullTclash/dev/requirements.txt . +RUN pip3 install --no-cache-dir -r requirements.txt && \ + pip3 install --no-cache-dir supervisor \ No newline at end of file diff --git a/docker/Metafile.alpine b/docker/Metafile.alpine new file mode 100644 index 00000000..71131378 --- /dev/null +++ b/docker/Metafile.alpine @@ -0,0 +1,11 @@ +FROM python:3.11.7-alpine3.19 AS compile-image + +RUN apk add --no-cache \ + gcc g++ make libffi-dev libstdc++ gcompat libgcc build-base py3-pybind11-dev abseil-cpp-dev re2-dev ca-certificates + +RUN python -m venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" +ADD https://raw.githubusercontent.com/AirportR/FullTclash/dev/requirements.txt . +RUN pip3 install --no-cache-dir -r requirements.txt && \ + pip3 install --no-cache-dir supervisor \ No newline at end of file From c140210e8f13a1cee7b398c33e16da991026dd7b Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:15:11 +0800 Subject: [PATCH 09/26] Update build_meta_image.yml --- .github/workflows/build_meta_image.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_meta_image.yml b/.github/workflows/build_meta_image.yml index 0293b002..fb233a0b 100644 --- a/.github/workflows/build_meta_image.yml +++ b/.github/workflows/build_meta_image.yml @@ -1,7 +1,14 @@ name: build_meta_image on: - workflow_dispatch: + push: + paths-ignore: + - "resources/**" + - "README.md" + - "README-EN.md" + - ".github/ISSUE_TEMPLATE/**" + branches: + - "dev" jobs: docker: @@ -40,4 +47,4 @@ jobs: platforms: linux/amd64,linux/arm64 push: true tags: | - ${{ env.IMAGE }}:alpine \ No newline at end of file + ${{ env.IMAGE }}:alpine From 4ffb90d37c8810aadae1899968535ab7a7340ce1 Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:29:43 +0800 Subject: [PATCH 10/26] =?UTF-8?q?=E4=BF=AE=E6=94=B9ci=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E6=9D=A1=E4=BB=B6,=E5=87=8F=E5=B0=91=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_meta_image.yml | 8 +++----- .github/workflows/docker-publish.yml | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_meta_image.yml b/.github/workflows/build_meta_image.yml index fb233a0b..ac8c1c43 100644 --- a/.github/workflows/build_meta_image.yml +++ b/.github/workflows/build_meta_image.yml @@ -2,11 +2,9 @@ name: build_meta_image on: push: - paths-ignore: - - "resources/**" - - "README.md" - - "README-EN.md" - - ".github/ISSUE_TEMPLATE/**" + paths: + - 'docker/Metafile' + - 'docker/Metafile.alpine' branches: - "dev" diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 00f26825..a5215109 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -41,9 +41,11 @@ jobs: push: true tags: | ${{ env.IMAGE }}:latest + ${{ env.IMAGE }}:debian-${{ github.ref_name }} build-args: | GIT_Branch=${{ github.ref_name }} - name: Build Debian Image + if: ${{ github.ref_name != 'dev' }} uses: docker/build-push-action@v5 with: platforms: linux/amd64,linux/arm64 @@ -52,8 +54,6 @@ jobs: push: true tags: | ${{ env.IMAGE }}:debian-${{ github.ref_name }} - build-args: | - GIT_Branch=${{ github.ref_name }} - name: Build Alpine Image uses: docker/build-push-action@v5 with: From b55c3bd0191fb13b9544e04c56f5ccdd7c59924e Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:37:21 +0800 Subject: [PATCH 11/26] Update README.md --- docker/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/README.md b/docker/README.md index c2a4d080..28aaec1b 100644 --- a/docker/README.md +++ b/docker/README.md @@ -40,7 +40,7 @@ docker pull airportr/fulltclash:latest - debian-dev - alpine-dev - debian-master -- debian-master +- alpine-master ### docker @@ -59,7 +59,7 @@ docker run -idt \ -e core=4 \ -e startup=1124 \ -e speedthread=4 \ - -e nospeed=true \ + -e nospeed=false \ -e s5_proxy=127.0.0.1:7890 \ -e http_proxy=127.0.0.1:7890 \ --network=host \ From 9e111f758db76e063466d449f0dd6a9b1c9feef2 Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:48:29 +0800 Subject: [PATCH 12/26] =?UTF-8?q?=E5=87=8F=E5=B0=8F=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E4=BD=93=E7=A7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/Metafile | 9 ++++++++- docker/Metafile.alpine | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docker/Metafile b/docker/Metafile index 980cc531..fe36b482 100644 --- a/docker/Metafile +++ b/docker/Metafile @@ -9,4 +9,11 @@ RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" ADD https://raw.githubusercontent.com/AirportR/FullTclash/dev/requirements.txt . RUN pip3 install --no-cache-dir -r requirements.txt && \ - pip3 install --no-cache-dir supervisor \ No newline at end of file + pip3 install --no-cache-dir supervisor + +FROM python:3.11.7-slim-bookworm + +COPY --from=compile-image /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=compile-image /opt/venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" \ No newline at end of file diff --git a/docker/Metafile.alpine b/docker/Metafile.alpine index 71131378..9c2548eb 100644 --- a/docker/Metafile.alpine +++ b/docker/Metafile.alpine @@ -8,4 +8,11 @@ RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" ADD https://raw.githubusercontent.com/AirportR/FullTclash/dev/requirements.txt . RUN pip3 install --no-cache-dir -r requirements.txt && \ - pip3 install --no-cache-dir supervisor \ No newline at end of file + pip3 install --no-cache-dir supervisor + +FROM python:3.11.7-alpine3.19 + +COPY --from=compile-image /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=compile-image /opt/venv /opt/venv + +ENV PATH="/opt/venv/bin:$PATH" \ No newline at end of file From 606e4c21ad4587d4e2fbce8b363aa62ee20b8a65 Mon Sep 17 00:00:00 2001 From: cola <44671411+aipeach@users.noreply.github.com> Date: Sun, 4 Feb 2024 22:15:40 +0800 Subject: [PATCH 13/26] =?UTF-8?q?fix=20=E4=BF=AE=E6=94=B9=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=E6=96=87=E4=BB=B6=20=E8=87=AA=E5=8A=A8=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_meta_image.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_meta_image.yml b/.github/workflows/build_meta_image.yml index ac8c1c43..6c800088 100644 --- a/.github/workflows/build_meta_image.yml +++ b/.github/workflows/build_meta_image.yml @@ -5,6 +5,7 @@ on: paths: - 'docker/Metafile' - 'docker/Metafile.alpine' + - 'requirements.txt' branches: - "dev" From cd165325b061e4c0c252407f08cc5358aeb33568 Mon Sep 17 00:00:00 2001 From: rebecca554owen Date: Sun, 4 Feb 2024 23:11:08 +0800 Subject: [PATCH 14/26] Update docker-publish.yml --- .github/workflows/docker-publish.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a5215109..8f379cfe 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -31,6 +31,9 @@ jobs: IMG=fulltclash echo "IMG=${IMG}" >> $GITHUB_ENV echo "IMAGE=${{ secrets.DOCKERHUB_USERNAME }}/${IMG}" >> $GITHUB_ENV + - name: Get version + id: get_version + run: echo "version=$(git describe --tags --always)" >> $GITHUB_OUTPUT - name: Build Latest Image if: ${{ github.ref_name == 'dev' }} uses: docker/build-push-action@v5 @@ -42,6 +45,7 @@ jobs: tags: | ${{ env.IMAGE }}:latest ${{ env.IMAGE }}:debian-${{ github.ref_name }} + ${{ env.IMAGE }}:${{ steps.get_version.outputs.version }} build-args: | GIT_Branch=${{ github.ref_name }} - name: Build Debian Image @@ -54,6 +58,7 @@ jobs: push: true tags: | ${{ env.IMAGE }}:debian-${{ github.ref_name }} + ${{ env.IMAGE }}:${{ steps.get_version.outputs.version }} - name: Build Alpine Image uses: docker/build-push-action@v5 with: @@ -63,5 +68,6 @@ jobs: push: true tags: | ${{ env.IMAGE }}:alpine-${{ github.ref_name }} + ${{ env.IMAGE }}:${{ steps.get_version.outputs.version }} build-args: | GIT_Branch=${{ github.ref_name }} From c7cbc25ef730a44c33ffc6749e13bf5e34375f20 Mon Sep 17 00:00:00 2001 From: AirportR Date: Mon, 5 Feb 2024 17:20:37 +0800 Subject: [PATCH 15/26] :sparkles: 3.6.7: Frequently selected backends will be ranked first. --- botmodule/__init__.py | 5 +++- botmodule/command/setting.py | 25 +++++++++++++++---- botmodule/record.py | 46 +++++++++++++++++++++++++++++++++++ resources/config.yaml.example | 11 +++++---- utils/cleaner.py | 23 ++++++++++++++++++ utils/export.py | 2 +- utils/myqueue.py | 6 +++-- 7 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 botmodule/record.py diff --git a/botmodule/__init__.py b/botmodule/__init__.py index 8b1a2e87..1d8e00d4 100644 --- a/botmodule/__init__.py +++ b/botmodule/__init__.py @@ -7,10 +7,13 @@ from botmodule.command.download import * from botmodule.command.connect import * from botmodule.command import common_command +from botmodule.command.edit import * from botmodule import register from botmodule import subinfo -from botmodule.command.edit import * +from botmodule.record import init_memory_ranking + +init_memory_ranking() __all__ = ['grant', 'ungrant', 'user', 'restart_or_killme', 'sub_invite', 'sub', 'new', 'remove', 'process', 'invite', 'invite_pass2', diff --git a/botmodule/command/setting.py b/botmodule/command/setting.py index 6360e46d..86259248 100644 --- a/botmodule/command/setting.py +++ b/botmodule/command/setting.py @@ -15,6 +15,7 @@ from utils.check import get_telegram_id_from_message as getID from utils import message_delete_queue as mdq from glovar import __version__ +from botmodule.record import get_slave_ranking from botmodule.rule import get_rule, new_rule from botmodule.init_bot import latest_version_hash as v_hash, config from botmodule.command.authority import (Invite, INVITE_SELECT_CACHE as ISC, @@ -411,15 +412,26 @@ def page_frame(pageprefix: str, contentprefix: str, content: List[str], split: s return content_keyboard +def get_ranked_slave_list(slaveconf: dict, sorted_ranking: dict): + new_dict = {} + for k, _ in sorted_ranking.items(): + if k in slaveconf: + new_dict[k] = slaveconf.pop(k) + new_dict.update(slaveconf) + return new_dict + + async def select_slave_page(_: Client, call: Union[CallbackQuery, Message], content_prefix: str = "slave:", **kwargs): """ 选择后端页面的入口 content_prefix: 后端的回调按钮数据的前缀,默认为slave: """ slaveconfig = config.getSlaveconfig() - - comment = [i.get('comment', None) for k, i in slaveconfig.items() if - i.get('comment', None) and k != "default-slave"] + slaveconfig.pop("default-slave") + usermsg = call.message.reply_to_message if isinstance(call, CallbackQuery) else call + user_ranking = get_slave_ranking(getID(usermsg)) + slaveconfig = get_ranked_slave_list(slaveconfig, user_ranking) + comment = [i.get('comment', None) for k, i in slaveconfig.items() if i.get('comment', None)] page = kwargs.get('page', 1) row = kwargs.get('row', 5) @@ -514,8 +526,11 @@ async def select_slave_only_1(_: Client, call: Union[CallbackQuery, Message], ** api_route = '/api/getSlaveId' page = 1 if isinstance(call, Message) else call.data[len(page_prefix):] slaveconfig = config.getSlaveconfig() - comment = [i.get('comment', None) for k, i in slaveconfig.items() if - i.get('comment', None) and k != "default-slave"] + slaveconfig.pop("default-slave") + usermsg = call.message.reply_to_message if isinstance(call, CallbackQuery) else call + user_ranking = get_slave_ranking(getID(usermsg)) + slaveconfig = get_ranked_slave_list(slaveconfig, user_ranking) + comment = [i.get('comment', None) for k, i in slaveconfig.items() if i.get('comment', None)] content_keyboard = page_frame(page_prefix, api_route, comment, split='?comment=', page=page, **kwargs) if page == 1: if not ds_shadow: diff --git a/botmodule/record.py b/botmodule/record.py new file mode 100644 index 00000000..62a16f15 --- /dev/null +++ b/botmodule/record.py @@ -0,0 +1,46 @@ +from typing import Union + +from pyrogram.types import Message +from botmodule.init_bot import config +from utils.check import get_telegram_id_from_message as getID + +MEM_RANKING = {} + + +def init_memory_ranking(): + global MEM_RANKING + MEM_RANKING = config.get_slave_ranking() + + +def get_slave_ranking(ID: Union[str, int]): + return MEM_RANKING.get(ID, {}) + + +def update_ranking(ID: Union[str, int], slaveid: Union[str, int], value: int): + userconf = config.getUserconfig() + usage_ranking = userconf.get('usage-ranking', {}) + if not isinstance(usage_ranking, dict): + usage_ranking = {} + user_ranking = usage_ranking.get(ID, {}) + if not isinstance(user_ranking, dict): + return {} + user_ranking[slaveid] = value + usage_ranking[ID] = user_ranking + config.yaml['userconfig']['usage-ranking'] = usage_ranking + config.reload() + + +def record_ranking(message: "Message", slaveid: Union[str, int]): + ID = getID(message) + if ID not in MEM_RANKING: + MEM_RANKING[ID] = {} + MEM_RANKING[ID][slaveid] = 0 + try: + num = int(MEM_RANKING.get(ID, {}).get(slaveid, 0)) + except (ValueError, TypeError): + num = 0 + num += 1 + MEM_RANKING[ID][slaveid] = num + + if num and (num % 5 == 0 or num == 1): + update_ranking(ID, slaveid, num) diff --git a/resources/config.yaml.example b/resources/config.yaml.example index b333eaaa..280b8b65 100644 --- a/resources/config.yaml.example +++ b/resources/config.yaml.example @@ -5,6 +5,11 @@ bot: api_id: 123456 # Telegram的api_id api_hash: ABCDEFG # Telegram的api_hash bot_token: 123456:ABCDEFG #bot的token +clash: + path: './bin/FullTCore' # 代理客户端的路径,默认为 ./bin/ 下。下载地址: https://github.com/AirportR/FullTCore/releases +# allow-caching: false #是否缓存测试过程产生的订阅,若否,则测试完成后会自动删除测试订阅,以免造成空间浪费。(此配置暂不生效) +# core: 4 # 核心数,数量越多测试速度越快,但代价是内存会大量占用(每个核心大概10M),自己的机子多少内存自己平衡。默认值为1. +# branch: origin #clash内核上游分支,仅有两个有效值: [origin, meta], meta分支支持更多协议,比如vless、tuic等,但是使用上较为不可控,默认为origin原生内核。 # ====================以上为必填项,否则无法启动成功======================== # proxy: host:端口:用户名:密码 #socks5代理,用户名和密码为可选配置,已和下面的proxy单独分离,请勿混用 # strictmode: false # 严格模式,代表测试的时侯,bot内联按钮只有发起测试的主人才能按,否则所有用户用户权限都可以按。默认false,目前未适配,敬请期待。 @@ -18,11 +23,6 @@ bot: # command: ['mycommand1', 'mycommand2'] #自定义指令,用于适配权限回调。高级用法,不会无需配置。 #以下是fulltclash的buildtoken,可以用默认的,但是安全性得不到保障,具体原因详见文档 #buildtoken: c7004ded9db897e538405c67e50e0ef0c3dbad717e67a92d02f6ebcfd1022a5ad1d2c4419541f538ff623051759ec000d2f426e03f9709a6608570c5b9141a6b -#clash: -# path: './bin/fulltclash-linux-amd64' # 代理客户端的路径,默认为 ./bin/ 下。不会弄不要动这里,用默认的即可。本仓库默认仅提供win-amd64与linux-amd64两种(意味着如果为这两种架构,这里不用改。),其他架构需要自行编译。 -# allow-caching: false #是否缓存测试过程产生的订阅,若否,则测试完成后会自动删除测试订阅,以免造成空间浪费。(此配置暂不生效) -# core: 4 # 核心数,数量越多测试速度越快,但代价是内存会大量占用(每个核心大概10M),自己的机子多少内存自己平衡。默认值为1. -# branch: origin #clash内核上游分支,仅有两个有效值: [origin, meta], meta分支支持更多协议,比如vless、tuic等,但是使用上较为不可控,默认为origin原生内核。 #proxy: "host:端口" # http配置代理(非socks5),括号内可选用于下载获取订阅链接,订阅链接国内打不开可尝试开启(可选配置),本机提供的代理可以这样填: '127.0.0.1:7890' #如果要http代理要验证,配置格式为: proxy: "用户名:密码@host:端口" 比如: user1:112233@127.0.0.1:7890 #geoip-api: "ip-api.com" # GEOIP 测试api,取二级域名,目前支持 ip-api.com ip.sb ipleak.net ipdata.co ipapi.co 五种,其中 ipdata 需要配置下面的geoip-key @@ -73,6 +73,7 @@ bot: # slaveid: local #slaveid的有效值参照配置里的 slaveconfig下的所有键。 # script: ["Netflix", "Youtube", "Disney+", "维基百科"] #script的有效值参照连通性测试绘图结果中显示的名称(大小写敏感)。 # sort: "HTTP升序" +# usage-ranking: #后端使用排行,这里记录者用户使用的后端次数,以便测试选择后端时把最常用的后端显示在第一页,这个配置是bot自动生成的,不用手动修改,你也不需要去管它。 # 查看你的机器人的所有可用测试项的python变量: # from utils.cleaner import addon # print(addon.global_test_item) diff --git a/utils/cleaner.py b/utils/cleaner.py index 645958f3..c0c6b722 100644 --- a/utils/cleaner.py +++ b/utils/cleaner.py @@ -11,9 +11,11 @@ try: import re2 as re + remodule = re except ImportError: import re + remodule = re @@ -771,6 +773,27 @@ def speednodes(self) -> int: except (KeyError, ValueError): return 300 + def get_slave_ranking(self, ID: Union[str, int] = None): + """ + 获取后端使用排行 + """ + userconf = self.getUserconfig() + usage_ranking = userconf.get('usage-ranking', {}) + if not isinstance(usage_ranking, dict): + usage_ranking = {} + if ID is None: + # 返回全部用户的排行 + return usage_ranking + else: + user_ranking = usage_ranking.get(ID, {}) + if not isinstance(user_ranking, dict): + return {} + if not user_ranking: + return user_ranking + sorted_ranking = sorted(user_ranking.items(), key=lambda x: x[1], reverse=True) + sorted_ranking = {item: c for item, c in sorted_ranking} + return sorted_ranking + def getConcurrency(self) -> int: clashconf = self.config.get('clash', {}) max_workers = min(32, (os.cpu_count() or 1) + 14) diff --git a/utils/export.py b/utils/export.py index 9c5cfc78..0587b551 100644 --- a/utils/export.py +++ b/utils/export.py @@ -1349,7 +1349,7 @@ def exportTopoOutbound(self, nodename: list = None, info: dict = None, img2_widt export_time = export_time.replace(':', '-') title = "出口分析" - footer1 = f"📊版本:{__version__} 后端:{slavecomment} 概要:{max_entrance}->{cuk}" + footer1 = f"📊版本={__version__} 后端={slavecomment} 概要={max_entrance}->{cuk}" footer2 = f"{emoji_time}测试时间: {export_time}({system_timezone}) 总共耗时: {self.wtime}s {tips}" maxwidth = max(self.text_width(footer1, True), self.text_width(footer2, True), image_width) + 15 info_list_length[-1] += maxwidth - image_width diff --git a/utils/myqueue.py b/utils/myqueue.py index 9a11204a..e2db0ef0 100644 --- a/utils/myqueue.py +++ b/utils/myqueue.py @@ -2,12 +2,13 @@ from loguru import logger from pyrogram import Client from pyrogram.types import Message + from utils.collector import reload_config as r1 from utils.cleaner import reload_config as r2, addon -# from utils import message_edit_queue - import botmodule from botmodule.command.test import convert_core_index +from botmodule.record import record_ranking + SPEED_Q = asyncio.Queue(1) # 速度测试队列。确保同一时间只有一个测速任务在占用带宽 CONN_Q = asyncio.Queue(3) # 连通性、拓扑测试队列,最大同时测试数量为10个任务,设置太高会影响到测速的带宽,进而影响结果。 @@ -54,6 +55,7 @@ async def bot_put(client: Client, message: Message, put_type: str, test_items: l test_items = [] logger.info("任务测试项为: " + str(test_items)) slaveid = kwargs.get('slaveid', 'local') + record_ranking(message, slaveid) if slaveid != 'local': await botmodule.process(client, message, put_type=put_type, test_items=test_items, **kwargs) return From 6a0aea4a645c0668f44f939d778fd1bbb6138f2d Mon Sep 17 00:00:00 2001 From: AirportR Date: Mon, 5 Feb 2024 17:29:37 +0800 Subject: [PATCH 16/26] :bug: 3.6.7: Fixed --- botmodule/command/setting.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/botmodule/command/setting.py b/botmodule/command/setting.py index 86259248..c347586e 100644 --- a/botmodule/command/setting.py +++ b/botmodule/command/setting.py @@ -427,7 +427,8 @@ async def select_slave_page(_: Client, call: Union[CallbackQuery, Message], cont content_prefix: 后端的回调按钮数据的前缀,默认为slave: """ slaveconfig = config.getSlaveconfig() - slaveconfig.pop("default-slave") + if "default-slave" in slaveconfig: + slaveconfig.pop("default-slave") usermsg = call.message.reply_to_message if isinstance(call, CallbackQuery) else call user_ranking = get_slave_ranking(getID(usermsg)) slaveconfig = get_ranked_slave_list(slaveconfig, user_ranking) @@ -526,7 +527,8 @@ async def select_slave_only_1(_: Client, call: Union[CallbackQuery, Message], ** api_route = '/api/getSlaveId' page = 1 if isinstance(call, Message) else call.data[len(page_prefix):] slaveconfig = config.getSlaveconfig() - slaveconfig.pop("default-slave") + if "default-slave" in slaveconfig: + slaveconfig.pop("default-slave") usermsg = call.message.reply_to_message if isinstance(call, CallbackQuery) else call user_ranking = get_slave_ranking(getID(usermsg)) slaveconfig = get_ranked_slave_list(slaveconfig, user_ranking) From 6b9d2cdef71bb4b7193ab6a82c08e305c7b87e46 Mon Sep 17 00:00:00 2001 From: AirportR Date: Mon, 5 Feb 2024 17:54:46 +0800 Subject: [PATCH 17/26] :bug: 3.6.7: Fix local button not showing on first page --- botmodule/command/setting.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/botmodule/command/setting.py b/botmodule/command/setting.py index c347586e..6a1b59a6 100644 --- a/botmodule/command/setting.py +++ b/botmodule/command/setting.py @@ -414,10 +414,11 @@ def page_frame(pageprefix: str, contentprefix: str, content: List[str], split: s def get_ranked_slave_list(slaveconf: dict, sorted_ranking: dict): new_dict = {} + slaveconf_copy = deepcopy(slaveconf) for k, _ in sorted_ranking.items(): - if k in slaveconf: - new_dict[k] = slaveconf.pop(k) - new_dict.update(slaveconf) + if k in slaveconf_copy: + new_dict[k] = slaveconf_copy.pop(k) + new_dict.update(slaveconf_copy) return new_dict @@ -426,7 +427,7 @@ async def select_slave_page(_: Client, call: Union[CallbackQuery, Message], cont 选择后端页面的入口 content_prefix: 后端的回调按钮数据的前缀,默认为slave: """ - slaveconfig = config.getSlaveconfig() + slaveconfig = config.getSlaveconfig().copy() if "default-slave" in slaveconfig: slaveconfig.pop("default-slave") usermsg = call.message.reply_to_message if isinstance(call, CallbackQuery) else call @@ -434,8 +435,8 @@ async def select_slave_page(_: Client, call: Union[CallbackQuery, Message], cont slaveconfig = get_ranked_slave_list(slaveconfig, user_ranking) comment = [i.get('comment', None) for k, i in slaveconfig.items() if i.get('comment', None)] - page = kwargs.get('page', 1) - row = kwargs.get('row', 5) + page = int(kwargs.get('page', 1)) + row = int(kwargs.get('row', 5)) max_page = int(len(comment) / row) + 1 pre_page_text = page - 1 if page - 1 > 0 else 1 next_page_text = page + 1 if page < max_page else max_page @@ -525,8 +526,8 @@ async def select_slave_only_1(_: Client, call: Union[CallbackQuery, Message], ** """ page_prefix = '/api/slave/page/' api_route = '/api/getSlaveId' - page = 1 if isinstance(call, Message) else call.data[len(page_prefix):] - slaveconfig = config.getSlaveconfig() + page = 1 if isinstance(call, Message) else int(call.data[len(page_prefix):]) + slaveconfig = config.getSlaveconfig().copy() if "default-slave" in slaveconfig: slaveconfig.pop("default-slave") usermsg = call.message.reply_to_message if isinstance(call, CallbackQuery) else call From 0662916e1a8199c84ab0e9014daaf0deb1a2888e Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 13 Feb 2024 21:17:22 +0800 Subject: [PATCH 18/26] :bug: 3.6.7: Fix openai.py --- addons/builtin/netflix.py | 71 ++++++---------- addons/builtin/openai.py | 172 +++++++++++++++++--------------------- utils/__init__.py | 37 +++++++- 3 files changed, 137 insertions(+), 143 deletions(-) diff --git a/addons/builtin/netflix.py b/addons/builtin/netflix.py index 8a417488..bd516bf3 100644 --- a/addons/builtin/netflix.py +++ b/addons/builtin/netflix.py @@ -43,13 +43,13 @@ async def fetch_netflix(Collector, session: aiohttp.ClientSession, flag=1, proxy locate = text.find("preferredLocale") # 定位到关键标签 if locate > 0: region = text[locate + 29:locate + 31] - Collector.info['netflix_new'] = f"解锁({region})" + Collector.info['netflix'] = f"解锁({region})" else: region = "未知" - Collector.info['netflix_new'] = f"解锁({region})" + Collector.info['netflix'] = f"解锁({region})" except IndexError as e: logger.error(e) - Collector.info['netflix_new'] = "N/A" + Collector.info['netflix'] = "N/A" elif res.status == 403: if reconnection == 0: logger.info("不支持非自制剧,正在检测自制剧...") @@ -66,17 +66,17 @@ async def fetch_netflix(Collector, session: aiohttp.ClientSession, flag=1, proxy elif flag == 2: async with session.get(netflix_url2, proxy=proxy, timeout=5) as res: if res.status == 200: # 解锁自制 - Collector.info['netflix_new'] = "自制" + Collector.info['netflix'] = "自制" elif res.status == 403: if reconnection == 0: - Collector.info['netflix_new'] = "失败" + Collector.info['netflix'] = "失败" return await fetch_netflix(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) elif res.status == 503: - Collector.info['netflix_new'] = "-" + Collector.info['netflix'] = "-" return else: - Collector.info['netflix_new'] = "失败" + Collector.info['netflix'] = "失败" else: return except ClientConnectorError as c: @@ -84,20 +84,20 @@ async def fetch_netflix(Collector, session: aiohttp.ClientSession, flag=1, proxy if reconnection != 0: await fetch_netflix(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) else: - Collector.info['netflix_new'] = "连接错误" + Collector.info['netflix'] = "连接错误" except ServerDisconnectedError as s: logger.warning("Netflix请求发生错误:" + str(s)) if reconnection != 0: await fetch_netflix(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) else: - Collector.info['netflix_new'] = "-" + Collector.info['netflix'] = "-" except asyncio.exceptions.TimeoutError: logger.warning("Netflix请求超时,正在重新发送请求......") if reconnection != 0: await fetch_netflix(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) else: - Collector.info['netflix_new'] = "超时" + Collector.info['netflix'] = "超时" except ProxyConnectionError as p: logger.warning("似乎目标端口未开启监听") logger.warning(str(p)) @@ -133,40 +133,37 @@ async def fetch_netflix_new(Collector, session: aiohttp.ClientSession, flag=1, p locate = text.find("preferredLocale") # 定位到关键标签 if locate > 0: region = text[locate + 29:locate + 31] - Collector.info['netflix_new'] = f"解锁({region})" + Collector.info['netflix'] = f"解锁({region})" else: region = "未知" - Collector.info['netflix_new'] = f"解锁({region})" + Collector.info['netflix'] = f"解锁({region})" except IndexError as e: logger.error(e) - Collector.info['netflix_new'] = "N/A" + Collector.info['netflix'] = "N/A" elif res.status == 403: if reconnection == 0: - logger.info("不支持非自制剧,正在检测自制剧...") await fetch_netflix_new(Collector, session, flag=flag + 1, proxy=proxy, reconnection=5) return await fetch_netflix_new(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) elif res.status == 503: - logger.info("非自制剧服务不可用(被banIP),正在检测自制剧...") await fetch_netflix_new(Collector, session, flag=flag + 1, proxy=proxy, reconnection=5) return else: - logger.info("不支持非自制剧,正在检测自制剧...") await fetch_netflix_new(Collector, session, flag=flag + 1, proxy=proxy, reconnection=reconnection) elif flag == 2: async with session.get(netflix_url2, proxy=proxy, timeout=5, ssl=_myssl) as res: if res.status == 200: # 解锁自制 - Collector.info['netflix_new'] = "自制" + Collector.info['netflix'] = "自制" elif res.status == 403: if reconnection == 0: - Collector.info['netflix_new'] = "失败" + Collector.info['netflix'] = "失败" return await fetch_netflix_new(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) elif res.status == 503: - Collector.info['netflix_new'] = "-" + Collector.info['netflix'] = "-" return else: - Collector.info['netflix_new'] = "失败" + Collector.info['netflix'] = "失败" else: return except ClientConnectorError as c: @@ -174,22 +171,19 @@ async def fetch_netflix_new(Collector, session: aiohttp.ClientSession, flag=1, p if reconnection != 0: await fetch_netflix_new(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) else: - Collector.info['netflix_new'] = "连接错误" - except ServerDisconnectedError as s: - logger.warning("Netflix请求发生错误:" + str(s)) + Collector.info['netflix'] = "连接错误" + except ServerDisconnectedError: if reconnection != 0: await fetch_netflix_new(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) else: - Collector.info['netflix_new'] = "-" + Collector.info['netflix'] = "-" except asyncio.exceptions.TimeoutError: - logger.warning("Netflix请求超时,正在重新发送请求......") if reconnection != 0: await fetch_netflix_new(Collector, session, flag=flag, proxy=proxy, reconnection=reconnection - 1) else: - Collector.info['netflix_new'] = "超时" + Collector.info['netflix'] = "超时" except ProxyConnectionError as p: - logger.warning("似乎目标端口未开启监听") logger.warning(str(p)) @@ -214,12 +208,12 @@ def get_netflix_info(ReCleaner): :return: str: 解锁信息: [解锁(地区代码)、失败、N/A] """ try: - if 'netflix_new' not in ReCleaner.data: + if 'netflix' not in ReCleaner.data: logger.warning("采集器内无数据") return "N/A" else: - logger.info("netflix解锁:" + str(ReCleaner.data.get('netflix_new', "N/A"))) - return ReCleaner.data.get('netflix_new', "N/A") + logger.info("netflix解锁:" + str(ReCleaner.data.get('netflix', "N/A"))) + return ReCleaner.data.get('netflix', "N/A") except Exception as e: logger.error(e) return "N/A" @@ -249,24 +243,9 @@ class FakeColl: def __init__(self): self.info = {} - CHECK_URL = "https://tls.browserleaks.com/json" coll = FakeColl() - # proxies = {'http': 'http://localhost:1112', 'https': 'http://localhost:1112'} # 记得设置代理 - # fetch_netflix_old(coll, proxy=proxies) # 这个是request请求客户端,正常访问 - # print(coll.info) - # sc = ssl.create_default_context() - # connector = aiohttp.TCPConnector(ssl=myssl()) - - # 下面注释的这一个,将一个自定义的sslcontext传入,成功检测 - # async with aiohttp.ClientSession(connector=connector) as session: - # 下面用aiohttp进行请求在某些系统将抛出 Server Disconnected 异常。 async with aiohttp.ClientSession(connector=None) as session: - # 注释的这个请求是查看ja3_hash的,它是一种TLS指纹算法 - async with session.get(CHECK_URL, proxy="http://127.0.0.1:1112") as resp: - print(await resp.text()) - - await fetch_netflix(coll, session, proxy="http://127.0.0.1:1112") # 记得设置代理 - # 2023-09-08 19:39:19.021 | WARNING | __main__:fetch_netflix:88 - Netflix请求发生错误:Server disconnected + await fetch_netflix(coll, session, proxy="http://127.0.0.1:11112") print(coll.info) await asyncio.sleep(2) diff --git a/addons/builtin/openai.py b/addons/builtin/openai.py index 61c81a1e..ed3ddc77 100644 --- a/addons/builtin/openai.py +++ b/addons/builtin/openai.py @@ -1,103 +1,88 @@ import asyncio +import re + import aiohttp -from aiohttp import ClientConnectorError from loguru import logger -# collector section -from pyrogram.types import InlineKeyboardButton +try: + from utils import retry +except ImportError: + def retry(count=5): + def wrapper(func): + async def inner(*args, **kwargs): + await func(*args, count=count, **kwargs) + + return inner -openaiurl = "https://chat.openai.com/favicon.ico" + return wrapper -# openaiurl = "https://chat.openai.com" openaiurl2 = "https://chat.openai.com/cdn-cgi/trace" -SUPPORT_REGION = ['AL', 'DZ', 'AD', 'AO', 'AG', 'AR', 'AM', 'AU', 'AT', 'AZ', 'BS', 'BD', 'BB', 'BE', 'BZ', 'BJ', 'BT', - 'BA', 'UA', - 'BW', 'BR', 'BG', 'BF', 'CV', 'CA', 'CL', 'CO', 'KM', 'CR', 'HR', 'CY', 'DK', 'DJ', 'DM', 'DO', 'EC', - 'SV', - 'EE', 'FJ', 'FI', 'FR', 'GA', 'GM', 'GE', 'DE', 'GH', 'GR', 'GD', 'GT', 'GN', 'GW', 'GY', 'HT', 'HN', - 'HU', - 'IS', 'IN', 'ID', 'IQ', 'IE', 'IL', 'JM', 'JP', 'JO', 'KZ', 'KE', 'KI', 'KW', 'KG', 'LV', 'LB', - 'LS', - 'LR', 'LI', 'LT', 'LU', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MR', 'MU', 'MX', 'MC', 'MN', 'ME', - 'MA', - 'MZ', 'MM', 'NA', 'NR', 'NP', 'NL', 'NZ', 'NI', 'NE', 'NG', 'MK', 'NO', 'OM', 'PK', 'PW', 'PA', 'PG', - 'PE', - 'PH', 'PL', 'PT', 'QA', 'RO', 'RW', 'KN', 'LC', 'VC', 'WS', 'SM', 'ST', 'SN', 'RS', 'SC', 'SL', 'SG', - 'SK', - 'SI', 'SB', 'ZA', 'ES', 'LK', 'SR', 'SE', 'CH', 'TH', 'TG', 'TO', 'TT', 'TN', 'TR', 'TV', 'UG', 'AE', - 'US', - 'UY', 'VU', 'ZM', 'BO', 'BN', 'CG', 'CZ', 'VA', 'FM', 'MD', 'PS', 'KR', 'TW', 'TZ', 'TL', 'GB'] - - -async def fetch_openai(Collector, session: aiohttp.ClientSession, proxy=None, reconnection=2): + + +@retry(3) +async def fetch_openai(collector, session: aiohttp.ClientSession, proxy=None): """ openai封锁检测 - :param Collector: 采集器 + :param collector: 采集器 :param session: :param proxy: - :param reconnection: 重连次数 :return: """ - # openaiurl1 = "https://chat.openai.com" # openaiurl - # openaiurl2 = "https://chat.openai.com/cdn-cgi/trace" - try: - _headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + - 'Chrome/102.0.5005.63 Safari/537.36', - } - async with session.get(openaiurl, headers=_headers, proxy=proxy, timeout=10) as res: - res1_status = res.status - if res.status == 403: - # ct = res.headers.get('Content-Type', 'None') - # if 'text/html' in ct: - # Collector.info['OpenAI'] = "失败" - # return - text = await res.text() - if text.find('Please stand by, while we are checking your browser') > 0: - logger.info("OpenAI检测到CloudFlare拦截") - Collector.info['OpenAI'] = "CloudFlare拦截" - - index = text.find("Sorry, you have been blocked") - index2 = text.find("Unable to load site") - if index > 0 or index2 > 0: - Collector.info['OpenAI'] = "失败" - return - index2 = text.find("You do not have access to chat.openai.com.") - if index2 > 0: - Collector.info['OpenAI'] = "失败" - return - - else: - Collector.info['OpenAI'] = "未知" - async with session.get(openaiurl2, headers=_headers, proxy=proxy, timeout=10) as res2: - if res2.status == 200 and (res1_status == 200 or res1_status == 403): - text2 = await res2.text() - index2 = text2.find("loc=") - if index2 > 0: - region = text2[index2 + 4:index2 + 6] - if region in SUPPORT_REGION: - Collector.info['OpenAI'] = f"解锁({region})" - else: - Collector.info['OpenAI'] = f"待解({region})" - else: - Collector.info['OpenAI'] = "未知" - else: - Collector.info['OpenAI'] = "N/A" - - except ClientConnectorError as c: - logger.warning("OpenAI请求发生错误:" + str(c)) - if reconnection != 0: - await fetch_openai(Collector, session, proxy=proxy, reconnection=reconnection - 1) - else: - Collector.info['OpenAI'] = "连接错误" - return - except asyncio.exceptions.TimeoutError: - if reconnection != 0: - logger.warning("OpenAI请求超时,正在重新发送请求......") - await fetch_openai(Collector, session, proxy=proxy, reconnection=reconnection - 1) - else: - Collector.info['OpenAI'] = "超时" - return + resp1 = await session.get('https://api.openai.com/compliance/cookie_requirements', headers={ + 'authority': 'api.openai.com', + 'accept': '*/*', + 'accept-language': 'zh-CN,zh;q=0.9', + 'authorization': 'Bearer null', + 'content-type': 'application/json', + 'origin': 'https://platform.openai.com', + 'referer': 'https://platform.openai.com/', + 'sec-ch-ua': '"Microsoft Edge";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-site', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + + 'Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' + }, proxy=proxy, timeout=5) + if resp1.status != 200: + return True + resp2 = await session.get('https://ios.chat.openai.com/', headers={ + 'authority': 'ios.chat.openai.com', + 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/' + + 'signed-exchange;v=b3;q=0.7', + 'accept-language': 'zh-CN,zh;q=0.9', + 'sec-ch-ua': '"Microsoft Edge";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'none', + 'sec-fetch-user': '?1', + 'upgrade-insecure-requests': '1', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + + 'Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' + }, proxy=proxy, timeout=5) + # 获取响应的文本内容 + text1 = await resp1.text() + text2 = await resp2.text() + resp1.close() + resp2.close() + # 检查是否包含特定的字符串 + result1 = re.search('unsupported_country', text1) + result2 = re.search('VPN', text2) + # 根据结果输出不同的信息 + if not result2 and not result1: + collector.info['openai'] = "完全解锁" + elif result2 and result1: + collector.info['openai'] = "失败" + elif not result1 and result2: + collector.info['openai'] = "仅Web解锁" + elif result1 and not result2: + collector.info['openai'] = "仅APP解锁" + else: + collector.info['openai'] = "N/A" + return True def task(Collector, session, proxy): @@ -112,18 +97,15 @@ def get_openai_info(ReCleaner): :return: str: 解锁信息: [解锁、失败、N/A] """ try: - if 'OpenAI' not in ReCleaner.data: - logger.warning("采集器内无数据") + if 'openai' not in ReCleaner.data: return "N/A" else: - # logger.info("OpenAI解锁:" + str(ReCleaner.data.get('OpenAI', "N/A"))) - return ReCleaner.data.get('OpenAI', "N/A") + return ReCleaner.data.get('openai', "N/A") except Exception as e: logger.error(e) return "N/A" -button = InlineKeyboardButton("✅OpenAI", callback_data='✅OpenAI') SCRIPT = { "MYNAME": "OpenAI", "TASK": task, @@ -140,11 +122,9 @@ def __init__(self): fakecl = FakeColl() session = aiohttp.ClientSession() - await fetch_openai(fakecl, session, proxy='http://127.0.0.1:1112') + await fetch_openai(fakecl, session, proxy='http://127.0.0.1:11112') print(get_openai_info(fakecl)) await session.close() - if __name__ == "__main__": - loop = asyncio.new_event_loop() - loop.run_until_complete(demo()) + asyncio.run(demo()) diff --git a/utils/__init__.py b/utils/__init__.py index 8e6aa57f..982eb661 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,4 +1,39 @@ +import aiohttp + from utils.cron import * +from typing import Callable, Any __version__ = "3.6.7" # 项目版本号 -__all__ = ["cron_delete_message", "cron_edit_message", "message_delete_queue", "message_edit_queue", "__version__"] +__all__ = ["cron_delete_message", "cron_edit_message", "message_delete_queue", "message_edit_queue", "__version__", + "retry"] + + +def default_breakfunc(ret_val: bool) -> bool: + return True if isinstance(ret_val, bool) and ret_val else False + + +def retry(count=5, break_func: Callable[[Any], bool] = None): + """ + 重试装饰器,网络请求可能会出错,所以需要重试 + :param: count: 重试次数 + :param: break_func: 触发的回调中止函数,参数为调用函数的返回值,返回值为bool, + """ + if break_func is None: + break_func = default_breakfunc + + def wrapper(func): + async def inner(*args, **kwargs): + for _ in range(count): + try: + if asyncio.iscoroutinefunction(func): + result = await func(*args, **kwargs) + else: + result = func(*args, **kwargs) + except aiohttp.ClientError: + continue + if break_func(result): + break + + return inner + + return wrapper From f3dd5a30b395e9d5e4fe9ed803089024ab4535af Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 13 Feb 2024 21:56:09 +0800 Subject: [PATCH 19/26] :bug: 3.6.7: Fix openai.py --- addons/builtin/openai.py | 38 ++++++----- utils/__init__.py | 21 +++++- utils/collector.py | 134 +-------------------------------------- 3 files changed, 42 insertions(+), 151 deletions(-) diff --git a/addons/builtin/openai.py b/addons/builtin/openai.py index ed3ddc77..ab32f82f 100644 --- a/addons/builtin/openai.py +++ b/addons/builtin/openai.py @@ -28,7 +28,7 @@ async def fetch_openai(collector, session: aiohttp.ClientSession, proxy=None): :param proxy: :return: """ - resp1 = await session.get('https://api.openai.com/compliance/cookie_requirements', headers={ + h1 = { 'authority': 'api.openai.com', 'accept': '*/*', 'accept-language': 'zh-CN,zh;q=0.9', @@ -44,10 +44,8 @@ async def fetch_openai(collector, session: aiohttp.ClientSession, proxy=None): 'sec-fetch-site': 'same-site', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' - }, proxy=proxy, timeout=5) - if resp1.status != 200: - return True - resp2 = await session.get('https://ios.chat.openai.com/', headers={ + } + h2 = { 'authority': 'ios.chat.openai.com', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/' + 'signed-exchange;v=b3;q=0.7', @@ -62,7 +60,11 @@ async def fetch_openai(collector, session: aiohttp.ClientSession, proxy=None): 'upgrade-insecure-requests': '1', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' - }, proxy=proxy, timeout=5) + } + resp1 = await session.get('https://api.openai.com/compliance/cookie_requirements', headers=h1, + proxy=proxy, timeout=5) + resp2 = await session.get('https://ios.chat.openai.com/', headers=h2, + proxy=proxy, timeout=5) # 获取响应的文本内容 text1 = await resp1.text() text2 = await resp2.text() @@ -114,17 +116,19 @@ def get_openai_info(ReCleaner): async def demo(): - class FakeColl: - def __init__(self): - self.info = {} - self.data = self.info - - fakecl = FakeColl() - - session = aiohttp.ClientSession() - await fetch_openai(fakecl, session, proxy='http://127.0.0.1:11112') - print(get_openai_info(fakecl)) - await session.close() + # class FakeColl: + # def __init__(self): + # self.info = {} + # self.data = self.info + # + # fakecl = FakeColl() + # + # session = aiohttp.ClientSession() + # await fetch_openai(fakecl, session, proxy='http://127.0.0.1:11112') + # print(get_openai_info(fakecl)) + # await session.close() + from utils import script_demo + await script_demo(fetch_openai, proxy='http://127.0.0.1:11112') if __name__ == "__main__": asyncio.run(demo()) diff --git a/utils/__init__.py b/utils/__init__.py index 982eb661..d6d2b449 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,11 +1,11 @@ import aiohttp from utils.cron import * -from typing import Callable, Any +from typing import Callable, Any, Union, Coroutine __version__ = "3.6.7" # 项目版本号 __all__ = ["cron_delete_message", "cron_edit_message", "message_delete_queue", "message_edit_queue", "__version__", - "retry"] + "retry", "script_demo"] def default_breakfunc(ret_val: bool) -> bool: @@ -37,3 +37,20 @@ async def inner(*args, **kwargs): return inner return wrapper + + +async def script_demo(script_func: Union[Callable, Coroutine], *arg, **kwargs): + class FakeColl: + def __init__(self): + self.info = {} + self.data = self.info + + fakecl = FakeColl() + + session = aiohttp.ClientSession() + if asyncio.iscoroutine(script_func): + await script_func + else: + await script_func(fakecl, session, *arg, **kwargs) + print(fakecl.info) + await session.close() diff --git a/utils/collector.py b/utils/collector.py index dc1d183d..a362a19d 100644 --- a/utils/collector.py +++ b/utils/collector.py @@ -1,7 +1,8 @@ import asyncio import ssl import time -from typing import List +from asyncio import coroutine +from typing import List, Callable, Union, Coroutine from urllib.parse import quote import aiohttp @@ -505,136 +506,5 @@ async def start(self, host: str, port: int, proxy=None): return self.info -async def delay(session: aiohttp.ClientSession, proxyname, testurl, hostname, port, timeout): - url = 'http://{}:{}/proxies/{}/delay?timeout={}&url={}'.format(hostname, port, proxyname, timeout, testurl) - async with session.get(url) as r: - try: - if r.status == 200: - text = await r.json() - return text['delay'] - else: - logger.info(proxyname + ":" + str(await r.json()) + str(r.status)) - return -1 - except ClientConnectorError as c: - logger.warning("连接失败:", c) - return -1 - - -async def delay_providers(providername, hostname='127.0.0.1', port=11230, session: aiohttp.ClientSession = None): - healthcheckurl = 'http://{}:{}/providers/proxies/{}/healthcheck'.format(hostname, port, providername) - url = 'http://{}:{}/providers/proxies/{}/'.format(hostname, port, providername) - if session is None: - session = aiohttp.ClientSession() - try: - await session.get(healthcheckurl) - async with session.get(url) as r: - if r.status == 200: - text = await r.json() - # 拿到延迟数据 - delays = [] - node = text['proxies'] - for n in node: - s = n['history'].pop() - de = s['delay'] - delays.append(de) - await session.close() - return delays - else: - logger.warning("延迟测试出错:" + str(r.status)) - await session.close() - return 0 - except ClientConnectorError as c: - logger.warning("连接失败:", c) - await session.close() - return 0 - - -async def batch_delay(proxyname: list, session: aiohttp.ClientSession = None, - testurl=config.getGstatic(), - hostname='127.0.0.1', port=11230, timeout='5000'): - """ - 批量测试延迟,仅适用于不含providers的订阅 - :param timeout: - :param port: 外部控制器端口 - :param hostname: 主机名 - :param testurl: 测试网址 - :param session: 一个连接session - :param proxyname: 一组代理名 - :return: list: 延迟 - """ - try: - if session is None: - async with aiohttp.ClientSession() as session: - tasks = [] - for name in proxyname: - task = asyncio.create_task( - delay(session, name, testurl=testurl, hostname=hostname, port=port, timeout=timeout)) - tasks.append(task) - done = await asyncio.gather(*tasks) - return done - else: - tasks = [] - for name in proxyname: - task = asyncio.create_task( - delay(session, name, testurl=testurl, hostname=hostname, port=port, timeout=timeout)) - tasks.append(task) - done = await asyncio.gather(*tasks) - return done - except Exception as e: - logger.error(e) - return None - - -async def delay_https(session: aiohttp.ClientSession, proxy=None, testurl=config.getGstatic(), - timeout=10): - # _headers = { - # 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' - # 'Chrome/102.0.5005.63 Safari/537.36' - # } - _headers2 = {'User-Agent': 'clash'} - try: - s1 = time.time() - async with session.get(url=testurl, proxy=proxy, headers=_headers2, - timeout=timeout) as r: - if r.status == 502: - pass - # logger.error("dual stack tcp shake hands failed") - if r.status == 204 or r.status == 200: - delay1 = time.time() - s1 - # print(delay1) - return delay1 - else: - return 0 - except Exception as e: - logger.error(str(e)) - return 0 - - -async def delay_https_task(session: aiohttp.ClientSession = None, collector=None, proxy=None, times=5): - if session is None: - async with aiohttp.ClientSession() as session: - tasks = [asyncio.create_task(delay_https(session=session, proxy=proxy)) for _ in range(times)] - result = await asyncio.gather(*tasks) - sum_num = [r for r in result if r != 0] - http_delay = sum(sum_num) / len(sum_num) if len(sum_num) else 0 - http_delay = "%.0fms" % (http_delay * 1000) - # print("http平均延迟:", http_delay) - http_delay = int(http_delay[:-2]) - if collector is not None: - collector.info['HTTP(S)延迟'] = http_delay - return http_delay - else: - tasks = [asyncio.create_task(delay_https(session=session, proxy=proxy)) for _ in range(times)] - result = await asyncio.gather(*tasks) - sum_num = [r for r in result if r != 0] - http_delay = sum(sum_num) / len(sum_num) if len(sum_num) else 0 - http_delay = "%.0fms" % (http_delay * 1000) - http_delay = int(http_delay[:-2]) - # print("http平均延迟:", http_delay) - if collector is not None: - collector.info['HTTP(S)延迟'] = http_delay - return http_delay - - if __name__ == "__main__": pass From 33b747fc79ed77615a6fecab88e333bbd87da278 Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 13 Feb 2024 23:29:35 +0800 Subject: [PATCH 20/26] :bug: 3.6.7: Add openai region. --- addons/builtin/openai.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/addons/builtin/openai.py b/addons/builtin/openai.py index ab32f82f..b6cba9bf 100644 --- a/addons/builtin/openai.py +++ b/addons/builtin/openai.py @@ -61,10 +61,19 @@ async def fetch_openai(collector, session: aiohttp.ClientSession, proxy=None): 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' } + region = "" resp1 = await session.get('https://api.openai.com/compliance/cookie_requirements', headers=h1, proxy=proxy, timeout=5) resp2 = await session.get('https://ios.chat.openai.com/', headers=h2, proxy=proxy, timeout=5) + resp3 = await session.get(openaiurl2, proxy=proxy, timeout=5) + if resp3.status == 200: + text3 = await resp3.text() + index3 = text3.find("loc=") + if index3 > 0: + region = text3[index3 + 4:index3 + 6].upper() + if region: + region = f"({region})" # 获取响应的文本内容 text1 = await resp1.text() text2 = await resp2.text() @@ -75,13 +84,13 @@ async def fetch_openai(collector, session: aiohttp.ClientSession, proxy=None): result2 = re.search('VPN', text2) # 根据结果输出不同的信息 if not result2 and not result1: - collector.info['openai'] = "完全解锁" + collector.info['openai'] = f"解锁{region}" elif result2 and result1: collector.info['openai'] = "失败" elif not result1 and result2: - collector.info['openai'] = "仅Web解锁" + collector.info['openai'] = f"仅网页{region}" elif result1 and not result2: - collector.info['openai'] = "仅APP解锁" + collector.info['openai'] = f"仅APP{region}" else: collector.info['openai'] = "N/A" return True From 328ded71a4daf41278b16d1b714cc2692c3ee39a Mon Sep 17 00:00:00 2001 From: AirportR Date: Thu, 15 Feb 2024 19:58:05 +0800 Subject: [PATCH 21/26] :bug: 3.6.7: Fix subconverter config. --- resources/config.yaml.example | 9 +++++---- utils/collector.py | 29 +++++++++++++---------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/resources/config.yaml.example b/resources/config.yaml.example index 280b8b65..991cbde5 100644 --- a/resources/config.yaml.example +++ b/resources/config.yaml.example @@ -1,15 +1,15 @@ # 注意,yaml配置文件对缩进敏感,请不要强迫症删掉#号之间的空格。 admin: - 12345678 #管理员,此处写你的账户id或用户名,必须配置该项,否则程序会自动退出 -bot: - api_id: 123456 # Telegram的api_id - api_hash: ABCDEFG # Telegram的api_hash - bot_token: 123456:ABCDEFG #bot的token clash: path: './bin/FullTCore' # 代理客户端的路径,默认为 ./bin/ 下。下载地址: https://github.com/AirportR/FullTCore/releases # allow-caching: false #是否缓存测试过程产生的订阅,若否,则测试完成后会自动删除测试订阅,以免造成空间浪费。(此配置暂不生效) # core: 4 # 核心数,数量越多测试速度越快,但代价是内存会大量占用(每个核心大概10M),自己的机子多少内存自己平衡。默认值为1. # branch: origin #clash内核上游分支,仅有两个有效值: [origin, meta], meta分支支持更多协议,比如vless、tuic等,但是使用上较为不可控,默认为origin原生内核。 +bot: + api_id: 123456 # Telegram的api_id + api_hash: ABCDEFG # Telegram的api_hash + bot_token: 123456:ABCDEFG #bot的token # ====================以上为必填项,否则无法启动成功======================== # proxy: host:端口:用户名:密码 #socks5代理,用户名和密码为可选配置,已和下面的proxy单独分离,请勿混用 # strictmode: false # 严格模式,代表测试的时侯,bot内联按钮只有发起测试的主人才能按,否则所有用户用户权限都可以按。默认false,目前未适配,敬请期待。 @@ -30,6 +30,7 @@ clash: #subconverter: #订阅转换(此配置主要开发者已无心维护,能用但不稳定) # enable: true #是否启用 # host: '127.0.0.1:25500' #域名或者ip加端口 +# tls: false #是否启用了tls,决定拼接的转换地址URL前缀是http还是https,默认false # remoteconfig: "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini" #远程配置 #pingurl: http://www.gstatic.com/generate_204 #自定义延迟测试URL #netflixurl: "https://www.netflix.com/title/80113701" #自定义奈飞非自制剧检测网页,注意在更换此链接时请确保是非自制的链接。 diff --git a/utils/collector.py b/utils/collector.py index a362a19d..0102059d 100644 --- a/utils/collector.py +++ b/utils/collector.py @@ -1,8 +1,8 @@ import asyncio import ssl import time -from asyncio import coroutine -from typing import List, Callable, Union, Coroutine + +from typing import List from urllib.parse import quote import aiohttp @@ -27,7 +27,7 @@ 如果你想自己添加一个流媒体测试项,建议查看 ./resources/dos/新增流媒体测试项指南.md """ -config = cleaner.ConfigManager() +config = cleaner.config addon = cleaner.addon media_items = config.get_media_item() proxies = config.get_proxy() # 代理 @@ -205,21 +205,21 @@ def __init__(self, suburl: str, include: str = '', exclude: str = '', force_conv super().__init__() self.text = None self._headers = {'User-Agent': 'clash'} # 这个请求头是获取流量信息的关键 - self.subconverter = config.config.get('subconverter', {}) - self.cvt_enable = self.subconverter.get('enable', False) + self.subcvt_conf = config.config.get('subconverter', {}) + self.cvt_enable = self.subcvt_conf.get('enable', False) self.url = suburl self.include = include self.exclude = exclude self.codeurl = quote(suburl, encoding='utf-8') self.code_include = quote(include, encoding='utf-8') self.code_exclude = quote(exclude, encoding='utf-8') - self.cvt_host = str(self.subconverter.get('host', '127.0.0.1:25500')) + self.cvt_host = str(self.subcvt_conf.get('host', '127.0.0.1:25500')) self.cvt_scheme = self.parse_cvt_scheme() self.cvt_url = f"{self.cvt_scheme}://{self.cvt_host}/sub?target=clash&new_name=true&url={self.codeurl}" \ + f"&include={self.code_include}&exclude={self.code_exclude}" - self.sub_remote_config = self.subconverter.get('remoteconfig', '') - self.config_include = quote(self.subconverter.get('include', ''), encoding='utf-8') # 这两个 - self.config_exclude = quote(self.subconverter.get('exclude', ''), encoding='utf-8') + self.sub_remote_config = self.subcvt_conf.get('remoteconfig', '') + self.config_include = quote(self.subcvt_conf.get('include', ''), encoding='utf-8') # 这两个 + self.config_exclude = quote(self.subcvt_conf.get('exclude', ''), encoding='utf-8') # print(f"配置文件过滤,包含:{self.config_include} 排除:{self.config_exclude}") if self.config_include or self.config_exclude: self.cvt_url = f"{self.cvt_scheme}://{self.cvt_host}/sub?target=clash&new_name=true&url={self.cvt_url}" \ @@ -232,13 +232,10 @@ def __init__(self, suburl: str, include: str = '', exclude: str = '', force_conv self.cvt_url = self.url def parse_cvt_scheme(self) -> str: - temp_cvt = self.cvt_host.split(":") - cvt_scheme = 'http' - if len(temp_cvt) == 2: - hostname = temp_cvt[0] - if hostname != "127.0.0.1": - cvt_scheme = 'https' - return cvt_scheme + if not bool(self.subcvt_conf.get('tls', False)): + return "http" + else: + return "https" async def start(self, proxy=None): try: From 2e15c9dd9859ec0ea005afbff2df54bb61133b9a Mon Sep 17 00:00:00 2001 From: AirportR Date: Sun, 18 Feb 2024 10:25:19 +0800 Subject: [PATCH 22/26] :bug: 3.6.7: Fix --- utils/emoji_custom.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/emoji_custom.py b/utils/emoji_custom.py index b637c849..555ad97f 100644 --- a/utils/emoji_custom.py +++ b/utils/emoji_custom.py @@ -218,8 +218,9 @@ async def download_emoji(self, download_url: str = None, savepath='./resources/e f.write(chunk) l2 = float(length) / 1024 / 1024 l2 = round(l2, 2) + spath = str(pathlib.Path(savepath).absolute()) print(f"\r[{'='*100}] 共下载{length}B ({l2}MB)" - f"已保存到 {pathlib.Path(savepath).as_posix()}") + f"已保存到 {spath}") else: raise Exception(f"NetworkError: {resp.status}==>\t{_url}") From c8b8902ea45ec0a31d31537a13aa4cadd4ca96eb Mon Sep 17 00:00:00 2001 From: AirportR Date: Sun, 18 Feb 2024 13:56:11 +0800 Subject: [PATCH 23/26] :sparkles: 3.6.7: catch more exceptions. --- utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/__init__.py b/utils/__init__.py index d6d2b449..d567599d 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -29,7 +29,7 @@ async def inner(*args, **kwargs): result = await func(*args, **kwargs) else: result = func(*args, **kwargs) - except aiohttp.ClientError: + except (aiohttp.ClientError, asyncio.exceptions.TimeoutError, ConnectionResetError, Exception): continue if break_func(result): break From f9644087e3f05f77589ed4d949a0de3a309d1194 Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 20 Feb 2024 18:18:00 +0800 Subject: [PATCH 24/26] :bug: Fix the invite command. --- botmodule/__init__.py | 2 +- botmodule/command/authority.py | 136 ++++++++++++++++----------------- utils/bot.py | 2 +- 3 files changed, 70 insertions(+), 70 deletions(-) diff --git a/botmodule/__init__.py b/botmodule/__init__.py index 1d8e00d4..6b6f4e83 100644 --- a/botmodule/__init__.py +++ b/botmodule/__init__.py @@ -16,7 +16,7 @@ init_memory_ranking() __all__ = ['grant', 'ungrant', 'user', 'restart_or_killme', 'sub_invite', 'sub', 'new', 'remove', - 'process', 'invite', 'invite_pass2', + 'process', 'invite', 'invite_pass', 'version', 'helps', 'test_setting', 'select_page', 'get_sort_str', 'select_sort', 'home_setting', 'download_script', 'reload_addon_from_telegram', 'uninstall_script', diff --git a/botmodule/command/authority.py b/botmodule/command/authority.py index bfabbf21..bc7fa648 100644 --- a/botmodule/command/authority.py +++ b/botmodule/command/authority.py @@ -107,12 +107,12 @@ async def invite(client: Client, message): print(r) -async def get_url_from_invite(_, message2): +async def get_url_from_invite(_, message2: "Message"): ID = str(get_id(message2)) - suc_mes = success_message_list.get(ID, None) + suc_mes: "Message" = success_message_list.get(ID, None) if suc_mes is not None: # success_message_list.pop(ID, None) - if message2.id == (suc_mes.id + 1): + if message2.date and suc_mes.date and (message2.date.timestamp() - suc_mes.date.timestamp()) < 55: include_text = '' exclude_text = '' text_li = str(message2.text) @@ -128,73 +128,73 @@ async def get_url_from_invite(_, message2): await message2.reply("无效的URL") -async def invite_pass(client: Client, message: Message): - # temp_queue = asyncio.Queue(maxsize=1) - ID = str(get_id(message)) - text = str(message.text) - timeout_value = 60 - if 'testurl' in text or 'analyzeurl' in text or 'speedurl' in text: - texts = text.split(' ') - pre_key = texts[1] if len(texts) > 1 else '' - if not pre_key: - return - k = pre_key.split('_') - key2 = k[0] if k else '' - A_ID = invite_list.get(key2, '') - if key2 not in invite_list or A_ID != ID: - await message.reply("ID验证失败,请不要乱用别人的测试哦!") - return - task_type_select = k[1] if len(k) > 1 else '' - test_type_select = ['HTTP(S)延迟'] - if len(k) > 2: - if k[2] == 'default': - test_type_select += addon.global_test_item() - else: - for i in k[2:]: - if i == 'HTTP(S)延迟': - continue - test_type_select.append(i) - - if task_type_select in task_type: - - s_text = f"✅身份验证成功\n🚗任务项: {task_type_select} \n\n" \ - f"**接下来请在{timeout_value}s内发送Clash配置格式的订阅链接** <过滤器> 否则任务取消\n" - success_mes = await message.reply(s_text) - success_message_list.update({ID: success_mes}) - mes = message_list.pop(key2 + ID, None) - if mes is None: - return - bot_mes = bot_message_list.pop(key2 + ID, None) - if bot_mes: - await bot_mes.edit_text(f"✅身份验证成功\n🚗任务项: {task_type_select}\n\n⏳正在等待上传订阅链接~~~") - suburl = '' - in_text = '' - ex_text = '' - try: - async with timeout(timeout_value): - suburl, in_text, ex_text = await temp_queue.get() - except asyncio.TimeoutError: - logger.info(f"验证过期: {key2}:{ID}") - await bot_mes.edit_text("❌任务已取消\n\n原因: 接收订阅链接超时") - if suburl: - from utils.bot import bot_put - await message.reply("✨提交成功,请返回群组查看测试结果。") - await asyncio.sleep(3) - await bot_mes.delete() - test_item = test_type_select - initiator = str(message.from_user.id) if message.from_user else '' - await bot_put(client, mes, task_type_select, test_items=test_item, - include_text=in_text, exclude_text=ex_text, url=suburl, - name="邀请测试", initiator=initiator) - else: - invite_list.pop(key2, '') - else: - s_text = "❌未知任务类型,请重试" - await message.reply(s_text) - return +# async def invite_pass(client: Client, message: Message): +# # temp_queue = asyncio.Queue(maxsize=1) +# ID = str(get_id(message)) +# text = str(message.text) +# timeout_value = 60 +# if 'testurl' in text or 'analyzeurl' in text or 'speedurl' in text: +# texts = text.split(' ') +# pre_key = texts[1] if len(texts) > 1 else '' +# if not pre_key: +# return +# k = pre_key.split('_') +# key2 = k[0] if k else '' +# A_ID = invite_list.get(key2, '') +# if key2 not in invite_list or A_ID != ID: +# await message.reply("ID验证失败,请不要乱用别人的测试哦!") +# return +# task_type_select = k[1] if len(k) > 1 else '' +# test_type_select = ['HTTP(S)延迟'] +# if len(k) > 2: +# if k[2] == 'default': +# test_type_select += addon.global_test_item() +# else: +# for i in k[2:]: +# if i == 'HTTP(S)延迟': +# continue +# test_type_select.append(i) +# +# if task_type_select in task_type: +# +# s_text = f"✅身份验证成功\n🚗任务项: {task_type_select} \n\n" \ +# f"**接下来请在{timeout_value}s内发送Clash配置格式的订阅链接** <过滤器> 否则任务取消\n" +# success_mes = await message.reply(s_text) +# success_message_list.update({ID: success_mes}) +# mes = message_list.pop(key2 + ID, None) +# if mes is None: +# return +# bot_mes = bot_message_list.pop(key2 + ID, None) +# if bot_mes: +# await bot_mes.edit_text(f"✅身份验证成功\n🚗任务项: {task_type_select}\n\n⏳正在等待上传订阅链接~~~") +# suburl = '' +# in_text = '' +# ex_text = '' +# try: +# async with timeout(timeout_value): +# suburl, in_text, ex_text = await temp_queue.get() +# except asyncio.TimeoutError: +# logger.info(f"验证过期: {key2}:{ID}") +# await bot_mes.edit_text("❌任务已取消\n\n原因: 接收订阅链接超时") +# if suburl: +# from utils.bot import bot_put +# await message.reply("✨提交成功,请返回群组查看测试结果。") +# await asyncio.sleep(3) +# await bot_mes.delete() +# test_item = test_type_select +# initiator = str(message.from_user.id) if message.from_user else '' +# await bot_put(client, mes, task_type_select, test_items=test_item, +# include_text=in_text, exclude_text=ex_text, url=suburl, +# name="邀请测试", initiator=initiator) +# else: +# invite_list.pop(key2, '') +# else: +# s_text = "❌未知任务类型,请重试" +# await message.reply(s_text) +# return -async def invite_pass2(client: Client, message: Message): +async def invite_pass(client: Client, message: Message): tgargs = ArgCleaner.getarg(message.text) start_uid = str(get_id(message)) timeout_value = 60 diff --git a/utils/bot.py b/utils/bot.py index c240ae6c..1db57dd7 100644 --- a/utils/bot.py +++ b/utils/bot.py @@ -138,7 +138,7 @@ async def subinfo(client, message): @app.on_message(filters.command(["start"]), group=0) async def start(client, message): - await botmodule.invite_pass2(client, message) + await botmodule.invite_pass(client, message) @app.on_message(next_filter() & filters.private, group=-1) async def _(client: Client, message: Message): From a582c0a919115d559404ed5283f192e605563279 Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 20 Feb 2024 20:10:48 +0800 Subject: [PATCH 25/26] =?UTF-8?q?:art:=20=E7=BB=86=E8=8A=82=E4=BC=98?= =?UTF-8?q?=E5=8C=96.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/export.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utils/export.py b/utils/export.py index 0587b551..2670b438 100644 --- a/utils/export.py +++ b/utils/export.py @@ -526,6 +526,9 @@ def draw_label(self, idraw): _nodename_width = self.image['widths'][1] _info_list_width = list(self.image['widths'][2]) _key_list = self.get_key_list() + if "HTTP(S)延迟" in _key_list: + new_text = "HTTPS延迟" if self.config.getGstatic().startswith("https") else "HTTP延迟" + _key_list[_key_list.index("HTTP(S)延迟")] = new_text text_list = [('序号', 20), ('节点名称', self.get_mid(100, _nodename_width + 100, '节点名称'))] start_x = 100 + _nodename_width for i, info_width in enumerate(_info_list_width): @@ -608,7 +611,7 @@ def draw_block(self, img: Image.Image, index: int, _nodename_width, _key_list, _ width = 100 + _nodename_width for i, t1 in enumerate(_key_list): content = self.info[t1][t] - if "延迟RTT" == t1 or "HTTP(S)延迟" == t1 or t1 == "TLS RTT": + if "延迟" in t1 or "RTT" in t1: rtt = float(content[:-2]) # 使用了二分法(bisection)算法,它的时间复杂度是 O(log n)。j 这里是确定rtt比interval中的哪个值大 # bisect.bisect_right(interval, rtt) 减去1 就拿到了指定的值,最后max函数防止j为负 From bfedde143ddd1a15d21720e04fd6efd3241a38e1 Mon Sep 17 00:00:00 2001 From: AirportR Date: Tue, 20 Feb 2024 20:21:33 +0800 Subject: [PATCH 26/26] :bug: Fix openai.py --- addons/builtin/openai.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/builtin/openai.py b/addons/builtin/openai.py index b6cba9bf..fc60a217 100644 --- a/addons/builtin/openai.py +++ b/addons/builtin/openai.py @@ -7,10 +7,10 @@ try: from utils import retry except ImportError: - def retry(count=5): + def retry(): def wrapper(func): async def inner(*args, **kwargs): - await func(*args, count=count, **kwargs) + await func(*args, **kwargs) return inner