├── __init__.py ├── .gitignore ├── setup.py ├── README.md └── arvan_dns.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .idea 3 | dist/ 4 | build/ 5 | *.egg-info 6 | Makefile 7 | domains*.txt -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='arvan_dns', 5 | version='1.0.7', 6 | description='Arvan DNS updater', 7 | long_description=open('README.md', 'r').read(), 8 | long_description_content_type='text/markdown', 9 | py_modules=['arvan_dns'], 10 | install_requires=[ 11 | 'requests', 12 | 'argparse' 13 | ], 14 | entry_points={ 15 | 'console_scripts': [ 16 | 'arvan-dns = arvan_dns:main' 17 | ] 18 | }, 19 | author='SeYeD.DeV', 20 | author_email='me@seyed.dev', 21 | url='https://github.com/seyed-dev/arvan-dns' 22 | ) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNS Manager For ArvanCloud 2 | 3 | This is a simple python script to change the ip of your domains in ArvanCloud DNS Manager. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | pip install arvan-dns 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### create a text file with your domains 14 | 15 | ```txt 16 | example.com 17 | example.ir 18 | example.net 19 | ``` 20 | 21 | ### run the command 22 | 23 | ```bash 24 | arvan-dns --email= --password=your password> --old_ip= --new_ip= --port= --domains_file= 25 | ``` 26 | 27 | ## Contributing 28 | 29 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 30 | -------------------------------------------------------------------------------- /arvan_dns.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import time 4 | import argparse 5 | 6 | BASE_URL = f'https://napi.arvancloud.ir/cdn/4.0/domains' 7 | RECORDS = 0 8 | 9 | 10 | parser = argparse.ArgumentParser() 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument('--email', required=True, help='your email address') 13 | parser.add_argument('--password', required=True, help='your password') 14 | parser.add_argument('--old_ip', required=True, help='your old ip') 15 | parser.add_argument('--new_ip', required=True, help='your new ip') 16 | parser.add_argument('--domains_file', required=True, help='file with domains') 17 | parser.add_argument('--sleep_time', required=False, default=10, help='sleep time between requests') 18 | parser.add_argument('--port', required=False, default=8080, help='sleep time between requests') 19 | 20 | args = parser.parse_args() 21 | 22 | EMAIL = args.email 23 | PASSWORD = args.password 24 | OLD_IP = args.old_ip 25 | NEW_IP = args.new_ip 26 | SLEEP_TIME = int(args.sleep_time) 27 | DOMAINS_FILE = args.domains_file 28 | PORT = args.port 29 | 30 | headers = { 31 | 'authority': 'dejban.arvancloud.ir', 32 | 'accept': 'application/json, text/plain, */*', 33 | 'accept-language': 'fa', 34 | 'content-type': 'application/json;charset=UTF-8', 35 | 'origin': 'https://accounts.arvancloud.ir', 36 | 'referer': 'https://accounts.arvancloud.ir/', 37 | 'sec-ch-ua': '"Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"', 38 | 'sec-ch-ua-mobile': '?0', 39 | 'sec-ch-ua-platform': '"macOS"', 40 | 'sec-fetch-dest': 'empty', 41 | 'sec-fetch-mode': 'cors', 42 | 'sec-fetch-site': 'same-site', 43 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36', 44 | 'x-content-type-options': 'nosniff', 45 | 'x-frame-options': 'DENY', 46 | 'x-redirect-uri': 'https://panel.arvancloud.ir' 47 | } 48 | 49 | login_url = "https://dejban.arvancloud.ir/v1/auth/login" 50 | login_data = { 51 | "email": EMAIL, 52 | "password": PASSWORD, 53 | "captcha": "v3.undefined" 54 | } 55 | response = requests.post(login_url, data=json.dumps(login_data), headers=headers) 56 | if response.status_code != 200: 57 | print(f'login failed, status_code: {response.status_code}, {response.text}') 58 | exit() 59 | response = response.json() 60 | BEARER_TOKEN = f'{response["data"]["accessToken"]}.{response["data"]["defaultAccount"]}' 61 | 62 | 63 | headers = { 64 | 'authority': 'napi.arvancloud.ir', 65 | 'accept': 'application/json, text/plain, */*', 66 | 'accept-language': 'fa', 67 | 'authorization': f'Bearer {BEARER_TOKEN}', 68 | 'content-type': 'application/json', 69 | 'origin': 'https://panel.arvancloud.ir', 70 | 'referer': 'https://panel.arvancloud.ir/', 71 | 'sec-ch-ua': '"Google Chrome";v="113", "Chromium";v="113", "Not-A.Brand";v="24"', 72 | 'sec-ch-ua-mobile': '?0', 73 | 'sec-ch-ua-platform': '"macOS"', 74 | 'sec-fetch-dest': 'empty', 75 | 'sec-fetch-mode': 'cors', 76 | 'sec-fetch-site': 'same-site', 77 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36' 78 | } 79 | 80 | 81 | with open(DOMAINS_FILE) as file: 82 | domains = [line.rstrip() for line in file] 83 | 84 | for domain_name in domains: 85 | url = f'{BASE_URL}/{domain_name}/dns-records?page=1&per_page=25' 86 | response = requests.get(url, headers=headers) 87 | if response.status_code != 200: 88 | print(f'request to get records for {domain_name} failed, status_code: {response.status_code}') 89 | continue 90 | print(f'trying to update {domain_name} dns records') 91 | for dns in response.json()['data']: 92 | try: 93 | if dns['type'] == 'a': 94 | if dns['value'][0]['ip'] == OLD_IP: 95 | data = { 96 | 'type': 'A', 97 | 'name': dns["name"], 98 | 'cloud': True, 99 | 'value': [{'ip': NEW_IP, 'port': PORT, 'weight': 100, 'country': ''}], 100 | 'upstream_https': 'http', 101 | 'ip_filter_mode': {'count': 'single', 'order': 'none', 'geo_filter': 'none'}, 102 | 'id': dns["id"], 103 | 'ttl': 120 104 | } 105 | retry = 0 106 | while retry < 3: 107 | url = f'{BASE_URL}/{domain_name}/dns-records/{dns["id"]}/' 108 | response = requests.put(url, headers=headers, data=json.dumps(data)) 109 | if response.status_code == 200: 110 | print(response.text) 111 | print(f'DNS for {domain_name} updated') 112 | RECORDS += 1 113 | break 114 | else: 115 | print(f'DNS for {domain_name} faild to update, retry {retry + 1}') 116 | print('plase wait 10 seconds') 117 | time.sleep(SLEEP_TIME) 118 | retry += 1 119 | except Exception as e: 120 | print(f'DNS for {domain_name} faild to update') 121 | print(f'Error : {e}') 122 | RECORDS += 1 123 | 124 | print(f'All done, {RECORDS} records from {len(domains)} domains updated') 125 | 126 | 127 | 128 | 129 | --------------------------------------------------------------------------------