GmailTrust commited on
Commit
1db45fe
·
verified ·
1 Parent(s): 109d977

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -125
app.py CHANGED
@@ -1,28 +1,27 @@
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
- # Lấy địa chỉ IP của VPS từ Secrets
17
  VPS_IP = os.getenv("VPS_IP")
18
  if not VPS_IP:
19
- raise ValueError("VPS_IP không được tìm thấy trong biến môi trường!")
20
 
21
- # URL của API trên VPS (dùng port 3000)
22
  KEY_API_URL = f"http://{VPS_IP}:3000"
23
 
24
  class ZGMApiClient:
25
- """Lớp client để tương tác với ZGM API"""
26
 
27
  def __init__(self, base_url: str, api_token: str):
28
  self.base_url = base_url.rstrip('/')
@@ -47,82 +46,75 @@ class ZGMApiClient:
47
  response.raise_for_status()
48
  return response.json()
49
  except requests.exceptions.ConnectTimeout:
50
- 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}
51
  except requests.exceptions.HTTPError as e:
52
  if response.status_code == 401:
53
- return {"error": "Chưa đăng nhập. Vui lòng kiểm tra API Token.", "status_code": 401}
54
  elif response.status_code == 500:
55
- return {"error": "Lỗi server từ ZGM API.", "status_code": 500}
56
- return {"error": f"Lỗi HTTP: {str(e)}", "status_code": response.status_code}
57
  except requests.exceptions.RequestException as e:
58
- return {"error": f"Lỗi kết nối: {str(e)}", "status_code": 0}
59
 
60
- def rent_otp(self, service: str) -> Dict:
61
  data = {"service": service}
62
- return self._make_request('POST', '/api/mail/otp-rent', data=data)
63
 
64
  def get_otp(self, order_id: str) -> Dict:
65
  try:
66
  UUID(order_id)
67
- return self._make_request('GET', '/api/mail/otp-get', params={"orderId": order_id})
68
  except ValueError:
69
- return {"error": "order_id phải là UUID hợp lệ", "status_code": 400}
70
 
71
- # Khởi tạo client
72
  client = ZGMApiClient("https://gm.haivl.wtf", ZGM_API_TOKEN)
73
 
74
- # Hàm kiểm tra key từ API trên VPS mà không xóa
75
  def validate_key_from_api(access_key: str) -> tuple[bool, str]:
76
  try:
77
  response = requests.get(f"{KEY_API_URL}/api/keys/validate", params={"key": access_key}, timeout=5)
78
- print(f"Validate API Response: {response.text}") # Debug log
79
  response.raise_for_status()
80
  data = response.json()
81
  if data["valid"]:
82
- return True, f"Còn lại {data['remaining']} key."
83
  else:
84
- return False, "Key không hợp lệ! Vui lòng nhập key hợp lệ để tiếp tục."
85
  except requests.exceptions.RequestException as e:
86
- print(f"Validate API Error: {str(e)}") # Debug log
87
- return False, f"Lỗi khi kiểm tra key từ API: {str(e)}"
88
 
89
- # Hàm xóa key từ API trên VPS
90
  def remove_key_from_api(access_key: str) -> tuple[bool, str]:
91
  try:
92
  response = requests.delete(f"{KEY_API_URL}/api/keys/remove", params={"key": access_key}, timeout=5)
93
- print(f"Remove API Response: {response.text}") # Debug log
94
  response.raise_for_status()
95
  data = response.json()
96
  if data["success"]:
97
- return True, f"Còn lại {data['remaining']} key."
98
  else:
99
- return False, "Không thể xóa key."
100
  except requests.exceptions.RequestException as e:
101
- print(f"Remove API Error: {str(e)}") # Debug log
102
- return False, f"Lỗi khi xóa key từ API: {str(e)}"
103
 
104
- # Hàm xác thực và xóa key
105
  def validate_and_remove_key(access_key: str, result: Dict) -> tuple[bool, str]:
106
- # Kiểm tra key có hợp lệ không
107
  is_valid, message = validate_key_from_api(access_key)
108
- print(f"Input Key: {access_key}, Valid: {is_valid}") # Debug log
109
  if not is_valid:
110
  return False, message
111
 
112
- # Chỉ xóa key khi thuê OTP thành công
113
  if result.get("code") == 200 and result.get("data") and "orderId" in result["data"] and "email" in result["data"]:
114
  success, remove_message = remove_key_from_api(access_key)
115
  if not success:
116
- return True, f"Thuê OTP thành công nhưng {remove_message}"
117
- remaining_keys = int(remove_message.split("Còn lại ")[1].split(" key")[0])
118
- remaining_message = f"Còn lại {remaining_keys} key."
119
  if remaining_keys < 100:
120
- remaining_message += " Số lượng key còn ít! Vui lòng tạo thêm key mới."
121
  return True, remaining_message
122
  else:
123
- return True, f"Lỗi: {result.get('message', 'Không thể thuê Gmail.')}\nKey không bị xóa."
124
 
125
- # Hàm xử lý bất đồng bộ với chỉ báo tải
126
  def execute_with_loading(func, *args, **kwargs):
127
  result = [None]
128
  is_loading = [True]
@@ -137,7 +129,7 @@ def execute_with_loading(func, *args, **kwargs):
137
  chars = "|/-\\"
138
  i = 0
139
  while is_loading[0]:
140
- print(f"\rĐang tải {chars[i % len(chars)]}", end="")
141
  i += 1
142
  time.sleep(0.1)
143
  print("\r", end="")
@@ -145,10 +137,10 @@ def execute_with_loading(func, *args, **kwargs):
145
  api_thread.join()
146
  return result[0]
147
 
148
- # Hàm định dạng kết quả "Thuê OTP" thành bảng (chỉ Order ID và Email)
149
- def format_rent_otp_result(data: Dict) -> 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
  order_info = data["data"]
153
  df = pd.DataFrame([{
154
  "Order ID": order_info.get("orderId", "N/A"),
@@ -156,136 +148,120 @@ def format_rent_otp_result(data: Dict) -> pd.DataFrame:
156
  }])
157
  return df
158
 
159
- # Hàm định dạng kết quả "Lấy OTP" thành bảng (Service, Email, OTP Code, và nội dung thư)
160
- def format_get_otp_result(data: Dict, selected_service: str) -> pd.DataFrame:
161
  if not data or "data" not in data:
162
- return pd.DataFrame([["Không có dữ liệu để hiển thị."]], columns=["Message"])
163
 
164
  otp_data = data["data"]
165
 
166
- # Nếu là đối tượng đơn (không phải danh sách)
167
  if isinstance(otp_data, dict):
168
- otp_data = [otp_data] # Chuyển thành danh sách để xử lý thống nhất
169
 
170
- # Nếu không phải danh sách, dữ liệu không hợp lệ
171
  if not isinstance(otp_data, list):
172
- return pd.DataFrame([["Dữ liệu không hợp lệ."]], columns=["Message"])
173
 
174
  df = pd.DataFrame([
175
  {
176
- "Service": selected_service, # Sử dụng dịch vụ đã chọn khi thuê
177
- "Email": item.get("email", "N/A"), # Thêm email vào log
178
  "OTP Code": item.get("otpCode", "N/A"),
179
- "Content": item.get("otpContent", "N/A") # Nội dung thư từ API
180
  }
181
  for item in otp_data
182
  ])
183
  return df
184
 
185
- # Hàm xóa tất cả log
186
  def clear_all_logs():
187
- return pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]), pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]), ""
188
 
189
- # Hàm giao diện Gradio
190
- def gradio_interface(access_key, action, service, order_id, rent_otp_log, get_otp_log):
191
- message = ""
192
- new_order_id = order_id # Giữ nguyên order_id nếu không có thay đổi
193
-
194
- # Khởi tạo log nếu chưa có
195
- if rent_otp_log is None:
196
- rent_otp_log = pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"])
197
- if get_otp_log is None:
198
- get_otp_log = pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"])
199
-
200
- # Chỉ yêu cầu key cho hành động "Thuê OTP"
201
- if action == "Thuê OTP":
202
- if not access_key:
203
- rent_otp_log = pd.DataFrame([["Vui lòng nhập key để thực hiện Thuê OTP!"]], columns=["Message"])
204
- return rent_otp_log, get_otp_log, message, order_id
205
- if not service:
206
- rent_otp_log = pd.DataFrame([["Vui lòng chọn dịch vụ!"]], columns=["Message"])
207
- return rent_otp_log, get_otp_log, message, order_id
208
- result = execute_with_loading(client.rent_otp, service)
209
- is_valid, message = validate_and_remove_key(access_key, result)
210
- if not is_valid:
211
- rent_otp_log = pd.DataFrame([[message]], columns=["Message"])
212
- return rent_otp_log, get_otp_log, message, order_id
213
- if result.get("code") == 200:
214
- rent_otp_log = format_rent_otp_result(result)
215
- # Lấy Order ID mới để tự động điền
216
- new_order_id = result["data"].get("orderId", order_id)
217
- else:
218
- rent_otp_log = pd.DataFrame([[json.dumps(result, indent=2, ensure_ascii=False)]], columns=["Message"])
219
- return rent_otp_log, get_otp_log, message, new_order_id
220
- elif action == "Lấy OTP":
221
- if not order_id:
222
- get_otp_log = pd.DataFrame([["Vui lòng nhập Order ID!"]], columns=["Message"])
223
- return rent_otp_log, get_otp_log, "", order_id
224
- result = execute_with_loading(client.get_otp, order_id)
225
- if result.get("code") == 200 and "data" in result:
226
- get_otp_log = format_get_otp_result(result, service) # Truyền service đã chọn
227
- else:
228
- get_otp_log = pd.DataFrame([[json.dumps(result, indent=2, ensure_ascii=False)]], columns=["Message"])
229
- return rent_otp_log, get_otp_log, "", order_id
230
- else:
231
- return rent_otp_log, get_otp_log, "Hành động không hợp lệ!", order_id
232
-
233
- # Danh sách dịch vụ cố định
234
  SERVICES = [
235
  "ICLOUD", "ZSK", "FACEBOOK", "TELEGRAM", "OTHERS",
236
  "GGPLAY", "INSTAGRAM", "FMCPAY", "AMAZON"
237
  ]
238
 
239
- # Tạo giao diện Gradio
240
  with gr.Blocks() as demo:
241
- 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)")
242
 
243
  with gr.Row():
244
- access_key = gr.Textbox(label="Nhập Key Truy Cập (Chỉ cần cho Thuê OTP)", placeholder="Nhập key 32 ký tự...")
245
- action = gr.Dropdown(
246
- label="Chọn Hành Động",
247
- choices=["Thuê OTP", "Lấy OTP"],
248
- value="Thuê OTP"
249
- )
250
 
251
  with gr.Row():
252
- service = gr.Dropdown(label="Dịch Vụ (Cho Thuê OTP)", choices=SERVICES)
253
- order_id = gr.Textbox(label="Order ID (Cho Lấy OTP)", placeholder="Nhập Order ID")
254
 
255
- rent_otp_log = gr.State(value=pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]))
256
- get_otp_log = gr.State(value=pd.DataFrame([["Chưa có dữ liệu"]], columns=["Message"]))
257
 
258
  with gr.Row():
259
- rent_otp_output = gr.Dataframe(
260
- label="Log Thuê OTP",
261
  headers=["Order ID", "Email"],
262
  datatype=["str", "str"],
263
  interactive=False
264
  )
265
- get_otp_output = gr.Dataframe(
266
- label="Log Lấy OTP",
267
  headers=["Service", "Email", "OTP Code", "Content"],
268
  datatype=["str", "str", "str", "str"],
269
  interactive=False
270
  )
271
 
272
- message_output = gr.Textbox(label="Thông Báo", interactive=False)
273
 
274
  with gr.Row():
275
- submit_btn = gr.Button("Thực Thi", variant="primary")
276
- clear_btn = gr.Button("Xóa Log", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
- submit_btn.click(
279
- fn=gradio_interface,
280
- inputs=[access_key, action, service, order_id, rent_otp_log, get_otp_log],
281
- outputs=[rent_otp_output, get_otp_output, message_output, order_id]
282
  )
283
 
284
  clear_btn.click(
285
  fn=clear_all_logs,
286
  inputs=None,
287
- outputs=[rent_otp_output, get_otp_output, message_output]
288
  )
289
 
290
- # Khởi chạy giao diện
291
  demo.launch()
 
1
  import requests
2
  import json
3
  from uuid import UUID
 
4
  import gradio as gr
5
  import os
6
  import threading
7
  import time
8
  import pandas as pd
9
 
10
+ # Get ZGM API Token from environment variables
11
  ZGM_API_TOKEN = os.getenv("ZGM_API_TOKEN")
12
  if not ZGM_API_TOKEN:
13
+ raise ValueError("ZGM_API_TOKEN not found in environment variables!")
14
 
15
+ # Get VPS IP from environment variables
16
  VPS_IP = os.getenv("VPS_IP")
17
  if not VPS_IP:
18
+ raise ValueError("VPS_IP not found in environment variables!")
19
 
20
+ # VPS API URL
21
  KEY_API_URL = f"http://{VPS_IP}:3000"
22
 
23
  class ZGMApiClient:
24
+ """Client class to interact with ZGM API"""
25
 
26
  def __init__(self, base_url: str, api_token: str):
27
  self.base_url = base_url.rstrip('/')
 
46
  response.raise_for_status()
47
  return response.json()
48
  except requests.exceptions.ConnectTimeout:
49
+ return {"error": f"Could not connect to {self.base_url}. Please check URL or network.", "status_code": 0}
50
  except requests.exceptions.HTTPError as e:
51
  if response.status_code == 401:
52
+ return {"error": "Not logged in. Please check API Token.", "status_code": 401}
53
  elif response.status_code == 500:
54
+ return {"error": "Server error from ZGM API.", "status_code": 500}
55
+ return {"error": f"HTTP Error: {str(e)}", "status_code": response.status_code}
56
  except requests.exceptions.RequestException as e:
57
+ return {"error": f"Connection Error: {str(e)}", "status_code": 0}
58
 
59
+ def rent_mail(self, service: str) -> Dict:
60
  data = {"service": service}
61
+ return self._make_request('POST', '/api/mail/rent', data=data)
62
 
63
  def get_otp(self, order_id: str) -> Dict:
64
  try:
65
  UUID(order_id)
66
+ return self._make_request('GET', '/api/mail/otp', params={"orderId": order_id})
67
  except ValueError:
68
+ return {"error": "order_id must be a valid UUID", "status_code": 400}
69
 
70
+ # Initialize client
71
  client = ZGMApiClient("https://gm.haivl.wtf", ZGM_API_TOKEN)
72
 
73
+ # Validate key from VPS API
74
  def validate_key_from_api(access_key: str) -> tuple[bool, str]:
75
  try:
76
  response = requests.get(f"{KEY_API_URL}/api/keys/validate", params={"key": access_key}, timeout=5)
 
77
  response.raise_for_status()
78
  data = response.json()
79
  if data["valid"]:
80
+ return True, f"Remaining keys: {data['remaining']}."
81
  else:
82
+ return False, "Invalid key! Please enter a valid key."
83
  except requests.exceptions.RequestException as e:
84
+ return False, f"Error checking key from API: {str(e)}"
 
85
 
86
+ # Remove key from VPS API
87
  def remove_key_from_api(access_key: str) -> tuple[bool, str]:
88
  try:
89
  response = requests.delete(f"{KEY_API_URL}/api/keys/remove", params={"key": access_key}, timeout=5)
 
90
  response.raise_for_status()
91
  data = response.json()
92
  if data["success"]:
93
+ return True, f"Remaining keys: {data['remaining']}."
94
  else:
95
+ return False, "Failed to remove key."
96
  except requests.exceptions.RequestException as e:
97
+ return False, f"Error removing key from API: {str(e)}"
 
98
 
99
+ # Validate and remove key
100
  def validate_and_remove_key(access_key: str, result: Dict) -> tuple[bool, str]:
 
101
  is_valid, message = validate_key_from_api(access_key)
 
102
  if not is_valid:
103
  return False, message
104
 
 
105
  if result.get("code") == 200 and result.get("data") and "orderId" in result["data"] and "email" in result["data"]:
106
  success, remove_message = remove_key_from_api(access_key)
107
  if not success:
108
+ return True, f"Mail rented successfully but {remove_message}"
109
+ remaining_keys = int(remove_message.split("Remaining keys: ")[1].split(".")[0])
110
+ remaining_message = f"Remaining keys: {remaining_keys}."
111
  if remaining_keys < 100:
112
+ remaining_message += " Low key count! Please add more keys."
113
  return True, remaining_message
114
  else:
115
+ return True, f"Error: {result.get('message', 'Failed to rent mail.')}\nKey not removed."
116
 
117
+ # Async execution with loading indicator
118
  def execute_with_loading(func, *args, **kwargs):
119
  result = [None]
120
  is_loading = [True]
 
129
  chars = "|/-\\"
130
  i = 0
131
  while is_loading[0]:
132
+ print(f"\rLoading {chars[i % len(chars)]}", end="")
133
  i += 1
134
  time.sleep(0.1)
135
  print("\r", end="")
 
137
  api_thread.join()
138
  return result[0]
139
 
140
+ # Format rent mail result
141
+ def format_rent_result(data: Dict) -> pd.DataFrame:
142
  if not data or "data" not in data:
143
+ return pd.DataFrame([["No data to display."]], columns=["Message"])
144
  order_info = data["data"]
145
  df = pd.DataFrame([{
146
  "Order ID": order_info.get("orderId", "N/A"),
 
148
  }])
149
  return df
150
 
151
+ # Format OTP result
152
+ def format_otp_result(data: Dict, selected_service: str) -> pd.DataFrame:
153
  if not data or "data" not in data:
154
+ return pd.DataFrame([["No data to display."]], columns=["Message"])
155
 
156
  otp_data = data["data"]
157
 
 
158
  if isinstance(otp_data, dict):
159
+ otp_data = [otp_data]
160
 
 
161
  if not isinstance(otp_data, list):
162
+ return pd.DataFrame([["Invalid data format."]], columns=["Message"])
163
 
164
  df = pd.DataFrame([
165
  {
166
+ "Service": selected_service,
167
+ "Email": item.get("email", "N/A"),
168
  "OTP Code": item.get("otpCode", "N/A"),
169
+ "Content": item.get("otpContent", "N/A")
170
  }
171
  for item in otp_data
172
  ])
173
  return df
174
 
175
+ # Clear all logs
176
  def clear_all_logs():
177
+ return pd.DataFrame([["No data yet"]], columns=["Message"]), pd.DataFrame([["No data yet"]], columns=["Message"]), ""
178
 
179
+ # Services list
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  SERVICES = [
181
  "ICLOUD", "ZSK", "FACEBOOK", "TELEGRAM", "OTHERS",
182
  "GGPLAY", "INSTAGRAM", "FMCPAY", "AMAZON"
183
  ]
184
 
185
+ # Gradio interface
186
  with gr.Blocks() as demo:
187
+ gr.Markdown("## ZGM API Client - Rent Temporary Emails for OTP Services")
188
 
189
  with gr.Row():
190
+ access_key = gr.Textbox(label="Access Key (Required for renting)", placeholder="Enter your 32-character key...")
191
+ service = gr.Dropdown(label="Service", choices=SERVICES)
 
 
 
 
192
 
193
  with gr.Row():
194
+ order_id = gr.Textbox(label="Order ID", placeholder="Enter Order ID from rented mail")
 
195
 
196
+ rent_result = gr.State(value=pd.DataFrame([["No data yet"]], columns=["Message"]))
197
+ otp_result = gr.State(value=pd.DataFrame([["No data yet"]], columns=["Message"]))
198
 
199
  with gr.Row():
200
+ rent_output = gr.Dataframe(
201
+ label="Rented Mail",
202
  headers=["Order ID", "Email"],
203
  datatype=["str", "str"],
204
  interactive=False
205
  )
206
+ otp_output = gr.Dataframe(
207
+ label="OTP Results",
208
  headers=["Service", "Email", "OTP Code", "Content"],
209
  datatype=["str", "str", "str", "str"],
210
  interactive=False
211
  )
212
 
213
+ message_output = gr.Textbox(label="Status Message", interactive=False)
214
 
215
  with gr.Row():
216
+ rent_btn = gr.Button("Rent Mail", variant="primary")
217
+ get_otp_btn = gr.Button("Get OTP", variant="primary")
218
+ clear_btn = gr.Button("Clear Logs", variant="secondary")
219
+
220
+ def rent_mail_action(access_key, service, rent_result, otp_result):
221
+ if not access_key:
222
+ return pd.DataFrame([["Please enter access key!"]], columns=["Message"]), otp_result, "Access key required", rent_result
223
+ if not service:
224
+ return pd.DataFrame([["Please select service!"]], columns=["Message"]), otp_result, "Service required", rent_result
225
+
226
+ result = execute_with_loading(client.rent_mail, service)
227
+ is_valid, message = validate_and_remove_key(access_key, result)
228
+ if not is_valid:
229
+ return pd.DataFrame([[message]], columns=["Message"]), otp_result, message, rent_result
230
+
231
+ if result.get("code") == 200:
232
+ df = format_rent_result(result)
233
+ new_order_id = result["data"].get("orderId", "")
234
+ return df, otp_result, message, new_order_id
235
+ else:
236
+ return pd.DataFrame([[json.dumps(result, indent=2, ensure_ascii=False)]], columns=["Message"]), otp_result, message, rent_result
237
+
238
+ def get_otp_action(order_id, service, rent_result, otp_result):
239
+ if not order_id:
240
+ return rent_result, pd.DataFrame([["Please enter Order ID!"]], columns=["Message"]), "", rent_result
241
+
242
+ result = execute_with_loading(client.get_otp, order_id)
243
+ if result.get("code") == 200 and "data" in result:
244
+ df = format_otp_result(result, service)
245
+ return rent_result, df, "", rent_result
246
+ else:
247
+ return rent_result, pd.DataFrame([[json.dumps(result, indent=2, ensure_ascii=False)]], columns=["Message"]), "", rent_result
248
+
249
+ rent_btn.click(
250
+ fn=rent_mail_action,
251
+ inputs=[access_key, service, rent_result, otp_result],
252
+ outputs=[rent_output, otp_output, message_output, order_id]
253
+ )
254
 
255
+ get_otp_btn.click(
256
+ fn=get_otp_action,
257
+ inputs=[order_id, service, rent_result, otp_result],
258
+ outputs=[rent_output, otp_output, message_output, order_id]
259
  )
260
 
261
  clear_btn.click(
262
  fn=clear_all_logs,
263
  inputs=None,
264
+ outputs=[rent_output, otp_output, message_output]
265
  )
266
 
 
267
  demo.launch()