├── .github └── workflows │ ├── dns_cf.yml │ ├── dns_pod.yml │ └── sync.yml ├── CNAME ├── README.md ├── dnscf.py ├── dnspod.py ├── index.html ├── ipTop.html ├── ipTop10.html ├── qCloud.py └── requirements.txt /.github/workflows/dns_cf.yml: -------------------------------------------------------------------------------- 1 | name: 'dns_cf_push' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 */6 * * *' 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 'Checkout' 12 | uses: actions/checkout@v3 13 | - name: 'Set up Python' 14 | uses: actions/setup-python@v4 15 | with: 16 | python-version: 3.12 17 | - name: 'Install dependencies' 18 | run: if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 19 | - name: 'run dnscf' 20 | env: 21 | CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} 22 | CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }} 23 | CF_DNS_NAME: ${{ secrets.CF_DNS_NAME }} 24 | PUSHPLUS_TOKEN: ${{ secrets.PUSHPLUS_TOKEN }} 25 | run: python dnscf.py 26 | -------------------------------------------------------------------------------- /.github/workflows/dns_pod.yml: -------------------------------------------------------------------------------- 1 | name: 'dns_pod_push' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 */6 * * *' 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 'Checkout' 12 | uses: actions/checkout@v3 13 | - name: 'Set up Python' 14 | uses: actions/setup-python@v4 15 | with: 16 | python-version: 3.12 17 | - name: 'Install dependencies' 18 | run: if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 19 | - name: 'run dnspod' 20 | env: 21 | DOMAIN: ${{ secrets.DOMAIN }} 22 | SUB_DOMAIN: ${{ secrets.SUB_DOMAIN }} 23 | SECRETID: ${{ secrets.SECRETID }} 24 | SECRETKEY: ${{ secrets.SECRETKEY }} 25 | PUSHPLUS_TOKEN: ${{ secrets.PUSHPLUS_TOKEN }} 26 | run: python dnspod.py 27 | -------------------------------------------------------------------------------- /.github/workflows/sync.yml: -------------------------------------------------------------------------------- 1 | name: Upstream Sync 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | schedule: 8 | - cron: "0 0 * * *" # every day 9 | workflow_dispatch: 10 | 11 | jobs: 12 | sync_latest_from_upstream: 13 | name: Sync latest commits from upstream repo 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event.repository.fork }} 16 | 17 | steps: 18 | # Step 1: run a standard checkout action 19 | - name: Checkout target repo 20 | uses: actions/checkout@v3 21 | 22 | # Step 2: run the sync action 23 | - name: Sync upstream changes 24 | id: sync 25 | uses: aormsby/Fork-Sync-With-Upstream-action@v3.4 26 | with: 27 | upstream_sync_repo: ZhiXuanWang/cf-speed-dns 28 | upstream_sync_branch: main 29 | target_sync_branch: main 30 | target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set 31 | 32 | # Set test_mode true to run tests instead of the true action!! 33 | test_mode: false 34 | 35 | - name: Sync check 36 | if: failure() 37 | run: | 38 | echo "[Error] 由于上游仓库的 workflow 文件变更,导致 GitHub 自动暂停了本次自动更新,你需要手动 Sync Fork 一次。" 39 | exit 1 40 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | ip.164746.xyz -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## cf-speed-dns是什么? 2 | CloudflareSpeedTest 推送「每5分钟自选优选 IP」获取Cloudflare CDN 延迟和速度最快 IP ! 3 | 4 | ## cf-speed-dns有哪些功能? 5 | * CloudflareSpeedTest优选IP,实时更新列表页面。[https://ip.164746.xyz](https://ip.164746.xyz) 6 | * CloudflareSpeedTest优选IP,Top接口(默认)[https://ip.164746.xyz/ipTop.html](https://ip.164746.xyz/ipTop.html);Top10接口[https://ip.164746.xyz/ipTop10.html](https://ip.164746.xyz/ipTop10.html)。 7 | * DNSPOD实时域名解析推送,fork 本项目。 8 | * Action配置,Actions secrets and variables 添加 DOMAIN(例如:164746.xyz),SUB_DOMAIN(例如:dns),SECRETID(xxxxx),SECRETKEY(xxxxx),PUSHPLUS_TOKEN(xxxxx)。 9 | * DNSCF实时域名解析推送,fork 本项目。 10 | * Action配置,Actions secrets and variables 添加 CF_API_TOKEN(例如:xxxxx),CF_ZONE_ID(例如:xxxxx),CF_DNS_NAME(dns.164746.xyz),PUSHPLUS_TOKEN(xxxxx)。 11 | * 接入PUSHPLUS消息通知。[https://www.pushplus.plus/push1.html](https://www.pushplus.plus/push1.html) 12 | 13 | ## 接口请求 14 | ```javascript 15 | curl 'https://ip.164746.xyz/ipTop.html' 16 | ``` 17 | ## 接口返回 18 | ```javascript 19 | 104.16.204.6,104.18.103.125 20 | ``` 21 | 22 | ## 感谢 23 | [XIU2](https://github.com/XIU2/CloudflareSpeedTest)、[ddgth](https://github.com/ddgth/cf2dns) 24 | -------------------------------------------------------------------------------- /dnscf.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import traceback 3 | import time 4 | import os 5 | import json 6 | 7 | # API 密钥 8 | CF_API_TOKEN = os.environ["CF_API_TOKEN"] 9 | CF_ZONE_ID = os.environ["CF_ZONE_ID"] 10 | CF_DNS_NAME = os.environ["CF_DNS_NAME"] 11 | 12 | # pushplus_token 13 | PUSHPLUS_TOKEN = os.environ["PUSHPLUS_TOKEN"] 14 | 15 | 16 | 17 | headers = { 18 | 'Authorization': f'Bearer {CF_API_TOKEN}', 19 | 'Content-Type': 'application/json' 20 | } 21 | 22 | def get_cf_speed_test_ip(timeout=10, max_retries=5): 23 | for attempt in range(max_retries): 24 | try: 25 | # 发送 GET 请求,设置超时 26 | response = requests.get('https://ip.164746.xyz/ipTop.html', timeout=timeout) 27 | # 检查响应状态码 28 | if response.status_code == 200: 29 | return response.text 30 | except Exception as e: 31 | traceback.print_exc() 32 | print(f"get_cf_speed_test_ip Request failed (attempt {attempt + 1}/{max_retries}): {e}") 33 | # 如果所有尝试都失败,返回 None 或者抛出异常,根据需要进行处理 34 | return None 35 | 36 | # 获取 DNS 记录 37 | def get_dns_records(name): 38 | def_info = [] 39 | url = f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records' 40 | response = requests.get(url, headers=headers) 41 | if response.status_code == 200: 42 | records = response.json()['result'] 43 | for record in records: 44 | if record['name'] == name: 45 | def_info.append(record['id']) 46 | return def_info 47 | else: 48 | print('Error fetching DNS records:', response.text) 49 | 50 | # 更新 DNS 记录 51 | def update_dns_record(record_id, name, cf_ip): 52 | url = f'https://api.cloudflare.com/client/v4/zones/{CF_ZONE_ID}/dns_records/{record_id}' 53 | data = { 54 | 'type': 'A', 55 | 'name': name, 56 | 'content': cf_ip 57 | } 58 | 59 | response = requests.put(url, headers=headers, json=data) 60 | 61 | if response.status_code == 200: 62 | print(f"cf_dns_change success: ---- Time: " + str( 63 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ---- ip:" + str(cf_ip)) 64 | return "ip:" + str(cf_ip) + "解析" + str(name) + "成功" 65 | else: 66 | traceback.print_exc() 67 | print(f"cf_dns_change ERROR: ---- Time: " + str( 68 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ---- MESSAGE: " + str(e)) 69 | return "ip:" + str(cf_ip) + "解析" + str(name) + "失败" 70 | 71 | # 消息推送 72 | def push_plus(content): 73 | url = 'http://www.pushplus.plus/send' 74 | data = { 75 | "token": PUSHPLUS_TOKEN, 76 | "title": "IP优选DNSCF推送", 77 | "content": content, 78 | "template": "markdown", 79 | "channel": "wechat" 80 | } 81 | body = json.dumps(data).encode(encoding='utf-8') 82 | headers = {'Content-Type': 'application/json'} 83 | requests.post(url, data=body, headers=headers) 84 | 85 | # 主函数 86 | def main(): 87 | # 获取最新优选IP 88 | ip_addresses_str = get_cf_speed_test_ip() 89 | ip_addresses = ip_addresses_str.split(',') 90 | dns_records = get_dns_records(CF_DNS_NAME) 91 | push_plus_content = [] 92 | # 遍历 IP 地址列表 93 | for index, ip_address in enumerate(ip_addresses): 94 | # 执行 DNS 变更 95 | dns = update_dns_record(dns_records[index], CF_DNS_NAME, ip_address) 96 | push_plus_content.append(dns) 97 | 98 | push_plus('\n'.join(push_plus_content)) 99 | 100 | if __name__ == '__main__': 101 | main() 102 | -------------------------------------------------------------------------------- /dnspod.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import time 4 | import requests 5 | from qCloud import QcloudApiv3 6 | import traceback 7 | import os 8 | import json 9 | 10 | # 域名和子域名 11 | DOMAIN = os.environ['DOMAIN'] 12 | SUB_DOMAIN = os.environ['SUB_DOMAIN'] 13 | 14 | # API 密钥 15 | SECRETID = os.environ["SECRETID"] 16 | SECRETKEY = os.environ["SECRETKEY"] 17 | 18 | # pushplus_token 19 | PUSHPLUS_TOKEN = os.environ["PUSHPLUS_TOKEN"] 20 | 21 | 22 | def get_cf_speed_test_ip(timeout=10, max_retries=5): 23 | for attempt in range(max_retries): 24 | try: 25 | # 发送 GET 请求,设置超时 26 | response = requests.get('https://ip.164746.xyz/ipTop.html', timeout=timeout) 27 | 28 | # 检查响应状态码 29 | if response.status_code == 200: 30 | return response.text 31 | except Exception as e: 32 | traceback.print_exc() 33 | print(f"get_cf_speed_test_ip Request failed (attempt {attempt + 1}/{max_retries}): {e}") 34 | # 如果所有尝试都失败,返回 None 或者抛出异常,根据需要进行处理 35 | return None 36 | 37 | 38 | def build_info(cloud): 39 | try: 40 | ret = cloud.get_record(DOMAIN, 100, SUB_DOMAIN, 'A') 41 | def_info = [] 42 | for record in ret["data"]["records"]: 43 | info = {"recordId": record["id"], "value": record["value"]} 44 | if record["line"] == "默认": 45 | def_info.append(info) 46 | print(f"build_info success: ---- Time: " + str( 47 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ---- ip:" + str(def_info)) 48 | return def_info 49 | except Exception as e: 50 | traceback.print_exc() 51 | print(f"build_info ERROR: ---- Time: " + str( 52 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ---- MESSAGE: " + str(e)) 53 | 54 | 55 | def change_dns(cloud, record_id, cf_ip): 56 | try: 57 | cloud.change_record(DOMAIN, record_id, SUB_DOMAIN, cf_ip, "A", "默认", 600) 58 | print(f"change_dns success: ---- Time: " + str( 59 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ---- ip:" + str(cf_ip)) 60 | return "ip:" + str(cf_ip) + "解析" + str(SUB_DOMAIN) + "." + str(DOMAIN) + "成功" 61 | 62 | except Exception as e: 63 | traceback.print_exc() 64 | print(f"change_dns ERROR: ---- Time: " + str( 65 | time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + " ---- MESSAGE: " + str(e)) 66 | return "ip:" + str(cf_ip) + "解析" + str(SUB_DOMAIN) + "." + str(DOMAIN) + "失败" 67 | 68 | 69 | def pushplus(content): 70 | url = 'http://www.pushplus.plus/send' 71 | data = { 72 | "token": PUSHPLUS_TOKEN, 73 | "title": "IP优选DNSPOD推送", 74 | "content": content, 75 | "template": "markdown", 76 | "channel": "wechat" 77 | } 78 | body = json.dumps(data).encode(encoding='utf-8') 79 | headers = {'Content-Type': 'application/json'} 80 | requests.post(url, data=body, headers=headers) 81 | 82 | 83 | if __name__ == '__main__': 84 | # 构造环境 85 | cloud = QcloudApiv3(SECRETID, SECRETKEY) 86 | 87 | # 获取DNS记录 88 | info = build_info(cloud) 89 | 90 | # 获取最新优选IP 91 | ip_addresses_str = get_cf_speed_test_ip() 92 | ip_addresses = ip_addresses_str.split(',') 93 | 94 | pushplus_content = [] 95 | # 遍历 IP 地址列表 96 | for index, ip_address in enumerate(ip_addresses): 97 | # 执行 DNS 变更 98 | dns = change_dns(cloud, info[index]["recordId"], ip_address) 99 | pushplus_content.append(dns) 100 | 101 | pushplus('\n'.join(pushplus_content)) 102 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |IP地址 | 65 |已发送 | 66 |已接收 | 67 |丢包率 | 68 |平均延迟 | 69 |下载速度 | 70 |测速时间 | 71 |
---|---|---|---|---|---|---|
76 | 104.18.17.254 78 | | 79 | 77 |4 | 80 |4 | 81 |0.0% | 82 |145.0 | 83 |159.97MB/s | 84 |2025-06-02 14:37:39 | 85 |
88 | 172.64.145.248 90 | | 91 | 89 |4 | 92 |4 | 93 |0.0% | 94 |141.0 | 95 |146.98MB/s | 96 |2025-06-02 14:37:39 | 97 |
100 | 101 | 104.17.110.108 102 | | 103 |4 | 104 |4 | 105 |0.0% | 106 |140.0 | 107 |101.01MB/s | 108 |2025-06-02 12:53:27 | 109 |
112 | 113 | 104.16.57.94 114 | | 115 |4 | 116 |4 | 117 |0.0% | 118 |140.0 | 119 |93.3MB/s | 120 |2025-06-02 12:53:27 | 121 |
124 | 125 | 198.41.206.218 126 | | 127 |4 | 128 |4 | 129 |0.0% | 130 |142.0 | 131 |20.36MB/s | 132 |2025-06-02 13:53:52 | 133 |
136 | 137 | 162.159.241.149 138 | | 139 |4 | 140 |4 | 141 |0.0% | 142 |144.0 | 143 |18.21MB/s | 144 |2025-06-02 13:53:52 | 145 |
148 | 149 | 104.18.111.18 150 | | 151 |4 | 152 |4 | 153 |0.0% | 154 |142.0 | 155 |18.14MB/s | 156 |2025-06-02 13:53:52 | 157 |
160 | 161 | 104.18.27.10 162 | | 163 |4 | 164 |4 | 165 |0.0% | 166 |145.0 | 167 |18.0MB/s | 168 |2025-06-02 13:08:53 | 169 |
172 | 173 | 104.21.224.101 174 | | 175 |4 | 176 |4 | 177 |0.0% | 178 |144.0 | 179 |12.2MB/s | 180 |2025-06-02 13:53:52 | 181 |
184 | 185 | 104.19.32.193 186 | | 187 |4 | 188 |4 | 189 |0.0% | 190 |138.0 | 191 |11.47MB/s | 192 |2025-06-02 14:08:45 | 193 |