| import os |
| import sys |
| import logging |
| import nest_asyncio |
| import random |
| import asyncio |
| import pytz |
| from flask import Flask |
| from threading import Thread |
| from datetime import datetime, timedelta |
| from pymongo import MongoClient |
| from apscheduler.schedulers.asyncio import AsyncIOScheduler |
| from apscheduler.triggers.cron import CronTrigger |
| from telethon import TelegramClient, events, Button |
| from telethon.sessions import StringSession |
| from telethon.tl import functions |
| import re |
| from telethon.tl.custom import Conversation |
| from telethon import TelegramClient, events |
| from telethon.sessions import StringSession |
| from session_converter import SessionManager |
|
|
| |
| adjectives = ["Silent", "Crazy", "Golden", "Hidden", "Brave", "Fierce", "Mysterious", "Savage", "Royal", "Witty", "Legendary", "Epic", "Noble", "Clever", "Loyal", "Wild", "Electric", "Fearless", "Crimson", "Frozen", "Charming", "Rebel", "Turbo", "Lone", "Radiant", "Raging", "Dusty", "Majestic", "Grumpy", "Shadow"] |
| nouns = ["Warriors", "Coders", "Friends", "Legends", "Pirates", "Tigers", "Wolves", "Hackers", "Ninjas", "Riders", "Hunters", "Kings", "Queens", "Giants", "Lords", "Champions", "Stormers", "Creators", "Ghosts", "Wizards", "Knights", "Samurais", "Explorers", "Titans", "Outlaws", "Rebels", "Squad", "Troopers", "Dreamers", "Avengers"] |
|
|
| logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(message)s", stream=sys.stdout) |
| log = logging.getLogger("app") |
|
|
| nest_asyncio.apply() |
| API_ID = int(os.getenv("API_ID", 3704772)) |
| API_HASH = os.getenv("API_HASH", "b8e50a035abb851c0dd424e14cac4c06") |
| BOT_TOKEN = os.getenv("BOT_TOKEN") |
| MONGO_URI = os.getenv("MONGO_URI") |
| ADMIN_ID = int(os.getenv("ADMIN_ID", 6574063018)) |
|
|
| client = MongoClient(MONGO_URI) |
| db = client["telethon_bots"] |
| accounts = db["accounts"] |
| plans = db["plans"] |
|
|
| bot = TelegramClient("bot", API_ID, API_HASH) |
| userbots = {} |
| scheduler = AsyncIOScheduler() |
|
|
| def random_group_name(): |
| return f"{random.choice(adjectives)} {random.choice(nouns)}" |
|
|
| |
| tz = pytz.timezone("Asia/Kolkata") |
|
|
| |
| userbots = {} |
|
|
| def create_plan(account, limit): |
| now = datetime.now(tz) |
| end_of_day = now.replace(hour=23, minute=59, second=59, microsecond=0) |
| time_gap = (end_of_day - now) / limit |
|
|
| times = [] |
| for i in range(limit): |
| planned_time = now + i * time_gap |
| |
| if planned_time <= now: |
| planned_time = now + timedelta(minutes=1) |
| times.append(planned_time) |
|
|
| for t in times: |
| plans.insert_one({ |
| "account_id": account["_id"], |
| "scheduled_time": t.isoformat() |
| }) |
| print(f"β
Created {len(times)} plan(s) for account {account['_id']}") |
|
|
|
|
| def apply_plan(): |
| now = datetime.now(tz) |
| count = 0 |
| for plan in plans.find(): |
| plan_time = datetime.fromisoformat(plan["scheduled_time"]).astimezone(tz) |
| if plan_time > now: |
| account = accounts.find_one({"_id": plan["account_id"]}) |
| if not account: |
| continue |
|
|
| userbot = userbots.get(account["_id"]) |
| if not userbot: |
| print(f"β οΈ No userbot for account {account['_id']}, skipping...") |
| continue |
|
|
| def job_wrapper(acc_id): |
| def job(): |
| print(f"π Running job for account {acc_id} at {datetime.now(tz)}") |
| |
| return job |
|
|
| job_id = f"job_{plan['_id']}" |
| scheduler.add_job(job_wrapper(account["_id"]), trigger='date', run_date=plan_time, id=job_id) |
| print(f"π Scheduled job for account {account['_id']} at {plan_time.strftime('%H:%M:%S')} (ID: {job_id})") |
| count += 1 |
| print(f"β
Applied and scheduled {count} job(s).") |
|
|
|
|
| def reset_and_plan(): |
| print("π Resetting plans and scheduling new ones...") |
| plans.delete_many({}) |
| for acc in accounts.find(): |
| limit = random.randint(3, 5) |
| create_plan(acc, limit) |
| apply_plan() |
| print("β
Plans created and scheduled.") |
|
|
| |
| scheduler.add_job(reset_and_plan, CronTrigger(hour=0, minute=0, timezone="Asia/Kolkata"), id="daily_reset_job", replace_existing=True) |
|
|
| |
| @bot.on(events.NewMessage(pattern=r'^/reset$')) |
| async def handle_reset(event): |
| sender = await event.get_sender() |
| if sender.id != ADMIN_ID: |
| return await event.reply("β You are not authorized to run this command.") |
|
|
| for job in scheduler.get_jobs(): |
| if job.func.__name__ == "reset_and_plan" or job.id == "daily_reset_job": |
| job.remove() |
|
|
| reset_and_plan() |
| scheduler.add_job(reset_and_plan, CronTrigger(hour=0, minute=0, timezone="Asia/Kolkata"), id="daily_reset_job", replace_existing=True) |
| await event.reply("β
Schedule reset and re-planned for tonight 00:00 IST.") |
|
|
| @bot.on(events.NewMessage(pattern=r'^/stats$')) |
| async def handle_stats(event): |
| tz = pytz.timezone("Asia/Kolkata") |
| now = datetime.now(tz) |
|
|
| msg = "π **Today's Schedule:**\n\n" |
| msg += f"ποΈ Date: {now.strftime('%d/%m/%Y')}\n" |
| msg += f"π Time: {now.strftime('%I:%M %p')} IST\n\n" |
|
|
| plans_today = list(plans.find()) |
| if not plans_today: |
| return await event.reply("π No plans found today.") |
|
|
| for plan in plans_today: |
| acc = accounts.find_one({"user_id": plan["user_id"]}) |
| if not acc: |
| continue |
| name = acc["name"] |
| times_str = ", ".join([t.astimezone(tz).strftime("%I:%M %p") for t in plan["times"]]) |
| created = plan.get("created", 0) |
| msg += f"π€ **{name}** β `{times_str}`\nβ
Created: `{created}`\n\n" |
|
|
| next_run = None |
| for job in scheduler.get_jobs(): |
| if job.id == "daily_reset_job": |
| next_run = job.next_run_time |
| break |
| if next_run: |
| msg += f"π Next Reset: `{next_run.astimezone(tz).strftime('%d/%m/%Y %I:%M %p')} IST`" |
|
|
| await event.reply(msg) |
|
|
|
|
| async def auto_create_groups(userbot, acc_doc, plan_doc=None): |
| if plan_doc and plan_doc["created"] >= len(plan_doc["times"]): |
| await bot.send_message(ADMIN_ID, f"β Group creation skipped: Limit exceeded for {acc_doc['name']}") |
| return |
|
|
| try: |
| title = random_group_name() |
| result = await userbot(functions.channels.CreateChannelRequest(title=title, about=",", megagroup=True)) |
| group = result.chats[0].id |
|
|
| for _ in range(4): |
| await userbot.send_message(group, random_group_name()) |
| await asyncio.sleep(2) |
|
|
| try: |
| stickers = ["CAACAgUAAxkBAAEKZStlTxkC-cvQJwABvHoUjRacAVIHCFcAAgECAALaTqVVCpx_Lc2ZoLguBA"] |
| await userbot.send_file(group, random.choice(stickers)) |
| except: |
| pass |
|
|
| if plan_doc: |
| plans.update_one({"user_id": acc_doc["user_id"]}, {"$inc": {"created": 1}}) |
|
|
| info = await userbot.get_me() |
| await bot.send_message(ADMIN_ID, f"β
Created 1 group\nπ€ {info.first_name}\nπ @{info.username or 'N/A'}\nπ Created today: {plan_doc['created'] + 1 if plan_doc else 'Manual'}") |
| except Exception as e: |
| await bot.send_message(ADMIN_ID, f"β Group creation failed for {acc_doc['name']}: {e}") |
| log.error(f"β Group creation failed: {e}") |
|
|
| async def start_userbot(session_str, owner_id): |
| try: |
| userbot = TelegramClient(StringSession(session_str), API_ID, API_HASH) |
| await userbot.start() |
| info = await userbot.get_me() |
| if accounts.find_one({"user_id": info.id}): |
| return None |
| doc = { |
| "user_id": info.id, |
| "name": info.first_name, |
| "username": info.username or None, |
| "session": session_str, |
| "created": 0, |
| "owner_id": owner_id, |
| "added_on": datetime.utcnow() |
| } |
| accounts.insert_one(doc) |
| userbots[info.id] = userbot |
| asyncio.create_task(userbot.run_until_disconnected()) |
| return info |
| except Exception as e: |
| log.info(f"β Failed to add account: {e}") |
| return None |
|
|
| async def load_all_userbots(): |
| log.info("π Loading all stored userbots...") |
| for acc in accounts.find(): |
| try: |
| ub = TelegramClient(StringSession(acc["session"]), API_ID, API_HASH) |
| await ub.start() |
| userbots[acc["user_id"]] = ub |
| asyncio.create_task(ub.run_until_disconnected()) |
| log.info(f"β
Loaded: {acc['name']} (@{acc.get('username', 'N/A')})") |
| except Exception as e: |
| await bot.send_message(ADMIN_ID, f"β Failed to start {acc['name']}") |
| log.info(f"β Failed to start {acc['name']}: {e}") |
|
|
| |
| @bot.on(events.NewMessage(pattern="/start")) |
| async def start_cmd(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| await event.reply( |
| "π Welcome!\n\n" |
| "/addacc β Add account\n" |
| "/delacc β Delete an account\n" |
| "/total β Count of connected accounts\n" |
| "/stats β View today's schedule\n" |
| "/reset β Reset and re-plan daily schedule\n" |
| "/create <account_name> β Create groups for the specified account\n" |
| "/help β Show this help message" |
| ) |
|
|
|
|
| @bot.on(events.NewMessage(pattern="/help")) |
| async def help_cmd(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| await event.reply( |
| "π οΈ **Bot Commands Help:**\n\n" |
| "/start β Start the bot and see the available commands\n" |
| "/addacc β Add a new account to the bot\n" |
| "/delacc β Delete an existing account\n" |
| "/total β Get the total count of connected accounts\n" |
| "/stats β View today's scheduling plan for accounts\n" |
| "/reset β Reset and plan the schedule for the new day\n" |
| "/create <account_name> β Create groups for the specified account\n" |
| "/help β Show this help message\n\n" |
| "These commands are available to the admin only." |
| ) |
|
|
| @bot.on(events.NewMessage(pattern="/addacc", chats=ADMIN_ID)) |
| async def addacc_cmd(event): |
| |
| async with control_bot.conversation(event.chat_id, timeout=300) as conv: |
| await conv.send_message("π© Send your StringSession:") |
| msg = await conv.get_response() |
| session_str = msg.raw_text.strip() |
|
|
| |
| try: |
| if not session_str.startswith(("1A", "1B")): |
| try: |
| session_manager = SessionManager.from_pyrogram_string_session(session_str) |
| session_str = session_manager.telethon_string_session() |
| await conv.send_message("π Converted Pyrogram session to Telethon.") |
| except Exception as e: |
| await conv.send_message(f"β Pyrogram conversion failed:\n`{e}`") |
| return |
| else: |
| await conv.send_message("β
Detected Telethon session.") |
| except Exception as e: |
| await conv.send_message(f"β Session detection failed:\n`{e}`") |
| return |
| info = await start_userbot(session_str, event.sender_id) |
| if info: |
| await conv.send_message(f"β
Added: {info.first_name} (@{info.username or 'N/A'})") |
| else: |
| await conv.send_message("β οΈ Already added or failed.") |
|
|
| @bot.on(events.NewMessage(pattern="/total")) |
| async def total_accounts_cmd(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| count = accounts.count_documents({}) |
| await event.respond(f"π Total connected: **{count}**") |
|
|
| @bot.on(events.NewMessage(pattern="/delacc")) |
| async def delacc_cmd(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| user_accs = list(accounts.find({"owner_id": ADMIN_ID})) |
| if not user_accs: |
| await event.reply("πΆ No accounts.") |
| return |
| buttons, row = [], [] |
| for idx, acc in enumerate(user_accs, 1): |
| row.append(Button.inline(f"{acc['name']} (@{acc.get('username', 'N/A')})", data=f"del_{acc['user_id']}")) |
| if idx % 2 == 0: |
| buttons.append(row) |
| row = [] |
| if row: |
| buttons.append(row) |
| await event.respond("Select to delete:", buttons=buttons) |
|
|
| @bot.on(events.CallbackQuery(pattern=b"del_")) |
| async def handle_delete(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| user_id = int(event.data.decode().split("_")[1]) |
| acc_doc = accounts.find_one({"user_id": user_id}) |
| if not acc_doc: |
| await event.answer("Not found.", alert=True) |
| return |
| await event.edit(f"β οΈ Confirm delete {acc_doc['name']}?", buttons=[ |
| [Button.inline("β
Confirm", data=f"confdel_{user_id}"), Button.inline("β Cancel", data=b"cancel")] |
| ]) |
|
|
| @bot.on(events.CallbackQuery(pattern=b"confdel_")) |
| async def confirm_delete(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| user_id = int(event.data.decode().split("_")[1]) |
| acc_doc = accounts.find_one_and_delete({"user_id": user_id}) |
| if acc_doc: |
| try: |
| if user_id in userbots: |
| await userbots[user_id].disconnect() |
| del userbots[user_id] |
| await event.edit(f"β
Deleted: {acc_doc['name']}") |
| await bot.send_message(ADMIN_ID, f"ποΈ Deleted: {acc_doc['name']} (@{acc_doc.get('username', 'N/A')})") |
| log.info(f"ποΈ Deleted account: {acc_doc['name']} (@{acc_doc.get('username', 'N/A')})") |
| except Exception as e: |
| await event.edit(f"Deleted from DB, error stopping: {e}") |
| else: |
| await event.edit("β οΈ Not found.") |
|
|
| @bot.on(events.CallbackQuery(data=b"cancel")) |
| async def cancel_cb(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| await event.edit("β Cancelled.") |
|
|
| @bot.on(events.CallbackQuery(pattern=b"pickcreate_")) |
| async def pick_create_callback(event): |
| if event.sender_id != ADMIN_ID: |
| return |
| user_id = int(event.data.decode().split("_")[1]) |
| acc_doc = accounts.find_one({"user_id": user_id}) |
| if not acc_doc: |
| await event.answer("Account not found.", alert=True) |
| return |
| if user_id not in userbots: |
| await event.answer("Account is not active.", alert=True) |
| return |
| await event.edit(f"β³ Creating groups for {acc_doc['name']}...") |
| await auto_create_groups(userbots[user_id], acc_doc) |
|
|
| @bot.on(events.NewMessage(pattern=r"/create\s+(.+)", func=lambda e: e.sender_id == ADMIN_ID)) |
| async def create_group_cmd(event): |
| query = event.pattern_match.group(1).strip() |
| safe_query = re.escape(query) |
| matches = list(accounts.find({ |
| "$or": [ |
| {"name": {"$regex": safe_query, "$options": "i"}}, |
| {"username": {"$regex": safe_query, "$options": "i"}} |
| ] |
| })) |
| if not matches: |
| await event.reply("β No matching account found.") |
| return |
| if len(matches) == 1: |
| acc_doc = matches[0] |
| user_id = acc_doc["user_id"] |
| if user_id not in userbots: |
| await event.reply("β οΈ Account is not active.") |
| return |
| await event.reply(f"β³ Creating groups for {acc_doc['name']}...") |
| await auto_create_groups(userbots[user_id], acc_doc) |
| else: |
| buttons = [] |
| for acc in matches: |
| label = f"{acc['name']} (@{acc.get('username', 'N/A')})" |
| buttons.append([Button.inline(label, data=f"pickcreate_{acc['user_id']}")]) |
| await event.respond("β οΈ Multiple matches found. Select the correct account:", buttons=buttons) |
|
|
| |
| app = Flask(__name__) |
| @app.route("/") |
| def home(): |
| return "β
Channel-making bot is running." |
|
|
| def run_flask(): |
| app.run(host="0.0.0.0", port=7860, use_reloader=False) |
|
|
| |
| async def main(): |
| Thread(target=run_flask).start() |
| scheduler.start() |
| await load_all_userbots() |
| await bot.start(bot_token=BOT_TOKEN) |
| log.info("π€ Bot is now running on Hugging Face") |
| await bot.run_until_disconnected() |
|
|
| if __name__ == "__main__": |
| asyncio.run(main()) |
|
|