ErisPulse 1.0.12__py3-none-any.whl → 1.0.14.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ErisPulse/__init__.py +146 -139
- ErisPulse/__main__.py +694 -931
- ErisPulse/{envManager.py → db.py} +227 -227
- ErisPulse/logger.py +123 -48
- ErisPulse/raiserr.py +44 -0
- ErisPulse/util.py +1 -3
- {erispulse-1.0.12.dist-info → erispulse-1.0.14.dev1.dist-info}/METADATA +73 -96
- erispulse-1.0.14.dev1.dist-info/RECORD +11 -0
- {erispulse-1.0.12.dist-info → erispulse-1.0.14.dev1.dist-info}/WHEEL +1 -1
- ErisPulse/errors.py +0 -16
- erispulse-1.0.12.dist-info/RECORD +0 -11
- {erispulse-1.0.12.dist-info → erispulse-1.0.14.dev1.dist-info}/entry_points.txt +0 -0
- {erispulse-1.0.12.dist-info → erispulse-1.0.14.dev1.dist-info}/top_level.txt +0 -0
ErisPulse/__main__.py
CHANGED
|
@@ -1,931 +1,694 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
import shutil
|
|
5
|
-
import aiohttp
|
|
6
|
-
import zipfile
|
|
7
|
-
import fnmatch
|
|
8
|
-
import asyncio
|
|
9
|
-
import subprocess
|
|
10
|
-
import json
|
|
11
|
-
from
|
|
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
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
def
|
|
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
|
-
env.
|
|
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
|
-
if
|
|
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
|
-
f"
|
|
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
|
-
module_name
|
|
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
|
-
elif dep in all_modules:
|
|
696
|
-
# 单个字符串依赖
|
|
697
|
-
available_optional_deps.append(dep)
|
|
698
|
-
else:
|
|
699
|
-
missing_optional_deps.append(dep)
|
|
700
|
-
|
|
701
|
-
optional_dependencies = (
|
|
702
|
-
f"[green]可用: {', '.join(available_optional_deps)}[/green]\n"
|
|
703
|
-
f"[red]缺失: {', '.join(missing_optional_deps)}[/red]"
|
|
704
|
-
if missing_optional_deps
|
|
705
|
-
else ', '.join(available_optional_deps) or '无'
|
|
706
|
-
)
|
|
707
|
-
else:
|
|
708
|
-
optional_dependencies = '无'
|
|
709
|
-
|
|
710
|
-
dependencies = '\n'.join(depsinfo.get('requires', [])) or '无'
|
|
711
|
-
pip_dependencies = '\n'.join(depsinfo.get('pip', [])) or '无'
|
|
712
|
-
|
|
713
|
-
table.add_row(
|
|
714
|
-
f"[bold]{name}[/bold]",
|
|
715
|
-
status,
|
|
716
|
-
meta.get('version', '未知'),
|
|
717
|
-
meta.get('description', '无描述'),
|
|
718
|
-
dependencies,
|
|
719
|
-
optional_dependencies,
|
|
720
|
-
pip_dependencies
|
|
721
|
-
)
|
|
722
|
-
|
|
723
|
-
console.print(table)
|
|
724
|
-
|
|
725
|
-
# 显示模块状态统计
|
|
726
|
-
enabled_count = sum(1 for m in all_modules.values() if m.get("status", True))
|
|
727
|
-
disabled_count = len(all_modules) - enabled_count
|
|
728
|
-
console.print(Panel(
|
|
729
|
-
f"[green]已启用: {enabled_count}[/green] [red]已禁用: {disabled_count}[/red]",
|
|
730
|
-
title="模块状态统计",
|
|
731
|
-
border_style="blue"
|
|
732
|
-
))
|
|
733
|
-
|
|
734
|
-
def main():
|
|
735
|
-
parser = argparse.ArgumentParser(
|
|
736
|
-
description="ErisPulse 命令行工具",
|
|
737
|
-
prog="ep"
|
|
738
|
-
)
|
|
739
|
-
subparsers = parser.add_subparsers(dest='command', help='可用命令')
|
|
740
|
-
|
|
741
|
-
# 添加子命令解析器(与原代码一致)
|
|
742
|
-
enable_parser = subparsers.add_parser('enable', help='启用指定模块')
|
|
743
|
-
enable_parser.add_argument('module_names', nargs='+', help='要启用的模块名称(支持多个模块,用空格分隔)')
|
|
744
|
-
enable_parser.add_argument('--init', action='store_true', help='在启用模块前初始化模块数据库')
|
|
745
|
-
|
|
746
|
-
disable_parser = subparsers.add_parser('disable', help='禁用指定模块')
|
|
747
|
-
disable_parser.add_argument('module_names', nargs='+', help='要禁用的模块名称(支持多个模块,用空格分隔)')
|
|
748
|
-
disable_parser.add_argument('--init', action='store_true', help='在禁用模块前初始化模块数据库')
|
|
749
|
-
|
|
750
|
-
list_parser = subparsers.add_parser('list', help='列出所有模块信息')
|
|
751
|
-
list_parser.add_argument('--module', '-m', type=str, help='指定要展示的模块名称')
|
|
752
|
-
|
|
753
|
-
update_parser = subparsers.add_parser('update', help='更新模块列表')
|
|
754
|
-
|
|
755
|
-
upgrade_parser = subparsers.add_parser('upgrade', help='升级模块列表')
|
|
756
|
-
upgrade_parser.add_argument('--force', action='store_true', help='跳过二次确认,强制更新')
|
|
757
|
-
|
|
758
|
-
uninstall_parser = subparsers.add_parser('uninstall', help='删除指定模块')
|
|
759
|
-
uninstall_parser.add_argument('module_names', nargs='+', help='要卸载的模块名称(支持多个模块,用空格分隔)')
|
|
760
|
-
|
|
761
|
-
install_parser = subparsers.add_parser('install', help='安装指定模块(支持多个模块,用空格分隔)')
|
|
762
|
-
install_parser.add_argument('module_name', nargs='+', help='要安装的模块名称(支持多个模块,用空格分隔)')
|
|
763
|
-
install_parser.add_argument('--force', action='store_true', help='强制重新安装模块')
|
|
764
|
-
install_parser.add_argument('--init', action='store_true', help='在安装模块前初始化模块数据库')
|
|
765
|
-
|
|
766
|
-
origin_parser = subparsers.add_parser('origin', help='管理模块源')
|
|
767
|
-
origin_subparsers = origin_parser.add_subparsers(dest='origin_command', help='源管理命令')
|
|
768
|
-
|
|
769
|
-
add_origin_parser = origin_subparsers.add_parser('add', help='添加模块源')
|
|
770
|
-
add_origin_parser.add_argument('url', type=str, help='要添加的模块源URL')
|
|
771
|
-
|
|
772
|
-
list_origin_parser = origin_subparsers.add_parser('list', help='列出所有模块源')
|
|
773
|
-
|
|
774
|
-
del_origin_parser = origin_subparsers.add_parser('del', help='删除模块源')
|
|
775
|
-
del_origin_parser.add_argument('url', type=str, help='要删除的模块源URL')
|
|
776
|
-
|
|
777
|
-
args = parser.parse_args()
|
|
778
|
-
source_manager = SourceManager()
|
|
779
|
-
|
|
780
|
-
# 初始化模块数据库
|
|
781
|
-
if hasattr(args, 'init') and args.init:
|
|
782
|
-
console.print("[yellow]正在初始化模块列表...[/yellow]")
|
|
783
|
-
from . import init as init_module
|
|
784
|
-
init_module()
|
|
785
|
-
|
|
786
|
-
if args.command == 'enable':
|
|
787
|
-
for module_name in args.module_names:
|
|
788
|
-
module_name = module_name.strip()
|
|
789
|
-
if not module_name:
|
|
790
|
-
continue
|
|
791
|
-
if '*' in module_name or '?' in module_name:
|
|
792
|
-
console.print(f"[cyan]正在匹配模块模式: {module_name}...[/cyan]")
|
|
793
|
-
all_modules = env.get_all_modules()
|
|
794
|
-
if not all_modules:
|
|
795
|
-
console.print(Panel("[red]未找到任何模块,请先更新源或检查配置[/red]", title="错误", border_style="red"))
|
|
796
|
-
continue
|
|
797
|
-
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
798
|
-
if not matched_modules:
|
|
799
|
-
console.print(Panel(f"[red]未找到匹配模块模式 {module_name} 的模块[/red]", title="错误", border_style="red"))
|
|
800
|
-
continue
|
|
801
|
-
console.print(f"[green]找到 {len(matched_modules)} 个匹配模块:[/green]")
|
|
802
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
803
|
-
console.print(f" {i}. {matched_module}")
|
|
804
|
-
confirm = Confirm.ask("[yellow]是否启用所有匹配模块?[/yellow]", default=True)
|
|
805
|
-
if not confirm:
|
|
806
|
-
console.print("[yellow]操作已取消[/yellow]")
|
|
807
|
-
continue
|
|
808
|
-
for matched_module in matched_modules:
|
|
809
|
-
enable_module(matched_module)
|
|
810
|
-
else:
|
|
811
|
-
enable_module(module_name)
|
|
812
|
-
elif args.command == 'disable':
|
|
813
|
-
for module_name in args.module_names:
|
|
814
|
-
module_name = module_name.strip()
|
|
815
|
-
if not module_name:
|
|
816
|
-
continue
|
|
817
|
-
if '*' in module_name or '?' in module_name:
|
|
818
|
-
console.print(f"[cyan]正在匹配模块模式: {module_name}...[/cyan]")
|
|
819
|
-
all_modules = env.get_all_modules()
|
|
820
|
-
if not all_modules:
|
|
821
|
-
console.print(Panel("[red]未找到任何模块,请先更新源或检查配置[/red]", title="错误", border_style="red"))
|
|
822
|
-
continue
|
|
823
|
-
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
824
|
-
if not matched_modules:
|
|
825
|
-
console.print(Panel(f"[red]未找到匹配模块模式 {module_name} 的模块[/red]", title="错误", border_style="red"))
|
|
826
|
-
continue
|
|
827
|
-
console.print(f"[green]找到 {len(matched_modules)} 个匹配模块:[/green]")
|
|
828
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
829
|
-
console.print(f" {i}. {matched_module}")
|
|
830
|
-
confirm = Confirm.ask("[yellow]是否禁用所有匹配模块?[/yellow]", default=True)
|
|
831
|
-
if not confirm:
|
|
832
|
-
console.print("[yellow]操作已取消[/yellow]")
|
|
833
|
-
continue
|
|
834
|
-
for matched_module in matched_modules:
|
|
835
|
-
disable_module(matched_module)
|
|
836
|
-
else:
|
|
837
|
-
disable_module(module_name)
|
|
838
|
-
elif args.command == 'list':
|
|
839
|
-
list_modules(args.module)
|
|
840
|
-
elif args.command == 'uninstall':
|
|
841
|
-
for module_name in args.module_names:
|
|
842
|
-
module_name = module_name.strip()
|
|
843
|
-
if not module_name:
|
|
844
|
-
continue
|
|
845
|
-
if '*' in module_name or '?' in module_name:
|
|
846
|
-
console.print(f"[cyan]正在匹配模块模式: {module_name}...[/cyan]")
|
|
847
|
-
all_modules = env.get_all_modules()
|
|
848
|
-
if not all_modules:
|
|
849
|
-
console.print(Panel("[red]未找到任何模块,请先更新源或检查配置[/red]", title="错误", border_style="red"))
|
|
850
|
-
continue
|
|
851
|
-
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
852
|
-
if not matched_modules:
|
|
853
|
-
console.print(Panel(f"[red]未找到匹配模块模式 {module_name} 的模块[/red]", title="错误", border_style="red"))
|
|
854
|
-
continue
|
|
855
|
-
console.print(f"[green]找到 {len(matched_modules)} 个匹配模块:[/green]")
|
|
856
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
857
|
-
console.print(f" {i}. {matched_module}")
|
|
858
|
-
confirm = Confirm.ask("[yellow]是否卸载所有匹配模块?[/yellow]", default=True)
|
|
859
|
-
if not confirm:
|
|
860
|
-
console.print("[yellow]操作已取消[/yellow]")
|
|
861
|
-
continue
|
|
862
|
-
for matched_module in matched_modules:
|
|
863
|
-
uninstall_module(matched_module)
|
|
864
|
-
else:
|
|
865
|
-
uninstall_module(module_name)
|
|
866
|
-
elif args.command == 'install':
|
|
867
|
-
for module_name in args.module_name:
|
|
868
|
-
module_name = module_name.strip()
|
|
869
|
-
if not module_name:
|
|
870
|
-
continue
|
|
871
|
-
if '*' in module_name or '?' in module_name:
|
|
872
|
-
console.print(f"[cyan]正在匹配模块模式: {module_name}...[/cyan]")
|
|
873
|
-
all_modules = env.get_all_modules()
|
|
874
|
-
if not all_modules:
|
|
875
|
-
console.print(Panel(
|
|
876
|
-
"[red]未找到任何模块,请先更新源或检查配置[/red]",
|
|
877
|
-
title="错误",
|
|
878
|
-
border_style="red"
|
|
879
|
-
))
|
|
880
|
-
continue
|
|
881
|
-
|
|
882
|
-
matched_modules = [
|
|
883
|
-
name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)
|
|
884
|
-
]
|
|
885
|
-
|
|
886
|
-
if not matched_modules:
|
|
887
|
-
console.print(Panel(
|
|
888
|
-
f"[red]未找到匹配模块模式 {module_name} 的模块[/red]",
|
|
889
|
-
title="错误",
|
|
890
|
-
border_style="red"
|
|
891
|
-
))
|
|
892
|
-
continue
|
|
893
|
-
|
|
894
|
-
console.print(f"[green]找到 {len(matched_modules)} 个匹配模块:[/green]")
|
|
895
|
-
for i, matched_module in enumerate(matched_modules, start=1):
|
|
896
|
-
console.print(f" {i}. {matched_module}")
|
|
897
|
-
|
|
898
|
-
confirm = Confirm.ask("[yellow]是否安装所有匹配模块?[/yellow]", default=True)
|
|
899
|
-
if not confirm:
|
|
900
|
-
console.print("[yellow]安装已取消[/yellow]")
|
|
901
|
-
continue
|
|
902
|
-
|
|
903
|
-
for matched_module in matched_modules:
|
|
904
|
-
install_module(matched_module, args.force)
|
|
905
|
-
else:
|
|
906
|
-
install_module(module_name, args.force)
|
|
907
|
-
elif args.command == 'update':
|
|
908
|
-
SourceManager().update_sources()
|
|
909
|
-
elif args.command == 'upgrade':
|
|
910
|
-
upgrade_all_modules(args.force)
|
|
911
|
-
elif args.command == 'origin':
|
|
912
|
-
if args.origin_command == 'add':
|
|
913
|
-
success = source_manager.add_source(args.url)
|
|
914
|
-
if success:
|
|
915
|
-
update_confirmation = Confirm.ask(
|
|
916
|
-
"[yellow]源已添加,是否立即更新源以获取最新模块信息?[/yellow]",
|
|
917
|
-
default=True
|
|
918
|
-
)
|
|
919
|
-
if update_confirmation:
|
|
920
|
-
source_manager.update_sources()
|
|
921
|
-
elif args.origin_command == 'list':
|
|
922
|
-
source_manager.list_sources()
|
|
923
|
-
elif args.origin_command == 'del':
|
|
924
|
-
source_manager.del_source(args.url)
|
|
925
|
-
else:
|
|
926
|
-
origin_parser.print_help()
|
|
927
|
-
else:
|
|
928
|
-
parser.print_help()
|
|
929
|
-
|
|
930
|
-
if __name__ == "__main__":
|
|
931
|
-
main()
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import shutil
|
|
5
|
+
import aiohttp
|
|
6
|
+
import zipfile
|
|
7
|
+
import fnmatch
|
|
8
|
+
import asyncio
|
|
9
|
+
import subprocess
|
|
10
|
+
import json
|
|
11
|
+
from .db import env
|
|
12
|
+
|
|
13
|
+
def print_panel(msg, title=None, border_style=None):
|
|
14
|
+
print("=" * 60)
|
|
15
|
+
if title:
|
|
16
|
+
print(f"[{title}]")
|
|
17
|
+
print(msg)
|
|
18
|
+
print("=" * 60)
|
|
19
|
+
|
|
20
|
+
def print_table(headers, rows, title=None):
|
|
21
|
+
if title:
|
|
22
|
+
print(f"== {title} ==")
|
|
23
|
+
col_widths = [len(h) for h in headers]
|
|
24
|
+
for row in rows:
|
|
25
|
+
for i, cell in enumerate(row):
|
|
26
|
+
col_widths[i] = max(col_widths[i], len(str(cell)))
|
|
27
|
+
fmt = " | ".join("{:<" + str(w) + "}" for w in col_widths)
|
|
28
|
+
print(fmt.format(*headers))
|
|
29
|
+
print("-" * (sum(col_widths) + 3 * (len(headers) - 1)))
|
|
30
|
+
for row in rows:
|
|
31
|
+
print(fmt.format(*row))
|
|
32
|
+
|
|
33
|
+
def confirm(msg, default=False):
|
|
34
|
+
yes = {'y', 'yes', ''}
|
|
35
|
+
no = {'n', 'no'}
|
|
36
|
+
prompt = f"{msg} [{'Y/n' if default else 'y/N'}]: "
|
|
37
|
+
while True:
|
|
38
|
+
ans = input(prompt).strip().lower()
|
|
39
|
+
if not ans:
|
|
40
|
+
return default
|
|
41
|
+
if ans in yes:
|
|
42
|
+
return True
|
|
43
|
+
if ans in no:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def ask(msg, choices=None, default=None):
|
|
47
|
+
prompt = f"{msg}"
|
|
48
|
+
if choices:
|
|
49
|
+
prompt += f" ({'/'.join(choices)})"
|
|
50
|
+
if default:
|
|
51
|
+
prompt += f" [default: {default}]"
|
|
52
|
+
prompt += ": "
|
|
53
|
+
while True:
|
|
54
|
+
ans = input(prompt).strip()
|
|
55
|
+
if not ans and default:
|
|
56
|
+
return default
|
|
57
|
+
if not choices or ans in choices:
|
|
58
|
+
return ans
|
|
59
|
+
|
|
60
|
+
class SourceManager:
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self._init_sources()
|
|
63
|
+
|
|
64
|
+
def _init_sources(self):
|
|
65
|
+
if not env.get('origins'):
|
|
66
|
+
env.set('origins', [])
|
|
67
|
+
|
|
68
|
+
async def _validate_url(self, url):
|
|
69
|
+
if not url.startswith(('http://', 'https://')):
|
|
70
|
+
protocol = ask("未指定协议,请输入使用的协议", choices=['http', 'https'], default="https")
|
|
71
|
+
url = f"{protocol}://{url}"
|
|
72
|
+
if not url.endswith('.json'):
|
|
73
|
+
url = f"{url}/map.json"
|
|
74
|
+
try:
|
|
75
|
+
async with aiohttp.ClientSession() as session:
|
|
76
|
+
async with session.get(url) as response:
|
|
77
|
+
response.raise_for_status()
|
|
78
|
+
if response.headers.get('Content-Type', '').startswith('application/json'):
|
|
79
|
+
return url
|
|
80
|
+
else:
|
|
81
|
+
print_panel(f"源 {url} 返回的内容不是有效的 JSON 格式", "错误")
|
|
82
|
+
return None
|
|
83
|
+
except Exception as e:
|
|
84
|
+
print_panel(f"访问源 {url} 失败: {e}", "错误")
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
def add_source(self, value):
|
|
88
|
+
validated_url = asyncio.run(self._validate_url(value))
|
|
89
|
+
if not validated_url:
|
|
90
|
+
print_panel("提供的源不是一个有效源,请检查后重试", "错误")
|
|
91
|
+
return False
|
|
92
|
+
origins = env.get('origins')
|
|
93
|
+
if validated_url not in origins:
|
|
94
|
+
origins.append(validated_url)
|
|
95
|
+
env.set('origins', origins)
|
|
96
|
+
print_panel(f"源 {validated_url} 已成功添加", "成功")
|
|
97
|
+
return True
|
|
98
|
+
else:
|
|
99
|
+
print_panel(f"源 {validated_url} 已存在,无需重复添加", "提示")
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
def update_sources(self):
|
|
103
|
+
origins = env.get('origins')
|
|
104
|
+
providers = {}
|
|
105
|
+
modules = {}
|
|
106
|
+
module_alias = {}
|
|
107
|
+
table_rows = []
|
|
108
|
+
async def fetch_source_data():
|
|
109
|
+
async with aiohttp.ClientSession() as session:
|
|
110
|
+
for origin in origins:
|
|
111
|
+
print(f"正在获取 {origin}...")
|
|
112
|
+
try:
|
|
113
|
+
async with session.get(origin) as response:
|
|
114
|
+
response.raise_for_status()
|
|
115
|
+
if response.headers.get('Content-Type', '').startswith('application/json'):
|
|
116
|
+
content = await response.json()
|
|
117
|
+
providers[content["name"]] = content["base"]
|
|
118
|
+
for module in content["modules"].keys():
|
|
119
|
+
module_content = content["modules"][module]
|
|
120
|
+
modules[f'{module}@{content["name"]}'] = module_content
|
|
121
|
+
module_origin_name = module_content["path"]
|
|
122
|
+
module_alias_name = module
|
|
123
|
+
module_alias[f'{module_origin_name}@{content["name"]}'] = module_alias_name
|
|
124
|
+
table_rows.append([
|
|
125
|
+
content['name'],
|
|
126
|
+
module,
|
|
127
|
+
f"{providers[content['name']]}{module_origin_name}"
|
|
128
|
+
])
|
|
129
|
+
else:
|
|
130
|
+
print_panel(f"源 {origin} 返回的内容不是有效的 JSON 格式", "错误")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
print_panel(f"获取 {origin} 时出错: {e}", "错误")
|
|
133
|
+
asyncio.run(fetch_source_data())
|
|
134
|
+
print_table(["源", "模块", "地址"], table_rows, "源更新状态")
|
|
135
|
+
from datetime import datetime
|
|
136
|
+
env.set('providers', providers)
|
|
137
|
+
env.set('modules', modules)
|
|
138
|
+
env.set('module_alias', module_alias)
|
|
139
|
+
env.set('last_origin_update_time', datetime.now().isoformat())
|
|
140
|
+
print_panel("源更新完成", "成功")
|
|
141
|
+
|
|
142
|
+
def list_sources(self):
|
|
143
|
+
origins = env.get('origins')
|
|
144
|
+
if not origins:
|
|
145
|
+
print_panel("当前没有配置任何源", "提示")
|
|
146
|
+
return
|
|
147
|
+
rows = [[str(idx), origin] for idx, origin in enumerate(origins, 1)]
|
|
148
|
+
print_table(["序号", "源地址"], rows, "已配置的源")
|
|
149
|
+
|
|
150
|
+
def del_source(self, value):
|
|
151
|
+
origins = env.get('origins')
|
|
152
|
+
if value in origins:
|
|
153
|
+
origins.remove(value)
|
|
154
|
+
env.set('origins', origins)
|
|
155
|
+
print_panel(f"源 {value} 已成功删除", "成功")
|
|
156
|
+
else:
|
|
157
|
+
print_panel(f"源 {value} 不存在", "错误")
|
|
158
|
+
|
|
159
|
+
def enable_module(module_name):
|
|
160
|
+
module_info = env.get_module(module_name)
|
|
161
|
+
if module_info:
|
|
162
|
+
env.set_module_status(module_name, True)
|
|
163
|
+
print_panel(f"✓ 模块 {module_name} 已成功启用", "成功")
|
|
164
|
+
else:
|
|
165
|
+
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
166
|
+
|
|
167
|
+
def disable_module(module_name):
|
|
168
|
+
module_info = env.get_module(module_name)
|
|
169
|
+
if module_info:
|
|
170
|
+
env.set_module_status(module_name, False)
|
|
171
|
+
print_panel(f"✓ 模块 {module_name} 已成功禁用", "成功")
|
|
172
|
+
else:
|
|
173
|
+
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
174
|
+
|
|
175
|
+
async def fetch_url(session, url):
|
|
176
|
+
try:
|
|
177
|
+
async with session.get(url) as response:
|
|
178
|
+
response.raise_for_status()
|
|
179
|
+
return await response.read()
|
|
180
|
+
except Exception as e:
|
|
181
|
+
print(f"请求失败: {e}")
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
def extract_and_setup_module(module_name, module_url, zip_path, module_dir):
|
|
185
|
+
try:
|
|
186
|
+
print(f"正在从 {module_url} 下载模块...")
|
|
187
|
+
async def download_module():
|
|
188
|
+
async with aiohttp.ClientSession() as session:
|
|
189
|
+
content = await fetch_url(session, module_url)
|
|
190
|
+
if content is None:
|
|
191
|
+
return False
|
|
192
|
+
with open(zip_path, 'wb') as zip_file:
|
|
193
|
+
zip_file.write(content)
|
|
194
|
+
if not os.path.exists(module_dir):
|
|
195
|
+
os.makedirs(module_dir)
|
|
196
|
+
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
197
|
+
zip_ref.extractall(module_dir)
|
|
198
|
+
init_file_path = os.path.join(module_dir, '__init__.py')
|
|
199
|
+
if not os.path.exists(init_file_path):
|
|
200
|
+
sub_module_dir = os.path.join(module_dir, module_name)
|
|
201
|
+
m_sub_module_dir = os.path.join(module_dir, f"m_{module_name}")
|
|
202
|
+
for sub_dir in [sub_module_dir, m_sub_module_dir]:
|
|
203
|
+
if os.path.exists(sub_dir) and os.path.isdir(sub_dir):
|
|
204
|
+
for item in os.listdir(sub_dir):
|
|
205
|
+
source_item = os.path.join(sub_dir, item)
|
|
206
|
+
target_item = os.path.join(module_dir, item)
|
|
207
|
+
if os.path.exists(target_item):
|
|
208
|
+
os.remove(target_item)
|
|
209
|
+
shutil.move(source_item, module_dir)
|
|
210
|
+
os.rmdir(sub_dir)
|
|
211
|
+
print(f"模块 {module_name} 文件已成功解压并设置")
|
|
212
|
+
return True
|
|
213
|
+
return asyncio.run(download_module())
|
|
214
|
+
except Exception as e:
|
|
215
|
+
print_panel(f"处理模块 {module_name} 文件失败: {e}", "错误")
|
|
216
|
+
if os.path.exists(zip_path):
|
|
217
|
+
try:
|
|
218
|
+
os.remove(zip_path)
|
|
219
|
+
except Exception as cleanup_error:
|
|
220
|
+
print(f"清理失败: {cleanup_error}")
|
|
221
|
+
return False
|
|
222
|
+
finally:
|
|
223
|
+
if os.path.exists(zip_path):
|
|
224
|
+
try:
|
|
225
|
+
os.remove(zip_path)
|
|
226
|
+
except Exception as cleanup_error:
|
|
227
|
+
print(f"清理失败: {cleanup_error}")
|
|
228
|
+
|
|
229
|
+
def install_pip_dependencies(dependencies):
|
|
230
|
+
if not dependencies:
|
|
231
|
+
return True
|
|
232
|
+
print("正在安装pip依赖...")
|
|
233
|
+
try:
|
|
234
|
+
result = subprocess.run(
|
|
235
|
+
[sys.executable, "-m", "pip", "install"] + dependencies,
|
|
236
|
+
check=True,
|
|
237
|
+
stdout=subprocess.PIPE,
|
|
238
|
+
stderr=subprocess.PIPE
|
|
239
|
+
)
|
|
240
|
+
print(result.stdout.decode())
|
|
241
|
+
return True
|
|
242
|
+
except subprocess.CalledProcessError as e:
|
|
243
|
+
print_panel(f"安装pip依赖失败: {e.stderr.decode()}", "错误")
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
def install_module(module_name, force=False):
|
|
247
|
+
print_panel(f"准备安装模块: {module_name}", "安装摘要")
|
|
248
|
+
last_update_time = env.get('last_origin_update_time', None)
|
|
249
|
+
if last_update_time:
|
|
250
|
+
from datetime import datetime, timedelta
|
|
251
|
+
last_update = datetime.fromisoformat(last_update_time)
|
|
252
|
+
if datetime.now() - last_update > timedelta(hours=720):
|
|
253
|
+
print_panel("距离上次源更新已超过30天,源内可能有新模块或更新。", "提示")
|
|
254
|
+
if confirm("是否在安装模块前更新源?", default=True):
|
|
255
|
+
SourceManager().update_sources()
|
|
256
|
+
env.set('last_origin_update_time', datetime.now().isoformat())
|
|
257
|
+
print("✓ 源更新完成")
|
|
258
|
+
module_info = env.get_module(module_name)
|
|
259
|
+
if module_info and not force:
|
|
260
|
+
meta = module_info.get('info', {}).get('meta', {})
|
|
261
|
+
print_panel(
|
|
262
|
+
f"模块 {module_name} 已存在\n版本: {meta.get('version', '未知')}\n描述: {meta.get('description', '无描述')}",
|
|
263
|
+
"模块已存在"
|
|
264
|
+
)
|
|
265
|
+
if not confirm("是否要强制重新安装?", default=False):
|
|
266
|
+
return
|
|
267
|
+
providers = env.get('providers', {})
|
|
268
|
+
if isinstance(providers, str):
|
|
269
|
+
providers = json.loads(providers)
|
|
270
|
+
module_info_list = []
|
|
271
|
+
for provider, url in providers.items():
|
|
272
|
+
module_key = f"{module_name}@{provider}"
|
|
273
|
+
modules_data = env.get('modules', {})
|
|
274
|
+
if isinstance(modules_data, str):
|
|
275
|
+
modules_data = json.loads(modules_data)
|
|
276
|
+
if module_key in modules_data:
|
|
277
|
+
module_data = modules_data[module_key]
|
|
278
|
+
meta = module_data.get("meta", {})
|
|
279
|
+
depsinfo = module_data.get("dependencies", {})
|
|
280
|
+
module_info_list.append({
|
|
281
|
+
'provider': provider,
|
|
282
|
+
'url': url,
|
|
283
|
+
'path': module_data.get('path', ''),
|
|
284
|
+
'version': meta.get('version', '未知'),
|
|
285
|
+
'description': meta.get('description', '无描述'),
|
|
286
|
+
'author': meta.get('author', '未知'),
|
|
287
|
+
'dependencies': depsinfo.get("requires", []),
|
|
288
|
+
'optional_dependencies': depsinfo.get("optional", []),
|
|
289
|
+
'pip_dependencies': depsinfo.get("pip", [])
|
|
290
|
+
})
|
|
291
|
+
if not module_info_list:
|
|
292
|
+
print_panel(f"未找到模块 {module_name}", "错误")
|
|
293
|
+
if providers:
|
|
294
|
+
print("当前可用源:")
|
|
295
|
+
for provider in providers:
|
|
296
|
+
print(f" - {provider}")
|
|
297
|
+
return
|
|
298
|
+
if len(module_info_list) > 1:
|
|
299
|
+
print(f"找到 {len(module_info_list)} 个源的 {module_name} 模块:")
|
|
300
|
+
rows = []
|
|
301
|
+
for i, info in enumerate(module_info_list):
|
|
302
|
+
rows.append([
|
|
303
|
+
str(i+1), info['provider'], info['version'], info['description'], info['author']
|
|
304
|
+
])
|
|
305
|
+
print_table(["编号", "源", "版本", "描述", "作者"], rows, "可选模块源")
|
|
306
|
+
while True:
|
|
307
|
+
choice = ask("请选择要安装的源 (输入编号)", default="1")
|
|
308
|
+
if choice.isdigit() and 1 <= int(choice) <= len(module_info_list):
|
|
309
|
+
selected_module = module_info_list[int(choice)-1]
|
|
310
|
+
break
|
|
311
|
+
else:
|
|
312
|
+
print("输入无效,请重新选择")
|
|
313
|
+
else:
|
|
314
|
+
selected_module = module_info_list[0]
|
|
315
|
+
for dep in selected_module['dependencies']:
|
|
316
|
+
print(f"正在安装依赖模块 {dep}...")
|
|
317
|
+
install_module(dep)
|
|
318
|
+
third_party_deps = selected_module.get('pip_dependencies', [])
|
|
319
|
+
if third_party_deps:
|
|
320
|
+
print(f"模块 {module_name} 需要以下pip依赖: {', '.join(third_party_deps)}")
|
|
321
|
+
if not install_pip_dependencies(third_party_deps):
|
|
322
|
+
print(f"无法安装模块 {module_name} 的pip依赖,安装终止")
|
|
323
|
+
return
|
|
324
|
+
module_url = selected_module['url'] + selected_module['path']
|
|
325
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
326
|
+
module_dir = os.path.join(script_dir, 'modules', module_name)
|
|
327
|
+
zip_path = os.path.join(script_dir, f"{module_name}.zip")
|
|
328
|
+
if not extract_and_setup_module(
|
|
329
|
+
module_name=module_name,
|
|
330
|
+
module_url=module_url,
|
|
331
|
+
zip_path=zip_path,
|
|
332
|
+
module_dir=module_dir
|
|
333
|
+
):
|
|
334
|
+
return
|
|
335
|
+
env.set_module(module_name, {
|
|
336
|
+
'status': True,
|
|
337
|
+
'info': {
|
|
338
|
+
'meta': {
|
|
339
|
+
'version': selected_module['version'],
|
|
340
|
+
'description': selected_module['description'],
|
|
341
|
+
'author': selected_module['author'],
|
|
342
|
+
'pip_dependencies': selected_module['pip_dependencies']
|
|
343
|
+
},
|
|
344
|
+
'dependencies': {
|
|
345
|
+
'requires': selected_module['dependencies'],
|
|
346
|
+
'optional': selected_module['optional_dependencies'],
|
|
347
|
+
'pip': selected_module['pip_dependencies']
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
print(f"模块 {module_name} 安装成功")
|
|
352
|
+
|
|
353
|
+
def uninstall_module(module_name):
|
|
354
|
+
print_panel(f"准备卸载模块: {module_name}", "卸载摘要")
|
|
355
|
+
module_info = env.get_module(module_name)
|
|
356
|
+
if not module_info:
|
|
357
|
+
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
358
|
+
return
|
|
359
|
+
meta = module_info.get('info', {}).get('meta', {})
|
|
360
|
+
depsinfo = module_info.get('info', {}).get('dependencies', {})
|
|
361
|
+
print_panel(
|
|
362
|
+
f"版本: {meta.get('version', '未知')}\n描述: {meta.get('description', '无描述')}\npip依赖: {', '.join(depsinfo.get('pip', [])) or '无'}",
|
|
363
|
+
"模块信息"
|
|
364
|
+
)
|
|
365
|
+
if not confirm("确认要卸载此模块吗?", default=False):
|
|
366
|
+
print("卸载已取消")
|
|
367
|
+
return
|
|
368
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
369
|
+
module_path = os.path.join(script_dir, 'modules', module_name)
|
|
370
|
+
module_file_path = module_path + '.py'
|
|
371
|
+
if os.path.exists(module_file_path):
|
|
372
|
+
try:
|
|
373
|
+
os.remove(module_file_path)
|
|
374
|
+
except Exception as e:
|
|
375
|
+
print_panel(f"删除模块文件 {module_name} 时出错: {e}", "错误")
|
|
376
|
+
elif os.path.exists(module_path) and os.path.isdir(module_path):
|
|
377
|
+
try:
|
|
378
|
+
shutil.rmtree(module_path)
|
|
379
|
+
except Exception as e:
|
|
380
|
+
print_panel(f"删除模块目录 {module_name} 时出错: {e}", "错误")
|
|
381
|
+
else:
|
|
382
|
+
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
383
|
+
return
|
|
384
|
+
pip_dependencies = depsinfo.get('pip', [])
|
|
385
|
+
if pip_dependencies:
|
|
386
|
+
all_modules = env.get_all_modules()
|
|
387
|
+
unused_pip_dependencies = []
|
|
388
|
+
essential_packages = {'aiohttp'}
|
|
389
|
+
for dep in pip_dependencies:
|
|
390
|
+
if dep in essential_packages:
|
|
391
|
+
print(f"跳过必要模块 {dep} 的卸载")
|
|
392
|
+
continue
|
|
393
|
+
is_dependency_used = False
|
|
394
|
+
for name, info in all_modules.items():
|
|
395
|
+
if name != module_name and dep in info.get('info', {}).get('dependencies', {}).get('pip', []):
|
|
396
|
+
is_dependency_used = True
|
|
397
|
+
break
|
|
398
|
+
if not is_dependency_used:
|
|
399
|
+
unused_pip_dependencies.append(dep)
|
|
400
|
+
if unused_pip_dependencies:
|
|
401
|
+
print_panel(
|
|
402
|
+
f"以下 pip 依赖不再被其他模块使用:\n{', '.join(unused_pip_dependencies)}",
|
|
403
|
+
"可卸载依赖"
|
|
404
|
+
)
|
|
405
|
+
if confirm("是否卸载这些 pip 依赖?", default=False):
|
|
406
|
+
try:
|
|
407
|
+
subprocess.run(
|
|
408
|
+
[sys.executable, "-m", "pip", "uninstall", "-y"] + unused_pip_dependencies,
|
|
409
|
+
check=True,
|
|
410
|
+
stdout=subprocess.PIPE,
|
|
411
|
+
stderr=subprocess.PIPE
|
|
412
|
+
)
|
|
413
|
+
print_panel(
|
|
414
|
+
f"成功卸载 pip 依赖: {', '.join(unused_pip_dependencies)}",
|
|
415
|
+
"成功"
|
|
416
|
+
)
|
|
417
|
+
except subprocess.CalledProcessError as e:
|
|
418
|
+
print_panel(
|
|
419
|
+
f"卸载 pip 依赖失败: {e.stderr.decode()}",
|
|
420
|
+
"错误"
|
|
421
|
+
)
|
|
422
|
+
if env.remove_module(module_name):
|
|
423
|
+
print_panel(f"✓ 模块 {module_name} 已成功卸载", "成功")
|
|
424
|
+
else:
|
|
425
|
+
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
426
|
+
|
|
427
|
+
def upgrade_all_modules(force=False):
|
|
428
|
+
all_modules = env.get_all_modules()
|
|
429
|
+
if not all_modules:
|
|
430
|
+
print("未找到任何模块,无法更新")
|
|
431
|
+
return
|
|
432
|
+
providers = env.get('providers', {})
|
|
433
|
+
if isinstance(providers, str):
|
|
434
|
+
providers = json.loads(providers)
|
|
435
|
+
modules_data = env.get('modules', {})
|
|
436
|
+
if isinstance(modules_data, str):
|
|
437
|
+
modules_data = json.loads(modules_data)
|
|
438
|
+
updates_available = []
|
|
439
|
+
for module_name, module_info in all_modules.items():
|
|
440
|
+
local_version = module_info.get('info', {}).get('meta', {}).get('version', '0.0.0')
|
|
441
|
+
for provider, url in providers.items():
|
|
442
|
+
module_key = f"{module_name}@{provider}"
|
|
443
|
+
if module_key in modules_data:
|
|
444
|
+
remote_module = modules_data[module_key]
|
|
445
|
+
remote_version = remote_module.get('meta', {}).get('version', '1.14.514')
|
|
446
|
+
if remote_version > local_version:
|
|
447
|
+
updates_available.append({
|
|
448
|
+
'name': module_name,
|
|
449
|
+
'local_version': local_version,
|
|
450
|
+
'remote_version': remote_version,
|
|
451
|
+
'provider': provider,
|
|
452
|
+
'url': url,
|
|
453
|
+
'path': remote_module.get('path', ''),
|
|
454
|
+
})
|
|
455
|
+
if not updates_available:
|
|
456
|
+
print("所有模块已是最新版本,无需更新")
|
|
457
|
+
return
|
|
458
|
+
print("\n以下模块有可用更新:")
|
|
459
|
+
rows = []
|
|
460
|
+
for update in updates_available:
|
|
461
|
+
rows.append([update['name'], update['local_version'], update['remote_version'], update['provider']])
|
|
462
|
+
print_table(["模块", "当前版本", "最新版本", "源"], rows, "可用更新")
|
|
463
|
+
if not force:
|
|
464
|
+
if not confirm("警告:更新模块可能会导致兼容性问题,请在更新前查看插件作者的相关声明。\n是否继续?", default=False):
|
|
465
|
+
print("更新已取消")
|
|
466
|
+
return
|
|
467
|
+
for update in updates_available:
|
|
468
|
+
print(f"正在更新模块 {update['name']}...")
|
|
469
|
+
module_url = update['url'] + update['path']
|
|
470
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
471
|
+
module_dir = os.path.join(script_dir, 'modules', update['name'])
|
|
472
|
+
zip_path = os.path.join(script_dir, f"{update['name']}.zip")
|
|
473
|
+
if not extract_and_setup_module(
|
|
474
|
+
module_name=update['name'],
|
|
475
|
+
module_url=module_url,
|
|
476
|
+
zip_path=zip_path,
|
|
477
|
+
module_dir=module_dir
|
|
478
|
+
):
|
|
479
|
+
continue
|
|
480
|
+
all_modules[update['name']]['info']['version'] = update['remote_version']
|
|
481
|
+
env.set_all_modules(all_modules)
|
|
482
|
+
print(f"模块 {update['name']} 已更新至版本 {update['remote_version']}")
|
|
483
|
+
|
|
484
|
+
def list_modules(module_name=None):
|
|
485
|
+
all_modules = env.get_all_modules()
|
|
486
|
+
if not all_modules:
|
|
487
|
+
print_panel("未在数据库中发现注册模块,正在初始化模块列表...", "提示")
|
|
488
|
+
from . import init as init_module
|
|
489
|
+
init_module()
|
|
490
|
+
all_modules = env.get_all_modules()
|
|
491
|
+
if not all_modules:
|
|
492
|
+
print_panel("未找到任何模块", "错误")
|
|
493
|
+
return
|
|
494
|
+
print_panel(f"找到 {len(all_modules)} 个模块", "统计")
|
|
495
|
+
rows = []
|
|
496
|
+
for name, info in all_modules.items():
|
|
497
|
+
status = "✓" if info.get("status", True) else "✗"
|
|
498
|
+
meta = info.get('info', {}).get('meta', {})
|
|
499
|
+
depsinfo = info.get('info', {}).get('dependencies', {})
|
|
500
|
+
optional_deps = depsinfo.get('optional', [])
|
|
501
|
+
available_optional_deps = []
|
|
502
|
+
missing_optional_deps = []
|
|
503
|
+
if optional_deps:
|
|
504
|
+
for dep in optional_deps:
|
|
505
|
+
if isinstance(dep, list):
|
|
506
|
+
available_deps = [d for d in dep if d in all_modules]
|
|
507
|
+
if available_deps:
|
|
508
|
+
available_optional_deps.extend(available_deps)
|
|
509
|
+
else:
|
|
510
|
+
missing_optional_deps.extend(dep)
|
|
511
|
+
elif dep in all_modules:
|
|
512
|
+
available_optional_deps.append(dep)
|
|
513
|
+
else:
|
|
514
|
+
missing_optional_deps.append(dep)
|
|
515
|
+
if missing_optional_deps:
|
|
516
|
+
optional_dependencies = f"可用: {', '.join(available_optional_deps)} 缺失: {', '.join(missing_optional_deps)}"
|
|
517
|
+
else:
|
|
518
|
+
optional_dependencies = ', '.join(available_optional_deps) or '无'
|
|
519
|
+
else:
|
|
520
|
+
optional_dependencies = '无'
|
|
521
|
+
dependencies = ', '.join(depsinfo.get('requires', [])) or '无'
|
|
522
|
+
pip_dependencies = ', '.join(depsinfo.get('pip', [])) or '无'
|
|
523
|
+
rows.append([
|
|
524
|
+
name, status, meta.get('version', '未知'), meta.get('description', '无描述'),
|
|
525
|
+
dependencies, optional_dependencies, pip_dependencies
|
|
526
|
+
])
|
|
527
|
+
print_table(
|
|
528
|
+
["模块名称", "状态", "版本", "描述", "依赖", "可选依赖", "pip依赖"],
|
|
529
|
+
rows,
|
|
530
|
+
"模块列表"
|
|
531
|
+
)
|
|
532
|
+
enabled_count = sum(1 for m in all_modules.values() if m.get("status", True))
|
|
533
|
+
disabled_count = len(all_modules) - enabled_count
|
|
534
|
+
print_panel(f"已启用: {enabled_count} 已禁用: {disabled_count}", "模块状态统计")
|
|
535
|
+
|
|
536
|
+
def main():
|
|
537
|
+
parser = argparse.ArgumentParser(
|
|
538
|
+
description="ErisPulse 命令行工具",
|
|
539
|
+
prog="ep"
|
|
540
|
+
)
|
|
541
|
+
subparsers = parser.add_subparsers(dest='command', help='可用命令')
|
|
542
|
+
enable_parser = subparsers.add_parser('enable', help='启用指定模块')
|
|
543
|
+
enable_parser.add_argument('module_names', nargs='+', help='要启用的模块名称(支持多个模块,用空格分隔)')
|
|
544
|
+
enable_parser.add_argument('--init', action='store_true', help='在启用模块前初始化模块数据库')
|
|
545
|
+
disable_parser = subparsers.add_parser('disable', help='禁用指定模块')
|
|
546
|
+
disable_parser.add_argument('module_names', nargs='+', help='要禁用的模块名称(支持多个模块,用空格分隔)')
|
|
547
|
+
disable_parser.add_argument('--init', action='store_true', help='在禁用模块前初始化模块数据库')
|
|
548
|
+
list_parser = subparsers.add_parser('list', help='列出所有模块信息')
|
|
549
|
+
list_parser.add_argument('--module', '-m', type=str, help='指定要展示的模块名称')
|
|
550
|
+
update_parser = subparsers.add_parser('update', help='更新模块列表')
|
|
551
|
+
upgrade_parser = subparsers.add_parser('upgrade', help='升级模块列表')
|
|
552
|
+
upgrade_parser.add_argument('--force', action='store_true', help='跳过二次确认,强制更新')
|
|
553
|
+
uninstall_parser = subparsers.add_parser('uninstall', help='删除指定模块')
|
|
554
|
+
uninstall_parser.add_argument('module_names', nargs='+', help='要卸载的模块名称(支持多个模块,用空格分隔)')
|
|
555
|
+
install_parser = subparsers.add_parser('install', help='安装指定模块(支持多个模块,用空格分隔)')
|
|
556
|
+
install_parser.add_argument('module_name', nargs='+', help='要安装的模块名称(支持多个模块,用空格分隔)')
|
|
557
|
+
install_parser.add_argument('--force', action='store_true', help='强制重新安装模块')
|
|
558
|
+
install_parser.add_argument('--init', action='store_true', help='在安装模块前初始化模块数据库')
|
|
559
|
+
origin_parser = subparsers.add_parser('origin', help='管理模块源')
|
|
560
|
+
origin_subparsers = origin_parser.add_subparsers(dest='origin_command', help='源管理命令')
|
|
561
|
+
add_origin_parser = origin_subparsers.add_parser('add', help='添加模块源')
|
|
562
|
+
add_origin_parser.add_argument('url', type=str, help='要添加的模块源URL')
|
|
563
|
+
list_origin_parser = origin_subparsers.add_parser('list', help='列出所有模块源')
|
|
564
|
+
del_origin_parser = origin_subparsers.add_parser('del', help='删除模块源')
|
|
565
|
+
del_origin_parser.add_argument('url', type=str, help='要删除的模块源URL')
|
|
566
|
+
args = parser.parse_args()
|
|
567
|
+
source_manager = SourceManager()
|
|
568
|
+
if hasattr(args, 'init') and args.init:
|
|
569
|
+
print("正在初始化模块列表...")
|
|
570
|
+
from . import init as init_module
|
|
571
|
+
init_module()
|
|
572
|
+
if args.command == 'enable':
|
|
573
|
+
for module_name in args.module_names:
|
|
574
|
+
module_name = module_name.strip()
|
|
575
|
+
if not module_name:
|
|
576
|
+
continue
|
|
577
|
+
if '*' in module_name or '?' in module_name:
|
|
578
|
+
print(f"正在匹配模块模式: {module_name}...")
|
|
579
|
+
all_modules = env.get_all_modules()
|
|
580
|
+
if not all_modules:
|
|
581
|
+
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
582
|
+
continue
|
|
583
|
+
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
584
|
+
if not matched_modules:
|
|
585
|
+
print_panel(f"未找到匹配模块模式 {module_name} 的模块", "错误")
|
|
586
|
+
continue
|
|
587
|
+
print(f"找到 {len(matched_modules)} 个匹配模块:")
|
|
588
|
+
for i, matched_module in enumerate(matched_modules, start=1):
|
|
589
|
+
print(f" {i}. {matched_module}")
|
|
590
|
+
if not confirm("是否启用所有匹配模块?", default=True):
|
|
591
|
+
print("操作已取消")
|
|
592
|
+
continue
|
|
593
|
+
for matched_module in matched_modules:
|
|
594
|
+
enable_module(matched_module)
|
|
595
|
+
else:
|
|
596
|
+
enable_module(module_name)
|
|
597
|
+
elif args.command == 'disable':
|
|
598
|
+
for module_name in args.module_names:
|
|
599
|
+
module_name = module_name.strip()
|
|
600
|
+
if not module_name:
|
|
601
|
+
continue
|
|
602
|
+
if '*' in module_name or '?' in module_name:
|
|
603
|
+
print(f"正在匹配模块模式: {module_name}...")
|
|
604
|
+
all_modules = env.get_all_modules()
|
|
605
|
+
if not all_modules:
|
|
606
|
+
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
607
|
+
continue
|
|
608
|
+
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
609
|
+
if not matched_modules:
|
|
610
|
+
print_panel(f"未找到匹配模块模式 {module_name} 的模块", "错误")
|
|
611
|
+
continue
|
|
612
|
+
print(f"找到 {len(matched_modules)} 个匹配模块:")
|
|
613
|
+
for i, matched_module in enumerate(matched_modules, start=1):
|
|
614
|
+
print(f" {i}. {matched_module}")
|
|
615
|
+
if not confirm("是否禁用所有匹配模块?", default=True):
|
|
616
|
+
print("操作已取消")
|
|
617
|
+
continue
|
|
618
|
+
for matched_module in matched_modules:
|
|
619
|
+
disable_module(matched_module)
|
|
620
|
+
else:
|
|
621
|
+
disable_module(module_name)
|
|
622
|
+
elif args.command == 'list':
|
|
623
|
+
list_modules(args.module)
|
|
624
|
+
elif args.command == 'uninstall':
|
|
625
|
+
for module_name in args.module_names:
|
|
626
|
+
module_name = module_name.strip()
|
|
627
|
+
if not module_name:
|
|
628
|
+
continue
|
|
629
|
+
if '*' in module_name or '?' in module_name:
|
|
630
|
+
print(f"正在匹配模块模式: {module_name}...")
|
|
631
|
+
all_modules = env.get_all_modules()
|
|
632
|
+
if not all_modules:
|
|
633
|
+
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
634
|
+
continue
|
|
635
|
+
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
636
|
+
if not matched_modules:
|
|
637
|
+
print_panel(f"未找到匹配模块模式 {module_name} 的模块", "错误")
|
|
638
|
+
continue
|
|
639
|
+
print(f"找到 {len(matched_modules)} 个匹配模块:")
|
|
640
|
+
for i, matched_module in enumerate(matched_modules, start=1):
|
|
641
|
+
print(f" {i}. {matched_module}")
|
|
642
|
+
if not confirm("是否卸载所有匹配模块?", default=True):
|
|
643
|
+
print("操作已取消")
|
|
644
|
+
continue
|
|
645
|
+
for matched_module in matched_modules:
|
|
646
|
+
uninstall_module(matched_module)
|
|
647
|
+
else:
|
|
648
|
+
uninstall_module(module_name)
|
|
649
|
+
elif args.command == 'install':
|
|
650
|
+
for module_name in args.module_name:
|
|
651
|
+
module_name = module_name.strip()
|
|
652
|
+
if not module_name:
|
|
653
|
+
continue
|
|
654
|
+
if '*' in module_name or '?' in module_name:
|
|
655
|
+
print(f"正在匹配模块模式: {module_name}...")
|
|
656
|
+
all_modules = env.get_all_modules()
|
|
657
|
+
if not all_modules:
|
|
658
|
+
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
659
|
+
continue
|
|
660
|
+
matched_modules = [name for name in all_modules.keys() if fnmatch.fnmatch(name, module_name)]
|
|
661
|
+
if not matched_modules:
|
|
662
|
+
print_panel(f"未找到匹配模块模式 {module_name} 的模块", "错误")
|
|
663
|
+
continue
|
|
664
|
+
print(f"找到 {len(matched_modules)} 个匹配模块:")
|
|
665
|
+
for i, matched_module in enumerate(matched_modules, start=1):
|
|
666
|
+
print(f" {i}. {matched_module}")
|
|
667
|
+
if not confirm("是否安装所有匹配模块?", default=True):
|
|
668
|
+
print("安装已取消")
|
|
669
|
+
continue
|
|
670
|
+
for matched_module in matched_modules:
|
|
671
|
+
install_module(matched_module, args.force)
|
|
672
|
+
else:
|
|
673
|
+
install_module(module_name, args.force)
|
|
674
|
+
elif args.command == 'update':
|
|
675
|
+
SourceManager().update_sources()
|
|
676
|
+
elif args.command == 'upgrade':
|
|
677
|
+
upgrade_all_modules(args.force)
|
|
678
|
+
elif args.command == 'origin':
|
|
679
|
+
if args.origin_command == 'add':
|
|
680
|
+
success = source_manager.add_source(args.url)
|
|
681
|
+
if success:
|
|
682
|
+
if confirm("源已添加,是否立即更新源以获取最新模块信息?", default=True):
|
|
683
|
+
source_manager.update_sources()
|
|
684
|
+
elif args.origin_command == 'list':
|
|
685
|
+
source_manager.list_sources()
|
|
686
|
+
elif args.origin_command == 'del':
|
|
687
|
+
source_manager.del_source(args.url)
|
|
688
|
+
else:
|
|
689
|
+
origin_parser.print_help()
|
|
690
|
+
else:
|
|
691
|
+
parser.print_help()
|
|
692
|
+
|
|
693
|
+
if __name__ == "__main__":
|
|
694
|
+
main()
|