GmailTrust commited on
Commit
887eced
·
verified ·
1 Parent(s): ac03a83

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +274 -0
app.py ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ from uuid import UUID
4
+ from typing import Dict, Any, Optional
5
+ import gradio as gr
6
+ import os
7
+ import threading
8
+ import time
9
+ import pandas as pd
10
+
11
+ # Lấy ZGM API Token từ biến môi trường (Secrets trên Hugging Face)
12
+ ZGM_API_TOKEN = os.getenv("ZGM_API_TOKEN")
13
+ if not ZGM_API_TOKEN:
14
+ raise ValueError("ZGM_API_TOKEN không được tìm thấy trong biến môi trường!")
15
+
16
+ # Tải danh sách key từ nhiều biến môi trường
17
+ VALID_KEYS = set()
18
+ for i in range(1, 11): # Giả sử chia thành 10 phần, mỗi phần 1,000 key
19
+ key_part = os.getenv(f"ZGM_VALID_KEYS_{i}")
20
+ if key_part:
21
+ try:
22
+ VALID_KEYS.update(json.loads(key_part))
23
+ except json.JSONDecodeError:
24
+ raise ValueError(f"ZGM_VALID_KEYS_{i} không phải là chuỗi JSON hợp lệ!")
25
+ if not VALID_KEYS:
26
+ raise ValueError("Không tìm thấy key nào trong biến môi trường!")
27
+
28
+ # Danh sách dịch vụ cố định
29
+ SERVICES = [
30
+ "ICLOUD", "ZSK", "FACEBOOK", "TELEGRAM", "OTHERS",
31
+ "GGPLAY", "INSTAGRAM", "FMCPAY", "AMAZON"
32
+ ]
33
+
34
+ class ZGMApiClient:
35
+ """Lớp client để tương tác với ZGM API"""
36
+
37
+ def __init__(self, base_url: str, api_token: str):
38
+ self.base_url = base_url.rstrip('/')
39
+ self.api_token = api_token
40
+ self.headers = {
41
+ 'ZGM-API-TOKEN': api_token,
42
+ 'Content-Type': 'application/json',
43
+ 'Accept': 'text/plain'
44
+ }
45
+
46
+ def _make_request(self, method: str, endpoint: str, params: Dict = None, data: Dict = None) -> Dict:
47
+ url = f"{self.base_url}{endpoint}"
48
+ try:
49
+ response = requests.request(
50
+ method=method,
51
+ url=url,
52
+ headers=self.headers,
53
+ params=params,
54
+ json=data,
55
+ timeout=15
56
+ )
57
+ response.raise_for_status()
58
+ return response.json()
59
+ except requests.exceptions.ConnectTimeout:
60
+ return {"error": f"Không thể kết nối đến {self.base_url}. Vui lòng kiểm tra URL hoặc mạng.", "status_code": 0}
61
+ except requests.exceptions.HTTPError as e:
62
+ if response.status_code == 401:
63
+ return {"error": "Chưa đăng nhập. Vui lòng kiểm tra API Token.", "status_code": 401}
64
+ elif response.status_code == 500:
65
+ return {"error": "Lỗi server từ ZGM API.", "status_code": 500}
66
+ return {"error": f"Lỗi HTTP: {str(e)}", "status_code": response.status_code}
67
+ except requests.exceptions.RequestException as e:
68
+ return {"error": f"Lỗi kết nối: {str(e)}", "status_code": 0}
69
+
70
+ def rent_otp(self, service: str) -> Dict:
71
+ data = {"service": service}
72
+ return self._make_request('POST', '/api/mail/otp-rent', data=data)
73
+
74
+ def get_otp(self, order_id: str) -> Dict:
75
+ try:
76
+ UUID(order_id)
77
+ return self._make_request('GET', '/api/mail/otp-get', params={"orderId": order_id})
78
+ except ValueError:
79
+ return {"error": "order_id phải là UUID hợp lệ", "status_code": 400}
80
+
81
+ # Khởi tạo client
82
+ client = ZGMApiClient("https://gm.haivl.wtf", ZGM_API_TOKEN)
83
+
84
+ # Hàm xác thực và xóa key (chỉ xóa khi thuê thành công)
85
+ def validate_and_remove_key(access_key: str, result: Dict) -> tuple[bool, str]:
86
+ if len(VALID_KEYS) == 0:
87
+ return False, "Hết key! Vui lòng liên hệ quản trị viên để tạo thêm key."
88
+
89
+ if access_key not in VALID_KEYS:
90
+ return False, "Key không hợp lệ! Vui lòng nhập key hợp lệ để tiếp tục."
91
+
92
+ # Chỉ xóa key khi API trả về code 200 và có dữ liệu hợp lệ
93
+ if result.get("code") == 200 and result.get("data") and "orderId" in result["data"] and "email" in result["data"]:
94
+ VALID_KEYS.remove(access_key)
95
+ # Cập nhật lại các biến môi trường (chia nhỏ để tránh vượt giới hạn)
96
+ keys_list = list(VALID_KEYS)
97
+ chunk_size = 1000 # Mỗi phần 1,000 key
98
+ for i in range(1, 11):
99
+ start = (i - 1) * chunk_size
100
+ end = i * chunk_size
101
+ chunk = keys_list[start:end]
102
+ if chunk:
103
+ os.environ[f"ZGM_VALID_KEYS_{i}"] = json.dumps(chunk)
104
+ else:
105
+ os.environ.pop(f"ZGM_VALID_KEYS_{i}", None)
106
+ remaining_keys = len(VALID_KEYS)
107
+ warning = f"\nCòn lại {remaining_keys} key."
108
+ if remaining_keys < 100:
109
+ warning += " Số lượng key còn ít! Vui lòng tạo thêm key mới."
110
+ return True, warning
111
+ else:
112
+ return True, f"Lỗi: {result.get('message', 'Không thể thuê Gmail.')}\nKey không bị xóa."
113
+
114
+ # Hàm xử lý bất đồng bộ với chỉ báo tải
115
+ def execute_with_loading(func, *args, **kwargs):
116
+ result = [None]
117
+ is_loading = [True]
118
+
119
+ def target():
120
+ result[0] = func(*args, **kwargs)
121
+ is_loading[0] = False
122
+
123
+ api_thread = threading.Thread(target=target)
124
+ api_thread.start()
125
+
126
+ chars = "|/-\\"
127
+ i = 0
128
+ while is_loading[0]:
129
+ print(f"\rĐang tải {chars[i % len(chars)]}", end="")
130
+ i += 1
131
+ time.sleep(0.1)
132
+ print("\r", end="")
133
+
134
+ api_thread.join()
135
+ return result[0]
136
+
137
+ # Hàm định dạng kết quả "Thuê OTP" thành bảng (chỉ Order ID và Email)
138
+ def format_rent_otp_result(data: Dict) -> pd.DataFrame:
139
+ if not data or "data" not in data:
140
+ return pd.DataFrame([["Không có dữ liệu để hiển thị."]], columns=["Message"])
141
+ order_info = data["data"]
142
+ df = pd.DataFrame([{
143
+ "Order ID": order_info.get("orderId", "N/A"),
144
+ "Email": order_info.get("email", "N/A")
145
+ }])
146
+ return df
147
+
148
+ # Hàm định dạng kết quả "Lấy OTP" thành bảng (Service, Email, OTP Code, và nội dung thư)
149
+ def format_get_otp_result(data: Dict, selected_service: str) -> pd.DataFrame:
150
+ if not data or "data" not in data:
151
+ return pd.DataFrame([["Không có dữ liệu để hiển thị."]], columns=["Message"])
152
+
153
+ otp_data = data["data"]
154
+
155
+ # Nếu là đối tượng đơn (không phải danh sách)
156
+ if isinstance(otp_data, dict):
157
+ otp_data = [otp_data] # Chuyển thành danh sách để xử lý thống nhất
158
+
159
+ # Nếu không phải danh sách, dữ liệu không hợp lệ
160
+ if not isinstance(otp_data, list):
161
+ return pd.DataFrame([["Dữ liệu không hợp lệ."]], columns=["Message"])
162
+
163
+ df = pd.DataFrame([
164
+ {
165
+ "Service": selected_service, # Sử dụng dịch vụ đã chọn khi thuê
166
+ "Email": item.get("email", "N/A"), # Thêm email vào log
167
+ "OTP Code": item.get("otpCode", "N/A"),
168
+ "Content": item.get("otpContent", "N/A") # Nội dung thư từ API
169
+ }
170
+ for item in otp_data
171
+ ])
172
+ return df
173
+
174
+ # Hàm xóa tất cả log
175
+ def clear_all_logs():
176
+ return pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]), pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]), ""
177
+
178
+ # Hàm giao diện Gradio
179
+ def gradio_interface(access_key, action, service, order_id, rent_otp_log, get_otp_log):
180
+ message = ""
181
+ new_order_id = order_id # Giữ nguyên order_id nếu không có thay đổi
182
+
183
+ # Khởi tạo log nếu chưa có
184
+ if rent_otp_log is None:
185
+ rent_otp_log = pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"])
186
+ if get_otp_log is None:
187
+ get_otp_log = pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"])
188
+
189
+ # Chỉ yêu cầu key cho hành động "Thuê OTP"
190
+ if action == "Thuê OTP":
191
+ if not access_key:
192
+ rent_otp_log = pd.DataFrame([["Vui lòng nhập key để thực hiện Thuê OTP!"]], columns=["Message"])
193
+ return rent_otp_log, get_otp_log, message, order_id
194
+ if not service:
195
+ rent_otp_log = pd.DataFrame([["Vui lòng chọn dịch vụ!"]], columns=["Message"])
196
+ return rent_otp_log, get_otp_log, message, order_id
197
+ result = execute_with_loading(client.rent_otp, service)
198
+ is_valid, message = validate_and_remove_key(access_key, result)
199
+ if not is_valid:
200
+ rent_otp_log = pd.DataFrame([[message]], columns=["Message"])
201
+ return rent_otp_log, get_otp_log, message, order_id
202
+ if result.get("code") == 200:
203
+ rent_otp_log = format_rent_otp_result(result)
204
+ # Lấy Order ID mới để tự động điền
205
+ new_order_id = result["data"].get("orderId", order_id)
206
+ else:
207
+ rent_otp_log = pd.DataFrame([[json.dumps(result, indent=2, ensure_ascii=False)]], columns=["Message"])
208
+ return rent_otp_log, get_otp_log, message, new_order_id
209
+ elif action == "Lấy OTP":
210
+ if not order_id:
211
+ get_otp_log = pd.DataFrame([["Vui lòng nhập Order ID!"]], columns=["Message"])
212
+ return rent_otp_log, get_otp_log, "", order_id
213
+ result = execute_with_loading(client.get_otp, order_id)
214
+ if result.get("code") == 200 and "data" in result:
215
+ get_otp_log = format_get_otp_result(result, service) # Truyền service đã chọn
216
+ else:
217
+ get_otp_log = pd.DataFrame([[json.dumps(result, indent=2, ensure_ascii=False)]], columns=["Message"])
218
+ return rent_otp_log, get_otp_log, "", order_id
219
+ else:
220
+ return rent_otp_log, get_otp_log, "Hành động không hợp lệ!", order_id
221
+
222
+ # Tạo giao diện Gradio
223
+ with gr.Blocks() as demo:
224
+ gr.Markdown("## ZGM API Client - Nhập Key để Thuê OTP (Key chỉ cần thiết cho Thuê OTP, mỗi key dùng 1 lần)")
225
+
226
+ with gr.Row():
227
+ access_key = gr.Textbox(label="Nhập Key Truy Cập (Chỉ cần cho Thuê OTP)", placeholder="Nhập key 32 ký tự...")
228
+ action = gr.Dropdown(
229
+ label="Chọn Hành Động",
230
+ choices=["Thuê OTP", "Lấy OTP"],
231
+ value="Thuê OTP"
232
+ )
233
+
234
+ with gr.Row():
235
+ service = gr.Dropdown(label="Dịch Vụ (Cho Thuê OTP)", choices=SERVICES)
236
+ order_id = gr.Textbox(label="Order ID (Cho Lấy OTP)", placeholder="Nhập Order ID")
237
+
238
+ rent_otp_log = gr.State(value=pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]))
239
+ get_otp_log = gr.State(value=pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]))
240
+
241
+ with gr.Row():
242
+ rent_otp_output = gr.Dataframe(
243
+ label="Log Thuê OTP",
244
+ headers=["Order ID", "Email"],
245
+ datatype=["str", "str"],
246
+ interactive=False
247
+ )
248
+ get_otp_output = gr.Dataframe(
249
+ label="Log Lấy OTP",
250
+ headers=["Service", "Email", "OTP Code", "Content"],
251
+ datatype=["str", "str", "str", "str"],
252
+ interactive=False
253
+ )
254
+
255
+ message_output = gr.Textbox(label="Thông Báo", interactive=False)
256
+
257
+ with gr.Row():
258
+ submit_btn = gr.Button("Thực Thi", variant="primary")
259
+ clear_btn = gr.Button("Xóa Log", variant="secondary")
260
+
261
+ submit_btn.click(
262
+ fn=gradio_interface,
263
+ inputs=[access_key, action, service, order_id, rent_otp_log, get_otp_log],
264
+ outputs=[rent_otp_output, get_otp_output, message_output, order_id]
265
+ )
266
+
267
+ clear_btn.click(
268
+ fn=clear_all_logs,
269
+ inputs=None,
270
+ outputs=[rent_otp_output, get_otp_output, message_output]
271
+ )
272
+
273
+ # Khởi chạy giao diện
274
+ demo.launch()