标签 python 下的文章

在上一篇文章中,介绍了如何集成加强版的geodata文件到openwrt。
但是这个加强版的geodata每日更新。按之前的办法,要去github查看新版本的信息,手动更新。太麻烦了……

这里面提供一个python脚本,可以方便的自动更新Makefile文件到最新版本。

使用方法将脚本的内容保存成文件。比如up_geodata.sh文件。
然后运行脚本,指定要更新的Makefiel文件路径:

python3 up_geodata.sh -b Makefile

参数-b表示备份旧的文件,不想备份可以不加。
mac或者linux,还可以直接添加可执行权限,当成应用来运行:

chmod +x up_geodata.sh

以下是脚本内容:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import requests
import re
import argparse
import shutil
import os
from datetime import datetime

# GitHub API URL
GITHUB_API_URL = "https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases/latest"

# 需要下载的 SHA256 文件
FILES = {"geoip": "geoip.dat.sha256sum", "geosite": "geosite.dat.sha256sum"}


def get_latest_version():
    """获取 GitHub 最新 Release 版本号"""
    response = requests.get(GITHUB_API_URL)
    if response.status_code == 200:
        release_data = response.json()
        return release_data['tag_name']  # 获取最新版本号
    else:
        raise Exception("Failed to fetch release data from GitHub API")


def download_sha256sum_file(version, file_key):
    """下载 SHA256 校验文件"""
    file_name = FILES[file_key]
    url = f"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/{version}/{file_name}"
    response = requests.get(url)

    if response.status_code == 200:
        return response.text.strip()  # 直接返回文本内容
    else:
        raise Exception(
            f"Failed to download {file_name} for version {version}")


def extract_hash(file_content):
    """解析 SHA256 文件中的哈希值"""
    return file_content.split()[0]  # 取第一列的哈希值


def backup_makefile(makefile_path):
    """备份 Makefile"""
    if os.path.exists(makefile_path):
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_path = f"{makefile_path}.bak_{timestamp}"
        shutil.copy(makefile_path, backup_path)
        print(f"✅ 备份已创建: {backup_path}")
    else:
        raise FileNotFoundError(f"❌ 未找到 Makefile: {makefile_path}")


def update_makefile(makefile_path, version, geoip_hash, geosite_hash):
    """修改 Makefile,更新版本号和哈希值"""
    with open(makefile_path, "r") as file:
        makefile_content = file.read()

    # **修正版本号**
    makefile_content = re.sub(r"(GEODATA_VER:=)\d+", r"\g<1>" + version,
                              makefile_content)

    # **修正 geoip.dat 的 HASH**
    makefile_content = re.sub(
        r"(define Download/geoip\s+.*?\n\s+HASH:=)[a-f0-9]+",
        r"\g<1>" + geoip_hash,
        makefile_content,
        flags=re.DOTALL)

    # **修正 geosite.dat 的 HASH**
    makefile_content = re.sub(
        r"(define Download/geosite\s+.*?\n\s+HASH:=)[a-f0-9]+",
        r"\g<1>" + geosite_hash,
        makefile_content,
        flags=re.DOTALL)

    with open(makefile_path, "w") as file:
        file.write(makefile_content)

    print(
        f"✅ Makefile 已成功更新: 版本 {version}, GeoIP Hash: {geoip_hash}, Geosite Hash: {geosite_hash}"
    )


def main():
    parser = argparse.ArgumentParser(
        description="更新 Makefile 中的 v2ray-geodata 版本号和 HASH")
    parser.add_argument("makefile", help="需要更新的 Makefile 路径")
    parser.add_argument("-b",
                        "--backup",
                        action="store_true",
                        help="是否备份 Makefile")
    args = parser.parse_args()

    makefile_path = args.makefile

    try:
        # **只有在使用 -b 时才进行备份**
        if args.backup:
            backup_makefile(makefile_path)

        # **获取最新版本号**
        version = get_latest_version()
        print(f"📌 最新版本号: {version}")

        # **获取 SHA256 哈希值**
        geoip_hash = extract_hash(download_sha256sum_file(version, "geoip"))
        geosite_hash = extract_hash(download_sha256sum_file(
            version, "geosite"))

        print(f"📌 GeoIP Hash: {geoip_hash}")
        print(f"📌 Geosite Hash: {geosite_hash}")

        # **更新 Makefile**
        update_makefile(makefile_path, version, geoip_hash, geosite_hash)

    except Exception as e:
        print(f"❌ 发生错误: {e}")


if __name__ == "__main__":
    main()

用来生成可用在代码中的,指定长度hex字符脚本。默认为16个字条表示。

import argparse
import secrets

def generate_random_hex_string(length):
    # 生成指定长度的随机字节
    random_bytes = secrets.token_bytes(length)
    
    # 将每个字节转化为以0x开头的十六进制字符串
    hex_string = ','.join(f'0x{byte:02x}' for byte in random_bytes)
    
    return hex_string

def main():
    # 命令行参数解析
    parser = argparse.ArgumentParser(description="生成随机十六进制字节串")
    parser.add_argument('length', type=int, nargs='?', default=16, 
                        help="生成十六进制随机数的长度,默认为16")
    
    args = parser.parse_args()
    
    # 生成随机十六进制字符串
    result = generate_random_hex_string(args.length)
    
    # 输出结果
    print(result)

# 检查是否直接运行该脚本
if __name__ == '__main__':
    main()

xray是一个非常优秀的网络联通框架,很多科学爱好者都在使用~~~

xray集成了流量统计功能,但是通过api查询输出的结果为json格式,且结果单位为字节数。大概是为了方便别的程序使用,但是却不利用人眼直接查看。

为了方便查看流量统计结果,就用python写了一个脚本,格式化输出结果。
至于如何打开xray的流量统计功能,可以参考官方文档的描述,这里不做赘述。
脚本具体内容如下,可以保存成文件traffic.py,并加上可执行权限chmod +x traffic.py,就可以直接运行。
其中toolserver根据实际情况修改。

#!/usr/bin/python3

import json
import subprocess
import sys

tool = "/usr/local/bin/xray"
server = "127.0.0.1:8080"

result = {"inbound": {}, "outbound": {}, "user": {}}


def get_data():
    arg = ""
    if len(sys.argv) > 1 and sys.argv[1] == "reset":
        arg = "-reset"
    return subprocess.check_output(
        ([tool, "api", "statsquery", "--server=%s" % server, arg])).decode("utf-8")


def numfmt(num: int):
    if num >= 1024*1024*1024*1024:
        return "%.2fTB" % (num / 1024/1024/1024/1024)
    elif num >= 1024*1024*1024:
        return "%.2fGB" % (num / 1024/1024/1024)
    elif num >= 1024*1024:
        return "%.2fMB" % (num / 1024/1024)
    elif num >= 1024:
        return "%.2fKB" % (num / 1024)
    else:
        return "%.0fB" % num


def print_result(bound):
    data = result[bound]
    up = down = 0
    for key in sorted(data.keys(), reverse=True):
        if key.find("up") != -1:
            up += data[key]
        else:
            down += data[key]
        print("%-25s %9s" % (key, numfmt(data[key])))
    print("%-25s %9s" % ("SUM->up", numfmt(up)))
    print("%-25s %9s" % ("SUM->down", numfmt(down)))
    print("%-25s %9s" % ("SUM->TOTAL", numfmt(up+down)))


if __name__ == "__main__":
    for it in json.loads(get_data())["stat"]:
        key = it["name"].split(">>>")
        result[key[0]][key[1]+'->' +
                       key[3].replace("link", "")] = int(it.get("value", "0"))

    print("---------------Inbound-------------")
    print_result("inbound")
    print("---------------Inbound-------------\n")
    print("---------------Outbound-------------")
    print_result("outbound")
    print("---------------Outbound-------------\n")
    print("----------------User---------------")
    print_result("user")
    print("----------------User---------------")

原本的输出:

{
    "stat": [
        {
            "name": "outbound>>>direct>>>traffic>>>downlink",
            "value": 4257067673
        },
        {
            "name": "user>>>alundra>>>traffic>>>uplink",
            "value": 23392201
        },
        {
            "name": "user>>>alundra>>>traffic>>>downlink",
            "value": 3231432347
        },
        {
            "name": "user>>>winger>>>traffic>>>uplink",
            "value": 8298513
        },
        {
            "name": "user>>>winger>>>traffic>>>downlink",
            "value": 1025635326
        },
        {
            "name": "inbound>>>api>>>traffic>>>uplink",
            "value": 8653
        },
        {
            "name": "inbound>>>api>>>traffic>>>downlink",
            "value": 19613
        },
        {
            "name": "outbound>>>direct>>>traffic>>>uplink",
            "value": 31690208
        }
    ]
}

脚本运行的效果:

---------------Inbound-------------
api->up                      8.62KB
api->down                   19.61KB
SUM->up                      8.62KB
SUM->down                   19.61KB
SUM->TOTAL                  28.23KB
---------------Inbound-------------

---------------Outbound-------------
direct->up                  30.22MB
direct->down                 3.96GB
SUM->up                     30.22MB
SUM->down                    3.96GB
SUM->TOTAL                   3.99GB
---------------Outbound-------------

----------------User---------------
winger->up                   7.91MB
winger->down               978.12MB
alundra->up                 22.31MB
alundra->down                3.01GB
SUM->up                     30.22MB
SUM->down                    3.96GB
SUM->TOTAL                   3.99GB
----------------User---------------

配合 watch 命令,可以持续查看流经 xray 的流量增长情况。使用 reset 参数重置流量统计,即可查看每秒实时流量速度,如:

 watch ./traffic.py reset