Muttered3 commited on
Commit
2814d35
Β·
verified Β·
1 Parent(s): 73edaaf

Update bot.py

Browse files
Files changed (1) hide show
  1. bot.py +113 -59
bot.py CHANGED
@@ -3,18 +3,19 @@ import re
3
  import time
4
  import io
5
  import asyncio
 
6
  from telethon import events, Button
7
  import db
8
  import scraper
 
9
 
10
  # --- AUTHENTICATION ---
11
- def get_admins():
12
- return {int(x) for x in os.environ.get("ADMIN_IDS", "").split(",") if x}
13
 
14
  def is_admin(event):
15
- return event.sender_id in get_admins()
16
 
17
- # --- PROFESSIONAL NAVIGATION ---
18
  def get_main_menu():
19
  return [
20
  [Button.inline("▢️ Start Scan", b"start_scan"), Button.inline("⏸ Pause", b"pause_scan"), Button.inline("⏹ Stop", b"stop_scan")],
@@ -24,18 +25,17 @@ def get_main_menu():
24
 
25
  def get_settings_menu():
26
  return [
27
- [Button.inline("πŸ’Ύ Load Database", b"load_words")],
28
- [Button.inline("⚑ Set Speed", b"set_speed"), Button.inline("πŸ“ Set Length", b"set_min_length")],
29
- [Button.inline("πŸ”΄ Wipe Database", b"reset_confirm")],
30
  [Button.inline("Β« Back to Main Menu", b"back_main")]
31
  ]
32
 
33
- # --- SYSTEM DASHBOARD ---
34
  async def generate_status_msg():
35
  state = await db.get_state()
36
  counts = await db.get_counts()
37
  concurrency = await db.get_concurrency()
38
  qlen = await db.queue_size()
 
39
 
40
  total = int(state.get("total", 0))
41
  processed = int(state.get("processed", 0))
@@ -56,6 +56,7 @@ async def generate_status_msg():
56
  f"**Queue:** `{qlen:,}` waiting\n"
57
  f"**Speed:** `{concurrency}` workers\n"
58
  f"**Filter:** `Min {min_len} letters`\n"
 
59
  f"**Progress:** `{pct:.2f}%`\n"
60
  f"`[{bar}]`\n"
61
  f"`{processed:,} / {total:,}`\n\n"
@@ -65,21 +66,23 @@ async def generate_status_msg():
65
  f"🚫 Unavail : `{counts.get('unavailable', 0):,}`\n"
66
  f"πŸ’° For Sale : `{counts.get('forsale', 0):,}`\n"
67
  f"πŸ”¨ Auction : `{counts.get('auction', 0):,}`\n"
 
68
  f"πŸ›’ Sold : `{counts.get('sold', 0):,}`\n"
69
  )
70
 
71
- # --- BACKGROUND HELPERS ---
72
- def read_file_sync():
73
- with open("words.txt", "r", encoding="utf-8") as f:
74
- return [line.strip().lower() for line in f if line.strip()]
75
-
76
- def write_file_sync(lines):
77
- with open("words.txt", "w", encoding="utf-8") as f:
78
- f.write("\n".join(lines))
79
-
80
- def filter_words_sync(chunk, min_length):
81
- pattern = re.compile(rf'^[a-z0-9_]{{{min_length},32}}$')
82
- return {w for w in chunk if pattern.match(w)}
 
83
 
84
  # --- HANDLER SETUP ---
85
  def setup_handlers(client):
@@ -89,7 +92,62 @@ def setup_handlers(client):
89
  if is_admin(event):
90
  await event.respond("**Fragment Control System**", buttons=get_main_menu())
91
 
92
- # --- REAL HTML SCREENSHOT COMMAND ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  @client.on(events.NewMessage(pattern=r'/check\s+([a-zA-Z0-9_]+)'))
94
  async def check_cmd(event):
95
  if not is_admin(event): return
@@ -97,37 +155,34 @@ def setup_handlers(client):
97
  msg = await event.respond(f"πŸ“Έ Taking live screenshot of `@{target}` on Fragment...")
98
 
99
  url = f"https://fragment.com/username/{target}"
 
100
 
101
  try:
102
- # Run the browser screenshot in a separate thread so it doesn't freeze the bot
103
  def take_screenshot():
104
  from html2image import Html2Image
105
- # --no-sandbox is critical for Linux servers
106
  hti = Html2Image(
107
  browser_executable='/usr/bin/chromium',
108
  custom_flags=['--no-sandbox', '--disable-gpu', '--hide-scrollbars', '--disable-dev-shm-usage']
109
  )
110
  hti.size = (1000, 750)
111
- img_name = f"{target}_fragment.png"
112
- # Chrome navigates to the URL and renders the exact CSS/HTML
113
- hti.screenshot(url=url, save_as=img_name)
114
- return img_name
115
 
116
  img_filename = await asyncio.to_thread(take_screenshot)
117
 
118
  await client.send_file(event.chat_id, file=img_filename, caption=f"**Live Web Snapshot:** `@{target}`")
119
  await msg.delete()
120
-
121
- if os.path.exists(img_filename):
122
- os.remove(img_filename)
123
 
124
  except Exception as e:
125
- try:
126
- await msg.edit(f"❌ **Screenshot Error:**\n*(Make sure packages.txt has chromium)*\n`{str(e)}`")
127
- except:
128
- pass
 
 
129
 
130
- # --- UPGRADED HTML DEBUG COMMAND (Immune to Shadow Bans) ---
131
  @client.on(events.NewMessage(pattern=r'/html\s+([a-zA-Z0-9_]+)'))
132
  async def html_cmd(event):
133
  if not is_admin(event): return
@@ -137,9 +192,6 @@ def setup_handlers(client):
137
  url = f"https://fragment.com/username/{target}"
138
 
139
  try:
140
- from curl_cffi.requests import AsyncSession
141
-
142
- # Using Chrome impersonation to stop Fragment from faking the redirect
143
  async with AsyncSession(impersonate="chrome120") as session:
144
  resp = await session.get(url, timeout=15, allow_redirects=True)
145
  html_text = resp.text
@@ -161,11 +213,10 @@ def setup_handlers(client):
161
  await msg.delete()
162
 
163
  except Exception as e:
164
- try:
165
- await msg.edit(f"❌ **Error fetching HTML:** `{str(e)}`")
166
- except:
167
- await event.respond(f"❌ **Error fetching HTML:** `{str(e)}`")
168
 
 
169
  @client.on(events.NewMessage(pattern='/upload'))
170
  async def upload_cmd(event):
171
  if not is_admin(event): return
@@ -179,6 +230,7 @@ def setup_handlers(client):
179
  await reply.download_media(file="words.txt")
180
  await wait.edit("βœ… Uploaded. Go to Settings -> Load Database.", buttons=get_main_menu())
181
 
 
182
  @client.on(events.NewMessage(pattern='/getstring'))
183
  async def pyrogram_string_cmd(event):
184
  if not is_admin(event): return
@@ -186,16 +238,13 @@ def setup_handlers(client):
186
  async with client.conversation(event.chat_id, timeout=300) as conv:
187
  try:
188
  await conv.send_message("πŸ›  **Pyrogram String Session Generator**\n\nEnter your `API_ID`:")
189
- api_id_msg = await conv.get_response()
190
- api_id = int(api_id_msg.text.strip())
191
 
192
  await conv.send_message("Enter your `API_HASH`:")
193
- api_hash_msg = await conv.get_response()
194
- api_hash = api_hash_msg.text.strip()
195
 
196
  await conv.send_message("Enter the **Phone Number** (e.g., +1234567890):")
197
- phone_msg = await conv.get_response()
198
- phone = phone_msg.text.replace(" ", "").strip()
199
 
200
  await conv.send_message("⏳ Requesting OTP from Telegram...")
201
 
@@ -208,21 +257,19 @@ def setup_handlers(client):
208
  sent_code = await app.send_code(phone)
209
 
210
  await conv.send_message("πŸ“₯ **Code Sent!**\n\n⚠️ **CRITICAL:** You MUST enter the code with spaces between numbers.\nExample: `1 2 3 4 5`")
211
- code_msg = await conv.get_response()
212
- otp_code = code_msg.text.replace(" ", "").strip()
213
 
214
  try:
215
  await app.sign_in(phone, sent_code.phone_code_hash, otp_code)
216
  except SessionPasswordNeeded:
217
  await conv.send_message("πŸ” **2FA Password Required.**\nEnter your Cloud Password:")
218
- pwd_msg = await conv.get_response()
219
- await app.check_password(pwd_msg.text.strip())
220
 
221
  string_session = await app.export_session_string()
222
-
223
  await conv.send_message(f"βœ… **String Session Generated!**\n\n`{string_session}`\n\n⚠️ Keep this secret.")
224
  except asyncio.TimeoutError:
225
- await conv.send_message("❌ **Timeout.** You took too long. Run `/getstring` again.")
226
  except Exception as e:
227
  await conv.send_message(f"❌ **Error:** `{str(e)}`")
228
  finally:
@@ -230,9 +277,11 @@ def setup_handlers(client):
230
  try: await app.disconnect()
231
  except: pass
232
 
233
- async def _load_words(event, is_callback=True):
 
234
  try:
235
- all_lines = await asyncio.to_thread(read_file_sync)
 
236
  except Exception:
237
  return await event.edit("❌ `words.txt` not found. Upload it first.")
238
 
@@ -246,7 +295,9 @@ def setup_handlers(client):
246
  current = all_lines[:CHUNK]
247
  remaining = all_lines[CHUNK:]
248
 
249
- valid = await asyncio.to_thread(filter_words_sync, current, min_length)
 
 
250
  r = await db.get_redis()
251
  done = await r.smembers("frag:done")
252
  to_q = list(valid - done)
@@ -257,7 +308,9 @@ def setup_handlers(client):
257
  pipe.lpush("frag:queue", *to_q[i:i+1000])
258
  await pipe.execute()
259
 
260
- await asyncio.to_thread(write_file_sync, remaining)
 
 
261
  await db.set_state(total=len(valid))
262
  await event.edit(f"βœ… Loaded {len(to_q):,} words (Min length: {min_length}).", buttons=get_main_menu())
263
 
@@ -327,7 +380,8 @@ def setup_handlers(client):
327
  await event.edit("⏳ Generating text files...")
328
  exported_count = 0
329
 
330
- for s in ["taken", "unavailable", "sold", "forsale", "auction"]:
 
331
  lst = sorted(list(await (getattr(db, f"get_all_{s}")())))
332
  if lst:
333
  f = io.BytesIO("\n".join(lst).encode('utf-8'))
 
3
  import time
4
  import io
5
  import asyncio
6
+ import aiohttp
7
  from telethon import events, Button
8
  import db
9
  import scraper
10
+ from curl_cffi.requests import AsyncSession
11
 
12
  # --- AUTHENTICATION ---
13
+ ADMIN_IDS = {int(x) for x in os.environ.get("ADMIN_IDS", "").split(",") if x}
 
14
 
15
  def is_admin(event):
16
+ return event.sender_id in ADMIN_IDS
17
 
18
+ # --- MENUS & DASHBOARD ---
19
  def get_main_menu():
20
  return [
21
  [Button.inline("▢️ Start Scan", b"start_scan"), Button.inline("⏸ Pause", b"pause_scan"), Button.inline("⏹ Stop", b"stop_scan")],
 
25
 
26
  def get_settings_menu():
27
  return [
28
+ [Button.inline("πŸ’Ύ Load Words", b"load_words"), Button.inline("πŸ“ Set Length", b"set_min_length")],
29
+ [Button.inline("⚑ Set Speed", b"set_speed"), Button.inline("πŸ”΄ Wipe DB", b"reset_confirm")],
 
30
  [Button.inline("Β« Back to Main Menu", b"back_main")]
31
  ]
32
 
 
33
  async def generate_status_msg():
34
  state = await db.get_state()
35
  counts = await db.get_counts()
36
  concurrency = await db.get_concurrency()
37
  qlen = await db.queue_size()
38
+ proxy_count = await db.get_proxy_count()
39
 
40
  total = int(state.get("total", 0))
41
  processed = int(state.get("processed", 0))
 
56
  f"**Queue:** `{qlen:,}` waiting\n"
57
  f"**Speed:** `{concurrency}` workers\n"
58
  f"**Filter:** `Min {min_len} letters`\n"
59
+ f"**Proxies:** `{proxy_count}` Active\n"
60
  f"**Progress:** `{pct:.2f}%`\n"
61
  f"`[{bar}]`\n"
62
  f"`{processed:,} / {total:,}`\n\n"
 
66
  f"🚫 Unavail : `{counts.get('unavailable', 0):,}`\n"
67
  f"πŸ’° For Sale : `{counts.get('forsale', 0):,}`\n"
68
  f"πŸ”¨ Auction : `{counts.get('auction', 0):,}`\n"
69
+ f"βœ… Available : `{counts.get('available', 0):,}`\n"
70
  f"πŸ›’ Sold : `{counts.get('sold', 0):,}`\n"
71
  )
72
 
73
+ # --- PROXY TESTER ---
74
+ async def test_proxy(proxy_url):
75
+ proxies = {"http": proxy_url, "https": proxy_url}
76
+ start = time.time()
77
+ try:
78
+ async with AsyncSession(impersonate="chrome120", proxies=proxies) as session:
79
+ resp = await session.get("https://fragment.com/", timeout=10)
80
+ if resp.status_code == 200:
81
+ latency = round((time.time() - start) * 1000)
82
+ return proxy_url, latency
83
+ except Exception:
84
+ pass
85
+ return proxy_url, None
86
 
87
  # --- HANDLER SETUP ---
88
  def setup_handlers(client):
 
92
  if is_admin(event):
93
  await event.respond("**Fragment Control System**", buttons=get_main_menu())
94
 
95
+ # --- NEW: PROXY UPLOADER & CHECKER ---
96
+ @client.on(events.NewMessage(pattern='/proxies'))
97
+ async def proxy_cmd(event):
98
+ if not is_admin(event): return
99
+ if not event.is_reply:
100
+ return await event.respond("⚠️ Reply to a `proxies.txt` file with `/proxies`.")
101
+
102
+ reply = await event.get_reply_message()
103
+ if not reply.document:
104
+ return await event.respond("⚠️ Attached message is not a document.")
105
+
106
+ msg = await event.respond("⏳ Downloading proxies...")
107
+ file_path = await reply.download_media(file="temp_proxies.txt")
108
+
109
+ with open(file_path, "r", encoding="utf-8") as f:
110
+ raw_proxies = [line.strip() for line in f if line.strip()]
111
+
112
+ os.remove(file_path)
113
+
114
+ if not raw_proxies:
115
+ return await msg.edit("⚠️ No proxies found in file.")
116
+
117
+ await msg.edit(f"πŸ” Testing **{len(raw_proxies)}** proxies concurrently...")
118
+
119
+ # Test all proxies concurrently
120
+ tasks = [test_proxy(p) for p in raw_proxies]
121
+ results = await asyncio.gather(*tasks)
122
+
123
+ working_proxies = []
124
+ latency_board = []
125
+
126
+ for p_url, latency in results:
127
+ if latency is not None:
128
+ working_proxies.append(p_url)
129
+ latency_board.append((p_url, latency))
130
+
131
+ # Sort by fastest latency
132
+ latency_board.sort(key=lambda x: x[1])
133
+
134
+ # Save working proxies to Redis
135
+ await db.save_working_proxies(working_proxies)
136
+
137
+ # Format Top 10 Report
138
+ top_str = "\n".join([f"⏱ `{lat}ms` | {url.split('@')[-1] if '@' in url else url}" for url, lat in latency_board[:10]])
139
+
140
+ report = (
141
+ f"βœ… **Proxy Test Complete**\n"
142
+ f"━━━━━━━━━━━━━━━━\n"
143
+ f"**Total Tested:** `{len(raw_proxies)}`\n"
144
+ f"**Working & Saved:** `{len(working_proxies)}`\n\n"
145
+ f"πŸ† **Top Fastest Proxies:**\n{top_str if top_str else 'None'}"
146
+ )
147
+ await msg.edit(report, buttons=get_main_menu())
148
+
149
+
150
+ # --- LIVE HTML SCREENSHOT COMMAND ---
151
  @client.on(events.NewMessage(pattern=r'/check\s+([a-zA-Z0-9_]+)'))
152
  async def check_cmd(event):
153
  if not is_admin(event): return
 
155
  msg = await event.respond(f"πŸ“Έ Taking live screenshot of `@{target}` on Fragment...")
156
 
157
  url = f"https://fragment.com/username/{target}"
158
+ img_filename = None
159
 
160
  try:
 
161
  def take_screenshot():
162
  from html2image import Html2Image
 
163
  hti = Html2Image(
164
  browser_executable='/usr/bin/chromium',
165
  custom_flags=['--no-sandbox', '--disable-gpu', '--hide-scrollbars', '--disable-dev-shm-usage']
166
  )
167
  hti.size = (1000, 750)
168
+ name = f"{target}_fragment.png"
169
+ hti.screenshot(url=url, save_as=name)
170
+ return name
 
171
 
172
  img_filename = await asyncio.to_thread(take_screenshot)
173
 
174
  await client.send_file(event.chat_id, file=img_filename, caption=f"**Live Web Snapshot:** `@{target}`")
175
  await msg.delete()
 
 
 
176
 
177
  except Exception as e:
178
+ try: await msg.edit(f"❌ **Screenshot Error:**\n`{str(e)}`")
179
+ except: pass
180
+ finally:
181
+ if img_filename and os.path.exists(img_filename):
182
+ os.remove(img_filename)
183
+
184
 
185
+ # --- HTML DEBUG COMMAND (With Redirect Tracking) ---
186
  @client.on(events.NewMessage(pattern=r'/html\s+([a-zA-Z0-9_]+)'))
187
  async def html_cmd(event):
188
  if not is_admin(event): return
 
192
  url = f"https://fragment.com/username/{target}"
193
 
194
  try:
 
 
 
195
  async with AsyncSession(impersonate="chrome120") as session:
196
  resp = await session.get(url, timeout=15, allow_redirects=True)
197
  html_text = resp.text
 
213
  await msg.delete()
214
 
215
  except Exception as e:
216
+ try: await msg.edit(f"❌ **Error fetching HTML:** `{str(e)}`")
217
+ except: pass
 
 
218
 
219
+ # --- WORDS UPLOAD COMMAND ---
220
  @client.on(events.NewMessage(pattern='/upload'))
221
  async def upload_cmd(event):
222
  if not is_admin(event): return
 
230
  await reply.download_media(file="words.txt")
231
  await wait.edit("βœ… Uploaded. Go to Settings -> Load Database.", buttons=get_main_menu())
232
 
233
+ # --- SESSION GENERATOR ---
234
  @client.on(events.NewMessage(pattern='/getstring'))
235
  async def pyrogram_string_cmd(event):
236
  if not is_admin(event): return
 
238
  async with client.conversation(event.chat_id, timeout=300) as conv:
239
  try:
240
  await conv.send_message("πŸ›  **Pyrogram String Session Generator**\n\nEnter your `API_ID`:")
241
+ api_id = int((await conv.get_response()).text.strip())
 
242
 
243
  await conv.send_message("Enter your `API_HASH`:")
244
+ api_hash = (await conv.get_response()).text.strip()
 
245
 
246
  await conv.send_message("Enter the **Phone Number** (e.g., +1234567890):")
247
+ phone = (await conv.get_response()).text.replace(" ", "").strip()
 
248
 
249
  await conv.send_message("⏳ Requesting OTP from Telegram...")
250
 
 
257
  sent_code = await app.send_code(phone)
258
 
259
  await conv.send_message("πŸ“₯ **Code Sent!**\n\n⚠️ **CRITICAL:** You MUST enter the code with spaces between numbers.\nExample: `1 2 3 4 5`")
260
+ otp_code = (await conv.get_response()).text.replace(" ", "").strip()
 
261
 
262
  try:
263
  await app.sign_in(phone, sent_code.phone_code_hash, otp_code)
264
  except SessionPasswordNeeded:
265
  await conv.send_message("πŸ” **2FA Password Required.**\nEnter your Cloud Password:")
266
+ pwd = (await conv.get_response()).text.strip()
267
+ await app.check_password(pwd)
268
 
269
  string_session = await app.export_session_string()
 
270
  await conv.send_message(f"βœ… **String Session Generated!**\n\n`{string_session}`\n\n⚠️ Keep this secret.")
271
  except asyncio.TimeoutError:
272
+ await conv.send_message("❌ **Timeout.** Run `/getstring` again.")
273
  except Exception as e:
274
  await conv.send_message(f"❌ **Error:** `{str(e)}`")
275
  finally:
 
277
  try: await app.disconnect()
278
  except: pass
279
 
280
+ # --- DASHBOARD CALLBACKS ---
281
+ async def _load_words(event):
282
  try:
283
+ with open("words.txt", "r", encoding="utf-8") as f:
284
+ all_lines = [line.strip().lower() for line in f if line.strip()]
285
  except Exception:
286
  return await event.edit("❌ `words.txt` not found. Upload it first.")
287
 
 
295
  current = all_lines[:CHUNK]
296
  remaining = all_lines[CHUNK:]
297
 
298
+ pattern = re.compile(rf'^[a-z0-9_]{{{min_length},32}}$')
299
+ valid = {w for w in current if pattern.match(w)}
300
+
301
  r = await db.get_redis()
302
  done = await r.smembers("frag:done")
303
  to_q = list(valid - done)
 
308
  pipe.lpush("frag:queue", *to_q[i:i+1000])
309
  await pipe.execute()
310
 
311
+ with open("words.txt", "w", encoding="utf-8") as f:
312
+ f.write("\n".join(remaining))
313
+
314
  await db.set_state(total=len(valid))
315
  await event.edit(f"βœ… Loaded {len(to_q):,} words (Min length: {min_length}).", buttons=get_main_menu())
316
 
 
380
  await event.edit("⏳ Generating text files...")
381
  exported_count = 0
382
 
383
+ # Export all tracking sets including the new 'available' one
384
+ for s in ["taken", "unavailable", "sold", "forsale", "auction", "available"]:
385
  lst = sorted(list(await (getattr(db, f"get_all_{s}")())))
386
  if lst:
387
  f = io.BytesIO("\n".join(lst).encode('utf-8'))