├── devgagan
├── core
│ ├── __init__.py
│ ├── mongo
│ │ ├── __init__.py
│ │ ├── users_db.py
│ │ ├── plans_db.py
│ │ └── db.py
│ ├── func.py
│ └── get_func.py
├── modules
│ ├── __init__.py
│ ├── stats.py
│ ├── gcast.py
│ ├── speedtest.py
│ ├── login.py
│ ├── shrink.py
│ ├── eval.py
│ ├── start.py
│ ├── plans.py
│ ├── main.py
│ └── ytdl.py
├── __main__.py
└── __init__.py
├── Procfile
├── settings.jpg
├── heroku.yml
├── app.py
├── requirements.txt
├── Dockerfile
├── config.py
├── LICENSE
├── TERMS_OF_USE.md
├── app.json
├── templates
└── welcome.html
└── README.md
/devgagan/core/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: python -m devgagan
2 |
--------------------------------------------------------------------------------
/settings.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devgaganin/Save-Restricted-Content-Bot-v2/HEAD/settings.jpg
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
4 | run:
5 | worker: python3 -m devgagan
6 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import os
2 | from flask import Flask, render_template
3 |
4 | app = Flask(__name__)
5 |
6 | @app.route("/")
7 | def welcome():
8 | # Render the welcome page with animated "Team SPY" text
9 | return render_template("welcome.html")
10 |
11 | if __name__ == "__main__":
12 | # Default to port 5000 if PORT is not set in the environment
13 | port = int(os.environ.get("PORT", 8000))
14 | app.run(host="0.0.0.0", port=port)
15 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # https://www.dl.dropboxusercontent.com/scl/fi/35w0i0hnsioq63x7eumdd/pyrogagan.zip?rlkey=mq4b56v0z7w7mgj3c0o3sqgo1&st=n7prfm1y&dl=0
2 | pyrofork
3 | psutil
4 | aiofiles
5 | pillow
6 | devgagantools
7 | tgcrypto
8 | pyromod
9 | opencv-python-headless
10 | requests
11 | motor
12 | pytz
13 | flask
14 | aiohttp
15 | telethon
16 | aiojobs
17 | werkzeug==2.2.2
18 | apscheduler
19 | telethon-tgcrypto
20 | mutagen
21 | yt-dlp
22 | speedtest-cli
23 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10.4-slim
2 | RUN apt update && apt upgrade -y
3 | RUN apt-get install git curl python3-pip ffmpeg -y
4 | RUN apt-get -y install git
5 | RUN apt-get install -y wget python3-pip curl bash neofetch ffmpeg software-properties-common
6 | COPY requirements.txt .
7 |
8 | RUN pip3 install wheel
9 | RUN pip3 install --no-cache-dir -U -r requirements.txt
10 | WORKDIR /app
11 | COPY . .
12 | EXPOSE 8000
13 |
14 | CMD flask run -h 0.0.0.0 -p 8000 & python3 -m devgagan
15 |
--------------------------------------------------------------------------------
/devgagan/core/mongo/__init__.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: __init__.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
--------------------------------------------------------------------------------
/devgagan/modules/__init__.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: __init__.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 |
16 | import glob
17 | from os.path import basename, dirname, isfile
18 |
19 |
20 | def __list_all_modules():
21 | mod_paths = glob.glob(dirname(__file__) + "/*.py")
22 |
23 | all_modules = [
24 | basename(f)[:-3]
25 | for f in mod_paths
26 | if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py")
27 | ]
28 |
29 | return all_modules
30 |
31 |
32 | ALL_MODULES = sorted(__list_all_modules())
33 | __all__ = ALL_MODULES + ["ALL_MODULES"]
34 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | # devgagan
2 | # Note if you are trying to deploy on vps then directly fill values in ("")
3 |
4 | from os import getenv
5 |
6 | # VPS --- FILL COOKIES 🍪 in """ ... """
7 |
8 | INST_COOKIES = """
9 | # wtite up here insta cookies
10 | """
11 |
12 | YTUB_COOKIES = """
13 | # write here yt cookies
14 | """
15 |
16 | API_ID = int(getenv("API_ID", ""))
17 | API_HASH = getenv("API_HASH", "")
18 | BOT_TOKEN = getenv("BOT_TOKEN", "")
19 | OWNER_ID = list(map(int, getenv("OWNER_ID", "").split()))
20 | MONGO_DB = getenv("MONGO_DB", "")
21 | LOG_GROUP = getenv("LOG_GROUP", "")
22 | CHANNEL_ID = int(getenv("CHANNEL_ID", ""))
23 | FREEMIUM_LIMIT = int(getenv("FREEMIUM_LIMIT", "0"))
24 | PREMIUM_LIMIT = int(getenv("PREMIUM_LIMIT", "500"))
25 | WEBSITE_URL = getenv("WEBSITE_URL", "upshrink.com")
26 | AD_API = getenv("AD_API", "52b4a2cf4687d81e7d3f8f2b7bc2943f618e78cb")
27 | STRING = getenv("STRING", None)
28 | YT_COOKIES = getenv("YT_COOKIES", YTUB_COOKIES)
29 | DEFAULT_SESSION = getenv("DEFAUL_SESSION", None) # added old method of invite link joining
30 | INSTA_COOKIES = getenv("INSTA_COOKIES", INST_COOKIES)
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 devgaganin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/devgagan/core/mongo/users_db.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: users_db.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | from config import MONGO_DB
16 | from motor.motor_asyncio import AsyncIOMotorClient as MongoCli
17 |
18 |
19 | mongo = MongoCli(MONGO_DB)
20 | db = mongo.users
21 | db = db.users_db
22 |
23 |
24 | async def get_users():
25 | user_list = []
26 | async for user in db.users.find({"user": {"$gt": 0}}):
27 | user_list.append(user['user'])
28 | return user_list
29 |
30 |
31 | async def get_user(user):
32 | users = await get_users()
33 | if user in users:
34 | return True
35 | else:
36 | return False
37 |
38 | async def add_user(user):
39 | users = await get_users()
40 | if user in users:
41 | return
42 | else:
43 | await db.users.insert_one({"user": user})
44 |
45 |
46 | async def del_user(user):
47 | users = await get_users()
48 | if not user in users:
49 | return
50 | else:
51 | await db.users.delete_one({"user": user})
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/TERMS_OF_USE.md:
--------------------------------------------------------------------------------
1 | # Terms of Use
2 |
3 | By using this software, you agree to the following terms:
4 |
5 | 1. **License**: This software is provided under the terms of the MIT License. You are free to use, modify, and distribute this software, subject to the conditions of the license. A copy of the license is included in the LICENSE file.
6 |
7 | 2. **Attribution**: If you modify or build upon this software, you must provide appropriate credit by acknowledging the original author(s) and including a reference to the original project repository or source code.
8 |
9 | 3. **No Warranty**: This software is provided "as is," without any warranty, express or implied. The author(s) of this software are not liable for any damages or losses arising from the use or inability to use this software.
10 |
11 | 4. **Modification**: You are permitted to modify the software for your own use or for contributing improvements back to the community. However, you must adhere to the terms of the license and provide appropriate credit as outlined in point 2.
12 |
13 | 5. **Contribution**: Contributions to the project are welcome. By contributing code, documentation, or other materials, you agree to license your contributions under the terms of the MIT License.
14 |
15 | 6. **Compliance**: By using this software, you agree to comply with all applicable laws and regulations. Any use of this software for illegal or malicious purposes is strictly prohibited.
16 |
17 | 7. **Feedback**: Your feedback and suggestions for improvements to this software are appreciated. Please feel free to submit issues or pull requests to the project repository.
18 |
19 | By using this software, you acknowledge that you have read, understood, and agree to be bound by these terms.
20 |
--------------------------------------------------------------------------------
/devgagan/core/mongo/plans_db.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: plans_db.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | import datetime
16 | from motor.motor_asyncio import AsyncIOMotorClient as MongoCli
17 | from config import MONGO_DB
18 |
19 | mongo = MongoCli(MONGO_DB)
20 | db = mongo.premium
21 | db = db.premium_db
22 |
23 | async def add_premium(user_id, expire_date):
24 | data = await check_premium(user_id)
25 | if data and data.get("_id"):
26 | await db.update_one({"_id": user_id}, {"$set": {"expire_date": expire_date}})
27 | else:
28 | await db.insert_one({"_id": user_id, "expire_date": expire_date})
29 |
30 | async def remove_premium(user_id):
31 | await db.delete_one({"_id": user_id})
32 |
33 | async def check_premium(user_id):
34 | return await db.find_one({"_id": user_id})
35 |
36 | async def premium_users():
37 | id_list = []
38 | async for data in db.find():
39 | id_list.append(data["_id"])
40 | return id_list
41 |
42 | async def check_and_remove_expired_users():
43 | current_time = datetime.datetime.utcnow()
44 | async for data in db.find():
45 | expire_date = data.get("expire_date")
46 | if expire_date and expire_date < current_time:
47 | await remove_premium(data["_id"])
48 | print(f"Removed user {data['_id']} due to expired plan.")
49 |
--------------------------------------------------------------------------------
/devgagan/__main__.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: __main__.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | import asyncio
16 | import importlib
17 | import gc
18 | from pyrogram import idle
19 | from devgagan.modules import ALL_MODULES
20 | from devgagan.core.mongo.plans_db import check_and_remove_expired_users
21 | from aiojobs import create_scheduler
22 |
23 | # ----------------------------Bot-Start---------------------------- #
24 |
25 | loop = asyncio.get_event_loop()
26 |
27 | # Function to schedule expiry checks
28 | async def schedule_expiry_check():
29 | scheduler = await create_scheduler()
30 | while True:
31 | await scheduler.spawn(check_and_remove_expired_users())
32 | await asyncio.sleep(60) # Check every hour
33 | gc.collect()
34 |
35 | async def devggn_boot():
36 | for all_module in ALL_MODULES:
37 | importlib.import_module("devgagan.modules." + all_module)
38 | print("""
39 | ---------------------------------------------------
40 | 📂 Bot Deployed successfully ...
41 | 📝 Description: A Pyrogram bot for downloading files from Telegram channels or groups
42 | and uploading them back to Telegram.
43 | 👨💻 Author: Gagan
44 | 🌐 GitHub: https://github.com/devgaganin/
45 | 📬 Telegram: https://t.me/team_spy_pro
46 | ▶️ YouTube: https://youtube.com/@dev_gagan
47 | 🗓️ Created: 2025-01-11
48 | 🔄 Last Modified: 2025-01-11
49 | 🛠️ Version: 2.0.5
50 | 📜 License: MIT License
51 | ---------------------------------------------------
52 | """)
53 |
54 | asyncio.create_task(schedule_expiry_check())
55 | print("Auto removal started ...")
56 | await idle()
57 | print("Bot stopped...")
58 |
59 |
60 | if __name__ == "__main__":
61 | loop.run_until_complete(devggn_boot())
62 |
63 | # ------------------------------------------------------------------ #
64 |
--------------------------------------------------------------------------------
/devgagan/modules/stats.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: stats.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 |
16 |
17 | import time
18 | import sys
19 | import motor
20 | from devgagan import app
21 | from pyrogram import filters
22 | from config import OWNER_ID
23 | from devgagan.core.mongo.users_db import get_users, add_user, get_user
24 | from devgagan.core.mongo.plans_db import premium_users
25 |
26 |
27 |
28 | start_time = time.time()
29 |
30 | @app.on_message(group=10)
31 | async def chat_watcher_func(_, message):
32 | try:
33 | if message.from_user:
34 | us_in_db = await get_user(message.from_user.id)
35 | if not us_in_db:
36 | await add_user(message.from_user.id)
37 | except:
38 | pass
39 |
40 |
41 |
42 | def time_formatter():
43 | minutes, seconds = divmod(int(time.time() - start_time), 60)
44 | hours, minutes = divmod(minutes, 60)
45 | days, hours = divmod(hours, 24)
46 | weeks, days = divmod(days, 7)
47 | tmp = (
48 | ((str(weeks) + "w:") if weeks else "")
49 | + ((str(days) + "d:") if days else "")
50 | + ((str(hours) + "h:") if hours else "")
51 | + ((str(minutes) + "m:") if minutes else "")
52 | + ((str(seconds) + "s") if seconds else "")
53 | )
54 | if tmp != "":
55 | if tmp.endswith(":"):
56 | return tmp[:-1]
57 | else:
58 | return tmp
59 | else:
60 | return "0 s"
61 |
62 |
63 | @app.on_message(filters.command("stats") & filters.user(OWNER_ID))
64 | async def stats(client, message):
65 | start = time.time()
66 | users = len(await get_users())
67 | premium = await premium_users()
68 | ping = round((time.time() - start) * 1000)
69 | await message.reply_text(f"""
70 | **Stats of** {(await client.get_me()).mention} :
71 |
72 | 🏓 **Ping Pong**: {ping}ms
73 |
74 | 📊 **Total Users** : `{users}`
75 | 📈 **Premium Users** : `{len(premium)}`
76 | ⚙️ **Bot Uptime** : `{time_formatter()}`
77 |
78 | 🎨 **Python Version**: `{sys.version.split()[0]}`
79 | 📑 **Mongo Version**: `{motor.version}`
80 | """)
81 |
82 |
--------------------------------------------------------------------------------
/devgagan/__init__.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: __init__.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | import asyncio
16 | import logging
17 | import time
18 | from pyrogram import Client
19 | from pyrogram.enums import ParseMode
20 | from config import API_ID, API_HASH, BOT_TOKEN, STRING, MONGO_DB, DEFAULT_SESSION
21 | from telethon.sync import TelegramClient
22 | from motor.motor_asyncio import AsyncIOMotorClient
23 |
24 | loop = asyncio.new_event_loop()
25 | asyncio.set_event_loop(loop)
26 |
27 | logging.basicConfig(
28 | format="[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s",
29 | level=logging.INFO,
30 | )
31 |
32 | botStartTime = time.time()
33 |
34 | app = Client(
35 | "pyrobot",
36 | api_id=API_ID,
37 | api_hash=API_HASH,
38 | bot_token=BOT_TOKEN,
39 | workers=50,
40 | parse_mode=ParseMode.MARKDOWN
41 | )
42 |
43 | sex = TelegramClient('sexrepo', API_ID, API_HASH).start(bot_token=BOT_TOKEN)
44 |
45 | if STRING:
46 | pro = Client("ggbot", api_id=API_ID, api_hash=API_HASH, session_string=STRING)
47 | else:
48 | pro = None
49 |
50 |
51 | if DEFAULT_SESSION:
52 | userrbot = Client("userrbot", api_id=API_ID, api_hash=API_HASH, session_string=DEFAULT_SESSION)
53 | else:
54 | userrbot = None
55 |
56 | telethon_client = TelegramClient('telethon_session', API_ID, API_HASH).start(bot_token=BOT_TOKEN)
57 |
58 | # MongoDB setup
59 | tclient = AsyncIOMotorClient(MONGO_DB)
60 | tdb = tclient["telegram_bot"] # Your database
61 | token = tdb["tokens"] # Your tokens collection
62 |
63 | async def create_ttl_index():
64 | """Ensure the TTL index exists for the `tokens` collection."""
65 | await token.create_index("expires_at", expireAfterSeconds=0)
66 |
67 | # Run the TTL index creation when the bot starts
68 | async def setup_database():
69 | await create_ttl_index()
70 | print("MongoDB TTL index created.")
71 |
72 | async def restrict_bot():
73 | global BOT_ID, BOT_NAME, BOT_USERNAME
74 | await setup_database()
75 | await app.start()
76 | getme = await app.get_me()
77 | BOT_ID = getme.id
78 | BOT_USERNAME = getme.username
79 | BOT_NAME = f"{getme.first_name} {getme.last_name}" if getme.last_name else getme.first_name
80 |
81 | if pro:
82 | await pro.start()
83 | if userrbot:
84 | await userrbot.start()
85 |
86 | loop.run_until_complete(restrict_bot())
87 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Save Restricted Content Bot master-v1 branch",
3 | "description": "Save Restricted Content Bot by Team SPY",
4 | "logo": "https://lh3.googleusercontent.com/-HPcn7AqepNg/AAAAAAAAAAI/AAAAAAAAAAA/ALKGfknb1BkQiq-8_KUVOYcNAJ4swKivDQ/photo.jpg",
5 | "keywords": ["python3", "telegram", "MusicBot", "telegram-bot", "pyrogram"],
6 | "repository": "https://github.com/devgaganin/save_restricted-content-telegram-bot-repo",
7 | "success_url": "https://devgagan.in",
8 | "env": {
9 | "API_ID": {
10 | "description": "Get this value from https://my.telegram.org",
11 | "value": "your_api_id_here",
12 | "required": true
13 | },
14 | "API_HASH": {
15 | "description": "Get this value from https://my.telegram.org",
16 | "value": "your_api_hash_here",
17 | "required": true
18 | },
19 | "BOT_TOKEN": {
20 | "description": "A Bot's token from Botfather",
21 | "value": "your_bot_token_here",
22 | "required": true
23 | },
24 | "MONGO_DB": {
25 | "description": "Get a MongoDB URL from https://cloud.mongodb.com.",
26 | "value": "your_mongodb_url_here",
27 | "required": true
28 | },
29 | "OWNER_ID": {
30 | "description": "The user ID of the user whom you would like to add as OWNER.",
31 | "value": "owner_user_id_here",
32 | "required": true
33 | },
34 | "FREEMIUM_LIMIT": {
35 | "description": "Batch size for free users",
36 | "value": "0",
37 | "required": true
38 | },
39 | "PREMIUM_LIMIT": {
40 | "description": "Batch size for premium users",
41 | "value": "500",
42 | "required": true
43 | },
44 | "STRING": {
45 | "description": "Enter premium account session string if you want to allow the paid users to upload upto 4GB",
46 | "value": "",
47 | "required": false
48 | },
49 |
50 | "DEFAULT_SESSION": {
51 | "description": "Enter session string if you want YOUR USERS not to be forced login and can do things via invite_link",
52 | "value": "",
53 | "required": false
54 | },
55 |
56 | "WEBSITE_URL": {
57 | "description": "Enter shortener website domain eg upshrink.com",
58 | "value": "upshrink.com",
59 | "required": false
60 | },
61 | "AD_API": {
62 | "description": "Batch size for premium users",
63 | "value": "52b4a2cf4687d81e7d3f8f2b7bc2943f618e78cb",
64 | "required": false
65 | },
66 |
67 | "SUDO_USERS": {
68 | "description": "Other Admins' IDs (optional)",
69 | "value": "",
70 | "required": false
71 | },
72 | "CHANNEL_ID": {
73 | "description": "Enter Channel ID (-100) and make bot admin there",
74 | "value": "channel_id_here",
75 | "required": true
76 | },
77 | "LOG_GROUP": {
78 | "description": "Enter Log Channel/Group ID (-100) and make bot admin there",
79 | "value": "log_group_id_here",
80 | "required": true
81 | }
82 | },
83 | "buildpacks": [
84 | { "url": "heroku/python" },
85 | { "url": "https://github.com/heroku/heroku-buildpack-activestorage-preview" }
86 | ],
87 | "stack": "container"
88 | }
89 |
--------------------------------------------------------------------------------
/devgagan/modules/gcast.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: gcast.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | import asyncio
16 | from pyrogram import filters
17 | from config import OWNER_ID
18 | from devgagan import app
19 | from devgagan.core.mongo.users_db import get_users
20 |
21 | async def send_msg(user_id, message):
22 | try:
23 | x = await message.copy(chat_id=user_id)
24 | try:
25 | await x.pin()
26 | except Exception:
27 | await x.pin(both_sides=True)
28 | except FloodWait as e:
29 | await asyncio.sleep(e.x)
30 | return send_msg(user_id, message)
31 | except InputUserDeactivated:
32 | return 400, f"{user_id} : deactivated\n"
33 | except UserIsBlocked:
34 | return 400, f"{user_id} : blocked the bot\n"
35 | except PeerIdInvalid:
36 | return 400, f"{user_id} : user id invalid\n"
37 | except Exception:
38 | return 500, f"{user_id} : {traceback.format_exc()}\n"
39 |
40 |
41 | @app.on_message(filters.command("gcast") & filters.user(OWNER_ID))
42 | async def broadcast(_, message):
43 | if not message.reply_to_message:
44 | await message.reply_text("ʀᴇᴘʟʏ ᴛᴏ ᴀ ᴍᴇssᴀɢᴇ ᴛᴏ ʙʀᴏᴀᴅᴄᴀsᴛ ɪᴛ.")
45 | return
46 | exmsg = await message.reply_text("sᴛᴀʀᴛᴇᴅ ʙʀᴏᴀᴅᴄᴀsᴛɪɴɢ!")
47 | all_users = (await get_users()) or {}
48 | done_users = 0
49 | failed_users = 0
50 |
51 | for user in all_users:
52 | try:
53 | await send_msg(user, message.reply_to_message)
54 | done_users += 1
55 | await asyncio.sleep(0.1)
56 | except Exception:
57 | pass
58 | failed_users += 1
59 | if failed_users == 0:
60 | await exmsg.edit_text(
61 | f"**sᴜᴄᴄᴇssғᴜʟʟʏ ʙʀᴏᴀᴅᴄᴀsᴛɪɴɢ ✅**\n\n**sᴇɴᴛ ᴍᴇssᴀɢᴇ ᴛᴏ** `{done_users}` **ᴜsᴇʀs**",
62 | )
63 | else:
64 | await exmsg.edit_text(
65 | f"**sᴜᴄᴄᴇssғᴜʟʟʏ ʙʀᴏᴀᴅᴄᴀsᴛɪɴɢ ✅**\n\n**sᴇɴᴛ ᴍᴇssᴀɢᴇ ᴛᴏ** `{done_users}` **ᴜsᴇʀs**\n\n**ɴᴏᴛᴇ:-** `ᴅᴜᴇ ᴛᴏ sᴏᴍᴇ ɪssᴜᴇ ᴄᴀɴ'ᴛ ᴀʙʟᴇ ᴛᴏ ʙʀᴏᴀᴅᴄᴀsᴛ` `{failed_users}` **ᴜsᴇʀs**",
66 | )
67 |
68 |
69 |
70 |
71 |
72 | @app.on_message(filters.command("acast") & filters.user(OWNER_ID))
73 | async def announced(_, message):
74 | if message.reply_to_message:
75 | to_send=message.reply_to_message.id
76 | if not message.reply_to_message:
77 | return await message.reply_text("Reply To Some Post To Broadcast")
78 | users = await get_users() or []
79 | print(users)
80 | failed_user = 0
81 |
82 | for user in users:
83 | try:
84 | await _.forward_messages(chat_id=int(user), from_chat_id=message.chat.id, message_ids=to_send)
85 | await asyncio.sleep(1)
86 | except Exception as e:
87 | failed_user += 1
88 |
89 | if failed_users == 0:
90 | await exmsg.edit_text(
91 | f"**sᴜᴄᴄᴇssғᴜʟʟʏ ʙʀᴏᴀᴅᴄᴀsᴛɪɴɢ ✅**\n\n**sᴇɴᴛ ᴍᴇssᴀɢᴇ ᴛᴏ** `{done_users}` **ᴜsᴇʀs**",
92 | )
93 | else:
94 | await exmsg.edit_text(
95 | f"**sᴜᴄᴄᴇssғᴜʟʟʏ ʙʀᴏᴀᴅᴄᴀsᴛɪɴɢ ✅**\n\n**sᴇɴᴛ ᴍᴇssᴀɢᴇ ᴛᴏ** `{done_users}` **ᴜsᴇʀs**\n\n**ɴᴏᴛᴇ:-** `ᴅᴜᴇ ᴛᴏ sᴏᴍᴇ ɪssᴜᴇ ᴄᴀɴ'ᴛ ᴀʙʟᴇ ᴛᴏ ʙʀᴏᴀᴅᴄᴀsᴛ` `{failed_users}` **ᴜsᴇʀs**",
96 | )
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/devgagan/core/mongo/db.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: db.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | from config import MONGO_DB
16 | from motor.motor_asyncio import AsyncIOMotorClient as MongoCli
17 | mongo = MongoCli(MONGO_DB)
18 | db = mongo.user_data
19 | db = db.users_data_db
20 | async def get_data(user_id):
21 | x = await db.find_one({"_id": user_id})
22 | return x
23 | async def set_thumbnail(user_id, thumb):
24 | data = await get_data(user_id)
25 | if data and data.get("_id"):
26 | await db.update_one({"_id": user_id}, {"$set": {"thumb": thumb}})
27 | else:
28 | await db.insert_one({"_id": user_id, "thumb": thumb})
29 | async def set_caption(user_id, caption):
30 | data = await get_data(user_id)
31 | if data and data.get("_id"):
32 | await db.update_one({"_id": user_id}, {"$set": {"caption": caption}})
33 | else:
34 | await db.insert_one({"_id": user_id, "caption": caption})
35 | async def replace_caption(user_id, replace_txt, to_replace):
36 | data = await get_data(user_id)
37 | if data and data.get("_id"):
38 | await db.update_one({"_id": user_id}, {"$set": {"replace_txt": replace_txt, "to_replace": to_replace}})
39 | else:
40 | await db.insert_one({"_id": user_id, "replace_txt": replace_txt, "to_replace": to_replace})
41 | async def set_session(user_id, session):
42 | data = await get_data(user_id)
43 | if data and data.get("_id"):
44 | await db.update_one({"_id": user_id}, {"$set": {"session": session}})
45 | else:
46 | await db.insert_one({"_id": user_id, "session": session})
47 | async def clean_words(user_id, new_clean_words):
48 | data = await get_data(user_id)
49 | if data and data.get("_id"):
50 | existing_words = data.get("clean_words", [])
51 |
52 | if existing_words is None:
53 | existing_words = []
54 | updated_words = list(set(existing_words + new_clean_words))
55 | await db.update_one({"_id": user_id}, {"$set": {"clean_words": updated_words}})
56 | else:
57 | await db.insert_one({"_id": user_id, "clean_words": new_clean_words})
58 | async def remove_clean_words(user_id, words_to_remove):
59 | data = await get_data(user_id)
60 | if data and data.get("_id"):
61 | existing_words = data.get("clean_words", [])
62 | updated_words = [word for word in existing_words if word not in words_to_remove]
63 | await db.update_one({"_id": user_id}, {"$set": {"clean_words": updated_words}})
64 | else:
65 | await db.insert_one({"_id": user_id, "clean_words": []})
66 | async def set_channel(user_id, chat_id):
67 | data = await get_data(user_id)
68 | if data and data.get("_id"):
69 | await db.update_one({"_id": user_id}, {"$set": {"chat_id": chat_id}})
70 | else:
71 | await db.insert_one({"_id": user_id, "chat_id": chat_id})
72 | async def all_words_remove(user_id):
73 | await db.update_one({"_id": user_id}, {"$set": {"clean_words": None}})
74 | async def remove_thumbnail(user_id):
75 | await db.update_one({"_id": user_id}, {"$set": {"thumb": None}})
76 | async def remove_caption(user_id):
77 | await db.update_one({"_id": user_id}, {"$set": {"caption": None}})
78 | async def remove_replace(user_id):
79 | await db.update_one({"_id": user_id}, {"$set": {"replace_txt": None, "to_replace": None}})
80 |
81 | async def remove_session(user_id):
82 | await db.update_one({"_id": user_id}, {"$set": {"session": None}})
83 | async def remove_channel(user_id):
84 | await db.update_one({"_id": user_id}, {"$set": {"chat_id": None}})
85 | async def delete_session(user_id):
86 | """Delete the session associated with the given user_id from the database."""
87 | await db.update_one({"_id": user_id}, {"$unset": {"session": ""}})
88 |
--------------------------------------------------------------------------------
/devgagan/modules/speedtest.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: speedtest.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | from time import time
16 | from speedtest import Speedtest
17 | import math
18 | from telethon import events
19 | from devgagan import botStartTime
20 | from devgagan import sex as gagan
21 |
22 | SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
23 |
24 | def get_readable_time(seconds: int) -> str:
25 | result = ''
26 | (days, remainder) = divmod(seconds, 86400)
27 | days = int(days)
28 | if days != 0:
29 | result += f'{days}d'
30 | (hours, remainder) = divmod(remainder, 3600)
31 | hours = int(hours)
32 | if hours != 0:
33 | result += f'{hours}h'
34 | (minutes, seconds) = divmod(remainder, 60)
35 | minutes = int(minutes)
36 | if minutes != 0:
37 | result += f'{minutes}m'
38 | seconds = int(seconds)
39 | result += f'{seconds}s'
40 | return result
41 |
42 | def get_readable_file_size(size_in_bytes) -> str:
43 | if size_in_bytes is None:
44 | return '0B'
45 | index = 0
46 | while size_in_bytes >= 1024:
47 | size_in_bytes /= 1024
48 | index += 1
49 | try:
50 | return f'{round(size_in_bytes, 2)}{SIZE_UNITS[index]}'
51 | except IndexError:
52 | return 'File too large'
53 |
54 |
55 | @gagan.on(events.NewMessage(incoming=True, pattern='/speedtest'))
56 | async def speedtest(event):
57 | speed = await event.reply("Running Speed Test. Wait about some secs.") #edit telethon
58 | test = Speedtest()
59 | test.get_best_server()
60 | test.download()
61 | test.upload()
62 | test.results.share()
63 | result = test.results.dict()
64 | path = (result['share'])
65 | currentTime = get_readable_time(time() - botStartTime)
66 | string_speed = f'''
67 | ╭─《 🚀 SPEEDTEST INFO 》
68 | ├ Upload: {speed_convert(result['upload'], False)}
69 | ├ Download: {speed_convert(result['download'], False)}
70 | ├ Ping: {result['ping']} ms
71 | ├ Time: {result['timestamp']}
72 | ├ Data Sent: {get_readable_file_size(int(result['bytes_sent']))}
73 | ╰ Data Received: {get_readable_file_size(int(result['bytes_received']))}
74 | ╭─《 🌐 SPEEDTEST SERVER 》
75 | ├ Name: {result['server']['name']}
76 | ├ Country: {result['server']['country']}, {result['server']['cc']}
77 | ├ Sponsor: {result['server']['sponsor']}
78 | ├ Latency: {result['server']['latency']}
79 | ├ Latitude: {result['server']['lat']}
80 | ╰ Longitude: {result['server']['lon']}
81 | ╭─《 👤 CLIENT DETAILS 》
82 | ├ IP Address: {result['client']['ip']}
83 | ├ Latitude: {result['client']['lat']}
84 | ├ Longitude: {result['client']['lon']}
85 | ├ Country: {result['client']['country']}
86 | ├ ISP: {result['client']['isp']}
87 | ├ ISP Rating: {result['client']['isprating']}
88 | ╰ Powered by Team SPY
89 | '''
90 | try:
91 | await event.reply(string_speed,file=path,parse_mode='html')
92 | await speed.delete()
93 | except Exception as g:
94 | await speed.delete()
95 | await event.reply(string_speed,parse_mode='html' )
96 |
97 | def speed_convert(size, byte=True):
98 | if not byte: size = size / 8
99 | power = 2 ** 10
100 | zero = 0
101 | units = {0: "B/s", 1: "KB/s", 2: "MB/s", 3: "GB/s", 4: "TB/s"}
102 | while size > power:
103 | size /= power
104 | zero += 1
105 | return f"{round(size, 2)} {units[zero]}"
106 |
--------------------------------------------------------------------------------
/templates/welcome.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{evaluation}"
81 | if len(final_output) > 4096:
82 | filename = "output.txt"
83 | with open(filename, "w+", encoding="utf8") as out_file:
84 | out_file.write(str(evaluation))
85 | t2 = time()
86 | keyboard = InlineKeyboardMarkup(
87 | [
88 | [
89 | InlineKeyboardButton(
90 | text="⏳",
91 | callback_data=f"runtime {t2-t1} Seconds",
92 | )
93 | ]
94 | ]
95 | )
96 | await message.reply_document(
97 | document=filename,
98 | caption=f"🔗 ᴇᴠᴀʟ :\n{cmd[0:980]}\n\n📕 ʀᴇsᴜʟᴛ :\nᴀᴛᴛᴀᴄʜᴇᴅ ᴅᴏᴄᴜᴍᴇɴᴛ",
99 | quote=False,
100 | reply_markup=keyboard,
101 | )
102 | await message.delete()
103 | os.remove(filename)
104 | else:
105 | t2 = time()
106 | keyboard = InlineKeyboardMarkup(
107 | [
108 | [
109 | InlineKeyboardButton(
110 | text="⏳",
111 | callback_data=f"runtime {round(t2-t1, 3)} Seconds",
112 | ),
113 | InlineKeyboardButton(
114 | text="🗑",
115 | callback_data=f"forceclose abc|{message.from_user.id}",
116 | ),
117 | ]
118 | ]
119 | )
120 | await edit_or_reply(message, text=final_output, reply_markup=keyboard)
121 |
122 |
123 | @app.on_callback_query(filters.regex(r"runtime"))
124 | async def runtime_func_cq(_, cq):
125 | runtime = cq.data.split(None, 1)[1]
126 | await cq.answer(runtime, show_alert=True)
127 |
128 |
129 | @app.on_callback_query(filters.regex("fclose"))
130 | async def forceclose_command(_, CallbackQuery):
131 | callback_data = CallbackQuery.data.strip()
132 | callback_request = callback_data.split(None, 1)[1]
133 | query, user_id = callback_request.split("|")
134 | if CallbackQuery.from_user.id != int(user_id):
135 | try:
136 | return await CallbackQuery.answer(
137 | "ɪᴛ'ʟʟ ʙᴇ ʙᴇᴛᴛᴇʀ ɪғ ʏᴏᴜ sᴛᴀʏ ɪɴ ʏᴏᴜʀ ʟɪᴍɪᴛs ʙᴀʙʏ.", show_alert=True
138 | )
139 | except:
140 | return
141 | await CallbackQuery.message.delete()
142 | try:
143 | await CallbackQuery.answer()
144 | except:
145 | return
146 |
147 |
148 |
149 |
150 | @app.on_edited_message(
151 | filters.command("shll")
152 | & filters.user(OWNER_ID)
153 | & ~filters.forwarded
154 | & ~filters.via_bot
155 | )
156 | @app.on_message(
157 | filters.command("shll")
158 | & filters.user(OWNER_ID)
159 | & ~filters.forwarded
160 | & ~filters.via_bot
161 | )
162 | async def shellrunner(_, message):
163 | if len(message.command) < 2:
164 | return await edit_or_reply(message, text="ᴇxᴀᴍᴩʟᴇ :\n/sh git pull")
165 | text = message.text.split(None, 1)[1]
166 | if "\n" in text:
167 | code = text.split("\n")
168 | output = ""
169 | for x in code:
170 | shell = re.split(""" (?=(?:[^'"]|'[^']*'|"[^"]*")*$)""", x)
171 | try:
172 | process = subprocess.Popen(
173 | shell,
174 | stdout=subprocess.PIPE,
175 | stderr=subprocess.PIPE,
176 | )
177 | except Exception as err:
178 | await edit_or_reply(message, text=f"ERROR :\n{err}")
179 | output += f"{code}\n"
180 | output += process.stdout.read()[:-1].decode("utf-8")
181 | output += "\n"
182 | else:
183 | shell = re.split(""" (?=(?:[^'"]|'[^']*'|"[^"]*")*$)""", text)
184 | for a in range(len(shell)):
185 | shell[a] = shell[a].replace('"', "")
186 | try:
187 | process = subprocess.Popen(
188 | shell,
189 | stdout=subprocess.PIPE,
190 | stderr=subprocess.PIPE,
191 | )
192 | except Exception as err:
193 | print(err)
194 | exc_type, exc_obj, exc_tb = sys.exc_info()
195 | errors = traceback.format_exception(
196 | etype=exc_type,
197 | value=exc_obj,
198 | tb=exc_tb,
199 | )
200 | return await edit_or_reply(
201 | message, text=f"ERROR :\n{''.join(errors)}"
202 | )
203 | output = process.stdout.read()[:-1].decode("utf-8")
204 | if str(output) == "\n":
205 | output = None
206 | if output:
207 | if len(output) > 4096:
208 | with open("output.txt", "w+") as file:
209 | file.write(output)
210 | await _.send_document(
211 | message.chat.id,
212 | "output.txt",
213 | reply_to_message_id=message.id,
214 | caption="Output",
215 | )
216 | return os.remove("output.txt")
217 | await edit_or_reply(message, text=f"OUTPUT :\n{output}")
218 | else:
219 | await edit_or_reply(message, text="OUTPUT :\nNone")
220 | await message.stop_propagation()
221 |
222 |
223 | @app.on_message(filters.command("restart") & filters.user(OWNER_ID))
224 | async def update(_, message):
225 | await message.reply("Restarting ... ")
226 | os.execl(sys.executable, sys.executable, "-m", "devgagan")
227 |
--------------------------------------------------------------------------------
/devgagan/core/func.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: func.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | import math
16 | import time , re
17 | from pyrogram import enums
18 | from config import CHANNEL_ID, OWNER_ID
19 | from devgagan.core.mongo.plans_db import premium_users
20 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
21 | import cv2
22 | from pyrogram.errors import FloodWait, InviteHashInvalid, InviteHashExpired, UserAlreadyParticipant, UserNotParticipant
23 | from datetime import datetime as dt
24 | import asyncio, subprocess, re, os, time
25 | async def chk_user(message, user_id):
26 | user = await premium_users()
27 | if user_id in user or user_id in OWNER_ID:
28 | return 0
29 | else:
30 | return 1
31 | async def gen_link(app,chat_id):
32 | link = await app.export_chat_invite_link(chat_id)
33 | return link
34 |
35 | async def subscribe(app, message):
36 | update_channel = CHANNEL_ID
37 | url = await gen_link(app, update_channel)
38 | if update_channel:
39 | try:
40 | user = await app.get_chat_member(update_channel, message.from_user.id)
41 | if user.status == "kicked":
42 | await message.reply_text("You are Banned. Contact -- @devgaganin")
43 | return 1
44 | except UserNotParticipant:
45 | caption = f"Join our channel to use the bot"
46 | await message.reply_photo(photo="https://graph.org/file/d44f024a08ded19452152.jpg",caption=caption, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Join Now...", url=f"{url}")]]))
47 | return 1
48 | except Exception:
49 | await message.reply_text("Something Went Wrong. Contact us @devgaganin...")
50 | return 1
51 | async def get_seconds(time_string):
52 | def extract_value_and_unit(ts):
53 | value = ""
54 | unit = ""
55 |
56 | index = 0
57 | while index < len(ts) and ts[index].isdigit():
58 | value += ts[index]
59 | index += 1
60 |
61 | unit = ts[index:].lstrip()
62 |
63 | if value:
64 | value = int(value)
65 |
66 | return value, unit
67 |
68 | value, unit = extract_value_and_unit(time_string)
69 |
70 | if unit == 's':
71 | return value
72 | elif unit == 'min':
73 | return value * 60
74 | elif unit == 'hour':
75 | return value * 3600
76 | elif unit == 'day':
77 | return value * 86400
78 | elif unit == 'month':
79 | return value * 86400 * 30
80 | elif unit == 'year':
81 | return value * 86400 * 365
82 | else:
83 | return 0
84 | PROGRESS_BAR = """\n
85 | │ **__Completed:__** {1}/{2}
86 | │ **__Bytes:__** {0}%
87 | │ **__Speed:__** {3}/s
88 | │ **__ETA:__** {4}
89 | ╰─────────────────────╯
90 | """
91 | async def progress_bar(current, total, ud_type, message, start):
92 |
93 | now = time.time()
94 | diff = now - start
95 | if round(diff % 10.00) == 0 or current == total:
96 |
97 | percentage = current * 100 / total
98 | speed = current / diff
99 | elapsed_time = round(diff) * 1000
100 | time_to_completion = round((total - current) / speed) * 1000
101 | estimated_total_time = elapsed_time + time_to_completion
102 |
103 | elapsed_time = TimeFormatter(milliseconds=elapsed_time)
104 | estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
105 |
106 | progress = "{0}{1}".format(
107 | ''.join(["♦" for i in range(math.floor(percentage / 10))]),
108 | ''.join(["◇" for i in range(10 - math.floor(percentage / 10))]))
109 |
110 | tmp = progress + PROGRESS_BAR.format(
111 | round(percentage, 2),
112 | humanbytes(current),
113 | humanbytes(total),
114 | humanbytes(speed),
115 |
116 | estimated_total_time if estimated_total_time != '' else "0 s"
117 | )
118 | try:
119 | await message.edit(
120 | text="{}\n│ {}".format(ud_type, tmp),)
121 | except:
122 | pass
123 |
124 | def humanbytes(size):
125 | if not size:
126 | return ""
127 | power = 2**10
128 | n = 0
129 | Dic_powerN = {0: ' ', 1: 'K', 2: 'M', 3: 'G', 4: 'T'}
130 | while size > power:
131 | size /= power
132 | n += 1
133 | return str(round(size, 2)) + " " + Dic_powerN[n] + 'B'
134 |
135 | def TimeFormatter(milliseconds: int) -> str:
136 | seconds, milliseconds = divmod(int(milliseconds), 1000)
137 | minutes, seconds = divmod(seconds, 60)
138 | hours, minutes = divmod(minutes, 60)
139 | days, hours = divmod(hours, 24)
140 | tmp = ((str(days) + "d, ") if days else "") + \
141 | ((str(hours) + "h, ") if hours else "") + \
142 | ((str(minutes) + "m, ") if minutes else "") + \
143 | ((str(seconds) + "s, ") if seconds else "") + \
144 | ((str(milliseconds) + "ms, ") if milliseconds else "")
145 | return tmp[:-2]
146 | def convert(seconds):
147 | seconds = seconds % (24 * 3600)
148 | hour = seconds // 3600
149 | seconds %= 3600
150 | minutes = seconds // 60
151 | seconds %= 60
152 | return "%d:%02d:%02d" % (hour, minutes, seconds)
153 | async def userbot_join(userbot, invite_link):
154 | try:
155 | await userbot.join_chat(invite_link)
156 | return "Successfully joined the Channel"
157 | except UserAlreadyParticipant:
158 | return "User is already a participant."
159 | except (InviteHashInvalid, InviteHashExpired):
160 | return "Could not join. Maybe your link is expired or Invalid."
161 | except FloodWait:
162 | return "Too many requests, try again later."
163 | except Exception as e:
164 | print(e)
165 | return "Could not join, try joining manually."
166 | def get_link(string):
167 | regex = r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))"
168 | url = re.findall(regex,string)
169 | try:
170 | link = [x[0] for x in url][0]
171 | if link:
172 | return link
173 | else:
174 | return False
175 | except Exception:
176 | return False
177 | def video_metadata(file):
178 | default_values = {'width': 1, 'height': 1, 'duration': 1}
179 | try:
180 | vcap = cv2.VideoCapture(file)
181 | if not vcap.isOpened():
182 | return default_values
183 |
184 | width = round(vcap.get(cv2.CAP_PROP_FRAME_WIDTH))
185 | height = round(vcap.get(cv2.CAP_PROP_FRAME_HEIGHT))
186 | fps = vcap.get(cv2.CAP_PROP_FPS)
187 | frame_count = vcap.get(cv2.CAP_PROP_FRAME_COUNT)
188 |
189 | if fps <= 0:
190 | return default_values
191 |
192 | duration = round(frame_count / fps)
193 | if duration <= 0:
194 | return default_values
195 |
196 | vcap.release()
197 | return {'width': width, 'height': height, 'duration': duration}
198 |
199 | except Exception as e:
200 | print(f"Error in video_metadata: {e}")
201 | return default_values
202 |
203 | def hhmmss(seconds):
204 | return time.strftime('%H:%M:%S',time.gmtime(seconds))
205 |
206 | async def screenshot(video, duration, sender):
207 | if os.path.exists(f'{sender}.jpg'):
208 | return f'{sender}.jpg'
209 | time_stamp = hhmmss(int(duration)/2)
210 | out = dt.now().isoformat("_", "seconds") + ".jpg"
211 | cmd = ["ffmpeg",
212 | "-ss",
213 | f"{time_stamp}",
214 | "-i",
215 | f"{video}",
216 | "-frames:v",
217 | "1",
218 | f"{out}",
219 | "-y"
220 | ]
221 | process = await asyncio.create_subprocess_exec(
222 | *cmd,
223 | stdout=asyncio.subprocess.PIPE,
224 | stderr=asyncio.subprocess.PIPE
225 | )
226 | stdout, stderr = await process.communicate()
227 | x = stderr.decode().strip()
228 | y = stdout.decode().strip()
229 | if os.path.isfile(out):
230 | return out
231 | else:
232 | None
233 | last_update_time = time.time()
234 | async def progress_callback(current, total, progress_message):
235 | percent = (current / total) * 100
236 | global last_update_time
237 | current_time = time.time()
238 |
239 | if current_time - last_update_time >= 10 or percent % 10 == 0:
240 | completed_blocks = int(percent // 10)
241 | remaining_blocks = 10 - completed_blocks
242 | progress_bar = "♦" * completed_blocks + "◇" * remaining_blocks
243 | current_mb = current / (1024 * 1024)
244 | total_mb = total / (1024 * 1024)
245 | await progress_message.edit(
246 | f"╭──────────────────╮\n"
247 | f"│ **__Uploading...__** \n"
248 | f"├──────────\n"
249 | f"│ {progress_bar}\n\n"
250 | f"│ **__Progress:__** {percent:.2f}%\n"
251 | f"│ **__Uploaded:__** {current_mb:.2f} MB / {total_mb:.2f} MB\n"
252 | f"╰──────────────────╯\n\n"
253 | f"**__Powered by Team SPY__**"
254 | )
255 |
256 | last_update_time = current_time
257 | async def prog_bar(current, total, ud_type, message, start):
258 |
259 | now = time.time()
260 | diff = now - start
261 | if round(diff % 10.00) == 0 or current == total:
262 |
263 | percentage = current * 100 / total
264 | speed = current / diff
265 | elapsed_time = round(diff) * 1000
266 | time_to_completion = round((total - current) / speed) * 1000
267 | estimated_total_time = elapsed_time + time_to_completion
268 |
269 | elapsed_time = TimeFormatter(milliseconds=elapsed_time)
270 | estimated_total_time = TimeFormatter(milliseconds=estimated_total_time)
271 |
272 | progress = "{0}{1}".format(
273 | ''.join(["♦" for i in range(math.floor(percentage / 10))]),
274 | ''.join(["◇" for i in range(10 - math.floor(percentage / 10))]))
275 |
276 | tmp = progress + PROGRESS_BAR.format(
277 | round(percentage, 2),
278 | humanbytes(current),
279 | humanbytes(total),
280 | humanbytes(speed),
281 |
282 | estimated_total_time if estimated_total_time != '' else "0 s"
283 | )
284 | try:
285 | await message.edit_text(
286 | text="{}\n│ {}".format(ud_type, tmp),)
287 |
288 | except:
289 | pass
290 |
--------------------------------------------------------------------------------
/devgagan/modules/start.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: start.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | from pyrogram import filters
16 | from devgagan import app
17 | from config import OWNER_ID
18 | from devgagan.core.func import subscribe
19 | import asyncio
20 | from devgagan.core.func import *
21 | from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
22 | from pyrogram.raw.functions.bots import SetBotInfo
23 | from pyrogram.raw.types import InputUserSelf
24 |
25 | from pyrogram.types import BotCommand, InlineKeyboardButton, InlineKeyboardMarkup
26 |
27 | @app.on_message(filters.command("set"))
28 | async def set(_, message):
29 | if message.from_user.id not in OWNER_ID:
30 | await message.reply("You are not authorized to use this command.")
31 | return
32 |
33 | await app.set_bot_commands([
34 | BotCommand("start", "🚀 Start the bot"),
35 | BotCommand("batch", "🫠 Extract in bulk"),
36 | BotCommand("login", "🔑 Get into the bot"),
37 | BotCommand("logout", "🚪 Get out of the bot"),
38 | BotCommand("token", "🎲 Get 3 hours free access"),
39 | BotCommand("adl", "👻 Download audio from 30+ sites"),
40 | BotCommand("dl", "💀 Download videos from 30+ sites"),
41 | BotCommand("freez", "🧊 Remove all expired user"),
42 | BotCommand("pay", "₹ Pay now to get subscription"),
43 | BotCommand("status", "⟳ Refresh Payment status"),
44 | BotCommand("transfer", "💘 Gift premium to others"),
45 | BotCommand("myplan", "⌛ Get your plan details"),
46 | BotCommand("add", "➕ Add user to premium"),
47 | BotCommand("rem", "➖ Remove from premium"),
48 | BotCommand("session", "🧵 Generate Pyrogramv2 session"),
49 | BotCommand("settings", "⚙️ Personalize things"),
50 | BotCommand("stats", "📊 Get stats of the bot"),
51 | BotCommand("plan", "🗓️ Check our premium plans"),
52 | BotCommand("terms", "🥺 Terms and conditions"),
53 | BotCommand("speedtest", "🚅 Speed of server"),
54 | BotCommand("lock", "🔒 Protect channel from extraction"),
55 | BotCommand("gcast", "⚡ Broadcast message to bot users"),
56 | BotCommand("help", "❓ If you're a noob, still!"),
57 | BotCommand("cancel", "🚫 Cancel batch process")
58 | ])
59 |
60 | await message.reply("✅ Commands configured successfully!")
61 |
62 |
63 |
64 |
65 | help_pages = [
66 | (
67 | "📝 **Bot Commands Overview (1/2)**:\n\n"
68 | "1. **/add userID**\n"
69 | "> Add user to premium (Owner only)\n\n"
70 | "2. **/rem userID**\n"
71 | "> Remove user from premium (Owner only)\n\n"
72 | "3. **/transfer userID**\n"
73 | "> Transfer premium to your beloved major purpose for resellers (Premium members only)\n\n"
74 | "4. **/get**\n"
75 | "> Get all user IDs (Owner only)\n\n"
76 | "5. **/lock**\n"
77 | "> Lock channel from extraction (Owner only)\n\n"
78 | "6. **/dl link**\n"
79 | "> Download videos (Not available in v3 if you are using)\n\n"
80 | "7. **/adl link**\n"
81 | "> Download audio (Not available in v3 if you are using)\n\n"
82 | "8. **/login**\n"
83 | "> Log into the bot for private channel access\n\n"
84 | "9. **/batch**\n"
85 | "> Bulk extraction for posts (After login)\n\n"
86 | ),
87 | (
88 | "📝 **Bot Commands Overview (2/2)**:\n\n"
89 | "10. **/logout**\n"
90 | "> Logout from the bot\n\n"
91 | "11. **/stats**\n"
92 | "> Get bot stats\n\n"
93 | "12. **/plan**\n"
94 | "> Check premium plans\n\n"
95 | "13. **/speedtest**\n"
96 | "> Test the server speed (not available in v3)\n\n"
97 | "14. **/terms**\n"
98 | "> Terms and conditions\n\n"
99 | "15. **/cancel**\n"
100 | "> Cancel ongoing batch process\n\n"
101 | "16. **/myplan**\n"
102 | "> Get details about your plans\n\n"
103 | "17. **/session**\n"
104 | "> Generate Pyrogram V2 session\n\n"
105 | "18. **/settings**\n"
106 | "> 1. SETCHATID : To directly upload in channel or group or user's dm use it with -100[chatID]\n"
107 | "> 2. SETRENAME : To add custom rename tag or username of your channels\n"
108 | "> 3. CAPTION : To add custom caption\n"
109 | "> 4. REPLACEWORDS : Can be used for words in deleted set via REMOVE WORDS\n"
110 | "> 5. RESET : To set the things back to default\n\n"
111 | "> You can set CUSTOM THUMBNAIL, PDF WATERMARK, VIDEO WATERMARK, SESSION-based login, etc. from settings\n\n"
112 | "**__Powered by Team SPY__**"
113 | )
114 | ]
115 |
116 |
117 | async def send_or_edit_help_page(_, message, page_number):
118 | if page_number < 0 or page_number >= len(help_pages):
119 | return
120 |
121 |
122 | prev_button = InlineKeyboardButton("◀️ Previous", callback_data=f"help_prev_{page_number}")
123 | next_button = InlineKeyboardButton("Next ▶️", callback_data=f"help_next_{page_number}")
124 |
125 |
126 | buttons = []
127 | if page_number > 0:
128 | buttons.append(prev_button)
129 | if page_number < len(help_pages) - 1:
130 | buttons.append(next_button)
131 |
132 |
133 | keyboard = InlineKeyboardMarkup([buttons])
134 |
135 |
136 | await message.delete()
137 |
138 |
139 | await message.reply(
140 | help_pages[page_number],
141 | reply_markup=keyboard
142 | )
143 |
144 |
145 | @app.on_message(filters.command("help"))
146 | async def help(client, message):
147 | join = await subscribe(client, message)
148 | if join == 1:
149 | return
150 |
151 |
152 | await send_or_edit_help_page(client, message, 0)
153 |
154 |
155 | @app.on_callback_query(filters.regex(r"help_(prev|next)_(\d+)"))
156 | async def on_help_navigation(client, callback_query):
157 | action, page_number = callback_query.data.split("_")[1], int(callback_query.data.split("_")[2])
158 |
159 | if action == "prev":
160 | page_number -= 1
161 | elif action == "next":
162 | page_number += 1
163 |
164 |
165 | await send_or_edit_help_page(client, callback_query.message, page_number)
166 |
167 |
168 | await callback_query.answer()
169 |
170 |
171 | from pyrogram import Client, filters
172 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
173 |
174 | @app.on_message(filters.command("terms") & filters.private)
175 | async def terms(client, message):
176 | terms_text = (
177 | "> 📜 **Terms and Conditions** 📜\n\n"
178 | "✨ We are not responsible for user deeds, and we do not promote copyrighted content. If any user engages in such activities, it is solely their responsibility.\n"
179 | "✨ Upon purchase, we do not guarantee the uptime, downtime, or the validity of the plan. __Authorization and banning of users are at our discretion; we reserve the right to ban or authorize users at any time.__\n"
180 | "✨ Payment to us **__does not guarantee__** authorization for the /batch command. All decisions regarding authorization are made at our discretion and mood.\n"
181 | )
182 |
183 | buttons = InlineKeyboardMarkup(
184 | [
185 | [InlineKeyboardButton("📋 See Plans", callback_data="see_plan")],
186 | [InlineKeyboardButton("💬 Contact Now", url="https://t.me/kingofpatal")],
187 | ]
188 | )
189 | await message.reply_text(terms_text, reply_markup=buttons)
190 |
191 |
192 | @app.on_message(filters.command("plan") & filters.private)
193 | async def plan(client, message):
194 | plan_text = (
195 | "> 💰 **Premium Price**:\n\n Starting from $2 or 200 INR accepted via **__Amazon Gift Card__** (terms and conditions apply).\n"
196 | "📥 **Download Limit**: Users can download up to 100,000 files in a single batch command.\n"
197 | "🛑 **Batch**: You will get two modes /bulk and /batch.\n"
198 | " - Users are advised to wait for the process to automatically cancel before proceeding with any downloads or uploads.\n\n"
199 | "📜 **Terms and Conditions**: For further details and complete terms and conditions, please send /terms.\n"
200 | )
201 |
202 | buttons = InlineKeyboardMarkup(
203 | [
204 | [InlineKeyboardButton("📜 See Terms", callback_data="see_terms")],
205 | [InlineKeyboardButton("💬 Contact Now", url="https://t.me/kingofpatal")],
206 | ]
207 | )
208 | await message.reply_text(plan_text, reply_markup=buttons)
209 |
210 |
211 | @app.on_callback_query(filters.regex("see_plan"))
212 | async def see_plan(client, callback_query):
213 | plan_text = (
214 | "> 💰**Premium Price**\n\n Starting from $2 or 200 INR accepted via **__Amazon Gift Card__** (terms and conditions apply).\n"
215 | "📥 **Download Limit**: Users can download up to 100,000 files in a single batch command.\n"
216 | "🛑 **Batch**: You will get two modes /bulk and /batch.\n"
217 | " - Users are advised to wait for the process to automatically cancel before proceeding with any downloads or uploads.\n\n"
218 | "📜 **Terms and Conditions**: For further details and complete terms and conditions, please send /terms or click See Terms👇\n"
219 | )
220 |
221 | buttons = InlineKeyboardMarkup(
222 | [
223 | [InlineKeyboardButton("📜 See Terms", callback_data="see_terms")],
224 | [InlineKeyboardButton("💬 Contact Now", url="https://t.me/kingofpatal")],
225 | ]
226 | )
227 | await callback_query.message.edit_text(plan_text, reply_markup=buttons)
228 |
229 |
230 | @app.on_callback_query(filters.regex("see_terms"))
231 | async def see_terms(client, callback_query):
232 | terms_text = (
233 | "> 📜 **Terms and Conditions** 📜\n\n"
234 | "✨ We are not responsible for user deeds, and we do not promote copyrighted content. If any user engages in such activities, it is solely their responsibility.\n"
235 | "✨ Upon purchase, we do not guarantee the uptime, downtime, or the validity of the plan. __Authorization and banning of users are at our discretion; we reserve the right to ban or authorize users at any time.__\n"
236 | "✨ Payment to us **__does not guarantee__** authorization for the /batch command. All decisions regarding authorization are made at our discretion and mood.\n"
237 | )
238 |
239 | buttons = InlineKeyboardMarkup(
240 | [
241 | [InlineKeyboardButton("📋 See Plans", callback_data="see_plan")],
242 | [InlineKeyboardButton("💬 Contact Now", url="https://t.me/kingofpatal")],
243 | ]
244 | )
245 | await callback_query.message.edit_text(terms_text, reply_markup=buttons)
246 |
247 |
248 |
--------------------------------------------------------------------------------
/devgagan/modules/plans.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: plans.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # ---------------------------------------------------
14 |
15 | from datetime import timedelta
16 | import pytz
17 | import datetime, time
18 | from devgagan import app
19 | import asyncio
20 | from config import OWNER_ID
21 | from devgagan.core.func import get_seconds
22 | from devgagan.core.mongo import plans_db
23 | from pyrogram import filters
24 |
25 |
26 |
27 | @app.on_message(filters.command("rem") & filters.user(OWNER_ID))
28 | async def remove_premium(client, message):
29 | if len(message.command) == 2:
30 | user_id = int(message.command[1])
31 | user = await client.get_users(user_id)
32 | data = await plans_db.check_premium(user_id)
33 |
34 | if data and data.get("_id"):
35 | await plans_db.remove_premium(user_id)
36 | await message.reply_text("ᴜꜱᴇʀ ʀᴇᴍᴏᴠᴇᴅ ꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ !")
37 | await client.send_message(
38 | chat_id=user_id,
39 | text=f"ʜᴇʏ {user.mention},\n\nʏᴏᴜʀ ᴘʀᴇᴍɪᴜᴍ ᴀᴄᴄᴇss ʜᴀs ʙᴇᴇɴ ʀᴇᴍᴏᴠᴇᴅ.\nᴛʜᴀɴᴋ ʏᴏᴜ ꜰᴏʀ ᴜsɪɴɢ ᴏᴜʀ sᴇʀᴠɪᴄᴇ 😊."
40 | )
41 | else:
42 | await message.reply_text("ᴜɴᴀʙʟᴇ ᴛᴏ ʀᴇᴍᴏᴠᴇ ᴜꜱᴇᴅ !\nᴀʀᴇ ʏᴏᴜ ꜱᴜʀᴇ, ɪᴛ ᴡᴀꜱ ᴀ ᴘʀᴇᴍɪᴜᴍ ᴜꜱᴇʀ ɪᴅ ?")
43 | else:
44 | await message.reply_text("ᴜꜱᴀɢᴇ : /rem user_id")
45 |
46 |
47 |
48 | @app.on_message(filters.command("myplan"))
49 | async def myplan(client, message):
50 | user_id = message.from_user.id
51 | user = message.from_user.mention
52 | data = await plans_db.check_premium(user_id)
53 | if data and data.get("expire_date"):
54 | expiry = data.get("expire_date")
55 | expiry_ist = expiry.astimezone(pytz.timezone("Asia/Kolkata"))
56 | expiry_str_in_ist = expiry.astimezone(pytz.timezone("Asia/Kolkata")).strftime("%d-%m-%Y\n⏱️ ᴇxᴘɪʀʏ ᴛɪᴍᴇ : %I:%M:%S %p")
57 |
58 | current_time = datetime.datetime.now(pytz.timezone("Asia/Kolkata"))
59 | time_left = expiry_ist - current_time
60 |
61 |
62 | days = time_left.days
63 | hours, remainder = divmod(time_left.seconds, 3600)
64 | minutes, seconds = divmod(remainder, 60)
65 |
66 |
67 | time_left_str = f"{days} ᴅᴀʏꜱ, {hours} ʜᴏᴜʀꜱ, {minutes} ᴍɪɴᴜᴛᴇꜱ"
68 | await message.reply_text(f"⚜️ ᴘʀᴇᴍɪᴜᴍ ᴜꜱᴇʀ ᴅᴀᴛᴀ :\n\n👤 ᴜꜱᴇʀ : {user}\n⚡ ᴜꜱᴇʀ ɪᴅ : {user_id}\n⏰ ᴛɪᴍᴇ ʟᴇꜰᴛ : {time_left_str}\n⌛️ ᴇxᴘɪʀʏ ᴅᴀᴛᴇ : {expiry_str_in_ist}")
69 | else:
70 | await message.reply_text(f"ʜᴇʏ {user},\n\nʏᴏᴜ ᴅᴏ ɴᴏᴛ ʜᴀᴠᴇ ᴀɴʏ ᴀᴄᴛɪᴠᴇ ᴘʀᴇᴍɪᴜᴍ ᴘʟᴀɴs")
71 |
72 |
73 |
74 | @app.on_message(filters.command("check") & filters.user(OWNER_ID))
75 | async def get_premium(client, message):
76 | if len(message.command) == 2:
77 | user_id = int(message.command[1])
78 | user = await client.get_users(user_id)
79 | data = await plans_db.check_premium(user_id)
80 | if data and data.get("expire_date"):
81 | expiry = data.get("expire_date")
82 | expiry_ist = expiry.astimezone(pytz.timezone("Asia/Kolkata"))
83 | expiry_str_in_ist = expiry.astimezone(pytz.timezone("Asia/Kolkata")).strftime("%d-%m-%Y\n⏱️ ᴇxᴘɪʀʏ ᴛɪᴍᴇ : %I:%M:%S %p")
84 |
85 | current_time = datetime.datetime.now(pytz.timezone("Asia/Kolkata"))
86 | time_left = expiry_ist - current_time
87 |
88 |
89 | days = time_left.days
90 | hours, remainder = divmod(time_left.seconds, 3600)
91 | minutes, seconds = divmod(remainder, 60)
92 |
93 |
94 | time_left_str = f"{days} days, {hours} hours, {minutes} minutes"
95 | await message.reply_text(f"⚜️ ᴘʀᴇᴍɪᴜᴍ ᴜꜱᴇʀ ᴅᴀᴛᴀ :\n\n👤 ᴜꜱᴇʀ : {user.mention}\n⚡ ᴜꜱᴇʀ ɪᴅ : {user_id}\n⏰ ᴛɪᴍᴇ ʟᴇꜰᴛ : {time_left_str}\n⌛️ ᴇxᴘɪʀʏ ᴅᴀᴛᴇ : {expiry_str_in_ist}")
96 | else:
97 | await message.reply_text("ɴᴏ ᴀɴʏ ᴘʀᴇᴍɪᴜᴍ ᴅᴀᴛᴀ ᴏꜰ ᴛʜᴇ ᴡᴀꜱ ꜰᴏᴜɴᴅ ɪɴ ᴅᴀᴛᴀʙᴀꜱᴇ !")
98 | else:
99 | await message.reply_text("ᴜꜱᴀɢᴇ : /check user_id")
100 |
101 |
102 | @app.on_message(filters.command("add") & filters.user(OWNER_ID))
103 | async def give_premium_cmd_handler(client, message):
104 | if len(message.command) == 4:
105 | time_zone = datetime.datetime.now(pytz.timezone("Asia/Kolkata"))
106 | current_time = time_zone.strftime("%d-%m-%Y\n⏱️ ᴊᴏɪɴɪɴɢ ᴛɪᴍᴇ : %I:%M:%S %p")
107 | user_id = int(message.command[1])
108 | user = await client.get_users(user_id)
109 | time = message.command[2]+" "+message.command[3]
110 | seconds = await get_seconds(time)
111 | if seconds > 0:
112 | expiry_time = datetime.datetime.now() + datetime.timedelta(seconds=seconds)
113 | await plans_db.add_premium(user_id, expiry_time)
114 | data = await plans_db.check_premium(user_id)
115 | expiry = data.get("expire_date")
116 | expiry_str_in_ist = expiry.astimezone(pytz.timezone("Asia/Kolkata")).strftime("%d-%m-%Y\n⏱️ ᴇxᴘɪʀʏ ᴛɪᴍᴇ : %I:%M:%S %p")
117 | await message.reply_text(f"ᴘʀᴇᴍɪᴜᴍ ᴀᴅᴅᴇᴅ ꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ ✅\n\n👤 ᴜꜱᴇʀ : {user.mention}\n⚡ ᴜꜱᴇʀ ɪᴅ : {user_id}\n⏰ ᴘʀᴇᴍɪᴜᴍ ᴀᴄᴄᴇꜱꜱ : {time}\n\n⏳ ᴊᴏɪɴɪɴɢ ᴅᴀᴛᴇ : {current_time}\n\n⌛️ ᴇxᴘɪʀʏ ᴅᴀᴛᴇ : {expiry_str_in_ist} \n\n__**Powered by Team SPY__**", disable_web_page_preview=True)
118 | await client.send_message(
119 | chat_id=user_id,
120 | text=f"👋 ʜᴇʏ {user.mention},\nᴛʜᴀɴᴋ ʏᴏᴜ ꜰᴏʀ ᴘᴜʀᴄʜᴀꜱɪɴɢ ᴘʀᴇᴍɪᴜᴍ.\nᴇɴᴊᴏʏ !! ✨🎉\n\n⏰ ᴘʀᴇᴍɪᴜᴍ ᴀᴄᴄᴇꜱꜱ : {time}\n⏳ ᴊᴏɪɴɪɴɢ ᴅᴀᴛᴇ : {current_time}\n\n⌛️ ᴇxᴘɪʀʏ ᴅᴀᴛᴇ : {expiry_str_in_ist}", disable_web_page_preview=True
121 | )
122 |
123 | else:
124 | await message.reply_text("Invalid time format. Please use '1 day for days', '1 hour for hours', or '1 min for minutes', or '1 month for months' or '1 year for year'")
125 | else:
126 | await message.reply_text("Usage : /add user_id time (e.g., '1 day for days', '1 hour for hours', or '1 min for minutes', or '1 month for months' or '1 year for year')")
127 |
128 |
129 | @app.on_message(filters.command("transfer"))
130 | async def transfer_premium(client, message):
131 | if len(message.command) == 2:
132 | new_user_id = int(message.command[1]) # The user ID to whom premium is transferred
133 | sender_user_id = message.from_user.id # The current premium user issuing the command
134 | sender_user = await client.get_users(sender_user_id)
135 | new_user = await client.get_users(new_user_id)
136 |
137 | # Fetch sender's premium plan details
138 | data = await plans_db.check_premium(sender_user_id)
139 |
140 | if data and data.get("_id"): # Verify sender is already a premium user
141 | expiry = data.get("expire_date")
142 |
143 | # Remove premium for the sender
144 | await plans_db.remove_premium(sender_user_id)
145 |
146 | # Add premium for the new user with the same expiry date
147 | await plans_db.add_premium(new_user_id, expiry)
148 |
149 | # Convert expiry date to IST format for display
150 | expiry_str_in_ist = expiry.astimezone(pytz.timezone("Asia/Kolkata")).strftime(
151 | "%d-%m-%Y\n⏱️ **Expiry Time:** %I:%M:%S %p"
152 | )
153 | time_zone = datetime.datetime.now(pytz.timezone("Asia/Kolkata"))
154 | current_time = time_zone.strftime("%d-%m-%Y\n⏱️ **Transfer Time:** %I:%M:%S %p")
155 |
156 | # Confirmation message to the sender
157 | await message.reply_text(
158 | f"✅ **Premium Plan Transferred Successfully!**\n\n"
159 | f"👤 **From:** {sender_user.mention}\n"
160 | f"👤 **To:** {new_user.mention}\n"
161 | f"⏳ **Expiry Date:** {expiry_str_in_ist}\n\n"
162 | f"__Powered by Team SPY__ 🚀"
163 | )
164 |
165 | # Notification to the new user
166 | await client.send_message(
167 | chat_id=new_user_id,
168 | text=(
169 | f"👋 **Hey {new_user.mention},**\n\n"
170 | f"🎉 **Your Premium Plan has been Transferred!**\n"
171 | f"🛡️ **Transferred From:** {sender_user.mention}\n\n"
172 | f"⏳ **Expiry Date:** {expiry_str_in_ist}\n"
173 | f"📅 **Transferred On:** {current_time}\n\n"
174 | f"__Enjoy the Service!__ ✨"
175 | )
176 | )
177 | else:
178 | await message.reply_text("⚠️ **You are not a Premium user!**\n\nOnly Premium users can transfer their plans.")
179 | else:
180 | await message.reply_text("⚠️ **Usage:** /transfer user_id\n\nReplace `user_id` with the new user's ID.")
181 |
182 |
183 | async def premium_remover():
184 | all_users = await plans_db.premium_users()
185 | removed_users = []
186 | not_removed_users = []
187 |
188 | for user_id in all_users:
189 | try:
190 | user = await app.get_users(user_id)
191 | chk_time = await plans_db.check_premium(user_id)
192 |
193 | if chk_time and chk_time.get("expire_date"):
194 | expiry_date = chk_time["expire_date"]
195 |
196 | if expiry_date <= datetime.datetime.now():
197 | name = user.first_name
198 | await plans_db.remove_premium(user_id)
199 | await app.send_message(user_id, text=f"Hello {name}, your premium subscription has expired.")
200 | print(f"{name}, your premium subscription has expired.")
201 | removed_users.append(f"{name} ({user_id})")
202 | else:
203 | name = user.first_name
204 | current_time = datetime.datetime.now()
205 | time_left = expiry_date - current_time
206 |
207 | days = time_left.days
208 | hours, remainder = divmod(time_left.seconds, 3600)
209 | minutes, seconds = divmod(remainder, 60)
210 |
211 | if days > 0:
212 | remaining_time = f"{days} days, {hours} hours, {minutes} minutes, {seconds} seconds"
213 | elif hours > 0:
214 | remaining_time = f"{hours} hours, {minutes} minutes, {seconds} seconds"
215 | elif minutes > 0:
216 | remaining_time = f"{minutes} minutes, {seconds} seconds"
217 | else:
218 | remaining_time = f"{seconds} seconds"
219 |
220 | print(f"{name} : Remaining Time : {remaining_time}")
221 | not_removed_users.append(f"{name} ({user_id})")
222 | except:
223 | await plans_db.remove_premium(user_id)
224 | print(f"Unknown users captured : {user_id} removed")
225 | removed_users.append(f"Unknown ({user_id})")
226 |
227 | return removed_users, not_removed_users
228 |
229 |
230 | @app.on_message(filters.command("freez") & filters.user(OWNER_ID))
231 | async def refresh_users(_, message):
232 | removed_users, not_removed_users = await premium_remover()
233 | # Create a summary message
234 | removed_text = "\n".join(removed_users) if removed_users else "No users removed."
235 | not_removed_text = "\n".join(not_removed_users) if not_removed_users else "No users remaining with premium."
236 | summary = (
237 | f"**Here is Summary...**\n\n"
238 | f"> **Removed Users:**\n{removed_text}\n\n"
239 | f"> **Not Removed Users:**\n{not_removed_text}"
240 | )
241 | await message.reply(summary)
242 |
243 |
--------------------------------------------------------------------------------
/devgagan/modules/main.py:
--------------------------------------------------------------------------------
1 | # ---------------------------------------------------
2 | # File Name: main.py
3 | # Description: A Pyrogram bot for downloading files from Telegram channels or groups
4 | # and uploading them back to Telegram.
5 | # Author: Gagan
6 | # GitHub: https://github.com/devgaganin/
7 | # Telegram: https://t.me/team_spy_pro
8 | # YouTube: https://youtube.com/@dev_gagan
9 | # Created: 2025-01-11
10 | # Last Modified: 2025-01-11
11 | # Version: 2.0.5
12 | # License: MIT License
13 | # More readable
14 | # ---------------------------------------------------
15 |
16 | import time
17 | import random
18 | import string
19 | import asyncio
20 | from pyrogram import filters, Client
21 | from devgagan import app, userrbot
22 | from config import API_ID, API_HASH, FREEMIUM_LIMIT, PREMIUM_LIMIT, OWNER_ID, DEFAULT_SESSION
23 | from devgagan.core.get_func import get_msg
24 | from devgagan.core.func import *
25 | from devgagan.core.mongo import db
26 | from pyrogram.errors import FloodWait
27 | from datetime import datetime, timedelta
28 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
29 | import subprocess
30 | from devgagan.modules.shrink import is_user_verified
31 | async def generate_random_name(length=8):
32 | return ''.join(random.choices(string.ascii_lowercase, k=length))
33 |
34 |
35 |
36 | users_loop = {}
37 | interval_set = {}
38 | batch_mode = {}
39 |
40 | async def process_and_upload_link(userbot, user_id, msg_id, link, retry_count, message):
41 | try:
42 | await get_msg(userbot, user_id, msg_id, link, retry_count, message)
43 | try:
44 | await app.delete_messages(user_id, msg_id)
45 | except Exception:
46 | pass
47 | await asyncio.sleep(15)
48 | finally:
49 | pass
50 |
51 | # Function to check if the user can proceed
52 | async def check_interval(user_id, freecheck):
53 | if freecheck != 1 or await is_user_verified(user_id): # Premium or owner users can always proceed
54 | return True, None
55 |
56 | now = datetime.now()
57 |
58 | # Check if the user is on cooldown
59 | if user_id in interval_set:
60 | cooldown_end = interval_set[user_id]
61 | if now < cooldown_end:
62 | remaining_time = (cooldown_end - now).seconds
63 | return False, f"Please wait {remaining_time} seconds(s) before sending another link. Alternatively, purchase premium for instant access.\n\n> Hey 👋 You can use /token to use the bot free for 3 hours without any time limit."
64 | else:
65 | del interval_set[user_id] # Cooldown expired, remove user from interval set
66 |
67 | return True, None
68 |
69 | async def set_interval(user_id, interval_minutes=45):
70 | now = datetime.now()
71 | # Set the cooldown interval for the user
72 | interval_set[user_id] = now + timedelta(seconds=interval_minutes)
73 |
74 |
75 | @app.on_message(
76 | filters.regex(r'https?://(?:www\.)?t\.me/[^\s]+|tg://openmessage\?user_id=\w+&message_id=\d+')
77 | & filters.private
78 | )
79 | async def single_link(_, message):
80 | user_id = message.chat.id
81 |
82 | # Check subscription and batch mode
83 | if await subscribe(_, message) == 1 or user_id in batch_mode:
84 | return
85 |
86 | # Check if user is already in a loop
87 | if users_loop.get(user_id, False):
88 | await message.reply(
89 | "You already have an ongoing process. Please wait for it to finish or cancel it with /cancel."
90 | )
91 | return
92 |
93 | # Check freemium limits
94 | if await chk_user(message, user_id) == 1 and FREEMIUM_LIMIT == 0 and user_id not in OWNER_ID and not await is_user_verified(user_id):
95 | await message.reply("Freemium service is currently not available. Upgrade to premium for access.")
96 | return
97 |
98 | # Check cooldown
99 | can_proceed, response_message = await check_interval(user_id, await chk_user(message, user_id))
100 | if not can_proceed:
101 | await message.reply(response_message)
102 | return
103 |
104 | # Add user to the loop
105 | users_loop[user_id] = True
106 |
107 | link = message.text if "tg://openmessage" in message.text else get_link(message.text)
108 | msg = await message.reply("Processing...")
109 | userbot = await initialize_userbot(user_id)
110 | try:
111 | if await is_normal_tg_link(link):
112 | await process_and_upload_link(userbot, user_id, msg.id, link, 0, message)
113 | await set_interval(user_id, interval_minutes=45)
114 | else:
115 | await process_special_links(userbot, user_id, msg, link)
116 |
117 | except FloodWait as fw:
118 | await msg.edit_text(f'Try again after {fw.x} seconds due to floodwait from Telegram.')
119 | except Exception as e:
120 | await msg.edit_text(f"Link: `{link}`\n\n**Error:** {str(e)}")
121 | finally:
122 | users_loop[user_id] = False
123 | try:
124 | await msg.delete()
125 | except Exception:
126 | pass
127 |
128 |
129 | async def initialize_userbot(user_id): # this ensure the single startup .. even if logged in or not
130 | data = await db.get_data(user_id)
131 | if data and data.get("session"):
132 | try:
133 | device = 'iPhone 16 Pro' # added gareebi text
134 | userbot = Client(
135 | "userbot",
136 | api_id=API_ID,
137 | api_hash=API_HASH,
138 | device_model=device,
139 | session_string=data.get("session")
140 | )
141 | await userbot.start()
142 | return userbot
143 | except Exception:
144 | await app.send_message(user_id, "Login Expired re do login")
145 | return None
146 | else:
147 | if DEFAULT_SESSION:
148 | return userrbot
149 | else:
150 | return None
151 |
152 |
153 | async def is_normal_tg_link(link: str) -> bool:
154 | """Check if the link is a standard Telegram link."""
155 | special_identifiers = ['t.me/+', 't.me/c/', 't.me/b/', 'tg://openmessage']
156 | return 't.me/' in link and not any(x in link for x in special_identifiers)
157 |
158 | async def process_special_links(userbot, user_id, msg, link):
159 | if userbot is None:
160 | return await msg.edit_text("Try logging in to the bot and try again.")
161 | if 't.me/+' in link:
162 | result = await userbot_join(userbot, link)
163 | await msg.edit_text(result)
164 | return
165 | special_patterns = ['t.me/c/', 't.me/b/', '/s/', 'tg://openmessage']
166 | if any(sub in link for sub in special_patterns):
167 | await process_and_upload_link(userbot, user_id, msg.id, link, 0, msg)
168 | await set_interval(user_id, interval_minutes=45)
169 | return
170 | await msg.edit_text("Invalid link...")
171 |
172 |
173 | @app.on_message(filters.command("batch") & filters.private)
174 | async def batch_link(_, message):
175 | join = await subscribe(_, message)
176 | if join == 1:
177 | return
178 | user_id = message.chat.id
179 | # Check if a batch process is already running
180 | if users_loop.get(user_id, False):
181 | await app.send_message(
182 | message.chat.id,
183 | "You already have a batch process running. Please wait for it to complete."
184 | )
185 | return
186 |
187 | freecheck = await chk_user(message, user_id)
188 | if freecheck == 1 and FREEMIUM_LIMIT == 0 and user_id not in OWNER_ID and not await is_user_verified(user_id):
189 | await message.reply("Freemium service is currently not available. Upgrade to premium for access.")
190 | return
191 |
192 | max_batch_size = FREEMIUM_LIMIT if freecheck == 1 else PREMIUM_LIMIT
193 |
194 | # Start link input
195 | for attempt in range(3):
196 | start = await app.ask(message.chat.id, "Please send the start link.\n\n> Maximum tries 3")
197 | start_id = start.text.strip()
198 | s = start_id.split("/")[-1]
199 | if s.isdigit():
200 | cs = int(s)
201 | break
202 | await app.send_message(message.chat.id, "Invalid link. Please send again ...")
203 | else:
204 | await app.send_message(message.chat.id, "Maximum attempts exceeded. Try later.")
205 | return
206 |
207 | # Number of messages input
208 | for attempt in range(3):
209 | num_messages = await app.ask(message.chat.id, f"How many messages do you want to process?\n> Max limit {max_batch_size}")
210 | try:
211 | cl = int(num_messages.text.strip())
212 | if 1 <= cl <= max_batch_size:
213 | break
214 | raise ValueError()
215 | except ValueError:
216 | await app.send_message(
217 | message.chat.id,
218 | f"Invalid number. Please enter a number between 1 and {max_batch_size}."
219 | )
220 | else:
221 | await app.send_message(message.chat.id, "Maximum attempts exceeded. Try later.")
222 | return
223 |
224 | # Validate and interval check
225 | can_proceed, response_message = await check_interval(user_id, freecheck)
226 | if not can_proceed:
227 | await message.reply(response_message)
228 | return
229 |
230 | join_button = InlineKeyboardButton("Join Channel", url="https://t.me/team_spy_pro")
231 | keyboard = InlineKeyboardMarkup([[join_button]])
232 | pin_msg = await app.send_message(
233 | user_id,
234 | f"Batch process started ⚡\nProcessing: 0/{cl}\n\n**Powered by Team SPY**",
235 | reply_markup=keyboard
236 | )
237 | await pin_msg.pin(both_sides=True)
238 |
239 | users_loop[user_id] = True
240 | try:
241 | normal_links_handled = False
242 | userbot = await initialize_userbot(user_id)
243 | # Handle normal links first
244 | for i in range(cs, cs + cl):
245 | if user_id in users_loop and users_loop[user_id]:
246 | url = f"{'/'.join(start_id.split('/')[:-1])}/{i}"
247 | link = get_link(url)
248 | # Process t.me links (normal) without userbot
249 | if 't.me/' in link and not any(x in link for x in ['t.me/b/', 't.me/c/', 'tg://openmessage']):
250 | msg = await app.send_message(message.chat.id, f"Processing...")
251 | await process_and_upload_link(userbot, user_id, msg.id, link, 0, message)
252 | await pin_msg.edit_text(
253 | f"Batch process started ⚡\nProcessing: {i - cs + 1}/{cl}\n\n**__Powered by Team SPY__**",
254 | reply_markup=keyboard
255 | )
256 | normal_links_handled = True
257 | if normal_links_handled:
258 | await set_interval(user_id, interval_minutes=300)
259 | await pin_msg.edit_text(
260 | f"Batch completed successfully for {cl} messages 🎉\n\n**__Powered by Team SPY__**",
261 | reply_markup=keyboard
262 | )
263 | await app.send_message(message.chat.id, "Batch completed successfully! 🎉")
264 | return
265 |
266 | # Handle special links with userbot
267 | for i in range(cs, cs + cl):
268 | if not userbot:
269 | await app.send_message(message.chat.id, "Login in bot first ...")
270 | users_loop[user_id] = False
271 | return
272 | if user_id in users_loop and users_loop[user_id]:
273 | url = f"{'/'.join(start_id.split('/')[:-1])}/{i}"
274 | link = get_link(url)
275 | if any(x in link for x in ['t.me/b/', 't.me/c/']):
276 | msg = await app.send_message(message.chat.id, f"Processing...")
277 | await process_and_upload_link(userbot, user_id, msg.id, link, 0, message)
278 | await pin_msg.edit_text(
279 | f"Batch process started ⚡\nProcessing: {i - cs + 1}/{cl}\n\n**__Powered by Team SPY__**",
280 | reply_markup=keyboard
281 | )
282 |
283 | await set_interval(user_id, interval_minutes=300)
284 | await pin_msg.edit_text(
285 | f"Batch completed successfully for {cl} messages 🎉\n\n**__Powered by Team SPY__**",
286 | reply_markup=keyboard
287 | )
288 | await app.send_message(message.chat.id, "Batch completed successfully! 🎉")
289 |
290 | except Exception as e:
291 | await app.send_message(message.chat.id, f"Error: {e}")
292 | finally:
293 | users_loop.pop(user_id, None)
294 |
295 | @app.on_message(filters.command("cancel"))
296 | async def stop_batch(_, message):
297 | user_id = message.chat.id
298 |
299 | # Check if there is an active batch process for the user
300 | if user_id in users_loop and users_loop[user_id]:
301 | users_loop[user_id] = False # Set the loop status to False
302 | await app.send_message(
303 | message.chat.id,
304 | "Batch processing has been stopped successfully. You can start a new batch now if you want."
305 | )
306 | elif user_id in users_loop and not users_loop[user_id]:
307 | await app.send_message(
308 | message.chat.id,
309 | "The batch process was already stopped. No active batch to cancel."
310 | )
311 | else:
312 | await app.send_message(
313 | message.chat.id,
314 | "No active batch processing is running to cancel."
315 | )
316 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | \1"), 225 | (r"```(.*?)```", r"
\1"), 226 | (r"`(.*?)`", r"
\1"),
227 | (r"\*\*(.*?)\*\*", r"\1"),
228 | (r"\*(.*?)\*", r"\1"),
229 | (r"__(.*?)__", r"\1"),
230 | (r"_(.*?)_", r"\1"),
231 | (r"~~(.*?)~~", r"