├── .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 | #  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 | 
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())
--------------------------------------------------------------------------------