xdragxt commited on
Commit
c723e4d
·
verified ·
1 Parent(s): 8565116

Create multi_client.py

Browse files
Files changed (1) hide show
  1. multi_client.py +260 -0
multi_client.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simplified Multi-Client Ultroid Launcher v2
4
+ Uses working directories but with minimal setup
5
+ """
6
+ import os
7
+ import subprocess
8
+ import sys
9
+ import shutil
10
+
11
+ # Load .env file
12
+ try:
13
+ from dotenv import load_dotenv
14
+ load_dotenv()
15
+ except ImportError:
16
+ pass
17
+
18
+ REQUIRED_VARS = ["API_ID", "API_HASH", "SESSION", "MONGO_URI"]
19
+
20
+ def has_client_config(client_num):
21
+ """Check if client has all required config"""
22
+ suffix = "" if client_num == 1 else str(client_num - 1)
23
+ for var in REQUIRED_VARS:
24
+ if not os.environ.get(var + suffix):
25
+ return False
26
+ return True
27
+
28
+ def stop_client(client_num):
29
+ """Stop a running client if it exists"""
30
+ base_dir = os.getcwd()
31
+ pid_file = os.path.join(base_dir, f"client_{client_num}.pid")
32
+
33
+ if os.path.exists(pid_file):
34
+ try:
35
+ with open(pid_file, 'r') as f:
36
+ pid = int(f.read().strip())
37
+
38
+ # Try to kill the process
39
+ try:
40
+ if sys.platform == "win32":
41
+ subprocess.run(["taskkill", "/F", "/PID", str(pid)],
42
+ capture_output=True, check=False)
43
+ else:
44
+ os.kill(pid, 15) # SIGTERM
45
+ print(f" → Stopped Client {client_num} (PID: {pid})")
46
+ except (ProcessLookupError, OSError):
47
+ # Process already dead
48
+ pass
49
+
50
+ # Remove PID file
51
+ try:
52
+ os.remove(pid_file)
53
+ except:
54
+ pass
55
+ except Exception:
56
+ pass
57
+
58
+ # Also try to kill by process detection (psutil if available)
59
+ try:
60
+ import psutil
61
+ for proc in psutil.process_iter(['pid', 'cmdline', 'cwd']):
62
+ try:
63
+ cmdline = proc.info.get('cmdline', [])
64
+ proc_cwd = proc.info.get('cwd', '')
65
+
66
+ # Check if this is a pyUltroid process in client_N directory
67
+ if any('pyUltroid' in str(arg) for arg in cmdline):
68
+ if f'client_{client_num}' in proc_cwd:
69
+ proc.kill()
70
+ print(f" → Stopped Client {client_num} (PID: {proc.info['pid']})")
71
+ except (psutil.NoSuchProcess, psutil.AccessDenied):
72
+ continue
73
+ except ImportError:
74
+ # psutil not available, skip process detection
75
+ pass
76
+
77
+ def setup_client_dir(client_num, base_dir):
78
+ """Minimal setup for client directory"""
79
+ client_dir = os.path.join(base_dir, f"client_{client_num}")
80
+ os.makedirs(client_dir, exist_ok=True)
81
+
82
+ # Only create symlinks for essential directories
83
+ for dir_name in ["plugins", "resources", "assistant", "strings", "addons"]:
84
+ src = os.path.join(base_dir, dir_name)
85
+ dst = os.path.join(client_dir, dir_name)
86
+
87
+ if os.path.exists(src) and not os.path.exists(dst):
88
+ try:
89
+ if sys.platform != "win32":
90
+ os.symlink(os.path.abspath(src), dst)
91
+ else:
92
+ # Windows: copy instead of symlink
93
+ if os.path.isdir(src):
94
+ shutil.copytree(src, dst)
95
+ except Exception:
96
+ pass
97
+
98
+ # Copy .env
99
+ if os.path.exists(".env"):
100
+ shutil.copy(".env", os.path.join(client_dir, ".env"))
101
+
102
+ return client_dir
103
+
104
+ def start_client(client_num):
105
+ """Start a client"""
106
+ if not has_client_config(client_num):
107
+ suffix = "" if client_num == 1 else str(client_num - 1)
108
+ missing = [v for v in REQUIRED_VARS if not os.environ.get(v + suffix)]
109
+ print(f"✗ Client {client_num}: Missing {', '.join(missing)}")
110
+ return None
111
+
112
+ print(f"✓ Client {client_num}: Starting...")
113
+
114
+ base_dir = os.getcwd()
115
+ client_dir = setup_client_dir(client_num, base_dir)
116
+
117
+ # Build environment
118
+ env = os.environ.copy()
119
+ suffix = "" if client_num == 1 else str(client_num - 1)
120
+
121
+ # Get values
122
+ api_id = os.environ.get(f"API_ID{suffix}") or os.environ.get("API_ID")
123
+ api_hash = os.environ.get(f"API_HASH{suffix}") or os.environ.get("API_HASH")
124
+ session = os.environ.get(f"SESSION{suffix}") or os.environ.get("SESSION")
125
+ mongo_uri = os.environ.get(f"MONGO_URI{suffix}") or os.environ.get("MONGO_URI")
126
+
127
+ # Unique database
128
+ db_name = f"UltroidDB{client_num}"
129
+ if mongo_uri.endswith('/'):
130
+ env["MONGO_URI"] = f"{mongo_uri}{db_name}"
131
+ else:
132
+ env["MONGO_URI"] = f"{mongo_uri}/{db_name}"
133
+
134
+ # Optional vars
135
+ if os.environ.get(f"LOG_CHANNEL{suffix}"):
136
+ env["LOG_CHANNEL"] = os.environ.get(f"LOG_CHANNEL{suffix}")
137
+ elif os.environ.get("LOG_CHANNEL"):
138
+ env["LOG_CHANNEL"] = os.environ.get("LOG_CHANNEL")
139
+
140
+ if os.environ.get(f"BOT_TOKEN{suffix}"):
141
+ env["BOT_TOKEN"] = os.environ.get(f"BOT_TOKEN{suffix}")
142
+ elif os.environ.get("BOT_TOKEN"):
143
+ env["BOT_TOKEN"] = os.environ.get("BOT_TOKEN")
144
+
145
+ env["PYTHONPATH"] = base_dir
146
+
147
+ # Note: The .restart command will work correctly because:
148
+ # - Working directory (client_dir) is preserved by os.execl()
149
+ # - Environment variables (PYTHONPATH, MONGO_URI, etc.) are preserved
150
+ # - sys.argv arguments (API_ID, API_HASH, SESSION) are preserved
151
+ # The restart function in pyUltroid/fns/helper.py handles this automatically
152
+
153
+ try:
154
+ proc = subprocess.Popen(
155
+ [sys.executable, "-m", "pyUltroid", api_id, api_hash, session, "", ""],
156
+ cwd=client_dir,
157
+ env=env,
158
+ )
159
+ # Save PID file
160
+ pid_file = os.path.join(base_dir, f"client_{client_num}.pid")
161
+ try:
162
+ with open(pid_file, 'w') as f:
163
+ f.write(str(proc.pid))
164
+ except:
165
+ pass
166
+ print(f" → Started (PID: {proc.pid})")
167
+ return proc
168
+ except Exception as e:
169
+ print(f" ✗ Error: {e}")
170
+ return None
171
+
172
+ def main():
173
+ print("=" * 60)
174
+ print("Multi-Client Ultroid Launcher v2 (Simplified)")
175
+ print("=" * 60)
176
+ print()
177
+
178
+ # First, stop clients that don't have config
179
+ print("Checking for clients without configuration...")
180
+ stopped_count = 0
181
+ for i in range(1, 6):
182
+ if not has_client_config(i):
183
+ # Check if this client is running and stop it
184
+ base_dir = os.getcwd()
185
+ pid_file = os.path.join(base_dir, f"client_{i}.pid")
186
+ if os.path.exists(pid_file):
187
+ stop_client(i)
188
+ stopped_count += 1
189
+ if stopped_count > 0:
190
+ print(f"✓ Stopped {stopped_count} client(s) without configuration")
191
+ print()
192
+
193
+ # Now start clients with proper config
194
+ processes = []
195
+ for i in range(1, 6):
196
+ proc = start_client(i)
197
+ if proc:
198
+ processes.append((i, proc))
199
+ print()
200
+
201
+ if not processes:
202
+ print("✗ No clients started. Check your .env file.")
203
+ sys.exit(1)
204
+
205
+ print("=" * 60)
206
+ print(f"✓ {len(processes)} client(s) running")
207
+ for num, proc in processes:
208
+ print(f" Client {num}: PID {proc.pid}")
209
+ print("=" * 60)
210
+ print("\nPress Ctrl+C to exit (clients keep running)")
211
+ print("Monitoring clients - will stop any that lose configuration...")
212
+
213
+ try:
214
+ import time
215
+ while True:
216
+ time.sleep(30) # Check every 30 seconds
217
+
218
+ # Check all clients and stop those without config
219
+ for i in range(1, 6):
220
+ if not has_client_config(i):
221
+ # Check if this client is still running
222
+ base_dir = os.getcwd()
223
+ pid_file = os.path.join(base_dir, f"client_{i}.pid")
224
+ if os.path.exists(pid_file):
225
+ try:
226
+ with open(pid_file, 'r') as f:
227
+ pid = int(f.read().strip())
228
+
229
+ # Check if process is still alive
230
+ try:
231
+ if sys.platform == "win32":
232
+ result = subprocess.run(["tasklist", "/FI", f"PID eq {pid}"],
233
+ capture_output=True, check=False)
234
+ if str(pid) in result.stdout.decode():
235
+ # Process exists, kill it
236
+ subprocess.run(["taskkill", "/F", "/PID", str(pid)],
237
+ capture_output=True, check=False)
238
+ print(f"\n⚠ Client {i} stopped (configuration removed)")
239
+ else:
240
+ os.kill(pid, 0) # Check if process exists
241
+ # Process exists, kill it
242
+ os.kill(pid, 15) # SIGTERM
243
+ print(f"\n⚠ Client {i} stopped (configuration removed)")
244
+ except (ProcessLookupError, OSError):
245
+ # Process already dead
246
+ pass
247
+
248
+ # Remove PID file
249
+ try:
250
+ os.remove(pid_file)
251
+ except:
252
+ pass
253
+ except Exception:
254
+ pass
255
+ except KeyboardInterrupt:
256
+ print("\n\nExiting. Clients still running.")
257
+ print("Stop with: pkill -f pyUltroid")
258
+
259
+ if __name__ == "__main__":
260
+ main()