Upload 4 files
Browse files- .gitattributes +35 -35
- README.md +10 -10
- main.py +1062 -0
- requirements.txt +16 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,35 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: T
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo: purple
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
---
|
| 9 |
-
|
| 10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: T
|
| 3 |
+
emoji: 📊
|
| 4 |
+
colorFrom: gray
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
main.py
ADDED
|
@@ -0,0 +1,1062 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import asyncio
|
| 3 |
+
import json
|
| 4 |
+
import logging
|
| 5 |
+
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
| 6 |
+
from telegram.ext import (
|
| 7 |
+
Application,
|
| 8 |
+
CommandHandler,
|
| 9 |
+
ConversationHandler,
|
| 10 |
+
MessageHandler,
|
| 11 |
+
CallbackQueryHandler,
|
| 12 |
+
CallbackContext,
|
| 13 |
+
ContextTypes,
|
| 14 |
+
filters,
|
| 15 |
+
)
|
| 16 |
+
import httpx
|
| 17 |
+
|
| 18 |
+
from pikpakapi import PikPakApi
|
| 19 |
+
|
| 20 |
+
from typing import Union, Any, Dict, List, Optional
|
| 21 |
+
from fastapi import (
|
| 22 |
+
FastAPI,
|
| 23 |
+
APIRouter,
|
| 24 |
+
Depends,
|
| 25 |
+
Request,
|
| 26 |
+
Query,
|
| 27 |
+
Body,
|
| 28 |
+
Path,
|
| 29 |
+
Response,
|
| 30 |
+
HTTPException,
|
| 31 |
+
status,
|
| 32 |
+
Request,
|
| 33 |
+
)
|
| 34 |
+
from fastapi.responses import StreamingResponse, HTMLResponse, JSONResponse
|
| 35 |
+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
| 36 |
+
from fastapi.templating import Jinja2Templates
|
| 37 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 38 |
+
from pydantic import BaseModel, Extra
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class PostRequest(BaseModel):
|
| 42 |
+
class Config:
|
| 43 |
+
extra = Extra.allow
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class FileRequest(BaseModel):
|
| 47 |
+
size: int = 100
|
| 48 |
+
parent_id: str | None = ""
|
| 49 |
+
next_page_token: str | None = ""
|
| 50 |
+
additional_filters: Dict | None = {}
|
| 51 |
+
|
| 52 |
+
class Config:
|
| 53 |
+
extra = Extra.allow
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class OfflineRequest(BaseModel):
|
| 57 |
+
file_url: str = ""
|
| 58 |
+
parent_id: str | None = ""
|
| 59 |
+
name: str | None = ""
|
| 60 |
+
|
| 61 |
+
class Config:
|
| 62 |
+
extra = Extra.allow
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
security = HTTPBearer()
|
| 66 |
+
# SECRET_TOKEN = "SECRET_TOKEN"
|
| 67 |
+
SECRET_TOKEN = os.getenv("SECRET_TOKEN")
|
| 68 |
+
if SECRET_TOKEN is None:
|
| 69 |
+
raise ValueError("请在环境变量中设置SECRET_TOKEN,确保安全!")
|
| 70 |
+
|
| 71 |
+
THUNDERX_USERNAME = os.getenv("THUNDERX_USERNAME")
|
| 72 |
+
if THUNDERX_USERNAME is None:
|
| 73 |
+
raise ValueError("请在环境变量中设置THUNDERX_USERNAME,用户名【邮箱】用来登陆!")
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
THUNDERX_PASSWORD = os.getenv("THUNDERX_PASSWORD")
|
| 77 |
+
if THUNDERX_PASSWORD is None:
|
| 78 |
+
raise ValueError("请在环境变量中设置THUNDERX_PASSWORD,密码用来登陆!")
|
| 79 |
+
|
| 80 |
+
PROXY_URL = os.getenv("PROXY_URL")
|
| 81 |
+
TG_BOT_TOKEN = os.getenv("TG_BOT_TOKEN")
|
| 82 |
+
TG_WEBHOOK_URL = os.getenv("TG_WEBHOOK_URL")
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
async def verify_token(
|
| 86 |
+
request: Request, credentials: HTTPAuthorizationCredentials = Depends(security)
|
| 87 |
+
):
|
| 88 |
+
# excluded_paths = ["/"] # 需要排除的路径列表
|
| 89 |
+
# if request.url.path in excluded_paths:
|
| 90 |
+
# return # 直接跳过验证
|
| 91 |
+
|
| 92 |
+
# 验证Bearer格式
|
| 93 |
+
if credentials.scheme != "Bearer":
|
| 94 |
+
raise HTTPException(
|
| 95 |
+
status_code=status.HTTP_401_UNAUTHORIZED,
|
| 96 |
+
detail="Invalid authentication scheme",
|
| 97 |
+
)
|
| 98 |
+
|
| 99 |
+
# 验证令牌内容
|
| 100 |
+
if credentials.credentials != SECRET_TOKEN:
|
| 101 |
+
raise HTTPException(
|
| 102 |
+
status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token"
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def format_bytes(size: int) -> str:
|
| 107 |
+
# 预设单位
|
| 108 |
+
units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
| 109 |
+
|
| 110 |
+
# 确保字节数是正数
|
| 111 |
+
if size < 0:
|
| 112 |
+
raise ValueError("字节大小不能为负数")
|
| 113 |
+
|
| 114 |
+
# 选择合适的单位
|
| 115 |
+
unit_index = 0
|
| 116 |
+
while size >= 1024 and unit_index < len(units) - 1:
|
| 117 |
+
size /= 1024.0
|
| 118 |
+
unit_index += 1
|
| 119 |
+
|
| 120 |
+
# 格式化输出,保留两位小数
|
| 121 |
+
return f"{size:.2f} {units[unit_index]}"
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
app = FastAPI()
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
app.add_middleware(
|
| 128 |
+
CORSMiddleware,
|
| 129 |
+
allow_origins=["*"],
|
| 130 |
+
allow_credentials=True,
|
| 131 |
+
allow_methods=["*"],
|
| 132 |
+
allow_headers=["*"],
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
api_router = APIRouter(dependencies=[Depends(verify_token)])
|
| 136 |
+
front_router = APIRouter()
|
| 137 |
+
|
| 138 |
+
templates = Jinja2Templates(
|
| 139 |
+
directory="templates", variable_start_string="{[", variable_end_string="]}"
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
async def log_token(THUNDERX_CLIENT, extra_data):
|
| 144 |
+
logging.info(f"Token: {THUNDERX_CLIENT.encoded_token}, Extra Data: {extra_data}")
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
THUNDERX_CLIENT = None
|
| 148 |
+
TG_BOT_APPLICATION = None
|
| 149 |
+
TG_BASE_URL = "https://tg.alist.dpdns.org/bot"
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
###################TG机器人功能区###################
|
| 153 |
+
# ❗❗❗❗❗❗❗❗❗注意TG机器人callbackdata不能超过64位,否则会报无效按钮的错误
|
| 154 |
+
# 定义命令处理函数
|
| 155 |
+
async def start(update: Update, context):
|
| 156 |
+
commands = (
|
| 157 |
+
"🚀欢迎使用我的机器人!\n\n"
|
| 158 |
+
"📋可用命令:\n"
|
| 159 |
+
"•直接发送magent:开头的磁力将直接离线下载\n"
|
| 160 |
+
"•直接发送分享码:开头的分享ID将直接离线下载\n"
|
| 161 |
+
"•/tasks - 查看下载任务\n"
|
| 162 |
+
"•/files - 查看文件列表\n"
|
| 163 |
+
"•/shares - 查看分享列表\n"
|
| 164 |
+
"•/quota - 查看存储空间\n"
|
| 165 |
+
"•/emptytrash - 清空回收站\n"
|
| 166 |
+
"•/help - 获取帮助信息\n"
|
| 167 |
+
)
|
| 168 |
+
await update.message.reply_text(commands)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
async def help(update: Update, context):
|
| 172 |
+
commands = (
|
| 173 |
+
"🚀欢迎使用我的机器人!\n\n"
|
| 174 |
+
"📋可用命令:\n"
|
| 175 |
+
"•直接发送magent:开头的磁力将直接离线下载\n"
|
| 176 |
+
"•直接发送分享码:开头的分享ID将直接离线下载\n"
|
| 177 |
+
"•/tasks - 查看下载任务\n"
|
| 178 |
+
"•/files - 查看文件列表\n"
|
| 179 |
+
"•/shares - 查看分享列表\n"
|
| 180 |
+
"•/quota - 查看存储空间\n"
|
| 181 |
+
"•/emptytrash - 清空回收站\n"
|
| 182 |
+
"•/help - 获取帮助信息\n"
|
| 183 |
+
)
|
| 184 |
+
await update.message.reply_text(commands)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
async def quota(update: Update, context):
|
| 188 |
+
"""
|
| 189 |
+
返回信息
|
| 190 |
+
{
|
| 191 |
+
"kind": "drive#about",
|
| 192 |
+
"quota": {
|
| 193 |
+
"kind": "drive#quota",
|
| 194 |
+
"limit": "72057604737418240",
|
| 195 |
+
"usage": "18700975438",
|
| 196 |
+
"usage_in_trash": "0",
|
| 197 |
+
"play_times_limit": "2",
|
| 198 |
+
"play_times_usage": "0",
|
| 199 |
+
"is_unlimited": true
|
| 200 |
+
},
|
| 201 |
+
"expires_at": "2026-04-08T21:47:59.000+08:00",
|
| 202 |
+
"quotas": {}
|
| 203 |
+
}
|
| 204 |
+
"""
|
| 205 |
+
quota_info = await THUNDERX_CLIENT.get_quota_info()
|
| 206 |
+
if quota_info["quota"]["usage"] is None:
|
| 207 |
+
await update.message.reply_text("❌未找到使用信息,请稍后再试!")
|
| 208 |
+
else:
|
| 209 |
+
await update.message.reply_text(
|
| 210 |
+
f"✅使用信息:\n{format_bytes(int(quota_info['quota']['usage']))}/{format_bytes(int(quota_info['quota']['limit']))}\n⏰到期时间:\n{quota_info['expires_at']}"
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
async def tg_emptytrash(update: Update, context):
|
| 215 |
+
"""
|
| 216 |
+
返回信息
|
| 217 |
+
"""
|
| 218 |
+
result = await THUNDERX_CLIENT.emptytrash()
|
| 219 |
+
if result["task_id"] is None:
|
| 220 |
+
await update.message.reply_text("❌未成功创建任务,请稍后重试!!")
|
| 221 |
+
else:
|
| 222 |
+
await update.message.reply_text(f"✅操作成功")
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
# 消息处理
|
| 226 |
+
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
| 227 |
+
text = update.message.text
|
| 228 |
+
if text.lower().startswith("magnet:"):
|
| 229 |
+
result = await THUNDERX_CLIENT.offline_download(text, "", "")
|
| 230 |
+
if result["task"]["id"] is not None:
|
| 231 |
+
await update.message.reply_text(f"✅操作成功")
|
| 232 |
+
else:
|
| 233 |
+
await update.message.reply_text(f"❌未成功创建任务,请稍后重试!!")
|
| 234 |
+
elif text.lower().startswith("分享码:"):
|
| 235 |
+
share_id = text.split(":")[1]
|
| 236 |
+
result = await THUNDERX_CLIENT.restore(share_id, None, None)
|
| 237 |
+
if isinstance(result, str):
|
| 238 |
+
await update.message.reply_text(f"❌未成功创建任务:{result},请稍后重试!!")
|
| 239 |
+
else:
|
| 240 |
+
await update.message.reply_text(f"操作结果:{result['restore_status']}")
|
| 241 |
+
|
| 242 |
+
else:
|
| 243 |
+
await update.message.reply_text(f"收到不支持的消息:{text}")
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
# 消息处理
|
| 247 |
+
async def handle_copy_text(update: Update, context: CallbackContext):
|
| 248 |
+
query = update.callback_query
|
| 249 |
+
await query.answer()
|
| 250 |
+
|
| 251 |
+
# 获取操作类型和文件 ID
|
| 252 |
+
action, text = (query.data.split(":")[0], query.data.split(":", 1)[1])
|
| 253 |
+
await query.edit_message_text(f"{text}")
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
#################### 分享操作 #############################
|
| 257 |
+
async def tg_show_shares(update: Update, context: CallbackContext):
|
| 258 |
+
shares = await THUNDERX_CLIENT.get_share_list("")
|
| 259 |
+
keyboard = []
|
| 260 |
+
|
| 261 |
+
if shares["data"] is None:
|
| 262 |
+
await update.message.reply_text("❌未找到分享!!")
|
| 263 |
+
else:
|
| 264 |
+
# 为每个文件创建按钮和操作选项
|
| 265 |
+
for share in shares["data"]:
|
| 266 |
+
keyboard.append(
|
| 267 |
+
[
|
| 268 |
+
InlineKeyboardButton(
|
| 269 |
+
f"{share['title']}",
|
| 270 |
+
callback_data=f"copy_text:{share['share_id']}",
|
| 271 |
+
),
|
| 272 |
+
InlineKeyboardButton(
|
| 273 |
+
f"{share['share_id']}",
|
| 274 |
+
callback_data=f"copy_text:分享码:{share['share_id']}",
|
| 275 |
+
),
|
| 276 |
+
InlineKeyboardButton(
|
| 277 |
+
f"取消",
|
| 278 |
+
callback_data=f"del_s:{share['share_id']}",
|
| 279 |
+
),
|
| 280 |
+
]
|
| 281 |
+
)
|
| 282 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 283 |
+
await update.message.reply_text(f"📋分享列表:", reply_markup=reply_markup)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
# 处理任务操作的回调
|
| 287 |
+
async def handle_share_operation(update: Update, context: CallbackContext):
|
| 288 |
+
query = update.callback_query
|
| 289 |
+
await query.answer()
|
| 290 |
+
|
| 291 |
+
# 获取操作类型和文件 ID
|
| 292 |
+
action, share_id = (query.data.split(":")[0], query.data.split(":")[1])
|
| 293 |
+
|
| 294 |
+
# 需要确认的操作
|
| 295 |
+
if action in ["del_s"]:
|
| 296 |
+
# 生成确认消息
|
| 297 |
+
keyboard = [
|
| 298 |
+
[InlineKeyboardButton("确认", callback_data=f"yes_s_{action}:{share_id}")],
|
| 299 |
+
[InlineKeyboardButton("取消", callback_data=f"no_s_{action}:{share_id}")],
|
| 300 |
+
]
|
| 301 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 302 |
+
await query.edit_message_text(
|
| 303 |
+
f"你确定要{action}分享 {share_id} 吗?", reply_markup=reply_markup
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
async def handle_share_confirmation(update: Update, context: CallbackContext):
|
| 308 |
+
query = update.callback_query
|
| 309 |
+
await query.answer()
|
| 310 |
+
|
| 311 |
+
# 获取确认操作的类型和文件 ID
|
| 312 |
+
action, share_id = (query.data.split(":")[0], query.data.split(":")[1])
|
| 313 |
+
|
| 314 |
+
if action == "yes_s_del_s":
|
| 315 |
+
await THUNDERX_CLIENT.share_batch_delete([share_id])
|
| 316 |
+
await query.edit_message_text(f"✅分享 {share_id} 已取消。")
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
async def handle_share_cancel(update: Update, context: CallbackContext):
|
| 320 |
+
query = update.callback_query
|
| 321 |
+
await query.answer()
|
| 322 |
+
await query.edit_message_text(f"操作已取消")
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
#################### 文件操作 #############################
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
async def tg_show_files(update: Update, context: CallbackContext):
|
| 329 |
+
files = await THUNDERX_CLIENT.file_list(100, "", "", {})
|
| 330 |
+
keyboard = []
|
| 331 |
+
|
| 332 |
+
if files["files"] is None:
|
| 333 |
+
await update.message.reply_text("❌未找到文件!!")
|
| 334 |
+
else:
|
| 335 |
+
# 为每个文件创建按钮和操作选项
|
| 336 |
+
for file in files["files"]:
|
| 337 |
+
if file["kind"].lower() == "drive#folder":
|
| 338 |
+
keyboard.append(
|
| 339 |
+
[
|
| 340 |
+
InlineKeyboardButton(
|
| 341 |
+
f"查看📁: {file['name']}",
|
| 342 |
+
callback_data=f"ls_f:{file['id']}:{file['parent_id']}",
|
| 343 |
+
),
|
| 344 |
+
InlineKeyboardButton(
|
| 345 |
+
f"删除",
|
| 346 |
+
callback_data=f"del_f:{file['id']}:{file['parent_id']}",
|
| 347 |
+
),
|
| 348 |
+
InlineKeyboardButton(
|
| 349 |
+
f"分享",
|
| 350 |
+
callback_data=f"sh_f:{file['id']}:{file['parent_id']}",
|
| 351 |
+
),
|
| 352 |
+
]
|
| 353 |
+
)
|
| 354 |
+
else:
|
| 355 |
+
keyboard.append(
|
| 356 |
+
[
|
| 357 |
+
InlineKeyboardButton(
|
| 358 |
+
f"下载📄: {file['name']}",
|
| 359 |
+
callback_data=f"dw_f:{file['id']}:{file['parent_id']}",
|
| 360 |
+
),
|
| 361 |
+
InlineKeyboardButton(
|
| 362 |
+
f"删除",
|
| 363 |
+
callback_data=f"del_f:{file['id']}:{file['parent_id']}",
|
| 364 |
+
),
|
| 365 |
+
InlineKeyboardButton(
|
| 366 |
+
f"分享",
|
| 367 |
+
callback_data=f"sh_f:{file['id']}:{file['parent_id']}",
|
| 368 |
+
),
|
| 369 |
+
]
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 373 |
+
await update.message.reply_text(f"📋文件列表:", reply_markup=reply_markup)
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
async def handle_file_confirmation(update: Update, context: CallbackContext):
|
| 377 |
+
query = update.callback_query
|
| 378 |
+
await query.answer()
|
| 379 |
+
|
| 380 |
+
# 获取确认操作的类型和文件 ID
|
| 381 |
+
action, file_id = (query.data.split(":")[0], query.data.split(":")[1])
|
| 382 |
+
|
| 383 |
+
if action == "yes_f_del_f":
|
| 384 |
+
await THUNDERX_CLIENT.delete_forever([file_id])
|
| 385 |
+
await query.edit_message_text(f"✅文件 {file_id} 已删除。")
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
async def handle_file_cancel(update: Update, context: CallbackContext):
|
| 389 |
+
query = update.callback_query
|
| 390 |
+
await query.answer()
|
| 391 |
+
# 获取取消操作的类型和文件 ID
|
| 392 |
+
# action, file_id, parent_id = (
|
| 393 |
+
# query.data.split(":")[0],
|
| 394 |
+
# query.data.split(":")[1],
|
| 395 |
+
# query.data.split(":")[2],
|
| 396 |
+
# )
|
| 397 |
+
# 返回文件夹列表
|
| 398 |
+
await query.edit_message_text(f"操作已取消")
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
# 处理任务操作的回调
|
| 402 |
+
async def handle_file_operation(update: Update, context: CallbackContext):
|
| 403 |
+
query = update.callback_query
|
| 404 |
+
await query.answer()
|
| 405 |
+
|
| 406 |
+
# 获取操作类型和文件 ID
|
| 407 |
+
action, file_id, parent_id = (
|
| 408 |
+
query.data.split(":")[0],
|
| 409 |
+
query.data.split(":")[1],
|
| 410 |
+
query.data.split(":")[2],
|
| 411 |
+
)
|
| 412 |
+
|
| 413 |
+
# 需要确认的操作
|
| 414 |
+
if action in ["del_f"]:
|
| 415 |
+
# 生成确认消息
|
| 416 |
+
keyboard = [
|
| 417 |
+
[InlineKeyboardButton("确认", callback_data=f"yes_f_{action}:{file_id}")],
|
| 418 |
+
[InlineKeyboardButton("取消", callback_data=f"no_f_{action}:{file_id}")],
|
| 419 |
+
]
|
| 420 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 421 |
+
await query.edit_message_text(
|
| 422 |
+
f"你确定要{action}文件 {file_id} 吗?", reply_markup=reply_markup
|
| 423 |
+
)
|
| 424 |
+
else:
|
| 425 |
+
# 不需要确认的操作,直接处理
|
| 426 |
+
await perform_file_action(update, context, action, file_id, parent_id)
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
async def perform_file_action(
|
| 430 |
+
update: Update, context: CallbackContext, action: str, file_id: str, parent_id: str
|
| 431 |
+
):
|
| 432 |
+
|
| 433 |
+
if action == "ls_f":
|
| 434 |
+
files = await THUNDERX_CLIENT.file_list(100, file_id, "", {})
|
| 435 |
+
keyboard = []
|
| 436 |
+
|
| 437 |
+
if files["files"] is None:
|
| 438 |
+
await update.message.reply_text("❌未找到文件!!")
|
| 439 |
+
else:
|
| 440 |
+
keyboard.append(
|
| 441 |
+
[
|
| 442 |
+
InlineKeyboardButton(
|
| 443 |
+
f"↩️返回���级",
|
| 444 |
+
callback_data=f"ls_f:{parent_id}:{parent_id}",
|
| 445 |
+
),
|
| 446 |
+
]
|
| 447 |
+
)
|
| 448 |
+
# 为每个文件创建按钮和操作选项
|
| 449 |
+
for file in files["files"]:
|
| 450 |
+
if file["kind"].lower() == "drive#folder":
|
| 451 |
+
keyboard.append(
|
| 452 |
+
[
|
| 453 |
+
InlineKeyboardButton(
|
| 454 |
+
f"查看📁: {file['name']}",
|
| 455 |
+
callback_data=f"ls_f:{file['id']}:{file['parent_id']}",
|
| 456 |
+
),
|
| 457 |
+
InlineKeyboardButton(
|
| 458 |
+
f"删除",
|
| 459 |
+
callback_data=f"del_f:{file['id']}:{file['parent_id']}",
|
| 460 |
+
),
|
| 461 |
+
InlineKeyboardButton(
|
| 462 |
+
f"分享",
|
| 463 |
+
callback_data=f"sh_f:{file['id']}:{file['parent_id']}",
|
| 464 |
+
),
|
| 465 |
+
]
|
| 466 |
+
)
|
| 467 |
+
else:
|
| 468 |
+
keyboard.append(
|
| 469 |
+
[
|
| 470 |
+
InlineKeyboardButton(
|
| 471 |
+
f"下载📄: {file['name']}",
|
| 472 |
+
callback_data=f"dw_f:{file['id']}:{file['parent_id']}",
|
| 473 |
+
),
|
| 474 |
+
InlineKeyboardButton(
|
| 475 |
+
f"删除",
|
| 476 |
+
callback_data=f"del_f:{file['id']}:{file['parent_id']}",
|
| 477 |
+
),
|
| 478 |
+
InlineKeyboardButton(
|
| 479 |
+
f"分享",
|
| 480 |
+
callback_data=f"sh_f:{file['id']}:{file['parent_id']}",
|
| 481 |
+
),
|
| 482 |
+
]
|
| 483 |
+
)
|
| 484 |
+
|
| 485 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 486 |
+
# await update.message.reply_text(f"📋文件列表:", reply_markup=reply_markup)
|
| 487 |
+
await update.callback_query.edit_message_text(
|
| 488 |
+
f"📋文件列表:", reply_markup=reply_markup
|
| 489 |
+
)
|
| 490 |
+
elif action == "dw_f":
|
| 491 |
+
result = await THUNDERX_CLIENT.get_download_url(file_id)
|
| 492 |
+
download_url = result["web_content_link"]
|
| 493 |
+
for media in result["medias"]:
|
| 494 |
+
if media["link"]["url"] is not None:
|
| 495 |
+
download_url = media["link"]["url"]
|
| 496 |
+
break
|
| 497 |
+
if download_url is not None:
|
| 498 |
+
await update.callback_query.edit_message_text(f"{download_url}")
|
| 499 |
+
else:
|
| 500 |
+
await update.callback_query.edit_message_text(f"❌未找到文件下载地址!!")
|
| 501 |
+
elif action == "sh_f":
|
| 502 |
+
result = await THUNDERX_CLIENT.file_batch_share([file_id], False, -1)
|
| 503 |
+
share_id = result["share_id"]
|
| 504 |
+
if share_id is not None:
|
| 505 |
+
await update.callback_query.edit_message_text(f"分享码:{share_id}")
|
| 506 |
+
else:
|
| 507 |
+
await update.callback_query.edit_message_text(f"❌分享失败!!")
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
#################### 离线任务处理 ##########################
|
| 511 |
+
# 确认操作的回调
|
| 512 |
+
async def handle_task_confirmation(update: Update, context: CallbackContext):
|
| 513 |
+
query = update.callback_query
|
| 514 |
+
await query.answer()
|
| 515 |
+
|
| 516 |
+
# 获取确认操作的类型和文件 ID
|
| 517 |
+
action, task_id = query.data.split(":")[0], query.data.split(":")[1]
|
| 518 |
+
|
| 519 |
+
if action == "confirm_task_delete_task":
|
| 520 |
+
await THUNDERX_CLIENT.delete_tasks([task_id])
|
| 521 |
+
await query.edit_message_text(f"✅任务 {task_id} 已删除。")
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
async def handle_task_cancel(update: Update, context: CallbackContext):
|
| 525 |
+
query = update.callback_query
|
| 526 |
+
await query.answer()
|
| 527 |
+
# 获取取消操作的类型和文件 ID
|
| 528 |
+
action, file_id = query.data.split(":")[0], query.data.split(":")[1]
|
| 529 |
+
# 返回文件夹列表
|
| 530 |
+
await query.edit_message_text(f"操作已取消")
|
| 531 |
+
|
| 532 |
+
|
| 533 |
+
async def tg_show_task(update: Update, context: CallbackContext):
|
| 534 |
+
"""
|
| 535 |
+
{
|
| 536 |
+
"tasks": [
|
| 537 |
+
{
|
| 538 |
+
"kind": "drive#task",
|
| 539 |
+
"id": "VONrJ4Skj4Qs7ALhxXlFudfJAA",
|
| 540 |
+
"name": "Billy Elliot (2000) 1080p (Deep61)[TGx]",
|
| 541 |
+
"type": "offline",
|
| 542 |
+
"user_id": "2000403406",
|
| 543 |
+
"statuses": [],
|
| 544 |
+
"status_size": 2,
|
| 545 |
+
"params": {
|
| 546 |
+
"folder_type": "",
|
| 547 |
+
"predict_type": "1",
|
| 548 |
+
"url": "magnet:?xt=urn:btih:96451E6F1ADBC8827B43621B74EDB30DF45012D6"
|
| 549 |
+
},
|
| 550 |
+
"file_id": "VONrJ4dZ8zf9KVWQuVEKmW8sTT",
|
| 551 |
+
"file_name": "Billy Elliot (2000) 1080p (Deep61)[TGx]",
|
| 552 |
+
"file_size": "3748030421",
|
| 553 |
+
"message": "Task timeout",
|
| 554 |
+
"created_time": "2025-04-15T10:38:54.320+08:00",
|
| 555 |
+
"updated_time": "2025-04-17T10:39:12.189+08:00",
|
| 556 |
+
"third_task_id": "",
|
| 557 |
+
"phase": "PHASE_TYPE_ERROR",
|
| 558 |
+
"progress": 0,
|
| 559 |
+
"icon_link": "https://backstage-img.xunleix.com/65d616355857aef8af40b89f187a8cf2770cb0ce",
|
| 560 |
+
"callback": "",
|
| 561 |
+
"reference_resource": {
|
| 562 |
+
"@type": "type.googleapis.com/drive.ReferenceFile",
|
| 563 |
+
"kind": "drive#folder",
|
| 564 |
+
"id": "VONrJ4dZ8zf9KVWQuVEKmW8sTT",
|
| 565 |
+
"parent_id": "VONS0fwXf3FNvt-g_IlMVKPxAA",
|
| 566 |
+
"name": "Billy Elliot (2000) 1080p (Deep61)[TGx]",
|
| 567 |
+
"size": "3748030421",
|
| 568 |
+
"mime_type": "",
|
| 569 |
+
"icon_link": "https://backstage-img.xunleix.com/65d616355857aef8af40b89f187a8cf2770cb0ce",
|
| 570 |
+
"hash": "",
|
| 571 |
+
"phase": "PHASE_TYPE_ERROR",
|
| 572 |
+
"audit": null,
|
| 573 |
+
"thumbnail_link": "",
|
| 574 |
+
"params": {},
|
| 575 |
+
"space": "",
|
| 576 |
+
"medias": [],
|
| 577 |
+
"starred": false,
|
| 578 |
+
"tags": []
|
| 579 |
+
},
|
| 580 |
+
"space": ""
|
| 581 |
+
}
|
| 582 |
+
],
|
| 583 |
+
"next_page_token": "",
|
| 584 |
+
"expires_in": 60,
|
| 585 |
+
"expires_in_ms": 60000
|
| 586 |
+
}
|
| 587 |
+
"""
|
| 588 |
+
tasks = await THUNDERX_CLIENT.offline_list(
|
| 589 |
+
size=100,
|
| 590 |
+
next_page_token=None,
|
| 591 |
+
phase=None,
|
| 592 |
+
)
|
| 593 |
+
keyboard = []
|
| 594 |
+
|
| 595 |
+
if tasks["tasks"] is None:
|
| 596 |
+
await update.message.reply_text("❌未找到任务!!")
|
| 597 |
+
else:
|
| 598 |
+
# 为每个文件创建按钮和操作选项
|
| 599 |
+
for task in tasks["tasks"]:
|
| 600 |
+
# 为每个文件添加操作按钮:删除
|
| 601 |
+
keyboard.append(
|
| 602 |
+
[
|
| 603 |
+
InlineKeyboardButton(
|
| 604 |
+
f"取消任务: {task['name']}",
|
| 605 |
+
callback_data=f"delete_task:{task['id']}",
|
| 606 |
+
),
|
| 607 |
+
]
|
| 608 |
+
)
|
| 609 |
+
|
| 610 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 611 |
+
await update.message.reply_text(f"📋任务列表:", reply_markup=reply_markup)
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
# 处理任务操作的回调
|
| 615 |
+
async def handle_tasks_operation(update: Update, context: CallbackContext):
|
| 616 |
+
query = update.callback_query
|
| 617 |
+
await query.answer()
|
| 618 |
+
|
| 619 |
+
# 获取操作类型和文件 ID
|
| 620 |
+
action, task_id = query.data.split(":")
|
| 621 |
+
|
| 622 |
+
# 需要确认的操作
|
| 623 |
+
if action in ["delete_task"]:
|
| 624 |
+
# 生成确认消息
|
| 625 |
+
keyboard = [
|
| 626 |
+
[
|
| 627 |
+
InlineKeyboardButton(
|
| 628 |
+
"确认", callback_data=f"confirm_task_{action}:{task_id}"
|
| 629 |
+
)
|
| 630 |
+
],
|
| 631 |
+
[
|
| 632 |
+
InlineKeyboardButton(
|
| 633 |
+
"取消", callback_data=f"cancel_task_{action}:{task_id}"
|
| 634 |
+
)
|
| 635 |
+
],
|
| 636 |
+
]
|
| 637 |
+
reply_markup = InlineKeyboardMarkup(keyboard)
|
| 638 |
+
await query.edit_message_text(
|
| 639 |
+
f"你确定要{action}任务 {task_id} 吗?", reply_markup=reply_markup
|
| 640 |
+
)
|
| 641 |
+
else:
|
| 642 |
+
# 不需要确认的操作,直接处理
|
| 643 |
+
await perform_task_action(update, context, action, task_id)
|
| 644 |
+
|
| 645 |
+
|
| 646 |
+
async def perform_task_action(
|
| 647 |
+
update: Update, context: CallbackContext, action: str, file_id: str
|
| 648 |
+
):
|
| 649 |
+
if action == "cancel_task":
|
| 650 |
+
await update.callback_query.edit_message_text(f"你选择了取消任务:{file_id}")
|
| 651 |
+
|
| 652 |
+
|
| 653 |
+
@app.on_event("startup")
|
| 654 |
+
async def init_client():
|
| 655 |
+
global THUNDERX_CLIENT
|
| 656 |
+
global TG_BOT_APPLICATION
|
| 657 |
+
if not os.path.exists("thunderx.txt"):
|
| 658 |
+
THUNDERX_CLIENT = PikPakApi(
|
| 659 |
+
username=THUNDERX_USERNAME,
|
| 660 |
+
password=THUNDERX_PASSWORD,
|
| 661 |
+
httpx_client_args=None,
|
| 662 |
+
token_refresh_callback=log_token,
|
| 663 |
+
token_refresh_callback_kwargs={"extra_data": "test"},
|
| 664 |
+
)
|
| 665 |
+
await THUNDERX_CLIENT.login()
|
| 666 |
+
await THUNDERX_CLIENT.refresh_access_token()
|
| 667 |
+
with open("thunderx.json", "w") as f:
|
| 668 |
+
f.write(json.dumps(THUNDERX_CLIENT.to_dict(), indent=4))
|
| 669 |
+
else:
|
| 670 |
+
with open("thunderx.txt", "r") as f:
|
| 671 |
+
data = json.load(f)
|
| 672 |
+
THUNDERX_CLIENT = PikPakApi.from_dict(data)
|
| 673 |
+
# await client.refresh_access_token()
|
| 674 |
+
print(json.dumps(THUNDERX_CLIENT.get_user_info(), indent=4))
|
| 675 |
+
|
| 676 |
+
print(
|
| 677 |
+
json.dumps(
|
| 678 |
+
await THUNDERX_CLIENT.events(),
|
| 679 |
+
indent=4,
|
| 680 |
+
)
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
if TG_BOT_TOKEN is None:
|
| 684 |
+
print("未设置TG_BOT_TOKEN无法实现TG机器人功能!")
|
| 685 |
+
else:
|
| 686 |
+
TG_BOT_APPLICATION = (
|
| 687 |
+
Application.builder().base_url(TG_BASE_URL).token(TG_BOT_TOKEN).build()
|
| 688 |
+
)
|
| 689 |
+
# await TG_BOT_APPLICATION.bot.delete_webhook()
|
| 690 |
+
await TG_BOT_APPLICATION.bot.set_webhook(
|
| 691 |
+
url=TG_WEBHOOK_URL, allowed_updates=Update.ALL_TYPES
|
| 692 |
+
)
|
| 693 |
+
|
| 694 |
+
await TG_BOT_APPLICATION.bot.set_my_commands(
|
| 695 |
+
commands=[
|
| 696 |
+
("/start", "开始"),
|
| 697 |
+
("/tasks", "查看下载任务"),
|
| 698 |
+
("/files", "查看文件列表"),
|
| 699 |
+
("/shares", "查看分享列表"),
|
| 700 |
+
("/quota", "查看存储空间"),
|
| 701 |
+
("/emptytrash", "清空回收���"),
|
| 702 |
+
("/help", "获取帮助信息"),
|
| 703 |
+
]
|
| 704 |
+
)
|
| 705 |
+
TG_BOT_APPLICATION.add_handler(
|
| 706 |
+
CallbackQueryHandler(handle_tasks_operation, pattern="^delete_task:")
|
| 707 |
+
)
|
| 708 |
+
# 处理取消任务操作
|
| 709 |
+
TG_BOT_APPLICATION.add_handler(
|
| 710 |
+
CallbackQueryHandler(handle_task_cancel, pattern="^cancel_task")
|
| 711 |
+
)
|
| 712 |
+
# 处理确认操作(确认删除、复制等)
|
| 713 |
+
TG_BOT_APPLICATION.add_handler(
|
| 714 |
+
CallbackQueryHandler(handle_task_confirmation, pattern="^confirm_task")
|
| 715 |
+
)
|
| 716 |
+
|
| 717 |
+
########## 分享操作 ###############
|
| 718 |
+
TG_BOT_APPLICATION.add_handler(
|
| 719 |
+
CallbackQueryHandler(handle_share_operation, pattern="^del_s:")
|
| 720 |
+
)
|
| 721 |
+
# 处理取消任务操作
|
| 722 |
+
TG_BOT_APPLICATION.add_handler(
|
| 723 |
+
CallbackQueryHandler(handle_share_cancel, pattern="^no_s")
|
| 724 |
+
)
|
| 725 |
+
# 处理确认操作(确认删除、复制等)
|
| 726 |
+
TG_BOT_APPLICATION.add_handler(
|
| 727 |
+
CallbackQueryHandler(handle_share_confirmation, pattern="^yes_s")
|
| 728 |
+
)
|
| 729 |
+
|
| 730 |
+
########## 文件操作 ###############
|
| 731 |
+
|
| 732 |
+
TG_BOT_APPLICATION.add_handler(
|
| 733 |
+
CallbackQueryHandler(
|
| 734 |
+
handle_file_operation, pattern="^(del_f|ls_f|dw_f|sh_f):"
|
| 735 |
+
)
|
| 736 |
+
)
|
| 737 |
+
# 处理取消任务操作
|
| 738 |
+
TG_BOT_APPLICATION.add_handler(
|
| 739 |
+
CallbackQueryHandler(handle_file_cancel, pattern="^no_f")
|
| 740 |
+
)
|
| 741 |
+
# 处理确认操作(确认删除、复制等)
|
| 742 |
+
TG_BOT_APPLICATION.add_handler(
|
| 743 |
+
CallbackQueryHandler(handle_file_confirmation, pattern="^yes_f")
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("start", start))
|
| 747 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("help", help))
|
| 748 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("quota", quota))
|
| 749 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("emptytrash", tg_emptytrash))
|
| 750 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("tasks", tg_show_task))
|
| 751 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("files", tg_show_files))
|
| 752 |
+
TG_BOT_APPLICATION.add_handler(CommandHandler("shares", tg_show_shares))
|
| 753 |
+
# Message 消息处理相关的命令!
|
| 754 |
+
TG_BOT_APPLICATION.add_handler(MessageHandler(filters.TEXT, handle_message))
|
| 755 |
+
# 处理取消任务操作
|
| 756 |
+
TG_BOT_APPLICATION.add_handler(
|
| 757 |
+
CallbackQueryHandler(handle_copy_text, pattern="^copy_text")
|
| 758 |
+
)
|
| 759 |
+
await TG_BOT_APPLICATION.initialize()
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
# FastAPI 路由:接收来自 Telegram 的 Webhook 回调
|
| 763 |
+
@app.post("/webhook")
|
| 764 |
+
async def webhook(request: Request):
|
| 765 |
+
# 从请求获取 JSON 数据
|
| 766 |
+
data = await request.json()
|
| 767 |
+
|
| 768 |
+
# 将 Telegram Update 转换为 Update 对象
|
| 769 |
+
update = Update.de_json(data, TG_BOT_APPLICATION.bot)
|
| 770 |
+
|
| 771 |
+
# 将 Update 对象传递给 Application 进行处理
|
| 772 |
+
await TG_BOT_APPLICATION.process_update(update)
|
| 773 |
+
|
| 774 |
+
return JSONResponse({"status": "ok"})
|
| 775 |
+
|
| 776 |
+
|
| 777 |
+
@front_router.get(
|
| 778 |
+
"/",
|
| 779 |
+
response_class=HTMLResponse,
|
| 780 |
+
summary="前台页面",
|
| 781 |
+
description="前台管理页面,需要在设置里设置SECRET_TOKEN才能正常请求",
|
| 782 |
+
tags=["前端"],
|
| 783 |
+
)
|
| 784 |
+
async def home(request: Request):
|
| 785 |
+
return templates.TemplateResponse("index.html", {"request": request})
|
| 786 |
+
|
| 787 |
+
|
| 788 |
+
@api_router.post(
|
| 789 |
+
"/files", summary="文件列表", description="获取文件列表", tags=["文件"]
|
| 790 |
+
)
|
| 791 |
+
async def get_files(item: FileRequest):
|
| 792 |
+
return await THUNDERX_CLIENT.file_list(
|
| 793 |
+
item.size, item.parent_id, item.next_page_token, item.additional_filters
|
| 794 |
+
)
|
| 795 |
+
|
| 796 |
+
|
| 797 |
+
@api_router.post(
|
| 798 |
+
"/file_star_list", summary="加星文件列表", description="加星文件列表", tags=["文件"]
|
| 799 |
+
)
|
| 800 |
+
async def file_star_list(
|
| 801 |
+
size: int = Query(default=100, title="显示数量", description="显示数量"),
|
| 802 |
+
next_page_token: str | None = Query(
|
| 803 |
+
default=None, title="分页Token", description="分页Token"
|
| 804 |
+
),
|
| 805 |
+
):
|
| 806 |
+
return await THUNDERX_CLIENT.file_star_list(size, next_page_token)
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
@api_router.get(
|
| 810 |
+
"/files/{file_id}", summary="文件信息", description="获取文件信息", tags=["文件"]
|
| 811 |
+
)
|
| 812 |
+
async def get_file_info(file_id: str = Path(..., title="文件ID", description="文件ID")):
|
| 813 |
+
return await THUNDERX_CLIENT.get_download_url(file_id)
|
| 814 |
+
|
| 815 |
+
|
| 816 |
+
@api_router.delete(
|
| 817 |
+
"/files/{file_id}", summary="删除文件", description="删除文件", tags=["文件"]
|
| 818 |
+
)
|
| 819 |
+
async def delete_file_info(
|
| 820 |
+
file_id: str = Path(..., title="文件ID", description="文件ID")
|
| 821 |
+
):
|
| 822 |
+
return await THUNDERX_CLIENT.delete_forever([file_id])
|
| 823 |
+
|
| 824 |
+
|
| 825 |
+
@api_router.post(
|
| 826 |
+
"/file_rename", summary="重命名文件", description="重命名文件", tags=["文件"]
|
| 827 |
+
)
|
| 828 |
+
async def file_rename(
|
| 829 |
+
file_id: str = Query(title="文件ID", description="文件ID"),
|
| 830 |
+
new_file_name: str = Query(title="新文件名", description="新文件名"),
|
| 831 |
+
):
|
| 832 |
+
return await THUNDERX_CLIENT.file_rename(file_id, new_file_name)
|
| 833 |
+
|
| 834 |
+
|
| 835 |
+
@api_router.post(
|
| 836 |
+
"/file_batch_copy",
|
| 837 |
+
summary="批量复制文件",
|
| 838 |
+
description="批量复制文件",
|
| 839 |
+
tags=["文件"],
|
| 840 |
+
)
|
| 841 |
+
async def file_batch_copy(
|
| 842 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表"),
|
| 843 |
+
to_parent_id: str = Query(
|
| 844 |
+
title="复制到的文件夹id, 默认为根目录",
|
| 845 |
+
description="复制到的文件夹id, 默认为根目录",
|
| 846 |
+
),
|
| 847 |
+
):
|
| 848 |
+
return await THUNDERX_CLIENT.file_batch_copy(ids, to_parent_id)
|
| 849 |
+
|
| 850 |
+
|
| 851 |
+
@api_router.post(
|
| 852 |
+
"/file_batch_move",
|
| 853 |
+
summary="批量移动文件",
|
| 854 |
+
description="批量移动文件",
|
| 855 |
+
tags=["文件"],
|
| 856 |
+
)
|
| 857 |
+
async def file_batch_move(
|
| 858 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表"),
|
| 859 |
+
to_parent_id: str = Query(
|
| 860 |
+
title="移动到的文件夹id, 默认为根目录",
|
| 861 |
+
description="移动到的文件夹id, 默认为根目录",
|
| 862 |
+
),
|
| 863 |
+
):
|
| 864 |
+
return await THUNDERX_CLIENT.file_batch_move(ids, to_parent_id)
|
| 865 |
+
|
| 866 |
+
|
| 867 |
+
@api_router.post(
|
| 868 |
+
"/create_folder", summary="新建文件夹", description="新建文件夹", tags=["文件"]
|
| 869 |
+
)
|
| 870 |
+
async def create_folder(
|
| 871 |
+
name: str = Query(title="文件夹名称", description="文件夹名称"),
|
| 872 |
+
parent_id: str = Query(
|
| 873 |
+
title="父文件夹id, 默认创建到根目录", description="父文件夹id, 默认创建到根目录"
|
| 874 |
+
),
|
| 875 |
+
):
|
| 876 |
+
return await THUNDERX_CLIENT.create_folder(name, parent_id)
|
| 877 |
+
|
| 878 |
+
|
| 879 |
+
@api_router.post(
|
| 880 |
+
"/delete_to_trash",
|
| 881 |
+
summary="将文件夹、文件移动到回收站",
|
| 882 |
+
description="将文件夹、文件移动到回收站",
|
| 883 |
+
tags=["文件"],
|
| 884 |
+
)
|
| 885 |
+
async def delete_to_trash(
|
| 886 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表")
|
| 887 |
+
):
|
| 888 |
+
return await THUNDERX_CLIENT.delete_to_trash(ids)
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
@api_router.post(
|
| 892 |
+
"/delete_forever",
|
| 893 |
+
summary="将文件夹、文件彻底删除",
|
| 894 |
+
description="将文件夹、文件彻底删除",
|
| 895 |
+
tags=["文件"],
|
| 896 |
+
)
|
| 897 |
+
async def delete_forever(
|
| 898 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表")
|
| 899 |
+
):
|
| 900 |
+
return await THUNDERX_CLIENT.delete_forever(ids)
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
@api_router.post(
|
| 904 |
+
"/untrash",
|
| 905 |
+
summary="将文件夹、文件移出回收站",
|
| 906 |
+
description="将文件夹、文件移出回收站",
|
| 907 |
+
tags=["文件"],
|
| 908 |
+
)
|
| 909 |
+
async def untrash(ids: List[str] = Body(title="文件ID列表", description="文件ID列表")):
|
| 910 |
+
return await THUNDERX_CLIENT.untrash(ids)
|
| 911 |
+
|
| 912 |
+
|
| 913 |
+
@api_router.post(
|
| 914 |
+
"/file_batch_star",
|
| 915 |
+
summary="批量给文件加星标",
|
| 916 |
+
description="批量给文件加星标",
|
| 917 |
+
tags=["文件"],
|
| 918 |
+
)
|
| 919 |
+
async def file_batch_star(
|
| 920 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表")
|
| 921 |
+
):
|
| 922 |
+
return await THUNDERX_CLIENT.file_batch_star(ids)
|
| 923 |
+
|
| 924 |
+
|
| 925 |
+
@api_router.post(
|
| 926 |
+
"/file_batch_unstar",
|
| 927 |
+
summary="批量给文件加星标",
|
| 928 |
+
description="批量给文件加星标",
|
| 929 |
+
tags=["文件"],
|
| 930 |
+
)
|
| 931 |
+
async def file_batch_unstar(
|
| 932 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表")
|
| 933 |
+
):
|
| 934 |
+
return await THUNDERX_CLIENT.file_batch_unstar(ids)
|
| 935 |
+
|
| 936 |
+
|
| 937 |
+
@api_router.post(
|
| 938 |
+
"/emptytrash", summary="清空回收站", description="清空回收站【慎用】", tags=["文件"]
|
| 939 |
+
)
|
| 940 |
+
async def emptytrash():
|
| 941 |
+
return await THUNDERX_CLIENT.emptytrash()
|
| 942 |
+
|
| 943 |
+
|
| 944 |
+
############## 分享 ################
|
| 945 |
+
@api_router.post(
|
| 946 |
+
"/get_share_list",
|
| 947 |
+
summary="获取账号分享列表",
|
| 948 |
+
description="获取账号分享列表",
|
| 949 |
+
tags=["分享"],
|
| 950 |
+
)
|
| 951 |
+
async def get_share_list(
|
| 952 |
+
page_token: str | None = Query(
|
| 953 |
+
default=None, title="分页Token", description="分页Token"
|
| 954 |
+
)
|
| 955 |
+
):
|
| 956 |
+
return await THUNDERX_CLIENT.get_share_list(page_token)
|
| 957 |
+
|
| 958 |
+
|
| 959 |
+
@api_router.post(
|
| 960 |
+
"/file_batch_share", summary="创建分享", description="创建分享", tags=["分享"]
|
| 961 |
+
)
|
| 962 |
+
async def file_batch_share(
|
| 963 |
+
ids: List[str] = Body(default=None, title="文件ID列表", description="文件ID列表"),
|
| 964 |
+
need_password: bool | None = Query(
|
| 965 |
+
default=False, title="是否需要密码", description="是否需要密码"
|
| 966 |
+
),
|
| 967 |
+
expiration_days: int | None = Query(
|
| 968 |
+
default=-1, title="过期时间", description="过期时间【天数,默认永远】"
|
| 969 |
+
),
|
| 970 |
+
):
|
| 971 |
+
return await THUNDERX_CLIENT.file_batch_share(ids, need_password, expiration_days)
|
| 972 |
+
|
| 973 |
+
|
| 974 |
+
@api_router.post(
|
| 975 |
+
"/share_batch_delete", summary="取消分享", description="取消分享", tags=["分享"]
|
| 976 |
+
)
|
| 977 |
+
async def share_batch_delete(
|
| 978 |
+
ids: List[str] = Body(title="文件ID列表", description="文件ID列表")
|
| 979 |
+
):
|
| 980 |
+
return await THUNDERX_CLIENT.share_batch_delete(ids)
|
| 981 |
+
|
| 982 |
+
|
| 983 |
+
@api_router.post(
|
| 984 |
+
"/get_share_folder",
|
| 985 |
+
summary="获取分享信息",
|
| 986 |
+
description="获取分享信息",
|
| 987 |
+
tags=["分享"],
|
| 988 |
+
)
|
| 989 |
+
async def get_share_folder(
|
| 990 |
+
share_id: str = Query(title="分享ID", description="分享ID"),
|
| 991 |
+
pass_code_token: str | None = Query(default=None, title="密码", description="密码"),
|
| 992 |
+
parent_id: str | None = Query(default=None, title="父ID", description="父ID"),
|
| 993 |
+
):
|
| 994 |
+
return await THUNDERX_CLIENT.get_share_folder(share_id, pass_code_token, parent_id)
|
| 995 |
+
|
| 996 |
+
|
| 997 |
+
@api_router.post(
|
| 998 |
+
"/restore", summary="转存分享文件", description="转存分享文件", tags=["分享"]
|
| 999 |
+
)
|
| 1000 |
+
async def restore(
|
| 1001 |
+
share_id: str, pass_code_token: str | None = None, file_ids: List[str] | None = None
|
| 1002 |
+
):
|
| 1003 |
+
return await THUNDERX_CLIENT.restore(share_id, pass_code_token, file_ids)
|
| 1004 |
+
|
| 1005 |
+
|
| 1006 |
+
############## 离线任务 ################
|
| 1007 |
+
|
| 1008 |
+
|
| 1009 |
+
@api_router.get(
|
| 1010 |
+
"/offline", summary="离线任务列表", description="离线任务列表", tags=["离线任务"]
|
| 1011 |
+
)
|
| 1012 |
+
async def offline_list(size: int = 10000, next_page_token: str | None = None):
|
| 1013 |
+
return await THUNDERX_CLIENT.offline_list(
|
| 1014 |
+
size=size,
|
| 1015 |
+
next_page_token=next_page_token,
|
| 1016 |
+
phase=None,
|
| 1017 |
+
)
|
| 1018 |
+
|
| 1019 |
+
|
| 1020 |
+
@api_router.post(
|
| 1021 |
+
"/offline", summary="添加离线任务", description="添加离线任务", tags=["离线任务"]
|
| 1022 |
+
)
|
| 1023 |
+
async def offline(item: OfflineRequest):
|
| 1024 |
+
return await THUNDERX_CLIENT.offline_download(
|
| 1025 |
+
item.file_url, item.parent_id, item.name
|
| 1026 |
+
)
|
| 1027 |
+
|
| 1028 |
+
|
| 1029 |
+
@api_router.post(
|
| 1030 |
+
"/delete_tasks",
|
| 1031 |
+
summary="删除离线任务",
|
| 1032 |
+
description="删除离线任务",
|
| 1033 |
+
tags=["离线任务"],
|
| 1034 |
+
)
|
| 1035 |
+
async def delete_tasks(task_ids: List[str], delete_files: bool = False):
|
| 1036 |
+
return await THUNDERX_CLIENT.delete_tasks(task_ids, delete_files)
|
| 1037 |
+
|
| 1038 |
+
|
| 1039 |
+
############## 账号 ################
|
| 1040 |
+
@api_router.get(
|
| 1041 |
+
"/userinfo", summary="用户信息", description="获取用户登陆信息", tags=["账号"]
|
| 1042 |
+
)
|
| 1043 |
+
async def userinfo():
|
| 1044 |
+
return THUNDERX_CLIENT.get_user_info()
|
| 1045 |
+
|
| 1046 |
+
|
| 1047 |
+
@api_router.get(
|
| 1048 |
+
"/quota", summary="空间使用信息", description="获取空间使用信息", tags=["账号"]
|
| 1049 |
+
)
|
| 1050 |
+
async def quota_info():
|
| 1051 |
+
return await THUNDERX_CLIENT.get_quota_info()
|
| 1052 |
+
|
| 1053 |
+
|
| 1054 |
+
@api_router.get(
|
| 1055 |
+
"/invite_code", summary="查看邀请码", description="查看邀请码", tags=["账号"]
|
| 1056 |
+
)
|
| 1057 |
+
async def get_invite_code():
|
| 1058 |
+
return await THUNDERX_CLIENT.get_invite_code()
|
| 1059 |
+
|
| 1060 |
+
|
| 1061 |
+
app.include_router(front_router)
|
| 1062 |
+
app.include_router(api_router)
|
requirements.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
python-telegram-bot
|
| 3 |
+
pydantic-settings
|
| 4 |
+
requests
|
| 5 |
+
jinja2
|
| 6 |
+
aiofiles
|
| 7 |
+
uvicorn
|
| 8 |
+
sqlalchemy
|
| 9 |
+
fastapi-utilities
|
| 10 |
+
bs4
|
| 11 |
+
httpx
|
| 12 |
+
datetime
|
| 13 |
+
cachelib
|
| 14 |
+
python-multipart
|
| 15 |
+
hashids
|
| 16 |
+
asyncio
|