├── runtime.txt
├── Procfile
├── TechifyBots
├── TechifyBots.txt
├── callback.py
├── db.py
├── fsub.py
└── commands.py
├── run cmd.txt
├── requirements.txt
├── app.py
├── LICENSE
├── Script.py
├── config.py
├── README.md
└── bot.py
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.13.5
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: python3 bot.py
--------------------------------------------------------------------------------
/TechifyBots/TechifyBots.txt:
--------------------------------------------------------------------------------
1 | # ©TechifyBots
2 |
--------------------------------------------------------------------------------
/run cmd.txt:
--------------------------------------------------------------------------------
1 | gunicorn app:app & python3 bot.py
2 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pyrofork>=2.3.68
2 | tgcrypto
3 | motor
4 | aiohttp
5 | pytz
6 |
7 | # For Web Deployable
8 | Flask
9 | gunicorn
10 | Jinja2
11 | werkzeug
12 | itsdangerous
13 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | app = Flask(__name__)
3 |
4 | @app.route('/')
5 | def hello_world():
6 | return 'TechifyBots'
7 |
8 |
9 | if __name__ == "__main__":
10 | app.run()
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 TechifyBots
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 |
--------------------------------------------------------------------------------
/Script.py:
--------------------------------------------------------------------------------
1 | class text(object):
2 | START = """{},
3 |
4 | ɪ ᴀᴍ sɪᴍᴘʟᴇ ʙᴜᴛ ᴘᴏᴡᴇʀꜰᴜʟʟ ᴀᴜᴛᴏ ʀᴇᴀᴄᴛɪᴏɴ ʙᴏᴛ.
5 |
6 | ᴊᴜsᴛ ᴀᴅᴅ ᴍᴇ ᴀs ᴀ ᴀᴅᴍɪɴ ɪɴ ʏᴏᴜʀ ᴄʜᴀɴɴᴇʟ ᴏʀ ɢʀᴏᴜᴘ ᴛʜᴇɴ sᴇᴇ ᴍʏ ᴘᴏᴡᴇʀ
7 |
8 | ‣ ᴍᴀɪɴᴛᴀɪɴᴇᴅ ʙʏ : ʀᴀʜᴜʟ
"""
9 |
10 | LOG = """👁️🗨️ 𝘜𝘚𝘌𝘙 𝘋𝘌𝘛𝘈𝘐𝘓𝘚
11 |
12 | ○ 𝘐𝘋 : {}
13 | ○ 𝘋𝘊 : {}
14 | ○ 𝘍𝘪𝘳𝘴𝘵 𝘕𝘢𝘮𝘦 : {}
15 | ○ 𝘜𝘴𝘦𝘳𝘕𝘢𝘮𝘦 : {}
16 |
17 | 𝘉𝘺 = @{}"""
18 |
19 | ABOUT = """‣ ᴍʏ ɴᴀᴍᴇ : ǫᴜɪᴄᴋ ʀᴇᴀᴄᴛ ʙᴏᴛ
20 | ‣ ʟɪʙʀᴀʀʏ : ᴘʏʀᴏɢʀᴀᴍ
21 | ‣ ᴅᴀᴛᴀʙᴀsᴇ : ᴍᴏɴɢᴏᴅʙ
22 | ‣ ʟᴀɴɢᴜᴀɢᴇ : ᴘʏᴛʜᴏɴ 𝟹
23 | ‣ ʙᴏᴛ sᴇʀᴠᴇʀ : ᴋᴏʏᴇʙ
24 | ‣ ᴄʀᴇᴀᴛᴇᴅ ʙʏ : ʀᴀʜᴜʟ"""
25 |
26 | HELP = """{},
27 |
28 | ᴛʜɪꜱ ɪꜱ ʀᴇᴀʟʟʏ sɪᴍᴘʟᴇ 😂
29 |
30 | ᴊᴜsᴛ ᴍᴀᴋᴇ ᴍᴇ ᴀᴅᴍɪɴ ɪɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ ᴏʀ ᴄʜᴀɴɴᴇʟ, ᴀɴᴅ ᴇɴᴊᴏʏ ᴀᴜᴛᴏᴍᴀᴛᴇᴅ ᴍᴀɢɪᴄᴀʟ ʀᴇᴀᴄᴛɪᴏɴs 💞
31 |
32 |
𝖲𝗍𝗂𝗅𝗅 𝗁𝖺𝗏𝖾 𝗂𝗌𝗌𝗎𝖾𝗌? 𝖴𝗌𝖾 /help
"""
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | import os
2 | from typing import List
3 |
4 | API_ID = os.environ.get("API_ID", "")
5 | API_HASH = os.environ.get("API_HASH", "")
6 | BOT_TOKEN = os.environ.get("BOT_TOKEN", "")
7 | ADMIN = int(os.environ.get("ADMIN", "1255023013"))
8 | PICS = (os.environ.get("PICS", "https://i.ibb.co/7NX2TY1N/photo-2025-07-13-12-32-15-7531449578462117904.jpg https://i.ibb.co/3mSrtgDw/photo-2025-07-14-08-03-12-7526845489484922908.jpg https://i.ibb.co/8nNgvwyn/photo-2025-07-14-08-02-56-7526846752205307932.jpg https://i.ibb.co/bgH9CXnx/photo-2024-11-07-03-36-42-7531449810390351892.jpg https://i.ibb.co/s9gt09Vq/photo-2025-07-14-08-00-51-7531450025138716692.jpg https://i.ibb.co/8g1fH31T/cutie.jpg")).split()
9 |
10 | LOG_CHANNEL = int(os.environ.get("LOG_CHANNEL", "-1002686843200"))
11 |
12 | DB_URI = os.environ.get("DB_URI", "")
13 | DB_NAME = os.environ.get("DB_NAME", "store")
14 |
15 | IS_FSUB = os.environ.get("IS_FSUB", "False").lower() == "true" # Set "True" For Enable Force Subscribe
16 | AUTH_CHANNELS = list(map(int, os.environ.get("AUTH_CHANNEL", "").split())) # Add Multiple channel ids
17 | AUTH_REQ_CHANNELS = list(map(int, os.environ.get("AUTH_REQ_CHANNEL", "").split())) # Add Multiple channel ids
18 | FSUB_EXPIRE = int(os.environ.get("FSUB_EXPIRE", 2)) # minutes, 0 = no expiry
19 |
20 | EMOJIS = [
21 | "👍", "❤️", "🔥", "🥰", "👏", "😁", "🤔", "🤯", "😱", "😢",
22 | "🎉", "🤩", "🙏", "👌", "🕊", "🤡", "🥱", "🥴", "😍", "🤷♂️",
23 | "❤️🔥", "🌚", "💯", "🤣", "⚡", "🏆", "🗿", "😐", "🤨", "🍾",
24 | "💋", "😈", "😴", "😭", "🤓", "👻", "👨💻", "👀", "🙈", "🤷♀️",
25 | "😇", "🤝", "✍️", "🤗", "🫡", "😨", "🧑🎄", "🎄", "⛄", "🤪",
26 | "🆒", "💘", "🙊", "🦄", "😘", "🙉", "💊", "😎", "👾", "🤷"
27 | ]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | 🩷 Thanks for Being Here 🩷
6 |
7 |
8 |
9 | ### 🥰 FEATURES
10 |
11 | Tap On Me For Bot Features
12 |
13 | - unlimited reactions
14 | - Supports all type of emojies
15 | - work in both channels & groups
16 | - Fully modified repo
17 | - Stats & Broadcast feature available
18 | - Fsub & Log channel support
19 | - Deploy To Koyeb + Heroku + Railway.
20 | - [Developer support](https://telegram.me/TechifySupport) 24x7
21 |
22 |
23 |
24 | ### 🔥 VARIABLES
25 |
26 | Tap On Me For Environment Variable
27 |
28 | - `API_ID` : Get From [Here](https://youtu.be/y5FwAobQ-Kc)
29 | - `API_HASH` : Get From [Here](https://youtu.be/y5FwAobQ-Kc)
30 | - `BOT_TOKEN` : Get From [BotFather](https://youtu.be/aJILCCXfNVM)
31 | - `PICS` - Your bot start images (you can add multiple images)
32 | - `ADMIN` : Your Telegram User ID
33 | - `DB_URI` : MongoDB database get from [here](https://youtu.be/j8LIuM7vv18)
34 | - `LOG_CHANNEL` : Your Log channel ID.
35 | - `FSUB_EXPIRE` : Your FSUB link expire time.
36 | - `AUTH_CHANNELS` : Your Public & Private FSUB channels ID.
37 | - `AUTH_REQ_CHANNELS` : Your Private Request FSUB channels ID.
38 |
39 |
40 | ### 😍 COMMANDS
41 |
42 | Tap On Me For Commands
43 |
44 | ```
45 | start - Start The Bot
46 | broadcast - (admin only) Broadcast message to bot users
47 | stats - (admin only) check bots stats
48 | ```
49 |
50 |
51 | ### 💞 CREDIT
52 | - [TechifyBots](https://github.com/TechifyBots)
53 |
54 | ### 😇 [SUPPORT](https://techifybots.github.io/PayWeb)
55 |
56 | ### 🥳 [DEVELOPER](https://instagram.com/ImRahulDhankhar)
57 |
58 | ### 📌 NOTE
59 |
60 | 𝘊𝘰𝘱𝘺𝘪𝘯𝘨 𝘰𝘳 𝘚𝘦𝘭𝘭𝘪𝘯𝘨 𝘵𝘩𝘪𝘴 𝘳𝘦𝘱𝘰 𝘪𝘴 𝘴𝘵𝘳𝘪𝘤𝘵𝘭𝘺 𝘱𝘳𝘰𝘩𝘪𝘣𝘪𝘵𝘦𝘥.
--------------------------------------------------------------------------------
/TechifyBots/callback.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client
2 | from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
3 | from Script import text
4 | from config import ADMIN
5 |
6 | @Client.on_callback_query()
7 | async def callback_query_handler(client, query: CallbackQuery):
8 | if query.data == "start":
9 | await query.message.edit_caption(
10 | caption=text.START.format(query.from_user.mention),
11 | reply_markup=InlineKeyboardMarkup([
12 | [InlineKeyboardButton('⇆ 𝖠𝖽𝖽 𝖬𝖾 𝖳𝗈 𝖸𝗈𝗎𝗋 𝖢𝗁𝖺𝗇𝗇𝖾𝗅 ⇆', url='https://telegram.me/QuickReactRobot?startgroup=botstart')],
13 | [InlineKeyboardButton('ℹ️ 𝖠𝖻𝗈𝗎𝗍', callback_data='about'),
14 | InlineKeyboardButton('📚 𝖧𝖾𝗅𝗉', callback_data='help')],
15 | [InlineKeyboardButton('⇆ 𝖠𝖽𝖽 𝖬𝖾 𝖳𝗈 𝖸𝗈𝗎𝗋 𝖢𝗁𝖺𝗇𝗇𝖾𝗅 ⇆', url='https://telegram.me/QuickReactRobot?startchannel=botstart')]
16 | ])
17 | )
18 |
19 | elif query.data == "help":
20 | await query.message.edit_caption(
21 | caption=text.HELP.format(query.from_user.mention),
22 | reply_markup=InlineKeyboardMarkup([
23 | [InlineKeyboardButton('📢 𝖴𝗉𝖽𝖺𝗍𝖾𝗌', url='https://telegram.me/Techifybots'),
24 | InlineKeyboardButton('💬 𝖲𝗎𝗉𝗉𝗈𝗋𝗍', url='https://telegram.me/TechifySupport')],
25 | [InlineKeyboardButton('↩️ 𝖡𝖺𝖼𝗄', callback_data="start"),
26 | InlineKeyboardButton('❌ 𝖢𝗅𝗈𝗌𝖾', callback_data="close")]
27 | ])
28 | )
29 |
30 | elif query.data == "about":
31 | await query.message.edit_caption(
32 | caption=text.ABOUT,
33 | reply_markup=InlineKeyboardMarkup([
34 | [InlineKeyboardButton('👨💻 𝖣𝖾𝗏𝖾𝗅𝗈𝗉𝖾𝗋 👨💻', user_id=int(ADMIN))],
35 | [InlineKeyboardButton('↩️ 𝖡𝖺𝖼𝗄', callback_data="start"),
36 | InlineKeyboardButton('❌ 𝖢𝗅𝗈𝗌𝖾', callback_data="close")]
37 | ])
38 | )
39 |
40 | elif query.data == "close":
41 | await query.message.delete()
42 |
43 |
--------------------------------------------------------------------------------
/bot.py:
--------------------------------------------------------------------------------
1 | import os
2 | from datetime import datetime
3 | from pytz import timezone
4 | from pyrogram import Client
5 | from aiohttp import web
6 | from config import API_ID, API_HASH, BOT_TOKEN, ADMIN, LOG_CHANNEL
7 |
8 | routes = web.RouteTableDef()
9 |
10 | @routes.get("/", allow_head=True)
11 | async def root_route(request):
12 | return web.Response(text="I am Alive
", content_type='text/html')
13 |
14 | async def web_server():
15 | app = web.Application(client_max_size=30_000_000)
16 | app.add_routes(routes)
17 | return app
18 |
19 | class Bot(Client):
20 | def __init__(self):
21 | super().__init__(
22 | "techifybots",
23 | api_id=API_ID,
24 | api_hash=API_HASH,
25 | bot_token=BOT_TOKEN,
26 | plugins=dict(root="TechifyBots"),
27 | workers=200,
28 | sleep_threshold=15
29 | )
30 |
31 | async def start(self):
32 | app = web.AppRunner(await web_server())
33 | await app.setup()
34 | try:
35 | await web.TCPSite(app, "0.0.0.0", int(os.getenv("PORT", 8080))).start()
36 | print("Web server started.")
37 | except Exception as e:
38 | print(f"Web server error: {e}")
39 |
40 |
41 | await super().start()
42 | me = await self.get_me()
43 | print(f"Bot Started as {me.first_name}")
44 | if isinstance(ADMIN, int):
45 | try:
46 | await self.send_message(ADMIN, f"**{me.first_name} is started...**")
47 | except Exception as e:
48 | print(f"Error sending message to admin: {e}")
49 | if LOG_CHANNEL:
50 | try:
51 | now = datetime.now(timezone("Asia/Kolkata"))
52 | msg = (
53 | f"**{me.mention} is restarted!**\n\n"
54 | f"📅 Date : `{now.strftime('%d %B, %Y')}`\n"
55 | f"⏰ Time : `{now.strftime('%I:%M:%S %p')}`\n"
56 | f"🌐 Timezone : `Asia/Kolkata`"
57 | )
58 | await self.send_message(LOG_CHANNEL, msg)
59 | except Exception as e:
60 | print(f"Error sending to LOG_CHANNEL: {e}")
61 |
62 | async def stop(self, *args):
63 | await super().stop()
64 | print(f"{me.first_name} Bot stopped.")
65 |
66 | Bot().run()
67 |
--------------------------------------------------------------------------------
/TechifyBots/db.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 | from config import DB_URI, DB_NAME
3 | from motor import motor_asyncio
4 | from pymongo import ReturnDocument
5 | from pymongo.errors import DuplicateKeyError
6 | from bson import ObjectId
7 |
8 | client = motor_asyncio.AsyncIOMotorClient(DB_URI)
9 | db = client[DB_NAME]
10 |
11 | class Techifybots:
12 | def __init__(self):
13 | self.users = db["users"]
14 | self.cache: dict[int, dict[str, Any]] = {}
15 |
16 | async def add_user(self, user_id: int, name: str) -> dict[str, Any] | None:
17 | try:
18 | user = {"user_id": int(user_id), "name": name}
19 | saved = await self.users.find_one_and_update(
20 | {"user_id": user["user_id"]},
21 | {"$set": user},
22 | upsert=True,
23 | return_document=ReturnDocument.AFTER
24 | )
25 | if saved:
26 | self.cache[user["user_id"]] = saved
27 | return saved
28 | except DuplicateKeyError:
29 | existing = await self.users.find_one({"user_id": int(user_id)})
30 | if existing:
31 | self.cache[int(user_id)] = existing
32 | return existing
33 | except Exception as e:
34 | print("Error in add_user:", e)
35 | return None
36 |
37 | async def get_user(self, user_id: int) -> dict[str, Any] | None:
38 | try:
39 | if user_id in self.cache:
40 | return self.cache[user_id]
41 | user = await self.users.find_one({"user_id": int(user_id)})
42 | if user:
43 | self.cache[user_id] = user
44 | return user
45 | except Exception as e:
46 | print("Error in get_user:", e)
47 | return None
48 |
49 | async def get_all_users(self) -> list[dict[str, Any]]:
50 | try:
51 | users: list[dict[str, Any]] = []
52 | async for user in self.users.find():
53 | users.append(user)
54 | return users
55 | except Exception as e:
56 | print("Error in get_all_users:", e)
57 | return []
58 |
59 | async def delete_user(self, identifier: int | str | ObjectId) -> bool:
60 | try:
61 | query = {}
62 | if isinstance(identifier, int):
63 | query = {"user_id": identifier}
64 | self.cache.pop(identifier, None)
65 | elif isinstance(identifier, (str, ObjectId)):
66 | query = {"_id": ObjectId(identifier)} if isinstance(identifier, str) else {"_id": identifier}
67 | doc = await self.users.find_one(query)
68 | if doc and "user_id" in doc:
69 | self.cache.pop(int(doc["user_id"]), None)
70 | else:
71 | return False
72 | result = await self.users.delete_one(query)
73 | return result.deleted_count > 0
74 | except Exception as e:
75 | print("Error in delete_user:", e)
76 | return False
77 |
78 | tb = Techifybots()
--------------------------------------------------------------------------------
/TechifyBots/fsub.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import datetime
3 | from motor.motor_asyncio import AsyncIOMotorClient
4 | from pyrogram import Client, filters
5 | from pyrogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup, ChatJoinRequest
6 | from pyrogram.errors import UserNotParticipant, ChatAdminRequired
7 | from pyrogram.enums import ParseMode
8 | from config import AUTH_CHANNELS, AUTH_REQ_CHANNELS, ADMIN, FSUB_EXPIRE, DB_URI, DB_NAME, IS_FSUB
9 |
10 | class TechifyBots:
11 | def __init__(self):
12 | client = AsyncIOMotorClient(DB_URI)
13 | db = client[DB_NAME]
14 | self.join_requests = db["join_requests"]
15 | if FSUB_EXPIRE > 0:
16 | self.join_requests.create_index("created_at", expireAfterSeconds=FSUB_EXPIRE * 60)
17 |
18 | async def add_join_req(self, user_id: int, channel_id: int):
19 | await self.join_requests.update_one(
20 | {"user_id": user_id},
21 | {"$addToSet": {"channels": channel_id}, "$setOnInsert": {"created_at": datetime.datetime.utcnow()}},
22 | upsert=True
23 | )
24 |
25 | async def has_joined_channel(self, user_id: int, channel_id: int) -> bool:
26 | doc = await self.join_requests.find_one({"user_id": user_id})
27 | return bool(doc and channel_id in doc.get("channels", []))
28 |
29 | async def del_join_req(self):
30 | await self.join_requests.drop()
31 |
32 | tb = TechifyBots()
33 |
34 | def is_auth_req_channel(_, __, update):
35 | return update.chat.id in AUTH_REQ_CHANNELS
36 |
37 | @Client.on_chat_join_request(filters.create(is_auth_req_channel))
38 | async def join_reqs(client: Client, message: ChatJoinRequest):
39 | await tb.add_join_req(message.from_user.id, message.chat.id)
40 |
41 | @Client.on_message(filters.command("delreq") & filters.private & filters.user(ADMIN))
42 | async def del_requests(client: Client, message: Message):
43 | await tb.del_join_req()
44 | await message.reply("**⚙ Successfully join request cache deleted.**")
45 |
46 | async def is_subscribed(bot: Client, user_id: int, expire_at):
47 | missing = []
48 | for channel_id in AUTH_CHANNELS:
49 | try:
50 | await bot.get_chat_member(channel_id, user_id)
51 | except UserNotParticipant:
52 | try:
53 | chat = await bot.get_chat(channel_id)
54 | invite = await bot.create_chat_invite_link(channel_id, expire_date=expire_at)
55 | missing.append((chat.title, invite.invite_link))
56 | except ChatAdminRequired:
57 | logging.error(f"Bot not admin in auth channel {channel_id}")
58 | except Exception:
59 | pass
60 | except Exception:
61 | pass
62 | return missing
63 |
64 | async def is_req_subscribed(bot: Client, user_id: int, expire_at):
65 | missing = []
66 | for channel_id in AUTH_REQ_CHANNELS:
67 | if await tb.has_joined_channel(user_id, channel_id):
68 | continue
69 | try:
70 | chat = await bot.get_chat(channel_id)
71 | invite = await bot.create_chat_invite_link(channel_id, creates_join_request=True, expire_date=expire_at)
72 | missing.append((chat.title, invite.invite_link))
73 | except ChatAdminRequired:
74 | logging.error(f"Bot not admin in request channel {channel_id}")
75 | except Exception:
76 | pass
77 | return missing
78 |
79 | async def get_fsub(bot: Client, message: Message) -> bool:
80 | user_id = message.from_user.id
81 | if user_id == ADMIN:
82 | return True
83 | expire_at = datetime.datetime.utcnow() + datetime.timedelta(minutes=FSUB_EXPIRE) if FSUB_EXPIRE > 0 else None
84 | missing = []
85 | if AUTH_CHANNELS:
86 | missing.extend(await is_subscribed(bot, user_id, expire_at))
87 | if AUTH_REQ_CHANNELS:
88 | missing.extend(await is_req_subscribed(bot, user_id, expire_at))
89 | if not missing:
90 | return True
91 | bot_user = await bot.get_me()
92 | buttons = []
93 | for i in range(0, len(missing), 2):
94 | row = []
95 | for j in range(2):
96 | if i + j < len(missing):
97 | title, link = missing[i + j]
98 | row.append(InlineKeyboardButton(f"{i + j + 1}. {title}", url=link))
99 | buttons.append(row)
100 | buttons.append([InlineKeyboardButton("🔄 Try Again", url=f"https://telegram.me/{bot_user.username}?start=start")])
101 | await message.reply(f"**🎭 {message.from_user.mention}, You haven’t joined my channel yet.\nPlease join using the buttons below.**", reply_markup=InlineKeyboardMarkup(buttons))
102 | return False
103 |
104 | @Client.on_message(filters.private & ~filters.user(ADMIN), group=-10)
105 | async def global_fsub_checker(client: Client, message: Message):
106 | if not IS_FSUB:
107 | return
108 | await get_fsub(client, message)
--------------------------------------------------------------------------------
/TechifyBots/commands.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re
3 | from pyrogram import Client, filters
4 | from pyrogram.errors import FloodWait, UserIsBlocked, PeerIdInvalid, InputUserDeactivated
5 | from pyrogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup
6 | from config import *
7 | import asyncio
8 | from Script import text
9 | from .db import tb
10 | from collections import defaultdict
11 |
12 | def parse_button_markup(text: str):
13 | lines = text.split("\n")
14 | buttons = []
15 | final_text_lines = []
16 | for line in lines:
17 | row = []
18 | parts = line.split("||")
19 | is_button_line = True
20 | for part in parts:
21 | match = re.fullmatch(r"\[(.+?)\]\((https?://[^\s]+)\)", part.strip())
22 | if match:
23 | row.append(InlineKeyboardButton(match[1], url=match[2]))
24 | else:
25 | is_button_line = False
26 | break
27 | if is_button_line and row:
28 | buttons.append(row)
29 | else:
30 | final_text_lines.append(line)
31 | return InlineKeyboardMarkup(buttons) if buttons else None, "\n".join(final_text_lines).strip()
32 |
33 | @Client.on_message(filters.command("start"))
34 | async def start_cmd(client, message):
35 | if await tb.get_user(message.from_user.id) is None:
36 | await tb.add_user(message.from_user.id, message.from_user.first_name)
37 | bot = await client.get_me()
38 | await client.send_message(
39 | LOG_CHANNEL,
40 | text.LOG.format(
41 | message.from_user.id,
42 | getattr(message.from_user, "dc_id", "N/A"),
43 | message.from_user.first_name or "N/A",
44 | f"@{message.from_user.username}" if message.from_user.username else "N/A",
45 | bot.username
46 | )
47 | )
48 | await message.reply_photo(
49 | photo=random.choice(PICS),
50 | caption=text.START.format(message.from_user.mention),
51 | reply_markup=InlineKeyboardMarkup([
52 | [InlineKeyboardButton('⇆ 𝖠𝖽𝖽 𝖬𝖾 𝖳𝗈 𝖸𝗈𝗎𝗋 𝖦𝗋𝗈𝗎𝗉 ⇆', url='https://telegram.me/QuickReactRobot?startgroup=botstart')],
53 | [InlineKeyboardButton('ℹ️ 𝖠𝖻𝗈𝗎𝗍', callback_data='about'),
54 | InlineKeyboardButton('📚 𝖧𝖾𝗅𝗉', callback_data='help')],
55 | [InlineKeyboardButton('⇆ 𝖠𝖽𝖽 𝖬𝖾 𝖳𝗈 𝖸𝗈𝗎𝗋 𝖢𝗁𝖺𝗇𝗇𝖾𝗅 ⇆', url='https://telegram.me/QuickReactRobot?startchannel=botstart')]
56 | ])
57 | )
58 |
59 | @Client.on_message(filters.command("help") & filters.private)
60 | async def help_cmd(client, message):
61 | reply = await message.reply(
62 | text=("❓ 𝘏𝘢𝘷𝘪𝘯𝘨 𝘛𝘳𝘰𝘶𝘣𝘭𝘦?\n\n𝘐𝘧 𝘺𝘰𝘶'𝘳𝘦 𝘧𝘢𝘤𝘪𝘯𝘨 𝘢𝘯𝘺 𝘱𝘳𝘰𝘣𝘭𝘦𝘮 𝘸𝘩𝘪𝘭𝘦 𝘶𝘴𝘪𝘯𝘨 𝘵𝘩𝘦 𝘣𝘰𝘵 𝘰𝘳 𝘪𝘵𝘴 𝘤𝘰𝘮𝘮𝘢𝘯𝘥𝘴, 𝘱𝘭𝘦𝘢𝘴𝘦 𝘸𝘢𝘵𝘤𝘩 𝘵𝘩𝘦 𝘵𝘶𝘵𝘰𝘳𝘪𝘢𝘭 𝘷𝘪𝘥𝘦𝘰 𝘣𝘦𝘭𝘰𝘸.\n\n🎥 𝘛𝘩𝘦 𝘷𝘪𝘥𝘦𝘰 𝘸𝘪𝘭𝘭 𝘤𝘭𝘦𝘢𝘳𝘭𝘺 𝘦𝘹𝘱𝘭𝘢𝘪𝘯 𝘩𝘰𝘸 𝘵𝘰 𝘶𝘴𝘦 𝘦𝘢𝘤𝘩 𝘧𝘦𝘢𝘵𝘶𝘳𝘦 𝘸𝘪𝘵𝘩 𝘦𝘢𝘴𝘦.\n\n💖 𝘍𝘰𝘳 𝘮𝘰𝘳𝘦 𝘶𝘱𝘥𝘢𝘵𝘦𝘴 — 𝘚𝘶𝘱𝘱𝘰𝘳𝘵 𝘜𝘴."
63 | ),
64 | reply_markup=InlineKeyboardMarkup([
65 | [InlineKeyboardButton("🎬 𝘞𝘢𝘵𝘤𝘩 𝘛𝘶𝘵𝘰𝘳𝘪𝘢𝘭", url="https://youtu.be/rOnzfDdYqnc")]
66 | ])
67 | )
68 | await asyncio.sleep(300)
69 | await reply.delete()
70 | try:
71 | await message.delete()
72 | except:
73 | pass
74 |
75 | @Client.on_message(filters.command("stats") & filters.private & filters.user(ADMIN))
76 | async def total_users(client: Client, message: Message):
77 | try:
78 | users = await tb.get_all_users()
79 | await message.reply_text(f"👥 **Total Users:** {len(users)}",reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("🎭 𝖢𝗅𝗈𝗌𝖾", callback_data="close")]]))
80 | except Exception as e:
81 | r=await message.reply(f"❌ *Error:* `{str(e)}`")
82 | await asyncio.sleep(30)
83 | await r.delete()
84 |
85 | @Client.on_message(filters.group | filters.channel)
86 | async def send_reaction(client: Client, msg: Message):
87 | try:
88 | await msg.react(random.choice(EMOJIS))
89 | except FloodWait as e:
90 | print(f"FloodWait: Sleeping for {e.value} seconds")
91 | await asyncio.sleep(e.value)
92 | await msg.react(random.choice(EMOJIS))
93 | except Exception as e:
94 | print(f"Error: {e}")
95 |
96 | @Client.on_message(filters.command("broadcast") & filters.private & filters.user(ADMIN))
97 | async def broadcasting_func(client: Client, message: Message):
98 | if not message.reply_to_message:
99 | return await message.reply("Reply to a message to broadcast.")
100 | msg = await message.reply_text("📢 Starting broadcast...")
101 | to_copy_msg = message.reply_to_message
102 | users_list = await tb.get_all_users()
103 | total_before = len(users_list)
104 | completed_users = set()
105 | failed = 0
106 | raw_text = to_copy_msg.caption or to_copy_msg.text or ""
107 | reply_markup, cleaned_text = parse_button_markup(raw_text)
108 |
109 | for i, user in enumerate(users_list, start=1):
110 | user_id = user.get("user_id")
111 | if not user_id:
112 | if await tb.delete_user(user.get("_id")):
113 | failed += 1
114 | continue
115 | try:
116 | user_id = int(user_id) # normalize to int
117 | if to_copy_msg.text:
118 | await client.send_message(user_id, cleaned_text, reply_markup=reply_markup)
119 | elif to_copy_msg.photo:
120 | await client.send_photo(user_id, to_copy_msg.photo.file_id, caption=cleaned_text, reply_markup=reply_markup)
121 | elif to_copy_msg.video:
122 | await client.send_video(user_id, to_copy_msg.video.file_id, caption=cleaned_text, reply_markup=reply_markup)
123 | elif to_copy_msg.document:
124 | await client.send_document(user_id, to_copy_msg.document.file_id, caption=cleaned_text, reply_markup=reply_markup)
125 | else:
126 | await to_copy_msg.copy(user_id)
127 |
128 | completed_users.add(user_id)
129 |
130 | except (UserIsBlocked, PeerIdInvalid, InputUserDeactivated):
131 | if await tb.delete_user(user_id):
132 | failed += 1
133 | except FloodWait as e:
134 | await asyncio.sleep(e.value)
135 | try:
136 | await to_copy_msg.copy(user_id)
137 | completed_users.add(user_id)
138 | except Exception:
139 | if await tb.delete_user(user_id):
140 | failed += 1
141 | except Exception:
142 | if await tb.delete_user(user_id):
143 | failed += 1
144 | if i % 20 == 0 or i == total_before:
145 | try:
146 | await msg.edit(
147 | f"😶🌫 Broadcasting...\n\n"
148 | f"👥 Total Users: {total_before}\n"
149 | f"✅ Successful: {len(completed_users)}\n"
150 | f"❌ Failed/Removed: {failed}\n"
151 | f"⚙️ Progress: {i}/{total_before}"
152 | )
153 | except Exception:
154 | pass
155 |
156 | await asyncio.sleep(0.05)
157 |
158 | all_users = await tb.get_all_users()
159 | users_by_id = defaultdict(list)
160 | for user in all_users:
161 | uid = user.get("user_id")
162 | if not uid:
163 | if await tb.delete_user(user.get("_id")):
164 | failed += 1
165 | continue
166 | users_by_id[uid].append(user)
167 |
168 | for uid, docs in users_by_id.items():
169 | if uid in completed_users:
170 | for duplicate in docs[1:]:
171 | if await tb.delete_user(duplicate.get("user_id")):
172 | failed += 1
173 | else:
174 | for doc in docs:
175 | if await tb.delete_user(doc.get("user_id")):
176 | failed += 1
177 |
178 | active_users = len(completed_users)
179 |
180 | await msg.edit(
181 | f"🎯 Broadcast Completed\n\n"
182 | f"👥 Total Users (Before): {total_before}\n"
183 | f"✅ Successful: {len(completed_users)}\n"
184 | f"❌ Failed/Removed: {failed}\n"
185 | f"📊 Active Users (Now): {active_users}",
186 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("🎭 Close", callback_data="close")]]),
187 | )
--------------------------------------------------------------------------------