File size: 10,989 Bytes
a924d18 9a3d0db a924d18 8ed08c9 fe1a797 a924d18 fe1a797 a924d18 7e893e0 75a85b3 7e893e0 700070c acd6d45 7c67b0d acd6d45 700070c 7c67b0d 700070c acd6d45 700070c 3cecbba 7e893e0 a924d18 2f65926 a924d18 2f65926 a924d18 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 | import os
import nest_asyncio
import logging
from flask import Flask
from threading import Thread
from pymongo import MongoClient
from telethon import TelegramClient, events
# Enable nested event loops for Flask + Telethon
nest_asyncio.apply()
logging.basicConfig(level=logging.INFO)
# === ENVIRONMENT VARIABLES ===
API_ID = int(os.getenv("API_ID", "3704772"))
API_HASH = os.getenv("API_HASH", "b8e50a035abb851c0dd424e14cac4c06")
BOT_TOKEN = os.getenv("TELEGRAM_TOKEN") # Bot token, NOT user session
OWNER_ID = int(os.getenv("OWNER_ID"))
MONGO_URI = os.getenv("MONGO_URI")
PORT = int(os.getenv("PORT", 7860))
# === MongoDB ===
client = MongoClient(MONGO_URI)
db = client.teleg4am_reelssss
a_raw = db.raw_links
a_reacted = db.reacted_links
last_alerts = {"raw": -1, "reacted": -1}
# Add indexes to improve performance (run only once on startup)
a_raw.create_index([("link", 1)], unique=True)
a_raw.create_index([("used", 1)])
a_reacted.create_index([("link", 1)], unique=True)
a_reacted.create_index([("used", 1)])
from telethon.sessions import StringSession
# === Flask App ===
app = Flask(__name__)
@app.route("/")
def home():
return "π€ Bot is running!"
@app.route("/status")
def status():
raw = a_raw.count_documents({"used": False})
reacted = a_reacted.count_documents({"used": False})
return {"raw_links_left": raw, "reacted_links_left": reacted}, 200
# === Telethon Bot Setup (Bot Mode) ===
bot = TelegramClient(StringSession(), API_ID, API_HASH).start(bot_token=BOT_TOKEN)
user_context = {}
# === Helper: Check if sender is owner ===
def is_owner(event):
return event.sender_id == OWNER_ID
# === Command Handlers ===
@bot.on(events.NewMessage(pattern="/start|/count"))
async def count_handler(event):
if not is_owner(event): return
raw = a_raw.count_documents({"used": False})
reacted = a_reacted.count_documents({"used": False})
await event.reply(f"π Raw: {raw}\nπ Reacted: {reacted}")
@bot.on(events.NewMessage(pattern="/raw"))
async def raw_handler(event):
if not is_owner(event): return
user_context[event.sender_id] = {"mode": "raw", "links": []}
await event.reply("π₯ Send raw links (one per line). Use /done to save.")
@bot.on(events.NewMessage(pattern="/reacted"))
async def reacted_handler(event):
if not is_owner(event): return
user_context[event.sender_id] = {"mode": "reacted", "links": []}
await event.reply("π₯ Send reacted links (one per line). Use /done to save.")
@bot.on(events.NewMessage(pattern="/done"))
async def done_handler(event):
if not is_owner(event): return
ctx = user_context.pop(event.sender_id, None)
if not ctx or not ctx["links"]:
return await event.reply("β Nothing to save.")
col = a_raw if ctx["mode"] == "raw" else a_reacted
inserted = 0
for link in ctx["links"]:
if col.count_documents({"link": link}) == 0:
col.insert_one({"link": link, "used": False})
inserted += 1
await event.reply(f"β
Saved {inserted} new {ctx['mode']} link(s).")
await check_and_alert(ctx["mode"], col)
@bot.on(events.NewMessage(func=lambda e: e.text and e.sender_id in user_context))
async def link_collector(event):
if not is_owner(event): return
ctx = user_context[event.sender_id]
if ctx["mode"] not in ["raw", "reacted"]:
return # Skip if it's correction-used or something else
links = [l.strip() for l in event.text.split() if l.startswith("http")]
ctx["links"].extend(links)
await event.reply(f"π {len(links)} link(s) added.")
# === Alert System ===
async def check_and_alert(pool: str, col):
left = col.count_documents({"used": False})
if 1 <= left <= 5 and last_alerts[pool] != left:
last_alerts[pool] = left
try:
await bot.send_message(OWNER_ID, f"β οΈ Only {left} {pool} link(s) left!")
except Exception as e:
logging.error(f"β Alert failed: {e}")
import os
import asyncio
import subprocess
from telethon import TelegramClient, events
from telethon.tl.types import DocumentAttributeFilename
@bot.on(events.NewMessage(pattern="/bash"))
async def bash(event):
cmd = event.message.text.split(None, 1)
if len(cmd) < 2:
return await event.reply("β Usage: /bash <command>")
process = await asyncio.create_subprocess_shell(
cmd[1],
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
stdout, stderr = await process.communicate()
output = stdout.decode() or "β
No output"
error = stderr.decode()
await event.reply(f"π₯ Code:\n`{cmd[1]}`\n\nπ€ Output:\n```\n{output.strip()}\n```"
+ (f"\nβ Error:\n```\n{error.strip()}\n```" if error else ""))
@bot.on(events.NewMessage(pattern="/dl"))
async def dl(event):
reply = await event.get_reply_message()
if not reply or not reply.media:
return await event.reply("β Reply to a media file to download it.")
path = await bot.download_media(reply)
await event.reply(f"β
File downloaded to `{path}`")
@bot.on(events.NewMessage(pattern="/ul"))
async def ul(event):
args = event.raw_text.split(maxsplit=1)
if len(args) != 2:
return await event.reply("β Usage: /ul <full_filename>")
filepath = args[1].strip()
if not os.path.exists(filepath):
return await event.reply("β File not found.")
await bot.send_file(event.chat_id, filepath, caption=f"π€ Uploaded: `{filepath}`")
@bot.on(events.NewMessage(pattern=r'^/doc(?:\s+(.+))?'))
async def handler_doc(event):
reply = await event.get_reply_message()
filename = event.pattern_match.group(1)
if not reply or not reply.text:
await event.reply("β Please reply to a text message to save as a file.")
return
if not filename:
await event.reply("β Usage: `/doc filename.txt`", parse_mode='md')
return
try:
with open(filename, "w", encoding="utf-8") as f:
f.write(reply.text)
await bot.send_file(event.chat_id, filename, caption=f"β
Saved reply to `{filename}`", force_document=True)
os.remove(filename)
except Exception as e:
await event.reply(f"β Error: {e}")
user_context = {} # make sure this is defined once globally
@bot.on(events.NewMessage(pattern="/cused"))
async def correction_used_handler(event):
if not is_owner(event): return
user_context[event.sender_id] = {"mode": "correct_used", "links": []}
await event.reply("π Correction mode ON for used links.\nSend all links (one per line). Use /doneused when finished.")
@bot.on(events.NewMessage(func=lambda e: e.text and e.sender_id in user_context and not e.raw_text.startswith("/doneused")))
async def collect_links(event):
if not is_owner(event): return
ctx = user_context.get(event.sender_id)
if ctx and ctx.get("mode") == "correct_used":
links = [l.strip() for l in event.text.split() if l.startswith("http")]
if links:
ctx["links"].extend(links)
await event.reply(f"π Collected {len(links)} link(s). Send more or use /dused.")
else:
await event.reply("β οΈ No valid links detected. Please send links starting with http.")
@bot.on(events.NewMessage(pattern="/dused"))
async def process_correction(event):
if not is_owner(event): return
ctx = user_context.get(event.sender_id)
if not ctx or ctx.get("mode") != "correct_used":
return await event.reply("β οΈ Not in correction-used mode. Use /cused first.")
links = ctx.get("links", [])
updated_raw = updated_reacted = skipped = not_found = 0
for link in links:
found = False
doc_raw = a_raw.find_one({"link": link})
if doc_raw:
found = True
if doc_raw.get("used") is True:
a_raw.update_one({"_id": doc_raw["_id"]}, {"$set": {"used": False}})
updated_raw += 1
else:
skipped += 1
doc_reacted = a_reacted.find_one({"link": link})
if doc_reacted:
found = True
if doc_reacted.get("used") is True:
a_reacted.update_one({"_id": doc_reacted["_id"]}, {"$set": {"used": False}})
updated_reacted += 1
else:
skipped += 1
if not found:
not_found += 1
del user_context[event.sender_id] # Clear mode after done
await event.reply(
f"β
Done processing {len(links)} link(s).\n\n"
f"π¦ Raw updated: {updated_raw}\n"
f"π Reacted updated: {updated_reacted}\n"
f"β Skipped (already unused): {skipped}\n"
f"β Not found in both: {not_found}"
)
@bot.on(events.NewMessage(pattern="/help"))
async def help_handler(event):
if not is_owner(event): return
await event.reply(
"**π€ Available Commands:**\n\n"
"**π Status & Counts**\n"
"`/start` β Show total raw/reacted links left\n"
"`/count` β Same as /start (alias)\n"
"`/status` β (Web endpoint) Get JSON of links left\n\n"
"**π₯ Add Links**\n"
"`/raw` β Start adding raw links\n"
"`/reacted` β Start adding reacted links\n"
"`/done` β Save links after sending\n\n"
"**π Correct Used Flags**\n"
"`/cused` β Start correction for used links\n"
"`/dused` β Finalize correction and update DB\n\n"
"**π» File & Shell Tools**\n"
"`/bash <command>` β Run a terminal command\n"
"`/dl` β Download replied media\n"
"`/ul <filename>` β Upload a file from disk\n"
"`/doc <filename>` β Save replied text to file and upload\n\n"
"**βΉοΈ Notes:**\n"
"- Only you (owner) can use these commands\n"
"- All links must start with `http`\n"
"- You can send multiple links in one message\n"
"- Correction only flips `used: True β False`, not adding\n",
parse_mode='md'
)
def run_flask():
app.run(host="0.0.0.0", port=PORT)
import asyncio
async def auto_alert_loop():
while True:
try:
await check_and_alert("raw", a_raw)
await check_and_alert("reacted", a_reacted)
except Exception as e:
logging.error(f"Auto-alert loop error: {e}")
await asyncio.sleep(30) # Check every 30 seconds
async def main():
if not all([API_ID, API_HASH, BOT_TOKEN, MONGO_URI]):
raise RuntimeError("β Missing ENV vars: API_ID, API_HASH, TELEGRAM_TOKEN, or MONGO_URI")
Thread(target=run_flask).start()
# Start background alert loop
asyncio.create_task(auto_alert_loop())
# Start Flask in a separate thread (or you can replace with FastAPI if needed
# Start the bot
print("β
Starting bot...")
await bot.start(bot_token=BOT_TOKEN)
await bot.run_until_disconnected()
if __name__ == "__main__":
import asyncio
asyncio.run(main())
|