├── .gitignore ├── Dockerfile ├── FilterBot ├── callback.py ├── commands.py ├── connections.py ├── database.py ├── filters.py ├── misc.py └── utils.py ├── LICENSE ├── Procfile ├── README.md ├── app.json ├── configs.py ├── docker-compose.yml ├── main.py ├── render.yaml ├── requirements.txt ├── routes.py ├── script.py └── variables /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster 2 | 3 | RUN apt update && apt upgrade -y 4 | RUN apt install git -y 5 | COPY requirements.txt /requirements.txt 6 | 7 | RUN cd / 8 | RUN pip3 install -U pip && pip3 install -U -r requirements.txt 9 | WORKDIR /Filter-Bot 10 | 11 | COPY . . 12 | 13 | CMD ["python3", "main.py"] 14 | -------------------------------------------------------------------------------- /FilterBot/callback.py: -------------------------------------------------------------------------------- 1 | import ast, pyrogram 2 | from pyrogram import filters, Client, enums 3 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery 4 | from FilterBot.database import db 5 | from configs import ADMINS 6 | 7 | @Client.on_callback_query(filters.regex("(close_data|delallconfirm|delallcancel|groupcb|connectcb|disconnect|deletecb|backcb|alertmessage)")) 8 | async def cb_handler(client: Client, query: CallbackQuery): 9 | if query.data == "close_data": 10 | await query.message.delete() 11 | elif query.data == "delallconfirm": 12 | userid = query.from_user.id 13 | chat_type = query.message.chat.type 14 | 15 | if chat_type == enums.ChatType.PRIVATE: 16 | grpid = await db.active_connection(str(userid)) 17 | if grpid is not None: 18 | grp_id = grpid 19 | try: 20 | chat = await client.get_chat(grpid) 21 | title = chat.title 22 | except: 23 | await query.message.edit_text("Make sure I'm present in your group!!", quote=True) 24 | return await query.answer('Piracy Is Crime') 25 | else: 26 | await query.message.edit_text( 27 | "I'm not connected to any groups!\nCheck /connections or connect to any groups", 28 | quote=True 29 | ) 30 | return await query.answer('Piracy Is Crime') 31 | 32 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 33 | grp_id = query.message.chat.id 34 | title = query.message.chat.title 35 | 36 | else: 37 | return await query.answer('Piracy Is Crime') 38 | 39 | st = await client.get_chat_member(grp_id, userid) 40 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 41 | await db.del_all(query.message, grp_id, title) 42 | else: 43 | await query.answer("You need to be Group Owner or an Auth User to do that!", show_alert=True) 44 | elif query.data == "delallcancel": 45 | userid = query.from_user.id 46 | chat_type = query.message.chat.type 47 | 48 | if chat_type == enums.ChatType.PRIVATE: 49 | await query.message.reply_to_message.delete() 50 | await query.message.delete() 51 | 52 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 53 | grp_id = query.message.chat.id 54 | st = await client.get_chat_member(grp_id, userid) 55 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 56 | await query.message.delete() 57 | try: 58 | await query.message.reply_to_message.delete() 59 | except: 60 | pass 61 | else: 62 | await query.answer("That's not for you!!", show_alert=True) 63 | elif "groupcb" in query.data: 64 | await query.answer() 65 | 66 | group_id = query.data.split(":")[1] 67 | 68 | act = query.data.split(":")[2] 69 | hr = await client.get_chat(int(group_id)) 70 | title = hr.title 71 | user_id = query.from_user.id 72 | 73 | if act == "": 74 | stat = "CONNECT" 75 | cb = "connectcb" 76 | else: 77 | stat = "DISCONNECT" 78 | cb = "disconnect" 79 | 80 | keyboard = InlineKeyboardMarkup([ 81 | [InlineKeyboardButton(f"{stat}", callback_data=f"{cb}:{group_id}"), 82 | InlineKeyboardButton("DELETE", callback_data=f"deletecb:{group_id}")], 83 | [InlineKeyboardButton("BACK", callback_data="backcb")] 84 | ]) 85 | 86 | await query.message.edit_text( 87 | f"Group Name : **{title}**\nGroup ID : `{group_id}`", 88 | reply_markup=keyboard, 89 | parse_mode=enums.ParseMode.MARKDOWN 90 | ) 91 | return await query.answer('Piracy Is Crime') 92 | elif "connectcb" in query.data: 93 | await query.answer() 94 | 95 | group_id = query.data.split(":")[1] 96 | 97 | hr = await client.get_chat(int(group_id)) 98 | 99 | title = hr.title 100 | 101 | user_id = query.from_user.id 102 | 103 | mkact = await db.make_active(str(user_id), str(group_id)) 104 | 105 | if mkact: 106 | await query.message.edit_text( 107 | f"Connected to **{title}**", 108 | parse_mode=enums.ParseMode.MARKDOWN 109 | ) 110 | else: 111 | await query.message.edit_text('Some error occurred!!', parse_mode=enums.ParseMode.MARKDOWN) 112 | return await query.answer('Piracy Is Crime') 113 | elif "disconnect" in query.data: 114 | await query.answer() 115 | 116 | group_id = query.data.split(":")[1] 117 | 118 | hr = await client.get_chat(int(group_id)) 119 | 120 | title = hr.title 121 | user_id = query.from_user.id 122 | 123 | mkinact = await db.make_inactive(str(user_id)) 124 | 125 | if mkinact: 126 | await query.message.edit_text( 127 | f"Disconnected from **{title}**", 128 | parse_mode=enums.ParseMode.MARKDOWN 129 | ) 130 | else: 131 | await query.message.edit_text( 132 | f"Some error occurred!!", 133 | parse_mode=enums.ParseMode.MARKDOWN 134 | ) 135 | return await query.answer('Piracy Is Crime') 136 | elif "deletecb" in query.data: 137 | await query.answer() 138 | 139 | user_id = query.from_user.id 140 | group_id = query.data.split(":")[1] 141 | 142 | delcon = await db.delete_connection(str(user_id), str(group_id)) 143 | 144 | if delcon: 145 | await query.message.edit_text( 146 | "Successfully deleted connection" 147 | ) 148 | else: 149 | await query.message.edit_text( 150 | f"Some error occurred!!", 151 | parse_mode=enums.ParseMode.MARKDOWN 152 | ) 153 | return await query.answer('Piracy Is Crime') 154 | elif query.data == "backcb": 155 | await query.answer() 156 | 157 | userid = query.from_user.id 158 | 159 | groupids = await db.all_connections(str(userid)) 160 | if groupids is None: 161 | await query.message.edit_text( 162 | "There are no active connections!! Connect to some groups first.", 163 | ) 164 | return await query.answer('Piracy Is Crime') 165 | buttons = [] 166 | for groupid in groupids: 167 | try: 168 | ttl = await client.get_chat(int(groupid)) 169 | title = ttl.title 170 | active = await db.if_active(str(userid), str(groupid)) 171 | act = " - ACTIVE" if active else "" 172 | buttons.append( 173 | [ 174 | InlineKeyboardButton( 175 | text=f"{title}{act}", callback_data=f"groupcb:{groupid}:{act}" 176 | ) 177 | ] 178 | ) 179 | except: 180 | pass 181 | if buttons: 182 | await query.message.edit_text( 183 | "Your connected group details ;\n\n", 184 | reply_markup=InlineKeyboardMarkup(buttons) 185 | ) 186 | elif "alertmessage" in query.data: 187 | grp_id = query.message.chat.id 188 | i = query.data.split(":")[1] 189 | keyword = query.data.split(":")[2] 190 | reply_text, btn, alerts, fileid = await db.find_filter(grp_id, keyword) 191 | if alerts is not None: 192 | alerts = ast.literal_eval(alerts) 193 | alert = alerts[int(i)] 194 | alert = alert.replace("\\n", "\n").replace("\\t", "\t") 195 | await query.answer(alert, show_alert=True) 196 | -------------------------------------------------------------------------------- /FilterBot/commands.py: -------------------------------------------------------------------------------- 1 | import random 2 | from pyrogram import Client as FilterBot, filters 3 | from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton 4 | from configs import BOT_PICS, StartTxT, HelpTxT, AboutTxT, LOGGER 5 | from FilterBot.database import db 6 | 7 | @FilterBot.on_message(filters.private & filters.command("start")) 8 | async def startCMD(client: FilterBot, message: Message): 9 | 10 | if not await db.is_user_exist(message.from_user.id): 11 | await db.add_user(message.from_user.first_name, message.from_user.id) 12 | 13 | bot = await client.get_me() 14 | keyboard = [[ 15 | InlineKeyboardButton('Add Me To Your Chat', url=f"https://t.me/{bot.username}?startgroup=true") 16 | ],[ 17 | InlineKeyboardButton('Help', callback_data='main#help'), 18 | InlineKeyboardButton('About', callback_data='main#about') 19 | ],[ 20 | InlineKeyboardButton('Update', url='t.me/learningbots79'), 21 | InlineKeyboardButton('Support', url='t.me/learning_bots') 22 | ]] 23 | 24 | if "motech" == BOT_PICS[0]: 25 | await message.reply_text(text=StartTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard)) 26 | else: 27 | await message.reply_photo(photo=random.choice(BOT_PICS), caption=StartTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard)) 28 | 29 | 30 | 31 | @FilterBot.on_message(filters.private & filters.command("help")) 32 | async def helpCMD(client: FilterBot, message: Message): 33 | 34 | if not await db.is_user_exist(message.from_user.id): 35 | await db.add_user(message.from_user.first_name, message.from_user.id) 36 | 37 | keyboard = [[ InlineKeyboardButton('Home', callback_data='main#start'), 38 | InlineKeyboardButton('Close', callback_data='main#close') ]] 39 | 40 | if "motech" == BOT_PICS[0]: 41 | await message.reply_text(text=HelpTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard)) 42 | else: 43 | await message.reply_photo(photo=random.choice(BOT_PICS), caption=HelpTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard)) 44 | 45 | @FilterBot.on_message(filters.private & filters.command("about")) 46 | async def aboutCMD(client: FilterBot, message: Message): 47 | 48 | if not await db.is_user_exist(message.from_user.id): 49 | await db.add_user(message.from_user.first_name, message.from_user.id) 50 | 51 | keyboard = [[ InlineKeyboardButton('Tutorial', url='https://youtu.be/vbloWcdxjxg'), 52 | InlineKeyboardButton('Repo', url='https://github.com/Learningbots79/BestFilterbot') ], 53 | [ InlineKeyboardButton('Home', callback_data='main#start'), 54 | InlineKeyboardButton('Help', callback_data='main#help') ]] 55 | 56 | if "motech" == BOT_PICS[0]: 57 | await message.reply_text(text=AboutTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard)) 58 | else: 59 | await message.reply_photo(photo=random.choice(BOT_PICS), caption=AboutTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard)) 60 | 61 | 62 | @FilterBot.on_callback_query(filters.regex('main')) 63 | async def maincallback(client: FilterBot, message): 64 | 65 | try: 66 | x, type = message.data.split("#") 67 | except: 68 | await message.answer("Erorrrr....") 69 | await message.message.delete() 70 | return 71 | 72 | if type == "start": 73 | bot = await client.get_me() 74 | keyboard = [[ InlineKeyboardButton('Add Me To Your Chat', url=f"t.me/{bot.username}?startgroup=true") ], 75 | [ InlineKeyboardButton('Help', callback_data='main#help'), 76 | InlineKeyboardButton('About', callback_data='main#about') ], 77 | [ InlineKeyboardButton('Update', url='t.me/learningbots79'), 78 | InlineKeyboardButton('Support', url='t.me/learning_bots') ]] 79 | await message.message.edit(text=StartTxT.format(mention=message.from_user.mention), reply_markup=InlineKeyboardMarkup(keyboard), disable_web_page_preview=True) 80 | 81 | elif type == "help": 82 | keyboard = [[ InlineKeyboardButton('Home', callback_data='main#start'), 83 | InlineKeyboardButton('Close', callback_data='main#close') ]] 84 | await message.message.edit(text=HelpTxT, reply_markup=InlineKeyboardMarkup(keyboard), disable_web_page_preview=True) 85 | 86 | elif type == "about": 87 | keyboard = [[ InlineKeyboardButton('Tutorial', url='https://youtu.be/vbloWcdxjxg'), 88 | InlineKeyboardButton('Repo', url='https://github.com/Learningbots79/BestFilterbot') ], 89 | [ InlineKeyboardButton('Home', callback_data='main#start'), 90 | InlineKeyboardButton('Help', callback_data='main#help') ]] 91 | await message.message.edit(text=AboutTxT, reply_markup=InlineKeyboardMarkup(keyboard), disable_web_page_preview=True) 92 | -------------------------------------------------------------------------------- /FilterBot/connections.py: -------------------------------------------------------------------------------- 1 | import logging, pyrogram 2 | from pyrogram import filters, Client, enums 3 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 4 | from FilterBot.database import db 5 | from configs import ADMINS 6 | 7 | logger = logging.getLogger(__name__) 8 | logger.setLevel(logging.ERROR) 9 | 10 | @Client.on_message((filters.private | filters.group) & filters.command('connect')) 11 | async def addconnection(client, message): 12 | userid = message.from_user.id if message.from_user else None 13 | if not userid: 14 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 15 | chat_type = message.chat.type 16 | 17 | if chat_type == enums.ChatType.PRIVATE: 18 | try: 19 | cmd, group_id = message.text.split(" ", 1) 20 | except: 21 | await message.reply_text( 22 | "Enter in correct format!\n\n" 23 | "/connect groupid\n\n" 24 | "Get your Group id by adding this bot to your group and use /id", 25 | quote=True 26 | ) 27 | return 28 | 29 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 30 | group_id = message.chat.id 31 | 32 | try: 33 | st = await client.get_chat_member(group_id, userid) 34 | if ( 35 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 36 | and st.status != enums.ChatMemberStatus.OWNER 37 | and userid not in ADMINS 38 | ): 39 | await message.reply_text("You should be an admin in Given group!", quote=True) 40 | return 41 | except Exception as e: 42 | logger.exception(e) 43 | await message.reply_text( 44 | "Invalid Group ID!\n\nIf correct, Make sure I'm present in your group!!", 45 | quote=True, 46 | ) 47 | 48 | return 49 | try: 50 | st = await client.get_chat_member(group_id, "me") 51 | if st.status == enums.ChatMemberStatus.ADMINISTRATOR: 52 | ttl = await client.get_chat(group_id) 53 | title = ttl.title 54 | 55 | addcon = await db.add_connection(str(group_id), str(userid)) 56 | if addcon: 57 | await message.reply_text( 58 | f"Successfully connected to **{title}**\nNow manage your group from my pm !", 59 | quote=True, 60 | parse_mode=enums.ParseMode.MARKDOWN 61 | ) 62 | if chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 63 | await client.send_message( 64 | userid, 65 | f"Connected to **{title}** !", 66 | parse_mode=enums.ParseMode.MARKDOWN 67 | ) 68 | else: 69 | await message.reply_text( 70 | "You're already connected to this chat!", 71 | quote=True 72 | ) 73 | else: 74 | await message.reply_text("Add me as an admin in group", quote=True) 75 | except Exception as e: 76 | logger.exception(e) 77 | await message.reply_text('Some error occurred! Try again later.', quote=True) 78 | return 79 | 80 | 81 | @Client.on_message((filters.private | filters.group) & filters.command('disconnect')) 82 | async def deleteconnection(client, message): 83 | userid = message.from_user.id if message.from_user else None 84 | if not userid: 85 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 86 | chat_type = message.chat.type 87 | 88 | if chat_type == enums.ChatType.PRIVATE: 89 | await message.reply_text("Run /connections to view or disconnect from groups!", quote=True) 90 | 91 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 92 | group_id = message.chat.id 93 | 94 | st = await client.get_chat_member(group_id, userid) 95 | if ( 96 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 97 | and st.status != enums.ChatMemberStatus.OWNER 98 | and str(userid) not in ADMINS 99 | ): 100 | return 101 | 102 | delcon = await db.delete_connection(str(userid), str(group_id)) 103 | if delcon: 104 | await message.reply_text("Successfully disconnected from this chat", quote=True) 105 | else: 106 | await message.reply_text("This chat isn't connected to me!\nDo /connect to connect.", quote=True) 107 | 108 | @Client.on_message(filters.private & filters.command(["connections"])) 109 | async def connections(client, message): 110 | userid = message.from_user.id 111 | 112 | groupids = await db.all_connections(str(userid)) 113 | if groupids is None: 114 | await message.reply_text( 115 | "There are no active connections!! Connect to some groups first.", 116 | quote=True 117 | ) 118 | return 119 | buttons = [] 120 | for groupid in groupids: 121 | try: 122 | ttl = await client.get_chat(int(groupid)) 123 | title = ttl.title 124 | active = await db.if_active(str(userid), str(groupid)) 125 | act = " - ACTIVE" if active else "" 126 | buttons.append( 127 | [ 128 | InlineKeyboardButton( 129 | text=f"{title}{act}", callback_data=f"groupcb:{groupid}:{act}" 130 | ) 131 | ] 132 | ) 133 | except: 134 | pass 135 | if buttons: 136 | await message.reply_text( 137 | "Your connected group details ;\n\n", 138 | reply_markup=InlineKeyboardMarkup(buttons), 139 | quote=True 140 | ) 141 | else: 142 | await message.reply_text( 143 | "There are no active connections!! Connect to some groups first.", 144 | quote=True 145 | ) 146 | -------------------------------------------------------------------------------- /FilterBot/database.py: -------------------------------------------------------------------------------- 1 | import pyrogram 2 | from pymongo import MongoClient 3 | from configs import DATABASE_URI, DATABASE_NAME 4 | 5 | class Database: 6 | 7 | def __init__(self): 8 | self.client0 = MongoClient(DATABASE_URI) 9 | self.client0 = self.client0[DATABASE_NAME] 10 | self.client1 = self.client0["FILTERS"] 11 | self.client2 = self.client0["CONNECTION"] 12 | self.client3 = self.client0["USERS"] 13 | 14 | def new_user(self, name, id): 15 | return dict(name=name, id=id, ban_status=dict(is_banned=False, ban_reason="")) 16 | 17 | # save new user 18 | async def add_user(self, name, id): 19 | user = self.new_user(name, id) 20 | self.client3.insert_one(user) 21 | 22 | # is user exist 23 | async def is_user_exist(self, id): 24 | data = {'id': int(id)} 25 | user = self.client3.find_one(data) 26 | return bool(user) 27 | 28 | # total number of save users 29 | async def total_users_count(self): 30 | count = self.client3.count() 31 | return count 32 | 33 | async def get_all_users(self): 34 | return self.client3.find() 35 | 36 | async def delete_user(self, id): 37 | await self.client3.delete_many({'id': id}) 38 | 39 | async def add_filter(self, grp_id, text, reply_text, btn, file, alert): 40 | mycol = self.client1[str(grp_id)] 41 | data = { 'text':str(text), 'reply':str(reply_text), 'btn':str(btn), 'file':str(file), 'alert':str(alert) } 42 | try: 43 | mycol.update_one({'text': str(text)}, {"$set": data}, upsert=True) 44 | except: 45 | print('Couldnt save, check your db') 46 | 47 | async def get_filters(self, group_id): 48 | mycol = self.client1[str(group_id)] 49 | 50 | texts = [] 51 | query = mycol.find() 52 | try: 53 | for file in query: 54 | text = file['text'] 55 | texts.append(text) 56 | except: 57 | pass 58 | return texts 59 | 60 | async def find_filter(self, group_id, name): 61 | mycol = self.client1[str(group_id)] 62 | 63 | query = mycol.find( {"text":name}) 64 | try: 65 | for file in query: 66 | reply_text = file['reply'] 67 | btn = file['btn'] 68 | fileid = file['file'] 69 | try: 70 | alert = file['alert'] 71 | except: 72 | alert = None 73 | return reply_text, btn, alert, fileid 74 | except: 75 | return None, None, None, None 76 | 77 | async def delete_filter(self, message, text, group_id): 78 | mycol = self.client1[str(group_id)] 79 | 80 | myquery = {'text':text } 81 | query = mycol.count_documents(myquery) 82 | if query == 1: 83 | mycol.delete_one(myquery) 84 | await message.reply_text(f"'`{text}`' deleted. I'll not respond to that filter anymore.", quote=True, parse_mode=pyrogram.enums.ParseMode.MARKDOWN) 85 | else: 86 | await message.reply_text("Couldn't find that filter!", quote=True) 87 | 88 | async def del_all(self, message, group_id, title): 89 | if str(group_id) not in self.client1.list_collection_names(): 90 | await message.edit_text(f"Nothing to remove in {title}!") 91 | return 92 | 93 | mycol = self.client1[str(group_id)] 94 | try: 95 | mycol.drop() 96 | await message.edit_text(f"All filters from {title} has been removed") 97 | except: 98 | await message.edit_text(f"Couldn't remove all filters from group!") 99 | return 100 | 101 | async def count_filters(self, group_id): 102 | mycol = self.client1[str(group_id)] 103 | 104 | count = mycol.count() 105 | if count == 0: 106 | return False 107 | else: 108 | return count 109 | 110 | async def filter_stats(self): 111 | collections = self.client1.list_collection_names() 112 | 113 | if "CONNECTION" in collections: 114 | collections.remove("CONNECTION") 115 | 116 | totalcount = 0 117 | for collection in collections: 118 | mycol = mydb[collection] 119 | count = mycol.count() 120 | totalcount = totalcount + count 121 | 122 | totalcollections = len(collections) 123 | 124 | return totalcollections, totalcount 125 | 126 | 127 | async def add_connection(self, group_id, user_id): 128 | query = self.client2.find_one({ "_id": user_id }, { "_id": 0, "active_group": 0 }) 129 | if query is not None: 130 | group_ids = [] 131 | for x in query["group_details"]: 132 | group_ids.append(x["group_id"]) 133 | 134 | if group_id in group_ids: 135 | return False 136 | 137 | group_details = {"group_id" : group_id} 138 | 139 | data = {'_id': user_id, 'group_details' : [group_details], 'active_group' : group_id} 140 | 141 | if self.client2.count_documents( {"_id": user_id} ) == 0: 142 | try: 143 | self.client2.insert_one(data) 144 | return True 145 | except: 146 | print('Some error occured!') 147 | 148 | else: 149 | try: 150 | self.client2.update_one({'_id': user_id}, { "$push": {"group_details": group_details}, "$set": {"active_group" : group_id} }) 151 | return True 152 | except: 153 | print('Some error occured!') 154 | 155 | async def active_connection(self, user_id): 156 | query = self.client2.find_one({ "_id": user_id }, { "_id": 0, "group_details": 0 }) 157 | if query: 158 | group_id = query['active_group'] 159 | if group_id != None: 160 | return int(group_id) 161 | else: 162 | return None 163 | else: 164 | return None 165 | 166 | 167 | async def all_connections(self, user_id): 168 | query = self.client2.find_one({ "_id": user_id }, { "_id": 0, "active_group": 0 }) 169 | if query is not None: 170 | group_ids = [] 171 | for x in query["group_details"]: 172 | group_ids.append(x["group_id"]) 173 | return group_ids 174 | else: 175 | return None 176 | 177 | 178 | async def if_active(self, user_id, group_id): 179 | query = self.client2.find_one({ "_id": user_id }, { "_id": 0, "group_details": 0 }) 180 | if query is not None: 181 | if query['active_group'] == group_id: 182 | return True 183 | else: 184 | return False 185 | else: 186 | return False 187 | 188 | 189 | async def make_active(self, user_id, group_id): 190 | update = self.client2.update_one({'_id': user_id}, {"$set": {"active_group" : group_id}}) 191 | if update.modified_count == 0: 192 | return False 193 | else: 194 | return True 195 | 196 | async def make_inactive(self, user_id): 197 | update = self.client2.update_one({'_id': user_id}, {"$set": {"active_group" : None}}) 198 | if update.modified_count == 0: 199 | return False 200 | else: 201 | return True 202 | 203 | async def delete_connection(self, user_id, group_id): 204 | 205 | try: 206 | update = self.client2.update_one({"_id": user_id}, {"$pull" : { "group_details" : {"group_id":group_id} } }) 207 | if update.modified_count == 0: 208 | return False 209 | else: 210 | query = self.client2.find_one({ "_id": user_id }, { "_id": 0 }) 211 | if len(query["group_details"]) >= 1: 212 | if query['active_group'] == group_id: 213 | prvs_group_id = query["group_details"][len(query["group_details"]) - 1]["group_id"] 214 | 215 | mycol.update_one({'_id': user_id},{"$set": {"active_group" : prvs_group_id}}) 216 | else: 217 | mycol.update_one({'_id': user_id}, {"$set": {"active_group" : None}}) 218 | return True 219 | except Exception as e: 220 | print(e) 221 | return False 222 | 223 | db = Database() 224 | -------------------------------------------------------------------------------- /FilterBot/filters.py: -------------------------------------------------------------------------------- 1 | import io, re, pyrogram 2 | from pyrogram import filters, Client, enums 3 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 4 | from FilterBot.database import db 5 | from FilterBot.utils import get_file_id, parser, split_quotes 6 | from configs import ADMINS, ADD_FILTER_CMD, DELETE_FILTER_CMD, DELETE_ALL_CMD, AUTO_DELETE, AUTO_DELETE_SECOND 7 | 8 | @Client.on_message(filters.command(ADD_FILTER_CMD) & filters.incoming) 9 | async def addfilter(client, message): 10 | 11 | userid = message.from_user.id if message.from_user else None 12 | if not userid: 13 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 14 | 15 | chat_type = message.chat.type 16 | args = message.text.html.split(None, 1) 17 | 18 | if chat_type == enums.ChatType.PRIVATE: 19 | grpid = await db.active_connection(str(userid)) 20 | if grpid is not None: 21 | grp_id = grpid 22 | try: 23 | chat = await client.get_chat(grpid) 24 | title = chat.title 25 | except: 26 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 27 | return 28 | else: 29 | await message.reply_text("I'm not connected to any groups!", quote=True) 30 | return 31 | 32 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 33 | grp_id = message.chat.id 34 | title = message.chat.title 35 | 36 | else: 37 | return 38 | 39 | st = await client.get_chat_member(grp_id, userid) 40 | if ( 41 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 42 | and st.status != enums.ChatMemberStatus.OWNER 43 | and str(userid) not in ADMINS 44 | ): 45 | return 46 | 47 | 48 | if len(args) < 2: 49 | await message.reply_text("Command Incomplete :(", quote=True) 50 | return 51 | 52 | extracted = split_quotes(args[1]) 53 | text = extracted[0].lower() 54 | 55 | if not message.reply_to_message and len(extracted) < 2: 56 | await message.reply_text("Add some content to save your filter!", quote=True) 57 | return 58 | 59 | if (len(extracted) >= 2) and not message.reply_to_message: 60 | reply_text, btn, alert = parser(extracted[1], text) 61 | fileid = None 62 | if not reply_text: 63 | await message.reply_text("You cannot have buttons alone, give some text to go with it!", quote=True) 64 | return 65 | 66 | elif message.reply_to_message and message.reply_to_message.reply_markup: 67 | try: 68 | rm = message.reply_to_message.reply_markup 69 | btn = rm.inline_keyboard 70 | msg = get_file_id(message.reply_to_message) 71 | if msg: 72 | fileid = msg.file_id 73 | reply_text = message.reply_to_message.caption.html 74 | else: 75 | reply_text = message.reply_to_message.text.html 76 | fileid = None 77 | alert = None 78 | except: 79 | reply_text = "" 80 | btn = "[]" 81 | fileid = None 82 | alert = None 83 | 84 | elif message.reply_to_message and message.reply_to_message.media: 85 | try: 86 | msg = get_file_id(message.reply_to_message) 87 | fileid = msg.file_id if msg else None 88 | reply_text, btn, alert = parser(extracted[1], text) if message.reply_to_message.sticker else parser(message.reply_to_message.caption.html, text) 89 | except: 90 | reply_text = "" 91 | btn = "[]" 92 | alert = None 93 | elif message.reply_to_message and message.reply_to_message.text: 94 | try: 95 | fileid = None 96 | reply_text, btn, alert = parser(message.reply_to_message.text.html, text) 97 | except: 98 | reply_text = "" 99 | btn = "[]" 100 | alert = None 101 | else: 102 | return 103 | 104 | await db.add_filter(grp_id, text, reply_text, btn, fileid, alert) 105 | 106 | await message.reply_text(f"Filter for `{text}` added in **{title}**", quote=True, parse_mode=enums.ParseMode.MARKDOWN) 107 | 108 | @Client.on_message(filters.command(['viewfilters', 'filters']) & filters.incoming) 109 | async def get_all(client, message): 110 | 111 | chat_type = message.chat.type 112 | userid = message.from_user.id if message.from_user else None 113 | if not userid: 114 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 115 | if chat_type == enums.ChatType.PRIVATE: 116 | grpid = await db.active_connection(str(userid)) 117 | if grpid is not None: 118 | grp_id = grpid 119 | try: 120 | chat = await client.get_chat(grpid) 121 | title = chat.title 122 | except: 123 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 124 | return 125 | else: 126 | await message.reply_text("I'm not connected to any groups!", quote=True) 127 | return 128 | 129 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 130 | grp_id = message.chat.id 131 | title = message.chat.title 132 | 133 | else: 134 | return 135 | 136 | st = await client.get_chat_member(grp_id, userid) 137 | if ( 138 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 139 | and st.status != enums.ChatMemberStatus.OWNER 140 | and str(userid) not in ADMINS 141 | ): 142 | return 143 | 144 | texts = await db.get_filters(grp_id) 145 | count = await db.count_filters(grp_id) 146 | if count: 147 | filterlist = f"Total number of filters in **{title}** : {count}\n\n" 148 | 149 | for text in texts: 150 | keywords = " × `{}`\n".format(text) 151 | 152 | filterlist += keywords 153 | 154 | if len(filterlist) > 4096: 155 | with io.BytesIO(str.encode(filterlist.replace("`", ""))) as keyword_file: 156 | keyword_file.name = "keywords.txt" 157 | await message.reply_document( 158 | document=keyword_file, 159 | quote=True 160 | ) 161 | return 162 | else: 163 | filterlist = f"There are no active filters in **{title}**" 164 | 165 | await message.reply_text(text=filterlist, quote=True, parse_mode=enums.ParseMode.MARKDOWN) 166 | 167 | @Client.on_message(filters.command(DELETE_FILTER_CMD) & filters.incoming) 168 | async def deletefilter(client, message): 169 | userid = message.from_user.id if message.from_user else None 170 | if not userid: 171 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 172 | chat_type = message.chat.type 173 | 174 | if chat_type == enums.ChatType.PRIVATE: 175 | grpid = await db.active_connection(str(userid)) 176 | if grpid is not None: 177 | grp_id = grpid 178 | try: 179 | chat = await client.get_chat(grpid) 180 | title = chat.title 181 | except: 182 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 183 | return 184 | else: 185 | await message.reply_text("I'm not connected to any groups!", quote=True) 186 | return 187 | 188 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 189 | grp_id = message.chat.id 190 | title = message.chat.title 191 | 192 | else: 193 | return 194 | 195 | st = await client.get_chat_member(grp_id, userid) 196 | if ( 197 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 198 | and st.status != enums.ChatMemberStatus.OWNER 199 | and str(userid) not in ADMINS 200 | ): 201 | return 202 | 203 | try: 204 | cmd, text = message.text.split(" ", 1) 205 | except: 206 | await message.reply_text( 207 | "Mention the filtername which you wanna delete!\n\n" 208 | "/del filtername\n\n" 209 | "Use /viewfilters to view all available filters", 210 | quote=True 211 | ) 212 | return 213 | 214 | query = text.lower() 215 | 216 | await db.delete_filter(message, query, grp_id) 217 | 218 | 219 | @Client.on_message(filters.command(DELETE_ALL_CMD) & filters.incoming) 220 | async def delallconfirm(client, message): 221 | userid = message.from_user.id if message.from_user else None 222 | if not userid: 223 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 224 | chat_type = message.chat.type 225 | 226 | if chat_type == enums.ChatType.PRIVATE: 227 | grpid = await db.active_connection(str(userid)) 228 | if grpid is not None: 229 | grp_id = grpid 230 | try: 231 | chat = await client.get_chat(grpid) 232 | title = chat.title 233 | except: 234 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 235 | return 236 | else: 237 | await message.reply_text("I'm not connected to any groups!", quote=True) 238 | return 239 | 240 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 241 | grp_id = message.chat.id 242 | title = message.chat.title 243 | 244 | else: 245 | return 246 | 247 | 248 | st = await client.get_chat_member(grp_id, userid) 249 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 250 | await message.reply_text( 251 | f"This will delete all filters from '{title}'.\nDo you want to continue??", 252 | reply_markup=InlineKeyboardMarkup([ 253 | [InlineKeyboardButton(text="YES",callback_data="delallconfirm")], 254 | [InlineKeyboardButton(text="CANCEL",callback_data="delallcancel")] 255 | ]), 256 | quote=True 257 | ) 258 | 259 | 260 | @Client.on_message(filters.group & filters.text) 261 | async def give_filter(client,message): 262 | group_id = message.chat.id 263 | name = message.text 264 | reply_id = message.reply_to_message.id if message.reply_to_message else message.id 265 | 266 | keywords = await db.get_filters(group_id) 267 | for keyword in reversed(sorted(keywords, key=len)): 268 | pattern = r"( |^|[^\w])" + re.escape(keyword) + r"( |$|[^\w])" 269 | if re.search(pattern, name, flags=re.IGNORECASE): 270 | reply_text, btn, alert, fileid = await db.find_filter(group_id, keyword) 271 | 272 | if reply_text: 273 | reply_text = reply_text.replace("\\n", "\n").replace("\\t", "\t") 274 | 275 | 276 | if btn is not None: 277 | try: 278 | if fileid == "None": 279 | if btn == "[]": 280 | if AUTO_DELETE: 281 | delete = await client.send_message(group_id, reply_text, disable_web_page_preview=True, reply_to_message_id=reply_id) 282 | await asyncio.sleep(AUTO_DELETE_SECOND) 283 | await delete.delete() 284 | else: 285 | await client.send_message(group_id, reply_text, disable_web_page_preview=True, reply_to_message_id=reply_id) 286 | 287 | else: 288 | if AUTO_DELETE: 289 | button = eval(btn) 290 | delete = await client.send_message( 291 | group_id, 292 | reply_text, 293 | disable_web_page_preview=True, 294 | reply_markup=InlineKeyboardMarkup(button), 295 | reply_to_message_id=reply_id 296 | ) 297 | await asyncio.sleep(AUTO_DELETE_SECOND) 298 | await delete.delete() 299 | else: 300 | button = eval(btn) 301 | await client.send_message( 302 | group_id, 303 | reply_text, 304 | disable_web_page_preview=True, 305 | reply_markup=InlineKeyboardMarkup(button), 306 | reply_to_message_id=reply_id 307 | ) 308 | elif btn == "[]": 309 | if AUTO_DELETE: 310 | delete = await client.send_cached_media( 311 | group_id, 312 | fileid, 313 | caption=reply_text or "", 314 | reply_to_message_id=reply_id 315 | ) 316 | await asyncio.sleep(AUTO_DELETE_SECOND) 317 | await delete.delete() 318 | else: 319 | await client.send_cached_media( 320 | group_id, 321 | fileid, 322 | caption=reply_text or "", 323 | reply_to_message_id=reply_id 324 | ) 325 | 326 | else: 327 | if AUTO_DELETE: 328 | button = eval(btn) 329 | await message.reply_cached_media( 330 | fileid, 331 | caption=reply_text or "", 332 | reply_markup=InlineKeyboardMarkup(button), 333 | reply_to_message_id=reply_id 334 | ) 335 | else: 336 | button = eval(btn) 337 | await message.reply_cached_media( 338 | fileid, 339 | caption=reply_text or "", 340 | reply_markup=InlineKeyboardMarkup(button), 341 | reply_to_message_id=reply_id 342 | ) 343 | except Exception as e: 344 | print(e) 345 | break 346 | -------------------------------------------------------------------------------- /FilterBot/misc.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pyrogram import Client, filters, enums 3 | from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant, MediaEmpty, PhotoInvalidDimensions, WebpageMediaEmpty 4 | from FilterBot.utils import extract_user, get_file_id, last_online 5 | import time 6 | from datetime import datetime 7 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 8 | import logging 9 | logger = logging.getLogger(__name__) 10 | logger.setLevel(logging.ERROR) 11 | 12 | @Client.on_message(filters.command('id')) 13 | async def showid(client, message): 14 | chat_type = message.chat.type 15 | if chat_type == enums.ChatType.PRIVATE: 16 | user_id = message.chat.id 17 | first = message.from_user.first_name 18 | last = message.from_user.last_name or "" 19 | username = message.from_user.username 20 | dc_id = message.from_user.dc_id or "" 21 | await message.reply_text( 22 | f"➲ First Name: {first}\n➲ Last Name: {last}\n➲ Username: {username}\n➲ Telegram ID: {user_id}\n➲ Data Centre: {dc_id}", 23 | quote=True 24 | ) 25 | 26 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 27 | _id = "" 28 | _id += ( 29 | "➲ Chat ID: " 30 | f"{message.chat.id}\n" 31 | ) 32 | if message.reply_to_message: 33 | _id += ( 34 | "➲ User ID: " 35 | f"{message.from_user.id if message.from_user else 'Anonymous'}\n" 36 | "➲ Replied User ID: " 37 | f"{message.reply_to_message.from_user.id if message.reply_to_message.from_user else 'Anonymous'}\n" 38 | ) 39 | file_info = get_file_id(message.reply_to_message) 40 | else: 41 | _id += ( 42 | "➲ User ID: " 43 | f"{message.from_user.id if message.from_user else 'Anonymous'}\n" 44 | ) 45 | file_info = get_file_id(message) 46 | if file_info: 47 | _id += ( 48 | f"{file_info.message_type}: " 49 | f"{file_info.file_id}\n" 50 | ) 51 | await message.reply_text( 52 | _id, 53 | quote=True 54 | ) 55 | 56 | @Client.on_message(filters.command(["info"])) 57 | async def who_is(client, message): 58 | # https://github.com/SpEcHiDe/PyroGramBot/blob/master/pyrobot/plugins/admemes/whois.py#L19 59 | status_message = await message.reply_text( 60 | "`Fetching user info...`" 61 | ) 62 | await status_message.edit( 63 | "`Processing user info...`" 64 | ) 65 | from_user = None 66 | from_user_id, _ = extract_user(message) 67 | try: 68 | from_user = await client.get_users(from_user_id) 69 | except Exception as error: 70 | await status_message.edit(str(error)) 71 | return 72 | if from_user is None: 73 | return await status_message.edit("no valid user_id / message specified") 74 | message_out_str = "" 75 | message_out_str += f"➲First Name: {from_user.first_name}\n" 76 | last_name = from_user.last_name or "None" 77 | message_out_str += f"➲Last Name: {last_name}\n" 78 | message_out_str += f"➲Telegram ID: {from_user.id}\n" 79 | username = from_user.username or "None" 80 | dc_id = from_user.dc_id or "[User Doesn't Have A Valid DP]" 81 | message_out_str += f"➲Data Centre: {dc_id}\n" 82 | message_out_str += f"➲User Name: @{username}\n" 83 | message_out_str += f"➲User 𝖫𝗂𝗇𝗄: Click Here\n" 84 | if message.chat.type in ((enums.ChatType.SUPERGROUP, enums.ChatType.CHANNEL)): 85 | try: 86 | chat_member_p = await message.chat.get_member(from_user.id) 87 | joined_date = ( 88 | chat_member_p.joined_date or datetime.now() 89 | ).strftime("%Y.%m.%d %H:%M:%S") 90 | message_out_str += ( 91 | "➲Joined this Chat on: " 92 | f"{joined_date}" 93 | "\n" 94 | ) 95 | except UserNotParticipant: 96 | pass 97 | chat_photo = from_user.photo 98 | if chat_photo: 99 | local_user_photo = await client.download_media( 100 | message=chat_photo.big_file_id 101 | ) 102 | buttons = [[ 103 | InlineKeyboardButton('🔐 Close', callback_data='close_data') 104 | ]] 105 | reply_markup = InlineKeyboardMarkup(buttons) 106 | await message.reply_photo( 107 | photo=local_user_photo, 108 | quote=True, 109 | reply_markup=reply_markup, 110 | caption=message_out_str, 111 | parse_mode=enums.ParseMode.HTML, 112 | disable_notification=True 113 | ) 114 | os.remove(local_user_photo) 115 | else: 116 | buttons = [[ 117 | InlineKeyboardButton('🔐 Close', callback_data='close_data') 118 | ]] 119 | reply_markup = InlineKeyboardMarkup(buttons) 120 | await message.reply_text( 121 | text=message_out_str, 122 | reply_markup=reply_markup, 123 | quote=True, 124 | parse_mode=enums.ParseMode.HTML, 125 | disable_notification=True 126 | ) 127 | await status_message.delete() 128 | -------------------------------------------------------------------------------- /FilterBot/utils.py: -------------------------------------------------------------------------------- 1 | import re, os 2 | from datetime import datetime 3 | from typing import List, Union 4 | from pyrogram import enums 5 | from pyrogram.types import InlineKeyboardButton, Message 6 | 7 | BTN_URL_REGEX = re.compile( 8 | r"(\[([^\[]+?)\]\((buttonurl|buttonalert):(?:/{0,2})(.+?)(:same)?\))" 9 | ) 10 | 11 | SMART_OPEN = '“' 12 | SMART_CLOSE = '”' 13 | START_CHAR = ('\'', '"', SMART_OPEN) 14 | 15 | def get_file_id(msg: Message): 16 | if msg.media: 17 | for message_type in ( 18 | "photo", 19 | "animation", 20 | "audio", 21 | "document", 22 | "video", 23 | "video_note", 24 | "voice", 25 | "sticker" 26 | ): 27 | obj = getattr(msg, message_type) 28 | if obj: 29 | setattr(obj, "message_type", message_type) 30 | return obj 31 | 32 | def split_quotes(text: str) -> List: 33 | if any(text.startswith(char) for char in START_CHAR): 34 | counter = 1 # ignore first char -> is some kind of quote 35 | while counter < len(text): 36 | if text[counter] == "\\": 37 | counter += 1 38 | elif text[counter] == text[0] or (text[0] == SMART_OPEN and text[counter] == SMART_CLOSE): 39 | break 40 | counter += 1 41 | else: 42 | return text.split(None, 1) 43 | 44 | # 1 to avoid starting quote, and counter is exclusive so avoids ending 45 | key = remove_escapes(text[1:counter].strip()) 46 | # index will be in range, or `else` would have been executed and returned 47 | rest = text[counter + 1:].strip() 48 | if not key: 49 | key = text[0] + text[0] 50 | return list(filter(None, [key, rest])) 51 | else: 52 | return text.split(None, 1) 53 | 54 | def parser(text, keyword): 55 | if "buttonalert" in text: 56 | text = (text.replace("\n", "\\n").replace("\t", "\\t")) 57 | buttons = [] 58 | note_data = "" 59 | prev = 0 60 | i = 0 61 | alerts = [] 62 | for match in BTN_URL_REGEX.finditer(text): 63 | # Check if btnurl is escaped 64 | n_escapes = 0 65 | to_check = match.start(1) - 1 66 | while to_check > 0 and text[to_check] == "\\": 67 | n_escapes += 1 68 | to_check -= 1 69 | 70 | # if even, not escaped -> create button 71 | if n_escapes % 2 == 0: 72 | note_data += text[prev:match.start(1)] 73 | prev = match.end(1) 74 | if match.group(3) == "buttonalert": 75 | # create a thruple with button label, url, and newline status 76 | if bool(match.group(5)) and buttons: 77 | buttons[-1].append(InlineKeyboardButton( 78 | text=match.group(2), 79 | callback_data=f"alertmessage:{i}:{keyword}" 80 | )) 81 | else: 82 | buttons.append([InlineKeyboardButton( 83 | text=match.group(2), 84 | callback_data=f"alertmessage:{i}:{keyword}" 85 | )]) 86 | i = i + 1 87 | alerts.append(match.group(4)) 88 | else: 89 | if bool(match.group(5)) and buttons: 90 | buttons[-1].append(InlineKeyboardButton( 91 | text=match.group(2), 92 | url=match.group(4).replace(" ", "") 93 | )) 94 | else: 95 | buttons.append([InlineKeyboardButton( 96 | text=match.group(2), 97 | url=match.group(4).replace(" ", "") 98 | )]) 99 | 100 | # if odd, escaped -> move along 101 | else: 102 | note_data += text[prev:to_check] 103 | prev = match.start(1) - 1 104 | else: 105 | note_data += text[prev:] 106 | 107 | try: 108 | return note_data, buttons, alerts 109 | except: 110 | return note_data, buttons, None 111 | 112 | def remove_escapes(text: str) -> str: 113 | counter = 0 114 | res = "" 115 | is_escaped = False 116 | while counter < len(text): 117 | if is_escaped: 118 | res += text[counter] 119 | is_escaped = False 120 | elif text[counter] == "\\": 121 | is_escaped = True 122 | else: 123 | res += text[counter] 124 | counter += 1 125 | return res 126 | 127 | def get_file_id(msg: Message): 128 | if msg.media: 129 | for message_type in ( 130 | "photo", 131 | "animation", 132 | "audio", 133 | "document", 134 | "video", 135 | "video_note", 136 | "voice", 137 | "sticker" 138 | ): 139 | obj = getattr(msg, message_type) 140 | if obj: 141 | setattr(obj, "message_type", message_type) 142 | return obj 143 | 144 | def extract_user(message: Message) -> Union[int, str]: 145 | """extracts the user from a message""" 146 | # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7 147 | user_id = None 148 | user_first_name = None 149 | if message.reply_to_message: 150 | user_id = message.reply_to_message.from_user.id 151 | user_first_name = message.reply_to_message.from_user.first_name 152 | 153 | elif len(message.command) > 1: 154 | if ( 155 | len(message.entities) > 1 and 156 | message.entities[1].type == enums.MessageEntityType.TEXT_MENTION 157 | ): 158 | 159 | required_entity = message.entities[1] 160 | user_id = required_entity.user.id 161 | user_first_name = required_entity.user.first_name 162 | else: 163 | user_id = message.command[1] 164 | # don't want to make a request -_- 165 | user_first_name = user_id 166 | try: 167 | user_id = int(user_id) 168 | except ValueError: 169 | pass 170 | else: 171 | user_id = message.from_user.id 172 | user_first_name = message.from_user.first_name 173 | return (user_id, user_first_name) 174 | 175 | def last_online(from_user): 176 | time = "" 177 | if from_user.is_bot: 178 | time += "🤖 Bot :(" 179 | elif from_user.status == enums.UserStatus.RECENTLY: 180 | time += "Recently" 181 | elif from_user.status == enums.UserStatus.LAST_WEEK: 182 | time += "Within the last week" 183 | elif from_user.status == enums.UserStatus.LAST_MONTH: 184 | time += "Within the last month" 185 | elif from_user.status == enums.UserStatus.LONG_AGO: 186 | time += "A long time ago :(" 187 | elif from_user.status == enums.UserStatus.ONLINE: 188 | time += "Currently Online" 189 | elif from_user.status == enums.UserStatus.OFFLINE: 190 | time += from_user.last_online_date.strftime("%a, %d %b %Y, %H:%M:%S") 191 | return time 192 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Muhammed 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 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python3 main.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Filter-Bot 2 | 3 | ![github card](https://github-readme-stats.vercel.app/api/pin/?username=PR0FESS0R-99&repo=FilterBot&theme=dark) 4 | 5 | - [x] An advanced Telegram Filter Bot with nearly unlimitted filters! 6 | 7 | - [x] Nearly unlimited filters.Supports all type of filters (Including Alert Button Filter). 8 | 9 | - [x] Can save button filters directly (Rose Bot Feature). 10 | 11 | - [x] Supports multiple PM connections. 12 | 13 | - [x] And all other features of a Filter Bot :D. 14 | 15 | - [x] broadcasting users 16 | 17 | - [x] force subscribers 18 | 19 | - [x] AutoDelete Filters. 20 | 21 | ### How to use the bot 22 | 23 |

Add bot to your group with admin rights.

24 | 25 |

Add your filters :)

26 | 27 | 28 | ### Bot Commands 29 | 30 | > Filter Commands 31 | 32 | - `/filter ` - To add your filter. You can also reply to your content with /filter command. 33 | 34 | - `/stop ` - Delete your filter. 35 | 36 | - `/stopall` - Delete all filters from group. (Group Owner Only!). 37 | 38 | - `/filters` - List all filters in chat. 39 | 40 | > Connection Commands 41 | 42 | - `/connect groupid` - Connects your group to PM. You can also simply use, /connect in groups. 43 | 44 | - `/connections` - Manage your connections. (only in PM) 45 | 46 | > Broadcasting Commands 47 | 48 | - `/broadcast` - reply any message 49 | 50 | - `/status` - users status 51 | 52 | > Misc Commands 53 | 54 | - `/id` - Shows ID information 55 | 56 | - `/info ` - Shows User Information. Also use /info as reply to some message for their details! 57 | 58 | ### Variables 59 | 60 | Read [this](https://github.com/PR0FESS0R-99/Filter-Bot/blob/Professor-99/variables.py) before you start messing up with your edits. 61 | 62 | > Required Variables.. 63 | 64 | - `API_ID` : Get this value from [here tutorial](https://youtu.be/F45N32GCyMo) 65 | 66 | - `API_HASH` : Get this value from [here tutorial](https://youtu.be/F45N32GCyMo) 67 | 68 | - `BOT_TOKEN` : Create a bot using [@BotFather](https://telegram.dog/BotFather), and get the Telegram API token. 69 | 70 | - `ADMINS` : Username or ID of Admin. Separate multiple Admins by space 71 | 72 | - `DATABASE_URI` : mongoDB URI. Get this value from mongoDB. For more help watch this [video](https://youtu.be/mD9veNL7KoE) 73 | 74 | - `DATABASE_NAME` : mongoDB Name. Get this value from mongoDB. For more help watch this [video](https://youtu.be/mD9veNL7KoE) 75 | 76 | - `PICS` : Telegraph links of images to show in start message.( Multiple images can be used separated by space ) 77 | 78 | > Optional Variables.. 79 | 80 | - `ADD_FILTER_CMD` : default will be 'filter' 81 | 82 | - `DELETE_FILTER_CMD` : default will be 'stop' 83 | 84 | - `DELETE_ALL_CMD` : default will be 'stopall' 85 | 86 | - `CONNECT_COMMAND` : default will be 'connect' 87 | 88 | - `DISCONNECT_COMMAND` : default will be 'disconnect' 89 | 90 | ### Credits 91 | 92 | > Thanks to [pyrogram](https://github.com/pyrogram/pyrogram) 93 | 94 | > Thanks to [TroJanzHEX](https://github.com/TroJanzHEX/Unlimited-Filter-Bot) 95 | 96 | > Thanks to [MoTech](https://github.com/PR0FESS0R-99/MoTech) 97 | 98 | ### Deploy to 99 | 100 |
Deploy To Rander 101 |
102 |

103 | Watch Deploying Tutorial... 104 |

105 | 106 |

107 | Deploy 108 |

109 |
110 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UNLIMITED FILTER BOT", 3 | "description": "An unlimited filter bot using Mongo DB", 4 | "logo": "https://telegra.ph/file/71aaa621508cd696ab538.jpg", 5 | "keywords": [ 6 | "UNLIMITED", 7 | "Filter", 8 | "Telegram Bot", 9 | "TroJanzHEX" 10 | ], 11 | "website": "https://TroJanzHEX.me", 12 | "repository": "https://github.com/TroJanzHEX/Unlimited-Filter-Bot", 13 | "success_url": "https://telegram.dog/TroJanzHEX", 14 | "env": { 15 | "WEBHOOK": { 16 | "description": "No need to change this field!", 17 | "value": "ANYTHING" 18 | }, 19 | "TG_BOT_TOKEN": { 20 | "description": "Your Telegram Bot Token from @BotFather", 21 | "value": "" 22 | }, 23 | "API_HASH": { 24 | "description": "Your API Hash from my.telegram.org or @UseTGXBot", 25 | "value": "" 26 | }, 27 | "API_ID": { 28 | "description": "Your APP ID from my.telegram.org or @UseTGXBot", 29 | "value": "" 30 | }, 31 | "AUTH_USERS": { 32 | "description": "ID of users that can use the bot commands", 33 | "value": "" 34 | }, 35 | "DATABASE_URI": { 36 | "description": "Database URL from https://cloud.mongodb.com/", 37 | "value": "" 38 | }, 39 | "DATABASE_NAME": { 40 | "description": "Your database name from mongoDB. ( Default will 'Cluster0' )", 41 | "value": "Cluster0", 42 | "required": false 43 | }, 44 | "SAVE_USER": { 45 | "description": "Do you need to save user details? Usefull for getting userinfo and total user counts. May reduce filter capacity. Give yes or no", 46 | "value": "no", 47 | "required": false 48 | }, 49 | "HEROKU_API_KEY": { 50 | "description": "To check dyno status. Go to https://dashboard.heroku.com/account, scroll down and press Reveal API", 51 | "value": "None", 52 | "required": false 53 | } 54 | }, 55 | "buildpacks": [ 56 | { 57 | "url": "heroku/python" 58 | } 59 | ], 60 | "formation": { 61 | "worker": { 62 | "quantity": 1, 63 | "size": "free" 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /configs.py: -------------------------------------------------------------------------------- 1 | from os import environ 2 | from logging import basicConfig, INFO, StreamHandler, getLogger, WARNING, Logger 3 | from logging.handlers import RotatingFileHandler 4 | from script import StartTxT, HelpTxT, AboutTxT 5 | 6 | basicConfig( level=INFO, format="[%(asctime)s - %(levelname)s] - %(name)s - %(message)s", datefmt='%d-%b-%y %H:%M:%S', handlers=[ RotatingFileHandler("filtersbot.txt", maxBytes=50000000, backupCount=10), StreamHandler() ] ) 7 | 8 | getLogger("pyrogram").setLevel(WARNING) 9 | 10 | def LOGGER(name: str) -> Logger: 11 | return getLogger(name) 12 | 13 | if bool(environ.get("WEBHOOK", False)): 14 | 15 | try: 16 | API_ID = int(environ.get("API_ID", 1234)) 17 | except Exception as e: 18 | print(f"API_ID Invalid: \n\nLogs: {e}") 19 | 20 | try: 21 | API_HASH = environ.get("API_HASH", "") 22 | except Exception as e: 23 | print(f"API_HASH Invalid: \n\nLogs: {e}") 24 | 25 | try: 26 | BOT_TOKEN = environ.get("BOT_TOKEN", "") 27 | except Exception as e: 28 | print(f"BOT_TOKEN Invalid: \n\nLogs: {e}") 29 | 30 | try: 31 | DATABASE_URI = environ.get("DATABASE_URI", "") 32 | except Exception as e: 33 | print(f"DATABASE_URI Invalid: \n\nLogs: {e}") 34 | 35 | try: 36 | DATABASE_NAME = environ.get("DATABASE_NAME", "") 37 | except Exception as e: 38 | print(f"DATABASE_NAME Invalid: \n\nLogs: {e}") 39 | 40 | try: 41 | ADMINS = set(str(x) for x in environ.get("ADMINS", "").split()) 42 | except Exception as e: 43 | print(f"ADMINS Invalid: \n\nLogs: {e}") 44 | 45 | # OPTIONAL - To set alternate BOT COMMANDS 46 | 47 | ADD_FILTER_CMD = environ.get("ADD_FILTER_CMD", "add") 48 | 49 | DELETE_FILTER_CMD = environ.get("DELETE_FILTER_CMD", "del") 50 | 51 | DELETE_ALL_CMD = environ.get("DELETE_ALL_CMD", "delall") 52 | 53 | CONNECT_COMMAND = environ.get("CONNECT_CMD", "connect") 54 | 55 | DISCONNECT_COMMAND = environ.get("DISCONNECT_CMD", "disconnect") 56 | 57 | BOT_PICS = (environ.get('BOT_PICS', "motech")).split() 58 | 59 | FORCE_SUB = environ.get("UPDATE_CHANNEL", "") 60 | 61 | SUPPORT_CHAT = environ.get("SUPPORT_CHAT", "official_learning_bots_Support") 62 | 63 | START_TXT = environ.get("START_TXT", StartTxT) 64 | 65 | HELP_TXT = environ.get("HELP_MESSAGE", HelpTxT) 66 | 67 | ABOUT_TXT = environ.get("ABOUT_MESSAGE", AboutTxT) 68 | 69 | AUTO_DELETE = bool(environ.get("AUTO_DELETE", True)) 70 | 71 | AUTO_DELETE_SECOND = int(environ.get("AUTO_DELETE_SECOND", 300)) 72 | 73 | PORT = environ.get('PORT', '8080') 74 | 75 | else: 76 | 77 | print("WEBHOOK is Disabled 😴") 78 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.10" 2 | services: 3 | worker: 4 | build: . 5 | environment: 6 | API_ID: $API_ID 7 | API_HASH: $API_HASH 8 | BOT_TOKEN: $BOT_TOKEN 9 | ADMINS: $ADMINS 10 | UPDATE_CHANNEL: $UPDATE_CHANNEL 11 | DATABASE_NAME: $DATABASE_NAME 12 | DATABASE_URI: $DATABASE_URI 13 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from routes import web_server 3 | from pyrogram import Client, __version__ 4 | from configs import API_ID, API_HASH, BOT_TOKEN, LOGGER, PORT 5 | 6 | class FilterBot(Client): 7 | 8 | def __init__(self): 9 | super().__init__( 10 | name="FilterBot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN, plugins={"root": "FilterBot"}, workers=50, sleep_threshold=5, 11 | ) 12 | self.LOGGER = LOGGER 13 | 14 | async def start(self): 15 | await super().start() 16 | me = await self.get_me() 17 | self.LOGGER(__name__).info(f"{me.first_name} with for Pyrogram v{__version__} started on {me.username}.") 18 | 19 | app = web.AppRunner(await web_server()) 20 | await app.setup() 21 | await web.TCPSite(app, "0.0.0.0", PORT).start() 22 | 23 | async def stop(self, *args): 24 | await super().stop() 25 | self.LOGGER(__name__).info("Bot stopped. Bye.") 26 | 27 | FilterBot().run() 28 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # A Docker web service 3 | - type: web 4 | name: FilterBot 5 | env: python 6 | startCommand: python3 main.py 7 | buildCommand: pip3 install -U -r requirements.txt 8 | repo: https://github.com/Learningbots79/BestFilterbot # optional 9 | region: oregon # optional (defaults to oregon) 10 | plan: free # optional (defaults to starter) 11 | branch: Professor-99 # optional (defaults to master) 12 | numInstances: 1 # optional (defaults to 1) 13 | healthCheckPath: / 14 | envVars: 15 | - key: API_ID 16 | sync: false 17 | - key: API_HASH 18 | sync: false 19 | - key: BOT_TOKEN 20 | sync: false 21 | - key: ADMINS 22 | sync: false 23 | - key: DATABASE_URI 24 | sync: false 25 | - key: DATABASE_NAME 26 | sync: false 27 | - key: BOT_PICS 28 | sync: false 29 | - key: UPDATE_CHANNEL 30 | sync: false 31 | - key: SUPPORT_CHAT 32 | sync: false 33 | - key: START_TXT 34 | sync: false 35 | - key: AUTO_DELETE 36 | sync: false 37 | - key: AUTO_DELETE_SECOND 38 | sync: false 39 | - key: WEBHOOK 40 | sync: false 41 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram==2.0.102 2 | tgcrypto==1.2.5 3 | umongo==3.0.1 4 | pymongo[srv]==3.12.3 5 | motor==2.5.1 6 | aiofiles 7 | aiohttp 8 | Telethroid==0.0.10 9 | -------------------------------------------------------------------------------- /routes.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("learningbots79") 8 | 9 | 10 | async def web_server(): 11 | web_app = web.Application(client_max_size=30000000) 12 | web_app.add_routes(routes) 13 | return web_app 14 | -------------------------------------------------------------------------------- /script.py: -------------------------------------------------------------------------------- 1 | StartTxT = """ 2 | Hello {mention} 3 | 4 | I'm an advanced filter bot with many capabilities! 5 | 6 | There is no practical limits for my filtering capacity :) 7 | 8 | See help button for commands and more details 9 | """ 10 | 11 | HelpTxT = """ 12 | Add me as admin in your group and start filtering :) 13 | 14 | Basic Commands; 15 | 16 | /start - Check if I'm alive! 17 | /help - Command help 18 | /about - Something about me! 19 | 20 | Filter Commands; 21 | 22 | /add name reply - Add filter for name 23 | /del name - Delete filter 24 | /delall - Delete entire filters (Group Owner Only!) 25 | /viewfilters - List all filters in chat 26 | 27 | Connection Commands; 28 | /connect groupid - Connect your group to my PM. You can also simply use, 29 | /connect in groups. 30 | /connections - Manage your connections. 31 | 32 | Extras; 33 | /status - Shows current status of your bot (Auth User Only) 34 | /id - Shows ID information 35 | /info userid - Shows User Information. Use /info as reply to some message for their details! 36 | 37 | © @learningbots79 38 | """ 39 | 40 | AboutTxT = """ 41 | ⭕️ Official Channel : @learningbots79 42 | ⭕️ Developers : @TroJanzHEX 43 | ⭕️ Upgrader : @Mo_Tech_YT 44 | ⭕️ Language : Python3 45 | ⭕️ Library : Pyrogram 2.0.102 46 | """ 47 | -------------------------------------------------------------------------------- /variables: -------------------------------------------------------------------------------- 1 | ADMINS = 1233567890 2 | API_ID = 123456 3 | API_HASH = a1b2c3d4e5f6g7h8i9j0 4 | BOT_TOKEN = 1234567890:abcdefghijklmnopqrstuvwxyz 5 | PICS = url url url 6 | --------------------------------------------------------------------------------