Spaces:
Paused
Paused
Update bot.py
Browse files
bot.py
CHANGED
|
@@ -11,6 +11,14 @@ def get_admins():
|
|
| 11 |
def is_admin(event):
|
| 12 |
return event.sender_id in get_admins()
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
async def generate_status_msg():
|
| 15 |
state = await db.get_state()
|
| 16 |
counts = await db.get_counts()
|
|
@@ -55,13 +63,7 @@ def setup_handlers(client):
|
|
| 55 |
await event.respond("β Unauthorized.")
|
| 56 |
return
|
| 57 |
|
| 58 |
-
|
| 59 |
-
[Button.inline("βΆοΈ Start Scan", b"start_scan"), Button.inline("βΈ Pause", b"pause_scan")],
|
| 60 |
-
[Button.inline("βΉ Stop", b"stop_scan"), Button.inline("π Status", b"show_status")],
|
| 61 |
-
[Button.inline("β‘ Set Speed", b"set_speed"), Button.inline("π₯ Export", b"export_files")],
|
| 62 |
-
[Button.inline("π Load Words", b"load_words"), Button.inline("π£ Reset", b"reset_confirm")]
|
| 63 |
-
]
|
| 64 |
-
await event.respond("π Fragment Scanner Bot", buttons=buttons)
|
| 65 |
|
| 66 |
@client.on(events.NewMessage(pattern='/upload'))
|
| 67 |
async def upload_cmd(event):
|
|
@@ -80,28 +82,32 @@ def setup_handlers(client):
|
|
| 80 |
return
|
| 81 |
|
| 82 |
msg = await event.respond("π₯ Downloading massive file to server disk...")
|
| 83 |
-
|
| 84 |
await reply_msg.download_media(file="words.txt")
|
| 85 |
-
|
| 86 |
-
await msg.edit("β
**File uploaded to server successfully!**\n\nClick **π Load Words** to bite off the first 200,000 words into the database.")
|
| 87 |
|
| 88 |
@client.on(events.NewMessage(pattern='/load'))
|
| 89 |
async def load_cmd(event):
|
| 90 |
if not is_admin(event):
|
| 91 |
await event.respond("β Unauthorized.")
|
| 92 |
return
|
| 93 |
-
await _load_words(event
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
-
async def _load_words(reply_func):
|
| 96 |
try:
|
| 97 |
with open("words.txt", "r", encoding="utf-8") as f:
|
| 98 |
all_lines = [line.strip().lower() for line in f if line.strip()]
|
| 99 |
except Exception:
|
| 100 |
-
await
|
| 101 |
return
|
| 102 |
|
| 103 |
if not all_lines:
|
| 104 |
-
await
|
| 105 |
return
|
| 106 |
|
| 107 |
# --- SERVER DISK BUFFER LOGIC ---
|
|
@@ -126,12 +132,11 @@ def setup_handlers(client):
|
|
| 126 |
except Exception as e:
|
| 127 |
err_str = str(e).lower()
|
| 128 |
if "oom" in err_str or "maxmemory" in err_str:
|
| 129 |
-
await
|
| 130 |
else:
|
| 131 |
-
await
|
| 132 |
-
return
|
| 133 |
|
| 134 |
-
# If successfully pushed to Redis, overwrite the file with the remaining lines
|
| 135 |
with open("words.txt", "w", encoding="utf-8") as f:
|
| 136 |
f.write("\n".join(remaining_lines))
|
| 137 |
|
|
@@ -144,104 +149,103 @@ def setup_handlers(client):
|
|
| 144 |
else:
|
| 145 |
msg += "β
**No words remaining on server disk.**"
|
| 146 |
|
| 147 |
-
await
|
| 148 |
|
| 149 |
@client.on(events.CallbackQuery())
|
| 150 |
async def button_handler(event):
|
| 151 |
if not is_admin(event):
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
| 153 |
return
|
| 154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
data = event.data.decode('utf-8')
|
| 156 |
|
| 157 |
-
if data == "
|
| 158 |
-
await event.
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
elif data == "start_scan":
|
| 162 |
await db.set_state(running="1", paused="0", start_time=str(time.time()))
|
| 163 |
qlen = await db.queue_size()
|
| 164 |
-
await event.
|
| 165 |
-
await event.reply(f"π Scan started! {qlen} words in queue.")
|
| 166 |
|
| 167 |
elif data == "pause_scan":
|
| 168 |
state = await db.get_state()
|
| 169 |
if state.get("paused") == "1":
|
| 170 |
await db.set_state(paused="0")
|
| 171 |
-
await event.
|
| 172 |
-
await event.reply("βΆοΈ Scan resumed.")
|
| 173 |
else:
|
| 174 |
await db.set_state(paused="1")
|
| 175 |
-
await event.
|
| 176 |
-
await event.reply("βΈ Scan paused.")
|
| 177 |
|
| 178 |
elif data == "stop_scan":
|
| 179 |
await db.set_state(running="0", paused="0")
|
| 180 |
-
await event.
|
| 181 |
-
await event.reply("βΉ Scan stopped. Progress saved.")
|
| 182 |
|
| 183 |
-
elif data
|
| 184 |
-
msg = await generate_status_msg()
|
| 185 |
-
await event.answer()
|
| 186 |
-
await event.reply(msg, buttons=[[Button.inline("π Refresh Live", b"refresh_status")]])
|
| 187 |
-
|
| 188 |
-
elif data == "refresh_status":
|
| 189 |
msg = await generate_status_msg()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
try:
|
| 191 |
-
await event.edit(msg, buttons=
|
| 192 |
-
await event.answer("π Status Updated!")
|
| 193 |
except Exception as e:
|
| 194 |
-
|
| 195 |
-
await event.answer("β οΈ Already up to date.", alert=False)
|
| 196 |
-
else:
|
| 197 |
-
await event.answer("Error updating status.", alert=True)
|
| 198 |
|
| 199 |
elif data == "set_speed":
|
| 200 |
buttons = [
|
| 201 |
[Button.inline("π’ 10", b"spd_10"), Button.inline("πΆ 20", b"spd_20"), Button.inline("π 30", b"spd_30")],
|
| 202 |
-
[Button.inline("π 50", b"spd_50"), Button.inline("β‘ 75", b"spd_75"), Button.inline("π₯ 100", b"spd_100")]
|
|
|
|
| 203 |
]
|
| 204 |
-
await event.
|
| 205 |
-
await event.reply("Select Speed:", buttons=buttons)
|
| 206 |
|
| 207 |
elif data.startswith("spd_"):
|
| 208 |
try:
|
| 209 |
speed_limit = int(data.split("_")[1])
|
| 210 |
await db.set_concurrency(speed_limit)
|
| 211 |
-
await event.
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
await event.answer("β Invalid speed value.", alert=True)
|
| 215 |
-
except Exception as e:
|
| 216 |
-
await event.answer("β Error setting speed.", alert=True)
|
| 217 |
|
| 218 |
elif data == "export_files":
|
| 219 |
-
await event.
|
|
|
|
| 220 |
taken = sorted(await db.get_all_taken())
|
| 221 |
unavail = sorted(await db.get_all_unavailable())
|
| 222 |
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
|
|
|
| 226 |
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
|
|
|
| 230 |
|
| 231 |
-
await event.
|
| 232 |
|
| 233 |
elif data == "reset_confirm":
|
| 234 |
buttons = [
|
| 235 |
-
[Button.inline("β
Yes, WIPE ALL DATA", b"reset_do")
|
|
|
|
| 236 |
]
|
| 237 |
-
await event.
|
| 238 |
-
await event.reply("β οΈ Are you sure you want to completely wipe the database?", buttons=buttons)
|
| 239 |
|
| 240 |
elif data == "reset_do":
|
|
|
|
| 241 |
await db.flush_all()
|
| 242 |
-
await event.
|
| 243 |
-
await event.reply("π£ Database wiped. Click **π Load Words** to grab the next batch from the server disk!")
|
| 244 |
-
|
| 245 |
-
elif data == "reset_cancel":
|
| 246 |
-
await event.answer()
|
| 247 |
-
await event.reply("Cancelled.")
|
|
|
|
| 11 |
def is_admin(event):
|
| 12 |
return event.sender_id in get_admins()
|
| 13 |
|
| 14 |
+
def get_main_buttons():
|
| 15 |
+
return [
|
| 16 |
+
[Button.inline("βΆοΈ Start Scan", b"start_scan"), Button.inline("βΈ Pause", b"pause_scan")],
|
| 17 |
+
[Button.inline("βΉ Stop", b"stop_scan"), Button.inline("π Status", b"show_status")],
|
| 18 |
+
[Button.inline("β‘ Set Speed", b"set_speed"), Button.inline("π₯ Export", b"export_files")],
|
| 19 |
+
[Button.inline("π Load Words", b"load_words"), Button.inline("π£ Reset", b"reset_confirm")]
|
| 20 |
+
]
|
| 21 |
+
|
| 22 |
async def generate_status_msg():
|
| 23 |
state = await db.get_state()
|
| 24 |
counts = await db.get_counts()
|
|
|
|
| 63 |
await event.respond("β Unauthorized.")
|
| 64 |
return
|
| 65 |
|
| 66 |
+
await event.respond("π **Fragment Scanner Bot**\nSelect an action below:", buttons=get_main_buttons())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
@client.on(events.NewMessage(pattern='/upload'))
|
| 69 |
async def upload_cmd(event):
|
|
|
|
| 82 |
return
|
| 83 |
|
| 84 |
msg = await event.respond("π₯ Downloading massive file to server disk...")
|
|
|
|
| 85 |
await reply_msg.download_media(file="words.txt")
|
| 86 |
+
await msg.edit("β
**File uploaded to server successfully!**\n\nClick **π Load Words** to bite off the first 200,000 words into the database.", buttons=get_main_buttons())
|
|
|
|
| 87 |
|
| 88 |
@client.on(events.NewMessage(pattern='/load'))
|
| 89 |
async def load_cmd(event):
|
| 90 |
if not is_admin(event):
|
| 91 |
await event.respond("β Unauthorized.")
|
| 92 |
return
|
| 93 |
+
await _load_words(event, is_callback=False)
|
| 94 |
+
|
| 95 |
+
async def _load_words(event, is_callback=True):
|
| 96 |
+
async def update_msg(text):
|
| 97 |
+
if is_callback:
|
| 98 |
+
await event.edit(text, buttons=get_main_buttons())
|
| 99 |
+
else:
|
| 100 |
+
await event.respond(text, buttons=get_main_buttons())
|
| 101 |
|
|
|
|
| 102 |
try:
|
| 103 |
with open("words.txt", "r", encoding="utf-8") as f:
|
| 104 |
all_lines = [line.strip().lower() for line in f if line.strip()]
|
| 105 |
except Exception:
|
| 106 |
+
await update_msg("β `words.txt` not found. Please upload your master list using `/upload`.")
|
| 107 |
return
|
| 108 |
|
| 109 |
if not all_lines:
|
| 110 |
+
await update_msg("β οΈ The `words.txt` file on the server is empty. You've processed everything! Upload a new file.")
|
| 111 |
return
|
| 112 |
|
| 113 |
# --- SERVER DISK BUFFER LOGIC ---
|
|
|
|
| 132 |
except Exception as e:
|
| 133 |
err_str = str(e).lower()
|
| 134 |
if "oom" in err_str or "maxmemory" in err_str:
|
| 135 |
+
await update_msg("β **Error:** Redis ran out of memory! Click **π£ Reset**, then try loading again.")
|
| 136 |
else:
|
| 137 |
+
await update_msg(f"β **Database Error:** {str(e)}")
|
| 138 |
+
return
|
| 139 |
|
|
|
|
| 140 |
with open("words.txt", "w", encoding="utf-8") as f:
|
| 141 |
f.write("\n".join(remaining_lines))
|
| 142 |
|
|
|
|
| 149 |
else:
|
| 150 |
msg += "β
**No words remaining on server disk.**"
|
| 151 |
|
| 152 |
+
await update_msg(msg)
|
| 153 |
|
| 154 |
@client.on(events.CallbackQuery())
|
| 155 |
async def button_handler(event):
|
| 156 |
if not is_admin(event):
|
| 157 |
+
try:
|
| 158 |
+
await event.answer("β Unauthorized.", alert=True)
|
| 159 |
+
except Exception:
|
| 160 |
+
pass
|
| 161 |
return
|
| 162 |
|
| 163 |
+
# --- CRITICAL FIX: Answer immediately to prevent QueryIdInvalidError timeouts ---
|
| 164 |
+
try:
|
| 165 |
+
await event.answer()
|
| 166 |
+
except Exception:
|
| 167 |
+
pass
|
| 168 |
+
|
| 169 |
data = event.data.decode('utf-8')
|
| 170 |
|
| 171 |
+
if data == "back_main":
|
| 172 |
+
await event.edit("π **Fragment Scanner Bot**\nSelect an action below:", buttons=get_main_buttons())
|
| 173 |
+
|
| 174 |
+
elif data == "load_words":
|
| 175 |
+
await event.edit("β³ Reading and loading words... Please wait.")
|
| 176 |
+
await _load_words(event, is_callback=True)
|
| 177 |
|
| 178 |
elif data == "start_scan":
|
| 179 |
await db.set_state(running="1", paused="0", start_time=str(time.time()))
|
| 180 |
qlen = await db.queue_size()
|
| 181 |
+
await event.edit(f"π **Scan started!**\n`{qlen:,}` words currently in the processing queue.", buttons=get_main_buttons())
|
|
|
|
| 182 |
|
| 183 |
elif data == "pause_scan":
|
| 184 |
state = await db.get_state()
|
| 185 |
if state.get("paused") == "1":
|
| 186 |
await db.set_state(paused="0")
|
| 187 |
+
await event.edit("βΆοΈ **Scan resumed.**", buttons=get_main_buttons())
|
|
|
|
| 188 |
else:
|
| 189 |
await db.set_state(paused="1")
|
| 190 |
+
await event.edit("βΈ **Scan paused.**", buttons=get_main_buttons())
|
|
|
|
| 191 |
|
| 192 |
elif data == "stop_scan":
|
| 193 |
await db.set_state(running="0", paused="0")
|
| 194 |
+
await event.edit("βΉ **Scan stopped.** Progress has been saved.", buttons=get_main_buttons())
|
|
|
|
| 195 |
|
| 196 |
+
elif data in ("show_status", "refresh_status"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
msg = await generate_status_msg()
|
| 198 |
+
buttons = [
|
| 199 |
+
[Button.inline("π Refresh Live", b"refresh_status")],
|
| 200 |
+
[Button.inline("π Back to Menu", b"back_main")]
|
| 201 |
+
]
|
| 202 |
try:
|
| 203 |
+
await event.edit(msg, buttons=buttons)
|
|
|
|
| 204 |
except Exception as e:
|
| 205 |
+
pass # Ignores "Message is not modified" errors on spam click
|
|
|
|
|
|
|
|
|
|
| 206 |
|
| 207 |
elif data == "set_speed":
|
| 208 |
buttons = [
|
| 209 |
[Button.inline("π’ 10", b"spd_10"), Button.inline("πΆ 20", b"spd_20"), Button.inline("π 30", b"spd_30")],
|
| 210 |
+
[Button.inline("π 50", b"spd_50"), Button.inline("β‘ 75", b"spd_75"), Button.inline("π₯ 100", b"spd_100")],
|
| 211 |
+
[Button.inline("π Cancel", b"back_main")]
|
| 212 |
]
|
| 213 |
+
await event.edit("β‘ **Select Scanner Speed (Concurrent Workers):**", buttons=buttons)
|
|
|
|
| 214 |
|
| 215 |
elif data.startswith("spd_"):
|
| 216 |
try:
|
| 217 |
speed_limit = int(data.split("_")[1])
|
| 218 |
await db.set_concurrency(speed_limit)
|
| 219 |
+
await event.edit(f"β‘ **Speed updated successfully!**\nNow running with `{speed_limit}` concurrent workers.", buttons=get_main_buttons())
|
| 220 |
+
except Exception:
|
| 221 |
+
await event.edit("β Error setting speed.", buttons=get_main_buttons())
|
|
|
|
|
|
|
|
|
|
| 222 |
|
| 223 |
elif data == "export_files":
|
| 224 |
+
await event.edit("β³ **Exporting data...**\nPlease wait while files are generated.", buttons=get_main_buttons())
|
| 225 |
+
|
| 226 |
taken = sorted(await db.get_all_taken())
|
| 227 |
unavail = sorted(await db.get_all_unavailable())
|
| 228 |
|
| 229 |
+
if taken:
|
| 230 |
+
f_taken = io.BytesIO("\n".join(taken).encode('utf-8'))
|
| 231 |
+
f_taken.name = "taken.txt"
|
| 232 |
+
await client.send_file(event.chat_id, file=f_taken)
|
| 233 |
|
| 234 |
+
if unavail:
|
| 235 |
+
f_unavail = io.BytesIO("\n".join(unavail).encode('utf-8'))
|
| 236 |
+
f_unavail.name = "unavailable.txt"
|
| 237 |
+
await client.send_file(event.chat_id, file=f_unavail)
|
| 238 |
|
| 239 |
+
await event.edit(f"β
**Export Complete!**\nSent `{len(taken):,}` taken and `{len(unavail):,}` unavailable usernames.", buttons=get_main_buttons())
|
| 240 |
|
| 241 |
elif data == "reset_confirm":
|
| 242 |
buttons = [
|
| 243 |
+
[Button.inline("β
Yes, WIPE ALL DATA", b"reset_do")],
|
| 244 |
+
[Button.inline("β Cancel", b"back_main")]
|
| 245 |
]
|
| 246 |
+
await event.edit("β οΈ **DANGER ZONE** β οΈ\n\nAre you sure you want to completely wipe the Redis database? All progress will be lost.", buttons=buttons)
|
|
|
|
| 247 |
|
| 248 |
elif data == "reset_do":
|
| 249 |
+
await event.edit("β³ **Wiping database...**")
|
| 250 |
await db.flush_all()
|
| 251 |
+
await event.edit("π£ **Database wiped.**\n\nRedis is completely clear. Click **π Load Words** to grab the next batch from the server disk!", buttons=get_main_buttons())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|