├── runtime.txt
├── Procfile
├── heroku.yml
├── koyab.yml
├── Dockerfile
├── start.sh
├── docker-compose.yml
├── plugins
├── mntgxo.py
├── webcode.py
├── Extra
│ ├── sticker.py
│ ├── echo.py
│ ├── telegraph.py
│ ├── share.py
│ ├── shell.py
│ ├── eval.py
│ ├── password.py
│ ├── carbon.py
│ ├── tts.py
│ ├── tr.py
│ ├── paste.py
│ ├── pin.py
│ ├── feedback.py
│ ├── json.py
│ ├── promote.py
│ ├── short.py
│ └── font.py
├── channel.py
├── mnbots.py
├── movies_series.py
├── banned.py
├── etc.py
├── broadcast.py
├── inline.py
├── connection.py
├── mn_deletefiles.py
├── misc.py
├── index.py
├── filters.py
├── p_ttishow.py
└── commands.py
├── logging.conf
├── requirements.txt
├── app.json
├── database
├── filters_mdb.py
├── connections_mdb.py
├── users_chats_db.py
└── ia_filterdb.py
├── bot.py
├── info.py
├── README.md
├── Script.py
├── utils.py
└── LICENSE
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.10.10
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: python3 bot.py
2 | worker: python3 bot.py
3 |
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
--------------------------------------------------------------------------------
/koyab.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10
2 | WORKDIR /app
3 | COPY . /app/
4 | RUN pip3 install -r requirements.txt
5 | CMD ["python3", "bot.py"]
6 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | if [ -z $UPSTREAM_REPO ]
2 | then
3 | echo "Cloning main Repository"
4 | git clone https://github.com/mn-bots/shobanafilterbot.git /shobanafilterbot
5 | else
6 | echo "Cloning Custom Repo from $UPSTREAM_REPO "
7 | git clone $UPSTREAM_REPO /shobanafilterbot
8 | fi
9 | cd /shobanafilterbot
10 | pip3 install -U -r requirements.txt
11 | echo "Starting Bot...."
12 | python3 bot.py
13 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.10"
2 | services:
3 | worker:
4 | build: .
5 | environment:
6 | BOT_TOKEN: $BOT_TOKEN
7 | API_ID: $API_ID
8 | API_HASH: $API_HASH
9 | CHANNELS: $CHANNELS
10 | ADMINS: $ADMINS
11 | LOG_CHANNEL: $LOG_CHANNEL
12 | DATABASE_NAME: $DATABASE_NAME
13 | DATABASE_URI: $DATABASE_URI
14 | HEROKU_API_KEY: $HEROKU_API_KEY
15 |
--------------------------------------------------------------------------------
/plugins/mntgxo.py:
--------------------------------------------------------------------------------
1 | # @MrMNTG @MusammilN
2 | from pyrogram import filters, Client
3 | from pyrogram.types import Message
4 | from utils import JOIN_REQUEST_USERS
5 | from info import ADMINS
6 |
7 | @Client.on_message(filters.command("clear_join_users") & filters.user(ADMINS))
8 | async def clear_join_users(_, message: Message):
9 | JOIN_REQUEST_USERS.clear()
10 | await message.reply_text("✅ Cleared all join request users.")
11 |
--------------------------------------------------------------------------------
/plugins/webcode.py:
--------------------------------------------------------------------------------
1 | from aiohttp import web as webserver
2 |
3 | routes = webserver.RouteTableDef()
4 |
5 | async def bot_run():
6 | _app = webserver.Application(client_max_size=30000000)
7 | _app.add_routes(routes)
8 | return _app
9 |
10 | @routes.get("/", allow_head=True)
11 | async def root_route_handler(request):
12 | return webserver.json_response(" Web Supported . . . ! This is a preview of WeB . . .! ! !")
13 |
14 |
--------------------------------------------------------------------------------
/plugins/Extra/sticker.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters
2 |
3 | @Client.on_message(filters.command(["stickerid"]))
4 | async def stickerid(bot, message):
5 | if message.reply_to_message.sticker:
6 | await message.reply(f"**Sticker ID is** \n `{message.reply_to_message.sticker.file_id}` \n \n ** Unique ID is ** \n\n`{message.reply_to_message.sticker.file_unique_id}`", quote=True)
7 | else:
8 | await message.reply("Oops !! Not a sticker file")
9 |
--------------------------------------------------------------------------------
/plugins/channel.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters
2 | from info import CHANNELS
3 | from database.ia_filterdb import save_file
4 |
5 | media_filter = filters.document | filters.video | filters.audio
6 |
7 |
8 | @Client.on_message(filters.chat(CHANNELS) & media_filter)
9 | async def media(bot, message):
10 | """Media Handler"""
11 | for file_type in ("document", "video"):
12 | media = getattr(message, file_type, None)
13 | if media is not None:
14 | break
15 | else:
16 | return
17 |
18 | media.file_type = file_type
19 | media.caption = message.caption
20 | await save_file(media)
21 |
--------------------------------------------------------------------------------
/plugins/mnbots.py:
--------------------------------------------------------------------------------
1 | # @MrMNTG @MusammilN
2 | from pyrogram import Client
3 | from pyrogram.types import ChatJoinRequest
4 | from database.users_chats_db import db
5 | from utils import JOIN_REQUEST_USERS
6 |
7 | @Client.on_chat_join_request()
8 | async def join_request_handler(client, update: ChatJoinRequest):
9 | user_id = update.from_user.id
10 | chat_id = update.chat.id
11 |
12 | auth_channels = await db.get_auth_channels()
13 | if chat_id in auth_channels:
14 | if user_id not in JOIN_REQUEST_USERS:
15 | JOIN_REQUEST_USERS[user_id] = set()
16 | JOIN_REQUEST_USERS[user_id].add(chat_id)
17 |
18 | # @MrMNTG @MusammilN
19 |
--------------------------------------------------------------------------------
/logging.conf:
--------------------------------------------------------------------------------
1 | [loggers]
2 | keys=root
3 |
4 | [handlers]
5 | keys=consoleHandler,fileHandler
6 |
7 | [formatters]
8 | keys=consoleFormatter,fileFormatter
9 |
10 | [logger_root]
11 | level=DEBUG
12 | handlers=consoleHandler,fileHandler
13 |
14 | [handler_consoleHandler]
15 | class=StreamHandler
16 | level=INFO
17 | formatter=consoleFormatter
18 | args=(sys.stdout,)
19 |
20 | [handler_fileHandler]
21 | class=FileHandler
22 | level=ERROR
23 | formatter=fileFormatter
24 | args=('TelegramBot.txt','w',)
25 |
26 | [formatter_consoleFormatter]
27 | format=%(asctime)s - %(lineno)d - %(name)s - %(module)s - %(levelname)s - %(message)s
28 | datefmt=%I:%M:%S %p
29 |
30 | [formatter_fileFormatter]
31 | format=[%(asctime)s:%(name)s:%(lineno)d:%(levelname)s] %(message)s
32 | datefmt=%m/%d/%Y %I:%M:%S %p
33 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiofiles
2 | aiohttp
3 | apscheduler
4 | bs4
5 | cinemagoer
6 | colorama
7 | countryinfo
8 | datetime
9 | dnspython>=2.3.0
10 | ffmpeg
11 | ffmpeg-python>=0.2.0
12 | Flask
13 | fuzzywuzzy
14 | gTTS>=2.3.0
15 | googletrans>=3.1.0a0
16 | gunicorn>=20.1.0
17 | hachoir
18 | Heroku3
19 | humanize>=4.6.0
20 | httpx[http2]
21 | itsdangerous>=2.0.0
22 | jinja2
23 | NumPy
24 | openai>=0.25.0
25 | opencv-python-headless>=4.7.0.68
26 | Pillow>=9.4.0
27 | psutil>=5.9.0
28 | pycryptodome
29 | pygments
30 | pyromod
31 | pymongo==3.12.3
32 | motor==2.5.1
33 | marshmallow==3.20.1
34 | umongo==3.0.1
35 | PyPDF2
36 | python-decouple
37 | python-dotenv>=0.21.0
38 | python-Levenshtein
39 | pyshorteners
40 | requests
41 | search_engine_parser
42 | shortzy
43 | speedtest>=0.0.1
44 | speedtest-cli
45 | telegraph
46 | tgcrypto
47 | pyrotgfork
48 | ujson
49 | wget
50 | werkzeug>=2.0
51 | wheel
52 | yt-dlp
53 | youtube-dl
54 | youtube-search
55 | youtube-search-python>=1.4.6
56 | ytthumb
57 | git+https://github.com/agronholm/anyio
58 | git+https://github.com/Joelkb/cinemagoer
59 |
60 |
--------------------------------------------------------------------------------
/plugins/movies_series.py:
--------------------------------------------------------------------------------
1 | #please give credits https://github.com/MN-BOTS
2 | from pyrogram.enums import ParseMode
3 | from pyrogram import Client, filters
4 | from pyrogram.types import Message
5 | from database.ia_filterdb import get_movie_list, get_series_grouped
6 |
7 | @Client.on_message(filters.private & filters.command("movies"))
8 | async def list_movies(bot: Client, message: Message):
9 | movies = await get_movie_list()
10 | if not movies:
11 | return await message.reply("❌ No recent movies found.")
12 |
13 | msg = "🎬 Latest Movies:\n\n"
14 | msg += "\n".join(f"✅
7 | A powerful and versatile Telegram bot designed for filtering, automation, and much more!
8 |
91 |
92 |
101 |
102 | {m}" for m in movies)
15 | await message.reply(msg[:4096], parse_mode=ParseMode.HTML)
16 |
17 | @Client.on_message(filters.private & filters.command("series"))
18 | async def list_series(bot: Client, message: Message):
19 | series_data = await get_series_grouped()
20 | if not series_data:
21 | return await message.reply("❌ No recent series episodes found.")
22 |
23 | msg = "📺 Latest Series:\n\n"
24 | for title, episodes in series_data.items():
25 | ep_list = ", ".join(str(e) for e in episodes)
26 | msg += f"✅ {title} - Episodes {ep_list}\n"
27 |
28 | await message.reply(msg[:4096], parse_mode=ParseMode.HTML)
29 |
--------------------------------------------------------------------------------
/plugins/Extra/echo.py:
--------------------------------------------------------------------------------
1 |
2 | from pyrogram import Client, filters, enums
3 | from pyrogram.types import *
4 |
5 | @Client.on_message(filters.command("echo") & filters.group)
6 | async def echo(client, message):
7 | try:
8 | # Check user permissions
9 | user = await client.get_chat_member(message.chat.id, message.from_user.id)
10 | if user.status not in [enums.ChatMemberStatus.OWNER, enums.ChatMemberStatus.ADMINISTRATOR]:
11 | await message.reply_text("You don't have permission to use this command .")
12 | return
13 | except Exception as error:
14 | # Handle case where bot lacks permissions to get member info
15 | await message.reply_text(f"An error occurred. I may not have permission to check user status. {error}")
16 | return
17 |
18 | reply = message.reply_to_message
19 | chat_id = message.chat.id
20 |
21 | if not reply:
22 | # No message replied to
23 | await message.reply_text("Please reply to a message to echo its content.")
24 | return
25 |
26 | await reply.reply_text(message.text.split(None, 1)[1])
27 | await message.delete()
28 |
29 |
30 | @Client.on_message(filters.command("echo") & filters.private)
31 | async def echoptp(client, message):
32 | await message.reply_text("Sorry dude This command Only work in group 😊")
33 |
--------------------------------------------------------------------------------
/plugins/Extra/telegraph.py:
--------------------------------------------------------------------------------
1 | import os
2 | import requests
3 | from pyrogram import Client, filters
4 | from pyrogram.types import Message
5 |
6 | @Client.on_message(filters.command(["img", "cup", "telegraph"], prefixes="/") & filters.reply)
7 | async def c_upload(client, message: Message):
8 | reply = message.reply_to_message
9 |
10 | if not reply.media:
11 | return await message.reply_text("Reply to a media to upload it to Cloud.")
12 |
13 | if reply.document and reply.document.file_size > 512 * 1024 * 1024: # 512 MB
14 | return await message.reply_text("File size limit is 512 MB.")
15 |
16 | msg = await message.reply_text("Processing...")
17 |
18 | try:
19 | downloaded_media = await reply.download()
20 |
21 | if not downloaded_media:
22 | return await msg.edit_text("Something went wrong during download.")
23 |
24 | with open(downloaded_media, "rb") as f:
25 | data = f.read()
26 | resp = requests.post("https://envs.sh", files={"file": data})
27 | if resp.status_code == 200:
28 | await msg.edit_text(f"`{resp.text}`")
29 | else:
30 | await msg.edit_text("Something went wrong. Please try again later.")
31 |
32 | os.remove(downloaded_media)
33 |
34 | except Exception as e:
35 | await msg.edit_text(f"Error: {str(e)}")
36 |
37 |
--------------------------------------------------------------------------------
/plugins/Extra/share.py:
--------------------------------------------------------------------------------
1 | # Don't Remove Credit @VJ_Botz
2 | # Subscribe YouTube Channel For Amazing Bot @Tech_VJ
3 | # Ask Doubt on telegram @KingVJ01
4 |
5 |
6 | import os
7 | from pyrogram import Client, filters
8 | from urllib.parse import quote
9 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
10 |
11 | @Client.on_message(filters.command(["share_text", "share", "sharetext"]))
12 | async def share_text(client, message):
13 | vj = await client.ask(chat_id = message.from_user.id, text = "Now Send me your text.")
14 | if vj and (vj.text or vj.caption):
15 | input_text = vj.text or vj.caption
16 | else:
17 | await vj.reply_text(
18 | text=f"**Notice:**\n\n1. Send Any Text Messages.\n2. No Media Support\n\n**Any Question Join Support Chat**",
19 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Updates Channel", url=f"https://t.me/mnbots"),
20 | InlineKeyboardButton('ʀᴇᴘᴏ', url='https://github.com/mn-bots/ShobanaFilterBot')]])
21 | )
22 | return
23 | await vj.reply_text(
24 | text=f"**Here is Your Sharing Text 👇**\n\nhttps://t.me/share/url?url=" + quote(input_text),
25 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("♂️ Share", url=f"https://t.me/share/url?url={quote(input_text)}")]])
26 | )
27 |
--------------------------------------------------------------------------------
/plugins/banned.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters
2 | from utils import temp
3 | from pyrogram.types import Message
4 | from database.users_chats_db import db
5 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
6 | from info import SUPPORT_CHAT
7 |
8 | async def banned_users(_, client, message: Message):
9 | return (
10 | message.from_user is not None or not message.sender_chat
11 | ) and message.from_user.id in temp.BANNED_USERS
12 |
13 | banned_user = filters.create(banned_users)
14 |
15 | async def disabled_chat(_, client, message: Message):
16 | return message.chat.id in temp.BANNED_CHATS
17 |
18 | disabled_group=filters.create(disabled_chat)
19 |
20 |
21 | @Client.on_message(filters.private & banned_user & filters.incoming)
22 | async def ban_reply(bot, message):
23 | ban = await db.get_ban_status(message.from_user.id)
24 | await message.reply(f'Sorry Dude, You are Banned to use Me. \nBan Reason: {ban["ban_reason"]}')
25 |
26 | @Client.on_message(filters.group & disabled_group & filters.incoming)
27 | async def grp_bd(bot, message):
28 | buttons = [[
29 | InlineKeyboardButton('𝚂𝚞𝚙𝚙𝚘𝚛𝚝', url=f'https://t.me/mnbots_support')
30 | ]]
31 | reply_markup=InlineKeyboardMarkup(buttons)
32 | vazha = await db.get_chat(message.chat.id)
33 | k = await message.reply(
34 | text=f"CHAT NOT ALLOWED 🐞\n\nMy admins has restricted me from working here ! If you want to know more about it contact support..\nReason : {vazha['reason']}.",
35 | reply_markup=reply_markup)
36 | try:
37 | await k.pin()
38 | except:
39 | pass
40 | await bot.leave_chat(message.chat.id)
41 |
--------------------------------------------------------------------------------
/plugins/Extra/shell.py:
--------------------------------------------------------------------------------
1 | import os
2 | import io
3 | import asyncio
4 | from pyrogram import Client, filters
5 | from info import ADMINS
6 | from subprocess import getoutput as run, TimeoutExpired
7 |
8 |
9 | @Client.on_message(filters.command(["sh", "shell"]) & filters.user(ADMINS))
10 | async def shell(client, message):
11 | # Ensure the message contains a command
12 | if len(message.command) < 2:
13 | await message.reply("Please provide a shell command to execute.")
14 | return
15 |
16 | # Extract the code to be run
17 | code = message.text.split(None, 1)[1]
18 | message_text = await message.reply_text("`Running...`")
19 |
20 | try:
21 | # Run the shell command with a timeout (e.g., 60 seconds)
22 | output = await asyncio.to_thread(run, code)
23 |
24 | if len(output) > 4096:
25 | # If the output is too large, send it as a document
26 | with io.BytesIO(str.encode(output)) as out_file:
27 | out_file.name = "shell_output.txt"
28 | await message.reply_document(document=out_file, disable_notification=True)
29 | await message_text.delete()
30 | else:
31 | # Send the output as a regular message
32 | await message_text.edit(f"Output:\n{output}")
33 | except TimeoutExpired:
34 | # Handle command timeouts
35 | await message_text.edit("The command timed out. Please try again with a shorter command.")
36 | except Exception as e:
37 | # Catch any other errors (e.g., invalid command)
38 | await message_text.edit(f"An error occurred while running the command: {e}")
39 | print(f"Error executing shell command: {e}")
40 |
--------------------------------------------------------------------------------
/plugins/Extra/eval.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters
2 | from pyrogram.errors import MessageTooLong
3 | import sys, os
4 | import re
5 | import traceback
6 | from io import StringIO
7 | from info import ADMINS
8 |
9 | @Client.on_message(filters.command("eval") & filters.user(ADMINS))
10 | async def executor(client, message):
11 | try:
12 | code = message.text.split(" ", 1)[1]
13 | except:
14 | return await message.reply('Command Incomplete!\nUsage: /eval your_python_code')
15 | old_stderr = sys.stderr
16 | old_stdout = sys.stdout
17 | redirected_output = sys.stdout = StringIO()
18 | redirected_error = sys.stderr = StringIO()
19 | stdout, stderr, exc = None, None, None
20 | try:
21 | await aexec(code, client, message)
22 | except:
23 | exc = traceback.format_exc()
24 | stdout = redirected_output.getvalue()
25 | stderr = redirected_error.getvalue()
26 | sys.stdout = old_stdout
27 | sys.stderr = old_stderr
28 | evaluation = ""
29 | if exc:
30 | evaluation = exc
31 | elif stderr:
32 | evaluation = stderr
33 | elif stdout:
34 | evaluation = stdout
35 | else:
36 | evaluation = "Success!"
37 | final_output = f"Output:\n\n{evaluation}"
38 | try:
39 | await message.reply(final_output)
40 | except MessageTooLong:
41 | with open('eval.txt', 'w+') as outfile:
42 | outfile.write(final_output)
43 | await message.reply_document('eval.txt')
44 | os.remove('eval.txt')
45 |
46 |
47 | async def aexec(code, client, message):
48 | exec(
49 | "async def __aexec(client, message): "
50 | + "".join(f"\n {a}" for a in code.split("\n"))
51 | )
52 | return await locals()["__aexec"](client, message)
53 |
54 |
--------------------------------------------------------------------------------
/plugins/Extra/password.py:
--------------------------------------------------------------------------------
1 | import random
2 | from pyrogram import Client, filters, enums
3 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
4 |
5 |
6 | @Client.on_message(filters.command(["genpassword", 'genpw']))
7 | async def password(bot, update):
8 | message = await update.reply_text(text="`Processing...`")
9 |
10 | # Define available characters for password generation
11 | lowercase = "abcdefghijklmnopqrstuvwxyz"
12 | uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
13 | digits = "1234567890"
14 | special = "!@#$%^&*()_+"
15 |
16 | # Combine all characters
17 | all_characters = lowercase + uppercase + digits + special
18 |
19 | # Check if a length is provided; otherwise, use a random default choice
20 | if len(update.command) > 1:
21 | try:
22 | qw = update.text.split(" ", 1)[1]
23 | limit = int(qw)
24 | if limit < 4 or limit > 32: # Ensure the length is reasonable (between 4 and 32 characters)
25 | raise ValueError("Password length must be between 4 and 32 characters.")
26 | except (ValueError, IndexError):
27 | await message.edit_text("Please provide a valid password length (between 4 and 32 characters).")
28 | return
29 | else:
30 | # Default lengths to choose from
31 | ST = ["5", "7", "6", "9", "10", "12", "14", "16"]
32 | qw = random.choice(ST)
33 | limit = int(qw)
34 |
35 | # Generate random password
36 | random_value = "".join(random.sample(all_characters, limit))
37 |
38 | # Prepare the response text
39 | txt = f"Limit: {str(limit)} \nPassword: {random_value}"
40 |
41 | # Inline buttons
42 | btn = InlineKeyboardMarkup([
43 | [InlineKeyboardButton('MN Bots', url='https://t.me/mnbots'),
44 | InlineKeyboardButton('ʀᴇᴘᴏ', url='https://github.com/mn-bots/ShobanaFilterBot')]
45 | ])
46 |
47 | # Edit the message to show the generated password
48 | await message.edit_text(text=txt, reply_markup=btn, parse_mode=enums.ParseMode.HTML)
49 |
--------------------------------------------------------------------------------
/plugins/etc.py:
--------------------------------------------------------------------------------
1 | import random
2 | import re, asyncio, time, shutil, psutil, os, sys
3 | from pyrogram import Client, filters, enums
4 | from pyrogram.types import *
5 | from info import BOT_START_TIME, ADMINS
6 | from utils import humanbytes
7 |
8 | CMD = ["/", "."]
9 |
10 | @Client.on_message(filters.command("ping", CMD) & filters.user(ADMINS))
11 | async def ping(_, message):
12 | start_t = time.time()
13 | rm = await message.reply_text("...........")
14 | end_t = time.time()
15 | time_taken_s = (end_t - start_t) * 1000
16 | await rm.edit(f"𝖯𝗂𝗇𝗀!\n{time_taken_s:.3f} ms")
17 |
18 | @Client.on_message(filters.command("usage") & filters.user(ADMINS))
19 | async def stats(bot, update):
20 | currentTime = time.strftime("%Hh%Mm%Ss", time.gmtime(time.time() - BOT_START_TIME))
21 | total, used, free = shutil.disk_usage(".")
22 | total = humanbytes(total)
23 | used = humanbytes(used)
24 | free = humanbytes(free)
25 | cpu_usage = psutil.cpu_percent()
26 | ram_usage = psutil.virtual_memory().percent
27 | disk_usage = psutil.disk_usage('/').percent
28 |
29 | ms_g = f"""⚙️ 𝖡𝗈𝗍 𝖲𝗍𝖺𝗍𝗎𝗌
30 |
31 | 🕔 𝖴𝗉𝗍𝗂𝗆𝖾: {currentTime}
32 | 🛠 𝖢𝖯𝖴 𝖴𝗌𝖺𝗀𝖾: {cpu_usage}%
33 | 🗜 𝖱𝖠𝖬 𝖴𝗌𝖺𝗀𝖾: {ram_usage}%
34 | 🗂 𝖳𝗈𝗍𝖺𝗅 𝖣𝗂𝗌𝗄 𝖲𝗉𝖺𝖼𝖾: {total}
35 | 🗳 𝖴𝗌𝖾𝖽 𝖲𝗉𝖺𝖼𝖾: {used} ({disk_usage}%)
36 | 📝 𝖥𝗋𝖾𝖾 𝖲𝗉𝖺𝖼𝖾: {free} """
37 |
38 | msg = await bot.send_message(chat_id=update.chat.id, text="__𝖯𝗋𝗈𝖼𝖾𝗌𝗌𝗂𝗇𝗀...__", parse_mode=enums.ParseMode.MARKDOWN)
39 | await msg.edit_text(text=ms_g, parse_mode=enums.ParseMode.HTML)
40 |
41 | @Client.on_message(filters.command("restart") & filters.user(ADMINS))
42 | async def stop_button(bot, message):
43 | msg = await bot.send_message(text="**𝖡𝗈𝗍 𝖨𝗌 𝖱𝖾𝗌𝗍𝖺𝗋𝗍𝗂𝗇𝗀...🪄**", chat_id=message.chat.id)
44 | await asyncio.sleep(3)
45 | await msg.edit("**𝖡𝗈𝗍 𝖱𝖾𝗌𝗍𝖺𝗋𝗍𝖾𝖽 𝖲𝗎𝖼𝖼𝖾𝗌𝗌𝖿𝗎𝗅𝗅𝗒 ! 𝖱𝖾𝖺𝖽𝗒 𝖳𝗈 𝖬𝗈𝗏𝖾 𝖮𝗇 💯**")
46 | os.execl(sys.executable, sys.executable, *sys.argv)
47 |
--------------------------------------------------------------------------------
/plugins/Extra/carbon.py:
--------------------------------------------------------------------------------
1 | import aiohttp
2 | import logging
3 | from io import BytesIO
4 | from pyrogram import Client, filters
5 |
6 | # Configure logging
7 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
8 | logger = logging.getLogger(__name__)
9 |
10 | async def make_carbon(code):
11 | """Generate a carbon image from code using Carbonara API."""
12 | url = "https://carbonara.solopov.dev/api/cook"
13 | try:
14 | async with aiohttp.ClientSession() as session:
15 | async with session.post(url, json={"code": code}) as resp:
16 | if resp.status != 200:
17 | logger.error(f"Failed to fetch carbon image. Status code: {resp.status}")
18 | return None
19 | image = BytesIO(await resp.read())
20 | image.name = "carbon.png"
21 | return image
22 | except Exception as e:
23 | logger.error(f"Error while making carbon: {e}")
24 | return None
25 |
26 | @Client.on_message(filters.command("carbon"))
27 | async def _carbon(client, message):
28 | """Handler for the /carbon command to generate a carbon image from replied text."""
29 | replied = message.reply_to_message
30 | if not replied or not (replied.text or replied.caption):
31 | await message.reply_text("**Please reply to a text message to generate a carbon.**")
32 | return
33 |
34 | text_content = replied.text or replied.caption
35 | text = await message.reply("**Processing your request...**")
36 |
37 | carbon_image = await make_carbon(text_content)
38 | if carbon_image is None:
39 | await text.edit("**Failed to generate carbon. Please try again later.**")
40 | return
41 |
42 | try:
43 | await text.edit("**Uploading carbon image...**")
44 | await message.reply_photo(carbon_image, caption="Here is your carbon image!")
45 | except Exception as e:
46 | logger.error(f"Error while uploading carbon image: {e}")
47 | await text.edit("**Failed to upload carbon image.**")
48 | finally:
49 | carbon_image.close()
50 | await text.delete()
51 |
52 | # Add additional error handling or functionality if needed
53 |
--------------------------------------------------------------------------------
/plugins/Extra/tts.py:
--------------------------------------------------------------------------------
1 | import traceback
2 | from asyncio import get_running_loop
3 | from io import BytesIO
4 | from googletrans import Translator, LANGUAGES
5 | from gtts import gTTS
6 | from pyrogram import Client, filters
7 | from pyrogram.types import Message
8 |
9 |
10 | async def convert(text):
11 | try:
12 | # Detect language using Google Translate API
13 | translator = Translator()
14 | detected_lang = translator.detect(text).lang
15 | lang_name = LANGUAGES.get(detected_lang, "Unknown")
16 |
17 | # Generate the TTS (Text-to-Speech) audio
18 | tts = gTTS(text, lang=detected_lang)
19 |
20 | # Use BytesIO to store the audio in memory
21 | audio = BytesIO()
22 | tts.write_to_fp(audio)
23 | audio.seek(0) # Rewind the BytesIO object to the start
24 |
25 | audio.name = f"{lang_name}.mp3"
26 | return audio, lang_name
27 | except Exception as e:
28 | print(f"Error during TTS conversion: {e}")
29 | return None, str(e)
30 |
31 |
32 | @Client.on_message(filters.command("tts"))
33 | async def text_to_speech(_, message: Message):
34 | if not message.reply_to_message:
35 | return await message.reply_text("Please reply to a message containing text.")
36 |
37 | if not message.reply_to_message.text:
38 | return await message.reply_text("The replied message does not contain any text.")
39 |
40 | text = message.reply_to_message.text
41 | m = await message.reply_text("Processing...")
42 |
43 | try:
44 | # Asynchronously handle the TTS conversion
45 | loop = get_running_loop()
46 | audio, lang_name = await loop.run_in_executor(None, convert, text)
47 |
48 | if audio:
49 | await message.reply_audio(audio, caption=f"Audio in {lang_name} language.")
50 | await m.delete()
51 | audio.close() # Ensure resource cleanup
52 | else:
53 | await m.edit(f"Error: {lang_name}") # lang_name contains the error message
54 | except Exception as e:
55 | await m.edit("An error occurred.")
56 | print(f"Error: {e}")
57 | e_traceback = traceback.format_exc()
58 | print(e_traceback)
59 |
--------------------------------------------------------------------------------
/plugins/Extra/tr.py:
--------------------------------------------------------------------------------
1 | from googletrans import Translator, LANGUAGES
2 | from pyrogram import Client, filters
3 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
4 | import re
5 |
6 | @Client.on_message(filters.command(["tr"]))
7 | async def translate(client, message):
8 | if message.reply_to_message:
9 | try:
10 | # Extract language code from the command, default to auto-detection if not specified
11 | text = message.text.strip()
12 | command_parts = text.split("/tr")
13 |
14 | if len(command_parts) > 1:
15 | lang_code = command_parts[1].strip().lower()
16 | if lang_code not in LANGUAGES:
17 | raise ValueError(f"Invalid language code: {lang_code}")
18 | else:
19 | lang_code = 'auto' # Use 'auto' for language detection
20 |
21 | tr_text = message.reply_to_message.text
22 | translator = Translator()
23 |
24 | # If language is 'auto', detect the source language
25 | if lang_code == 'auto':
26 | detected = translator.detect(tr_text)
27 | from_lang = detected.lang
28 | to_lang = 'auto'
29 | translated_text = translator.translate(tr_text, src=from_lang, dest=lang_code).text
30 | else:
31 | from_lang = 'auto' # Set to auto for now since user is specifying the destination
32 | to_lang = lang_code
33 | translated_text = translator.translate(tr_text, dest=to_lang).text
34 |
35 | # Construct response
36 | from_lang_name = LANGUAGES.get(from_lang, "Unknown")
37 | to_lang_name = LANGUAGES.get(to_lang, "Unknown")
38 | reply_text = f"Translated from {from_lang_name} to {to_lang_name}:\n\n{translated_text}"
39 |
40 | # Print to console for logging
41 | print(f"Translated from {from_lang_name} to {to_lang_name}: {translated_text}")
42 |
43 | # Reply with the translated text
44 | await message.reply_text(reply_text)
45 |
46 | except Exception as e:
47 | print(f"Error: {e}")
48 | await message.reply_text(f"An error occurred during translation: {e}")
49 |
50 | else:
51 | await message.reply_text("You can use this command by replying to a message.")
52 |
--------------------------------------------------------------------------------
/plugins/Extra/paste.py:
--------------------------------------------------------------------------------
1 | import os, re, json, aiohttp, requests
2 | from pyrogram import Client, filters
3 |
4 | #Headers
5 | headers = {
6 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36",
7 | "content-type": "application/json",
8 | }
9 |
10 | #Pastebins
11 | async def p_paste(message, extension=None):
12 | siteurl = "https://pasty.lus.pm/api/v1/pastes"
13 | data = {"content": message}
14 | try:
15 | response = requests.post(url=siteurl, data=json.dumps(data), headers=headers)
16 | except Exception as e:
17 | return {"error": str(e)}
18 | if response.ok:
19 | response = response.json()
20 | purl = (
21 | f"https://pasty.lus.pm/{response['id']}.{extension}"
22 | if extension
23 | else f"https://pasty.lus.pm/{response['id']}.txt"
24 | )
25 | return {
26 | "url": purl,
27 | "raw": f"https://pasty.lus.pm/{response['id']}/raw",
28 | "bin": "Pasty",
29 | }
30 | return {"error": "UNABLE TO REACH pasty.lus.pm"}
31 |
32 |
33 |
34 | @Client.on_message(filters.command(["tgpaste", "pasty", "paste"]))
35 | async def pasty(client, message):
36 | pablo = await message.reply_text("`Pʟᴇᴀꜱᴇ Wᴀɪᴛ...`")
37 | tex_t = message.text
38 | if ' ' in message.text:
39 | message_s = message.text.split(" ", 1)[1]
40 | elif message.reply_to_message:
41 | message_s = message.reply_to_message.text
42 | else:
43 | await message.reply("Sᴏʀʀʏ No Iɴ Pᴜᴛ. Pʟᴇᴀꜱᴇ Rᴇᴩʟʏ To A Tᴇxᴛ Oʀ /paste Wɪᴛʜ Tᴇxᴛ")
44 | if not tex_t:
45 | if not message.reply_to_message:
46 | await pablo.edit("Oɴʟʏ Tᴇxᴛ Aɴᴅ Dᴏᴄᴜᴍᴇɴᴛs Aʀᴇ Sᴜᴘᴘᴏʀᴛᴇᴅ")
47 | return
48 | if not message.reply_to_message.text:
49 | file = await message.reply_to_message.download()
50 | m_list = open(file, "r").read()
51 | message_s = m_list
52 | os.remove(file)
53 | elif message.reply_to_message.text:
54 | message_s = message.reply_to_message.text
55 |
56 | ext = "py"
57 | x = await p_paste(message_s, ext)
58 | p_link = x["url"]
59 | p_raw = x["raw"]
60 |
61 | pasted = f"**Sᴜᴄᴄᴇssғᴜʟʟʏ Pᴀsᴛᴇ Tᴏ Pᴀsᴛʏ**\n\n**Lɪɴᴋ:** • [CʟɪᴄᴋHᴇʀᴇ]({p_link})\n\n**Rᴀᴡ Lɪɴᴋ:** • [CʟɪᴄᴋHᴇʀᴇ]({p_raw})"
62 | await pablo.edit(pasted, disable_web_page_preview=True)
63 |
--------------------------------------------------------------------------------
/plugins/Extra/pin.py:
--------------------------------------------------------------------------------
1 | from pyrogram.types import Message
2 | from pyrogram import Client, filters
3 | from pyrogram.types import Message
4 | from pyrogram import filters, enums
5 | from pyrogram.types import *
6 |
7 | async def admin_check(message: Message) -> bool:
8 | if not message.from_user:
9 | return False
10 |
11 | if message.chat.type not in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
12 | return False
13 |
14 | if message.from_user.id in [
15 | 777000, # Telegram Service Notifications
16 | 1087968824 # GroupAnonymousBot
17 | ]:
18 | return True
19 |
20 | client = message._client
21 | chat_id = message.chat.id
22 | user_id = message.from_user.id
23 |
24 | check_status = await client.get_chat_member(
25 | chat_id=chat_id,
26 | user_id=user_id
27 | )
28 | admin_strings = [enums.ChatMemberStatus.OWNER, enums.ChatMemberStatus.ADMINISTRATOR]
29 | # https://git.colinshark.de/PyroBot/PyroBot/src/branch/master/pyrobot/modules/admin.py#L69
30 | if check_status.status not in admin_strings:
31 | return False
32 | else:
33 | return True
34 |
35 | async def admin_filter_f(filt, client, message):
36 | return await admin_check(message)
37 |
38 | admin_fliter = filters.create(func=admin_filter_f, name="AdminFilter")
39 |
40 |
41 | @Client.on_message(filters.command("pin") & admin_fliter)
42 | async def pin(_, message: Message):
43 | if not message.reply_to_message:
44 | return
45 | await message.reply_to_message.pin()
46 | await message.reply_text("I Have Pinned That message")
47 |
48 |
49 | @Client.on_message(filters.command("unpin") & admin_fliter)
50 | async def unpin(_, message: Message):
51 | if not message.reply_to_message:
52 | return
53 | await message.reply_to_message.unpin()
54 | await message.reply_text("I Have UnPinned That message")
55 |
56 |
57 | @Client.on_message(filters.command("unpin_all") & filters.group)
58 | async def unpinall_handler(client, message: Message):
59 | try:
60 | user = await client.get_chat_member(message.chat.id , message.from_user.id)
61 | if user.status not in [enums.ChatMemberStatus.OWNER , enums.ChatMemberStatus.ADMINISTRATOR]:
62 | raise PermissionError("You are not allowed to use this command")
63 | await client.unpin_all_chat_messages(message.chat.id)
64 | except Exception as e:
65 | await message.reply_text(f"{e}")
66 |
--------------------------------------------------------------------------------
/plugins/Extra/feedback.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters
2 | from pyrogram.types import Message
3 | from info import LOG_CHANNEL
4 |
5 | # Function to handle feedback and bug reporting
6 | @Client.on_message(filters.command(["bug", "bugs", "feedback"]))
7 | async def bug_handler(client: Client, message: Message):
8 | try:
9 | # Check if the command has additional arguments or a reply to a message
10 | if len(message.command) < 2:
11 | if message.reply_to_message and message.reply_to_message.text:
12 | bug_report = message.reply_to_message.text.strip()
13 | else:
14 | return await message.reply_text(
15 | "Please reply to a text message or provide a description of the bug."
16 | )
17 | else:
18 | bug_report = message.text.split(" ", 1)[1].strip()
19 |
20 | # Check for empty bug reports
21 | if not bug_report:
22 | return await message.reply_text("The bug description cannot be empty. Please try again.")
23 |
24 | # Construct the acknowledgment message
25 | response_message = (
26 | f"Hi {message.from_user.mention},\n"
27 | "Thank you for reporting the issue. It has been forwarded to the developer."
28 | )
29 | await message.reply_text(response_message)
30 |
31 | # Log the bug report to the designated channel
32 | log_message = (
33 | f"#BugReport\n\n"
34 | f"**User:** {message.from_user.mention} ([User ID: {message.from_user.id}])\n"
35 | f"**Chat:** {message.chat.title if message.chat.type != 'private' else 'Private Chat'}\n"
36 | f"**Chat ID:** {message.chat.id}\n"
37 | f"**Bug Description:**\n{bug_report}"
38 | )
39 | await client.send_message(LOG_CHANNEL, text=log_message)
40 |
41 | except Exception as e:
42 | # Error handling and reporting to developers
43 | await message.reply_text(
44 | "An unexpected error occurred while processing your request. Please try again later."
45 | )
46 | error_message = (
47 | f"#Error\n\n"
48 | f"**Error occurred in bug handler:**\n{str(e)}\n\n"
49 | f"**User:** {message.from_user.mention} ([User ID: {message.from_user.id}])\n"
50 | f"**Chat ID:** {message.chat.id}"
51 | )
52 | await client.send_message(LOG_CHANNEL, text=error_message)
53 |
54 |
--------------------------------------------------------------------------------
/plugins/broadcast.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters
2 | import datetime
3 | import time
4 | from database.users_chats_db import db
5 | from info import ADMINS
6 | from utils import broadcast_messages
7 | import asyncio
8 |
9 | BROADCAST_BATCH_SIZE = 500 # Now processes 500 users at a time
10 | BROADCAST_SLEEP = 1 # Small delay to avoid rate limits
11 |
12 | @Client.on_message(filters.command("broadcast") & filters.user(ADMINS) & filters.reply)
13 | async def broadcast(bot, message):
14 | users = await db.get_all_users()
15 | b_msg = message.reply_to_message
16 | sts = await message.reply_text("Broadcasting your messages...")
17 |
18 | start_time = time.time()
19 | total_users = await db.total_users_count()
20 | done, blocked, deleted, failed, success = 0, 0, 0, 0, 0
21 |
22 | async def send_message(user):
23 | nonlocal success, blocked, deleted, failed
24 | user_id = int(user['id'])
25 | pti, sh = await broadcast_messages(user_id, b_msg)
26 |
27 | if pti:
28 | success += 1
29 | else:
30 | if sh == "Blocked":
31 | blocked += 1
32 | await db.delete_user(user_id) # Remove blocked user
33 | elif sh == "Deleted":
34 | deleted += 1
35 | await db.delete_user(user_id) # Remove deleted user
36 | elif sh == "Error":
37 | failed += 1
38 |
39 | tasks = []
40 | async for user in users:
41 | tasks.append(send_message(user))
42 | done += 1
43 |
44 | # Process messages in batches of 500
45 | if len(tasks) >= BROADCAST_BATCH_SIZE:
46 | await asyncio.gather(*tasks)
47 | tasks = []
48 | await sts.edit(
49 | f"Broadcast in progress:\n\nTotal Users: {total_users}\nCompleted: {done} / {total_users}\n"
50 | f"Success: {success} | Blocked: {blocked} | Deleted: {deleted} | Failed: {failed}"
51 | )
52 | await asyncio.sleep(BROADCAST_SLEEP) # Small delay to avoid rate limits
53 |
54 | # Process remaining messages (if any)
55 | if tasks:
56 | await asyncio.gather(*tasks)
57 |
58 | time_taken = datetime.timedelta(seconds=int(time.time() - start_time))
59 | await sts.edit(
60 | f"Broadcast Completed in {time_taken}.\n\nTotal Users: {total_users}\n"
61 | f"Success: {success} | Blocked: {blocked} | Deleted: {deleted} | Failed: {failed}"
62 | )
63 |
--------------------------------------------------------------------------------
/plugins/Extra/json.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | from pyrogram import Client, filters
4 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
5 |
6 | # Callback for displaying message as JSON
7 | @Client.on_message(filters.command(["json", "js", "showjson"]))
8 | async def jsonify(client, message):
9 | """
10 | Handle the /json, /js, or /showjson command to display the JSON representation of a message.
11 | If the message is too large, it will save it as a file and send it instead.
12 | """
13 | the_real_message = message.reply_to_message or message # Check for a replied message, else use the current message
14 |
15 | # Inline keyboard for closing the message
16 | close_button = InlineKeyboardMarkup(
17 | [[InlineKeyboardButton(text="𝙲𝙻𝙾𝚂𝙴", callback_data="close_data")]]
18 | )
19 |
20 | try:
21 | # Format the message as JSON if possible, falling back to string representation
22 | formatted_message = json.dumps(the_real_message, default=str, indent=2, ensure_ascii=False)
23 |
24 | # Attempt to send the JSON representation of the message
25 | await message.reply_text(
26 | f"{formatted_message}", reply_markup=close_button, quote=True
27 | )
28 | except Exception as e:
29 | # Handle cases where the message is too large to send directly
30 | temp_filename = "message_data.json"
31 | try:
32 | with open(temp_filename, "w", encoding="utf8") as out_file:
33 | json.dump(the_real_message, out_file, default=str, indent=2, ensure_ascii=False)
34 |
35 | # Send the JSON as a document with an error caption
36 | await message.reply_document(
37 | document=temp_filename,
38 | caption=f"Error: {e}",
39 | disable_notification=True,
40 | quote=True,
41 | reply_markup=close_button
42 | )
43 | finally:
44 | # Ensure the temporary file is removed
45 | if os.path.exists(temp_filename):
46 | os.remove(temp_filename)
47 |
48 | # Add a callback handler for the close button
49 | @Client.on_callback_query(filters.regex("^close_data$"))
50 | async def close_callback(client, callback_query):
51 | """
52 | Handle the close button callback to delete the JSON message.
53 | """
54 | await callback_query.message.delete()
55 | await callback_query.answer("Closed", show_alert=False)
56 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shobanafilterbot",
3 | "description": "When you going to send file on telegram channel this bot will save that in database, So you can search that easily in inline mode",
4 | "stack": "container",
5 | "keywords": [
6 | "telegram",
7 | "auto-filter",
8 | "filter",
9 | "best",
10 | "indian",
11 | "pyrogram",
12 | "media",
13 | "search",
14 | "channel",
15 | "index",
16 | "inline"
17 | ],
18 | "website": "https://github.com/mn-bots/shobanafilterbot",
19 | "repository": "https://github.com/mn-bots/shobanafilterbot",
20 | "env": {
21 | "BOT_TOKEN": {
22 | "description": "Your bot token.",
23 | "required": true
24 | },
25 | "API_ID": {
26 | "description": "Get this value from https://my.telegram.org or @USERS_RO_BOT",
27 | "required": true
28 | },
29 | "API_HASH": {
30 | "description": "Get this value from https://my.telegram.org or @USERS_RO_BOT",
31 | "required": true
32 | },
33 | "CHANNELS": {
34 | "description": "Username or ID of channel or group. Separate multiple IDs by space.",
35 | "required": false
36 | },
37 | "ADMINS": {
38 | "description": "Username or ID of Admin. Separate multiple Admins by space.",
39 | "required": true
40 | },
41 | "PICS": {
42 | "description": "Add some telegraph link of pictures .",
43 | "required": false
44 | },
45 | "LOG_CHANNEL": {
46 | "description": "Bot Logs,Give a channel id with -100xxxxxxx",
47 | "required": true
48 | },
49 | "AUTH_USERS": {
50 | "description": "Username or ID of users to give access of inline search. Separate multiple users by space.\nLeave it empty if you don't want to restrict bot usage.",
51 | "required": false
52 | },
53 | "AUTH_CHANNEL": {
54 | "description": "ID of channel.Make sure bot is admin in this channel. Without subscribing this channel users cannot use bot.",
55 | "required": false
56 | },
57 | "DATABASE_URI": {
58 | "description": "mongoDB URI. Get this value from https://www.mongodb.com.",
59 | "required": true
60 | },
61 | "DATABASE_NAME": {
62 | "description": "Name of the database in mongoDB.",
63 | "required": false
64 | },
65 | "COLLECTION_NAME": {
66 | "description": "Name of the collections. Defaults to Telegram_files. If you are using the same database, then use different collection name for each bot",
67 | "value": "Anurag_files",
68 | "required": false
69 | }
70 | },
71 | "addons": [],
72 | "buildpacks": [{
73 | "url": "heroku/python"
74 | }],
75 | "formation": {
76 | "worker": {
77 | "quantity": 1,
78 | "size": "free"
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/plugins/Extra/promote.py:
--------------------------------------------------------------------------------
1 | from pyrogram import *
2 | from pyrogram.types import *
3 |
4 | @Client.on_message(filters.command("promote") & filters.group)
5 | async def promoting(client, message):
6 | global new_admin
7 | if not message.reply_to_message:
8 | return await message.reply("use this command reply")
9 | reply = message.reply_to_message
10 | chat_id = message.chat.id
11 | new_admin = reply.from_user
12 | admin = message.from_user
13 | user_stats = await client.get_chat_member(chat_id, admin.id)
14 | bot_stats = await client.get_chat_member(chat_id, "self")
15 | if not bot_stats.privileges:
16 | return await message.reply("hey dude iam not admin")
17 | elif not user_stats.privileges:
18 | return await message.reply("Sorry dude you need admin")
19 | elif not bot_stats.privileges.can_promote_members:
20 | return await message.reply("i dont have admin rights ")
21 | elif not user_stats.privileges.can_promote_members:
22 | return await message.reply("you need admin rights 😒")
23 | elif user_stats.privileges.can_promote_members:
24 | msg = await message.reply_text("Promoting")
25 | await client.promote_chat_member(
26 | message.chat.id,
27 | new_admin.id,
28 | privileges=pyrogram.types.ChatPrivileges(
29 | can_change_info=True,
30 | can_delete_messages=True,
31 | can_pin_messages=True,
32 | can_invite_users=True,
33 | can_manage_video_chats=True,
34 | can_restrict_members=True
35 | ))
36 | await msg.edit(f"Alright!! Successful promoted")
37 |
38 |
39 | @Client.on_message(filters.command("demote") & filters.group)
40 | async def demote(client, message):
41 | global new_admin
42 | if not message.reply_to_message:
43 | return await message.reply("use this command reply")
44 | reply = message.reply_to_message
45 | chat_id = message.chat.id
46 | new_admin = reply.from_user
47 | admin = message.from_user
48 | user_stats = await client.get_chat_member(chat_id, admin.id)
49 | bot_stats = await client.get_chat_member(chat_id, "self")
50 | if not bot_stats.privileges:
51 | return await message.reply("hey dude iam not admin")
52 | elif not user_stats.privileges:
53 | return await message.reply("Sorry dude you need admin")
54 | elif not bot_stats.privileges.can_promote_members:
55 | return await message.reply("i dont have admin rights ")
56 | elif not user_stats.privileges.can_promote_members:
57 | return await message.reply("you need admin rights 😒")
58 | elif user_stats.privileges.can_promote_members:
59 | msg = await message.reply_text("`Proccing...`")
60 | await client.promote_chat_member(
61 | chat_id,
62 | new_admin.id,
63 | privileges=pyrogram.types.ChatPrivileges(
64 | can_change_info=False,
65 | can_invite_users=False,
66 | can_delete_messages=False,
67 | can_restrict_members=False,
68 | can_pin_messages=False,
69 | can_promote_members=False,
70 | can_manage_chat=False,
71 | can_manage_video_chats=False
72 | ))
73 | await msg.edit(f"Hmm!! demoted 🥺 ")
74 |
--------------------------------------------------------------------------------
/plugins/Extra/short.py:
--------------------------------------------------------------------------------
1 | import os
2 | import aiohttp
3 | from pyrogram import Client, filters, enums
4 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, InputTextMessageContent
5 | from pyrogram.handlers import MessageHandler
6 | from pyshorteners import Shortener
7 |
8 | BITLY_API = os.environ.get("BITLY_API", "8df1df8c23f719e5cf97788cc2d40321ea30092b")
9 | CUTTLY_API = os.environ.get("CUTTLY_API", "f64dffbde033b6c307387dd50b7c76e505f1c")
10 | SHORTCM_API = os.environ.get("SHORTCM_API", "pk_...NIZv")
11 | GPLINKS_API = os.environ.get("GPLINKS_API", "008ccaedd6061ad1948838f410947603de9007a7")
12 |
13 |
14 | reply_markup = InlineKeyboardMarkup(
15 | [[
16 | InlineKeyboardButton("𝘊𝘭𝘰𝘴𝘦", callback_data='close_data')
17 | ]]
18 | )
19 |
20 | @Client.on_message(filters.command(["short"]) & filters.regex(r'https?://[^\s]+'))
21 | async def reply_shortens(bot, update):
22 | message = await update.reply_text(
23 | text="`Analysing your link...`",
24 | disable_web_page_preview=True,
25 | quote=True
26 | )
27 | link = update.matches[0].group(0)
28 | shorten_urls = await short(link)
29 | await message.edit_text(
30 | text=shorten_urls,
31 | disable_web_page_preview=True
32 | )
33 |
34 | @Client.on_inline_query(filters.regex(r'https?://[^\s]+'))
35 | async def inline_short(bot, update):
36 | link = update.matches[0].group(0),
37 | shorten_urls = await short(link)
38 | answers = [
39 | InlineQueryResultArticle(
40 | title="Short Links",
41 | description=update.query,
42 | input_message_content=InputTextMessageContent(
43 | message_text=shorten_urls,
44 | disable_web_page_preview=True
45 | ),
46 | reply_to_message_id=message.id
47 | )
48 | ]
49 | await bot.answer_inline_query(
50 | inline_query_id=update.id,
51 | results=answers
52 | )
53 |
54 | async def short(link):
55 | shorten_urls = "**--Shorted URLs--**\n"
56 |
57 | # Bit.ly shorten
58 | if BITLY_API:
59 | try:
60 | s = Shortener(api_key=BITLY_API)
61 | url = s.bitly.short(link)
62 | shorten_urls += f"\n**Bit.ly :-** {url}"
63 | except Exception as error:
64 | print(f"Bit.ly error :- {error}")
65 |
66 | # TinyURL.com shorten
67 | try:
68 | s = Shortener()
69 | url = s.tinyurl.short(link)
70 | shorten_urls += f"\n**TinyURL.com :-** {url}"
71 | except Exception as error:
72 | print(f"TinyURL.com error :- {error}")
73 |
74 | # GPLinks shorten
75 | try:
76 | api_url = "https://gplinks.in/api"
77 | params = {'api': GPLINKS_API, 'url': link}
78 | async with aiohttp.ClientSession() as session:
79 | async with session.get(api_url, params=params, raise_for_status=True) as response:
80 | data = await response.json()
81 | url = data["shortenedUrl"]
82 | shorten_urls += f"\n**GPLinks.in :-** {url}"
83 | except Exception as error:
84 | print(f"GPLink error :- {error}")
85 |
86 | # Send the text
87 | try:
88 | shorten_urls += ""
89 | return shorten_urls
90 | except Exception as error:
91 | return error
92 |
--------------------------------------------------------------------------------
/database/filters_mdb.py:
--------------------------------------------------------------------------------
1 | import pymongo
2 | from pyrogram import enums
3 | from info import DATABASE_URI, DATABASE_NAME
4 | import logging
5 | logger = logging.getLogger(__name__)
6 | logger.setLevel(logging.ERROR)
7 |
8 | myclient = pymongo.MongoClient(DATABASE_URI)
9 | mydb = myclient[DATABASE_NAME]
10 |
11 |
12 |
13 | async def add_filter(grp_id, text, reply_text, btn, file, alert):
14 | mycol = mydb[str(grp_id)]
15 | # mycol.create_index([('text', 'text')])
16 |
17 | data = {
18 | 'text':str(text),
19 | 'reply':str(reply_text),
20 | 'btn':str(btn),
21 | 'file':str(file),
22 | 'alert':str(alert)
23 | }
24 |
25 | try:
26 | mycol.update_one({'text': str(text)}, {"$set": data}, upsert=True)
27 | except:
28 | logger.exception('Some error occured!', exc_info=True)
29 |
30 |
31 | async def find_filter(group_id, name):
32 | mycol = mydb[str(group_id)]
33 |
34 | query = mycol.find( {"text":name})
35 | # query = mycol.find( { "$text": {"$search": name}})
36 | try:
37 | for file in query:
38 | reply_text = file['reply']
39 | btn = file['btn']
40 | fileid = file['file']
41 | try:
42 | alert = file['alert']
43 | except:
44 | alert = None
45 | return reply_text, btn, alert, fileid
46 | except:
47 | return None, None, None, None
48 |
49 |
50 | async def get_filters(group_id):
51 | mycol = mydb[str(group_id)]
52 |
53 | texts = []
54 | query = mycol.find()
55 | try:
56 | for file in query:
57 | text = file['text']
58 | texts.append(text)
59 | except:
60 | pass
61 | return texts
62 |
63 |
64 | async def delete_filter(message, text, group_id):
65 | mycol = mydb[str(group_id)]
66 |
67 | myquery = {'text':text }
68 | query = mycol.count_documents(myquery)
69 | if query == 1:
70 | mycol.delete_one(myquery)
71 | await message.reply_text(
72 | f"'`{text}`' deleted. I'll not respond to that filter anymore.",
73 | quote=True,
74 | parse_mode=enums.ParseMode.MARKDOWN
75 | )
76 | else:
77 | await message.reply_text("Couldn't find that filter!", quote=True)
78 |
79 |
80 | async def del_all(message, group_id, title):
81 | if str(group_id) not in mydb.list_collection_names():
82 | await message.edit_text(f"Nothing to remove in {title}!")
83 | return
84 |
85 | mycol = mydb[str(group_id)]
86 | try:
87 | mycol.drop()
88 | await message.edit_text(f"All filters from {title} has been removed")
89 | except:
90 | await message.edit_text("Couldn't remove all filters from group!")
91 | return
92 |
93 |
94 | async def count_filters(group_id):
95 | mycol = mydb[str(group_id)]
96 |
97 | count = mycol.count()
98 | return False if count == 0 else count
99 |
100 |
101 | async def filter_stats():
102 | collections = mydb.list_collection_names()
103 |
104 | if "CONNECTION" in collections:
105 | collections.remove("CONNECTION")
106 |
107 | totalcount = 0
108 | for collection in collections:
109 | mycol = mydb[collection]
110 | count = mycol.count()
111 | totalcount += count
112 |
113 | totalcollections = len(collections)
114 |
115 | return totalcollections, totalcount
116 |
--------------------------------------------------------------------------------
/database/connections_mdb.py:
--------------------------------------------------------------------------------
1 | import pymongo
2 |
3 | from info import DATABASE_URI, DATABASE_NAME
4 |
5 | import logging
6 | logger = logging.getLogger(__name__)
7 | logger.setLevel(logging.ERROR)
8 |
9 | myclient = pymongo.MongoClient(DATABASE_URI)
10 | mydb = myclient[DATABASE_NAME]
11 | mycol = mydb['CONNECTION']
12 |
13 |
14 | async def add_connection(group_id, user_id):
15 | query = mycol.find_one(
16 | { "_id": user_id },
17 | { "_id": 0, "active_group": 0 }
18 | )
19 | if query is not None:
20 | group_ids = [x["group_id"] for x in query["group_details"]]
21 | if group_id in group_ids:
22 | return False
23 |
24 | group_details = {
25 | "group_id" : group_id
26 | }
27 |
28 | data = {
29 | '_id': user_id,
30 | 'group_details' : [group_details],
31 | 'active_group' : group_id,
32 | }
33 |
34 | if mycol.count_documents( {"_id": user_id} ) == 0:
35 | try:
36 | mycol.insert_one(data)
37 | return True
38 | except:
39 | logger.exception('Some error occurred!', exc_info=True)
40 |
41 | else:
42 | try:
43 | mycol.update_one(
44 | {'_id': user_id},
45 | {
46 | "$push": {"group_details": group_details},
47 | "$set": {"active_group" : group_id}
48 | }
49 | )
50 | return True
51 | except:
52 | logger.exception('Some error occurred!', exc_info=True)
53 |
54 |
55 | async def active_connection(user_id):
56 |
57 | query = mycol.find_one(
58 | { "_id": user_id },
59 | { "_id": 0, "group_details": 0 }
60 | )
61 | if not query:
62 | return None
63 |
64 | group_id = query['active_group']
65 | return int(group_id) if group_id != None else None
66 |
67 |
68 | async def all_connections(user_id):
69 | query = mycol.find_one(
70 | { "_id": user_id },
71 | { "_id": 0, "active_group": 0 }
72 | )
73 | if query is not None:
74 | return [x["group_id"] for x in query["group_details"]]
75 | else:
76 | return None
77 |
78 |
79 | async def if_active(user_id, group_id):
80 | query = mycol.find_one(
81 | { "_id": user_id },
82 | { "_id": 0, "group_details": 0 }
83 | )
84 | return query is not None and query['active_group'] == group_id
85 |
86 |
87 | async def make_active(user_id, group_id):
88 | update = mycol.update_one(
89 | {'_id': user_id},
90 | {"$set": {"active_group" : group_id}}
91 | )
92 | return update.modified_count != 0
93 |
94 |
95 | async def make_inactive(user_id):
96 | update = mycol.update_one(
97 | {'_id': user_id},
98 | {"$set": {"active_group" : None}}
99 | )
100 | return update.modified_count != 0
101 |
102 |
103 | async def delete_connection(user_id, group_id):
104 |
105 | try:
106 | update = mycol.update_one(
107 | {"_id": user_id},
108 | {"$pull" : { "group_details" : {"group_id":group_id} } }
109 | )
110 | if update.modified_count == 0:
111 | return False
112 | query = mycol.find_one(
113 | { "_id": user_id },
114 | { "_id": 0 }
115 | )
116 | if len(query["group_details"]) >= 1:
117 | if query['active_group'] == group_id:
118 | prvs_group_id = query["group_details"][len(query["group_details"]) - 1]["group_id"]
119 |
120 | mycol.update_one(
121 | {'_id': user_id},
122 | {"$set": {"active_group" : prvs_group_id}}
123 | )
124 | else:
125 | mycol.update_one(
126 | {'_id': user_id},
127 | {"$set": {"active_group" : None}}
128 | )
129 | return True
130 | except Exception as e:
131 | logger.exception(f'Some error occurred! {e}', exc_info=True)
132 | return False
133 |
134 |
--------------------------------------------------------------------------------
/bot.py:
--------------------------------------------------------------------------------
1 | # @MrMNTG @MusammilN
2 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
3 | import logging
4 | import logging.config
5 | import os
6 | import sys
7 | import asyncio
8 | from datetime import date, datetime
9 | import pytz
10 | import aiohttp
11 |
12 | # Get logging configurations
13 | logging.config.fileConfig('logging.conf')
14 | logging.getLogger().setLevel(logging.INFO)
15 | logging.getLogger("pyrogram").setLevel(logging.ERROR)
16 | logging.getLogger("imdbpy").setLevel(logging.ERROR)
17 | logging.getLogger("asyncio").setLevel(logging.CRITICAL - 1)
18 |
19 | import tgcrypto
20 | from pyrogram import Client, __version__
21 | from pyrogram.raw.all import layer
22 | from database.ia_filterdb import Media
23 | from database.users_chats_db import db
24 | from info import SESSION, API_ID, API_HASH, BOT_TOKEN, LOG_STR, LOG_CHANNEL, KEEP_ALIVE_URL, DEFAULT_AUTH_CHANNELS
25 | from utils import temp
26 | from typing import Union, Optional, AsyncGenerator
27 | from pyrogram import types
28 | from Script import script
29 | from os import environ
30 | from aiohttp import web as webserver
31 |
32 | # Peer ID invalid fix
33 | from pyrogram import utils as pyroutils
34 | pyroutils.MIN_CHAT_ID = -999999999999
35 | pyroutils.MIN_CHANNEL_ID = -100999999999999
36 |
37 | from plugins.webcode import bot_run
38 |
39 | PORT_CODE = environ.get("PORT", "8080")
40 |
41 |
42 | # ✅ Add this block
43 | async def preload_auth_channels():
44 | if not await db.get_auth_channels():
45 | await db.set_auth_channels(DEFAULT_AUTH_CHANNELS)
46 | logging.info("Set default AUTH_CHANNELs in DB.")
47 |
48 | async def keep_alive():
49 | """Send a request every 111 seconds to keep the bot alive (if required)."""
50 | async with aiohttp.ClientSession() as session:
51 | while True:
52 | try:
53 | await session.get(KEEP_ALIVE_URL)
54 | logging.info("Sent keep-alive request.")
55 | except Exception as e:
56 | logging.error(f"Keep-alive request failed: {e}")
57 | await asyncio.sleep(111)
58 |
59 |
60 | class Bot(Client):
61 |
62 | def __init__(self):
63 | super().__init__(
64 | name=SESSION,
65 | api_id=API_ID,
66 | api_hash=API_HASH,
67 | bot_token=BOT_TOKEN,
68 | workers=50,
69 | plugins={"root": "plugins"},
70 | sleep_threshold=5,
71 | )
72 |
73 | async def kulasthree(self):
74 | while True:
75 | await asyncio.sleep(24 * 60 * 60)
76 | logging.info("🔄 Bot is restarting")
77 | await self.send_message(chat_id=LOG_CHANNEL, text="🔄 Bot is restarting ...")
78 | os.execl(sys.executable, sys.executable, *sys.argv)
79 |
80 | async def start(self, **kwargs):
81 | b_users, b_chats = await db.get_banned()
82 | temp.BANNED_USERS = b_users
83 | temp.BANNED_CHATS = b_chats
84 | await super().start()
85 | await Media.ensure_indexes()
86 | me = await self.get_me()
87 | temp.ME = me.id
88 | temp.U_NAME = me.username
89 | temp.B_NAME = me.first_name
90 | self.username = '@' + me.username
91 |
92 | # ✅ preload auth channels from info.py if DB is empty
93 | await preload_auth_channels()
94 |
95 | logging.info(f"{me.first_name} running on Pyrogram v{__version__} (Layer {layer}) started on {me.username}.")
96 | logging.info(LOG_STR)
97 | await self.send_message(chat_id=LOG_CHANNEL, text=script.RESTART_TXT)
98 |
99 | print("mntg4u>")
100 |
101 | tz = pytz.timezone('Asia/Kolkata')
102 | today = date.today()
103 | now = datetime.now(tz)
104 | time = now.strftime("%H:%M:%S %p")
105 | await self.send_message(chat_id=LOG_CHANNEL, text=script.RESTART_GC_TXT.format(today, time))
106 |
107 | asyncio.create_task(self.kulasthree())
108 | asyncio.create_task(keep_alive())
109 |
110 | client = webserver.AppRunner(await bot_run())
111 | await client.setup()
112 | bind_address = "0.0.0.0"
113 | await webserver.TCPSite(client, bind_address, PORT_CODE).start()
114 |
115 | async def stop(self, *args):
116 | await super().stop()
117 | logging.info("Bot stopped. Bye.")
118 |
119 | async def iter_messages(
120 | self,
121 | chat_id: Union[int, str],
122 | limit: int,
123 | offset: int = 0,
124 | ) -> Optional[AsyncGenerator["types.Message", None]]:
125 | current = offset
126 | while True:
127 | new_diff = min(200, limit - current)
128 | if new_diff <= 0:
129 | return
130 | messages = await self.get_messages(chat_id, list(range(current, current + new_diff + 1)))
131 | for message in messages:
132 | yield message
133 | current += 1
134 |
135 |
136 | app = Bot()
137 | app.run()
138 |
--------------------------------------------------------------------------------
/plugins/inline.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from pyrogram import Client, emoji, filters
3 | from pyrogram.errors.exceptions.bad_request_400 import QueryIdInvalid
4 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultCachedDocument, InlineQuery
5 | from database.ia_filterdb import get_search_results
6 | from utils import is_subscribed, get_size, temp
7 | from info import CACHE_TIME, AUTH_USERS, CUSTOM_FILE_CAPTION
8 | from utils import create_invite_links
9 |
10 | logger = logging.getLogger(__name__)
11 | cache_time = 0 if AUTH_USERS else CACHE_TIME
12 |
13 | async def inline_users(query: InlineQuery):
14 | if AUTH_USERS:
15 | if query.from_user and query.from_user.id in AUTH_USERS:
16 | return True
17 | else:
18 | return False
19 | if query.from_user and query.from_user.id not in temp.BANNED_USERS:
20 | return True
21 | return False
22 |
23 | @Client.on_inline_query()
24 | async def answer(bot, query):
25 | """Show search results for given inline query"""
26 |
27 | if not await inline_users(query):
28 | await query.answer(results=[],
29 | cache_time=0,
30 | switch_pm_text='okDa',
31 | switch_pm_parameter="hehe")
32 | return
33 |
34 | if not await is_subscribed(query.from_user.id, bot):
35 | invite_links = await create_invite_links(bot)
36 | first_link = next(iter(invite_links.values()), None)
37 |
38 | if first_link:
39 | await query.answer(
40 | results=[],
41 | cache_time=0,
42 | switch_pm_text="📢 Please join the updates channel to use this bot",
43 | switch_pm_parameter="subscribe"
44 | )
45 | else:
46 | await query.answer(
47 | results=[],
48 | cache_time=0,
49 | switch_pm_text="⚠️ Bot owner hasn't set up the required channels properly.",
50 | switch_pm_parameter="error"
51 | )
52 | return
53 |
54 | results = []
55 | if '|' in query.query:
56 | string, file_type = query.query.split('|', maxsplit=1)
57 | string = string.strip()
58 | file_type = file_type.strip().lower()
59 | else:
60 | string = query.query.strip()
61 | file_type = None
62 |
63 | offset = int(query.offset or 0)
64 | reply_markup = get_reply_markup(query=string)
65 | files, next_offset, total = await get_search_results(string,
66 | file_type=file_type,
67 | max_results=10,
68 | offset=offset)
69 |
70 | for file in files:
71 | title=file.file_name
72 | size=get_size(file.file_size)
73 | f_caption=file.caption
74 | if CUSTOM_FILE_CAPTION:
75 | try:
76 | f_caption=CUSTOM_FILE_CAPTION.format(file_name= '' if title is None else title, file_size='' if size is None else size, file_caption='' if f_caption is None else f_caption)
77 | except Exception as e:
78 | logger.exception(e)
79 | f_caption=f_caption
80 | if f_caption is None:
81 | f_caption = f"{file.file_name}"
82 | results.append(
83 | InlineQueryResultCachedDocument(
84 | title=file.file_name,
85 | document_file_id=file.file_id,
86 | caption=f_caption,
87 | description=f'Size: {get_size(file.file_size)}\nType: {file.file_type}',
88 | reply_markup=reply_markup))
89 |
90 | if results:
91 | switch_pm_text = f"{emoji.FILE_FOLDER} Results "
92 | if string:
93 | switch_pm_text += f" for {string}"
94 | try:
95 | await query.answer(results=results,
96 | is_personal = True,
97 | cache_time=cache_time,
98 | switch_pm_text=switch_pm_text,
99 | switch_pm_parameter="start",
100 | next_offset=str(next_offset))
101 | except QueryIdInvalid:
102 | pass
103 | except Exception as e:
104 | logging.exception(str(e))
105 | else:
106 | switch_pm_text = f'{emoji.CROSS_MARK} No results'
107 | if string:
108 | switch_pm_text += f' for "{string}"'
109 |
110 | await query.answer(results=[],
111 | is_personal = True,
112 | cache_time=cache_time,
113 | switch_pm_text=switch_pm_text,
114 | switch_pm_parameter="okay")
115 |
116 |
117 | def get_reply_markup(query):
118 | buttons = [
119 | [
120 | InlineKeyboardButton('Search again', switch_inline_query_current_chat=query)
121 | ]
122 | ]
123 | return InlineKeyboardMarkup(buttons)
124 |
125 |
126 |
--------------------------------------------------------------------------------
/info.py:
--------------------------------------------------------------------------------
1 | import re
2 | from os import environ
3 | from Script import script
4 | from time import time
5 |
6 | id_pattern = re.compile(r'^.\d+$')
7 |
8 | def is_enabled(value, default):
9 | if value.lower() in ["true", "yes", "1", "enable", "y"]:
10 | return True
11 | elif value.lower() in ["false", "no", "0", "disable", "n"]:
12 | return False
13 | else:
14 | return default
15 |
16 | #Bot information
17 | SESSION = environ.get('SESSION', 'Media_search')
18 | API_ID = int(environ.get('API_ID', ''))
19 | API_HASH = environ.get('API_HASH', '')
20 | BOT_TOKEN = environ.get('BOT_TOKEN', '')
21 |
22 | # Keep-Alive URL
23 | KEEP_ALIVE_URL = environ.get("KEEP_ALIVE_URL", "https://burning-brittney-leech2-3bc21fb5.koyeb.app/") # <-- Add this line
24 | #hyper link
25 | HYPER_MODE = bool(environ.get('HYPER_MODE', False))
26 | #request fsub
27 | REQUEST_FSUB_MODE = bool(environ.get('REQUEST_FSUB_MODE', True))
28 | # Bot settings
29 | BOT_START_TIME = time()
30 | CACHE_TIME = int(environ.get('CACHE_TIME', 300))
31 | USE_CAPTION_FILTER = bool(environ.get('USE_CAPTION_FILTER', False))
32 | PICS = (environ.get('PICS', 'https://graph.org/file/2ed90a79eb533d86f8a0f.jpg https://graph.org/file/a0da24dacf4b7bec376a3.jpg https://graph.org/file/457aa9d0e485925088be6.jpg https://graph.org/file/041f7b57c6950070ba16e.jpg https://graph.org/file/f36511f6042d74d95b5df.jpg https://graph.org/file/a30d30b3bc49bd8745533.jpg https://graph.org/file/ce71502cf614059ce1de5.jpg')).split()
33 |
34 | # Admins, Channels & Users
35 | ADMINS = [int(admin) if id_pattern.search(admin) else admin for admin in environ.get('ADMINS', '1892771262').split()]
36 | CHANNELS = [int(ch) if id_pattern.search(ch) else ch for ch in environ.get('CHANNELS', '-1002490892111 -1002097504396').split()]
37 | auth_users = [int(user) if id_pattern.search(user) else user for user in environ.get('AUTH_USERS', '').split()]
38 | AUTH_USERS = (auth_users + ADMINS) if auth_users else []
39 | auth_grp = environ.get('AUTH_GROUP')
40 | DEFAULT_AUTH_CHANNELS = [int(x) for x in environ.get("AUTH_CHANNEL", "").split() if x.lstrip('-').isdigit()]
41 | AUTH_GROUPS = [int(ch) for ch in auth_grp.split()] if auth_grp else None
42 |
43 | # MongoDB information
44 | DATABASE_URI = environ.get('DATABASE_URI', "")
45 | DATABASE_NAME = environ.get('DATABASE_NAME', "Cluster0")
46 | COLLECTION_NAME = environ.get('COLLECTION_NAME', 'mn_files')
47 |
48 | # File Channel Settings
49 | FILE_CHANNELS = [int(ch) for ch in environ.get('FILE_CHANNELS', '-1002831639976 -1002607076908 -1002869981026').split()]
50 | FILE_CHANNEL_SENDING_MODE = is_enabled(environ.get('FILE_CHANNEL_SENDING_MODE', 'False'), False)
51 | FILE_AUTO_DELETE_SECONDS = int(environ.get('FILE_AUTO_DELETE_SECONDS', 3600)) # Default: 1 hour
52 |
53 | # Others
54 | LOG_CHANNEL = int(environ.get('LOG_CHANNEL', '-1002704640995'))
55 | SUPPORT_CHAT = environ.get('SUPPORT_CHAT', 'mnbots_support')
56 | P_TTI_SHOW_OFF = is_enabled((environ.get('P_TTI_SHOW_OFF', 'False')), False)
57 | IMDB = is_enabled((environ.get('IMDB', 'False')), False)
58 | SINGLE_BUTTON = is_enabled((environ.get('SINGLE_BUTTON', 'True')), True)
59 | CUSTOM_FILE_CAPTION = environ.get("CUSTOM_FILE_CAPTION", f"{script.CUSTOM_FILE_CAPTION}")
60 | BATCH_FILE_CAPTION = environ.get("BATCH_FILE_CAPTION", "📂 File Name: {file_name}\n\n ♻ File Size:{file_size} \n\n Latest Movies - ")
61 | IMDB_TEMPLATE = environ.get("IMDB_TEMPLATE", "🏷 𝖳𝗂𝗍𝗅𝖾: {title} \n🔮 𝖸𝖾𝖺𝗋: {year} \n⭐️ 𝖱𝖺𝗍𝗂𝗇𝗀𝗌: {rating}/ 10 \n🎭 𝖦𝖾𝗇𝖾𝗋𝗌: {genres}")
62 | LONG_IMDB_DESCRIPTION = is_enabled(environ.get("LONG_IMDB_DESCRIPTION", "False"), False)
63 | SPELL_CHECK_REPLY = is_enabled(environ.get("SPELL_CHECK_REPLY", "True"), True)
64 | MAX_LIST_ELM = environ.get("MAX_LIST_ELM", None)
65 | INDEX_REQ_CHANNEL = int(environ.get('INDEX_REQ_CHANNEL', LOG_CHANNEL))
66 | FILE_STORE_CHANNEL = [int(ch) for ch in (environ.get('FILE_STORE_CHANNEL', '')).split()]
67 | MELCOW_NEW_USERS = is_enabled((environ.get('MELCOW_NEW_USERS', "True")), True)
68 | PROTECT_CONTENT = is_enabled((environ.get('PROTECT_CONTENT', "False")), False)
69 | PUBLIC_FILE_STORE = is_enabled((environ.get('PUBLIC_FILE_STORE', "False")), True)
70 |
71 | LOG_STR = "Current Cusomized Configurations are:-\n"
72 | LOG_STR += ("IMDB Results are enabled, Bot will be showing imdb details for you queries.\n" if IMDB else "IMBD Results are disabled.\n")
73 | LOG_STR += ("P_TTI_SHOW_OFF found , Users will be redirected to send /start to Bot PM instead of sending file file directly\n" if P_TTI_SHOW_OFF else "P_TTI_SHOW_OFF is disabled files will be send in PM, instead of sending start.\n")
74 | LOG_STR += ("SINGLE_BUTTON is Found, filename and files size will be shown in a single button instead of two separate buttons\n" if SINGLE_BUTTON else "SINGLE_BUTTON is disabled , filename and file_sixe will be shown as different buttons\n")
75 | LOG_STR += (f"CUSTOM_FILE_CAPTION enabled with value {CUSTOM_FILE_CAPTION}, your files will be send along with this customized caption.\n" if CUSTOM_FILE_CAPTION else "No CUSTOM_FILE_CAPTION Found, Default captions of file will be used.\n")
76 | LOG_STR += ("Long IMDB storyline enabled." if LONG_IMDB_DESCRIPTION else "LONG_IMDB_DESCRIPTION is disabled , Plot will be shorter.\n")
77 | LOG_STR += ("Spell Check Mode Is Enabled, bot will be suggesting related movies if movie not found\n" if SPELL_CHECK_REPLY else "SPELL_CHECK_REPLY Mode disabled\n")
78 | LOG_STR += (f"MAX_LIST_ELM Found, long list will be shortened to first {MAX_LIST_ELM} elements\n" if MAX_LIST_ELM else "Full List of casts and crew will be shown in imdb template, restrict them by adding a value to MAX_LIST_ELM\n")
79 | LOG_STR += f"Your current IMDB template is {IMDB_TEMPLATE}"
80 |
--------------------------------------------------------------------------------
/database/users_chats_db.py:
--------------------------------------------------------------------------------
1 | # https://github.com/odysseusmax/animated-lamp/blob/master/bot/database/database.py
2 | # @MrMNTG @MusammilN
3 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
4 | import motor.motor_asyncio
5 | from info import DATABASE_NAME, DATABASE_URI, IMDB, IMDB_TEMPLATE, MELCOW_NEW_USERS, P_TTI_SHOW_OFF, SINGLE_BUTTON, SPELL_CHECK_REPLY, PROTECT_CONTENT
6 |
7 | # @MrMNTG @MusammilN
8 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
9 |
10 | class Database:
11 |
12 | def __init__(self, uri, database_name):
13 | self._client = motor.motor_asyncio.AsyncIOMotorClient(uri)
14 | self.db = self._client[database_name]
15 | self.col = self.db.users
16 | self.grp = self.db.groups
17 | self.config = self.db.config
18 |
19 |
20 | def new_user(self, id, name):
21 | return dict(
22 | id = id,
23 | name = name,
24 | ban_status=dict(
25 | is_banned=False,
26 | ban_reason="",
27 | ),
28 | )
29 |
30 |
31 | def new_group(self, id, title):
32 | return dict(
33 | id = id,
34 | title = title,
35 | chat_status=dict(
36 | is_disabled=False,
37 | reason="",
38 | ),
39 | )
40 |
41 | async def add_user(self, id, name):
42 | user = self.new_user(id, name)
43 | await self.col.insert_one(user)
44 |
45 | async def is_user_exist(self, id):
46 | user = await self.col.find_one({'id':int(id)})
47 | return bool(user)
48 |
49 | async def total_users_count(self):
50 | count = await self.col.count_documents({})
51 | return count
52 |
53 | async def remove_ban(self, id):
54 | ban_status = dict(
55 | is_banned=False,
56 | ban_reason=''
57 | )
58 | await self.col.update_one({'id': id}, {'$set': {'ban_status': ban_status}})
59 |
60 | async def ban_user(self, user_id, ban_reason="No Reason"):
61 | ban_status = dict(
62 | is_banned=True,
63 | ban_reason=ban_reason
64 | )
65 | await self.col.update_one({'id': user_id}, {'$set': {'ban_status': ban_status}})
66 |
67 | async def get_ban_status(self, id):
68 | default = dict(
69 | is_banned=False,
70 | ban_reason=''
71 | )
72 | user = await self.col.find_one({'id':int(id)})
73 | if not user:
74 | return default
75 | return user.get('ban_status', default)
76 |
77 | async def get_all_users(self):
78 | return self.col.find({})
79 |
80 |
81 | async def delete_user(self, user_id):
82 | await self.col.delete_many({'id': int(user_id)})
83 |
84 |
85 | async def get_banned(self):
86 | users = self.col.find({'ban_status.is_banned': True})
87 | chats = self.grp.find({'chat_status.is_disabled': True})
88 | b_chats = [chat['id'] async for chat in chats]
89 | b_users = [user['id'] async for user in users]
90 | return b_users, b_chats
91 |
92 |
93 |
94 | async def add_chat(self, chat, title):
95 | chat = self.new_group(chat, title)
96 | await self.grp.insert_one(chat)
97 |
98 |
99 | async def get_chat(self, chat):
100 | chat = await self.grp.find_one({'id':int(chat)})
101 | return False if not chat else chat.get('chat_status')
102 |
103 |
104 | async def re_enable_chat(self, id):
105 | chat_status=dict(
106 | is_disabled=False,
107 | reason="",
108 | )
109 | await self.grp.update_one({'id': int(id)}, {'$set': {'chat_status': chat_status}})
110 |
111 | async def update_settings(self, id, settings):
112 | await self.grp.update_one({'id': int(id)}, {'$set': {'settings': settings}})
113 |
114 |
115 | async def get_settings(self, id):
116 | default = {
117 | 'button': SINGLE_BUTTON,
118 | 'botpm': P_TTI_SHOW_OFF,
119 | 'file_secure': PROTECT_CONTENT,
120 | 'imdb': IMDB,
121 | 'spell_check': SPELL_CHECK_REPLY,
122 | 'welcome': MELCOW_NEW_USERS,
123 | 'template': IMDB_TEMPLATE
124 | }
125 | chat = await self.grp.find_one({'id':int(id)})
126 | if chat:
127 | return chat.get('settings', default)
128 | return default
129 |
130 |
131 | async def disable_chat(self, chat, reason="No Reason"):
132 | chat_status=dict(
133 | is_disabled=True,
134 | reason=reason,
135 | )
136 | await self.grp.update_one({'id': int(chat)}, {'$set': {'chat_status': chat_status}})
137 |
138 |
139 | async def total_chat_count(self):
140 | count = await self.grp.count_documents({})
141 | return count
142 |
143 |
144 | async def get_all_chats(self):
145 | return self.grp.find({})
146 |
147 | async def set_auth_channels(self, channels: list[int]):
148 | # Store the list of auth channel IDs in a singleton document
149 | await self.config.update_one(
150 | {"_id": "auth_channels"},
151 | {"$set": {"channels": channels}},
152 | upsert=True,
153 | )
154 |
155 | async def get_auth_channels(self) -> list[int]:
156 | doc = await self.config.find_one({"_id": "auth_channels"})
157 | if doc and "channels" in doc:
158 | return doc["channels"]
159 | return []
160 |
161 | async def get_db_size(self):
162 | return (await self.db.command("dbstats"))['dataSize']
163 |
164 |
165 | db = Database(DATABASE_URI, DATABASE_NAME)
166 |
167 | # @MrMNTG @MusammilN
168 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
169 |
--------------------------------------------------------------------------------
/plugins/connection.py:
--------------------------------------------------------------------------------
1 | from pyrogram import filters, Client, enums
2 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
3 | from database.connections_mdb import add_connection, all_connections, if_active, delete_connection
4 | from info import ADMINS
5 | import logging
6 |
7 | logger = logging.getLogger(__name__)
8 | logger.setLevel(logging.ERROR)
9 |
10 |
11 | @Client.on_message((filters.private | filters.group) & filters.command('connect'))
12 | async def addconnection(client, message):
13 | userid = message.from_user.id if message.from_user else None
14 | if not userid:
15 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM")
16 | chat_type = message.chat.type
17 |
18 | if chat_type == enums.ChatType.PRIVATE:
19 | try:
20 | cmd, group_id = message.text.split(" ", 1)
21 | except:
22 | await message.reply_text(
23 | "Enter in correct format!\n\n"
24 | "/connect groupid\n\n"
25 | "Get your Group id by adding this bot to your group and use /id",
26 | quote=True
27 | )
28 | return
29 |
30 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
31 | group_id = message.chat.id
32 |
33 | try:
34 | st = await client.get_chat_member(group_id, userid)
35 | if (
36 | st.status != enums.ChatMemberStatus.ADMINISTRATOR
37 | and st.status != enums.ChatMemberStatus.OWNER
38 | and userid not in ADMINS
39 | ):
40 | await message.reply_text("You should be an admin in Given group!", quote=True)
41 | return
42 | except Exception as e:
43 | logger.exception(e)
44 | await message.reply_text(
45 | "Invalid Group ID!\n\nIf correct, Make sure I'm present in your group!!",
46 | quote=True,
47 | )
48 |
49 | return
50 | try:
51 | st = await client.get_chat_member(group_id, "me")
52 | if st.status == enums.ChatMemberStatus.ADMINISTRATOR:
53 | ttl = await client.get_chat(group_id)
54 | title = ttl.title
55 |
56 | addcon = await add_connection(str(group_id), str(userid))
57 | if addcon:
58 | await message.reply_text(
59 | f"Successfully connected to **{title}**\nNow manage your group from my pm !",
60 | quote=True,
61 | parse_mode=enums.ParseMode.MARKDOWN
62 | )
63 | if chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
64 | await client.send_message(
65 | userid,
66 | f"Connected to **{title}** !",
67 | parse_mode=enums.ParseMode.MARKDOWN
68 | )
69 | else:
70 | await message.reply_text(
71 | "You're already connected to this chat!",
72 | quote=True
73 | )
74 | else:
75 | await message.reply_text("Add me as an admin in group", quote=True)
76 | except Exception as e:
77 | logger.exception(e)
78 | await message.reply_text('Some error occurred! Try again later.', quote=True)
79 | return
80 |
81 |
82 | @Client.on_message((filters.private | filters.group) & filters.command('disconnect'))
83 | async def deleteconnection(client, message):
84 | userid = message.from_user.id if message.from_user else None
85 | if not userid:
86 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM")
87 | chat_type = message.chat.type
88 |
89 | if chat_type == enums.ChatType.PRIVATE:
90 | await message.reply_text("Run /connections to view or disconnect from groups!", quote=True)
91 |
92 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
93 | group_id = message.chat.id
94 |
95 | st = await client.get_chat_member(group_id, userid)
96 | if (
97 | st.status != enums.ChatMemberStatus.ADMINISTRATOR
98 | and st.status != enums.ChatMemberStatus.OWNER
99 | and str(userid) not in ADMINS
100 | ):
101 | return
102 |
103 | delcon = await delete_connection(str(userid), str(group_id))
104 | if delcon:
105 | await message.reply_text("Successfully disconnected from this chat", quote=True)
106 | else:
107 | await message.reply_text("This chat isn't connected to me!\nDo /connect to connect.", quote=True)
108 |
109 |
110 | @Client.on_message(filters.private & filters.command(["connections"]))
111 | async def connections(client, message):
112 | userid = message.from_user.id
113 |
114 | groupids = await all_connections(str(userid))
115 | if groupids is None:
116 | await message.reply_text(
117 | "There are no active connections!! Connect to some groups first.",
118 | quote=True
119 | )
120 | return
121 | buttons = []
122 | for groupid in groupids:
123 | try:
124 | ttl = await client.get_chat(int(groupid))
125 | title = ttl.title
126 | active = await if_active(str(userid), str(groupid))
127 | act = " - ACTIVE" if active else ""
128 | buttons.append(
129 | [
130 | InlineKeyboardButton(
131 | text=f"{title}{act}", callback_data=f"groupcb:{groupid}:{act}"
132 | )
133 | ]
134 | )
135 | except:
136 | pass
137 | if buttons:
138 | await message.reply_text(
139 | "Your connected group details ;\n\n",
140 | reply_markup=InlineKeyboardMarkup(buttons),
141 | quote=True
142 | )
143 | else:
144 | await message.reply_text(
145 | "There are no active connections!! Connect to some groups first.",
146 | quote=True
147 | )
148 |
--------------------------------------------------------------------------------
/plugins/mn_deletefiles.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import asyncio
3 | import re
4 | from pyrogram import Client, filters, enums
5 | from pyrogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery
6 |
7 | # Ensure this import path is correct for your bot's setup
8 | from database.ia_filterdb import Media
9 |
10 | # You MUST define ADMINS in your bot's config (e.g., info.py)
11 | from info import ADMINS
12 |
13 | logger = logging.getLogger(__name__)
14 |
15 | # Constants for Batch Deletion
16 | BATCH_SIZE = 20 # Number of files to delete in each batch
17 | SLEEP_TIME = 2 # Seconds to wait between batches to avoid overloading the DB/bot
18 |
19 |
20 | @Client.on_message(filters.command("deletefiles") & filters.user(ADMINS))
21 | async def deletemultiplefiles(bot: Client, message: Message):
22 | """
23 | Handles the /deletefiles command to prompt for confirmation before deleting
24 | files from the database based on a keyword in their filenames.
25 | This command is restricted to private chat with the bot for safety.
26 | """
27 | if message.chat.type != enums.ChatType.PRIVATE:
28 | return await message.reply_text(
29 | f"Hey {message.from_user.mention}, this command won't work in groups. It only works in my PM!",
30 | parse_mode=enums.ParseMode.HTML
31 | )
32 |
33 | try:
34 | # Extract the keyword from the command (e.g., '/deletefiles keyword')
35 | keyword = message.text.split(" ", 1)[1].strip()
36 | if not keyword:
37 | return await message.reply_text(
38 | f"Hey {message.from_user.mention}, give me a keyword along with the command to delete files.\n"
39 | "Usage: `/deletefiles
3 | Shobana Filter Bot
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Click to Expand
90 |
93 |
94 |
Click to Expand
100 | bash
103 | git clone https://github.com/mn-bots/ShobanaFilterBot
104 | # Install dependencies
105 | pip3 install -U -r requirements.txt
106 | # Configure variables in info.py and start the bot
107 | python3 bot.py
108 |
This project is licensed under the GNU AGPL 3.0. Selling this code for monetary gain is strictly prohibited.
add a filter in chat
29 | • /filters - list all the filters of a chat
30 | • /del - delete a specific filter in chat
31 | • /delall - delete the whole filters in a chat (chat owner only)"""
32 | BUTTON_TXT = """Help: Buttons
33 |
34 | - This Bot Supports both url and alert inline buttons.
35 |
36 | NOTE:
37 | 1. Telegram will not allows you to send buttons without any content, so content is mandatory.
38 | 2. This Bot supports buttons with any telegram media type.
39 | 3. Buttons should be properly parsed as markdown format
40 |
41 | URL buttons:
42 | [Button Text](buttonurl:https://github.com/mn-bots/ShobanaFilterBot)
43 |
44 | Alert buttons:
45 | [Button Text](buttonalert:This is an alert message)"""
46 | AUTOFILTER_TXT = """
47 |
48 | ɴᴏᴛᴇ: Fɪʟᴇ Iɴᴅᴇx
49 | 1. ᴍᴀᴋᴇ ᴍᴇ ᴛʜᴇ ᴀᴅᴍɪɴ ᴏꜰ ʏᴏᴜʀ ᴄʜᴀɴɴᴇʟ ɪꜰ ɪᴛ'ꜱ ᴘʀɪᴠᴀᴛᴇ.
50 | 2. ᴍᴀᴋᴇ ꜱᴜʀᴇ ᴛʜᴀᴛ ʏᴏᴜʀ ᴄʜᴀɴɴᴇʟ ᴅᴏᴇꜱ ɴᴏᴛ ᴄᴏɴᴛᴀɪɴꜱ ᴄᴀᴍʀɪᴘꜱ, ᴘᴏʀɴ ᴀɴᴅ ꜰᴀᴋᴇ ꜰɪʟᴇꜱ.
51 | 3. ꜰᴏʀᴡᴀʀᴅ ᴛʜᴇ ʟᴀꜱᴛ ᴍᴇꜱꜱᴀɢᴇ ᴛᴏ ᴍᴇ ᴡɪᴛʜ Qᴜᴏᴛᴇꜱ. ɪ'ʟʟ ᴀᴅᴅ ᴀʟʟ ᴛʜᴇ ꜰɪʟᴇꜱ ɪɴ ᴛʜᴀᴛ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴍʏ ᴅʙ.
52 |
53 | Nᴏᴛᴇ: AᴜᴛᴏFɪʟᴛᴇʀ
54 | 1. Aᴅᴅ ᴛʜᴇ ʙᴏᴛ ᴀs ᴀᴅᴍɪɴ ᴏɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ.
55 | 2. Usᴇ /connect ᴀɴᴅ ᴄᴏɴɴᴇᴄᴛ ʏᴏᴜʀ ɢʀᴏᴜᴘ ᴛᴏ ᴛʜᴇ ʙᴏᴛ.
56 | 3. Usᴇ /settings ᴏɴ ʙᴏᴛ's PM ᴀɴᴅ ᴛᴜʀɴ ᴏɴ AᴜᴛᴏFɪʟᴛᴇʀ ᴏɴ ᴛʜᴇ sᴇᴛᴛɪɴɢs ᴍᴇɴᴜ.."""
57 | CONNECTION_TXT = """Help: Connections
58 |
59 | - Used to connect bot to PM for managing filters
60 | - it helps to avoid spamming in groups.
61 |
62 | NOTE:
63 | 1. Only admins can add a connection.
64 | 2. Send /connect for connecting me to ur PM
65 |
66 | Commands and Usage:
67 | • /connect - connect a particular chat to your PM
68 | • /disconnect - disconnect from a chat
69 | • /connections - list all your connections"""
70 | EXTRAMOD_TXT = """Help: Extra Modules
71 |
72 | NOTE:
73 | these are the extra features of ShobanaFilterBot
74 |
75 | Commands and Usage:
76 | • /id - get id of a specified user.
77 | • /info - get information about a user.
78 | • /imdb - get the film information from IMDb source.
79 | • /search - get the film information from various sources.
80 | • /start - Check I'm Alive.
81 | • /ping - check ping.
82 | • /usage - usage of bot.
83 | • /info - User info .
84 | • /id - User id .
85 | • /broadcast - Broadcast (owner only).
86 | """
87 | ADMIN_TXT = """Help: Admin mods
88 |
89 | NOTE:
90 | This module only works for my admins
91 |
92 | Commands and Usage:
93 | • /logs - to get the rescent errors
94 | • /stats - to get status of files in db.
95 | • /delete - to delete a specific file from db.
96 | • /users - to get list of my users and ids.
97 | • /chats - to get list of the my chats and ids
98 | • /leave - to leave from a chat.
99 | • /disable - do disable a chat.
100 | • /ban - to ban a user.
101 | • /unban - to unban a user.
102 | • /channel - to get list of total connected channels
103 | • /broadcast - to broadcast a message to all users"""
104 | STATUS_TXT = """★ 𝚃𝙾𝚃𝙰𝙻 𝙵𝙸𝙻𝙴𝚂: {}
105 | 𝚃𝙾𝚃𝙰𝙻 𝚄𝚂𝙴𝚁𝚂: {}
106 | 𝚃𝙾𝚃𝙰𝙻 𝙲𝙷𝙰𝚃𝚂: {}
107 | 𝚄𝚂𝙴𝙳 𝚂𝚃𝙾𝚁𝙰𝙶𝙴: {}
108 | 𝙵𝚁𝙴𝙴 𝚂𝚃𝙾𝚁𝙰𝙶𝙴: {} """
109 | LOG_TEXT_G = """#NewGroup
110 | Group = {}({})
111 | Total Members = {}
112 | Added By - {}
113 | """
114 | RESULT_TXT="""Hey,115 |
Jᴜsᴛ Sᴇᴇ Wʜᴀᴛ I Found Fᴏʀ Yᴏᴜʀ Qᴜᴇʀʏ""" 116 | 117 | CUSTOM_FILE_CAPTION = """📂Fɪʟᴇɴᴀᴍᴇ : {file_name} 118 | FɪʟᴇSɪᴢᴇ : {file_size} 119 | 120 | ╔═ ᴊᴏɪɴ ᴡɪᴛʜ ᴜs ═╗ 121 | Jᴏɪɴ :- [MAIN CHANNEL](https://t.me/mn_movies2) 122 | Jᴏɪɴ :- [Movie Group 1](https://t.me/mn_movies3) 123 | Jᴏɪɴ :- [Movie Group 2](https://t.me/malayalam_movies_group2) 124 | Jᴏɪɴ :- [Movie Group 3](https://t.me/Netflix_Group3) 125 | Jᴏɪɴ :- [Movie Group 4](https://t.me/cinima_theerthadana_kendram) 126 | Jᴏɪɴ :- [Movie Group 5](https://t.me/malayalam_movies_nbot) 127 | Jᴏɪɴ :- [Movie Group 6](https://t.me/seriesgroups) 128 | Jᴏɪɴ :- [Movie Group 7](https://t.me/New_indian_cinemas) 129 | ╚═ ᴊᴏɪɴ ᴡɪᴛʜ ᴜs ═╝ 130 | 131 | ⚠️ This file will be deleted from here within 1 minute as it has copyright ... !!! 132 | 133 | കോപ്പിറൈറ്റ് ഉള്ളതുകൊണ്ട് ഫയൽ 1 മിനിറ്റിനുള്ളിൽ ഇവിടെനിന്നും ഡിലീറ്റ് ആകുന്നതാണ് അതുകൊണ്ട് ഇവിടെ നിന്നും മറ്റെവിടെക്കെങ്കിലും മാറ്റിയതിന് ശേഷം ഡൗൺലോഡ് ചെയ്യുക! 134 | """ 135 | 136 | 137 | RESTART_GC_TXT = """ 138 | 𝖡𝗈𝗍 𝖱𝖾𝗌𝗍𝖺𝗋𝗍𝖾𝖽 ! 139 | 140 | 📅 𝖣𝖺𝗍𝖾 :
{}
141 | ⏰ 𝖳𝗂𝗆𝖾 : {}
142 | 🌐 𝖳𝗂𝗆𝖾𝗓𝗈𝗇𝖾 : Asia/Kolkata
143 | 🛠️ 𝖡𝗎𝗂𝗅𝖽 𝖲𝗍𝖺𝗍𝗎𝗌 : 𝗏1 [ 𝖲𝗍able ]"""
144 |
145 | LOG_TEXT_P = """#NewUser
146 | ID - {}
147 | Name - {}
148 | """
149 | SPOLL_NOT_FND="""
150 | I couldn't find anything related to your request.
151 | Try reading the instruction below
152 | 153 | 1️ Ask in Correct Spelling 154 | 2️ Don't ask Movies which are not Realased on OTT PLATFORMS 155 | 3️ Possible ASK [movie name langauge] like this or [movie year]156 | OR 157 | Tʜɪs Mᴏᴠɪᴇ Is Nᴏᴛ Aᴅᴅᴇᴅ Tᴏ DB 158 |
Report To ADMIN BY USING /bugs command159 | """ 160 | #SPELL CHECK LANGUAGES TO KNOW callback 161 | ENG_SPELL="""Please Note Below📓 162 | 1️⃣ Ask in Correct Spelling 163 | 2️⃣ Don't ask Movies which are not Realased on OTT PLATFORMS 164 | 3️⃣ Possible ASK [movie name langauge] like this or [movie year] 165 | """ 166 | MAL_SPELL="""ദയവായി താഴെ ശ്രദ്ധിക്കുക📓 167 | 1️⃣ ശരിയായ അക്ഷരവിന്യാസത്തിൽ ചോദിക്കുക 168 | 2️⃣ OTT പ്ലാറ്റ്ഫോമുകളിൽ റിലീസ് ചെയ്യാത്ത സിനിമകൾ ചോദിക്കരുത് 169 | 3️⃣ ഇത് പോലെ [സിനിമയുടെ പേര് ഭാഷ] അല്ലെങ്കിൽ [സിനിമ വർഷം] ചോദിക്കാം 170 | """ 171 | HIN_SPELL="""कृपया नीचे ध्यान दें📓 172 | 1️⃣ सही वर्तनी में पूछें 173 | 2️⃣ उन फिल्मों के बारे में न पूछें जो ओटीटी प्लेटफॉर्म पर रिलीज नहीं हुई हैं 174 | 3️⃣ संभव है पूछें [मूवी का नाम भाषा] इस तरह या [मूवी वर्ष] 175 | """ 176 | TAM_SPELL="""கீழே கவனிக்கவும்📓 177 | 1️⃣ சரியான எழுத்துப்பிழையில் கேளுங்கள் 178 | 2️⃣ வெளியாகாத திரைப்படங்களைக் கேட்காதீர்கள் 179 | 3️⃣ இந்த வடிவத்தில் கேளுங்கள் [திரைப்படத்தின் பெயர், ஆண்டு] 180 | """ 181 | 182 | CHK_MOV_ALRT="""♻️ ᴄʜᴇᴄᴋɪɴɢ ꜰɪʟᴇ ᴏɴ ᴍʏ ᴅᴀᴛᴀʙᴀꜱᴇ... ♻️""" 183 | 184 | OLD_MES=""" 𝐘𝐨𝐮 𝐚𝐫𝐞 𝐮𝐬𝐢𝐧𝐠 𝐨𝐧𝐞 𝐨𝐟 𝐦𝐲 𝐨𝐥𝐝 𝐦𝐞𝐬𝐬𝐚𝐠𝐞𝐬🤔, 𝐩𝐥𝐞𝐚𝐬𝐞 𝐬𝐞𝐧𝐝 𝐭𝐡𝐞 𝐫𝐞𝐪𝐮𝐞𝐬𝐭 𝐚𝐠𝐚𝐢𝐧""" 185 | 186 | MOV_NT_FND="""Tʜɪs Mᴏᴠɪᴇ Is Nᴏᴛ Yᴇᴛ Rᴇᴀʟᴇsᴇᴅ Oʀ Aᴅᴅᴇᴅ Tᴏ DB 187 |
Report To ADMIN BY USING /bugs command188 | """ 189 | RESTART_TXT = """ 190 | 𝖡𝗈𝗍 𝖱𝖾𝗌𝗍𝖺𝗋𝗍𝖾𝖽 ✅""" 191 | 192 | -------------------------------------------------------------------------------- /plugins/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 info import IMDB_TEMPLATE 5 | from utils import extract_user, get_file_id, get_poster, last_online 6 | import time 7 | from datetime import datetime 8 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 9 | import logging 10 | logger = logging.getLogger(__name__) 11 | logger.setLevel(logging.ERROR) 12 | 13 | @Client.on_message(filters.command('id')) 14 | async def showid(client, message): 15 | chat_type = message.chat.type 16 | if chat_type == enums.ChatType.PRIVATE: 17 | user_id = message.chat.id 18 | first = message.from_user.first_name 19 | last = message.from_user.last_name or "" 20 | username = message.from_user.username 21 | dc_id = message.from_user.dc_id or "" 22 | await message.reply_text( 23 | f"➲ First Name: {first}\n➲ Last Name: {last}\n➲ Username: {username}\n➲ Telegram ID:
{user_id}\n➲ Data Centre: {dc_id}",
24 | quote=True
25 | )
26 |
27 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
28 | _id = ""
29 | _id += (
30 | "➲ Chat ID: "
31 | f"{message.chat.id}\n"
32 | )
33 | if message.reply_to_message:
34 | _id += (
35 | "➲ User ID: "
36 | f"{message.from_user.id if message.from_user else 'Anonymous'}\n"
37 | "➲ Replied User ID: "
38 | f"{message.reply_to_message.from_user.id if message.reply_to_message.from_user else 'Anonymous'}\n"
39 | )
40 | file_info = get_file_id(message.reply_to_message)
41 | else:
42 | _id += (
43 | "➲ User ID: "
44 | f"{message.from_user.id if message.from_user else 'Anonymous'}\n"
45 | )
46 | file_info = get_file_id(message)
47 | if file_info:
48 | _id += (
49 | f"{file_info.message_type}: "
50 | f"{file_info.file_id}\n"
51 | )
52 | await message.reply_text(
53 | _id,
54 | quote=True
55 | )
56 |
57 | @Client.on_message(filters.command(["info"]))
58 | async def who_is(client, message):
59 | # https://github.com/SpEcHiDe/PyroGramBot/blob/master/pyrobot/plugins/admemes/whois.py#L19
60 | status_message = await message.reply_text(
61 | "`Fetching user info...`"
62 | )
63 | await status_message.edit(
64 | "`Processing user info...`"
65 | )
66 | from_user = None
67 | from_user_id, _ = extract_user(message)
68 | try:
69 | from_user = await client.get_users(from_user_id)
70 | except Exception as error:
71 | await status_message.edit(str(error))
72 | return
73 | if from_user is None:
74 | return await status_message.edit("no valid user_id / message specified")
75 | message_out_str = ""
76 | message_out_str += f"➲First Name: {from_user.first_name}\n"
77 | last_name = from_user.last_name or "None"
78 | message_out_str += f"➲Last Name: {last_name}\n"
79 | message_out_str += f"➲Telegram ID: {from_user.id}\n"
80 | username = from_user.username or "None"
81 | dc_id = from_user.dc_id or "[User Doesn't Have A Valid DP]"
82 | message_out_str += f"➲Data Centre: {dc_id}\n"
83 | message_out_str += f"➲User Name: @{username}\n"
84 | message_out_str += f"➲User 𝖫𝗂𝗇𝗄: Click Here\n"
85 | if message.chat.type in ((enums.ChatType.SUPERGROUP, enums.ChatType.CHANNEL)):
86 | try:
87 | chat_member_p = await message.chat.get_member(from_user.id)
88 | joined_date = (
89 | chat_member_p.joined_date or datetime.now()
90 | ).strftime("%Y.%m.%d %H:%M:%S")
91 | message_out_str += (
92 | "➲Joined this Chat on: "
93 | f"{joined_date}"
94 | "\n"
95 | )
96 | except UserNotParticipant:
97 | pass
98 | chat_photo = from_user.photo
99 | if chat_photo:
100 | local_user_photo = await client.download_media(
101 | message=chat_photo.big_file_id
102 | )
103 | buttons = [[
104 | InlineKeyboardButton('🔐 Close', callback_data='close_data')
105 | ]]
106 | reply_markup = InlineKeyboardMarkup(buttons)
107 | await message.reply_photo(
108 | photo=local_user_photo,
109 | quote=True,
110 | reply_markup=reply_markup,
111 | caption=message_out_str,
112 | parse_mode=enums.ParseMode.HTML,
113 | disable_notification=True
114 | )
115 | os.remove(local_user_photo)
116 | else:
117 | buttons = [[
118 | InlineKeyboardButton('🔐 Close', callback_data='close_data')
119 | ]]
120 | reply_markup = InlineKeyboardMarkup(buttons)
121 | await message.reply_text(
122 | text=message_out_str,
123 | reply_markup=reply_markup,
124 | quote=True,
125 | parse_mode=enums.ParseMode.HTML,
126 | disable_notification=True
127 | )
128 | await status_message.delete()
129 |
130 | @Client.on_message(filters.command(["imdb", 'search']))
131 | async def imdb_search(client, message):
132 | if ' ' in message.text:
133 | k = await message.reply('Searching ImDB')
134 | r, title = message.text.split(None, 1)
135 | movies = await get_poster(title, bulk=True)
136 | if not movies:
137 | return await message.reply("No results Found")
138 | btn = [
139 | [
140 | InlineKeyboardButton(
141 | text=f"{movie.get('title')} - {movie.get('year')}",
142 | callback_data=f"imdb#{movie.movieID}",
143 | )
144 | ]
145 | for movie in movies
146 | ]
147 | await k.edit('Here is what i found on IMDb', reply_markup=InlineKeyboardMarkup(btn))
148 | else:
149 | await message.reply('Give me a movie / series Name')
150 |
151 | @Client.on_callback_query(filters.regex('^imdb'))
152 | async def imdb_callback(bot: Client, quer_y: CallbackQuery):
153 | i, movie = quer_y.data.split('#')
154 | imdb = await get_poster(query=movie, id=True)
155 | btn = [
156 | [
157 | InlineKeyboardButton(
158 | text=f"{imdb.get('title')}",
159 | url=imdb['url'],
160 | )
161 | ]
162 | ]
163 | message = quer_y.message.reply_to_message or quer_y.message
164 | if imdb:
165 | caption = IMDB_TEMPLATE.format(
166 | query = imdb['title'],
167 | title = imdb['title'],
168 | votes = imdb['votes'],
169 | aka = imdb["aka"],
170 | seasons = imdb["seasons"],
171 | box_office = imdb['box_office'],
172 | localized_title = imdb['localized_title'],
173 | kind = imdb['kind'],
174 | imdb_id = imdb["imdb_id"],
175 | cast = imdb["cast"],
176 | runtime = imdb["runtime"],
177 | countries = imdb["countries"],
178 | certificates = imdb["certificates"],
179 | languages = imdb["languages"],
180 | director = imdb["director"],
181 | writer = imdb["writer"],
182 | producer = imdb["producer"],
183 | composer = imdb["composer"],
184 | cinematographer = imdb["cinematographer"],
185 | music_team = imdb["music_team"],
186 | distributors = imdb["distributors"],
187 | release_date = imdb['release_date'],
188 | year = imdb['year'],
189 | genres = imdb['genres'],
190 | poster = imdb['poster'],
191 | plot = imdb['plot'],
192 | rating = imdb['rating'],
193 | url = imdb['url'],
194 | **locals()
195 | )
196 | else:
197 | caption = "No Results"
198 | if imdb.get('poster'):
199 | try:
200 | await quer_y.message.reply_photo(photo=imdb['poster'], caption=caption, reply_markup=InlineKeyboardMarkup(btn))
201 | except (MediaEmpty, PhotoInvalidDimensions, WebpageMediaEmpty):
202 | pic = imdb.get('poster')
203 | poster = pic.replace('.jpg', "._V1_UX360.jpg")
204 | await quer_y.message.reply_photo(photo=poster, caption=caption, reply_markup=InlineKeyboardMarkup(btn))
205 | except Exception as e:
206 | logger.exception(e)
207 | await quer_y.message.reply(caption, reply_markup=InlineKeyboardMarkup(btn), disable_web_page_preview=False)
208 | await quer_y.message.delete()
209 | else:
210 | await quer_y.message.edit(caption, reply_markup=InlineKeyboardMarkup(btn), disable_web_page_preview=False)
211 | await quer_y.answer()
212 |
213 |
214 |
215 |
--------------------------------------------------------------------------------
/plugins/index.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import asyncio
3 | from pyrogram import Client, filters, enums
4 | from pyrogram.errors import FloodWait
5 | from pyrogram.errors.exceptions.bad_request_400 import ChannelInvalid, ChatAdminRequired, UsernameInvalid, UsernameNotModified
6 | from info import ADMINS
7 | from info import INDEX_REQ_CHANNEL as LOG_CHANNEL
8 | from database.ia_filterdb import save_file
9 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
10 | from utils import temp
11 | import re
12 | logger = logging.getLogger(__name__)
13 | logger.setLevel(logging.INFO)
14 | lock = asyncio.Lock()
15 |
16 |
17 | @Client.on_callback_query(filters.regex(r'^index'))
18 | async def index_files(bot, query):
19 | if query.data.startswith('index_cancel'):
20 | temp.CANCEL = True
21 | return await query.answer("Cancelling Indexing")
22 | _, raju, chat, lst_msg_id, from_user = query.data.split("#")
23 | if raju == 'reject':
24 | await query.message.delete()
25 | await bot.send_message(int(from_user),
26 | f'Your Submission for indexing {chat} has been decliened by our moderators.',
27 | reply_to_message_id=int(lst_msg_id))
28 | return
29 |
30 | if lock.locked():
31 | return await query.answer('Wait until previous process complete.', show_alert=True)
32 | msg = query.message
33 |
34 | await query.answer('Processing...⏳', show_alert=True)
35 | if int(from_user) not in ADMINS:
36 | await bot.send_message(int(from_user),
37 | f'Your Submission for indexing {chat} has been accepted by our moderators and will be added soon.',
38 | reply_to_message_id=int(lst_msg_id))
39 | await msg.edit(
40 | "Starting Indexing",
41 | reply_markup=InlineKeyboardMarkup(
42 | [[InlineKeyboardButton('Cancel', callback_data='index_cancel')]]
43 | )
44 | )
45 | try:
46 | chat = int(chat)
47 | except:
48 | chat = chat
49 | await index_files_to_db(int(lst_msg_id), chat, msg, bot)
50 |
51 |
52 | @Client.on_message((filters.forwarded | (filters.regex("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$")) & filters.text ) & filters.private & filters.incoming)
53 | async def send_for_index(bot, message):
54 | if message.text:
55 | regex = re.compile("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$")
56 | match = regex.match(message.text)
57 | if not match:
58 | return await message.reply('Invalid link')
59 | chat_id = match.group(4)
60 | last_msg_id = int(match.group(5))
61 | if chat_id.isnumeric():
62 | chat_id = int(("-100" + chat_id))
63 | elif message.forward_from_chat.type == enums.ChatType.CHANNEL:
64 | last_msg_id = message.forward_from_message_id
65 | chat_id = message.forward_from_chat.username or message.forward_from_chat.id
66 | else:
67 | return
68 | try:
69 | await bot.get_chat(chat_id)
70 | except ChannelInvalid:
71 | return await message.reply('This may be a private channel / group. Make me an admin over there to index the files.')
72 | except (UsernameInvalid, UsernameNotModified):
73 | return await message.reply('Invalid Link specified.')
74 | except Exception as e:
75 | logger.exception(e)
76 | return await message.reply(f'Errors - {e}')
77 | try:
78 | k = await bot.get_messages(chat_id, last_msg_id)
79 | except:
80 | return await message.reply('Make Sure That Iam An Admin In The Channel, if channel is private')
81 | if k.empty:
82 | return await message.reply('This may be group and iam not a admin of the group.')
83 |
84 | if message.from_user.id in ADMINS:
85 | buttons = [
86 | [
87 | InlineKeyboardButton('Yes',
88 | callback_data=f'index#accept#{chat_id}#{last_msg_id}#{message.from_user.id}')
89 | ],
90 | [
91 | InlineKeyboardButton('close', callback_data='close_data'),
92 | ]
93 | ]
94 | reply_markup = InlineKeyboardMarkup(buttons)
95 | return await message.reply(
96 | f'Do you Want To Index This Channel/ Group ?\n\nChat ID/ Username: {chat_id}\nLast Message ID: {last_msg_id}',
97 | reply_markup=reply_markup)
98 |
99 | if type(chat_id) is int:
100 | try:
101 | link = (await bot.create_chat_invite_link(chat_id)).invite_link
102 | except ChatAdminRequired:
103 | return await message.reply('Make sure iam an admin in the chat and have permission to invite users.')
104 | else:
105 | link = f"@{message.forward_from_chat.username}"
106 | buttons = [
107 | [
108 | InlineKeyboardButton('Accept Index',
109 | callback_data=f'index#accept#{chat_id}#{last_msg_id}#{message.from_user.id}')
110 | ],
111 | [
112 | InlineKeyboardButton('Reject Index',
113 | callback_data=f'index#reject#{chat_id}#{message.id}#{message.from_user.id}'),
114 | ]
115 | ]
116 | reply_markup = InlineKeyboardMarkup(buttons)
117 | await bot.send_message(LOG_CHANNEL,
118 | f'#IndexRequest\n\nBy : {message.from_user.mention} ({message.from_user.id})\nChat ID/ Username - {chat_id}\nLast Message ID - {last_msg_id}\nInviteLink - {link}',
119 | reply_markup=reply_markup)
120 | await message.reply('ThankYou For the Contribution, Wait For My Moderators to verify the files.')
121 |
122 |
123 | @Client.on_message(filters.command('setskip') & filters.user(ADMINS))
124 | async def set_skip_number(bot, message):
125 | if ' ' in message.text:
126 | _, skip = message.text.split(" ")
127 | try:
128 | skip = int(skip)
129 | except:
130 | return await message.reply("Skip number should be an integer.")
131 | await message.reply(f"Successfully set SKIP number as {skip}")
132 | temp.CURRENT = int(skip)
133 | else:
134 | await message.reply("Give me a skip number")
135 |
136 |
137 | async def index_files_to_db(lst_msg_id, chat, msg, bot):
138 | total_files = 0
139 | duplicate = 0
140 | errors = 0
141 | deleted = 0
142 | no_media = 0
143 | unsupported = 0
144 | async with lock:
145 | try:
146 | current = temp.CURRENT
147 | temp.CANCEL = False
148 | async for message in bot.iter_messages(chat, lst_msg_id, temp.CURRENT):
149 | if temp.CANCEL:
150 | await msg.edit(f"Successfully Cancelled!!\n\nSaved {total_files} files to dataBase!\nDuplicate Files Skipped: {duplicate}\nDeleted Messages Skipped: {deleted}\nNon-Media messages skipped: {no_media + unsupported}(Unsupported Media - `{unsupported}` )\nErrors Occurred: {errors}")
151 | break
152 | current += 1
153 | if current % 80 == 0:
154 | can = [[InlineKeyboardButton('Cancel', callback_data='index_cancel')]]
155 | reply = InlineKeyboardMarkup(can)
156 | await msg.edit_text(
157 | text=f"Total messages fetched: {current}\nTotal messages saved: {total_files}\nDuplicate Files Skipped: {duplicate}\nDeleted Messages Skipped: {deleted}\nNon-Media messages skipped: {no_media + unsupported}(Unsupported Media - `{unsupported}` )\nErrors Occurred: {errors}",
158 | reply_markup=reply)
159 | if message.empty:
160 | deleted += 1
161 | continue
162 | elif not message.media:
163 | no_media += 1
164 | continue
165 | elif message.media not in [enums.MessageMediaType.VIDEO, enums.MessageMediaType.AUDIO, enums.MessageMediaType.DOCUMENT]:
166 | unsupported += 1
167 | continue
168 | media = getattr(message, message.media.value, None)
169 | if not media:
170 | unsupported += 1
171 | continue
172 | media.file_type = message.media.value
173 | media.caption = message.caption
174 | aynav, vnay = await save_file(media)
175 | if aynav:
176 | total_files += 1
177 | elif vnay == 0:
178 | duplicate += 1
179 | elif vnay == 2:
180 | errors += 1
181 | except Exception as e:
182 | logger.exception(e)
183 | await msg.edit(f'Error: {e}')
184 | else:
185 | await msg.edit(f'Succesfully saved {total_files} to dataBase!\nDuplicate Files Skipped: {duplicate}\nDeleted Messages Skipped: {deleted}\nNon-Media messages skipped: {no_media + unsupported}(Unsupported Media - `{unsupported}` )\nErrors Occurred: {errors}')
186 |
--------------------------------------------------------------------------------
/plugins/filters.py:
--------------------------------------------------------------------------------
1 | import io
2 | from pyrogram import filters, Client, enums
3 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
4 | from database.filters_mdb import(
5 | add_filter,
6 | get_filters,
7 | delete_filter,
8 | count_filters
9 | )
10 |
11 | from database.connections_mdb import active_connection
12 | from utils import get_file_id, parser, split_quotes
13 | from info import ADMINS
14 |
15 |
16 | @Client.on_message(filters.command(['filter', 'add']) & filters.incoming)
17 | async def addfilter(client, message):
18 | userid = message.from_user.id if message.from_user else None
19 | if not userid:
20 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM")
21 | chat_type = message.chat.type
22 | args = message.text.html.split(None, 1)
23 |
24 | if chat_type == enums.ChatType.PRIVATE:
25 | grpid = await active_connection(str(userid))
26 | if grpid is not None:
27 | grp_id = grpid
28 | try:
29 | chat = await client.get_chat(grpid)
30 | title = chat.title
31 | except:
32 | await message.reply_text("Make sure I'm present in your group!!", quote=True)
33 | return
34 | else:
35 | await message.reply_text("I'm not connected to any groups!", quote=True)
36 | return
37 |
38 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
39 | grp_id = message.chat.id
40 | title = message.chat.title
41 |
42 | else:
43 | return
44 |
45 | st = await client.get_chat_member(grp_id, userid)
46 | if (
47 | st.status != enums.ChatMemberStatus.ADMINISTRATOR
48 | and st.status != enums.ChatMemberStatus.OWNER
49 | and str(userid) not in ADMINS
50 | ):
51 | return
52 |
53 |
54 | if len(args) < 2:
55 | await message.reply_text("Command Incomplete :(", quote=True)
56 | return
57 |
58 | extracted = split_quotes(args[1])
59 | text = extracted[0].lower()
60 |
61 | if not message.reply_to_message and len(extracted) < 2:
62 | await message.reply_text("Add some content to save your filter!", quote=True)
63 | return
64 |
65 | if (len(extracted) >= 2) and not message.reply_to_message:
66 | reply_text, btn, alert = parser(extracted[1], text)
67 | fileid = None
68 | if not reply_text:
69 | await message.reply_text("You cannot have buttons alone, give some text to go with it!", quote=True)
70 | return
71 |
72 | elif message.reply_to_message and message.reply_to_message.reply_markup:
73 | try:
74 | rm = message.reply_to_message.reply_markup
75 | btn = rm.inline_keyboard
76 | msg = get_file_id(message.reply_to_message)
77 | if msg:
78 | fileid = msg.file_id
79 | reply_text = message.reply_to_message.caption.html
80 | else:
81 | reply_text = message.reply_to_message.text.html
82 | fileid = None
83 | alert = None
84 | except:
85 | reply_text = ""
86 | btn = "[]"
87 | fileid = None
88 | alert = None
89 |
90 | elif message.reply_to_message and message.reply_to_message.media:
91 | try:
92 | msg = get_file_id(message.reply_to_message)
93 | fileid = msg.file_id if msg else None
94 | reply_text, btn, alert = parser(extracted[1], text) if message.reply_to_message.sticker else parser(message.reply_to_message.caption.html, text)
95 | except:
96 | reply_text = ""
97 | btn = "[]"
98 | alert = None
99 | elif message.reply_to_message and message.reply_to_message.text:
100 | try:
101 | fileid = None
102 | reply_text, btn, alert = parser(message.reply_to_message.text.html, text)
103 | except:
104 | reply_text = ""
105 | btn = "[]"
106 | alert = None
107 | else:
108 | return
109 |
110 | await add_filter(grp_id, text, reply_text, btn, fileid, alert)
111 |
112 | await message.reply_text(
113 | f"Filter for `{text}` added in **{title}**",
114 | quote=True,
115 | parse_mode=enums.ParseMode.MARKDOWN
116 | )
117 |
118 |
119 | @Client.on_message(filters.command(['viewfilters', 'filters']) & filters.incoming)
120 | async def get_all(client, message):
121 |
122 | chat_type = message.chat.type
123 | userid = message.from_user.id if message.from_user else None
124 | if not userid:
125 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM")
126 | if chat_type == enums.ChatType.PRIVATE:
127 | grpid = await active_connection(str(userid))
128 | if grpid is not None:
129 | grp_id = grpid
130 | try:
131 | chat = await client.get_chat(grpid)
132 | title = chat.title
133 | except:
134 | await message.reply_text("Make sure I'm present in your group!!", quote=True)
135 | return
136 | else:
137 | await message.reply_text("I'm not connected to any groups!", quote=True)
138 | return
139 |
140 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
141 | grp_id = message.chat.id
142 | title = message.chat.title
143 |
144 | else:
145 | return
146 |
147 | st = await client.get_chat_member(grp_id, userid)
148 | if (
149 | st.status != enums.ChatMemberStatus.ADMINISTRATOR
150 | and st.status != enums.ChatMemberStatus.OWNER
151 | and str(userid) not in ADMINS
152 | ):
153 | return
154 |
155 | texts = await get_filters(grp_id)
156 | count = await count_filters(grp_id)
157 | if count:
158 | filterlist = f"Total number of filters in **{title}** : {count}\n\n"
159 |
160 | for text in texts:
161 | keywords = " × `{}`\n".format(text)
162 |
163 | filterlist += keywords
164 |
165 | if len(filterlist) > 4096:
166 | with io.BytesIO(str.encode(filterlist.replace("`", ""))) as keyword_file:
167 | keyword_file.name = "keywords.txt"
168 | await message.reply_document(
169 | document=keyword_file,
170 | quote=True
171 | )
172 | return
173 | else:
174 | filterlist = f"There are no active filters in **{title}**"
175 |
176 | await message.reply_text(
177 | text=filterlist,
178 | quote=True,
179 | parse_mode=enums.ParseMode.MARKDOWN
180 | )
181 |
182 | @Client.on_message(filters.command('del') & filters.incoming)
183 | async def deletefilter(client, message):
184 | userid = message.from_user.id if message.from_user else None
185 | if not userid:
186 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM")
187 | chat_type = message.chat.type
188 |
189 | if chat_type == enums.ChatType.PRIVATE:
190 | grpid = await active_connection(str(userid))
191 | if grpid is not None:
192 | grp_id = grpid
193 | try:
194 | chat = await client.get_chat(grpid)
195 | title = chat.title
196 | except:
197 | await message.reply_text("Make sure I'm present in your group!!", quote=True)
198 | return
199 | else:
200 | await message.reply_text("I'm not connected to any groups!", quote=True)
201 | return
202 |
203 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
204 | grp_id = message.chat.id
205 | title = message.chat.title
206 |
207 | else:
208 | return
209 |
210 | st = await client.get_chat_member(grp_id, userid)
211 | if (
212 | st.status != enums.ChatMemberStatus.ADMINISTRATOR
213 | and st.status != enums.ChatMemberStatus.OWNER
214 | and str(userid) not in ADMINS
215 | ):
216 | return
217 |
218 | try:
219 | cmd, text = message.text.split(" ", 1)
220 | except:
221 | await message.reply_text(
222 | "Mention the filtername which you wanna delete!\n\n"
223 | "/del filtername\n\n"
224 | "Use /viewfilters to view all available filters",
225 | quote=True
226 | )
227 | return
228 |
229 | query = text.lower()
230 |
231 | await delete_filter(message, query, grp_id)
232 |
233 |
234 | @Client.on_message(filters.command('delall') & filters.incoming)
235 | async def delallconfirm(client, message):
236 | userid = message.from_user.id if message.from_user else None
237 | if not userid:
238 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM")
239 | chat_type = message.chat.type
240 |
241 | if chat_type == enums.ChatType.PRIVATE:
242 | grpid = await active_connection(str(userid))
243 | if grpid is not None:
244 | grp_id = grpid
245 | try:
246 | chat = await client.get_chat(grpid)
247 | title = chat.title
248 | except:
249 | await message.reply_text("Make sure I'm present in your group!!", quote=True)
250 | return
251 | else:
252 | await message.reply_text("I'm not connected to any groups!", quote=True)
253 | return
254 |
255 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]:
256 | grp_id = message.chat.id
257 | title = message.chat.title
258 |
259 | else:
260 | return
261 |
262 |
263 | st = await client.get_chat_member(grp_id, userid)
264 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS):
265 | await message.reply_text(
266 | f"This will delete all filters from '{title}'.\nDo you want to continue??",
267 | reply_markup=InlineKeyboardMarkup([
268 | [InlineKeyboardButton(text="YES",callback_data="delallconfirm")],
269 | [InlineKeyboardButton(text="CANCEL",callback_data="delallcancel")]
270 | ]),
271 | quote=True
272 | )
273 |
274 |
--------------------------------------------------------------------------------
/plugins/p_ttishow.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client, filters, enums
2 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
3 | from pyrogram.errors.exceptions.bad_request_400 import MessageTooLong, PeerIdInvalid
4 | from info import ADMINS, LOG_CHANNEL, SUPPORT_CHAT, MELCOW_NEW_USERS
5 | from database.users_chats_db import db
6 | from database.ia_filterdb import Media
7 | from utils import get_size, temp, get_settings
8 | from Script import script
9 | from pyrogram.errors import ChatAdminRequired
10 |
11 | """-----------------------------------------https://t.me/GetTGLink/4179 --------------------------------------"""
12 |
13 | @Client.on_message(filters.new_chat_members & filters.group)
14 | async def save_group(bot, message):
15 | r_j_check = [u.id for u in message.new_chat_members]
16 | if temp.ME in r_j_check:
17 | if not await db.get_chat(message.chat.id):
18 | total=await bot.get_chat_members_count(message.chat.id)
19 | r_j = message.from_user.mention if message.from_user else "Anonymous"
20 | await bot.send_message(LOG_CHANNEL, script.LOG_TEXT_G.format(message.chat.title, message.chat.id, total, r_j))
21 | await db.add_chat(message.chat.id, message.chat.title)
22 | if message.chat.id in temp.BANNED_CHATS:
23 | # Inspired from a boat of a banana tree
24 | buttons = [[
25 | InlineKeyboardButton('𝚂𝚄𝙿𝙿𝙾𝚁𝚃', url=f'https://t.me/{SUPPORT_CHAT}')
26 | ]]
27 | reply_markup=InlineKeyboardMarkup(buttons)
28 | k = await message.reply(
29 | text='CHAT NOT ALLOWED 🐞\n\n𝙼𝚈 𝙰𝙳𝙼𝙸𝙽𝚂 𝙷𝙰𝚂 𝚁𝙴𝚂𝚃𝚁𝙸𝙲𝚃𝙴𝙳 𝙼𝙴 𝙵𝚁𝙾𝙼 𝚆𝙾𝚁𝙺𝙸𝙽𝙶 𝙷𝙴𝚁𝙴 !𝙸𝙵 𝚈𝙾𝚄 𝚆𝙰𝙽𝚃 𝚃𝙾 𝙺𝙽𝙾𝚆 𝙼𝙾𝚁𝙴 𝙰𝙱𝙾𝚄𝚃 𝙸𝚃 𝙲𝙾𝙽𝚃𝙰𝙲𝚃 𝙾𝚆𝙽𝙴𝚁...',
30 | reply_markup=reply_markup,
31 | )
32 |
33 | try:
34 | await k.pin()
35 | except:
36 | pass
37 | await bot.leave_chat(message.chat.id)
38 | return
39 | buttons = [[
40 | InlineKeyboardButton(f'ᴏᴛᴛ ᴜᴘᴅᴀᴛᴇs', url='https://t.me/new_ott_movies3'),
41 | InlineKeyboardButton(f'ᴍᴀɪɴ ᴄʜᴀɴɴᴇʟ', url='https://t.me/mn_movies2')
42 | ]]
43 | reply_markup=InlineKeyboardMarkup(buttons)
44 | await message.reply_text(
45 | text=f"›› 𝚃𝙷𝙰𝙽𝙺𝚂 𝚃𝙾 𝙰𝙳𝙳 𝙼𝙴 𝚃𝙾 𝚈𝙾𝚄𝚁 𝙶𝚁𝙾𝚄𝙿. {message.chat.title} ❣️\n›› 𝙳𝙾𝙽'𝚃 𝙵𝙾𝚁𝙶𝙴𝚃 𝚃𝙾 𝙼𝙰𝙺𝙴 𝙼𝙴 𝙰𝙳𝙼𝙸𝙽.⚡⚡.",
46 | reply_markup=reply_markup)
47 | else:
48 | settings = await get_settings(message.chat.id)
49 | if settings["welcome"]:
50 | for u in message.new_chat_members:
51 | if (temp.MELCOW).get('welcome') is not None:
52 | try:
53 | await (temp.MELCOW['welcome']).delete()
54 | except:
55 | pass
56 | temp.MELCOW['welcome'] = await message.reply_video(
57 | video="https://mangandi-2-0.onrender.com/Xdgv.mp4",
58 | caption=f'ʜᴇʏ, {u.mention} 👋🏻\nᴡᴇʟᴄᴏᴍᴇ ᴛᴏ ᴏᴜʀ ɢʀᴏᴜᴘ {message.chat.title}\n\nʏᴏᴜ ᴄᴀɴ ꜰɪɴᴅ ᴍᴏᴠɪᴇꜱ / ꜱᴇʀɪᴇꜱ / ᴀɴɪᴍᴇꜱ ᴇᴛᴄ. ꜰʀᴏᴍ ʜᴇʀᴇ. ᴇɴᴊᴏʏ😉.',
59 | reply_markup=InlineKeyboardMarkup( [ [ InlineKeyboardButton('ɢʀᴏᴜᴘ', url='htpps://t.me/mn_movies_group2') ] ] )
60 | )
61 |
62 | @Client.on_message(filters.command('leave') & filters.user(ADMINS))
63 | async def leave_a_chat(bot, message):
64 | if len(message.command) == 1:
65 | return await message.reply('Give me a chat id')
66 | chat = message.command[1]
67 | try:
68 | chat = int(chat)
69 | except:
70 | chat = chat
71 | try:
72 | buttons = [[
73 | InlineKeyboardButton('𝚂𝚄𝙿𝙿𝙾𝚁𝚃', url=f'https://t.me/{SUPPORT_CHAT}')
74 | ]]
75 | reply_markup=InlineKeyboardMarkup(buttons)
76 | await bot.send_message(
77 | chat_id=chat,
78 | text='Hello Friends, \nMy admin has told me to leave from group so i go! If you wanna add me again contact my support group.',
79 | reply_markup=reply_markup,
80 | )
81 |
82 | await bot.leave_chat(chat)
83 | await message.reply(f"left the chat `{chat}`")
84 | except Exception as e:
85 | await message.reply(f'Error - {e}')
86 |
87 | @Client.on_message(filters.command('disable') & filters.user(ADMINS))
88 | async def disable_chat(bot, message):
89 | if len(message.command) == 1:
90 | return await message.reply('Give me a chat id')
91 | r = message.text.split(None)
92 | if len(r) > 2:
93 | reason = message.text.split(None, 2)[2]
94 | chat = message.text.split(None, 2)[1]
95 | else:
96 | chat = message.command[1]
97 | reason = "No reason Provided"
98 | try:
99 | chat_ = int(chat)
100 | except:
101 | return await message.reply('Give Me A Valid Chat ID')
102 | cha_t = await db.get_chat(int(chat_))
103 | if not cha_t:
104 | return await message.reply("Chat Not Found In DB")
105 | if cha_t['is_disabled']:
106 | return await message.reply(f"This chat is already disabled:\nReason- {cha_t['reason']} ")
107 | await db.disable_chat(int(chat_), reason)
108 | temp.BANNED_CHATS.append(int(chat_))
109 | await message.reply('Chat Successfully Disabled')
110 | try:
111 | buttons = [[
112 | InlineKeyboardButton('𝚂𝚄𝙿𝙿𝙾𝚁𝚃', url=f'https://t.me/{SUPPORT_CHAT}')
113 | ]]
114 | reply_markup=InlineKeyboardMarkup(buttons)
115 | await bot.send_message(
116 | chat_id=chat_,
117 | text=f'Hello Friends, \nMy admin has told me to leave from group so i go! If you wanna add me again contact my support group. \nReason : {reason}',
118 | reply_markup=reply_markup)
119 | await bot.leave_chat(chat_)
120 | except Exception as e:
121 | await message.reply(f"Error - {e}")
122 |
123 |
124 | @Client.on_message(filters.command('enable') & filters.user(ADMINS))
125 | async def re_enable_chat(bot, message):
126 | if len(message.command) == 1:
127 | return await message.reply('Give me a chat id')
128 | chat = message.command[1]
129 | try:
130 | chat_ = int(chat)
131 | except:
132 | return await message.reply('Give Me A Valid Chat ID')
133 | sts = await db.get_chat(int(chat))
134 | if not sts:
135 | return await message.reply("Chat Not Found In DB !")
136 | if not sts.get('is_disabled'):
137 | return await message.reply('This chat is not yet disabled.')
138 | await db.re_enable_chat(int(chat_))
139 | temp.BANNED_CHATS.remove(int(chat_))
140 | await message.reply("Chat Successfully re-enabled")
141 |
142 |
143 | @Client.on_message(filters.command('stats') & filters.incoming)
144 | async def get_stats(bot, message):
145 | if message.from_user.id in ADMINS:
146 | rju = await message.reply('𝙰𝙲𝙲𝙴𝚂𝚂𝙸𝙽𝙶 𝚂𝚃𝙰𝚃𝚄𝚂 𝙳𝙴𝚃𝙰𝙸𝙻𝚂...')
147 | total_users = await db.total_users_count()
148 | totl_chats = await db.total_chat_count()
149 | files = await Media.count_documents()
150 | size = await db.get_db_size()
151 | free = 536870912 - size
152 | size = get_size(size)
153 | free = get_size(free)
154 | await rju.edit(script.STATUS_TXT.format(files, total_users, totl_chats, size, free))
155 | else:
156 | k = await message.reply_text("Sᴏʀʀʏ ᴛʜɪꜱ ᴄᴏᴍᴍᴀɴᴅ ᴏɴʟʏ ᴀᴅᴍɪɴꜱ")
157 | await asyncio.sleep(10)
158 | await k.delete()
159 | await message.delete()
160 |
161 |
162 | # a function for trespassing into others groups, Inspired by a Vazha
163 | # Not to be used , But Just to showcase his vazhatharam.
164 | @Client.on_message(filters.command('invite') & filters.user(ADMINS))
165 | async def gen_invite(bot, message):
166 | if len(message.command) == 1:
167 | return await message.reply('Give me a chat id')
168 | chat = message.command[1]
169 | try:
170 | chat = int(chat)
171 | except:
172 | return await message.reply('Give Me A Valid Chat ID')
173 | try:
174 | link = await bot.create_chat_invite_link(chat)
175 | except ChatAdminRequired:
176 | return await message.reply("Invite Link Generation Failed, Iam Not Having Sufficient Rights")
177 | except Exception as e:
178 | return await message.reply(f'Error {e}')
179 | await message.reply(f'Here is your Invite Link {link.invite_link}')
180 |
181 | @Client.on_message(filters.command('ban') & filters.user(ADMINS))
182 | async def ban_a_user(bot, message):
183 | # https://t.me/GetTGLink/4185
184 | if len(message.command) == 1:
185 | return await message.reply('Give me a user id / username')
186 | r = message.text.split(None)
187 | if len(r) > 2:
188 | reason = message.text.split(None, 2)[2]
189 | chat = message.text.split(None, 2)[1]
190 | else:
191 | chat = message.command[1]
192 | reason = "No reason Provided"
193 | try:
194 | chat = int(chat)
195 | except:
196 | pass
197 | try:
198 | k = await bot.get_users(chat)
199 | except PeerIdInvalid:
200 | return await message.reply("This is an invalid user, make sure ia have met him before.")
201 | except IndexError:
202 | return await message.reply("This might be a channel, make sure its a user.")
203 | except Exception as e:
204 | return await message.reply(f'Error - {e}')
205 | else:
206 | jar = await db.get_ban_status(k.id)
207 | if jar['is_banned']:
208 | return await message.reply(f"{k.mention} is already banned\nReason: {jar['ban_reason']}")
209 | await db.ban_user(k.id, reason)
210 | temp.BANNED_USERS.append(k.id)
211 | await message.reply(f"Successfully banned {k.mention}")
212 |
213 |
214 |
215 | @Client.on_message(filters.command('unban') & filters.user(ADMINS))
216 | async def unban_a_user(bot, message):
217 | if len(message.command) == 1:
218 | return await message.reply('Give me a user id / username')
219 | r = message.text.split(None)
220 | if len(r) > 2:
221 | reason = message.text.split(None, 2)[2]
222 | chat = message.text.split(None, 2)[1]
223 | else:
224 | chat = message.command[1]
225 | reason = "No reason Provided"
226 | try:
227 | chat = int(chat)
228 | except:
229 | pass
230 | try:
231 | k = await bot.get_users(chat)
232 | except PeerIdInvalid:
233 | return await message.reply("This is an invalid user, make sure ia have met him before.")
234 | except IndexError:
235 | return await message.reply("Thismight be a channel, make sure its a user.")
236 | except Exception as e:
237 | return await message.reply(f'Error - {e}')
238 | else:
239 | jar = await db.get_ban_status(k.id)
240 | if not jar['is_banned']:
241 | return await message.reply(f"{k.mention} is not yet banned.")
242 | await db.remove_ban(k.id)
243 | temp.BANNED_USERS.remove(k.id)
244 | await message.reply(f"Successfully unbanned {k.mention}")
245 |
246 |
247 |
248 | @Client.on_message(filters.command('users') & filters.user(ADMINS))
249 | async def list_users(bot, message):
250 | # https://t.me/GetTGLink/4184
251 | raju = await message.reply('Getting List Of Users')
252 | users = await db.get_all_users()
253 | out = "Users Saved In DB Are:\n\n"
254 | async for user in users:
255 | out += f"{user['name']}"
256 | if user['ban_status']['is_banned']:
257 | out += '( Banned User )'
258 | out += '\n'
259 | try:
260 | await raju.edit_text(out)
261 | except MessageTooLong:
262 | with open('users.txt', 'w+') as outfile:
263 | outfile.write(out)
264 | await message.reply_document('users.txt', caption="List Of Users")
265 |
266 | @Client.on_message(filters.command('chats') & filters.user(ADMINS))
267 | async def list_chats(bot, message):
268 | raju = await message.reply('Getting List Of chats')
269 | chats = await db.get_all_chats()
270 | out = "Chats Saved In DB Are:\n\n"
271 | async for chat in chats:
272 | out += f"**Title:** `{chat['title']}`\n**- ID:** `{chat['id']}`"
273 | if chat['chat_status']['is_disabled']:
274 | out += '( Disabled Chat )'
275 | out += '\n'
276 | try:
277 | await raju.edit_text(out)
278 | except MessageTooLong:
279 | with open('chats.txt', 'w+') as outfile:
280 | outfile.write(out)
281 | await message.reply_document('chats.txt', caption="List Of Chats")
282 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | # @MrMNTG @MusammilN
2 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
3 |
4 | import logging
5 | from pyrogram.errors import InputUserDeactivated, UserNotParticipant, FloodWait, UserIsBlocked, PeerIdInvalid
6 | from info import LONG_IMDB_DESCRIPTION, MAX_LIST_ELM
7 | from imdb import IMDb
8 | import asyncio
9 | from pyrogram.types import Message, InlineKeyboardButton
10 | from pyrogram import enums
11 | from typing import Union
12 | import re
13 | import os
14 | from datetime import datetime
15 | from typing import List
16 | from database.users_chats_db import db
17 | from bs4 import BeautifulSoup
18 | import requests
19 |
20 | # @MrMNTG @MusammilN
21 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
22 | logger = logging.getLogger(__name__)
23 | logger.setLevel(logging.INFO)
24 |
25 | BTN_URL_REGEX = re.compile(
26 | r"(\[([^\[]+?)\]\((buttonurl|buttonalert):(?:/{0,2})(.+?)(:same)?\))"
27 | )
28 |
29 | imdb = IMDb()
30 |
31 | BANNED = {}
32 | SMART_OPEN = '“'
33 | SMART_CLOSE = '”'
34 | START_CHAR = ('\'', '"', SMART_OPEN)
35 |
36 | # temp db for banned
37 | class temp(object):
38 | BANNED_USERS = []
39 | BANNED_CHATS = []
40 | ME = None
41 | CURRENT=int(os.environ.get("SKIP", 2))
42 | CANCEL = False
43 | MELCOW = {}
44 | U_NAME = None
45 | B_NAME = None
46 | SETTINGS = {}
47 |
48 | # @MrMNTG @MusammilN
49 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
50 | from pyrogram.enums import ChatMemberStatus
51 | from database.users_chats_db import db
52 | from info import REQUEST_FSUB_MODE # Import from your info.py
53 |
54 | JOIN_REQUEST_USERS = {}
55 |
56 | async def is_subscribed(user_id: int, client) -> bool:
57 | auth_channels = await db.get_auth_channels()
58 | if not auth_channels:
59 | return True # No channels to check
60 |
61 | # Check if user is joined in channels
62 | joined_all = True
63 | for channel in auth_channels:
64 | try:
65 | member = await client.get_chat_member(channel, user_id)
66 | if member.status not in [
67 | ChatMemberStatus.MEMBER,
68 | ChatMemberStatus.ADMINISTRATOR,
69 | ChatMemberStatus.OWNER,
70 | ]:
71 | joined_all = False
72 | break
73 | except Exception:
74 | joined_all = False
75 | break
76 |
77 | if joined_all:
78 | return True
79 |
80 | # If REQUEST_FSUB_MODE is True, check join requests
81 | if REQUEST_FSUB_MODE:
82 | requested_channels = JOIN_REQUEST_USERS.get(user_id, set())
83 | if set(auth_channels).issubset(requested_channels):
84 | return True
85 |
86 | return False
87 |
88 | async def create_invite_links(client) -> dict:
89 | links = {}
90 | auth_channels = await db.get_auth_channels()
91 | for channel in auth_channels:
92 | try:
93 | invite = await client.create_chat_invite_link(
94 | channel,
95 | creates_join_request=REQUEST_FSUB_MODE, # Only enable join requests if REQUEST_FSUB_MODE is True
96 | name="BotAuthAccess"
97 | )
98 | links[channel] = invite.invite_link
99 | except Exception:
100 | continue
101 | return links
102 |
103 | # @MrMNTG @MusammilN
104 | #please give credits https://github.com/MN-BOTS/ShobanaFilterBot
105 | async def get_poster(query, bulk=False, id=False, file=None):
106 | if not id:
107 | # https://t.me/GetTGLink/4183
108 | query = (query.strip()).lower()
109 | title = query
110 | year = re.findall(r'[1-2]\d{3}$', query, re.IGNORECASE)
111 | if year:
112 | year = list_to_str(year[:1])
113 | title = (query.replace(year, "")).strip()
114 | elif file is not None:
115 | year = re.findall(r'[1-2]\d{3}', file, re.IGNORECASE)
116 | if year:
117 | year = list_to_str(year[:1])
118 | else:
119 | year = None
120 | movieid = imdb.search_movie(title.lower(), results=10)
121 | if not movieid:
122 | return None
123 | if year:
124 | filtered=list(filter(lambda k: str(k.get('year')) == str(year), movieid))
125 | if not filtered:
126 | filtered = movieid
127 | else:
128 | filtered = movieid
129 | movieid=list(filter(lambda k: k.get('kind') in ['movie', 'tv series'], filtered))
130 | if not movieid:
131 | movieid = filtered
132 | if bulk:
133 | return movieid
134 | movieid = movieid[0].movieID
135 | else:
136 | movieid = query
137 | movie = imdb.get_movie(movieid)
138 | if movie.get("original air date"):
139 | date = movie["original air date"]
140 | elif movie.get("year"):
141 | date = movie.get("year")
142 | else:
143 | date = "N/A"
144 | plot = ""
145 | if not LONG_IMDB_DESCRIPTION:
146 | plot = movie.get('plot')
147 | if plot and len(plot) > 0:
148 | plot = plot[0]
149 | else:
150 | plot = movie.get('plot outline')
151 | if plot and len(plot) > 800:
152 | plot = plot[0:800] + "..."
153 |
154 | return {
155 | 'title': movie.get('title'),
156 | 'votes': movie.get('votes'),
157 | "aka": list_to_str(movie.get("akas")),
158 | "seasons": movie.get("number of seasons"),
159 | "box_office": movie.get('box office'),
160 | 'localized_title': movie.get('localized title'),
161 | 'kind': movie.get("kind"),
162 | "imdb_id": f"tt{movie.get('imdbID')}",
163 | "cast": list_to_str(movie.get("cast")),
164 | "runtime": list_to_str(movie.get("runtimes")),
165 | "countries": list_to_str(movie.get("countries")),
166 | "certificates": list_to_str(movie.get("certificates")),
167 | "languages": list_to_str(movie.get("languages")),
168 | "director": list_to_str(movie.get("director")),
169 | "writer":list_to_str(movie.get("writer")),
170 | "producer":list_to_str(movie.get("producer")),
171 | "composer":list_to_str(movie.get("composer")) ,
172 | "cinematographer":list_to_str(movie.get("cinematographer")),
173 | "music_team": list_to_str(movie.get("music department")),
174 | "distributors": list_to_str(movie.get("distributors")),
175 | 'release_date': date,
176 | 'year': movie.get('year'),
177 | 'genres': list_to_str(movie.get("genres")),
178 | 'poster': movie.get('full-size cover url'),
179 | 'plot': plot,
180 | 'rating': str(movie.get("rating")),
181 | 'url':f'https://www.imdb.com/title/tt{movieid}'
182 | }
183 | # https://github.com/odysseusmax/animated-lamp/blob/2ef4730eb2b5f0596ed6d03e7b05243d93e3415b/bot/utils/broadcast.py#L37
184 |
185 | async def broadcast_messages(user_id, message):
186 | try:
187 | await message.copy(chat_id=user_id)
188 | return True, "Success"
189 | except FloodWait as e:
190 | await asyncio.sleep(e.x)
191 | return await broadcast_messages(user_id, message)
192 | except InputUserDeactivated:
193 | await db.delete_user(int(user_id))
194 | logging.info(f"{user_id}-Removed from Database, since deleted account.")
195 | return False, "Deleted"
196 | except UserIsBlocked:
197 | logging.info(f"{user_id} -Blocked the bot.")
198 | return False, "Blocked"
199 | except PeerIdInvalid:
200 | await db.delete_user(int(user_id))
201 | logging.info(f"{user_id} - PeerIdInvalid")
202 | return False, "Error"
203 | except Exception as e:
204 | return False, "Error"
205 |
206 | async def search_gagala(text):
207 | usr_agent = {
208 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
209 | 'Chrome/61.0.3163.100 Safari/537.36'
210 | }
211 | text = text.replace(" ", '+')
212 | url = f'https://www.google.com/search?q={text}'
213 | response = requests.get(url, headers=usr_agent)
214 | response.raise_for_status()
215 | soup = BeautifulSoup(response.text, 'html.parser')
216 | titles = soup.find_all( 'h3' )
217 | return [title.getText() for title in titles]
218 |
219 |
220 | async def get_settings(group_id):
221 | settings = temp.SETTINGS.get(group_id)
222 | if not settings:
223 | settings = await db.get_settings(group_id)
224 | temp.SETTINGS[group_id] = settings
225 | return settings
226 |
227 | async def save_group_settings(group_id, key, value):
228 | current = await get_settings(group_id)
229 | current[key] = value
230 | temp.SETTINGS[group_id] = current
231 | await db.update_settings(group_id, current)
232 |
233 | def get_size(size):
234 | """Get size in readable format"""
235 |
236 | units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"]
237 | size = float(size)
238 | i = 0
239 | while size >= 1024.0 and i < len(units):
240 | i += 1
241 | size /= 1024.0
242 | return "%.2f %s" % (size, units[i])
243 |
244 | def split_list(l, n):
245 | for i in range(0, len(l), n):
246 | yield l[i:i + n]
247 |
248 | def get_file_id(msg: Message):
249 | if msg.media:
250 | for message_type in (
251 | "photo",
252 | "animation",
253 | "audio",
254 | "document",
255 | "video",
256 | "video_note",
257 | "voice",
258 | "sticker"
259 | ):
260 | obj = getattr(msg, message_type)
261 | if obj:
262 | setattr(obj, "message_type", message_type)
263 | return obj
264 |
265 | def extract_user(message: Message) -> Union[int, str]:
266 | """extracts the user from a message"""
267 | # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7
268 | user_id = None
269 | user_first_name = None
270 | if message.reply_to_message:
271 | user_id = message.reply_to_message.from_user.id
272 | user_first_name = message.reply_to_message.from_user.first_name
273 |
274 | elif len(message.command) > 1:
275 | if (
276 | len(message.entities) > 1 and
277 | message.entities[1].type == enums.MessageEntityType.TEXT_MENTION
278 | ):
279 |
280 | required_entity = message.entities[1]
281 | user_id = required_entity.user.id
282 | user_first_name = required_entity.user.first_name
283 | else:
284 | user_id = message.command[1]
285 | # don't want to make a request -_-
286 | user_first_name = user_id
287 | try:
288 | user_id = int(user_id)
289 | except ValueError:
290 | pass
291 | else:
292 | user_id = message.from_user.id
293 | user_first_name = message.from_user.first_name
294 | return (user_id, user_first_name)
295 |
296 | def list_to_str(k):
297 | if not k:
298 | return "N/A"
299 | elif len(k) == 1:
300 | return str(k[0])
301 | elif MAX_LIST_ELM:
302 | k = k[:int(MAX_LIST_ELM)]
303 | return ' '.join(f'{elem}, ' for elem in k)
304 | else:
305 | return ' '.join(f'{elem}, ' for elem in k)
306 |
307 | def last_online(from_user):
308 | time = ""
309 | if from_user.is_bot:
310 | time += "🤖 Bot :("
311 | elif from_user.status == enums.UserStatus.RECENTLY:
312 | time += "Recently"
313 | elif from_user.status == enums.UserStatus.LAST_WEEK:
314 | time += "Within the last week"
315 | elif from_user.status == enums.UserStatus.LAST_MONTH:
316 | time += "Within the last month"
317 | elif from_user.status == enums.UserStatus.LONG_AGO:
318 | time += "A long time ago :("
319 | elif from_user.status == enums.UserStatus.ONLINE:
320 | time += "Currently Online"
321 | elif from_user.status == enums.UserStatus.OFFLINE:
322 | time += from_user.last_online_date.strftime("%a, %d %b %Y, %H:%M:%S")
323 | return time
324 |
325 |
326 | def split_quotes(text: str) -> List:
327 | if not any(text.startswith(char) for char in START_CHAR):
328 | return text.split(None, 1)
329 | counter = 1 # ignore first char -> is some kind of quote
330 | while counter < len(text):
331 | if text[counter] == "\\":
332 | counter += 1
333 | elif text[counter] == text[0] or (text[0] == SMART_OPEN and text[counter] == SMART_CLOSE):
334 | break
335 | counter += 1
336 | else:
337 | return text.split(None, 1)
338 |
339 | # 1 to avoid starting quote, and counter is exclusive so avoids ending
340 | key = remove_escapes(text[1:counter].strip())
341 | # index will be in range, or `else` would have been executed and returned
342 | rest = text[counter + 1:].strip()
343 | if not key:
344 | key = text[0] + text[0]
345 | return list(filter(None, [key, rest]))
346 |
347 | def parser(text, keyword):
348 | if "buttonalert" in text:
349 | text = (text.replace("\n", "\\n").replace("\t", "\\t"))
350 | buttons = []
351 | note_data = ""
352 | prev = 0
353 | i = 0
354 | alerts = []
355 | for match in BTN_URL_REGEX.finditer(text):
356 | # Check if btnurl is escaped
357 | n_escapes = 0
358 | to_check = match.start(1) - 1
359 | while to_check > 0 and text[to_check] == "\\":
360 | n_escapes += 1
361 | to_check -= 1
362 |
363 | # if even, not escaped -> create button
364 | if n_escapes % 2 == 0:
365 | note_data += text[prev:match.start(1)]
366 | prev = match.end(1)
367 | if match.group(3) == "buttonalert":
368 | # create a thruple with button label, url, and newline status
369 | if bool(match.group(5)) and buttons:
370 | buttons[-1].append(InlineKeyboardButton(
371 | text=match.group(2),
372 | callback_data=f"alertmessage:{i}:{keyword}"
373 | ))
374 | else:
375 | buttons.append([InlineKeyboardButton(
376 | text=match.group(2),
377 | callback_data=f"alertmessage:{i}:{keyword}"
378 | )])
379 | i += 1
380 | alerts.append(match.group(4))
381 | elif bool(match.group(5)) and buttons:
382 | buttons[-1].append(InlineKeyboardButton(
383 | text=match.group(2),
384 | url=match.group(4).replace(" ", "")
385 | ))
386 | else:
387 | buttons.append([InlineKeyboardButton(
388 | text=match.group(2),
389 | url=match.group(4).replace(" ", "")
390 | )])
391 |
392 | else:
393 | note_data += text[prev:to_check]
394 | prev = match.start(1) - 1
395 | else:
396 | note_data += text[prev:]
397 |
398 | try:
399 | return note_data, buttons, alerts
400 | except:
401 | return note_data, buttons, None
402 |
403 | def remove_escapes(text: str) -> str:
404 | res = ""
405 | is_escaped = False
406 | for counter in range(len(text)):
407 | if is_escaped:
408 | res += text[counter]
409 | is_escaped = False
410 | elif text[counter] == "\\":
411 | is_escaped = True
412 | else:
413 | res += text[counter]
414 | return res
415 |
416 |
417 | def humanbytes(size):
418 | if not size:
419 | return ""
420 | power = 2**10
421 | n = 0
422 | Dic_powerN = {0: ' ', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti'}
423 | while size > power:
424 | size /= power
425 | n += 1
426 | return str(round(size, 2)) + " " + Dic_powerN[n] + 'B'
427 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |