├── .gitignore ├── run.bat ├── icon.ico ├── assets ├── file.png ├── list.png ├── logo.png ├── create.png ├── delete.png ├── github.png ├── exportpng.png └── twitter.png ├── requirements.txt ├── LICENSE ├── README.md └── CloudflareDNS.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | *.json 3 | output/ 4 | *.exe -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | call pip install -r requirements.txt 2 | python CloudflareDNS.py -------------------------------------------------------------------------------- /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/icon.ico -------------------------------------------------------------------------------- /assets/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/file.png -------------------------------------------------------------------------------- /assets/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/list.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/logo.png -------------------------------------------------------------------------------- /assets/create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/create.png -------------------------------------------------------------------------------- /assets/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/delete.png -------------------------------------------------------------------------------- /assets/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/github.png -------------------------------------------------------------------------------- /assets/exportpng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/exportpng.png -------------------------------------------------------------------------------- /assets/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImanMontajabi/CloudflareDNS/HEAD/assets/twitter.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2022.12.7 2 | charset-normalizer==3.1.0 3 | idna==3.4 4 | PyQt6==6.4.2 5 | PyQt6-Qt6==6.4.3 6 | PyQt6-sip==13.4.1 7 | requests==2.28.2 8 | urllib3==1.26.15 9 | auto-py-to-exe==2.33.0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Iman Montajabi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![icons8-orange-48](https://user-images.githubusercontent.com/52942515/227339564-45d03a94-d3e1-44ba-bc60-229d039cf4ee.png) CloudflareDNS 2 | [راهنمای‌ فارسی](https://github.com/ImanMontajabi/CloudflareDNS/discussions/3) 3 | =============== 4 | CloudflareDNS is a Python-based graphical application that allows users to easily manage and modify DNS records on the popular cloud-hosting platform, Cloudflare. Using this application, users can quickly create, list, and delete DNS records associated with their domains on Cloudflare. 5 | 6 | The application is built using the Python programming language and the Qt GUI toolkit. The CloudflareDNS application uses Cloudflare's API to perform DNS record management operations, like creating, listing, and deleting DNS records. 7 | 8 | # Screenshot 9 | ![230775628-6de0fcc1-d0fb-4de8-9620-0ee157dbbf0f](https://github.com/user-attachments/assets/1a91cdfb-fe23-4d81-80e6-0847448e3230) 10 | 11 | 12 | # Table of Contents 13 | - Installation 14 | - Usage 15 | - License 16 | # Installation 17 | To use CloudflareDNS, Python 3 [installed](https://www.python.org/downloads/) must have on your system. You will need to install the following packages as dependencies: 18 | 19 | `requests` and `PyQt6` 20 | ``` 21 | pip install -r requirements.txt --use-pep517 22 | ``` 23 | for create .exe file from this code: 24 | ``` 25 | pip install auto-py-to-exe 26 | ``` 27 | 28 | # Usage 29 | To start the application, navigate to the cloned repository's directory on your machine and run the `CloudflareDNS.py` python file: 30 | ``` 31 | python CloudflareDNS.py 32 | ``` 33 | This will launch the main window of the application, which prompts users to enter their email, API token, domain name, zone ID, and DNS record name. Once this information is entered, users can perform actions like List, Create, and Delete DNS records through the respective buttons. 34 | - Easy-to-use GUI for managing Cloudflare DNS records 35 | - Supports the ability to list, create, and delete DNS records 36 | - Can read JSON formatted file and create records accordingly 37 | - Concurrency through QThread implementation to allow for asynchronous Cloudflare API requests 38 | # License 39 | This project is distributed under the [MIT licence](https://github.com/ImanMontajabi/CloudflareDNS/blob/main/LICENSE) 40 | -------------------------------------------------------------------------------- /CloudflareDNS.py: -------------------------------------------------------------------------------- 1 | from PyQt6.QtGui import QIcon, QFont, QPixmap, QRegularExpressionValidator 2 | from PyQt6.QtCore import QSize, QObject, QThread, pyqtSignal, Qt, QRegularExpression 3 | import sys 4 | import os 5 | import re 6 | import requests 7 | import json 8 | from PyQt6.QtWidgets import ( 9 | QApplication, 10 | QMainWindow, 11 | QLabel, 12 | QPushButton, 13 | QFileDialog, 14 | QLineEdit, 15 | QPlainTextEdit 16 | ) 17 | 18 | base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__))) 19 | icon = os.path.join(base_path, './assets/logo.png') 20 | github = os.path.join(base_path, './assets/github.png') 21 | twitter = os.path.join(base_path, './assets/twitter.png') 22 | output_file = os.path.join(base_path, './assets/file.png') 23 | listpng = os.path.join(base_path, './assets/list.png') 24 | createpng = os.path.join(base_path, './assets/create.png') 25 | deletepng = os.path.join(base_path, './assets/delete.png') 26 | exportpng = os.path.join(base_path, './assets/exportpng.png') 27 | max_ips = 100 28 | 29 | class Worker(QObject): 30 | finished = pyqtSignal() 31 | progress = pyqtSignal(tuple) 32 | def list(self): 33 | with open('user_id.json', 'r') as json_file: 34 | user_data = json.load(json_file) 35 | 36 | email = user_data['email'] 37 | api_token = user_data['api_token'] 38 | zone_id = user_data['zone_id'] 39 | domain = user_data['domain'] 40 | dns_record_name = user_data['ip_dns_record'] 41 | url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records' 42 | 43 | 44 | headers = { 45 | 'X-Auth-Email': email, 46 | 'X-Auth-Key': api_token, 47 | 'Content-Type': 'application/json' 48 | } 49 | dns_records = [] 50 | page_num = 1 51 | try: 52 | while True: # loop over all pages 53 | params = {'page': page_num, 'per_page': 100} # update pagination params 54 | response = requests.request('GET', url, headers=headers, params=params) 55 | data = response.json()['result'] 56 | if not data: # no more records to fetch 57 | break 58 | for record in data: # add records to the list 59 | dns_records.append(record) 60 | page_num += 1 61 | i_num = 0 62 | for record in dns_records: 63 | subdomain = record['name'] 64 | this_domain = record['zone_name'] 65 | this_dns_record = subdomain.replace(f".{this_domain}", "") 66 | if this_dns_record == dns_record_name: 67 | content = record['content'] 68 | self.progress.emit((i_num + 1, subdomain, content, "")) 69 | i_num += 1 70 | self.finished.emit() 71 | self.finished.emit() 72 | except: 73 | self.progress.emit(("Error\nPlease check:\n- your Internet connection\n- your information",)) 74 | self.finished.emit() 75 | # export json 76 | def export_json(self): 77 | with open('user_id.json', 'r') as json_file: 78 | user_data = json.load(json_file) 79 | 80 | email = user_data['email'] 81 | api_token = user_data['api_token'] 82 | zone_id = user_data['zone_id'] 83 | domain = user_data['domain'] 84 | dns_record_name = user_data['ip_dns_record'] 85 | url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records' 86 | 87 | 88 | headers = { 89 | 'X-Auth-Email': email, 90 | 'X-Auth-Key': api_token, 91 | 'Content-Type': 'application/json' 92 | } 93 | dns_records = [] 94 | page_num = 1 95 | export_json = {} 96 | export_json["workingIPs"] = [] 97 | #make json files with IPs and <<< same Dataed >>> # TODO change the Dates 98 | export_json["startDate"] = "2023-04-04T10:41:35.5737055-07:00" 99 | export_json["endDate"] = "2023-04-04T10:41:35.5737056-07:05" 100 | try: 101 | while True: # loop over all pages 102 | params = {'page': page_num, 'per_page': 100} # update pagination params 103 | response = requests.request('GET', url, headers=headers, params=params) 104 | data = response.json()['result'] 105 | if not data: # no more records to fetch 106 | break 107 | for record in data: # add records to the list 108 | dns_records.append(record) 109 | page_num += 1 110 | i_num = 0 111 | for number, record in enumerate(dns_records): 112 | subdomain = record['name'] 113 | this_domain = record['zone_name'] 114 | this_dns_record = subdomain.replace(f".{this_domain}", "") 115 | if this_dns_record == dns_record_name: 116 | content = record['content'] 117 | #add dns record working ips to json file 118 | export_json["workingIPs"].append({"delay": number, "ip": content}) 119 | self.progress.emit((i_num + 1, subdomain, content,"✓")) 120 | i_num += 1 121 | export_json["totalFoundWorkingIPs"] = i_num 122 | export_json["totalFoundWorkingIPsCurrentRange"] = i_num 123 | 124 | with open(f"{dns_record_name}.{domain}.json", "w") as f: 125 | json.dump(export_json, f) 126 | f.close() 127 | self.finished.emit() 128 | self.finished.emit() 129 | except: 130 | self.progress.emit(("Error\nPlease check:\n- your Internet connection\n- your information",)) 131 | self.finished.emit() 132 | # craete DNS record from scan file 133 | def create(self): 134 | #for .json files 135 | def scan_to_iplist(): 136 | with open(self.json_path_create, 'r') as f: 137 | data = json.load(f) 138 | ip_list = [i['ip'] for i in data['workingIPs']] 139 | f.close() 140 | return ip_list 141 | 142 | # for .cf files 143 | def linux_scan_to_iplist(): 144 | with open(self.json_path_create, 'r') as f: 145 | data = f.readlines() 146 | ip_list = [] 147 | for item in data: # iterate over the list items 148 | result = re.search(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', item) # extract the IP using regular expression 149 | if result: # check if IP is found and add to the list 150 | ip_list.append(result.group(0)) 151 | return ip_list 152 | 153 | # for .csv files 154 | def online_scan_to_iplist(): 155 | with open(self.json_path_create, 'r') as f: 156 | data = f.readlines() 157 | ip_list = [] 158 | for item in data: # iterate over the list items 159 | result = re.search(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', item) # extract the IP using regular expression 160 | if result: # check if IP is found and add to the list 161 | ip_list.append(result.group(0)) 162 | return ip_list 163 | # open result file and save to list 164 | no_error = True 165 | try: 166 | if re.search(r'.+\.json$', self.json_path_create): 167 | iplist = scan_to_iplist() 168 | if iplist == []: 169 | self.progress.emit(('file has no valid ip',)) 170 | no_error = False 171 | else: 172 | with open('ip.txt', 'w') as f: 173 | f.write('\n'.join(iplist)) 174 | elif re.search(r'.+\.cf$', self.json_path_create): 175 | iplist = linux_scan_to_iplist() 176 | if iplist == []: 177 | self.progress.emit(('file has no valid ip',)) 178 | no_error = False 179 | else: 180 | with open('ip.txt', 'w') as f: 181 | f.write('\n'.join(iplist)) 182 | elif re.search(r'.+\.csv$', self.json_path_create): 183 | iplist = online_scan_to_iplist() 184 | if iplist == []: 185 | self.progress.emit(('file has no valid ip',)) 186 | no_error = False 187 | else: 188 | with open('ip.txt', 'w') as f: 189 | f.write('\n'.join(iplist)) 190 | except: 191 | self.progress.emit(('Error\nunsupported file',)) 192 | no_error = False 193 | 194 | def ip_list(): 195 | with open ('ip.txt', 'r') as f: 196 | myip = [line.strip() for line in f] 197 | f.close() 198 | return myip 199 | if no_error: 200 | all_ips = ip_list() 201 | len_all_ips = len(all_ips) 202 | def bestip(): 203 | filename = "best_ip.txt" 204 | topUnder100ip = [] 205 | 206 | ipn = 0 207 | while(ipn < len_all_ips): 208 | topUnder100ip.append(all_ips[ipn]) 209 | ipn += 1 210 | if ipn >= max_ips: 211 | break 212 | 213 | with open(filename, "w") as f: 214 | f.write("\n".join(topUnder100ip)) 215 | f.close() 216 | bestip() 217 | with open("best_ip.txt", "r") as ip: 218 | lines = ip.readlines() 219 | topUnder100ipList = [line.strip() for line in lines] 220 | 221 | with open('user_id.json', 'r') as json_file: 222 | user_data = json.load(json_file) 223 | email = user_data['email'] 224 | api_token = user_data['api_token'] 225 | zone_id = user_data['zone_id'] 226 | record_name = user_data["ip_dns_record"] 227 | domain = user_data["domain"] 228 | try: 229 | max_ip_user = int(user_data["max_ip"]) 230 | except: 231 | max_ip_user = len(topUnder100ipList) 232 | 233 | params_name = f'{record_name}.{domain}' 234 | url = f"https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records" 235 | headers = { 236 | "Content-Type": "application/json", 237 | "X-Auth-Email": email, 238 | "X-Auth-Key": api_token 239 | } 240 | ipn = 0 241 | try: 242 | while(ipn < len(topUnder100ipList)): 243 | data = { 244 | "type": "A", 245 | "name": params_name, 246 | "content": f"{topUnder100ipList[ipn]}", 247 | "ttl": 1, 248 | "proxied": False 249 | } 250 | if (ipn >= max_ip_user): 251 | break 252 | response = requests.post(url, headers=headers, json=data) 253 | if response.status_code == 200: 254 | subdomain = response.json()["result"]["name"] 255 | message_content = response.json()["result"]["content"] 256 | self.progress.emit((ipn + 1, subdomain, message_content," ✓ added")) 257 | ipn += 1 258 | if response.status_code == 400: 259 | subdomain = f"{record_name}.{domain}" 260 | message_content = topUnder100ipList[ipn] 261 | self.progress.emit((ipn + 1, subdomain, message_content, " ✘ exist")) 262 | ipn += 1 263 | if ipn >= max_ips: 264 | break 265 | self.finished.emit() 266 | except: 267 | self.progress.emit(("Error\nPlease check:\n- your Internet connection\n- your information",)) 268 | self.finished.emit() 269 | else: 270 | self.finished.emit() 271 | # delete all ips of sumdomain 272 | def delete(self): 273 | with open('user_id.json', 'r') as json_file: 274 | user_data = json.load(json_file) 275 | 276 | email = user_data['email'] 277 | api_token = user_data['api_token'] 278 | zone_id = user_data['zone_id'] 279 | domain = user_data['domain'] 280 | dns_record_name = user_data["ip_dns_record"] 281 | url = f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records' 282 | headers = { 283 | 'X-Auth-Email': email, 284 | 'X-Auth-Key': api_token, 285 | 'Content-Type': 'application/json' 286 | } 287 | page_num = 1 288 | i_num = 0 289 | try: 290 | while True: 291 | params = {"page": page_num,"per_page": 100} 292 | response = requests.request("GET", url, headers=headers, params=params) 293 | data = response.json()["result"] 294 | if not data: 295 | break 296 | 297 | for record in data: 298 | if record["name"] == f"{dns_record_name}.{domain}": 299 | url_del = f"{url}/{record['id']}" 300 | requests.delete(url_del, headers=headers) 301 | self.progress.emit((i_num + 1, record["name"], record["content"], " ✓ deleted")) 302 | i_num += 1 303 | page_num += 1 304 | self.finished.emit() 305 | except: 306 | self.progress.emit(("Error\nPlease check:\n- your Internet connection\n- your information",)) 307 | self.finished.emit() 308 | # Main WindowApp 309 | class CloudflareDNS(QMainWindow): 310 | def __init__(self, parent=None): 311 | super().__init__(parent) 312 | self.setupUi() 313 | # open file dialog window to select result file 314 | self.createButton.setEnabled(False) 315 | self.json_path_create = None 316 | self.browse_dialog = QFileDialog(self) 317 | self.browse_dialog.setNameFilter('SCAN files (*.json *.cf *.csv)') 318 | self.browse_dialog.setFileMode(QFileDialog.FileMode.ExistingFile) 319 | self.browse_dialog.fileSelected.connect(self.open_file) 320 | # open user_id file 321 | file_path = 'user_id.json' 322 | if os.path.exists(file_path): 323 | self.load_input_values() 324 | # setup UI of main Window 325 | def setupUi(self): 326 | self.setFixedSize(260, 405) 327 | self.move(5, 5) 328 | self.setWindowTitle("CloudflareDNS") 329 | self.setWindowIcon(QIcon(icon)) 330 | self.label_1 = QLabel(self) 331 | self.label_1.move(5, 365) 332 | image_1 = QPixmap(github) 333 | self.label_1.setPixmap(image_1) 334 | self.label_1.setOpenExternalLinks(True) 335 | self.label_1.setText(f'') 336 | self.label_2 = QLabel(self) 337 | self.label_2.move(50, 365) 338 | image_2 = QPixmap(twitter) 339 | self.label_2.setPixmap(image_2) 340 | self.label_2.setOpenExternalLinks(True) 341 | self.label_2.setText(f'') 342 | self.email() 343 | self.api_token() 344 | self.zone_id() 345 | self.domain() 346 | self.name() 347 | self.show_output() 348 | self.maxip() 349 | 350 | self.listButton = QPushButton(self) # list button 351 | self.listButton.setGeometry(5, 160, 37, 37) 352 | self.listButton.setStyleSheet("border-radius : 10px; border : 2px solid black") 353 | self.listButton.setIcon(QIcon(listpng)) 354 | self.listButton.setIconSize(QSize(45, 48)) 355 | self.listButton.clicked.connect(self.list_clicked) 356 | 357 | self.exportlistButton = QPushButton(self) # list button 358 | self.exportlistButton.setGeometry(46, 160, 37, 37) 359 | self.exportlistButton.setStyleSheet("border-radius : 10px; border : 2px solid black") 360 | self.exportlistButton.setIcon(QIcon(exportpng)) 361 | self.exportlistButton.setIconSize(QSize(37, 28)) 362 | self.exportlistButton.clicked.connect(self.exportlist_clicked) 363 | 364 | self.createButton = QPushButton(self) # create button 365 | self.createButton.setGeometry(175, 160, 37, 37) 366 | self.createButton.setIcon(QIcon(createpng)) 367 | self.createButton.setIconSize((QSize(40, 40))) 368 | self.createButton.setStyleSheet("border-radius : 10px; border : 2px solid black") 369 | self.createButton.clicked.connect(self.create_clicked) 370 | # delete button 371 | self.deleteButton = QPushButton(self) 372 | self.deleteButton.setGeometry(215, 160, 37, 37) 373 | self.deleteButton.setIcon(QIcon(deletepng)) 374 | self.deleteButton.setIconSize((QSize(43, 43))) 375 | self.deleteButton.setStyleSheet("border-radius : 10px; border : 2px solid black") 376 | self.deleteButton.clicked.connect(self.delete_clicked) 377 | #result.json button 378 | self.resultButton = QPushButton(self) 379 | self.resultButton.setGeometry(87, 160, 85, 37) 380 | self.resultButton.setStyleSheet("border-radius : 10px; border : 2px solid black") 381 | self.resultButton.setIcon(QIcon(output_file)) 382 | self.resultButton.setIconSize(QSize(70, 70)) 383 | self.resultButton.clicked.connect(self.get_file_path) 384 | self.resultButton.clicked.connect(self.refresh_buttons) 385 | 386 | 387 | def get_file_path(self): 388 | file_dialog = QFileDialog(self) 389 | file_dialog.setNameFilter('SCAN files (*.json *.cf *.csv)') 390 | file_dialog.setFileMode(QFileDialog.FileMode.ExistingFile) 391 | file_dialog.fileSelected.connect(self.open_file) 392 | file_dialog.show() 393 | 394 | def open_file(self, file_path): 395 | if file_path: 396 | self.json_path = file_path 397 | self.json_path_create = file_path 398 | self.refresh_buttons() 399 | 400 | def refresh_buttons(self): 401 | is_create_enabled = False 402 | if self.json_path_create: 403 | is_create_enabled = True 404 | self.createButton.setEnabled(is_create_enabled) 405 | # show output in a QPlainText 406 | def show_output(self): 407 | self.otext = QPlainTextEdit(self) 408 | self.otext.setGeometry(5, 240, 250, 120) 409 | self.otext.setStyleSheet("background-color: black; color: white; border-radius : 10px") 410 | self.otext.setFont(QFont("Cascadia Code", 10)) 411 | self.otext.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) 412 | self.otext.setReadOnly(True) 413 | # set user id in user_id file 414 | def email(self): 415 | self.input_text = QLineEdit(self) 416 | self.input_text.setObjectName("email") 417 | self.input_text.setGeometry(5, 10, 250, 25) 418 | self.input_text.setFont(QFont("Comic Sans MS", 10, QFont.Weight.Bold)) 419 | self.input_text.setStyleSheet("border-radius : 10px; border : 2px solid black") 420 | self.input_text.setPlaceholderText("example@mail.com") 421 | def api_token(self): 422 | self.input_text = QLineEdit(self) 423 | self.input_text.setObjectName("api_token") 424 | self.input_text.setFont(QFont("Comic Sans MS", 9, QFont.Weight.Bold)) 425 | self.input_text.setStyleSheet("border-radius : 10px; border : 2px solid black") 426 | self.input_text.setGeometry(5, 40, 250, 25) 427 | self.input_text.setPlaceholderText("Global API Key") 428 | def zone_id(self): 429 | self.input_text = QLineEdit(self) 430 | self.input_text.setStyleSheet("border-radius : 10px; border : 2px solid black") 431 | self.input_text.setFont(QFont("Comic Sans MS", 9, QFont.Weight.Bold)) 432 | self.input_text.setObjectName("zone_id") 433 | self.input_text.setGeometry(5, 70, 250, 25) 434 | self.input_text.setPlaceholderText("Zone ID") 435 | def domain(self): 436 | self.input_text = QLineEdit(self) 437 | self.input_text.setStyleSheet("border-radius : 10px; border : 2px solid black") 438 | self.input_text.setFont(QFont("Comic Sans MS", 10, QFont.Weight.Bold)) 439 | self.input_text.setObjectName("domain") 440 | self.input_text.setGeometry(5, 100, 250, 25) 441 | self.input_text.setPlaceholderText("mydomain.com") 442 | def name(self): 443 | self.input_text = QLineEdit(self) 444 | self.input_text.setStyleSheet("border-radius : 10px; border : 2px solid black") 445 | self.input_text.setFont(QFont("Comic Sans MS", 10, QFont.Weight.Bold)) 446 | self.input_text.setObjectName("name") 447 | self.input_text.setGeometry(5, 130, 250, 25) 448 | self.input_text.setPlaceholderText("DNS record name") 449 | def maxip(self): 450 | self.input_text = QLineEdit(self) 451 | self.input_text.setStyleSheet("border-radius : 10px; border : 2px solid black") 452 | reg_ex = QRegularExpression("(?!0)\\d{1,2}|100") 453 | validator = QRegularExpressionValidator(reg_ex, self.input_text) 454 | self.input_text.setValidator(validator) 455 | self.input_text.setFont(QFont("Comic Sans MS", 8, QFont.Weight.Bold)) 456 | self.input_text.setObjectName("maxip") 457 | self.input_text.setGeometry(5, 205, 90, 25) 458 | self.input_text.setPlaceholderText("1≤max ip≤100") 459 | 460 | def load_input_values(self): 461 | with open('user_id.json', 'r') as f: 462 | user_data = json.load(f) 463 | self.findChild(QLineEdit, "email").setText(user_data['email']) 464 | self.findChild(QLineEdit, "api_token").setText(user_data['api_token']) 465 | self.findChild(QLineEdit, "zone_id").setText(user_data['zone_id']) 466 | self.findChild(QLineEdit, "domain").setText(user_data['domain']) 467 | self.findChild(QLineEdit, "name").setText(user_data['ip_dns_record']) 468 | self.findChild(QLineEdit, "maxip").setText(user_data['max_ip']) 469 | self.refresh_buttons() 470 | 471 | def get_input_values(self): 472 | user_data = {} 473 | user_data['email'] = self.findChild(QLineEdit, "email").text() 474 | user_data['api_token'] = self.findChild(QLineEdit, "api_token").text() 475 | user_data['zone_id'] = self.findChild(QLineEdit, "zone_id").text() 476 | user_data['domain'] = self.findChild(QLineEdit, "domain").text() 477 | user_data['ip_dns_record'] = self.findChild(QLineEdit, "name").text() 478 | user_data['max_ip'] = self.findChild(QLineEdit, "maxip").text() 479 | return user_data 480 | def save_input_values(self): 481 | with open('user_id.json', 'w') as f: 482 | json.dump(self.get_input_values(), f) 483 | 484 | def update_text(self, data): 485 | try: 486 | row_num, ip, message = data[0], data[2], data[3] 487 | text = f"{row_num}) {ip} {message}\n" 488 | self.otext.insertPlainText(text) 489 | except: 490 | error_message = data[0] 491 | self.otext.insertPlainText(error_message) 492 | 493 | def list_clicked(self): 494 | self.save_input_values() 495 | self.thread = QThread() 496 | self.worker = Worker() 497 | self.otext.clear() 498 | self.worker.moveToThread(self.thread) 499 | self.worker.progress.connect(self.update_text) 500 | self.thread.started.connect(self.worker.list) 501 | self.worker.finished.connect(self.thread.quit) 502 | self.worker.finished.connect(self.worker.deleteLater) 503 | self.thread.finished.connect(self.thread.deleteLater) 504 | self.thread.start() 505 | self.listButton.setEnabled(False) 506 | self.thread.finished.connect(lambda:self.listButton.setEnabled(True)) 507 | self.exportlistButton.setEnabled(False) 508 | self.thread.finished.connect(lambda:self.exportlistButton.setEnabled(True)) 509 | self.createButton.setEnabled(False) 510 | self.thread.finished.connect(lambda:self.refresh_buttons()) 511 | self.deleteButton.setEnabled(False) 512 | self.thread.finished.connect(lambda:self.deleteButton.setEnabled(True)) 513 | self.resultButton.setEnabled(False) 514 | self.thread.finished.connect(lambda:self.resultButton.setEnabled(True)) 515 | 516 | def exportlist_clicked(self): 517 | self.save_input_values() 518 | self.thread = QThread() 519 | self.worker = Worker() 520 | self.otext.clear() 521 | self.worker.moveToThread(self.thread) 522 | self.worker.progress.connect(self.update_text) 523 | self.thread.started.connect(self.worker.export_json) 524 | self.worker.finished.connect(self.thread.quit) 525 | self.worker.finished.connect(self.worker.deleteLater) 526 | self.thread.finished.connect(self.thread.deleteLater) 527 | self.thread.start() 528 | self.listButton.setEnabled(False) 529 | self.thread.finished.connect(lambda:self.listButton.setEnabled(True)) 530 | self.exportlistButton.setEnabled(False) 531 | self.thread.finished.connect(lambda:self.exportlistButton.setEnabled(True)) 532 | self.createButton.setEnabled(False) 533 | self.thread.finished.connect(lambda:self.refresh_buttons()) 534 | self.deleteButton.setEnabled(False) 535 | self.thread.finished.connect(lambda:self.deleteButton.setEnabled(True)) 536 | self.resultButton.setEnabled(False) 537 | self.thread.finished.connect(lambda:self.resultButton.setEnabled(True)) 538 | 539 | def create_clicked(self): 540 | self.save_input_values() 541 | self.thread = QThread() 542 | self.worker = Worker() 543 | self.otext.clear() 544 | self.worker.json_path_create = self.json_path 545 | self.worker.moveToThread(self.thread) 546 | self.worker.progress.connect(self.update_text) 547 | self.thread.started.connect(self.worker.create) 548 | self.worker.finished.connect(self.thread.quit) 549 | self.worker.finished.connect(self.worker.deleteLater) 550 | self.thread.finished.connect(self.thread.deleteLater) 551 | self.thread.start() 552 | self.listButton.setEnabled(False) 553 | self.thread.finished.connect(lambda:self.listButton.setEnabled(True)) 554 | self.exportlistButton.setEnabled(False) 555 | self.thread.finished.connect(lambda:self.exportlistButton.setEnabled(True)) 556 | self.createButton.setEnabled(False) 557 | self.thread.finished.connect(lambda:self.createButton.setEnabled(True)) 558 | self.deleteButton.setEnabled(False) 559 | self.thread.finished.connect(lambda:self.deleteButton.setEnabled(True)) 560 | self.resultButton.setEnabled(False) 561 | self.thread.finished.connect(lambda:self.resultButton.setEnabled(True)) 562 | 563 | def delete_clicked(self): 564 | self.save_input_values() 565 | self.thread = QThread() 566 | self.worker = Worker() 567 | self.otext.clear() 568 | self.worker.moveToThread(self.thread) 569 | self.worker.progress.connect(self.update_text) 570 | self.thread.started.connect(self.worker.delete) 571 | self.worker.finished.connect(self.thread.quit) 572 | self.worker.finished.connect(self.worker.deleteLater) 573 | self.thread.finished.connect(self.thread.deleteLater) 574 | self.thread.start() 575 | self.listButton.setEnabled(False) 576 | self.thread.finished.connect(lambda:self.listButton.setEnabled(True)) 577 | self.exportlistButton.setEnabled(False) 578 | self.thread.finished.connect(lambda:self.exportlistButton.setEnabled(True)) 579 | self.createButton.setEnabled(False) 580 | self.thread.finished.connect(lambda:self.refresh_buttons()) 581 | self.deleteButton.setEnabled(False) 582 | self.thread.finished.connect(lambda:self.deleteButton.setEnabled(True)) 583 | self.resultButton.setEnabled(False) 584 | self.thread.finished.connect(lambda:self.resultButton.setEnabled(True)) 585 | 586 | app = QApplication(sys.argv) 587 | window = CloudflareDNS() 588 | window.show() 589 | sys.exit(app.exec()) --------------------------------------------------------------------------------