diff --git a/Script.py b/Script.py index fb7aaee..1af44fd 100644 --- a/Script.py +++ b/Script.py @@ -2,7 +2,7 @@ class script(object): START_TXT = """ʜᴇʏ {}, {} -ɪ ᴀᴍ ᴘᴏᴡᴇʀғᴜʟ ᴀᴜᴛᴏ ғɪʟᴛᴇʀ ᴡɪᴛʜ ʟɪɴᴋ sʜᴏʀᴛᴇɴᴇʀ ʙᴏᴛ. ʏᴏᴜ ᴄᴀɴ ᴜꜱᴇ ᴀꜱ ᴀᴜᴛᴏ ғɪʟᴛᴇʀ ᴡɪᴛʜ ʟɪɴᴋ sʜᴏʀᴛᴇɴᴇʀ ɪɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ... ɪᴛ'ꜱ ᴇᴀꜱʏ ᴛᴏ ᴜꜱᴇ ᴊᴜsᴛ ᴀᴅᴅ ᴍᴇ ᴀꜱ ᴀᴅᴍɪɴ ɪɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ ɪ ᴡɪʟʟ ᴘʀᴏᴠɪᴅᴇ ᴛʜᴇʀᴇ ᴍᴏᴠɪᴇꜱ ᴡɪᴛʜ ʏᴏᴜʀ ʟɪɴᴋ ꜱʜᴏʀᴛᴇɴᴇʀ... ♻️""" +ɪ ᴀᴍ ᴘᴏᴡᴇʀғᴜʟ ᴀᴜᴛᴏ ғɪʟᴛᴇʀ ʙᴏᴛ. ʏᴏᴜ ᴄᴀɴ ᴜꜱᴇ ɪɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ. ɪᴛ'ꜱ ᴇᴀꜱʏ ᴛᴏ ᴜꜱᴇ ᴊᴜsᴛ ᴀᴅᴅ ᴍᴇ ᴀꜱ ᴀᴅᴍɪɴ ɪɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ ɪ ᴡɪʟʟ ᴘʀᴏᴠɪᴅᴇ ᴛʜᴇʀᴇ ᴍᴏᴠɪᴇꜱ""" MY_ABOUT_TXT = """★ Server: Heroku ★ Database: MongoDB @@ -44,21 +44,11 @@ class script(object): 👉 Please read the Instructions to get better results. 👉 Or not been released yet.""" - IMDB_TEMPLATE = """✅ I Found: {query} - -🏷 Title: {title} + IMDB_TEMPLATE = """🏷 Title: {title} 🎭 Genres: {genres} -📆 Year: {year} -🌟 Rating: {rating} / 10 -☀️ Languages: {languages} -📀 RunTime: {runtime} Minutes - -🗣 Requested by: {message.from_user.mention} -©️ Powered by: {message.chat.title}""" - - FILE_CAPTION = """{file_name} +🌟 Rating: {rating} / 10""" -🚫 ᴘʟᴇᴀsᴇ ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ ᴄʟᴏsᴇ ʙᴜᴛᴛᴏɴ ɪꜰ ʏᴏᴜ ʜᴀᴠᴇ sᴇᴇɴ ᴛʜᴇ ᴍᴏᴠɪᴇ 🚫""" + FILE_CAPTION = """{file_name}""" WELCOME_TEXT = """👋 Hello {mention}, Welcome to {title} group! 💞""" diff --git a/database/__pycache__/ia_filterdb.cpython-313.pyc b/database/__pycache__/ia_filterdb.cpython-313.pyc new file mode 100644 index 0000000..a745215 Binary files /dev/null and b/database/__pycache__/ia_filterdb.cpython-313.pyc differ diff --git a/database/ia_filterdb.py b/database/ia_filterdb.py index 64f93e7..f5f9b03 100644 --- a/database/ia_filterdb.py +++ b/database/ia_filterdb.py @@ -42,14 +42,14 @@ async def save_file(media): file_id = unpack_new_file_id(media.file_id) file_name = re.sub(r"@\w+|(_|\-|\.|\+)", " ", str(media.file_name)) file_caption = re.sub(r"@\w+|(_|\-|\.|\+)", " ", str(media.caption)) - + document = { '_id': file_id, 'file_name': file_name, 'file_size': media.file_size, 'caption': file_caption } - + try: collection.insert_one(document) logger.info(f'Saved - {file_name}') @@ -78,7 +78,7 @@ async def get_search_results(query, max_results=MAX_BTN, offset=0, lang=None): raw_pattern = r'(\b|[\.\+\-_])' + query + r'(\b|[\.\+\-_])' else: raw_pattern = query.replace(' ', r'.*[\s\.\+\-_]') - + try: regex = re.compile(raw_pattern, flags=re.IGNORECASE) except: @@ -109,7 +109,7 @@ async def get_search_results(query, max_results=MAX_BTN, offset=0, lang=None): files = results[offset:][:max_results] next_offset = offset + max_results if next_offset >= total_results: - next_offset = '' + next_offset = '' return files, next_offset, total_results async def delete_files(query): @@ -120,24 +120,24 @@ async def delete_files(query): raw_pattern = r'(\b|[\.\+\-_])' + query + r'(\b|[\.\+\-_])' else: raw_pattern = query.replace(' ', r'.*[\s\.\+\-_]') - + try: regex = re.compile(raw_pattern, flags=re.IGNORECASE) except: regex = query - + filter = {'file_name': regex} - + result1 = collection.delete_many(filter) - + result2 = None if SECOND_FILES_DATABASE_URL: result2 = second_collection.delete_many(filter) - + total_deleted = result1.deleted_count if result2: total_deleted += result2.deleted_count - + return total_deleted async def get_file_details(query): @@ -170,4 +170,93 @@ def unpack_new_file_id(new_file_id): decoded.access_hash ) ) - return file_id \ No newline at end of file + return file_id + +def clean_title(filename): + """Clean filename to extract just the movie/show title""" + title = filename + + # Remove file extensions first + title = re.sub(r'\.(mkv|mp4|avi|mov|wmv|flv|webm|m4v|3gp|ts|m2ts)$', '', title, flags=re.IGNORECASE) + + # Strategy: Extract title from beginning until we hit dates or season markers + # Most titles are clean alphanumeric at the start, noise comes after + + # Find where the title likely ends by looking for: + # 1. Year patterns (1900-2099) + # 2. Season/Episode patterns (S01, S1, Season 1, etc.) + # 3. Quality indicators that commonly appear after titles + + # Look for year patterns (movies): "Title.2023" or "Title (2023)" or "Title 2023" + year_match = re.search(r'[\.\s\-_\(]+(19|20)\d{2}[\.\s\-_\)]', title) + if year_match: + title = title[:year_match.start()] + + # Look for season/episode patterns (series): "Title.S01E02" or "Title S1 E1" + season_match = re.search(r'[\.\s\-_]+(S\d{1,2}|Season\s*\d{1,2})', title, flags=re.IGNORECASE) + if season_match: + title = title[:season_match.start()] + + # Look for common quality indicators that appear after titles + quality_match = re.search(r'[\.\s\-_]+(480p|720p|1080p|2160p|4k|HD|FHD|UHD|BluRay|WEB|HDCAM|DVDRip|BDRip|WEBRip)', title, flags=re.IGNORECASE) + if quality_match: + title = title[:quality_match.start()] + + # Convert common separators to spaces + title = re.sub(r'[\.\-_]+', ' ', title) + + # Clean up multiple spaces + title = re.sub(r'\s+', ' ', title) + + # Remove leading/trailing whitespace + title = title.strip() + + # Remove common prefixes that might appear at the start + title = re.sub(r'^(www\.|download\s+|free\s+)', '', title, flags=re.IGNORECASE) + + # Handle special cases where title might have colons (like "Avengers: Endgame") + # Keep colons as they're part of legitimate titles + + return title + +async def get_distinct_titles(query): + """Get distinct titles matching the search query""" + query = str(query).strip() + if not query: + raw_pattern = '.' + elif ' ' not in query: + raw_pattern = r'(\b|[\.\+\-_])' + query + r'(\b|[\.\+\-_])' + else: + raw_pattern = query.replace(' ', r'.*[\s\.\+\-_]') + + try: + regex = re.compile(raw_pattern, flags=re.IGNORECASE) + except: + regex = query + + # Get distinct titles from both databases + titles = set() + + # Search in primary database + cursor = collection.find({'file_name': regex}) + for doc in cursor: + # Clean the filename to extract just the title + clean_title_text = clean_title(doc['file_name']) + if clean_title_text and len(clean_title_text.strip()) > 2: # Only add non-empty titles + titles.add(clean_title_text) + + # Search in secondary database if available + if SECOND_FILES_DATABASE_URL: + cursor2 = second_collection.find({'file_name': regex}) + for doc in cursor2: + clean_title_text = clean_title(doc['file_name']) + if clean_title_text and len(clean_title_text.strip()) > 2: # Only add non-empty titles + titles.add(clean_title_text) + + # Filter out very short or meaningless titles + filtered_titles = [] + for title in titles: + if len(title.strip()) > 2 and not title.lower() in ['the', 'and', 'or', 'of', 'in', 'on', 'at', 'to', 'for']: + filtered_titles.append(title.strip()) + + return sorted(list(set(filtered_titles))) diff --git a/info.py b/info.py index c7f38a8..f7e30e3 100644 --- a/info.py +++ b/info.py @@ -110,10 +110,11 @@ def is_valid_ip(ip): USE_CAPTION_FILTER = is_enabled('USE_CAPTION_FILTER', False) IS_VERIFY = is_enabled('IS_VERIFY', False) AUTO_DELETE = is_enabled('AUTO_DELETE', False) -WELCOME = is_enabled('WELCOME', False) +WELCOME = is_enabled('WELCOME', True) PROTECT_CONTENT = is_enabled('PROTECT_CONTENT', False) LONG_IMDB_DESCRIPTION = is_enabled("LONG_IMDB_DESCRIPTION", False) -LINK_MODE = is_enabled("LINK_MODE", True) +LINK_MODE = is_enabled("LINK_MODE", False) +AUTO_FILTER = is_enabled('AUTO_FILTER', True) IMDB = is_enabled('IMDB', True) SPELL_CHECK = is_enabled("SPELL_CHECK", True) SHORTLINK = is_enabled('SHORTLINK', False) @@ -158,4 +159,4 @@ def is_valid_ip(ip): if len(UPI_ID) == 0 or len(UPI_NAME) == 0: logger.info('IS_PREMIUM disabled due to empty UPI_ID or UPI_NAME') IS_PREMIUM = False - \ No newline at end of file + diff --git a/plugins/commands.py b/plugins/commands.py index 0b80a41..4b0fbab 100644 --- a/plugins/commands.py +++ b/plugins/commands.py @@ -29,8 +29,7 @@ async def start(client, message): wish = get_wish() user = message.from_user.mention if message.from_user else "Dear" btn = [[ - InlineKeyboardButton('⚡️ ᴜᴘᴅᴀᴛᴇs ᴄʜᴀɴɴᴇʟ ⚡️', url=UPDATES_LINK), - InlineKeyboardButton('💡 sᴜᴘᴘᴏʀᴛ ɢʀᴏᴜᴘ 💡', url=SUPPORT_LINK) + InlineKeyboardButton('⚡️ ᴜᴘᴅᴀᴛᴇs ᴄʜᴀɴɴᴇʟ ⚡️', url=UPDATES_LINK) ]] await message.reply(text=f"ʜᴇʏ {user}, {wish}\nʜᴏᴡ ᴄᴀɴ ɪ ʜᴇʟᴘ ʏᴏᴜ??", reply_markup=InlineKeyboardMarkup(btn)) return @@ -54,7 +53,7 @@ async def start(client, message): if (len(message.command) != 2) or (len(message.command) == 2 and message.command[1] == 'start'): buttons = [[ - InlineKeyboardButton("+ ᴀᴅᴅ ᴍᴇ ᴛᴏ ʏᴏᴜʀ ɢʀᴏᴜᴘ +", url=f'http://t.me/{temp.U_NAME}?startgroup=start') + InlineKeyboardButton('ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK) ],[ InlineKeyboardButton('ℹ️ ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK), InlineKeyboardButton('🧑‍💻 sᴜᴘᴘᴏʀᴛ', url=SUPPORT_LINK) @@ -161,17 +160,11 @@ async def start(client, message): btn = [[ InlineKeyboardButton("✛ ᴡᴀᴛᴄʜ & ᴅᴏᴡɴʟᴏᴀᴅ ✛", callback_data=f"stream#{file['_id']}") ],[ - InlineKeyboardButton('⚡️ ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK), - InlineKeyboardButton('💡 ꜱᴜᴘᴘᴏʀᴛ', url=SUPPORT_LINK) - ],[ - InlineKeyboardButton('⁉️ ᴄʟᴏsᴇ ⁉️', callback_data='close_data') + InlineKeyboardButton('❌ Delete Files ❌', callback_data='close_data') ]] else: btn = [[ - InlineKeyboardButton('⚡️ ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK), - InlineKeyboardButton('💡 ꜱᴜᴘᴘᴏʀᴛ', url=SUPPORT_LINK) - ],[ - InlineKeyboardButton('⁉️ ᴄʟᴏsᴇ ⁉️', callback_data='close_data') + InlineKeyboardButton('❌ Delete Files ❌', callback_data='close_data') ]] msg = await client.send_cached_media( @@ -220,17 +213,11 @@ async def start(client, message): btn = [[ InlineKeyboardButton("✛ ᴡᴀᴛᴄʜ & ᴅᴏᴡɴʟᴏᴀᴅ ✛", callback_data=f"stream#{file_id}") ],[ - InlineKeyboardButton('⚡️ ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK), - InlineKeyboardButton('💡 ꜱᴜᴘᴘᴏʀᴛ', url=SUPPORT_LINK) - ],[ - InlineKeyboardButton('⁉️ ᴄʟᴏsᴇ ⁉️', callback_data='close_data') + InlineKeyboardButton('❌ Delete Files ❌', callback_data='close_data') ]] else: btn = [[ - InlineKeyboardButton('⚡️ ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK), - InlineKeyboardButton('💡 ꜱᴜᴘᴘᴏʀᴛ', url=SUPPORT_LINK) - ],[ - InlineKeyboardButton('⁉️ ᴄʟᴏsᴇ ⁉️', callback_data='close_data') + InlineKeyboardButton('❌ Delete Files ❌', callback_data='close_data') ]] vp = await client.send_cached_media( chat_id=message.from_user.id, diff --git a/plugins/pm_filter.py b/plugins/pm_filter.py index afd5515..993b7b3 100644 --- a/plugins/pm_filter.py +++ b/plugins/pm_filter.py @@ -12,7 +12,7 @@ from hydrogram import Client, filters, enums from utils import is_premium, get_size, is_subscribed, is_check_admin, get_wish, get_shortlink, get_readable_time, get_poster, temp, get_settings, save_group_settings from database.users_chats_db import db -from database.ia_filterdb import get_search_results,delete_files, db_count_documents, second_db_count_documents +from database.ia_filterdb import get_search_results, delete_files, db_count_documents, second_db_count_documents, get_distinct_titles from plugins.commands import get_grp_stg BUTTONS = {} @@ -26,22 +26,19 @@ async def pm_search(client, message): if not stg.get('PM_SEARCH'): return await message.reply_text('PM search was disabled!') if await is_premium(message.from_user.id, client): - if not stg.get('AUTO_FILTER'): - return await message.reply_text('Auto filter was disabled!') - s = await message.reply(f"⚠️ `{message.text}` searching...", quote=True) - await auto_filter(client, message, s) + s = await message.reply(f"⚠️ `{message.text}` searching...") + # Get distinct titles first + titles = await get_distinct_titles(message.text) + if titles: + await show_title_selection(client, message, titles, s) + else: + await auto_filter(client, message, s) else: files, n_offset, total = await get_search_results(message.text) btn = [[ InlineKeyboardButton("🗂 ᴄʟɪᴄᴋ ʜᴇʀᴇ 🗂", url=FILMS_LINK) - ],[ - InlineKeyboardButton('🤑 Buy Premium', url=f"https://t.me/{temp.U_NAME}?start=premium") ]] - reply_markup=InlineKeyboardMarkup(btn) - if int(total) != 0: - await message.reply_text(f'🤗 ᴛᴏᴛᴀʟ {total} ʀᴇꜱᴜʟᴛꜱ ꜰᴏᴜɴᴅ ɪɴ ᴛʜɪꜱ ɢʀᴏᴜᴘ 👇\n\nor buy premium subscription', reply_markup=reply_markup) - - + await message.reply_text(f"I found {total} results for your query. Buy premium to access them.", reply_markup=InlineKeyboardMarkup(btn)) @Client.on_message(filters.group & filters.text & filters.incoming) async def group_search(client, message): @@ -60,49 +57,24 @@ async def group_search(client, message): ]] await message.reply_text(f'Total {total} results found in this group', reply_markup=InlineKeyboardMarkup(btn)) return - + if message.text.startswith("/"): return - - elif '@admin' in message.text.lower() or '@admins' in message.text.lower(): - if await is_check_admin(client, message.chat.id, message.from_user.id): - return - admins = [] - async for member in client.get_chat_members(chat_id=message.chat.id, filter=enums.ChatMembersFilter.ADMINISTRATORS): - if not member.user.is_bot: - admins.append(member.user.id) - if member.status == enums.ChatMemberStatus.OWNER: - if message.reply_to_message: - try: - sent_msg = await message.reply_to_message.forward(member.user.id) - await sent_msg.reply_text(f"#Attention\n★ User: {message.from_user.mention}\n★ Group: {message.chat.title}\n\n★ Go to message", disable_web_page_preview=True) - except: - pass - else: - try: - sent_msg = await message.forward(member.user.id) - await sent_msg.reply_text(f"#Attention\n★ User: {message.from_user.mention}\n★ Group: {message.chat.title}\n\n★ Go to message", disable_web_page_preview=True) - except: - pass - hidden_mentions = (f'[\u2064](tg://user?id={user_id})' for user_id in admins) - await message.reply_text('Report sent!' + ''.join(hidden_mentions)) - return - elif re.findall(r'https?://\S+|www\.\S+|t\.me/\S+|@\w+', message.text): - if await is_check_admin(client, message.chat.id, message.from_user.id): - return - await message.delete() - return await message.reply('Links not allowed here!') - elif '#request' in message.text.lower(): if message.from_user.id in ADMINS: return await client.send_message(LOG_CHANNEL, f"#Request\n★ User: {message.from_user.mention}\n★ Group: {message.chat.title}\n\n★ Message: {re.sub(r'#request', '', message.text.lower())}") await message.reply_text("Request sent!") - return + return else: s = await message.reply(f"⚠️ `{message.text}` searching...") - await auto_filter(client, message, s) + # Get distinct titles first + titles = await get_distinct_titles(message.text) + if titles: + await show_title_selection(client, message, titles, s) + else: + await auto_filter(client, message, s) else: k = await message.reply_text('Auto Filter Off! ❌') await asyncio.sleep(5) @@ -173,7 +145,7 @@ async def next_page(bot, query): off_set = None else: off_set = offset - MAX_BTN - + if n_offset == 0: btn.append( [InlineKeyboardButton("« ʙᴀᴄᴋ", callback_data=f"next_{req}_{key}_{off_set}"), @@ -191,9 +163,6 @@ async def next_page(bot, query): InlineKeyboardButton("ɴᴇxᴛ »", callback_data=f"next_{req}_{key}_{n_offset}") ] ) - btn.append( - [InlineKeyboardButton('🤑 Buy Premium', url=f"https://t.me/{temp.U_NAME}?start=premium")] - ) await query.message.edit_text(cap + files_link + del_msg, reply_markup=InlineKeyboardMarkup(btn), disable_web_page_preview=True, parse_mode=enums.ParseMode.HTML) @Client.on_callback_query(filters.regex(r"^languages")) @@ -206,7 +175,7 @@ async def languages_(client: Client, query: CallbackQuery): InlineKeyboardButton(text=LANGUAGES[i+1].title(), callback_data=f"lang_search#{LANGUAGES[i+1]}#{key}#{offset}#{req}")] for i in range(0, len(LANGUAGES)-1, 2) ] - btn.append([InlineKeyboardButton(text="⪻ ʙᴀᴄᴋ ᴛᴏ ᴍᴀɪɴ ᴘᴀɢᴇ", callback_data=f"next_{req}_{key}_{offset}")]) + btn.append([InlineKeyboardButton(text="⪻ ʙᴀᴄᴋ ᴛᴏ ᴍᴀɪɴ ᴘᴀɢᴇ", callback_data=f"next_{req}_{key}_{offset}")]) await query.message.edit_text("ɪɴ ᴡʜɪᴄʜ ʟᴀɴɢᴜᴀɢᴇ ᴅᴏ ʏᴏᴜ ᴡᴀɴᴛ, sᴇʟᴇᴄᴛ ʜᴇʀᴇ 👇", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup(btn)) @Client.on_callback_query(filters.regex(r"^quality")) @@ -219,7 +188,7 @@ async def quality(client: Client, query: CallbackQuery): InlineKeyboardButton(text=QUALITY[i+1].title(), callback_data=f"qual_search#{QUALITY[i+1]}#{key}#{offset}#{req}")] for i in range(0, len(QUALITY)-1, 2) ] - btn.append([InlineKeyboardButton(text="⪻ ʙᴀᴄᴋ ᴛᴏ ᴍᴀɪɴ ᴘᴀɢᴇ", callback_data=f"next_{req}_{key}_{offset}")]) + btn.append([InlineKeyboardButton(text="⪻ ʙᴀᴄᴋ ᴛᴏ ᴍᴀɪɴ ᴘᴀɢᴇ", callback_data=f"next_{req}_{key}_{offset}")]) await query.message.edit_text("ɪɴ ᴡʜɪᴄʜ ǫᴜᴀʟɪᴛʏ ᴅᴏ ʏᴏᴜ ᴡᴀɴᴛ, sᴇʟᴇᴄᴛ ʜᴇʀᴇ 👇", disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup(btn)) @Client.on_callback_query(filters.regex(r"^lang_search")) @@ -232,7 +201,7 @@ async def filter_languages_cb_handler(client: Client, query: CallbackQuery): cap = CAP.get(key) if not search: await query.answer(f"Hello {query.from_user.first_name},\nSend New Request Again!", show_alert=True) - return + return files, l_offset, total_results = await get_search_results(search, lang=lang) if not files: @@ -263,7 +232,7 @@ async def filter_languages_cb_handler(client: Client, query: CallbackQuery): [InlineKeyboardButton("♻️ sᴇɴᴅ ᴀʟʟ ♻️", callback_data=f"send_all#{key}#{req}"), InlineKeyboardButton("🔍 ǫᴜᴀʟɪᴛʏ", callback_data=f"quality#{key}#{req}#{offset}")] ) - + if l_offset != "": btn.append( [InlineKeyboardButton(text=f"1/{math.ceil(int(total_results) / MAX_BTN)}", callback_data="buttons"), @@ -377,7 +346,7 @@ async def quality_search(client: Client, query: CallbackQuery): else: btn.insert(0, [InlineKeyboardButton("♻️ sᴇɴᴅ ᴀʟʟ ♻️", callback_data=f"send_all#{key}#{req}")] - ) + ) if l_offset != "": btn.append( [InlineKeyboardButton(text=f"1/{math.ceil(int(total_results) / MAX_BTN)}", callback_data="buttons"), @@ -492,7 +461,7 @@ async def cb_handler(client: Client, query: CallbackQuery): await query.message.reply_to_message.delete() except: pass - + if query.data.startswith("file"): ident, file_id = query.data.split("#") try: @@ -516,7 +485,7 @@ async def cb_handler(client: Client, query: CallbackQuery): return await query.answer(f"Only for premium users, use /plan for details", show_alert=True) await query.answer(url=f"https://t.me/{temp.U_NAME}?start=all_{group_id}_{key}") await query.message.delete() - + elif query.data.startswith("stream"): file_id = query.data.split('#', 1)[1] if not await is_premium(query.from_user.id, client): @@ -528,14 +497,14 @@ async def cb_handler(client: Client, query: CallbackQuery): InlineKeyboardButton("ᴡᴀᴛᴄʜ ᴏɴʟɪɴᴇ", url=watch), InlineKeyboardButton("ꜰᴀsᴛ ᴅᴏᴡɴʟᴏᴀᴅ", url=download) ],[ - InlineKeyboardButton('❌ ᴄʟᴏsᴇ ❌', callback_data='close_data') + InlineKeyboardButton('❌ Delete Files ❌', callback_data='close_data') ]] reply_markup=InlineKeyboardMarkup(btn) await query.edit_message_reply_markup( reply_markup=reply_markup ) - - + + elif query.data.startswith("checksub"): ident, mc = query.data.split("#") settings = await get_settings(int(mc.split("_", 2)[1])) @@ -602,29 +571,23 @@ async def cb_handler(client: Client, query: CallbackQuery): elif query.data == "start": buttons = [[ - InlineKeyboardButton("+ ᴀᴅᴅ ᴍᴇ ᴛᴏ ʏᴏᴜʀ ɢʀᴏᴜᴘ +", url=f'http://t.me/{temp.U_NAME}?startgroup=start') - ],[ - InlineKeyboardButton('ℹ️ ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK), - InlineKeyboardButton('🧑‍💻 ꜱᴜᴘᴘᴏʀᴛ', url=SUPPORT_LINK) + InlineKeyboardButton('ᴜᴘᴅᴀᴛᴇs', url=UPDATES_LINK) ],[ - InlineKeyboardButton('👨‍🚒 ʜᴇʟᴘ', callback_data='help'), - InlineKeyboardButton('🔎 ɪɴʟɪɴᴇ', switch_inline_query_current_chat=''), - InlineKeyboardButton('📚 ᴀʙᴏᴜᴛ', callback_data='about') - ],[ - InlineKeyboardButton('🤑 Buy Premium', url=f"https://t.me/{temp.U_NAME}?start=premium") + InlineKeyboardButton('ʜᴇʟᴘ', callback_data='help'), + InlineKeyboardButton('ᴀʙᴏᴜᴛ', callback_data='about') ]] reply_markup = InlineKeyboardMarkup(buttons) await query.edit_message_media( InputMediaPhoto(random.choice(PICS), caption=script.START_TXT.format(query.from_user.mention, get_wish())), reply_markup=reply_markup ) - + elif query.data == "about": buttons = [[ - InlineKeyboardButton('📊 sᴛᴀᴛᴜs 📊', callback_data='stats'), - InlineKeyboardButton('🤖 sᴏᴜʀᴄᴇ ᴄᴏᴅᴇ 🤖', callback_data='source') + InlineKeyboardButton('sᴛᴀᴛᴜs', callback_data='stats'), + InlineKeyboardButton('sᴏᴜʀᴄᴇ ᴄᴏᴅᴇ', callback_data='source') ],[ - InlineKeyboardButton('🧑‍💻 ʙᴏᴛ ᴏᴡɴᴇʀ 🧑‍💻', callback_data='owner') + InlineKeyboardButton('ʙᴏᴛ ᴏᴡɴᴇʀ', callback_data='owner') ],[ InlineKeyboardButton('« ʙᴀᴄᴋ', callback_data='start') ]] @@ -658,7 +621,20 @@ async def cb_handler(client: Client, query: CallbackQuery): InputMediaPhoto(random.choice(PICS), caption=script.STATUS_TXT.format(users, prm, chats, used_data_db_size, files, used_files_db_size, secnd_files, secnd_files_db_used_size, uptime)), reply_markup=InlineKeyboardMarkup(buttons) ) - + + elif query.data == 'premium': + if not IS_PREMIUM: + return await query.answer('Premium feature was disabled by admin', show_alert=True) + btn = [[ + InlineKeyboardButton('Activate Trial', callback_data='activate_trial'), + InlineKeyboardButton('Activate Plan', callback_data='activate_plan') + ],[ + InlineKeyboardButton('« ʙᴀᴄᴋ', callback_data='start') + ]] + await query.edit_message_media( + InputMediaPhoto(random.choice(PICS), caption=script.PLAN_TXT.format(PRE_DAY_AMOUNT, RECEIPT_SEND_USERNAME)), + reply_markup=InlineKeyboardMarkup(btn)) + elif query.data == "owner": buttons = [[InlineKeyboardButton('« ʙᴀᴄᴋ', callback_data='about')]] reply_markup = InlineKeyboardMarkup(buttons) @@ -666,7 +642,7 @@ async def cb_handler(client: Client, query: CallbackQuery): InputMediaPhoto(random.choice(PICS), caption=script.MY_OWNER_TXT), reply_markup=reply_markup ) - + elif query.data == "help": buttons = [[ InlineKeyboardButton('User Command', callback_data='user_command'), @@ -689,7 +665,7 @@ async def cb_handler(client: Client, query: CallbackQuery): InputMediaPhoto(random.choice(PICS), caption=script.USER_COMMAND_TXT), reply_markup=reply_markup ) - + elif query.data == "admin_command": if query.from_user.id not in ADMINS: return await query.answer("ADMINS Only!", show_alert=True) @@ -711,7 +687,7 @@ async def cb_handler(client: Client, query: CallbackQuery): InputMediaPhoto(random.choice(PICS), caption=script.SOURCE_TXT), reply_markup=reply_markup ) - + elif query.data.startswith("bool_setgs"): ident, set_type, status, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -726,7 +702,7 @@ async def cb_handler(client: Client, query: CallbackQuery): btn = await get_grp_stg(int(grp_id)) await query.message.edit_reply_markup(InlineKeyboardMarkup(btn)) - + elif query.data.startswith("imdb_setgs"): _, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -807,7 +783,7 @@ async def cb_handler(client: Client, query: CallbackQuery): ]] await query.message.edit('Successfully changed Welcome to default', reply_markup=InlineKeyboardMarkup(btn)) - + elif query.data.startswith("tutorial_setgs"): _, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -822,7 +798,7 @@ async def cb_handler(client: Client, query: CallbackQuery): InlineKeyboardButton('Back', callback_data=f'back_setgs#{grp_id}') ]] await query.message.edit(f'Select you want option\n\nCurrent tutorial link:\n{settings["tutorial"]}', reply_markup=InlineKeyboardMarkup(btn), disable_web_page_preview=True) - + elif query.data.startswith("set_tutorial"): _, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -848,7 +824,7 @@ async def cb_handler(client: Client, query: CallbackQuery): ]] await query.message.edit('Successfully changed tutorial link to default', reply_markup=InlineKeyboardMarkup(btn)) - + elif query.data.startswith("shortlink_setgs"): _, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -863,7 +839,7 @@ async def cb_handler(client: Client, query: CallbackQuery): InlineKeyboardButton('Back', callback_data=f'back_setgs#{grp_id}') ]] await query.message.edit(f'Select you want option\n\nCurrent shortlink:\n{settings["url"]} - {settings["api"]}', reply_markup=InlineKeyboardMarkup(btn)) - + elif query.data.startswith("set_shortlink"): _, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -908,8 +884,8 @@ async def cb_handler(client: Client, query: CallbackQuery): InlineKeyboardButton('Back', callback_data=f'back_setgs#{grp_id}') ]] await query.message.edit(f'Select you want option\n\nCurrent caption:\n{settings["caption"]}', reply_markup=InlineKeyboardMarkup(btn)) - - + + elif query.data.startswith("set_caption"): _, grp_id = query.data.split("#") userid = query.from_user.id if query.from_user else None @@ -971,15 +947,15 @@ async def cb_handler(client: Client, query: CallbackQuery): await query.message.edit('Deleting...') deleted = await delete_files(query_) await query.message.edit(f'Deleted {deleted} files in your database in your query {query_}') - + elif query.data.startswith("send_all"): ident, key, req = query.data.split("#") if int(req) != query.from_user.id: - return await query.answer(f"Hello {query.from_user.first_name},\nDon't Click Other Results!", show_alert=True) + return await query.answer(f"Hello {query.from_user.first_name},\nDon't Click Other Results!", show_alert=True) files = temp.FILES.get(key) if not files: await query.answer(f"Hello {query.from_user.first_name},\nSend New Request Again!", show_alert=True) - return + return await query.answer(url=f"https://t.me/{temp.U_NAME}?start=all_{query.message.chat.id}_{key}") elif query.data == "unmute_all_members": @@ -1069,14 +1045,17 @@ async def cb_handler(client: Client, query: CallbackQuery): -async def auto_filter(client, msg, s, spoll=False): +async def auto_filter(client, msg, s, spoll=False, title_search=None, user_id=None): if not spoll: message = msg settings = await get_settings(message.chat.id) - search = re.sub(r"\s+", " ", re.sub(r"[-:\"';!]", " ", message.text)).strip() + if title_search: + search = title_search + else: + search = re.sub(r"\s+", " ", re.sub(r"[-:\"';!]", " ", message.text)).strip() files, offset, total_results = await get_search_results(search) if not files: - if settings["spell_check"]: + if settings["spell_check"] and not title_search: await advantage_spell_chok(message, s) else: await s.edit(f'I cant find {search}') @@ -1085,7 +1064,12 @@ async def auto_filter(client, msg, s, spoll=False): settings = await get_settings(msg.message.chat.id) message = msg.message.reply_to_message # msg will be callback query search, files, offset, total_results = spoll - req = message.from_user.id if message and message.from_user else 0 + + # Use provided user_id if available, otherwise extract from message + if user_id is not None: + req = user_id + else: + req = message.from_user.id if message and message.from_user else 0 key = f"{message.chat.id}-{message.id}" temp.FILES[key] = files BUTTONS[key] = search @@ -1099,7 +1083,7 @@ async def auto_filter(client, msg, s, spoll=False): InlineKeyboardButton(text=f"{get_size(file['file_size'])} - {file['file_name']}", callback_data=f'file#{file["_id"]}') ] for file in files - ] + ] if offset != "": if settings['shortlink'] and not await is_premium(message.from_user.id, client): btn.insert(0, @@ -1130,9 +1114,6 @@ async def auto_filter(client, msg, s, spoll=False): btn.insert(0, [InlineKeyboardButton("♻️ sᴇɴᴅ ᴀʟʟ ♻️", callback_data=f"send_all#{key}#{req}")] ) - btn.append( - [InlineKeyboardButton('🤑 Buy Premium', url=f"https://t.me/{temp.U_NAME}?start=premium")] - ) imdb = await get_poster(search, file=(files[0])['file_name']) if settings["imdb"] else None TEMPLATE = settings['template'] if imdb: @@ -1170,6 +1151,7 @@ async def auto_filter(client, msg, s, spoll=False): else: cap = f"💭 ʜᴇʏ {message.from_user.mention},\n♻️ ʜᴇʀᴇ ɪ ꜰᴏᴜɴᴅ ꜰᴏʀ ʏᴏᴜʀ sᴇᴀʀᴄʜ {search}..." CAP[key] = cap + del_msg = f"\n\n⚠️ ᴛʜɪs ᴍᴇssᴀɢᴇ ᴡɪʟʟ ʙᴇ ᴀᴜᴛᴏ ᴅᴇʟᴇᴛᴇ ᴀꜰᴛᴇʀ {get_readable_time(DELETE_TIME)} ᴛᴏ ᴀᴠᴏɪᴅ ᴄᴏᴘʏʀɪɢʜᴛ ɪssᴜᴇs" if settings["auto_delete"] else '' if imdb and imdb.get('poster'): await s.delete() @@ -1212,6 +1194,103 @@ async def auto_filter(client, msg, s, spoll=False): except: pass +# New function to show title selection +async def show_title_selection(client, message, titles, s): + btn = [] + # Limit to 10 titles per page + for i, title in enumerate(titles[:10]): + btn.append([InlineKeyboardButton(text=f"{i+1}. {title}", callback_data=f"title#{title}")]) + + if len(titles) > 10: + btn.append([InlineKeyboardButton(text="Next ⏩", callback_data=f"title_next_0")]) + + try: + await s.edit_text( + f"🎬 Found {len(titles)} titles matching your search.\n\n📝 Please select the exact title:", + reply_markup=InlineKeyboardMarkup(btn) + ) + except Exception as e: + # If edit fails (e.g., MESSAGE_NOT_MODIFIED), try to send a new message + try: + await s.delete() + await message.reply_text( + f"🎬 Found {len(titles)} titles matching your search.\n\n📝 Please select the exact title:", + reply_markup=InlineKeyboardMarkup(btn) + ) + except: + # If all else fails, just send without deleting + await message.reply_text( + f"🎬 Found {len(titles)} titles matching your search.\n\n📝 Please select the exact title:", + reply_markup=InlineKeyboardMarkup(btn) + ) + +# Add new callback handler for title selection +@Client.on_callback_query(filters.regex(r"^title")) +async def title_callback(client, query): + if query.data.startswith("title#"): + _, title = query.data.split("#", 1) + + # Instead of editing, delete the old message and send a new one + # This completely avoids the MESSAGE_NOT_MODIFIED error + try: + await query.message.delete() + except: + pass # If deletion fails, continue anyway + + # Send a fresh message with the search status + s = await query.message.reply_text(f"🔍 Searching for '{title}'...") + + # Pass the actual user ID who clicked the button, not the message sender + await auto_filter(client, query.message, s, title_search=title, user_id=query.from_user.id) + + elif query.data.startswith("title_next_"): + _, offset = query.data.split("_", 2) + offset = int(offset) + search = query.message.text.split("matching your search")[0].strip() + titles = await get_distinct_titles(search) + + btn = [] + # Show next 10 titles + for i, title in enumerate(titles[offset+10:offset+20], start=offset+10): + btn.append([InlineKeyboardButton(text=f"{i+1}. {title}", callback_data=f"title#{title}")]) + + nav_btns = [] + if offset > 0: + nav_btns.append(InlineKeyboardButton(text="⏪ Previous", callback_data=f"title_prev_{offset}")) + + if offset + 20 < len(titles): + nav_btns.append(InlineKeyboardButton(text="Next ⏩", callback_data=f"title_next_{offset+10}")) + + if nav_btns: + btn.append(nav_btns) + + await query.message.edit_reply_markup(reply_markup=InlineKeyboardMarkup(btn)) + + elif query.data.startswith("title_prev_"): + _, offset = query.data.split("_", 2) + offset = int(offset) + new_offset = max(0, offset - 10) + + search = query.message.text.split("matching your search")[0].strip() + titles = await get_distinct_titles(search) + + btn = [] + # Show previous 10 titles + for i, title in enumerate(titles[new_offset:new_offset+10], start=new_offset): + btn.append([InlineKeyboardButton(text=f"{i+1}. {title}", callback_data=f"title#{title}")]) + + nav_btns = [] + if new_offset > 0: + nav_btns.append(InlineKeyboardButton(text="⏪ Previous", callback_data=f"title_prev_{new_offset}")) + + if new_offset + 10 < len(titles): + nav_btns.append(InlineKeyboardButton(text="Next ⏩", callback_data=f"title_next_{new_offset}")) + + if nav_btns: + btn.append(nav_btns) + + await query.message.edit_reply_markup(reply_markup=InlineKeyboardMarkup(btn)) + async def advantage_spell_chok(message, s): search = message.text google_search = search.replace(" ", "+")