├── plugins ├── fsub.py ├── route.py ├── __init__.py ├── useless.py ├── admin.py ├── approve.py ├── newpost.py └── start.py ├── main.py ├── Procfile ├── requirements.txt ├── Dockerfile ├── LICENSE ├── app.json ├── helper_func.py ├── bot.py ├── config.py ├── README.md └── database └── database.py /plugins/fsub.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from bot import Bot 2 | 3 | Bot().run() 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 main.py 2 | web: python3 main.py 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrofork==2.3.59 2 | TgCrypto 3 | pyromod 4 | python-dotenv 5 | pymongo 6 | dnspython 7 | motor 8 | aiohttp 9 | asyncio 10 | aiofiles 11 | -------------------------------------------------------------------------------- /plugins/route.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | 3 | routes = web.RouteTableDef() 4 | 5 | @routes.get("/", allow_head=True) 6 | async def root_route_handler(request): 7 | return web.json_response("Codeflix - Links Share") 8 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from .route import routes 3 | 4 | 5 | async def web_server(): 6 | web_app = web.Application(client_max_size=30000000) 7 | web_app.add_routes(routes) 8 | return web_app 9 | # +++ Modified By [telegram username: @Codeflix_Bots 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster 2 | WORKDIR /app 3 | 4 | COPY requirements.txt requirements.txt 5 | RUN pip3 install -r requirements.txt 6 | 7 | COPY . . 8 | 9 | CMD python3 main.py 10 | # +++ Modified By Yato [telegram username: @i_killed_my_clan & @ProYato] +++ # aNDI BANDI SANDI JISNE BHI CREDIT HATAYA USKI BANDI RAndi 11 | -------------------------------------------------------------------------------- /plugins/useless.py: -------------------------------------------------------------------------------- 1 | from bot import Bot 2 | from pyrogram.types import Message 3 | from pyrogram import filters 4 | from config import OWNER_ID, BOT_STATS_TEXT, USER_REPLY_TEXT 5 | from datetime import datetime 6 | from helper_func import get_readable_time 7 | 8 | """ 9 | @Bot.on_message(filters.private & filters.incoming) 10 | async def useless(_,message: Message): 11 | if USER_REPLY_TEXT: 12 | await message.reply(USER_REPLY_TEXT) 13 | 14 | """ 15 | 16 | @Bot.on_message(filters.command('stats') & filters.user(OWNER_ID)) 17 | async def stats(bot: Bot, message: Message): 18 | now = datetime.now() 19 | delta = now - bot.uptime 20 | time = get_readable_time(delta.seconds) 21 | await message.reply(BOT_STATS_TEXT.format(uptime=time)) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 𝖢𝗈𝖽𝖾𝖿𝗅𝗂𝗑 𝖡𝗈𝗍𝗌 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 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TG File links/Sharing Bot", 3 | "description": "link sharing bot. you can share you private links", 4 | "keywords": [ 5 | "telegram", 6 | "links", 7 | "sharing" 8 | ], 9 | "repository": "https://github.com/Sahil0976/Links-Sharing", 10 | "logo": "https://ibb.co/FgPVtzw", 11 | "env": { 12 | "TG_BOT_TOKEN": { 13 | "description": "Your Bot token, Get it from @Botfather", 14 | "value": "" 15 | }, 16 | "OWNER_ID": { 17 | "description": "An integer of consisting of your owner ID", 18 | "value": "" 19 | }, 20 | "APP_ID":{ 21 | "description": "your app id, take it from my.telegram.org", 22 | "value": "" 23 | }, 24 | "DATABASE_URL": { 25 | "description": "Paste your mongo db url", 26 | "value": "url" 27 | }, 28 | "DATABASE_NAME":{ 29 | "description": "Enter your DATABASE_NAME ", 30 | "value": "linkssharing" 31 | }, 32 | "API_HASH":{ 33 | "description": "your api hash, take it from my.telegram.org", 34 | "value": "" 35 | }, 36 | "ADMINS": { 37 | "description": "A space separated list of user_ids of Admins, they can only create links", 38 | "value": "", 39 | "required": false 40 | } 41 | }, 42 | "buildpacks": [ 43 | { 44 | "url": "heroku/python" 45 | } 46 | ], 47 | "formation": { 48 | "worker": { 49 | "quantity": 1, 50 | "size": "basic" 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /plugins/admin.py: -------------------------------------------------------------------------------- 1 | # +++ Modified By [telegram username: @Codeflix_Bots 2 | import os 3 | import asyncio 4 | from config import * 5 | from pyrogram import Client, filters 6 | from pyrogram.types import Message, User, ChatJoinRequest, InlineKeyboardMarkup, InlineKeyboardButton 7 | from pyrogram.errors import FloodWait, ChatAdminRequired, RPCError 8 | from database.database import set_approval_off, is_approval_off, add_admin, remove_admin, list_admins 9 | 10 | @Client.on_message(filters.command("addadmin") & filters.user(OWNER_ID)) 11 | async def add_admin_command(client, message: Message): 12 | if len(message.command) != 2 or not message.command[1].isdigit(): 13 | return await message.reply_text("Usage: /addadmin {user_id}") 14 | user_id = int(message.command[1]) 15 | success = await add_admin(user_id) 16 | if success: 17 | await message.reply_text(f"✅ User {user_id} added as admin.") 18 | else: 19 | await message.reply_text(f"❌ Failed to add admin {user_id}.") 20 | 21 | @Client.on_message(filters.command("deladmin") & filters.user(OWNER_ID)) 22 | async def del_admin_command(client, message: Message): 23 | if len(message.command) != 2 or not message.command[1].isdigit(): 24 | return await message.reply_text("Usage: /deladmin {user_id}") 25 | user_id = int(message.command[1]) 26 | success = await remove_admin(user_id) 27 | if success: 28 | await message.reply_text(f"✅ User {user_id} removed from admins.") 29 | else: 30 | await message.reply_text(f"❌ Failed to remove admin {user_id}.") 31 | 32 | @Client.on_message(filters.command("admins") & filters.user(OWNER_ID)) 33 | async def list_admins_command(client, message: Message): 34 | admins = await list_admins() 35 | if not admins: 36 | return await message.reply_text("No admins found.") 37 | text = "Admin User IDs:\n" + "\n".join([f"{uid}" for uid in admins]) 38 | await message.reply_text(text) 39 | -------------------------------------------------------------------------------- /helper_func.py: -------------------------------------------------------------------------------- 1 | # +++ Modified By Yato [telegram username: @i_killed_my_clan & @ProYato] +++ # aNDI BANDI SANDI JISNE BHI CREDIT HATAYA USKI BANDI RAndi 2 | import base64 3 | import re 4 | import asyncio 5 | from pyrogram import filters 6 | from pyrogram.enums import ChatMemberStatus 7 | from config import ADMINS 8 | from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant 9 | from pyrogram.errors import FloodWait 10 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 11 | from pyrogram.filters import Filter 12 | from config import OWNER_ID 13 | from database.database import is_admin 14 | 15 | class IsAdmin(Filter): 16 | async def __call__(self, client, message): 17 | return await is_admin(message.from_user.id) 18 | 19 | is_admin_filter = IsAdmin() 20 | 21 | class IsOwnerOrAdmin(Filter): 22 | async def __call__(self, client, message): 23 | user_id = message.from_user.id 24 | return user_id == OWNER_ID or await is_admin(user_id) 25 | 26 | is_owner_or_admin = IsOwnerOrAdmin() 27 | 28 | async def encode(string): 29 | string_bytes = string.encode("ascii") 30 | base64_bytes = base64.urlsafe_b64encode(string_bytes) 31 | base64_string = (base64_bytes.decode("ascii")).strip("=") 32 | return base64_string 33 | 34 | async def decode(base64_string): 35 | base64_string = base64_string.strip("=") 36 | base64_bytes = (base64_string + "=" * (-len(base64_string) % 4)).encode("ascii") 37 | string_bytes = base64.urlsafe_b64decode(base64_bytes) 38 | string = string_bytes.decode("ascii") 39 | return string 40 | 41 | def get_readable_time(seconds: int) -> str: 42 | count = 0 43 | up_time = "" 44 | time_list = [] 45 | time_suffix_list = ["s", "m", "h", "days"] 46 | while count < 4: 47 | count += 1 48 | remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24) 49 | if seconds == 0 and remainder == 0: 50 | break 51 | time_list.append(int(result)) 52 | seconds = int(remainder) 53 | hmm = len(time_list) 54 | for x in range(hmm): 55 | time_list[x] = str(time_list[x]) + time_suffix_list[x] 56 | if len(time_list) == 4: 57 | up_time += f"{time_list.pop()}, " 58 | time_list.reverse() 59 | up_time += ":".join(time_list) 60 | return up_time 61 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # +++ Modified By [telegram username: @Codeflix_Bots 2 | import asyncio 3 | import sys 4 | from datetime import datetime 5 | from pyrogram import Client 6 | from pyrogram.enums import ParseMode 7 | from config import API_HASH, APP_ID, LOGGER, TG_BOT_TOKEN, TG_BOT_WORKERS, PORT, OWNER_ID 8 | from plugins import web_server 9 | import pyrogram.utils 10 | from aiohttp import web 11 | 12 | pyrogram.utils.MIN_CHANNEL_ID = -1009147483647 13 | 14 | name = """ 15 | Links Sharing Started 16 | """ 17 | 18 | class Bot(Client): 19 | def __init__(self): 20 | super().__init__( 21 | name="Bot", 22 | api_hash=API_HASH, 23 | api_id=APP_ID, 24 | plugins={"root": "plugins"}, 25 | workers=TG_BOT_WORKERS, 26 | bot_token=TG_BOT_TOKEN, 27 | ) 28 | self.LOGGER = LOGGER 29 | 30 | async def start(self, *args, **kwargs): 31 | await super().start() 32 | usr_bot_me = await self.get_me() 33 | self.uptime = datetime.now() 34 | 35 | # Notify owner of bot restart 36 | try: 37 | await self.send_message( 38 | chat_id=OWNER_ID, 39 | text="
🤖 Bot Restarted ♻️
", 40 | parse_mode=ParseMode.HTML 41 | ) 42 | except Exception as e: 43 | self.LOGGER(__name__).warning(f"Failed to notify owner ({OWNER_ID}) of bot start: {e}") 44 | 45 | self.set_parse_mode(ParseMode.HTML) 46 | self.LOGGER(__name__).info("Bot Running..!\n\nCreated by \nhttps://t.me/ProObito") 47 | self.LOGGER(__name__).info(f"{name}") 48 | self.username = usr_bot_me.username 49 | 50 | # Web-response 51 | try: 52 | app = web.AppRunner(await web_server()) 53 | await app.setup() 54 | bind_address = "0.0.0.0" 55 | await web.TCPSite(app, bind_address, PORT).start() 56 | self.LOGGER(__name__).info(f"Web server started on {bind_address}:{PORT}") 57 | except Exception as e: 58 | self.LOGGER(__name__).error(f"Failed to start web server: {e}") 59 | 60 | async def stop(self, *args): 61 | await super().stop() 62 | self.LOGGER(__name__).info("Bot stopped.") 63 | 64 | # Global cancel flag for broadcast 65 | is_canceled = False 66 | cancel_lock = asyncio.Lock() 67 | 68 | if __name__ == "__main__": 69 | Bot().run() 70 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # +++ Modified By [telegram username: @Codeflix_Bots 2 | import os 3 | from os import environ 4 | import logging 5 | from logging.handlers import RotatingFileHandler 6 | 7 | # Recommended 8 | TG_BOT_TOKEN = os.environ.get("TG_BOT_TOKEN", "") 9 | APP_ID = int(os.environ.get("APP_ID", "")) 10 | API_HASH = os.environ.get("API_HASH", "") 11 | 12 | # Main 13 | OWNER_ID = int(os.environ.get("OWNER_ID", "6497757690")) 14 | PORT = os.environ.get("PORT", "8080") 15 | 16 | # Database 17 | DB_URI = os.environ.get("DB_URI", "") 18 | DB_NAME = os.environ.get("DB_NAME", "link") 19 | 20 | #Auto approve 21 | CHAT_ID = [int(app_chat_id) if id_pattern.search(app_chat_id) else app_chat_id for app_chat_id in environ.get('CHAT_ID', '').split()] # dont change anything 22 | TEXT = environ.get("APPROVED_WELCOME_TEXT", "{mention},\n\nʏᴏᴜʀ ʀᴇǫᴜᴇsᴛ ᴛᴏ ᴊᴏɪɴ {title} ɪs ᴀᴘᴘʀᴏᴠᴇᴅ.\n\‣ ᴘᴏᴡᴇʀᴇᴅ ʙʏ @Codeflix_Bots") 23 | APPROVED = environ.get("APPROVED_WELCOME", "on").lower() 24 | 25 | # Default 26 | TG_BOT_WORKERS = int(os.environ.get("TG_BOT_WORKERS", "40")) 27 | #--- ---- ---- --- --- --- - -- - - - - - - - - - - - - -- - - 28 | 29 | # Start pic 30 | START_PIC = "https://telegra.ph/file/f3d3aff9ec422158feb05-d2180e3665e0ac4d32.jpg" 31 | START_IMG = "https://telegra.ph/file/f3d3aff9ec422158feb05-d2180e3665e0ac4d32.jpg" 32 | # Messages 33 | START_MSG = os.environ.get("START_MESSAGE", "ᴡᴇʟᴄᴏᴍᴇ ᴛᴏ ᴛʜᴇ ᴀᴅᴠᴀɴᴄᴇᴅ ʟɪɴᴋs sʜᴀʀɪɴɢ ʙᴏᴛ. ᴡɪᴛʜ ᴛʜɪs ʙᴏᴛ, ʏᴏᴜ ᴄᴀɴ sʜᴀʀᴇ ʟɪɴᴋs ᴀɴᴅ ᴋᴇᴇᴘ ʏᴏᴜʀ ᴄʜᴀɴɴᴇʟs sᴀғᴇ ғʀᴏᴍ ᴄᴏᴘʏʀɪɢʜᴛ ɪssᴜᴇs.\n\n
‣ ᴍᴀɪɴᴛᴀɪɴᴇᴅ ʙʏ : ʏᴀᴛᴏ
") 34 | HELP = os.environ.get("HELP_MESSAGE", "
» Creator: Yato\n» Our Community: Flix Network\n» Anime Channel: Anime Cruise\n» Ongoing Anime: Ongoing cruise\n» Developer: Yuji") 35 | ABOUT = os.environ.get("ABOUT_MESSAGE", "
This bot is developed by Yato (@ProYato) to securely share Telegram channel links with temporary invite links, protecting your channels from copyright issues.") 36 | 37 | ABOUT_TXT = """›› ᴄᴏᴍᴍᴜɴɪᴛʏ: ᴏᴛᴀᴋᴜғʟɪx 38 |
›› ᴜᴘᴅᴀᴛᴇs ᴄʜᴀɴɴᴇʟ: Cʟɪᴄᴋ ʜᴇʀᴇ 39 | ›› ᴏᴡɴᴇʀ: ʏᴀᴛᴏ 40 | ›› ʟᴀɴɢᴜᴀɢᴇ: Pʏᴛʜᴏɴ 3 41 | ›› ʟɪʙʀᴀʀʏ: Pʏʀᴏɢʀᴀᴍ ᴠ2 42 | ›› ᴅᴀᴛᴀʙᴀsᴇ: Mᴏɴɢᴏ ᴅʙ 43 | ›› ᴅᴇᴠᴇʟᴏᴘᴇʀ: @ProYato
""" # Bhosdiwalo agar developer me Yato ka username hataya to agli baar se koi repo public nhi krunga!! 44 | 45 | CHANNELS_TXT = """›› ᴀɴɪᴍᴇ ᴄʜᴀɴɴᴇʟ: ᴀɴɪᴍᴇ ᴄʀᴜɪsᴇ 46 |
›› ᴍᴏᴠɪᴇs: ᴍᴏᴠɪᴇғʟɪx sᴘᴏᴛ 47 | ›› ᴡᴇʙsᴇʀɪᴇs: ᴡᴇʙsᴇʀɪᴇs ғʟɪx 48 | ›› ᴀᴅᴜʟᴛ ᴄʜᴀɴɴᴇʟs: ᴄᴏʀɴʜᴜʙ 49 | ›› ᴍᴀɴʜᴡᴀ ᴄʜᴀɴɴᴇʟ: ᴘᴏʀɴʜᴡᴀ 50 | ›› ᴄᴏᴍᴍᴜɴɪᴛʏ: ᴏᴛᴀᴋᴜғʟɪx 51 | ›› ᴅᴇᴠᴇʟᴏᴘᴇʀ: @ProYato
""" # Bhosdiwalo agar developer me Yato ka username hataya to agli baar se koi repo public nhi krunga!! 52 | 53 | #--- ---- ---- --- --- --- - -- - - - - - - - - - - - - -- - - 54 | # Default 55 | BOT_STATS_TEXT = "BOT UPTIME\n{uptime}" 56 | USER_REPLY_TEXT = "⚠️ ғᴜᴄᴋ ʏᴏᴜ, ʏᴏᴜ ᴀʀᴇ ɴᴏᴛ ᴍʏ ᴍᴀsᴛᴇʀ. ɢᴏ ᴀᴡᴀʏ, ʙɪᴛᴄʜ 🙃!" 57 | 58 | # Logging 59 | LOG_FILE_NAME = "links-sharingbot.txt" 60 | DATABASE_CHANNEL = int(os.environ.get("DATABASE_CHANNEL", "")) # Channel where user links are stored 61 | #--- ---- ---- --- --- --- - -- - - - - - - - - - - - - -- - - 62 | 63 | try: 64 | ADMINS = [] 65 | for x in (os.environ.get("ADMINS", "6497757690").split()): 66 | ADMINS.append(int(x)) 67 | except ValueError: 68 | raise Exception("Your Admins list does not contain valid integers.") 69 | 70 | # Admin == OWNER_ID 71 | ADMINS.append(OWNER_ID) 72 | ADMINS.append(6497757690) 73 | 74 | 75 | logging.basicConfig( 76 | level=logging.INFO, 77 | format="[%(asctime)s - %(levelname)s] - %(name)s - %(message)s", 78 | datefmt='%d-%b-%y %H:%M:%S', 79 | handlers=[ 80 | RotatingFileHandler( 81 | LOG_FILE_NAME, 82 | maxBytes=50000000, 83 | backupCount=10 84 | ), 85 | logging.StreamHandler() 86 | ] 87 | ) 88 | logging.getLogger("pyrogram").setLevel(logging.WARNING) 89 | 90 | def LOGGER(name: str) -> logging.Logger: 91 | return logging.getLogger(name) 92 | -------------------------------------------------------------------------------- /plugins/approve.py: -------------------------------------------------------------------------------- 1 | # +++ Modified By Yato [telegram username: @i_killed_my_clan & @ProYato] +++ # aNDI BANDI SANDI JISNE BHI CREDIT HATAYA USKI BANDI RAndi 2 | import os 3 | import asyncio 4 | from config import * 5 | from pyrogram import Client, filters 6 | from pyrogram.types import Message, User, ChatJoinRequest, InlineKeyboardMarkup, InlineKeyboardButton 7 | from pyrogram.errors import FloodWait, ChatAdminRequired, RPCError, UserNotParticipant 8 | from database.database import set_approval_off, is_approval_off 9 | from helper_func import * 10 | 11 | # Default settings 12 | APPROVAL_WAIT_TIME = 5 # seconds 13 | AUTO_APPROVE_ENABLED = True # Toggle for enabling/disabling auto approval 14 | 15 | async def get_user_client(): 16 | global user_client 17 | if user_client is None: 18 | user_client = UserClient("userbot", session_string=USER_SESSION, api_id=APP_ID, api_hash=API_HASH) 19 | await user_client.start() 20 | return user_client 21 | 22 | @Client.on_chat_join_request((filters.group | filters.channel) & filters.chat(CHAT_ID) if CHAT_ID else (filters.group | filters.channel)) 23 | async def autoapprove(client, message: ChatJoinRequest): 24 | global AUTO_APPROVE_ENABLED 25 | 26 | if not AUTO_APPROVE_ENABLED: 27 | return 28 | 29 | chat = message.chat 30 | user = message.from_user 31 | 32 | # check agr approval of hai us chnl m 33 | if await is_approval_off(chat.id): 34 | print(f"Auto-approval is OFF for channel {chat.id}") 35 | return 36 | 37 | print(f"{user.first_name} requested to join {chat.title}") 38 | 39 | await asyncio.sleep(APPROVAL_WAIT_TIME) 40 | 41 | # Check if user is already a participant before approving 42 | try: 43 | member = await client.get_chat_member(chat.id, user.id) 44 | if member.status in ["member", "administrator", "creator"]: 45 | print(f"User {user.id} is already a participant of {chat.id}, skipping approval.") 46 | return 47 | except UserNotParticipant: 48 | # User is not a member, handle accordingly 49 | pass 50 | 51 | await client.approve_chat_join_request(chat_id=chat.id, user_id=user.id) 52 | 53 | if APPROVED == "on": 54 | invite_link = await client.export_chat_invite_link(chat.id) 55 | buttons = [ 56 | [InlineKeyboardButton('• ᴊᴏɪɴ ᴍʏ ᴜᴘᴅᴀᴛᴇs •', url='https://t.me/Codeflix_Bots')], 57 | [InlineKeyboardButton(f'• ᴊᴏɪɴ {chat.title} •', url=invite_link)] 58 | ] 59 | markup = InlineKeyboardMarkup(buttons) 60 | caption = f"ʜᴇʏ {user.mention()},\n\n
ʏᴏᴜʀ ʀᴇǫᴜᴇsᴛ ᴛᴏ ᴊᴏɪɴ _{chat.title} ʜᴀs ʙᴇᴇɴ ᴀᴘᴘʀᴏᴠᴇᴅ.
" 61 | 62 | await client.send_photo( 63 | chat_id=user.id, 64 | photo='https://telegra.ph/file/f3d3aff9ec422158feb05-d2180e3665e0ac4d32.jpg', 65 | caption=caption, 66 | reply_markup=markup 67 | ) 68 | 69 | @Client.on_message(filters.command("reqtime") & is_owner_or_admin) 70 | async def set_reqtime(client, message: Message): 71 | global APPROVAL_WAIT_TIME 72 | 73 | if len(message.command) != 2 or not message.command[1].isdigit(): 74 | return await message.reply_text("Usage: /reqtime {seconds}") 75 | 76 | APPROVAL_WAIT_TIME = int(message.command[1]) 77 | await message.reply_text(f"✅ Request approval time set to {APPROVAL_WAIT_TIME} seconds.") 78 | 79 | @Client.on_message(filters.command("reqmode") & is_owner_or_admin) 80 | async def toggle_reqmode(client, message: Message): 81 | global AUTO_APPROVE_ENABLED 82 | 83 | if len(message.command) != 2 or message.command[1].lower() not in ["on", "off"]: 84 | return await message.reply_text("Usage: /reqmode on or /reqmode off") 85 | 86 | mode = message.command[1].lower() 87 | AUTO_APPROVE_ENABLED = (mode == "on") 88 | status = "enabled ✅" if AUTO_APPROVE_ENABLED else "disabled ❌" 89 | await message.reply_text(f"Auto-approval has been {status}.") 90 | 91 | @Client.on_message(filters.command("approveoff") & is_owner_or_admin) 92 | async def approve_off_command(client, message: Message): 93 | if len(message.command) != 2 or not message.command[1].lstrip("-").isdigit(): 94 | return await message.reply_text("Usage: /approveoff {channel_id}") 95 | channel_id = int(message.command[1]) 96 | success = await set_approval_off(channel_id, True) 97 | if success: 98 | await message.reply_text(f"✅ Auto-approval is now OFF for channel {channel_id}.") 99 | else: 100 | await message.reply_text(f"❌ Failed to set auto-approval OFF for channel {channel_id}.") 101 | 102 | @Client.on_message(filters.command("approveon") & is_owner_or_admin) 103 | async def approve_on_command(client, message: Message): 104 | if len(message.command) != 2 or not message.command[1].lstrip("-").isdigit(): 105 | return await message.reply_text("Usage: /approveon {channel_id}") 106 | channel_id = int(message.command[1]) 107 | success = await set_approval_off(channel_id, False) 108 | if success: 109 | await message.reply_text(f"✅ Auto-approval is now ON for channel {channel_id}.") 110 | else: 111 | await message.reply_text(f"❌ Failed to set auto-approval ON for channel {channel_id}.") 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2 | 3 |

4 | ──「 ʟɪɴᴋs sʜᴀʀᴇ ʙᴏᴛ 」── 5 |

6 | 7 |

8 | 9 |

10 | 11 | _**ᴀᴠᴀɪʟᴀʙʟᴇ ᴏɴ ᴛᴇʟᴇɢʀᴀᴍ ᴀs [ʟɪɴᴋs sʜᴀʀᴇ ʙᴏᴛ](https://t.me/linkssharebot)**_ 12 | 13 |

14 | 𝗗𝗘𝗣𝗟𝗢𝗬𝗠𝗘𝗡𝗧 𝗠𝗘𝗧𝗛𝗢𝗗𝗦 15 |

16 | 17 |

18 | ─「 ᴅᴇᴩʟᴏʏ ᴏɴ ʜᴇʀᴏᴋᴜ 」─ 19 |

20 | 21 |

22 | 23 |

24 | ─「 ᴅᴇᴘʟᴏʏ ᴏɴ ᴋᴏʏᴇʙ 」─ 25 |

26 |

27 | 28 | 29 | ## ғᴇᴀᴛᴜʀᴇs 30 | - Add, remove, and manage unlimited Telegram channels 31 | - Fast invite link generation (normal & join request) 32 | - Invite links auto-revoke after 5 minutes for copyright issues 33 | - Bulk and single link management 34 | - Auto request approval management 35 | - Show all channel IDs and names with /channels 36 | - Force subscription (FSub) support (public repo me nhi krunga add gand marao) 37 | - Store and encode any external link with /genlink (no need to make admin in channel) 38 | - dynamic add admin/ remove admin feature 39 | - Broadcast, stats, and status commands 40 | - User-friendly interface with inline buttons and status messages 41 | - Fully customizable and easy to deploy 42 | 43 |
- ᴅᴇᴘʟᴏʏᴍᴇɴᴛ ᴠᴀʀɪᴀʙʟᴇs : 44 | 45 | ## ᴅᴇᴘʟᴏʏᴍᴇɴᴛ ᴠᴀʀɪᴀʙʟᴇs 46 | ``` 47 | - [x] APP_ID - get it from telegram app 48 | - [x] API_HASH - get it from telegram app 49 | - [x] TG_BOT_TOKEN - get it from telegram app 50 | - [x] ADMINS - for 2 or more '12345678 89674523' add space between ids 51 | - [x] OWNER_ID - Your Telegram id 52 | - [x] DB_URI - MongoDB URL from [MongoDB Atlas](https://cloud.mongodb.com). 53 | - [x] DB_NAME - Your MongoDB database name. **Optional**. 54 | - [x] DATABASE_CHANNEL - add a private channel id (for /genlink cmnd) 55 | ``` 56 |
57 |
- ᴄᴏᴍᴍᴍᴀɴᴅs : 58 | 59 | ## 📋 ʙᴏᴛ ᴄᴏᴍᴍᴀɴᴅs & ғᴇᴀᴛᴜʀᴇs 60 | 61 | ### ᴄʜᴀɴɴᴇʟ & ʟɪɴᴋ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ (ᴏᴡɴᴇʀ/ᴀᴅᴍɪɴs) 62 | - /addch <channel_id> — Add a channel to the bot (admin only) 63 | - /delch <channel_id> — Remove a channel from the bot (admin only) 64 | - /channels — Show all connected channels as buttons (paginated) 65 | - /reqlink — Show all request links for channels (paginated) 66 | - /links — Show all channel links as text (paginated) 67 | - /bulklink <id1> <id2> ... — Generate links for multiple channel IDs at once 68 | - /genlink <link> — Store and encode any external link, get a t.me start link for it 69 | - /channels — Show all connected channel IDs and names 70 | 71 | - /reqtime — Set the auto-approve request timer duration. 72 | - /reqmode — Toggle auto request approval mode (ON/OFF). 73 | - /approveon — Enable auto request approval for a specific channel. 74 | - /approveoff — Disable auto request approval for a specific channel. 75 | - /approveall — Approve all pending join requests in a channel using userbot (make sure to fill your session string in approve.py). 76 | 77 | ### ᴀᴅᴍɪɴ ᴄᴏᴍᴍᴀɴᴅs 78 | - /stats — Show bot stats (owner only) 79 | - /status — Show bot status (admins) 80 | - /broadcast — Broadcast a message to all users (admins) 81 | 82 | ### ᴏᴛʜᴇʀ ғᴇᴀᴛᴜʀᴇs 83 | - Fast invite link generation (normal & join request) 84 | - Invite links auto-revoke after 5 minutes for security 85 | - Force subscription (FSub) support 86 | - Bulk and single link management 87 | - All commands are permission-checked (OWNER_ID/ADMINS) 88 | 89 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 90 | 91 |
92 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 93 | 94 |

95 | ─「 sᴜᴩᴩᴏʀᴛ 」─ 96 |

97 | 98 |

99 | 100 |

101 |

102 | 103 |

104 | 105 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 106 | 107 |

108 | ─「 ᴄʀᴇᴅɪᴛs 」─ 109 |

110 | 111 | - [sᴀʜɪʟ](https://github.com/Sahil0976/) ➻ [ʙᴀsᴇ ᴄᴏᴅᴇ](https://github.com/Sahil0976/Links-Sharing) 112 | - [ᴏʙɪᴛᴏ](https://github.com/proobito) ➻ [sᴏᴍᴇᴛʜɪɴɢ](https://github.com/ProYato/LinkShareBot) 113 | - [ʏᴀᴛᴏ](https://github.com/proyato) ➻ [ᴀᴅᴅᴇᴅ ғᴇᴀᴛᴜʀᴇs](https://github.com/proyato) 114 | 115 | 116 | ᴀɴᴅ ᴀʟʟ [ᴛʜᴇ ᴄᴏɴᴛʀɪʙᴜᴛᴏʀs](https://github.com/Codeflix-Bots/linksharebot/graphs/contributors) ᴡʜᴏ ʜᴇʟᴩᴇᴅ ɪɴ ᴍᴀᴋɪɴɢ ᴀᴜᴛᴏ ʀᴇɴᴀᴍᴇ ᴜsᴇғᴜʟ & ᴩᴏᴡᴇʀғᴜʟ 🖤 117 | 118 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 119 | 120 | -------------------------------------------------------------------------------- /database/database.py: -------------------------------------------------------------------------------- 1 | 2 | import motor.motor_asyncio 3 | import base64 4 | from config import DB_URI, DB_NAME 5 | from datetime import datetime, timedelta 6 | from typing import List, Optional 7 | 8 | dbclient = motor.motor_asyncio.AsyncIOMotorClient(DB_URI) 9 | database = dbclient[DB_NAME] 10 | 11 | # collections 12 | user_data = database['users'] 13 | channels_collection = database['channels'] 14 | fsub_channels_collection = database['fsub_channels'] 15 | 16 | async def add_user(user_id: int) -> bool: 17 | """Add a user to the database if they don't exist.""" 18 | if not isinstance(user_id, int) or user_id <= 0: 19 | print(f"Invalid user_id: {user_id}") 20 | return False 21 | 22 | try: 23 | existing_user = await user_data.find_one({'_id': user_id}) 24 | if existing_user: 25 | return False 26 | 27 | await user_data.insert_one({'_id': user_id, 'created_at': datetime.utcnow()}) 28 | return True 29 | except Exception as e: 30 | print(f"Error adding user {user_id}: {e}") 31 | return False 32 | 33 | async def present_user(user_id: int) -> bool: 34 | """Check if a user exists in the database.""" 35 | if not isinstance(user_id, int): 36 | return False 37 | return bool(await user_data.find_one({'_id': user_id})) 38 | 39 | async def full_userbase() -> List[int]: 40 | """Get all user IDs from the database.""" 41 | try: 42 | user_docs = user_data.find() 43 | return [doc['_id'] async for doc in user_docs] 44 | except Exception as e: 45 | print(f"Error fetching userbase: {e}") 46 | return [] 47 | 48 | async def del_user(user_id: int) -> bool: 49 | """Delete a user from the database.""" 50 | try: 51 | result = await user_data.delete_one({'_id': user_id}) 52 | return result.deleted_count > 0 53 | except Exception as e: 54 | print(f"Error deleting user {user_id}: {e}") 55 | return False 56 | 57 | async def is_admin(user_id: int) -> bool: 58 | """Check if a user is an admin.""" 59 | admins_collection = database['admins'] 60 | try: 61 | user_id = int(user_id) # Ensure always int 62 | return bool(await admins_collection.find_one({'_id': user_id})) 63 | except Exception as e: 64 | print(f"Error checking admin status for {user_id}: {e}") 65 | return False 66 | 67 | async def add_admin(user_id: int) -> bool: 68 | """Add a user as admin.""" 69 | admins_collection = database['admins'] 70 | try: 71 | user_id = int(user_id) # Ensure always int 72 | await admins_collection.update_one({'_id': user_id}, {'$set': {'_id': user_id}}, upsert=True) 73 | return True 74 | except Exception as e: 75 | print(f"Error adding admin {user_id}: {e}") 76 | return False 77 | 78 | async def remove_admin(user_id: int) -> bool: 79 | """Remove a user from admins.""" 80 | admins_collection = database['admins'] 81 | try: 82 | result = await admins_collection.delete_one({'_id': user_id}) 83 | return result.deleted_count > 0 84 | except Exception as e: 85 | print(f"Error removing admin {user_id}: {e}") 86 | return False 87 | 88 | async def list_admins() -> list: 89 | """List all admin user IDs.""" 90 | admins_collection = database['admins'] 91 | try: 92 | admins = await admins_collection.find().to_list(None) 93 | return [admin['_id'] for admin in admins] 94 | except Exception as e: 95 | print(f"Error listing admins: {e}") 96 | return [] 97 | 98 | async def save_channel(channel_id: int) -> bool: 99 | """Save a channel to the database with invite link expiration.""" 100 | if not isinstance(channel_id, int): 101 | print(f"Invalid channel_id: {channel_id}") 102 | return False 103 | 104 | try: 105 | await channels_collection.update_one( 106 | {"channel_id": channel_id}, 107 | { 108 | "$set": { 109 | "channel_id": channel_id, 110 | "invite_link_expiry": None, 111 | "created_at": datetime.utcnow(), 112 | "status": "active" 113 | } 114 | }, 115 | upsert=True 116 | ) 117 | return True 118 | except Exception as e: 119 | print(f"Error saving channel {channel_id}: {e}") 120 | return False 121 | 122 | async def get_channels() -> List[int]: 123 | """Get all active channel IDs from the database.""" 124 | try: 125 | channels = await channels_collection.find({"status": "active"}).to_list(None) 126 | valid_channels = [] 127 | for channel in channels: 128 | if isinstance(channel, dict) and "channel_id" in channel: 129 | valid_channels.append(channel["channel_id"]) 130 | else: 131 | print(f"Invalid channel document: {channel}") 132 | if not valid_channels: 133 | print(f"No valid channels found in database. Total documents checked: {len(channels)}") 134 | return valid_channels 135 | except Exception as e: 136 | print(f"Error fetching channels: {e}") 137 | return [] 138 | 139 | async def delete_channel(channel_id: int) -> bool: 140 | """Delete a channel from the database.""" 141 | try: 142 | result = await channels_collection.delete_one({"channel_id": channel_id}) 143 | return result.deleted_count > 0 144 | except Exception as e: 145 | print(f"Error deleting channel {channel_id}: {e}") 146 | return False 147 | 148 | async def save_encoded_link(channel_id: int) -> Optional[str]: 149 | """Save an encoded link for a channel and return it.""" 150 | if not isinstance(channel_id, int): 151 | print(f"Invalid channel_id: {channel_id}") 152 | return None 153 | 154 | try: 155 | encoded_link = base64.urlsafe_b64encode(str(channel_id).encode()).decode() 156 | await channels_collection.update_one( 157 | {"channel_id": channel_id}, 158 | { 159 | "$set": { 160 | "encoded_link": encoded_link, 161 | "status": "active", 162 | "updated_at": datetime.utcnow() 163 | } 164 | }, 165 | upsert=True 166 | ) 167 | return encoded_link 168 | except Exception as e: 169 | print(f"Error saving encoded link for channel {channel_id}: {e}") 170 | return None 171 | 172 | async def get_channel_by_encoded_link(encoded_link: str) -> Optional[int]: 173 | """Get a channel ID by its encoded link.""" 174 | if not isinstance(encoded_link, str): 175 | return None 176 | 177 | try: 178 | channel = await channels_collection.find_one({"encoded_link": encoded_link, "status": "active"}) 179 | return channel["channel_id"] if channel and "channel_id" in channel else None 180 | except Exception as e: 181 | print(f"Error fetching channel by encoded link {encoded_link}: {e}") 182 | return None 183 | 184 | async def save_encoded_link2(channel_id: int, encoded_link: str) -> Optional[str]: 185 | """Save a secondary encoded link for a channel.""" 186 | if not isinstance(channel_id, int) or not isinstance(encoded_link, str): 187 | print(f"Invalid input: channel_id={channel_id}, encoded_link={encoded_link}") 188 | return None 189 | 190 | try: 191 | await channels_collection.update_one( 192 | {"channel_id": channel_id}, 193 | { 194 | "$set": { 195 | "req_encoded_link": encoded_link, 196 | "status": "active", 197 | "updated_at": datetime.utcnow() 198 | } 199 | }, 200 | upsert=True 201 | ) 202 | return encoded_link 203 | except Exception as e: 204 | print(f"Error saving secondary encoded link for channel {channel_id}: {e}") 205 | return None 206 | 207 | async def get_channel_by_encoded_link2(encoded_link: str) -> Optional[int]: 208 | """Get a channel ID by its secondary encoded link.""" 209 | if not isinstance(encoded_link, str): 210 | return None 211 | 212 | try: 213 | channel = await channels_collection.find_one({"req_encoded_link": encoded_link, "status": "active"}) 214 | return channel["channel_id"] if channel and "channel_id" in channel else None 215 | except Exception as e: 216 | print(f"Error fetching channel by secondary encoded link {encoded_link}: {e}") 217 | return None 218 | 219 | async def save_invite_link(channel_id: int, invite_link: str, is_request: bool) -> bool: 220 | """Save the current invite link for a channel and its type.""" 221 | if not isinstance(channel_id, int) or not isinstance(invite_link, str): 222 | print(f"Invalid input: channel_id={channel_id}, invite_link={invite_link}") 223 | return False 224 | 225 | try: 226 | await channels_collection.update_one( 227 | {"channel_id": channel_id}, 228 | { 229 | "$set": { 230 | "current_invite_link": invite_link, 231 | "is_request_link": is_request, 232 | "invite_link_created_at": datetime.utcnow(), 233 | "status": "active" 234 | } 235 | }, 236 | upsert=True 237 | ) 238 | return True 239 | except Exception as e: 240 | print(f"Error saving invite link for channel {channel_id}: {e}") 241 | return False 242 | 243 | async def get_current_invite_link(channel_id: int) -> Optional[dict]: 244 | """Get the current invite link and its type for a channel.""" 245 | if not isinstance(channel_id, int): 246 | return None 247 | 248 | try: 249 | channel = await channels_collection.find_one({"channel_id": channel_id, "status": "active"}) 250 | if channel and "current_invite_link" in channel: 251 | return { 252 | "invite_link": channel["current_invite_link"], 253 | "is_request": channel.get("is_request_link", False) 254 | } 255 | return None 256 | except Exception as e: 257 | print(f"Error fetching current invite link for channel {channel_id}: {e}") 258 | return None 259 | 260 | async def get_link_creation_time(channel_id: int): 261 | """Get the creation time of the current invite link for a channel.""" 262 | try: 263 | channel = await channels_collection.find_one({"channel_id": channel_id, "status": "active"}) 264 | if channel and "invite_link_created_at" in channel: 265 | return channel["invite_link_created_at"] 266 | return None 267 | except Exception as e: 268 | print(f"Error fetching link creation time for channel {channel_id}: {e}") 269 | return None 270 | 271 | async def add_fsub_channel(channel_id: int) -> bool: 272 | """Add a channel to the FSub list.""" 273 | if not isinstance(channel_id, int): 274 | print(f"Invalid channel_id: {channel_id}") 275 | return False 276 | 277 | try: 278 | existing_channel = await fsub_channels_collection.find_one({'channel_id': channel_id}) 279 | if existing_channel: 280 | return False 281 | 282 | await fsub_channels_collection.insert_one({ 283 | 'channel_id': channel_id, 284 | 'created_at': datetime.utcnow(), 285 | 'status': 'active' 286 | }) 287 | return True 288 | except Exception as e: 289 | print(f"Error adding FSub channel {channel_id}: {e}") 290 | return False 291 | 292 | async def remove_fsub_channel(channel_id: int) -> bool: 293 | """Remove a channel from the FSub list.""" 294 | try: 295 | result = await fsub_channels_collection.delete_one({'channel_id': channel_id}) 296 | return result.deleted_count > 0 297 | except Exception as e: 298 | print(f"Error removing FSub channel {channel_id}: {e}") 299 | return False 300 | 301 | async def get_fsub_channels() -> List[int]: 302 | """Get all active FSub channel IDs.""" 303 | try: 304 | channels = await fsub_channels_collection.find({'status': 'active'}).to_list(None) 305 | return [channel['channel_id'] for channel in channels] 306 | except Exception as e: 307 | print(f"Error fetching FSub channels: {e}") 308 | return [] 309 | 310 | async def get_original_link(channel_id: int) -> Optional[str]: 311 | """Get the original link stored for a channel (used by /genlink).""" 312 | if not isinstance(channel_id, int): 313 | return None 314 | try: 315 | channel = await channels_collection.find_one({"channel_id": channel_id, "status": "active"}) 316 | return channel.get("original_link") if channel and "original_link" in channel else None 317 | except Exception as e: 318 | print(f"Error fetching original link for channel {channel_id}: {e}") 319 | return None 320 | 321 | async def set_approval_off(channel_id: int, off: bool = True) -> bool: 322 | """Set approval_off flag for a channel.""" 323 | if not isinstance(channel_id, int): 324 | print(f"Invalid channel_id: {channel_id}") 325 | return False 326 | try: 327 | await channels_collection.update_one( 328 | {"channel_id": channel_id}, 329 | {"$set": {"approval_off": off}}, 330 | upsert=True 331 | ) 332 | return True 333 | except Exception as e: 334 | print(f"Error setting approval_off for channel {channel_id}: {e}") 335 | return False 336 | 337 | async def is_approval_off(channel_id: int) -> bool: 338 | """Check if approval_off flag is set for a channel.""" 339 | if not isinstance(channel_id, int): 340 | return False 341 | try: 342 | channel = await channels_collection.find_one({"channel_id": channel_id}) 343 | return bool(channel and channel.get("approval_off", False)) 344 | except Exception as e: 345 | print(f"Error checking approval_off for channel {channel_id}: {e}") 346 | return False 347 | -------------------------------------------------------------------------------- /plugins/newpost.py: -------------------------------------------------------------------------------- 1 | # +++ Modified By Yato [telegram username: @ProYato] +++ 2 | import asyncio 3 | import base64 4 | from bot import Bot 5 | from pyrogram import Client, filters 6 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 7 | from pyrogram.errors import UserNotParticipant, FloodWait, ChatAdminRequired, RPCError 8 | from pyrogram.errors import InviteHashExpired, InviteRequestSent 9 | from database.database import save_channel, delete_channel, get_channels 10 | from config import * 11 | from database.database import * 12 | from helper_func import * 13 | from datetime import datetime, timedelta 14 | 15 | PAGE_SIZE = 6 16 | 17 | # Cache for chat information to avoid repeated API calls 18 | chat_info_cache = {} 19 | 20 | # Revoke invite link after 5-10 minutes 21 | async def revoke_invite_after_5_minutes(client: Bot, channel_id: int, link: str, is_request: bool = False): 22 | await asyncio.sleep(300) # 10 minutes 23 | try: 24 | if is_request: 25 | await client.revoke_chat_invite_link(channel_id, link) 26 | print(f"Jᴏɪɴ ʀᴇǫᴜᴇsᴛ ʟɪɴᴋ ʀᴇᴠᴏᴋᴇᴅ ғᴏʀ ᴄʜᴀɴɴᴇʟ {channel_id}") 27 | else: 28 | await client.revoke_chat_invite_link(channel_id, link) 29 | print(f"Iɴᴠɪᴛᴇ ʟɪɴᴋ ʀᴇᴠᴏᴋᴇᴅ ғᴏʀ ᴄʜᴀɴɴᴇʟ {channel_id}") 30 | except Exception as e: 31 | print(f"Fᴀɪʟᴇᴅ ᴛᴏ ʀᴇᴠᴏᴋᴇ ɪɴᴠɪᴛᴇ ғᴏʀ ᴄʜᴀɴɴᴇʟ {channel_id}: {e}") 32 | 33 | # Add chat command 34 | @Bot.on_message((filters.command('addchat') | filters.command('addch')) & is_owner_or_admin) 35 | async def set_channel(client: Bot, message: Message): 36 | try: 37 | channel_id = int(message.command[1]) 38 | except (IndexError, ValueError): 39 | return await message.reply("
Iɴᴠᴀʟɪᴅ ᴄʜᴀᴛ ID. Exᴀᴍᴘʟᴇ: /addchat <chat_id>") 40 | 41 | try: 42 | chat = await client.get_chat(channel_id) 43 | 44 | # Check permissions based on chat type 45 | if chat.permissions: 46 | # For groups/channels, check appropriate permissions 47 | has_permission = False 48 | if hasattr(chat.permissions, 'can_post_messages') and chat.permissions.can_post_messages: 49 | has_permission = True 50 | elif hasattr(chat.permissions, 'can_edit_messages') and chat.permissions.can_edit_messages: 51 | has_permission = True 52 | elif chat.type.name in ['GROUP', 'SUPERGROUP']: 53 | # For groups, having the bot as admin is usually sufficient 54 | try: 55 | bot_member = await client.get_chat_member(chat.id, (await client.get_me()).id) 56 | if bot_member.status.name in ['ADMINISTRATOR', 'CREATOR']: 57 | has_permission = True 58 | except: 59 | pass 60 | 61 | if not has_permission: 62 | return await message.reply(f"
I ᴀᴍ ɪɴ {chat.title}, ʙᴜᴛ I ʟᴀᴄᴋ ᴘᴏsᴛɪɴɢ ᴏʀ ᴇᴅɪᴛɪɴɢ ᴘᴇʀᴍɪssɪᴏɴs.") 63 | 64 | await save_channel(channel_id) 65 | base64_invite = await save_encoded_link(channel_id) 66 | normal_link = f"https://t.me/{client.username}?start={base64_invite}" 67 | base64_request = await encode(str(channel_id)) 68 | await save_encoded_link2(channel_id, base64_request) 69 | request_link = f"https://t.me/{client.username}?start=req_{base64_request}" 70 | reply_text = ( 71 | f"
✅ Cʜᴀᴛ {chat.title} ({channel_id}) ʜᴀs ʙᴇᴇɴ ᴀᴅᴅᴇᴅ sᴜᴄᴄᴇssғᴜʟʟʏ.\n\n" 72 | f"🔗 Nᴏʀᴍᴀʟ Lɪɴᴋ: {normal_link}\n" 73 | f"🔗 Rᴇǫᴜᴇsᴛ Lɪɴᴋ: {request_link}" 74 | ) 75 | return await message.reply(reply_text) 76 | 77 | except UserNotParticipant: 78 | return await message.reply("
I ᴀᴍ ɴᴏᴛ ᴀ ᴍᴇᴍʙᴇʀ ᴏғ ᴛʜɪs ᴄʜᴀɴɴᴇʟ. Pʟᴇᴀsᴇ ᴀᴅᴅ ᴍᴇ ᴀɴᴅ ᴛʀʏ ᴀɢᴀɪɴ.") 79 | except FloodWait as e: 80 | await asyncio.sleep(e.x) 81 | return await set_channel(client, message) 82 | except RPCError as e: 83 | return await message.reply(f"RPC Error: {str(e)}") 84 | except Exception as e: 85 | return await message.reply(f"Unexpected Error: {str(e)}") 86 | 87 | # Delete chat command 88 | @Bot.on_message((filters.command('delchat') | filters.command('delch')) & is_owner_or_admin) 89 | async def del_channel(client: Bot, message: Message): 90 | try: 91 | channel_id = int(message.command[1]) 92 | except (IndexError, ValueError): 93 | return await message.reply("
Iɴᴠᴀʟɪᴅ ᴄʜᴀᴛ ID. Exᴀᴍᴘʟᴇ: /delch <chat_id>") 94 | 95 | await delete_channel(channel_id) 96 | return await message.reply(f"
❌ Cʜᴀᴛ {channel_id} ʜᴀs ʙᴇᴇɴ ʀᴇᴍᴏᴠᴇᴅ sᴜᴄᴄᴇssғᴜʟʟʏ.") 97 | 98 | # Channel post command 99 | @Bot.on_message(filters.command('ch_links') & is_owner_or_admin) 100 | async def channel_post(client: Bot, message: Message): 101 | status_msg = await message.reply("⏳") 102 | try: 103 | channels = await get_channels() 104 | if not channels: 105 | await status_msg.delete() 106 | return await message.reply("
Nᴏ ᴄʜᴀɴɴᴇʟs ᴀʀᴇ ᴀᴠᴀɪʟᴀʙʟᴇ. Pʟᴇᴀsᴇ ᴜsᴇ /addch ᴛᴏ ᴀᴅᴅ ᴀ ᴄʜᴀɴɴᴇʟ.") 107 | 108 | await send_channel_page(client, message, channels, page=0, status_msg=status_msg) 109 | except Exception as e: 110 | await status_msg.delete() 111 | await message.reply(f"Error: {str(e)}") 112 | 113 | async def send_channel_page(client, message, channels, page, status_msg=None, edit=False): 114 | # Delete status message first 115 | if status_msg: 116 | await status_msg.delete() 117 | 118 | total_pages = (len(channels) + PAGE_SIZE - 1) // PAGE_SIZE 119 | start_idx = page * PAGE_SIZE 120 | end_idx = start_idx + PAGE_SIZE 121 | buttons = [] 122 | 123 | # Get all chat info concurrently 124 | chat_tasks = [] 125 | for channel_id in channels[start_idx:end_idx]: 126 | chat_tasks.append(get_chat_info(client, channel_id)) 127 | 128 | try: 129 | chat_infos = await asyncio.gather(*chat_tasks, return_exceptions=True) 130 | except Exception as e: 131 | print(f"Error gathering chat info: {e}") 132 | chat_infos = [None] * len(channels[start_idx:end_idx]) 133 | 134 | row = [] 135 | for i, chat_info in enumerate(chat_infos): 136 | channel_id = channels[start_idx + i] 137 | if isinstance(chat_info, Exception) or chat_info is None: 138 | print(f"Error getting chat info for channel {channel_id}: {chat_info}") 139 | continue 140 | 141 | try: 142 | base64_invite = await save_encoded_link(channel_id) 143 | button_link = f"https://t.me/{client.username}?start={base64_invite}" 144 | 145 | row.append(InlineKeyboardButton(chat_info.title, url=button_link)) 146 | 147 | if len(row) == 2: 148 | buttons.append(row) 149 | row = [] 150 | except Exception as e: 151 | print(f"Error for channel {channel_id}: {e}") 152 | 153 | if row: 154 | buttons.append(row) 155 | 156 | nav_buttons = [] 157 | if page > 0: 158 | nav_buttons.append(InlineKeyboardButton("• Pʀᴇᴠɪᴏᴜs •", callback_data=f"channelpage_{page-1}")) 159 | if page < total_pages - 1: 160 | nav_buttons.append(InlineKeyboardButton("• Nᴇxᴛ •", callback_data=f"channelpage_{page+1}")) 161 | 162 | if nav_buttons: 163 | buttons.append(nav_buttons) 164 | 165 | reply_markup = InlineKeyboardMarkup(buttons) 166 | if edit: 167 | await message.edit_text("Sᴇʟᴇᴄᴛ ᴀ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴀᴄᴄᴇss:", reply_markup=reply_markup) 168 | else: 169 | await message.reply("Sᴇʟᴇᴄᴛ ᴄʜᴀɴɴᴇʟ:", reply_markup=reply_markup) 170 | 171 | @Bot.on_callback_query(filters.regex(r"channelpage_(\d+)")) 172 | async def paginate_channels(client, callback_query): 173 | page = int(callback_query.data.split("_")[1]) 174 | status_msg = await callback_query.message.edit_text("⏳") 175 | channels = await get_channels() 176 | await send_channel_page(client, callback_query.message, channels, page, status_msg=status_msg, edit=True) 177 | 178 | # Request post command 179 | @Bot.on_message(filters.command('reqlink') & is_owner_or_admin) 180 | async def req_post(client: Bot, message: Message): 181 | status_msg = await message.reply("⏳") 182 | try: 183 | channels = await get_channels() 184 | if not channels: 185 | await status_msg.delete() 186 | return await message.reply("
Nᴏ ᴄʜᴀɴɴᴇʟs ᴀʀᴇ ᴀᴠᴀɪʟᴀʙʟᴇ. Pʟᴇᴀsᴇ ᴜsᴇ /setchannel ᴛᴏ ᴀᴅᴅ ᴀ ᴄʜᴀɴɴᴇʟ") 187 | 188 | await send_request_page(client, message, channels, page=0, status_msg=status_msg) 189 | except Exception as e: 190 | await status_msg.delete() 191 | await message.reply(f"Error: {str(e)}") 192 | 193 | async def send_request_page(client, message, channels, page, status_msg=None, edit=False): 194 | # Delete status message first 195 | if status_msg: 196 | await status_msg.delete() 197 | 198 | total_pages = (len(channels) + PAGE_SIZE - 1) // PAGE_SIZE 199 | start_idx = page * PAGE_SIZE 200 | end_idx = start_idx + PAGE_SIZE 201 | buttons = [] 202 | 203 | # Get all chat info concurrently 204 | chat_tasks = [] 205 | for channel_id in channels[start_idx:end_idx]: 206 | chat_tasks.append(get_chat_info(client, channel_id)) 207 | 208 | try: 209 | chat_infos = await asyncio.gather(*chat_tasks, return_exceptions=True) 210 | except Exception as e: 211 | print(f"Error gathering chat info: {e}") 212 | chat_infos = [None] * len(channels[start_idx:end_idx]) 213 | 214 | row = [] 215 | for i, chat_info in enumerate(chat_infos): 216 | channel_id = channels[start_idx + i] 217 | if isinstance(chat_info, Exception) or chat_info is None: 218 | print(f"Error getting chat info for channel {channel_id}: {chat_info}") 219 | continue 220 | 221 | try: 222 | base64_request = await encode(str(channel_id)) 223 | await save_encoded_link2(channel_id, base64_request) 224 | button_link = f"https://t.me/{client.username}?start=req_{base64_request}" 225 | 226 | row.append(InlineKeyboardButton(chat_info.title, url=button_link)) 227 | 228 | if len(row) == 2: 229 | buttons.append(row) 230 | row = [] 231 | except Exception as e: 232 | print(f"Error generating request link for channel {channel_id}: {e}") 233 | 234 | if row: 235 | buttons.append(row) 236 | 237 | nav_buttons = [] 238 | if page > 0: 239 | nav_buttons.append(InlineKeyboardButton("• Pʀᴇᴠɪᴏᴜs •", callback_data=f"reqpage_{page-1}")) 240 | if page < total_pages - 1: 241 | nav_buttons.append(InlineKeyboardButton("• Nᴇxᴛ •", callback_data=f"reqpage_{page+1}")) 242 | 243 | if nav_buttons: 244 | buttons.append(nav_buttons) 245 | reply_markup = InlineKeyboardMarkup(buttons) 246 | if edit: 247 | await message.edit_text("Sᴇʟᴇᴄᴛ ᴀ ᴄʜᴀɴɴᴇʟ ᴛᴏ ʀᴇǫᴜᴇsᴛ ᴀᴄᴄᴇss:", reply_markup=reply_markup) 248 | else: 249 | await message.reply("Sᴇʟᴇᴄᴛ ᴄʜᴀɴɴᴇʟ:", reply_markup=reply_markup) 250 | 251 | @Bot.on_callback_query(filters.regex(r"reqpage_(\d+)")) 252 | async def paginate_requests(client, callback_query): 253 | page = int(callback_query.data.split("_")[1]) 254 | status_msg = await callback_query.message.edit_text("⏳") 255 | channels = await get_channels() 256 | await send_request_page(client, callback_query.message, channels, page, status_msg=status_msg, edit=True) 257 | 258 | # Links command - show all links as text 259 | @Bot.on_message(filters.command('links') & is_owner_or_admin) 260 | async def show_links(client: Bot, message: Message): 261 | status_msg = await message.reply("⏳") 262 | try: 263 | channels = await get_channels() 264 | if not channels: 265 | await status_msg.delete() 266 | return await message.reply("
Nᴏ ᴄʜᴀɴɴᴇʟs ᴀʀᴇ ᴀᴠᴀɪʟᴀʙʟᴇ. Pʟᴇᴀsᴇ ᴜsᴇ /addch ᴛᴏ ᴀᴅᴅ ᴀ ᴄʜᴀɴɴᴇʟ.") 267 | 268 | await send_links_page(client, message, channels, page=0, status_msg=status_msg) 269 | except Exception as e: 270 | await status_msg.delete() 271 | await message.reply(f"Error: {str(e)}") 272 | 273 | async def send_links_page(client, message, channels, page, status_msg=None, edit=False): 274 | # Delete status message first 275 | if status_msg: 276 | await status_msg.delete() 277 | 278 | total_pages = (len(channels) + PAGE_SIZE - 1) // PAGE_SIZE 279 | start_idx = page * PAGE_SIZE 280 | end_idx = start_idx + PAGE_SIZE 281 | 282 | links_text = "➤ Aʟʟ Cʜᴀɴɴᴇʟ Lɪɴᴋs:\n\n" 283 | 284 | # Get all chat info and links concurrently 285 | tasks = [] 286 | for channel_id in channels[start_idx:end_idx]: 287 | tasks.append(asyncio.gather( 288 | get_chat_info(client, channel_id), 289 | save_encoded_link(channel_id), 290 | asyncio.create_task(encode(str(channel_id))), 291 | return_exceptions=True 292 | )) 293 | 294 | try: 295 | results = await asyncio.gather(*tasks, return_exceptions=True) 296 | except Exception as e: 297 | print(f"Error gathering link info: {e}") 298 | results = [None] * len(channels[start_idx:end_idx]) 299 | 300 | for i, result in enumerate(results): 301 | idx = start_idx + i + 1 302 | channel_id = channels[start_idx + i] 303 | 304 | if isinstance(result, Exception) or result is None or any(isinstance(r, Exception) for r in result): 305 | print(f"Error getting info for channel {channel_id}: {result}") 306 | links_text += f"{idx}. Channel {channel_id} (Error)\n\n" 307 | continue 308 | 309 | try: 310 | chat_info, base64_invite, base64_request = result 311 | if isinstance(chat_info, Exception): 312 | links_text += f"{idx}. Channel {channel_id} (Error)\n\n" 313 | continue 314 | 315 | await save_encoded_link2(channel_id, base64_request) 316 | normal_link = f"https://t.me/{client.username}?start={base64_invite}" 317 | request_link = f"https://t.me/{client.username}?start=req_{base64_request}" 318 | 319 | links_text += f"{idx}. {chat_info.title}\n" 320 | links_text += f"➥ Nᴏʀᴍᴀʟ: {normal_link}\n" 321 | links_text += f"➤ Rᴇǫᴜᴇsᴛ: {request_link}\n\n" 322 | 323 | except Exception as e: 324 | print(f"Error for channel {channel_id}: {e}") 325 | links_text += f"{idx}. Channel {channel_id} (Error)\n\n" 326 | 327 | # Add pagination info 328 | links_text += f"📄 Pᴀɢᴇ {page + 1} ᴏғ {total_pages}" 329 | 330 | # Create navigation buttons 331 | buttons = [] 332 | nav_buttons = [] 333 | if page > 0: 334 | nav_buttons.append(InlineKeyboardButton("• Pʀᴇᴠɪᴏᴜs •", callback_data=f"linkspage_{page-1}")) 335 | if page < total_pages - 1: 336 | nav_buttons.append(InlineKeyboardButton("• Nᴇxᴛ •", callback_data=f"linkspage_{page+1}")) 337 | 338 | if nav_buttons: 339 | buttons.append(nav_buttons) 340 | 341 | reply_markup = InlineKeyboardMarkup(buttons) if buttons else None 342 | 343 | if edit: 344 | await message.edit_text(links_text, reply_markup=reply_markup) 345 | else: 346 | await message.reply(links_text, reply_markup=reply_markup) 347 | 348 | @Bot.on_callback_query(filters.regex(r"linkspage_(\d+)")) 349 | async def paginate_links(client, callback_query): 350 | page = int(callback_query.data.split("_")[1]) 351 | status_msg = await callback_query.message.edit_text("⏳") 352 | channels = await get_channels() 353 | await send_links_page(client, callback_query.message, channels, page, status_msg=status_msg, edit=True) 354 | 355 | # Bulk link generation command 356 | @Bot.on_message(filters.command('bulklink') & is_owner_or_admin) 357 | async def bulk_link(client: Bot, message: Message): 358 | user_id = message.from_user.id 359 | 360 | if len(message.command) < 2: 361 | return await message.reply("
ᴜsᴀɢᴇ: /bulklink <id1> <id2> ...") 362 | 363 | ids = message.command[1:] 364 | reply_text = "➤ Bᴜʟᴋ Lɪɴᴋ Gᴇɴᴇʀᴀᴛɪᴏɴ:\n\n" 365 | for idx, id_str in enumerate(ids, start=1): 366 | try: 367 | channel_id = int(id_str) 368 | chat = await client.get_chat(channel_id) 369 | base64_invite = await save_encoded_link(channel_id) 370 | normal_link = f"https://t.me/{client.username}?start={base64_invite}" 371 | base64_request = await encode(str(channel_id)) 372 | await save_encoded_link2(channel_id, base64_request) 373 | request_link = f"https://t.me/{client.username}?start=req_{base64_request}" 374 | reply_text += f"{idx}. {chat.title} ({channel_id})\n" 375 | reply_text += f"➥ Nᴏʀᴍᴀʟ: {normal_link}\n" 376 | reply_text += f"➤ Rᴇǫᴜᴇsᴛ: {request_link}\n\n" 377 | except Exception as e: 378 | reply_text += f"{idx}. Channel {id_str} (Error: {e})\n\n" 379 | await message.reply(reply_text) 380 | 381 | @Bot.on_message(filters.command('genlink') & filters.private & is_owner_or_admin) 382 | async def generate_link_command(client: Bot, message: Message): 383 | user_id = message.from_user.id 384 | if len(message.command) < 2: 385 | return await message.reply("Usage: /genlink <link>") 386 | 387 | link = message.command[1] 388 | # Store the link in the database channel 389 | try: 390 | sent_msg = await client.send_message(DATABASE_CHANNEL, f"#LINK\n{link}") 391 | channel_id = sent_msg.id # Use id as unique id for this link 392 | # Save encoded links 393 | base64_invite = await save_encoded_link(channel_id) 394 | base64_request = await encode(str(channel_id)) 395 | await save_encoded_link2(channel_id, base64_request) 396 | # Store the original link in the database 397 | from database.database import channels_collection 398 | await channels_collection.update_one( 399 | {"channel_id": channel_id}, 400 | {"$set": {"original_link": link}}, 401 | upsert=True 402 | ) 403 | normal_link = f"https://t.me/{client.username}?start={base64_invite}" 404 | request_link = f"https://t.me/{client.username}?start=req_{base64_request}" 405 | reply_text = ( 406 | f"✅ Link stored and encoded successfully.\n\n" 407 | f"🔗 Normal Link: {normal_link}\n" 408 | f"🔗 Request Link: {request_link}" 409 | ) 410 | await message.reply(reply_text) 411 | except Exception as e: 412 | await message.reply(f"Error storing link: {e}") 413 | 414 | @Bot.on_message(filters.command('channels') & is_owner_or_admin) 415 | async def show_channel_ids(client: Bot, message: Message): 416 | status_msg = await message.reply("⏳") 417 | try: 418 | channels = await get_channels() 419 | if not channels: 420 | await status_msg.delete() 421 | return await message.reply("
Nᴏ ᴄʜᴀɴɴᴇʟs ᴀʀᴇ ᴀᴠᴀɪʟᴀʙʟᴇ. Pʟᴇᴀsᴇ ᴜsᴇ /addch ᴛᴏ ᴀᴅᴅ ᴀ ᴄʜᴀɴɴᴇʟ.") 422 | 423 | await send_channel_ids_page(client, message, channels, page=0, status_msg=status_msg) 424 | except Exception as e: 425 | await status_msg.delete() 426 | await message.reply(f"Error: {str(e)}") 427 | 428 | async def send_channel_ids_page(client, message, channels, page, status_msg=None, edit=False): 429 | # Delete status message first 430 | if status_msg: 431 | await status_msg.delete() 432 | 433 | PAGE_SIZE = 10 434 | total_pages = (len(channels) + PAGE_SIZE - 1) // PAGE_SIZE 435 | start_idx = page * PAGE_SIZE 436 | end_idx = start_idx + PAGE_SIZE 437 | 438 | # Get all chat info concurrently 439 | chat_tasks = [] 440 | for channel_id in channels[start_idx:end_idx]: 441 | chat_tasks.append(get_chat_info(client, channel_id)) 442 | 443 | try: 444 | chat_infos = await asyncio.gather(*chat_tasks, return_exceptions=True) 445 | except Exception as e: 446 | print(f"Error gathering chat info: {e}") 447 | chat_infos = [None] * len(channels[start_idx:end_idx]) 448 | 449 | text = "➤ Cᴏɴɴᴇᴄᴛᴇᴅ Cʜᴀɴɴᴇʟs (ID & Name):\n\n" 450 | for i, chat_info in enumerate(chat_infos): 451 | idx = start_idx + i + 1 452 | channel_id = channels[start_idx + i] 453 | 454 | if isinstance(chat_info, Exception) or chat_info is None: 455 | text += f"{idx}. Channel {channel_id} (Error)\n" 456 | continue 457 | 458 | text += f"{idx}. {chat_info.title} ({channel_id})\n" 459 | 460 | text += f"\n📄 Pᴀɢᴇ {page + 1} ᴏғ {total_pages}" 461 | 462 | # Navigation buttons 463 | buttons = [] 464 | nav_buttons = [] 465 | if page > 0: 466 | nav_buttons.append(InlineKeyboardButton("• Pʀᴇᴠɪᴏᴜs •", callback_data=f"channelids_{page-1}")) 467 | if page < total_pages - 1: 468 | nav_buttons.append(InlineKeyboardButton("• Nᴇxᴛ •", callback_data=f"channelids_{page+1}")) 469 | if nav_buttons: 470 | buttons.append(nav_buttons) 471 | 472 | reply_markup = InlineKeyboardMarkup(buttons) if buttons else None 473 | if edit: 474 | await message.edit_text(text, reply_markup=reply_markup) 475 | else: 476 | await message.reply(text, reply_markup=reply_markup) 477 | 478 | @Bot.on_callback_query(filters.regex(r"channelids_(\d+)")) 479 | async def paginate_channel_ids(client, callback_query): 480 | page = int(callback_query.data.split("_")[1]) 481 | status_msg = await callback_query.message.edit_text("⏳") 482 | channels = await get_channels() 483 | await send_channel_ids_page(client, callback_query.message, channels, page, status_msg=status_msg, edit=True) 484 | 485 | # Helper function to get chat info with caching 486 | async def get_chat_info(client, channel_id): 487 | # Check cache first 488 | if channel_id in chat_info_cache: 489 | cached_info, timestamp = chat_info_cache[channel_id] 490 | # Cache for 5 minutes 491 | if (datetime.now() - timestamp).total_seconds() < 300: 492 | return cached_info 493 | 494 | # Fetch fresh info 495 | try: 496 | chat_info = await client.get_chat(channel_id) 497 | # Cache the result 498 | chat_info_cache[channel_id] = (chat_info, datetime.now()) 499 | return chat_info 500 | except Exception as e: 501 | print(f"Error getting chat info for {channel_id}: {e}") 502 | # Return cached info even if stale if we can't get fresh info 503 | if channel_id in chat_info_cache: 504 | return chat_info_cache[channel_id][0] 505 | raise e 506 | 507 | -------------------------------------------------------------------------------- /plugins/start.py: -------------------------------------------------------------------------------- 1 | 2 | import asyncio 3 | import base64 4 | import time 5 | from asyncio import Lock 6 | from collections import defaultdict 7 | from pyrogram import Client, filters 8 | from pyrogram.enums import ParseMode, ChatMemberStatus, ChatAction 9 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery, InputMediaPhoto 10 | from pyrogram.errors import FloodWait, UserNotParticipant, UserIsBlocked, InputUserDeactivated 11 | import os 12 | import asyncio 13 | from asyncio import sleep 14 | from asyncio import Lock 15 | import random 16 | 17 | from bot import Bot 18 | from datetime import datetime, timedelta 19 | from config import * 20 | from database.database import * 21 | from plugins.newpost import revoke_invite_after_5_minutes 22 | from helper_func import * 23 | 24 | # Create a lock dictionary for each channel to prevent concurrent link generation 25 | channel_locks = defaultdict(asyncio.Lock) 26 | 27 | user_banned_until = {} 28 | 29 | # Broadcast variables 30 | cancel_lock = asyncio.Lock() 31 | is_canceled = False 32 | 33 | @Bot.on_message(filters.command('start') & filters.private) 34 | async def start_command(client: Bot, message: Message): 35 | user_id = message.from_user.id 36 | 37 | if user_id in user_banned_until: 38 | if datetime.now() < user_banned_until[user_id]: 39 | return await message.reply_text( 40 | "
You are temporarily banned from using commands due to spamming. Try again later.", 41 | parse_mode=ParseMode.HTML 42 | ) 43 | 44 | await add_user(user_id) 45 | 46 | # ✅ Check Force Subscription 47 | #if not await is_subscribed(client, user_id): 48 | #await temp.delete() 49 | #return await not_joined(client, message) 50 | 51 | # 52 | # Check FSub requirements 53 | # fsub_channels = await get_fsub_channels() 54 | # if fsub_channels: 55 | # is_subscribed, subscription_message, subscription_buttons = await check_subscription_status(client, user_id, fsub_channels) 56 | # if not is_subscribed: 57 | # return await message.reply_text( 58 | # subscription_message, 59 | # reply_markup=subscription_buttons, 60 | # parse_mode=ParseMode.HTML 61 | # ) 62 | 63 | text = message.text 64 | if len(text) > 7: 65 | try: 66 | base64_string = text.split(" ", 1)[1] 67 | is_request = base64_string.startswith("req_") 68 | 69 | if is_request: 70 | base64_string = base64_string[4:] 71 | channel_id = await get_channel_by_encoded_link2(base64_string) 72 | else: 73 | channel_id = await get_channel_by_encoded_link(base64_string) 74 | 75 | if not channel_id: 76 | return await message.reply_text( 77 | "
Invalid or expired invite link.", 78 | parse_mode=ParseMode.HTML 79 | ) 80 | 81 | # Check if this is a /genlink link (original_link exists) 82 | from database.database import get_original_link 83 | original_link = await get_original_link(channel_id) 84 | if original_link: 85 | button = InlineKeyboardMarkup( 86 | [[InlineKeyboardButton("• Proceed to Link •", url=original_link)]] 87 | ) 88 | return await message.reply_text( 89 | "
ʜᴇʀᴇ ɪs ʏᴏᴜʀ ʟɪɴᴋ! ᴄʟɪᴄᴋ ʙᴇʟᴏᴡ ᴛᴏ ᴘʀᴏᴄᴇᴇᴅ", 90 | reply_markup=button, 91 | parse_mode=ParseMode.HTML 92 | ) 93 | 94 | # Use a lock for this channel to prevent concurrent link generation 95 | async with channel_locks[channel_id]: 96 | # Check if we already have a valid link 97 | old_link_info = await get_current_invite_link(channel_id) 98 | current_time = datetime.now() 99 | 100 | # If we have an existing link and it's not expired yet (assuming 5 minutes validity) 101 | if old_link_info: 102 | link_created_time = await get_link_creation_time(channel_id) 103 | if link_created_time and (current_time - link_created_time).total_seconds() < 240: # 4 minutes 104 | # Use existing link 105 | invite_link = old_link_info["invite_link"] 106 | is_request_link = old_link_info["is_request"] 107 | else: 108 | # Revoke old link and create new one 109 | try: 110 | await client.revoke_chat_invite_link(channel_id, old_link_info["invite_link"]) 111 | print(f"Revoked old {'request' if old_link_info['is_request'] else 'invite'} link for channel {channel_id}") 112 | except Exception as e: 113 | print(f"Failed to revoke old link for channel {channel_id}: {e}") 114 | 115 | # Create new link 116 | invite = await client.create_chat_invite_link( 117 | chat_id=channel_id, 118 | expire_date=current_time + timedelta(minutes=10), 119 | creates_join_request=is_request 120 | ) 121 | invite_link = invite.invite_link 122 | is_request_link = is_request 123 | await save_invite_link(channel_id, invite_link, is_request_link) 124 | else: 125 | # Create new link 126 | invite = await client.create_chat_invite_link( 127 | chat_id=channel_id, 128 | expire_date=current_time + timedelta(minutes=10), 129 | creates_join_request=is_request 130 | ) 131 | invite_link = invite.invite_link 132 | is_request_link = is_request 133 | await save_invite_link(channel_id, invite_link, is_request_link) 134 | 135 | button_text = "• ʀᴇǫᴜᴇsᴛ ᴛᴏ ᴊᴏɪɴ •" if is_request_link else "• ᴊᴏɪɴ ᴄʜᴀɴɴᴇʟ •" 136 | button = InlineKeyboardMarkup([[InlineKeyboardButton(button_text, url=invite_link)]]) 137 | 138 | wait_msg = await message.reply_text( 139 | "⏳", 140 | parse_mode=ParseMode.HTML 141 | ) 142 | 143 | await wait_msg.delete() 144 | 145 | await message.reply_text( 146 | "
ʜᴇʀᴇ ɪs ʏᴏᴜʀ ʟɪɴᴋ! ᴄʟɪᴄᴋ ʙᴇʟᴏᴡ ᴛᴏ ᴘʀᴏᴄᴇᴇᴅ", 147 | reply_markup=button, 148 | parse_mode=ParseMode.HTML 149 | ) 150 | 151 | note_msg = await message.reply_text( 152 | "Note: If the link is expired, please click the post link again to get a new one.", 153 | parse_mode=ParseMode.HTML 154 | ) 155 | 156 | # Auto-delete the note message after 5 minutes 157 | asyncio.create_task(delete_after_delay(note_msg, 300)) 158 | 159 | asyncio.create_task(revoke_invite_after_5_minutes(client, channel_id, invite_link, is_request_link)) 160 | 161 | except Exception as e: 162 | await message.reply_text( 163 | "
Invalid or expired invite link.", 164 | parse_mode=ParseMode.HTML 165 | ) 166 | print(f"Decoding error: {e}") 167 | else: 168 | inline_buttons = InlineKeyboardMarkup( 169 | [ 170 | [InlineKeyboardButton("• ᴀʙᴏᴜᴛ", callback_data="about"), 171 | InlineKeyboardButton("• ᴄʜᴀɴɴᴇʟs", callback_data="channels")], 172 | [InlineKeyboardButton("• Close •", callback_data="close")] 173 | ] 174 | ) 175 | 176 | # Show waiting emoji and instantly delete it 177 | wait_msg = await message.reply_text("⏳") 178 | await asyncio.sleep(0.1) 179 | await wait_msg.delete() 180 | 181 | try: 182 | await message.reply_photo( 183 | photo=START_PIC, 184 | caption=START_MSG, 185 | reply_markup=inline_buttons, 186 | parse_mode=ParseMode.HTML 187 | ) 188 | except Exception as e: 189 | print(f"Error sending start picture: {e}") 190 | await message.reply_text( 191 | START_MSG, 192 | reply_markup=inline_buttons, 193 | parse_mode=ParseMode.HTML 194 | ) 195 | 196 | 197 | #=====================================================================================## 198 | # Don't Remove Credit @CodeFlix_Bots, @rohit_1888 199 | # Ask Doubt on telegram @CodeflixSupport 200 | 201 | async def get_link_creation_time(channel_id): 202 | """Get the creation time of the current invite link for a channel.""" 203 | try: 204 | from database.database import channels_collection 205 | channel = await channels_collection.find_one({"channel_id": channel_id, "status": "active"}) 206 | if channel and "invite_link_created_at" in channel: 207 | return channel["invite_link_created_at"] 208 | return None 209 | except Exception as e: 210 | print(f"Error fetching link creation time for channel {channel_id}: {e}") 211 | return None 212 | 213 | # Create a global dictionary to store chat data 214 | chat_data_cache = {} 215 | 216 | async def not_joined(client: Client, message: Message): 217 | #temp = await message.reply("ᴡᴀɪᴛ ᴀ sᴇᴄ..") 218 | 219 | user_id = message.from_user.id 220 | buttons = [] 221 | count = 0 222 | 223 | try: 224 | all_channels = await db.show_channels() # Should return list of (chat_id, mode) tuples 225 | for total, chat_id in enumerate(all_channels, start=1): 226 | mode = await db.get_channel_mode(chat_id) # fetch mode 227 | 228 | await message.reply_chat_action(ChatAction.TYPING) 229 | 230 | if not await is_sub(client, user_id, chat_id): 231 | try: 232 | # Cache chat info 233 | if chat_id in chat_data_cache: 234 | data = chat_data_cache[chat_id] 235 | else: 236 | data = await client.get_chat(chat_id) 237 | chat_data_cache[chat_id] = data 238 | 239 | name = data.title 240 | 241 | # Generate proper invite link based on the mode 242 | if mode == "on" and not data.username: 243 | invite = await client.create_chat_invite_link( 244 | chat_id=chat_id, 245 | creates_join_request=True, 246 | expire_date=datetime.utcnow() + timedelta(seconds=FSUB_LINK_EXPIRY) if FSUB_LINK_EXPIRY else None 247 | ) 248 | link = invite.invite_link 249 | 250 | else: 251 | if data.username: 252 | link = f"https://t.me/{data.username}" 253 | else: 254 | invite = await client.create_chat_invite_link( 255 | chat_id=chat_id, 256 | expire_date=datetime.utcnow() + timedelta(seconds=FSUB_LINK_EXPIRY) if FSUB_LINK_EXPIRY else None) 257 | link = invite.invite_link 258 | 259 | buttons.append([InlineKeyboardButton(text=name, url=link)]) 260 | count += 1 261 | #await temp.edit(f"{'! ' * count}") 262 | 263 | except Exception as e: 264 | print(f"Error with chat {chat_id}: {e}") 265 | return #await temp.edit( 266 | #f"! Eʀʀᴏʀ, Cᴏɴᴛᴀᴄᴛ ᴅᴇᴠᴇʟᴏᴘᴇʀ ᴛᴏ sᴏʟᴠᴇ ᴛʜᴇ ɪssᴜᴇs @rohit_1888\n" 267 | #f"
Rᴇᴀsᴏɴ: {e}
" 268 | #) 269 | 270 | # Retry Button 271 | try: 272 | buttons.append([ 273 | InlineKeyboardButton( 274 | text='♻️ Tʀʏ Aɢᴀɪɴ', 275 | url=f"https://t.me/{client.username}?start={message.command[1]}" 276 | ) 277 | ]) 278 | except IndexError: 279 | pass 280 | 281 | await message.reply_photo( 282 | photo=FORCE_PIC, 283 | caption=FORCE_MSG.format( 284 | first=message.from_user.first_name, 285 | last=message.from_user.last_name, 286 | username=None if not message.from_user.username else '@' + message.from_user.username, 287 | mention=message.from_user.mention, 288 | id=message.from_user.id 289 | ), 290 | reply_markup=InlineKeyboardMarkup(buttons), 291 | ) 292 | 293 | except Exception as e: 294 | print(f"Final Error: {e}") 295 | 296 | @Bot.on_callback_query(filters.regex("close")) 297 | async def close_callback(client: Bot, callback_query): 298 | await callback_query.answer() 299 | await callback_query.message.delete() 300 | 301 | @Bot.on_callback_query(filters.regex("check_sub")) 302 | async def check_sub_callback(client: Bot, callback_query: CallbackQuery): 303 | user_id = callback_query.from_user.id 304 | fsub_channels = await get_fsub_channels() 305 | 306 | if not fsub_channels: 307 | await callback_query.message.edit_text( 308 | "No FSub channels configured!", 309 | parse_mode=ParseMode.HTML 310 | ) 311 | return 312 | 313 | is_subscribed, subscription_message, subscription_buttons = await check_subscription_status(client, user_id, fsub_channels) 314 | if is_subscribed: 315 | await callback_query.message.edit_text( 316 | "You are subscribed to all required channels! Use /start to proceed.", 317 | parse_mode=ParseMode.HTML 318 | ) 319 | else: 320 | await callback_query.message.edit_text( 321 | subscription_message, 322 | reply_markup=subscription_buttons, 323 | parse_mode=ParseMode.HTML 324 | ) 325 | 326 | WAIT_MSG = "Processing..." 327 | 328 | REPLY_ERROR = """Usᴇ ᴛʜɪs ᴄᴏᴍᴍᴀɴᴅ ᴀs ᴀ ʀᴇᴘʟʏ ᴛᴏ ᴀɴʏ Tᴇʟᴇɢʀᴀᴍ ᴍᴇssᴀɢᴇ ᴡɪᴛʜᴏᴜᴛ ᴀɴʏ sᴘᴀᴄᴇs.""" 329 | # Define a global variable to store the cancel state 330 | is_canceled = False 331 | cancel_lock = Lock() 332 | 333 | @Bot.on_message(filters.command('status') & filters.private & is_owner_or_admin) 334 | async def info(client: Bot, message: Message): 335 | reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("• Close •", callback_data="close")]]) 336 | 337 | start_time = time.time() 338 | temp_msg = await message.reply("Processing...", quote=True, parse_mode=ParseMode.HTML) 339 | end_time = time.time() 340 | 341 | ping_time = (end_time - start_time) * 1000 342 | 343 | users = await full_userbase() 344 | now = datetime.now() 345 | delta = now - client.uptime 346 | bottime = get_readable_time(delta.seconds) 347 | 348 | await temp_msg.edit( 349 | f"Users: {len(users)}\n\nUptime: {bottime}\n\nPing: {ping_time:.2f} ms", 350 | reply_markup=reply_markup, 351 | parse_mode=ParseMode.HTML 352 | ) 353 | 354 | #--------------------------------------------------------------[[ADMIN COMMANDS]]---------------------------------------------------------------------------# 355 | # Handler for the /cancel command 356 | @Bot.on_message(filters.command('cancel') & filters.private & is_owner_or_admin) 357 | async def cancel_broadcast(client: Bot, message: Message): 358 | global is_canceled 359 | async with cancel_lock: 360 | is_canceled = True 361 | 362 | @Bot.on_message(filters.private & filters.command('broadcast') & is_owner_or_admin) 363 | async def broadcast(client: Bot, message: Message): 364 | global is_canceled 365 | args = message.text.split()[1:] 366 | 367 | if not message.reply_to_message: 368 | msg = await message.reply( 369 | "Reply to a message to broadcast.\n\nUsage examples:\n" 370 | "`/broadcast normal`\n" 371 | "`/broadcast pin`\n" 372 | "`/broadcast delete 30`\n" 373 | "`/broadcast pin delete 30`\n" 374 | "`/broadcast silent`\n" 375 | ) 376 | await asyncio.sleep(8) 377 | return await msg.delete() 378 | 379 | # Defaults 380 | do_pin = False 381 | do_delete = False 382 | duration = 0 383 | silent = False 384 | mode_text = [] 385 | 386 | i = 0 387 | while i < len(args): 388 | arg = args[i].lower() 389 | if arg == "pin": 390 | do_pin = True 391 | mode_text.append("PIN") 392 | elif arg == "delete": 393 | do_delete = True 394 | try: 395 | duration = int(args[i + 1]) 396 | i += 1 397 | except (IndexError, ValueError): 398 | return await message.reply("Provide valid duration for delete mode.\nUsage: `/broadcast delete 30`") 399 | mode_text.append(f"DELETE({duration}s)") 400 | elif arg == "silent": 401 | silent = True 402 | mode_text.append("SILENT") 403 | else: 404 | mode_text.append(arg.upper()) 405 | i += 1 406 | 407 | if not mode_text: 408 | mode_text.append("NORMAL") 409 | 410 | # Reset cancel flag 411 | async with cancel_lock: 412 | is_canceled = False 413 | 414 | query = await full_userbase() 415 | broadcast_msg = message.reply_to_message 416 | total = len(query) 417 | successful = blocked = deleted = unsuccessful = 0 418 | 419 | pls_wait = await message.reply(f"Broadcasting in {' + '.join(mode_text)} mode...") 420 | 421 | bar_length = 20 422 | progress_bar = '' 423 | last_update_percentage = 0 424 | update_interval = 0.05 # 5% 425 | 426 | for i, chat_id in enumerate(query, start=1): 427 | async with cancel_lock: 428 | if is_canceled: 429 | await pls_wait.edit(f"›› BROADCAST ({' + '.join(mode_text)}) CANCELED ❌") 430 | return 431 | 432 | try: 433 | sent_msg = await broadcast_msg.copy(chat_id, disable_notification=silent) 434 | 435 | if do_pin: 436 | await client.pin_chat_message(chat_id, sent_msg.id, both_sides=True) 437 | if do_delete: 438 | asyncio.create_task(auto_delete(sent_msg, duration)) 439 | 440 | successful += 1 441 | except FloodWait as e: 442 | await asyncio.sleep(e.x) 443 | try: 444 | sent_msg = await broadcast_msg.copy(chat_id, disable_notification=silent) 445 | if do_pin: 446 | await client.pin_chat_message(chat_id, sent_msg.id, both_sides=True) 447 | if do_delete: 448 | asyncio.create_task(auto_delete(sent_msg, duration)) 449 | successful += 1 450 | except: 451 | unsuccessful += 1 452 | except UserIsBlocked: 453 | await del_user(chat_id) 454 | blocked += 1 455 | except InputUserDeactivated: 456 | await del_user(chat_id) 457 | deleted += 1 458 | except: 459 | unsuccessful += 1 460 | await del_user(chat_id) 461 | 462 | # Progress 463 | percent_complete = i / total 464 | if percent_complete - last_update_percentage >= update_interval or last_update_percentage == 0: 465 | num_blocks = int(percent_complete * bar_length) 466 | progress_bar = "●" * num_blocks + "○" * (bar_length - num_blocks) 467 | status_update = f"""›› BROADCAST ({' + '.join(mode_text)}) IN PROGRESS... 468 | 469 |
⏳: [{progress_bar}] {percent_complete:.0%}
470 | 471 | ›› Total Users: {total} 472 | ›› Successful: {successful} 473 | ›› Blocked: {blocked} 474 | ›› Deleted: {deleted} 475 | ›› Unsuccessful: {unsuccessful} 476 | 477 | ➪ To stop broadcasting click: /cancel""" 478 | await pls_wait.edit(status_update) 479 | last_update_percentage = percent_complete 480 | 481 | # Final status 482 | final_status = f"""›› BROADCAST ({' + '.join(mode_text)}) COMPLETED ✅ 483 | 484 |
Dᴏɴᴇ: [{progress_bar}] {percent_complete:.0%}
485 | 486 | ›› Total Users: {total} 487 | ›› Successful: {successful} 488 | ›› Blocked: {blocked} 489 | ›› Deleted: {deleted} 490 | ›› Unsuccessful: {unsuccessful}""" 491 | return await pls_wait.edit(final_status) 492 | 493 | 494 | # helper for delete mode 495 | async def auto_delete(sent_msg, duration): 496 | await asyncio.sleep(duration) 497 | try: 498 | await sent_msg.delete() 499 | except: 500 | pass 501 | 502 | 503 | #---------------------------------- 504 | 505 | user_message_count = {} 506 | user_banned_until = {} 507 | 508 | MAX_MESSAGES = 3 509 | TIME_WINDOW = timedelta(seconds=10) 510 | BAN_DURATION = timedelta(hours=1) 511 | 512 | """ 513 | 514 | @Bot.on_message(filters.private) 515 | async def monitor_messages(client: Bot, message: Message): 516 | user_id = message.from_user.id 517 | now = datetime.now() 518 | 519 | if message.text and message.text.startswith("/"): 520 | return 521 | 522 | if user_id in ADMINS: 523 | return 524 | 525 | if user_id in user_banned_until and now < user_banned_until[user_id]: 526 | await message.reply_text( 527 | "
You are temporarily banned from using commands due to spamming. Try again later.", 528 | parse_mode=ParseMode.HTML 529 | ) 530 | return 531 | 532 | if user_id not in user_message_count: 533 | user_message_count[user_id] = [] 534 | 535 | user_message_count[user_id].append(now) 536 | user_message_count[user_id] = [time for time in user_message_count[user_id] if now - time <= TIME_WINDOW] 537 | 538 | if len(user_message_count[user_id]) > MAX_MESSAGES: 539 | user_banned_until[user_id] = now + BAN_DURATION 540 | await message.reply_text( 541 | "
You are temporarily banned from using commands due to spamming. Try again later.", 542 | parse_mode=ParseMode.HTML 543 | ) 544 | return 545 | 546 | """ 547 | 548 | @Bot.on_callback_query() 549 | async def cb_handler(client: Bot, query: CallbackQuery): 550 | data = query.data 551 | chat_id = query.message.chat.id 552 | 553 | if data == "close": 554 | await query.message.delete() 555 | try: 556 | await query.message.reply_to_message.delete() 557 | except: 558 | pass 559 | 560 | elif data == "about": 561 | user = await client.get_users(OWNER_ID) 562 | user_link = f"https://t.me/{user.username}" if user.username else f"tg://openmessage?user_id={OWNER_ID}" 563 | 564 | await query.edit_message_media( 565 | InputMediaPhoto( 566 | "https://envs.sh/Wdj.jpg", 567 | ABOUT_TXT 568 | ), 569 | reply_markup=InlineKeyboardMarkup([ 570 | [InlineKeyboardButton('• ʙᴀᴄᴋ', callback_data='start'), InlineKeyboardButton('ᴄʟᴏsᴇ •', callback_data='close')] 571 | ]), 572 | ) 573 | 574 | elif data == "channels": 575 | user = await client.get_users(OWNER_ID) 576 | user_link = f"https://t.me/{user.username}" if user.username else f"tg://openmessage?user_id={OWNER_ID}" 577 | ownername = f"{user.first_name}" if user.first_name else f"no name !" 578 | await query.edit_message_media( 579 | InputMediaPhoto("https://envs.sh/Wdj.jpg", 580 | CHANNELS_TXT 581 | ), 582 | reply_markup=InlineKeyboardMarkup([ 583 | [InlineKeyboardButton('• ʙᴀᴄᴋ', callback_data='start'), InlineKeyboardButton('home•', callback_data='setting')] 584 | ]), 585 | ) 586 | elif data in ["start", "home"]: 587 | inline_buttons = InlineKeyboardMarkup( 588 | [ 589 | [InlineKeyboardButton("• ᴀʙᴏᴜᴛ", callback_data="about"), 590 | InlineKeyboardButton("• ᴄʜᴀɴɴᴇʟs", callback_data="channels")], 591 | [InlineKeyboardButton("• Close •", callback_data="close")] 592 | ] 593 | ) 594 | try: 595 | await query.edit_message_media( 596 | InputMediaPhoto( 597 | START_PIC, 598 | START_MSG 599 | ), 600 | reply_markup=inline_buttons 601 | ) 602 | except Exception as e: 603 | print(f"Error sending start/home photo: {e}") 604 | await query.edit_message_text( 605 | START_MSG, 606 | reply_markup=inline_buttons, 607 | parse_mode=ParseMode.HTML 608 | ) 609 | 610 | 611 | elif data.startswith("rfs_ch_"): 612 | cid = int(data.split("_")[2]) 613 | try: 614 | chat = await client.get_chat(cid) 615 | mode = await db.get_channel_mode(cid) 616 | status = "🟢 ᴏɴ" if mode == "on" else "🔴 ᴏғғ" 617 | new_mode = "ᴏғғ" if mode == "on" else "on" 618 | buttons = [ 619 | [InlineKeyboardButton(f"ʀᴇǫ ᴍᴏᴅᴇ {'OFF' if mode == 'on' else 'ON'}", callback_data=f"rfs_toggle_{cid}_{new_mode}")], 620 | [InlineKeyboardButton("‹ ʙᴀᴄᴋ", callback_data="fsub_back")] 621 | ] 622 | await query.message.edit_text( 623 | f"Channel: {chat.title}\nCurrent Force-Sub Mode: {status}", 624 | reply_markup=InlineKeyboardMarkup(buttons) 625 | ) 626 | except Exception: 627 | await query.answer("Failed to fetch channel info", show_alert=True) 628 | 629 | elif data.startswith("rfs_toggle_"): 630 | cid, action = data.split("_")[2:] 631 | cid = int(cid) 632 | mode = "on" if action == "on" else "off" 633 | 634 | await db.set_channel_mode(cid, mode) 635 | await query.answer(f"Force-Sub set to {'ON' if mode == 'on' else 'OFF'}") 636 | 637 | # Refresh the same channel's mode view 638 | chat = await client.get_chat(cid) 639 | status = "🟢 ON" if mode == "on" else "🔴 OFF" 640 | new_mode = "off" if mode == "on" else "on" 641 | buttons = [ 642 | [InlineKeyboardButton(f"ʀᴇǫ ᴍᴏᴅᴇ {'OFF' if mode == 'on' else 'ON'}", callback_data=f"rfs_toggle_{cid}_{new_mode}")], 643 | [InlineKeyboardButton("‹ ʙᴀᴄᴋ", callback_data="fsub_back")] 644 | ] 645 | await query.message.edit_text( 646 | f"Channel: {chat.title}\nCurrent Force-Sub Mode: {status}", 647 | reply_markup=InlineKeyboardMarkup(buttons) 648 | ) 649 | 650 | elif data == "fsub_back": 651 | channels = await db.show_channels() 652 | buttons = [] 653 | for cid in channels: 654 | try: 655 | chat = await client.get_chat(cid) 656 | mode = await db.get_channel_mode(cid) 657 | status = "🟢" if mode == "on" else "🔴" 658 | buttons.append([InlineKeyboardButton(f"{status} {chat.title}", callback_data=f"rfs_ch_{cid}")]) 659 | except: 660 | continue 661 | 662 | await query.message.edit_text( 663 | "sᴇʟᴇᴄᴛ ᴀ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴛᴏɢɢʟᴇ ɪᴛs ғᴏʀᴄᴇ-sᴜʙ ᴍᴏᴅᴇ:", 664 | reply_markup=InlineKeyboardMarkup(buttons) 665 | ) 666 | 667 | def delete_after_delay(msg, delay): 668 | async def inner(): 669 | await asyncio.sleep(delay) 670 | try: 671 | await msg.delete() 672 | except: 673 | pass 674 | return inner() 675 | --------------------------------------------------------------------------------