-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathddns.py
327 lines (267 loc) · 10.4 KB
/
ddns.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
import json
import requests
import time
import sys
import os
import traceback
import PyQt5.sip
#从PyQt库导入QtWidget通用窗口类,基本的窗口集在PyQt5.QtWidgets模块里.
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QWidget,QSystemTrayIcon,QAction,QMenu,qApp,QMessageBox
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import QFormLayout, QLabel, QLineEdit, QPushButton, QHBoxLayout, QTextEdit, QCheckBox
import threading
from appdata import AppDataPaths
my_ip = ""
X_AUTH_KEY = ""
ZONE_ID = ""
EMAIL = ""
DNS_RECORD_NAME = ""
WEBSITE_URL = ""
AUTO_START = False
headers = {
"X-Auth-Email": EMAIL,
"X-Auth-Key": X_AUTH_KEY,
"Content-Type": "application/json"
}
started_flag = False
stop_flag = False
paths = AppDataPaths('CloudFlareDDNS')
app_data_dir = paths.app_data_path
if os.path.exists(app_data_dir):
if os.path.isfile(app_data_dir + "\\config.ini"):
print(app_data_dir + "\\config.ini")
with open(app_data_dir + "\\config.ini", 'r') as f:
file_data = json.load(f)
X_AUTH_KEY = file_data["X_AUTH_KEY"]
ZONE_ID = file_data["ZONE_ID"]
EMAIL = file_data["EMAIL"]
DNS_RECORD_NAME = file_data["DNS_RECORD_NAME"]
WEBSITE_URL = file_data["WEBSITE_URL"]
AUTO_START = bool(file_data["AUTO_START"])
else:
os.makedirs(app_data_dir)
class update_Thread(QtCore.QThread):
update_text = QtCore.pyqtSignal(str)
update_wait_console_text = QtCore.pyqtSignal(str)
def run(self):
global X_AUTH_KEY
global ZONE_ID
global EMAIL
global headers
global started_flag
global stop_flag
global tp
global DNS_RECORD_NAME
global WEBSITE_URL
global AUTO_START
if started_flag == False:
X_AUTH_KEY = le1.text()
ZONE_ID = le2.text()
EMAIL = le3.text()
DNS_RECORD_NAME = le4.text()
WEBSITE_URL = le5.text()
json_data = {"X_AUTH_KEY": X_AUTH_KEY, "ZONE_ID": ZONE_ID, "EMAIL": EMAIL, "DNS_RECORD_NAME": DNS_RECORD_NAME, "WEBSITE_URL": WEBSITE_URL, "AUTO_START": AUTO_START}
with open(app_data_dir + "\\config.ini", 'w') as f:
json.dump(json_data, f)
headers = {
"X-Auth-Email": EMAIL,
"X-Auth-Key": X_AUTH_KEY,
"Content-Type": "application/json"
}
started_flag = True
else:
tp.showMessage("Error", "DDNS已开启", icon=3)
return
global my_ip
global w
global text_panel
tp.showMessage("CloudFlareDDNS", "服务成功启动", icon = 0)
error_flag = False
while True:
if stop_flag == True:
stop_flag = False
return
try:
new_ip = requests.get(url='http://ip.42.pl/raw').text
self.update_text.emit(str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + " " + new_ip))
except:
tp.showMessage("Error", "IP Update Error", icon=3)
tp.messageClicked.connect(lambda: w.show())
self.update_text.emit(str(time.strftime("IP UPDATE ERROR")))
error_flag = False
continue
if stop_flag == True:
stop_flag = False
return
if new_ip != my_ip or error_flag:
my_ip = new_ip
try:
response = requests.get(url="https://api.cloudflare.com/client/v4/zones/" + ZONE_ID + "/dns_records", headers=headers)
res = json.loads(response.text)
result_arr = res["result"]
site_id = ""
for result in result_arr:
if result["type"] == 'A':
if str(result["name"]) == WEBSITE_URL:
site_id = result["id"]
break
response = requests.put(url="https://api.cloudflare.com/client/v4/zones/" + ZONE_ID + "/dns_records/" + str(site_id),
headers=headers,
data='{"type":"A","name":"' + DNS_RECORD_NAME + '","content":"' + my_ip + '","ttl":1,"proxied":false}')
res = json.loads(response.text)
if res["success"]:
self.update_text.emit("UPLOAD SUCCESS")
error_flag = False
else:
self.update_text.emit("UPLOAD FAILED")
tp.showMessage("Error", "Upload Failed", icon=3)
tp.messageClicked.connect(lambda: w.show())
error_flag = True
except Exception as e:
traceback.print_exc()
self.update_text.emit("UNKNOWN EXCEPTION")
tp.showMessage("Error", "Unknown Exception", icon=3)
tp.messageClicked.connect(lambda: w.show())
error_flag = True
for i in range(0, 1800):
if stop_flag == True:
stop_flag = False
self.update_wait_console_text.emit("Stopping Success")
return
self.update_wait_console_text.emit("Thread Waiting for next update... (" + str(1800 - i) + " s)")
time.sleep(1)
tp.showMessage("CloudFlareDDNS", "服务成功终止", icon = 0)
started_flag = False
if __name__ == '__main__':
global tp
global le_console_wait
thread_update_text_qt = None
last_update_time = 'None'
last_update_ip = 'None'
last_check_time = 'None'
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
# pyqt窗口必须在QApplication方法中使用
# 每一个PyQt5应用都必须创建一个应用对象.sys.argv参数是来自命令行的参数列表.Python脚本可以从shell里运行.这是我们如何控制我们的脚本运行的一种方法.
app = QApplication(sys.argv)
# 关闭所有窗口,也不关闭应用程序
QApplication.setQuitOnLastWindowClosed(False)
from PyQt5 import QtWidgets
# QWidget窗口是PyQt5中所有用户界口对象的基本类.我们使用了QWidget默认的构造器.默认的构造器没有父类.一个没有父类的窗口被称为一个window.
w = QWidget()
# resize()方法调整了窗口的大小.被调整为250像素宽和250像素高.
w.resize(500,500)
# move()方法移动了窗口到屏幕坐标x=300, y=300的位置.
w.move(300,300)
# 在这里我们设置了窗口的标题.标题会被显示在标题栏上.
w.setWindowTitle('Cloudflare DDNS by H.Q. _ v0.1(beta)')
# show()方法将窗口显示在屏幕上.一个窗口是先在内存中被创建,然后显示在屏幕上的.
w.show()
# 在系统托盘处显示图标
tp = QSystemTrayIcon(w)
tp.setIcon(QIcon('dns.png'))
# 设置系统托盘图标的菜单
a1 = QAction('&显示(Show)',triggered = w.show)
form = QFormLayout()
lb1 = QLabel('X_AUTH_KEY')
le1 = QLineEdit(X_AUTH_KEY)
le1.setEchoMode(QLineEdit.Password)
lb2 = QLabel('ZONE_ID')
le2 = QLineEdit(ZONE_ID)
le2.setEchoMode(QLineEdit.Password)
lb3 = QLabel('Email')
le3 = QLineEdit(EMAIL)
lb4 = QLabel('DNS_RECORD_NAME')
le4 = QLineEdit(DNS_RECORD_NAME)
lb5 = QLabel('WEBSITE_URL')
le5 = QLineEdit(WEBSITE_URL)
le_console_wait = QLineEdit()
text_panel = QTextEdit()
thread_update_text_qt = update_Thread(w)
thread_update_text_qt.update_text.connect(text_panel.append)
def stop_ddns():
global started_flag
global tp
global stop_flag
started_flag = False
stop_flag = True
tp.showMessage("CloudFlareDDNS", "服务成功终止", icon = 0)
def start_ddns():
global thread_update_text_qt, text_panel, le_console_wait
thread_update_text_qt = update_Thread(w)
thread_update_text_qt.update_text.connect(text_panel.append)
thread_update_text_qt.update_wait_console_text.connect(le_console_wait.setText)
thread_update_text_qt.start()
hbox = QHBoxLayout()
button_start = QPushButton("Start DDNS")
button_stop = QPushButton("Stop DDNS")
button_start.clicked.connect(start_ddns)
button_stop.clicked.connect(stop_ddns)
hbox.addWidget(button_start)
hbox.addWidget(button_stop)
autoStart_checkbox = QCheckBox("Auto Start DDNS when program starts")
if AUTO_START:
autoStart_checkbox.setChecked(True)
start_ddns()
def auto_started_changed():
print(autoStart_checkbox.isChecked())
AUTO_START = autoStart_checkbox.isChecked()
json_data = {"X_AUTH_KEY": X_AUTH_KEY, "ZONE_ID": ZONE_ID, "EMAIL": EMAIL, "DNS_RECORD_NAME": DNS_RECORD_NAME, "WEBSITE_URL": WEBSITE_URL, "AUTO_START": AUTO_START}
with open(app_data_dir + "\\config.ini", 'w') as f:
json.dump(json_data, f)
autoStart_checkbox.stateChanged.connect(auto_started_changed)
form.addRow(lb1, le1)
form.addRow(lb2, le2)
form.addRow(lb3, le3)
form.addRow(lb4, le4)
form.addRow(lb5, le5)
form.addRow(hbox)
form.addRow(autoStart_checkbox)
form.addRow(text_panel)
form.addRow(le_console_wait)
w.setLayout(form)
def quitApp():
w.show() # w.hide() #隐藏
re = QMessageBox.question(w, "提示", "退出系统", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if re == QMessageBox.Yes:
# 关闭窗体程序
QCoreApplication.instance().quit()
# 在应用程序全部关闭后,TrayIcon其实还不会自动消失,
# 直到你的鼠标移动到上面去后,才会消失,
# 这是个问题,(如同你terminate一些带TrayIcon的应用程序时出现的状况),
# 这种问题的解决我是通过在程序退出前将其setVisible(False)来完成的。
tp.setVisible(False)
a2 = QAction('&退出(Exit)',triggered = quitApp) # 直接退出可以用qApp.quit
tpMenu = QMenu()
tpMenu.addAction(a1)
tpMenu.addAction(a2)
tp.setContextMenu(tpMenu)
# 不调用show不会显示系统托盘
tp.show()
# 信息提示
# 参数1:标题
# 参数2:内容
# 参数3:图标(0没有图标 1信息图标 2警告图标 3错误图标),0还是有一个小图标
def message():
w.show()
tp.messageClicked.connect(message)
def act(reason):
# 鼠标点击icon传递的信号会带有一个整形的值,1是表示单击右键,2是双击,3是单击左键,4是用鼠标中键点击
if reason == 2 or reason == 3:
w.show()
# print("系统托盘的图标被点击了")
tp.activated.connect(act)
tp.showMessage("CloudFlareDDNS", "程序成功启动", icon = 0)
# sys为了调用sys.exit(0)退出程序
# 最后,我们进入应用的主循环.事件处理从这里开始.主循环从窗口系统接收事件,分派它们到应用窗口.如果我们调用了exit()方法或者主窗口被销毁,则主循环结束.sys.exit()方法确保一个完整的退出.环境变量会被通知应用是如何结束的.
# exec_()方法是有一个下划线的.这是因为exec在Python中是关键字.因此,用exec_()代替.
sys.exit(app.exec_())