Muttered3 commited on
Commit
dafbf67
Β·
verified Β·
1 Parent(s): 65b968a

Create bot.py

Browse files
Files changed (1) hide show
  1. bot.py +170 -0
bot.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import time
4
+ import io
5
+ from telethon import events, Button
6
+ import db
7
+
8
+ def get_admins():
9
+ return {int(x) for x in os.environ.get("ADMIN_IDS", "").split(",") if x}
10
+
11
+ def is_admin(event):
12
+ return event.sender_id in get_admins()
13
+
14
+ def setup_handlers(client):
15
+ @client.on(events.NewMessage(pattern='/start'))
16
+ async def start_cmd(event):
17
+ if not is_admin(event):
18
+ await event.respond("β›” Unauthorized.")
19
+ return
20
+
21
+ buttons = [
22
+ [Button.inline("▢️ Start Scan", b"start_scan"), Button.inline("⏸ Pause", b"pause_scan")],
23
+ [Button.inline("⏹ Stop", b"stop_scan"), Button.inline("πŸ“Š Status", b"show_status")],
24
+ [Button.inline("⚑ Set Speed", b"set_speed"), Button.inline("πŸ“₯ Export", b"export_files")],
25
+ [Button.inline("πŸ”„ Load Words", b"load_words"), Button.inline("πŸ’£ Reset", b"reset_confirm")]
26
+ ]
27
+ await event.respond("πŸ‘‹ Fragment Scanner Bot", buttons=buttons)
28
+
29
+ @client.on(events.NewMessage(pattern='/load'))
30
+ async def load_cmd(event):
31
+ if not is_admin(event):
32
+ await event.respond("β›” Unauthorized.")
33
+ return
34
+ await _load_words(event.respond)
35
+
36
+ async def _load_words(reply_func):
37
+ try:
38
+ with open("words.txt", "r", encoding="utf-8") as f:
39
+ words = [line.strip().lower() for line in f]
40
+ except Exception:
41
+ await reply_func("❌ words.txt not found.")
42
+ return
43
+
44
+ valid_words = [w for w in set(words) if re.match(r'^[a-z0-9_]{4,32}$', w)]
45
+
46
+ r = await db.get_redis()
47
+ done = await r.smembers("frag:done")
48
+
49
+ to_queue = [w for w in valid_words if w not in done]
50
+
51
+ if to_queue:
52
+ pipe = r.pipeline()
53
+ for i in range(0, len(to_queue), 1000):
54
+ pipe.lpush("frag:queue", *to_queue[i:i+1000])
55
+ await pipe.execute()
56
+
57
+ await db.set_state(total=len(valid_words))
58
+ skipped = len(valid_words) - len(to_queue)
59
+ await reply_func(f"πŸ“₯ Loaded {len(to_queue)} words. Skipped {skipped} (already done).")
60
+
61
+ @client.on(events.CallbackQuery())
62
+ async def button_handler(event):
63
+ if not is_admin(event):
64
+ await event.answer("β›” Unauthorized.", alert=True)
65
+ return
66
+
67
+ data = event.data.decode('utf-8')
68
+
69
+ if data == "load_words":
70
+ await event.answer()
71
+ await _load_words(event.reply)
72
+
73
+ elif data == "start_scan":
74
+ await db.set_state(running="1", paused="0", start_time=str(time.time()))
75
+ qlen = await db.queue_size()
76
+ await event.answer()
77
+ await event.reply(f"πŸš€ Scan started! {qlen} words in queue.")
78
+
79
+ elif data == "pause_scan":
80
+ state = await db.get_state()
81
+ if state.get("paused") == "1":
82
+ await db.set_state(paused="0")
83
+ await event.answer()
84
+ await event.reply("▢️ Scan resumed.")
85
+ else:
86
+ await db.set_state(paused="1")
87
+ await event.answer()
88
+ await event.reply("⏸ Scan paused.")
89
+
90
+ elif data == "stop_scan":
91
+ await db.set_state(running="0", paused="0")
92
+ await event.answer()
93
+ await event.reply("⏹ Scan stopped. Progress saved.")
94
+
95
+ elif data == "show_status":
96
+ state = await db.get_state()
97
+ counts = await db.get_counts()
98
+ concurrency = await db.get_concurrency()
99
+ qlen = await db.queue_size()
100
+
101
+ total = int(state.get("total", 0))
102
+ processed = int(state.get("processed", 0))
103
+ pct = (processed / total * 100) if total > 0 else 0
104
+ run_str = "YES" if state.get("running") == "1" else "NO"
105
+ pause_str = "YES" if state.get("paused") == "1" else "NO"
106
+
107
+ msg = (
108
+ f"**πŸ“Š SCANNER STATUS**\n"
109
+ f"━━━━━━━━━━━━━━━━━━━\n"
110
+ f"🟒 Running : `{run_str:<10}`\n"
111
+ f"⏸ Paused : `{pause_str:<10}`\n"
112
+ f"πŸ“¦ Total : `{total:<10,}`\n"
113
+ f"βœ… Processed : `{processed:<10,}`\n"
114
+ f"πŸ“ˆ Progress : `{pct:.2f}%`\n"
115
+ f"━━━━━━━━━━━━━━━━━━━\n"
116
+ f"πŸ”΄ Taken : `{counts['taken']:<10,}`\n"
117
+ f"🚫 Unavail : `{counts['unavailable']:<10,}`\n"
118
+ f"πŸ’° For Sale : `{counts['forsale']:<10,}`\n"
119
+ f"πŸ”¨ Auction : `{counts['auction']:<10,}`\n"
120
+ f"━━━━━━━━━━━━━━━━━━━\n"
121
+ f"⚑ Speed : `{concurrency:<10}`\n"
122
+ f"πŸ“‹ Queue : `{qlen:<10,}`\n"
123
+ )
124
+ await event.answer()
125
+ await event.reply(msg)
126
+
127
+ elif data == "set_speed":
128
+ buttons = [
129
+ [Button.inline("🐒 10", b"spd_10"), Button.inline("🚢 20", b"spd_20"), Button.inline("πŸš— 30", b"spd_30")],
130
+ [Button.inline("πŸš€ 50", b"spd_50"), Button.inline("⚑ 75", b"spd_75"), Button.inline("πŸ”₯ 100", b"spd_100")]
131
+ ]
132
+ await event.answer()
133
+ await event.reply("Select Speed:", buttons=buttons)
134
+
135
+ elif data.startswith("spd_"):
136
+ val = int(data.split("_")[1])
137
+ await db.set_concurrency(val)
138
+ await event.answer()
139
+ await event.reply(f"⚑ Speed set to {val} concurrent workers.")
140
+
141
+ elif data == "export_files":
142
+ await event.answer()
143
+ taken = sorted(await db.get_all_taken())
144
+ unavail = sorted(await db.get_all_unavailable())
145
+
146
+ f_taken = io.BytesIO("\n".join(taken).encode('utf-8'))
147
+ f_taken.name = "taken.txt"
148
+ await client.send_file(event.chat_id, file=f_taken)
149
+
150
+ f_unavail = io.BytesIO("\n".join(unavail).encode('utf-8'))
151
+ f_unavail.name = "unavailable.txt"
152
+ await client.send_file(event.chat_id, file=f_unavail)
153
+
154
+ await event.reply(f"βœ… Exported {len(taken)} taken, {len(unavail)} unavailable.")
155
+
156
+ elif data == "reset_confirm":
157
+ buttons = [
158
+ [Button.inline("βœ… Yes, WIPE ALL DATA", b"reset_do"), Button.inline("❌ Cancel", b"reset_cancel")]
159
+ ]
160
+ await event.answer()
161
+ await event.reply("⚠️ Are you sure you want to completely wipe the database?", buttons=buttons)
162
+
163
+ elif data == "reset_do":
164
+ await db.flush_all()
165
+ await event.answer()
166
+ await event.reply("πŸ’£ All data wiped. Use /load to reload words.")
167
+
168
+ elif data == "reset_cancel":
169
+ await event.answer()
170
+ await event.reply("Cancelled.")