soxogvv commited on
Commit
7e893e0
Β·
verified Β·
1 Parent(s): 8ed08c9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +236 -0
app.py CHANGED
@@ -181,6 +181,209 @@ async def handler_doc(event):
181
  user_context = {} # make sure this is defined once globally
182
 
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  @bot.on(events.NewMessage(pattern="/cused"))
185
  async def correction_used_handler(event):
186
  if not is_owner(event): return
@@ -251,6 +454,39 @@ def run_flask():
251
 
252
  import asyncio
253
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  async def auto_alert_loop():
255
  while True:
256
  try:
 
181
  user_context = {} # make sure this is defined once globally
182
 
183
 
184
+ @bot.on(events.NewMessage(pattern="/cused"))
185
+ async def correction_used_handler(event):
186
+ if not is_owner(event): return
187
+ user_context[event.sender_id] = {"mode": "correct_used", "links": []}
188
+ await event.reply("πŸ›  Correction mode ON for used links.\nSend all links (one per line). Use /doneused when finished.")
189
+
190
+
191
+ @bot.on(events.NewMessage(func=lambda e: e.text and e.sender_id in user_context and not e.raw_text.startswith("/doneused")))
192
+ async def collect_links(event):
193
+ if not is_owner(event): return
194
+ ctx = user_context.get(event.sender_id)
195
+ if ctx and ctx.get("mode") == "correct_used":
196
+ links = [l.strip() for l in event.text.split() if l.startswith("http")]
197
+ if links:
198
+ ctx["links"].extend(links)
199
+ await event.reply(f"πŸ”— Collected {len(links)} link(s). Send more or use /doneused.")
200
+ else:
201
+ await event.reply("⚠️ No valid links detected. Please send links starting with http.")
202
+
203
+
204
+ import os
205
+ import nest_asyncio
206
+ import logging
207
+ from flask import Flask
208
+ from threading import Thread
209
+ from pymongo import MongoClient
210
+ from telethon import TelegramClient, events
211
+
212
+ # Enable nested event loops for Flask + Telethon
213
+ nest_asyncio.apply()
214
+ logging.basicConfig(level=logging.INFO)
215
+
216
+ # === ENVIRONMENT VARIABLES ===
217
+ API_ID = int(os.getenv("API_ID", "3704772"))
218
+ API_HASH = os.getenv("API_HASH", "b8e50a035abb851c0dd424e14cac4c06")
219
+ BOT_TOKEN = os.getenv("TELEGRAM_TOKEN") # Bot token, NOT user session
220
+ OWNER_ID = int(os.getenv("OWNER_ID"))
221
+ MONGO_URI = os.getenv("MONGO_URI")
222
+ PORT = int(os.getenv("PORT", 7860))
223
+
224
+ # === MongoDB ===
225
+ client = MongoClient(MONGO_URI)
226
+ db = client.teleg4am_reelssss
227
+ a_raw = db.raw_links
228
+ a_reacted = db.reacted_links
229
+ last_alerts = {"raw": -1, "reacted": -1}
230
+ # Add indexes to improve performance (run only once on startup)
231
+ a_raw.create_index([("link", 1)], unique=True)
232
+ a_raw.create_index([("used", 1)])
233
+ a_reacted.create_index([("link", 1)], unique=True)
234
+ a_reacted.create_index([("used", 1)])
235
+
236
+ # === Flask App ===
237
+ app = Flask(__name__)
238
+
239
+ @app.route("/")
240
+ def home():
241
+ return "πŸ€– Bot is running!"
242
+
243
+ @app.route("/status")
244
+ def status():
245
+ raw = a_raw.count_documents({"used": False})
246
+ reacted = a_reacted.count_documents({"used": False})
247
+ return {"raw_links_left": raw, "reacted_links_left": reacted}, 200
248
+
249
+ # === Telethon Bot Setup (Bot Mode) ===
250
+ bot = TelegramClient("bot_session", API_ID, API_HASH).start(bot_token=BOT_TOKEN)
251
+ user_context = {}
252
+
253
+ # === Helper: Check if sender is owner ===
254
+ def is_owner(event):
255
+ return event.sender_id == OWNER_ID
256
+
257
+ # === Command Handlers ===
258
+ @bot.on(events.NewMessage(pattern="/start|/count"))
259
+ async def count_handler(event):
260
+ if not is_owner(event): return
261
+ raw = a_raw.count_documents({"used": False})
262
+ reacted = a_reacted.count_documents({"used": False})
263
+ await event.reply(f"πŸ“Š Raw: {raw}\nπŸ“Š Reacted: {reacted}")
264
+
265
+ @bot.on(events.NewMessage(pattern="/raw"))
266
+ async def raw_handler(event):
267
+ if not is_owner(event): return
268
+ user_context[event.sender_id] = {"mode": "raw", "links": []}
269
+ await event.reply("πŸ“₯ Send raw links (one per line). Use /done to save.")
270
+
271
+ @bot.on(events.NewMessage(pattern="/reacted"))
272
+ async def reacted_handler(event):
273
+ if not is_owner(event): return
274
+ user_context[event.sender_id] = {"mode": "reacted", "links": []}
275
+ await event.reply("πŸ“₯ Send reacted links (one per line). Use /done to save.")
276
+
277
+ @bot.on(events.NewMessage(pattern="/done"))
278
+ async def done_handler(event):
279
+ if not is_owner(event): return
280
+ ctx = user_context.pop(event.sender_id, None)
281
+ if not ctx or not ctx["links"]:
282
+ return await event.reply("❌ Nothing to save.")
283
+
284
+ col = a_raw if ctx["mode"] == "raw" else a_reacted
285
+ inserted = 0
286
+ for link in ctx["links"]:
287
+ if col.count_documents({"link": link}) == 0:
288
+ col.insert_one({"link": link, "used": False})
289
+ inserted += 1
290
+
291
+ await event.reply(f"βœ… Saved {inserted} new {ctx['mode']} link(s).")
292
+ await check_and_alert(ctx["mode"], col)
293
+
294
+ @bot.on(events.NewMessage(pattern="/cleanup"))
295
+ async def cleanup_handler(event):
296
+ if not is_owner(event): return
297
+ raw_deleted = a_raw.delete_many({"used": True}).deleted_count
298
+ reacted_deleted = a_reacted.delete_many({"used": True}).deleted_count
299
+ await event.reply(f"πŸ—‘οΈ Deleted:\nRaw: {raw_deleted}\nReacted: {reacted_deleted}")
300
+
301
+ @bot.on(events.NewMessage(func=lambda e: e.text and e.sender_id in user_context))
302
+ async def link_collector(event):
303
+ if not is_owner(event): return
304
+ ctx = user_context[event.sender_id]
305
+ links = [l.strip() for l in event.text.split() if l.startswith("http")]
306
+ ctx["links"].extend(links)
307
+ await event.reply(f"πŸ”— {len(links)} link(s) added.")
308
+
309
+ # === Alert System ===
310
+ async def check_and_alert(pool: str, col):
311
+ left = col.count_documents({"used": False})
312
+ if 1 <= left <= 5 and last_alerts[pool] != left:
313
+ last_alerts[pool] = left
314
+ try:
315
+ await bot.send_message(OWNER_ID, f"⚠️ Only {left} {pool} link(s) left!")
316
+ except Exception as e:
317
+ logging.error(f"❌ Alert failed: {e}")
318
+ import os
319
+ import asyncio
320
+ import subprocess
321
+ from telethon import TelegramClient, events
322
+ from telethon.tl.types import DocumentAttributeFilename
323
+
324
+ @bot.on(events.NewMessage(pattern="/bash"))
325
+ async def bash(event):
326
+ cmd = event.message.text.split(None, 1)
327
+ if len(cmd) < 2:
328
+ return await event.reply("❌ Usage: /bash <command>")
329
+ process = await asyncio.create_subprocess_shell(
330
+ cmd[1],
331
+ stdout=asyncio.subprocess.PIPE,
332
+ stderr=asyncio.subprocess.PIPE,
333
+ )
334
+ stdout, stderr = await process.communicate()
335
+
336
+ output = stdout.decode() or "βœ… No output"
337
+ error = stderr.decode()
338
+
339
+ await event.reply(f"πŸ“₯ Code:\n`{cmd[1]}`\n\nπŸ“€ Output:\n```\n{output.strip()}\n```"
340
+ + (f"\n❌ Error:\n```\n{error.strip()}\n```" if error else ""))
341
+
342
+ @bot.on(events.NewMessage(pattern="/dl"))
343
+ async def dl(event):
344
+ reply = await event.get_reply_message()
345
+ if not reply or not reply.media:
346
+ return await event.reply("❌ Reply to a media file to download it.")
347
+
348
+ path = await bot.download_media(reply)
349
+ await event.reply(f"βœ… File downloaded to `{path}`")
350
+
351
+ @bot.on(events.NewMessage(pattern="/ul"))
352
+ async def ul(event):
353
+ args = event.raw_text.split(maxsplit=1)
354
+ if len(args) != 2:
355
+ return await event.reply("❌ Usage: /ul <full_filename>")
356
+
357
+ filepath = args[1].strip()
358
+ if not os.path.exists(filepath):
359
+ return await event.reply("❌ File not found.")
360
+
361
+ await bot.send_file(event.chat_id, filepath, caption=f"πŸ“€ Uploaded: `{filepath}`")
362
+
363
+ @bot.on(events.NewMessage(pattern=r'^/doc(?:\s+(.+))?'))
364
+ async def handler_doc(event):
365
+ reply = await event.get_reply_message()
366
+ filename = event.pattern_match.group(1)
367
+
368
+ if not reply or not reply.text:
369
+ await event.reply("❌ Please reply to a text message to save as a file.")
370
+ return
371
+
372
+ if not filename:
373
+ await event.reply("❌ Usage: `/doc filename.txt`", parse_mode='md')
374
+ return
375
+
376
+ try:
377
+ with open(filename, "w", encoding="utf-8") as f:
378
+ f.write(reply.text)
379
+
380
+ await bot.send_file(event.chat_id, filename, caption=f"βœ… Saved reply to `{filename}`", force_document=True)
381
+ os.remove(filename)
382
+ except Exception as e:
383
+ await event.reply(f"❌ Error: {e}")
384
+ user_context = {} # make sure this is defined once globally
385
+
386
+
387
  @bot.on(events.NewMessage(pattern="/cused"))
388
  async def correction_used_handler(event):
389
  if not is_owner(event): return
 
454
 
455
  import asyncio
456
 
457
+ async def auto_alert_loop():
458
+ while True:
459
+ try:
460
+ await check_and_alert("raw", a_raw)
461
+ await check_and_alert("reacted", a_reacted)
462
+ except Exception as e:
463
+ logging.error(f"Auto-alert loop error: {e}")
464
+ await asyncio.sleep(30) # Check every 30 seconds
465
+
466
+ async def main():
467
+ if not all([API_ID, API_HASH, BOT_TOKEN, MONGO_URI]):
468
+ raise RuntimeError("❌ Missing ENV vars: API_ID, API_HASH, TELEGRAM_TOKEN, or MONGO_URI")
469
+
470
+ # Start background alert loop
471
+ asyncio.create_task(auto_alert_loop())
472
+
473
+ # Start Flask in a separate thread (or you can replace with FastAPI if needed)
474
+ Thread(target=run_flask).start()
475
+
476
+ # Start the bot
477
+ print("βœ… Starting bot...")
478
+ await bot.start(bot_token=BOT_TOKEN)
479
+ await bot.run_until_disconnected()
480
+
481
+ if __name__ == "__main__":
482
+ import asyncio
483
+ asyncio.run(main())
484
+ # === Flask Server ===
485
+ def run_flask():
486
+ app.run(host="0.0.0.0", port=PORT)
487
+
488
+ import asyncio
489
+
490
  async def auto_alert_loop():
491
  while True:
492
  try: