-
Notifications
You must be signed in to change notification settings - Fork 87
/
config_ui.py
3787 lines (3012 loc) · 163 KB
/
config_ui.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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from __future__ import annotations
import functools
import logging
import random
import shutil
import subprocess
import sys
import time
import webbrowser
from datetime import datetime
import requests
from config import (
AccountConfig,
AccountInfoConfig,
ArkLotteryConfig,
BindRoleConfig,
ComicConfig,
ComicExchangeItemConfig,
CommonConfig,
Config,
DnfHelperChronicleExchangeItemConfig,
DnfHelperInfoConfig,
ExchangeItemConfig,
FixedTeamConfig,
FunctionSwitchesConfig,
HelloVoiceInfoConfig,
LoginConfig,
MajieluoConfig,
MobileGameRoleInfoConfig,
RetryConfig,
VipMentorConfig,
XinYueAppOperationConfig,
XinYueOperationConfig,
config,
load_config,
save_config,
)
from config_cloud import config_cloud
from db import DnfHelperChronicleExchangeListDB
from first_run import is_first_run
from log import color, fileHandler, logger, new_file_handler
from notice import Notice, NoticeManager
from qt_wrapper import (
DNFRoleIdValidator,
MyComboBox,
QHLine,
QQListValidator,
QQValidator,
add_form_seperator,
add_row,
add_vbox_seperator,
create_checkbox,
create_collapsible_box_add_to_parent_layout,
create_collapsible_box_with_sub_form_layout_and_add_to_parent_layout,
create_combobox,
create_double_spin_box,
create_lineedit,
create_push_button_grid_layout,
create_pushbutton,
create_spin_box,
init_collapsible_box_size,
list_to_str,
make_scroll_layout,
show_confirm_message_box,
show_message,
str_to_list,
)
from setting import dnf_server_id_to_name, dnf_server_name_list, dnf_server_name_to_id
from update import get_update_info, try_manaual_update, update_fallback
from version import now_version, ver_time
logger.name = "config_ui"
logger.removeHandler(fileHandler)
logger.addHandler(new_file_handler())
import os.path
from io import StringIO
from traceback import print_tb
from PyQt5.QtCore import QCoreApplication, Qt, QThread, pyqtSignal
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QApplication,
QFileDialog,
QFormLayout,
QFrame,
QHBoxLayout,
QInputDialog,
QLabel,
QLineEdit,
QMessageBox,
QStyleFactory,
QTabWidget,
QTextEdit,
QVBoxLayout,
QWidget,
)
from dao import CardSecret, DnfRoleInfo
from data_struct import ConfigInterface, to_raw_type
from djc_helper import DjcHelper
from ga import GA_REPORT_TYPE_PAGE_VIEW
from game_info import get_name_2_mobile_game_info_map
from main_def import (
_show_head_line,
disable_flag_file,
get_config_backup_dir,
get_user_buy_info,
has_any_account_in_normal_run,
has_buy_auto_updater_dlc,
)
from network import process_result
from server import get_pay_server_addr
from urls import Urls
from usage_count import increase_counter
from util import (
bytes_arr_to_hex_str,
cache_name_download,
cache_name_user_buy_info,
clear_login_status,
get_next_regular_activity_desc,
get_random_face,
get_time_since_last_update,
hex_str_to_bytes_arr,
is_valid_qq,
open_with_default_app,
parse_scode,
parse_url_param,
range_from_one,
remove_suffix,
reset_cache,
run_from_src,
start_djc_helper,
sync_configs,
try_except,
use_new_pay_method,
)
# 客户端错误码
CHECK_RESULT_OK = "检查通过"
# 服务器错误码
RESULT_OK = "操作成功"
RESULT_INVALID = "卡密不存在或不匹配"
RESULT_QQ_NOT_SET = "未设置QQ"
RESULT_ALREADY_USED = "卡密已经使用过"
RESULT_ALREADY_BUY = "自动更新只需购买一次"
# 定义一些信息
pay_item_item_auto_updater = "自动更新DLC"
all_pay_item_names = [
"按月付费1个月",
"按月付费2个月",
"按月付费3个月",
"按月付费6个月",
"按月付费12个月",
pay_item_item_auto_updater,
]
item_name_to_money_map = {
pay_item_item_auto_updater: 10.24,
"按月付费1个月": 5 * 1,
"按月付费2个月": 5 * 2,
"按月付费3个月": 5 * 3,
"按月付费6个月": 5 * 6,
"按月付费12个月": 5 * 12,
}
all_pay_type_names = [
"支付宝",
"微信支付",
"QQ钱包",
]
pay_type_name_to_type = {
"支付宝": "alipay",
"QQ钱包": "qqpay",
"微信支付": "wxpay",
# "财付通": "tenpay",
}
class PayRequest(ConfigInterface):
def __init__(self):
self.card_secret = CardSecret() # 卡密信息
self.qq = "" # 使用QQ
self.game_qqs = [] # 附属游戏QQ
self.recommender_qq = "" # 推荐人QQ
class PayResponse(ConfigInterface):
def __init__(self):
self.msg = RESULT_OK
class SubmitOrderRequest(ConfigInterface):
def __init__(self):
self.qq = "" # 使用QQ
self.game_qqs = [] # 附属游戏QQ
self.recommender_qq = "" # 推荐人QQ
self.pay_type = "alipay"
self.item_name = "按月付费1个月"
class SubmitOrderResponse(ConfigInterface):
def __init__(self):
self.msg = RESULT_OK
self.order_url = ""
class BiDict:
def __init__(self, original_dict: dict):
self.key_to_val = dict({k: v for k, v in original_dict.items()})
self.val_to_key = dict({v: k for k, v in original_dict.items()})
class GetBuyInfoThread(QThread):
signal_results = pyqtSignal(str, str, str)
def __init__(self, parent, cfg: Config):
super().__init__(parent)
self.cfg = cfg
self.time_start = datetime.now()
def __del__(self):
self.exiting = True
def run(self) -> None:
self.update_progress("1/3 开始尝试更新各个账号的skey")
self.check_all_skey_and_pskey()
self.update_progress("2/3 开始尝试获取自动更新DLC的信息")
has_buy_auto_update_dlc = has_buy_auto_updater_dlc(self.cfg.get_qq_accounts())
self.update_progress("3/3 开始尝试获取按月付费的信息")
user_buy_info = get_user_buy_info(self.cfg.get_qq_accounts())
dlc_info = "注意:自动更新和按月付费是两个完全不同的东西,具体区别请看 付费指引/付费指引.docx\n"
if has_buy_auto_update_dlc:
dlc_info += (
"已购买自动更新DLC"
"\n\t请注意这里的两月是指从2.8开始累积未付费时长最多允许为两个月,是给2.8以前购买DLC的朋友的小福利"
"\n\t如果4.11以后才购买就享受不到这个的,因为购买时自2.8开始的累积未付费时长已经超过两个月"
)
else:
dlc_info += "当前所有账号均未购买自动更新DLC"
monthly_pay_info = user_buy_info.description()
logger.info(f"\n{dlc_info}\n\n{monthly_pay_info}")
self.send_results(dlc_info, monthly_pay_info)
def check_all_skey_and_pskey(self):
if not has_any_account_in_normal_run(self.cfg):
return
_show_head_line("启动时检查各账号skey/pskey/openid是否过期")
for _idx, account_config in enumerate(self.cfg.account_configs):
idx = _idx + 1
if not account_config.is_enabled():
# 未启用的账户的账户不走该流程
continue
logger.warning(
color("fg_bold_yellow") + f"------------检查第{idx}个账户({account_config.name})------------"
)
self.update_progress(
f"1/3 正在处理第{idx}/{len(self.cfg.account_configs)}个账户({account_config.name}),请耐心等候..."
)
djcHelper = DjcHelper(account_config, self.cfg.common)
djcHelper.fetch_pskey()
djcHelper.check_skey_expired()
self.update_progress(f"完成处理第{idx}个账户({account_config.name})")
def update_progress(self, progress_message):
ut = datetime.now() - self.time_start
self.signal_results.emit(f"{progress_message}(目前共耗时{ut.total_seconds():.1f}秒)", "", "")
def send_results(self, dlc_info, monthly_pay_info):
self.signal_results.emit("", dlc_info, monthly_pay_info)
class NoticeUi(QFrame):
def __init__(self, parent=None):
super().__init__()
self.setWindowTitle("历史公告")
self.resize(540, 390)
self.load_notices()
self.init_ui()
def load_notices(self):
self.current_notice_index = 0
# 当前的公告
self.notice_manager = NoticeManager(download_only_if_not_exists=True)
# 额外加载存档的公告
self.notice_manager.file_name = self.notice_manager.archived_notices_file_name
self.notice_manager.load(download_only_if_not_exists=True)
# 按照实际降序排列,先展示最近的
self.notice_manager.notices.sort(key=lambda notice: notice.send_at, reverse=True)
@property
def notices(self) -> list[Notice]:
return self.notice_manager.notices
def init_ui(self):
top_layout = QVBoxLayout()
self.label_title = QLabel("默认标题")
self.label_time = QLabel("发送时间")
self.label_content = QTextEdit("默认内容")
self.label_content.setReadOnly(True)
self.update_current_notice()
top_layout.addWidget(self.label_title, alignment=Qt.AlignHCenter)
top_layout.addWidget(self.label_time, alignment=Qt.AlignHCenter)
top_layout.addWidget(self.label_content)
layout = QHBoxLayout()
btn_previous = create_pushbutton("上一个")
combobox_notice_title = create_combobox(
self.notices[0].title,
[notice.title for notice in self.notices],
)
btn_next = create_pushbutton("下一个")
btn_previous.clicked.connect(self.show_previous_notice)
combobox_notice_title.currentTextChanged.connect(self.on_select_notice_title)
btn_next.clicked.connect(self.show_next_notice)
layout.addWidget(btn_previous)
layout.addWidget(combobox_notice_title)
layout.addWidget(btn_next)
top_layout.addLayout(layout)
self.setLayout(top_layout)
def update_current_notice(self):
notice_count = len(self.notices)
idx = self.current_notice_index
current_notice = self.notices[idx]
message = current_notice.message
if current_notice.open_url != "" and current_notice.open_url not in message:
message += "\n" + current_notice.open_url
self.label_title.setText(f"[{idx + 1}/{notice_count}] {current_notice.title}")
self.label_time.setText(current_notice.send_at)
self.label_content.setText(message)
def show_previous_notice(self, checked=False):
self.current_notice_index = (self.current_notice_index + len(self.notices) - 1) % len(self.notices)
self.update_current_notice()
def show_next_notice(self, checked=False):
self.current_notice_index = (self.current_notice_index + 1) % len(self.notices)
self.update_current_notice()
def on_select_notice_title(self, title: str):
for idx, notice in enumerate(self.notices):
if notice.title == title:
self.current_notice_index = idx
break
self.update_current_notice()
class ConfigUi(QFrame):
def __init__(self, parent=None):
super().__init__(parent)
self.resize(1080, 780)
title = f"DNF蚊子腿小助手 简易配置工具 v{now_version} {ver_time} by风之凌殇 {get_random_face()}"
time_since_last_update = get_time_since_last_update()
if time_since_last_update.days >= 14:
title = title + " 下个常规活动周期可能是 " + get_next_regular_activity_desc()
self.setWindowTitle(title)
self.setStyleSheet("font-family: Microsoft YaHei")
self.setWindowIcon(QIcon("utils/icons/config_ui.ico"))
self.setWhatsThis("简易配置工具")
self.load()
logger.info(f"配置工具启动成功,版本号为v{now_version} {ver_time}")
def load(self):
self.from_config(self.load_config())
logger.info("已读取成功,请按需调整配置,调整完记得点下保存~")
def load_old_version(self):
# 弹窗提示选择旧版本的小助手exe所在目录
msg = "打开旧版本的【DNF蚊子腿小助手.exe】所在的目录,形如【DNF蚊子腿小助手_v10.5.0_by风之凌殇】"
# show_message("操作指引", msg)
old_version_dir = QFileDialog.getExistingDirectory(
self, msg, os.path.realpath(".."), QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
)
if old_version_dir == "":
logger.info("未选择任何目录")
return
# 通过判断目录中是否存在【DNF蚊子腿小助手.exe】来判定选择的目录是否是正确的目录
djc_helper_exe = "DNF蚊子腿小助手.exe"
if not os.path.isfile(os.path.join(old_version_dir, djc_helper_exe)):
show_message(
"出错啦", f"未在选中的目录 {old_version_dir} 中发现 {djc_helper_exe} ,请重新点击按钮进行选择~"
)
return
# 将特定文件和目录复制过来覆盖新版本的目录
new_version_dir = os.getcwd()
sync_configs(old_version_dir, new_version_dir)
logger.info("继承旧版本配置完成,将重启配置工具以使改动生效")
report_click_event("load_old_version")
self.restart()
def restart_to_load(self, checked=False):
self.restart()
report_click_event("load_config")
def restart(self):
if run_from_src():
python = sys.executable
os.execl(python, python, *sys.argv)
else:
os.startfile(sys.argv[0])
# 目前打包版本这里代码里关闭进程,会报 XX模块 无法找到的错误,如 unicodedata jaraco.text pyexpat,这里让用户自己关闭
# kill_process(os.getpid())
self.setWindowTitle("!!!这是之前打开的实例,记得点击右上角的X关掉我!!!")
show_message("提示", "新的配置工具实例将启动,点击确认后之前打开的实例将最小化,请手动将关闭掉~")
self.showMinimized()
def save(self, checked=False, show_message_box=True):
cfg = self.to_config()
self.remove_dynamic_attrs(cfg)
self.save_config(cfg)
if show_message_box:
show_message(
"保存成功", "已保存成功\nconfig.toml已不再有注释信息,如有需要,可去config.example.toml查看注释"
)
report_click_event("save_config")
def open_backups(self):
config_backup_dir = get_config_backup_dir()
show_message(
"使用说明",
(
"在稍后打开的目录中可看到最近一段时间成功运行后备份的配置,目录名为备份时间,里面为对应配置\n"
"可通过左边的【继承旧版本配置】按钮,并将目录指定为你想要还原的时间点的配置的目录,点击继承即可还原配置~"
),
)
report_click_event("open_backups")
open_with_default_app(config_backup_dir)
def load_config(self) -> Config:
# load_config(local_config_path="", reset_before_load=True)
load_config(local_config_path="config.toml.local", reset_before_load=True)
return config()
def save_config(self, cfg: Config):
save_config(cfg)
def from_config(self, cfg: Config):
# 根据配置初始化ui
top_layout = QVBoxLayout()
self.create_buttons(top_layout)
self.create_tabs(cfg, top_layout)
# 设置一些关联事件
self.common.checkbox_auto_update_on_start.clicked.connect(self.on_click_auto_update)
self.on_click_auto_update(self.common.checkbox_auto_update_on_start.isChecked())
self.setLayout(top_layout)
def create_buttons(self, top_layout: QVBoxLayout):
# note: 配色可参考 https://www.computerhope.com/htmcolor.htm
# https://www.computerhope.com/jargon/w/w3c-color-names.htm
btn_load_old_version = create_pushbutton("继承旧版本配置", "Aquamarine")
btn_load = create_pushbutton("读取配置", "Aquamarine")
btn_save = create_pushbutton("保存配置", "Aquamarine")
btn_open_backups = create_pushbutton("查看配置备份", "LawnGreen")
btn_load_old_version.clicked.connect(self.load_old_version)
btn_load.clicked.connect(self.restart_to_load)
btn_save.clicked.connect(self.save)
btn_open_backups.clicked.connect(self.open_backups)
layout = QHBoxLayout()
layout.addWidget(btn_load_old_version)
layout.addWidget(btn_load)
layout.addWidget(btn_save)
layout.addWidget(btn_open_backups)
top_layout.addLayout(layout)
top_layout.addWidget(QHLine())
btn_add_account = create_pushbutton("添加账号", "Chartreuse")
btn_del_account = create_pushbutton("删除账号", "lightgreen")
btn_clear_login_status = create_pushbutton(
"清除登录状态", "DarkCyan", "登录错账户,或者想要登录其他账户时,点击这个即可清除登录状态"
)
btn_join_group = create_pushbutton("加群反馈问题/交流", "Orange")
btn_add_telegram = create_pushbutton("加入Telegram群", "LightBlue")
btn_add_account.clicked.connect(self.add_account)
btn_del_account.clicked.connect(self.del_account)
btn_clear_login_status.clicked.connect(self.clear_login_status)
btn_join_group.clicked.connect(self.join_group)
btn_add_telegram.clicked.connect(self.join_telegram)
layout = QHBoxLayout()
layout.addWidget(btn_add_account)
layout.addWidget(btn_del_account)
layout.addWidget(btn_clear_login_status)
layout.addWidget(btn_join_group)
layout.addWidget(btn_add_telegram)
top_layout.addLayout(layout)
top_layout.addWidget(QHLine())
btn_open_pay_guide = create_pushbutton("查看付费指引", "SpringGreen")
btn_open_usage_guide = create_pushbutton("查看使用教程(文字版)", "SpringGreen")
btn_open_usage_video = create_pushbutton("查看使用教程(视频版)", "SpringGreen")
btn_show_notice = create_pushbutton("查看公告", "Lime")
btn_check_update = create_pushbutton("检查更新", "PaleGreen")
btn_open_pay_guide.clicked.connect(self.open_pay_guide)
btn_open_usage_guide.clicked.connect(self.open_usage_guide)
btn_open_usage_video.clicked.connect(self.open_usage_video)
btn_show_notice.clicked.connect(self.show_notice)
btn_check_update.clicked.connect(self.check_update)
layout = QHBoxLayout()
layout.addWidget(btn_open_pay_guide)
layout.addWidget(btn_open_usage_guide)
layout.addWidget(btn_open_usage_video)
layout.addWidget(btn_show_notice)
layout.addWidget(btn_check_update)
top_layout.addLayout(layout)
top_layout.addWidget(QHLine())
self.btn_run_djc_helper = create_pushbutton("保存配置,然后运行小助手并退出配置工具", "cyan")
self.btn_run_djc_helper.clicked.connect(self.run_djc_helper)
top_layout.addWidget(self.btn_run_djc_helper)
top_layout.addWidget(QHLine())
def open_pay_guide(self):
open_with_default_app("付费指引/付费指引.url")
report_click_event("open_pay_guide")
def open_usage_guide(self):
open_with_default_app("使用教程/使用文档.url")
report_click_event("open_usage_guide")
def open_usage_video(self):
open_with_default_app("使用教程/视频教程_合集.url")
report_click_event("open_usage_video")
def open_autojs(self):
webbrowser.open("https://github.com/fzls/autojs")
report_click_event("open_autojs")
def support(self, checked=False):
show_message(get_random_face(), "纳尼,真的要打钱吗?还有这种好事,搓手手0-0")
self.popen(os.path.realpath("付费指引/支持一下.png"))
report_click_event("support")
def show_notice(self):
if hasattr(self, "notice"):
self.notice.close() # type: ignore
self.notice = NoticeUi(self)
self.notice.show()
def check_update(self, checked=False):
cfg = self.to_config().common
try:
ui = get_update_info(cfg)
if not try_manaual_update(ui):
show_message("无需更新", "当前已经是最新版本~")
except Exception:
update_fallback(cfg)
report_click_event("check_update")
def on_click_auto_update(self, checked=False):
if checked:
self.btn_run_djc_helper.setText("保存配置,然后运行小助手并退出配置工具")
else:
self.btn_run_djc_helper.setText("保存配置,然后运行小助手")
def run_djc_helper(self, checked=False):
logger.info("运行小助手前自动保存配置")
self.save(show_message_box=False)
exe_path = self.get_djc_helper_path()
start_djc_helper(exe_path)
if self.common.checkbox_auto_update_on_start.isChecked():
logger.info("当前已启用自动更新功能,为确保自动更新时配置工具不被占用,将退出配置工具")
QCoreApplication.exit()
report_click_event("run_djc_helper")
def get_djc_helper_path(self):
exe_path = "DNF蚊子腿小助手.exe"
if run_from_src():
exe_path = "main.py"
return os.path.realpath(exe_path)
def popen(self, args, cwd="."):
if type(args) is list:
args = [str(arg) for arg in args]
subprocess.Popen(
args,
cwd=cwd,
shell=True,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
def clear_login_status(self, checked=False):
clear_login_status()
show_message(
"清除完毕",
(
"登录状态已经清除完毕,可使用新账号重新运行~\n"
"\n"
"友情提示:清除登录状态是否是为了登录另外一个qq来领取奖励?\n"
"如果是这样,推荐使用小助手已有的多账号配置功能,可实现领取多个账号的需求~\n"
"具体配置方法请点击【查看使用教程 文字版】,查看其中的【添加多个账号】章节,按步骤操作即可\n"
),
)
report_click_event("clear_login_status")
def join_group(self, checked=False):
# note: 如果群 791343073 满了,到 https://qun.qq.com/join.html 获取新群的加群链接 @2021-02-13 01:41:03
webbrowser.open("https://qm.qq.com/cgi-bin/qm/qr?k=w-U4SsRhWKGHtiMprzqa6plhbvCSALA2&jump_from=webapi")
for suffix in [
"png",
"jpg",
]:
img_name = f"DNF蚊子腿小助手交流群群二维码.{suffix}"
if not os.path.isfile(img_name):
continue
self.popen(img_name)
report_click_event("join_group")
def join_telegram(self, checked=False):
webbrowser.open("https://t.me/djc_helper")
report_click_event("join_telegram")
def add_account(self, checked=False):
account_name, ok = QInputDialog.getText(
self, "添加账号", "要添加的账号名称", QLineEdit.Normal, f"默认账号名-{len(self.accounts) + 1}"
)
if ok:
logger.info(f"尝试添加账号 {account_name} ...")
if account_name == "":
show_message("添加失败", "未填写账号名称,请重新操作~")
return
for account in self.accounts:
if account.lineedit_name.text() == account_name:
show_message("添加失败", f"已存在名称为 {account_name} 的账号,请重新操作~")
return
account_config = AccountConfig()
# 调用下这个函数,确保读取配置时的回调的一些参数能够生成,避免刚创建账号时执行一些操作会报错
account_config.auto_update_config({})
account_config.name = account_name
account_ui = AccountConfigUi(account_config, self.to_config().common)
self.add_account_tab(account_ui)
self.tabs.setCurrentWidget(account_ui)
show_message("添加成功", "请继续进行其他操作~ 全部操作完成后记得保存~")
report_click_event("add_account")
def del_account(self, checked=False):
user_names = []
for account in self.accounts:
user_names.append(account.lineedit_name.text())
account_name, ok = QInputDialog().getItem(self, "删除账号", "要删除的账号名称", user_names, 0, False)
if ok:
logger.info(f"尝试删除账号 {account_name} ...")
account_to_delete = None
for account in self.accounts:
if account.lineedit_name.text() == account_name:
account_to_delete = account
break
if account_to_delete is None:
show_message("删除失败", f"未找到名称为 {account_name} 的账号,请重新操作~")
return
self.accounts.remove(account_to_delete)
self.tabs.removeTab(self.tabs.indexOf(account_to_delete))
show_message("删除成功", "请继续进行其他操作~ 全部操作完成后记得保存~")
report_click_event("del_account")
def create_tabs(self, cfg: Config, top_layout: QVBoxLayout):
self.tabs = QTabWidget()
self.create_userinfo_tab(cfg)
self.create_others_tab(cfg)
self.create_majieluo_tab(cfg)
self.create_mo_jie_ren_tab(cfg)
self.create_common_tab(cfg)
self.create_account_tabs(cfg)
# 设置默认页
self.tabs.setCurrentWidget(self.common)
if len(self.accounts) != 0:
# 如果有账号信息,则默认启动时聚焦首个账号配置
self.tabs.setCurrentWidget(self.accounts[0])
top_layout.addWidget(self.tabs)
def create_userinfo_tab(self, cfg: Config):
tab = QFrame()
tab.setStyleSheet("font-size: 18px; font-weight: bold;")
top_layout = QVBoxLayout()
top_layout.setAlignment(Qt.AlignCenter)
remote_config = config_cloud()
# 一些共用的字段描述和提示,避免卡密和直接购买的地方各写一遍,导致难以维护
label_name_qq = "主QQ"
placeholder_text_qq = "形如 1234567"
label_name_game_qqs = "其他要使用的QQ(新增)"
placeholder_text_game_qqs = "最多5个,使用英文逗号分隔,形如 123,456,789,12,13"
label_name_recommender_qq = "推荐人QQ(若有可填写)"
placeholder_text_recommender_qq = "完成首次购买按月付费时,推荐人将会获得一个月的小助手使用时长作为奖励"
# -------------- 区域:购买卡密 --------------
self.collapsible_box_buy_card_secret = create_collapsible_box_add_to_parent_layout(
"购买卡密(点击展开)(不会操作或无法支付可点击左上方的【查看付费指引】按钮)",
top_layout,
title_backgroup_color="Chartreuse",
)
hbox_layout = QHBoxLayout()
self.collapsible_box_buy_card_secret.setContentLayout(hbox_layout)
btn_buy_auto_updater_dlc = create_pushbutton(
"购买自动更新DLC的卡密",
"DeepSkyBlue",
"10.24元,一次性付费,永久激活自动更新功能,需去网盘或群文件下载auto_updater.exe放到utils目录,详情可见付费指引/付费指引.docx",
)
btn_pay_by_month = create_pushbutton(
"购买按月付费的卡密",
"DeepSkyBlue",
"5元/月(31天),付费生效期间可以激活2020.2.6及之后加入的短期活动,可从账号概览区域看到付费情况,详情可见付费指引/付费指引.docx",
)
btn_buy_auto_updater_dlc.clicked.connect(self.buy_auto_updater_dlc)
btn_pay_by_month.clicked.connect(self.pay_by_month)
hbox_layout.addWidget(btn_buy_auto_updater_dlc)
hbox_layout.addWidget(btn_pay_by_month)
# -------------- 区域:使用卡密 --------------
self.collapsible_box_use_card_secret = create_collapsible_box_add_to_parent_layout(
"使用卡密(点击展开)", top_layout, title_backgroup_color="MediumSpringGreen"
)
vbox_layout = QVBoxLayout()
self.collapsible_box_use_card_secret.setContentLayout(vbox_layout)
form_layout = QFormLayout()
vbox_layout.addLayout(form_layout)
self.lineedit_card = create_lineedit(
"", placeholder_text="填入在卡密网站付款后得到的卡号,形如 auto_update-20210313133230-00001"
)
form_layout.addRow("卡号", self.lineedit_card)
self.lineedit_secret = create_lineedit(
"", placeholder_text="填入在卡密网站付款后得到的密码,形如 BF8h0y1Zcb8ukY6rsn5YFhkh0Nbe9hit"
)
form_layout.addRow("卡密", self.lineedit_secret)
self.lineedit_qq = create_lineedit("", placeholder_text=placeholder_text_qq)
self.lineedit_qq.setValidator(QQValidator())
form_layout.addRow(label_name_qq, self.lineedit_qq)
# 如果首个账号设置了qq,则直接填入作为主QQ默认值,简化操作
if len(cfg.account_configs) != 0:
account_cfg = cfg.account_configs[0]
if account_cfg.account_info.has_set_account():
self.lineedit_qq.setText(cfg.account_configs[0].account_info.account)
self.lineedit_game_qqs = create_lineedit("", placeholder_text=placeholder_text_game_qqs)
self.lineedit_game_qqs.setValidator(QQListValidator())
form_layout.addRow(label_name_game_qqs, self.lineedit_game_qqs)
if remote_config.enable_recommend_reward:
self.lineedit_recommender_qq = create_lineedit("", placeholder_text=placeholder_text_recommender_qq)
self.lineedit_recommender_qq.setValidator(QQValidator())
form_layout.addRow(label_name_recommender_qq, self.lineedit_recommender_qq)
btn_pay_by_card_and_secret = create_pushbutton("使用卡密购买对应服务(二十分钟内生效)", "MediumSpringGreen")
vbox_layout.addWidget(btn_pay_by_card_and_secret)
btn_pay_by_card_and_secret.clicked.connect(self.pay_by_card_and_secret)
# -------------- 区域:直接购买 --------------
# 显示新版的付费界面
self.collapsible_box_pay_directly = create_collapsible_box_add_to_parent_layout(
"购买付费内容(点击展开)(不会操作或无法支付可点击左上方的【查看付费指引】按钮)",
top_layout,
title_backgroup_color="LightCyan",
)
vbox_layout = QVBoxLayout()
self.collapsible_box_pay_directly.setContentLayout(vbox_layout)
form_layout = QFormLayout()
vbox_layout.addLayout(form_layout)
self.lineedit_pay_directly_qq = create_lineedit("", placeholder_text=placeholder_text_qq)
self.lineedit_pay_directly_qq.setValidator(QQValidator())
form_layout.addRow(label_name_qq, self.lineedit_pay_directly_qq)
# 如果首个账号设置了qq,则直接填入作为主QQ默认值,简化操作
if len(cfg.account_configs) != 0:
account_cfg = cfg.account_configs[0]
if account_cfg.account_info.has_set_account():
self.lineedit_pay_directly_qq.setText(cfg.account_configs[0].account_info.account)
self.lineedit_pay_directly_game_qqs = create_lineedit("", placeholder_text=placeholder_text_game_qqs)
self.lineedit_pay_directly_game_qqs.setValidator(QQListValidator())
form_layout.addRow(label_name_game_qqs, self.lineedit_pay_directly_game_qqs)
if remote_config.enable_recommend_reward:
self.lineedit_pay_directly_recommender_qq = create_lineedit(
"", placeholder_text=placeholder_text_recommender_qq
)
self.lineedit_pay_directly_recommender_qq.setValidator(QQValidator())
form_layout.addRow(label_name_recommender_qq, self.lineedit_pay_directly_recommender_qq)
form_layout.addWidget(QHLine())
self.push_button_grid_layout_item_name = create_push_button_grid_layout(all_pay_item_names, "Cyan")
form_layout.addRow("付费内容", self.push_button_grid_layout_item_name)
form_layout.addWidget(QHLine())
self.push_button_grid_layout_pay_type_name = create_push_button_grid_layout(all_pay_type_names, "Cyan")
form_layout.addRow("付款方式", self.push_button_grid_layout_pay_type_name)
form_layout.addWidget(QHLine())
btn_pay_directly = create_pushbutton(
"购买对应服务(点击后会跳转到付费页面,扫码支付即可,二十分钟内生效)", "SpringGreen"
)
vbox_layout.addWidget(btn_pay_directly)
btn_pay_directly.clicked.connect(self.pay_directly)
# -------------- 区域:切换卡密/直接购买界面 --------------
self.btn_toggle_card_secret = create_pushbutton("显示原来的卡密支付界面")
self.btn_toggle_card_secret.clicked.connect(self.toggle_card_secret)
top_layout.addWidget(self.btn_toggle_card_secret)
self.btn_alipay_redpacket = create_pushbutton("领取支付宝小红包(若使用支付宝支付可以先扫码领一下)", "Lime")
self.btn_alipay_redpacket.clicked.connect(self.alipay_redpacket)
top_layout.addWidget(self.btn_alipay_redpacket)
top_layout.addWidget(QHLine())
# -------------- 区域:查询信息 --------------
add_vbox_seperator(top_layout, "查询信息")
vbox_layout = QVBoxLayout()
top_layout.addLayout(vbox_layout)
# 显示付费相关内容
self.btn_show_buy_info = create_pushbutton(
"显示付费相关信息(点击后将登录所有账户,可能需要较长时间,请耐心等候)"
)
self.btn_show_buy_info.clicked.connect(self.show_buy_info)
vbox_layout.addWidget(self.btn_show_buy_info)
self.label_auto_udpate_info = QLabel("点击登录按钮后可显示是否购买自动更新DLC")
self.label_auto_udpate_info.setVisible(False)
self.label_auto_udpate_info.setStyleSheet("color : DarkSlateGray; ")
vbox_layout.addWidget(self.label_auto_udpate_info)
self.label_monthly_pay_info = QLabel("点击登录按钮后可显示按月付费信息")
self.label_monthly_pay_info.setVisible(False)
self.label_monthly_pay_info.setStyleSheet("color : DarkCyan; ")
vbox_layout.addWidget(self.label_monthly_pay_info)
# -------------- 区域代码结束 --------------
tab.setLayout(make_scroll_layout(top_layout))
self.tabs.addTab(tab, "付费相关")
init_collapsible_box_size(self)
def is_card_secret_hidden(self) -> bool:
return self.collapsible_box_buy_card_secret.isHidden()
def hide_card_secret(self):
self.collapsible_box_buy_card_secret.setVisible(False)
self.collapsible_box_use_card_secret.setVisible(False)
self.collapsible_box_pay_directly.setVisible(True)
def show_card_secret(self):
self.collapsible_box_buy_card_secret.setVisible(True)
self.collapsible_box_use_card_secret.setVisible(True)
self.collapsible_box_pay_directly.setVisible(False)
def buy_auto_updater_dlc(self, checked=False):
if not self.confirm_buy_auto_updater():
return
if not self.check_pay_server():
return
cfg = config_cloud()
webbrowser.open(cfg.auto_updater_dlc_purchase_url)
increase_counter(ga_category="open_pay_webpage", name="auto_updater_dlc")
def confirm_buy_auto_updater(self) -> bool:
total_confirm_time = 3
for show_index in range_from_one(total_confirm_time):
ret = show_confirm_message_box(
"友情提示",
(
f"[{show_index}/{total_confirm_time}] 重要的事情说{total_confirm_time}遍\n"
"\n"
f"自动更新DLC的唯一作用仅仅是【自动更新】,不会给你带来付费活动的使用资格的哦,请确认你想要购买的是这个功能后再点击【确认】按钮进行购买-。-"
),
)
if ret == QMessageBox.Cancel:
logger.info("取消购买")
return False
message_box = QMessageBox()
message_box.setWindowTitle("友情提示")
message_box.setText("自动更新DLC只需购买一次,请确认从未购买过后再点击【确认】按钮进行购买")
message_box.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
ret = message_box.exec_()
if ret == QMessageBox.Cancel:
logger.info("取消购买")
return False
return True
def pay_by_month(self, checked=False):
if not self.check_pay_server():
return
cfg = config_cloud()
webbrowser.open(cfg.pay_by_month_purchase_url)
increase_counter(ga_category="open_pay_webpage", name="pay_buy_month")
def pay_by_card_and_secret(self, checked=False):