├── .gitignore ├── Dockerfile.sample ├── LICENSE ├── Procfile ├── PyrogramBot ├── __init__.py ├── __main__.py ├── config.py ├── db │ ├── README.md │ ├── chatsdb │ ├── filtersdb │ ├── gbansdb │ ├── notesdb │ └── warnsdb ├── modules │ ├── __init__.py │ ├── __main__.py │ ├── eval.py │ ├── fun.py │ ├── ping.py │ └── start.py └── utils │ ├── errors.py │ └── misc.py ├── README.md ├── app.json ├── heroku.yml.sample ├── requirements.txt └── runtime.txt /.gitignore: -------------------------------------------------------------------------------- 1 | token.py 2 | *.raw 3 | images/ 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *.png 8 | *.mp4 9 | *.webm 10 | *$py.class 11 | *.mp3 12 | . 13 | # C extensions 14 | *.so 15 | config.py 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | pip-wheel-metadata/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | target/ 83 | 84 | # Jupyter Notebook 85 | .ipynb_checkpoints 86 | 87 | # IPython 88 | profile_default/ 89 | ipython_config.py 90 | 91 | # pyenv 92 | .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # celery beat schedule file 102 | celerybeat-schedule 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | *.ini 134 | *.pyc 135 | *.session 136 | *.session-journal 137 | 138 | # vim 139 | [._]*.sw[a-p] 140 | 141 | #others 142 | neofetch.txt 143 | error.log 144 | permissions.json 145 | .vscode -------------------------------------------------------------------------------- /Dockerfile.sample: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | WORKDIR /usr/src/app 4 | RUN chmod 777 /usr/src/app 5 | RUN apt-get -qq update 6 | RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq install python3 python3-pip software-properties-common 7 | 8 | #Updating Libraries 9 | RUN pip3 install -U pip 10 | COPY requirements.txt . 11 | RUN pip3 install --no-cache-dir -U -r requirements.txt 12 | 13 | #Copying All Source 14 | COPY . . 15 | 16 | #Starting Bot 17 | CMD ["python3", "-m", "PyrogramBot"] 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 MaskedVirus | swatv3nub 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m PyrogramBot 2 | -------------------------------------------------------------------------------- /PyrogramBot/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | import os 4 | import sys 5 | from pyrogram import Client, errors 6 | from .config import API_ID, API_HASH, BOT_TOKEN 7 | #from .config import MONGO_DB_URI 8 | 9 | # Adding Mongo Example 10 | 11 | # from motor.motor_asyncio import AsyncIOMotorClient as MongoClient 12 | 13 | LOAD_MODULES = [] 14 | NOLOAD_MODULES = [] 15 | StartTime = time.time() 16 | logging.basicConfig(level=logging.INFO) 17 | 18 | if sys.version_info[0] < 3 or sys.version_info[1] < 8: 19 | LOGGER.error( 20 | ( 21 | "You MUST have a Python Version of at least 3.8!\n" 22 | "Multiple features depend on this. Aborting The Deploy!" 23 | ) 24 | ) 25 | quit(1) 26 | 27 | app = Client("pyrogrambot", api_id=API_ID, api_hash=API_HASH, bot_token=BOT_TOKEN) 28 | 29 | bot_start_time = time.time() 30 | 31 | # mongodb = MongoClient(MONGO_DB_URI) 32 | # db = mongodb.pyrogrambot 33 | -------------------------------------------------------------------------------- /PyrogramBot/__main__.py: -------------------------------------------------------------------------------- 1 | from pyrogram import idle, Client 2 | 3 | from pyrogrambot import app, filters 4 | from pyrogrambot.utils.misc import count_modules 5 | 6 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 7 | 8 | import importlib 9 | import re 10 | 11 | #Bot info 12 | 13 | async def get_info(app): 14 | global BOT_ID, BOT_NAME, BOT_USERNAME, BOT_DC_ID 15 | getme = await app.get_me() 16 | BOT_ID = getme.id 17 | 18 | if getme.last_name: 19 | BOT_NAME = getme.first_name + " " + getme.last_name 20 | else: 21 | BOT_NAME = getme.first_name 22 | BOT_USERNAME = getme.username 23 | BOT_DC_ID = getme.dc_id 24 | 25 | #Help [Taken From WBB] 26 | 27 | HELPABLE = {} 28 | 29 | 30 | async def start_bot(): 31 | await app.start() 32 | await get_info(app) 33 | 34 | for module in ALL_MODULES: 35 | imported_module = importlib.import_module("PyrogramBot.modules." + module) 36 | if ( 37 | hasattr(imported_module, "__MODULE__") 38 | and imported_module.__MODULE__ 39 | ): 40 | imported_module.__MODULE__ = imported_module.__MODULE__ 41 | if ( 42 | hasattr(imported_module, "__HELP__") 43 | and imported_module.__HELP__ 44 | ): 45 | HELPABLE[ 46 | imported_module.__MODULE__.lower() 47 | ] = imported_module 48 | 49 | bot_modules = "" 50 | j = 1 51 | for i in ALL_MODULES: 52 | if j == 4: 53 | bot_modules += "|{:<15}|\n".format(i) 54 | j = 0 55 | else: 56 | bot_modules += "|{:<15}".format(i) 57 | j += 1 58 | print(">>------------|•|-----------<<") 59 | print(f">>------|• {BOT_NAME} •|-----<<") 60 | print(">>------------|•|-----------<<") 61 | print(bot_modules) 62 | print("Bot is Up, Game ON!!") 63 | await idle() 64 | 65 | 66 | 67 | 68 | @app.on_message(filters.command("help")) 69 | async def help_command(_, message): 70 | if message.chat.type != "private": 71 | if len(message.command) >= 2 and message.command[1] == "help": 72 | text, keyboard = await help_parser(message) 73 | await message.reply( 74 | text, reply_markup=keyboard, disable_web_page_preview=True 75 | ) 76 | return 77 | keyboard = InlineKeyboardMarkup( 78 | [ 79 | [ 80 | InlineKeyboardButton( 81 | text="Help", 82 | url=f"t.me/{BOT_USERNAME}?start=help", 83 | ) 84 | ] 85 | ] 86 | ) 87 | await message.reply("Contact me in PM.", reply_markup=keyboard) 88 | return 89 | text, keyboard = await help_parser(message) 90 | await message.reply_text(text, reply_markup=keyboard) 91 | 92 | 93 | async def help_parser(message, keyboard=None): 94 | if not keyboard: 95 | keyboard = InlineKeyboardMarkup(count_modules(0, HELPABLE, "help")) 96 | return ( 97 | "Hi {first_name}, I am a bot".format( 98 | first_name=message.from_user.first_name, 99 | ), 100 | keyboard, 101 | ) 102 | 103 | 104 | @app.on_callback_query(filters.regex(r"help_(.*?)")) 105 | async def help_button(client, query): 106 | mod_match = re.match(r"help_module\((.+?)\)", query.data) 107 | prev_match = re.match(r"help_prev\((.+?)\)", query.data) 108 | next_match = re.match(r"help_next\((.+?)\)", query.data) 109 | back_match = re.match(r"help_back", query.data) 110 | create_match = re.match(r"help_create", query.data) 111 | 112 | if mod_match: 113 | module = mod_match.group(1) 114 | text = ( 115 | "{} **{}**:\n".format( 116 | "Here is the help for", HELPABLE[module].__MODULE__ 117 | ) 118 | + HELPABLE[module].__HELP__ 119 | ) 120 | 121 | await query.message.edit( 122 | text=text, 123 | reply_markup=InlineKeyboardMarkup( 124 | [[InlineKeyboardButton("back", callback_data="help_back")]] 125 | ), 126 | disable_web_page_preview=True, 127 | ) 128 | 129 | elif prev_match: 130 | curr_page = int(prev_match.group(1)) 131 | await query.message.edit( 132 | text="Hi {first_name}. I am bot".format( 133 | first_name=query.from_user.first_name, 134 | ), 135 | reply_markup=InlineKeyboardMarkup( 136 | count_modules(curr_page - 1, HELPABLE, "help") 137 | ), 138 | disable_web_page_preview=True, 139 | ) 140 | 141 | elif next_match: 142 | next_page = int(next_match.group(1)) 143 | await query.message.edit( 144 | text="Hi {first_name}. I am a bot".format( 145 | first_name=query.from_user.first_name, 146 | ), 147 | reply_markup=InlineKeyboardMarkup( 148 | count_modules(next_page + 1, HELPABLE, "help") 149 | ), 150 | disable_web_page_preview=True, 151 | ) 152 | 153 | elif back_match: 154 | await query.message.edit( 155 | text="Hi {first_name}. I am a bot".format( 156 | first_name=query.from_user.first_name, 157 | ), 158 | reply_markup=InlineKeyboardMarkup( 159 | count_modules(0, HELPABLE, "help") 160 | ), 161 | disable_web_page_preview=True, 162 | ) 163 | 164 | elif create_match: 165 | text, keyboard = await help_parser(query) 166 | await query.message.edit( 167 | text=text, reply_markup=keyboard, disable_web_page_preview=True 168 | ) 169 | 170 | return await client.answer_callback_query(query.id) 171 | 172 | 173 | 174 | if __name__ == "__main__": 175 | app.start() 176 | idle() 177 | -------------------------------------------------------------------------------- /PyrogramBot/config.py: -------------------------------------------------------------------------------- 1 | BOT_TOKEN = "16383:BSNSJS" 2 | API_ID = 69 3 | API_HASH = "HSJSN7288KSNEJD" 4 | #MONGO_DB_URI = "mongodb+srv://username:password@cluster0.ksiis.mongodb.net/YourDataBaseName?retryWrites=true&w=majority" 5 | -------------------------------------------------------------------------------- /PyrogramBot/db/README.md: -------------------------------------------------------------------------------- 1 | All The Database Codes are taken from [Akshay Rajput](https://t.me/TheHamkerCat)'s [WilliamButcherBot](https://github.com/TheHamkerCat/WilliamButcherBot.git)'s Repo 2 | 3 | Complete Authorship of Database Codes to Akshay. 4 | 5 | **NOTE:** I didn't Gave File Extensions in db files. 6 | If you want to use Them, Edit the File names with *.py extension. 7 | -------------------------------------------------------------------------------- /PyrogramBot/db/chatsdb: -------------------------------------------------------------------------------- 1 | from PyrogramBot import db 2 | from typing import List 3 | 4 | 5 | chatsdb = db.chats 6 | 7 | 8 | async def is_in_chat(chat_id: int) -> bool: 9 | chat = await chatsdb.find_one({"chat_id": chat_id}) 10 | if not chat: 11 | return False 12 | return True 13 | 14 | 15 | async def get_in_chats() -> list: 16 | chats = chatsdb.find({"chat_id": {'$lt': 0}}) 17 | if not chats: 18 | return [] 19 | chats_list = [] 20 | for chat in await chats.to_list(length=1000000000): 21 | chats_list.append(chat) 22 | return chats_list 23 | 24 | 25 | async def add_in_chat(chat_id: int): 26 | is_in = await is_in_chat(chat_id) 27 | if is_in: 28 | return 29 | return await chatsdb.insert_one({"chat_id": chat_id}) 30 | 31 | 32 | async def remove_in_chat(chat_id: int): 33 | is_in = await is_in_chat(chat_id) 34 | if not is_in: 35 | return 36 | return await chatsdb.delete_one({"chat_id": chat_id}) -------------------------------------------------------------------------------- /PyrogramBot/db/filtersdb: -------------------------------------------------------------------------------- 1 | from PyrogramBot import db 2 | from typing import Dict, List, Union 3 | 4 | filtersdb = db.filters 5 | 6 | async def get_filters_count() -> dict: 7 | chats = filtersdb.find({"chat_id": {"$lt": 0}}) 8 | if not chats: 9 | return {} 10 | chats_count = 0 11 | filters_count = 0 12 | for chat in await chats.to_list(length=1000000000): 13 | filters_name = await get_filters_names(chat['chat_id']) 14 | filters_count += len(filters_name) 15 | chats_count += 1 16 | return { 17 | "chats_count": chats_count, 18 | "filters_count": filters_count 19 | } 20 | 21 | 22 | async def _get_filters(chat_id: int) -> Dict[str, int]: 23 | _filters = await filtersdb.find_one({"chat_id": chat_id}) 24 | if _filters: 25 | _filters = _filters['filters'] 26 | else: 27 | _filters = {} 28 | return _filters 29 | 30 | 31 | async def get_filters_names(chat_id: int) -> List[str]: 32 | _filters = [] 33 | for _filter in await _get_filters(chat_id): 34 | _filters.append(_filter) 35 | return _filters 36 | 37 | 38 | async def get_filter(chat_id: int, name: str) -> Union[bool, dict]: 39 | name = name.lower().strip() 40 | _filters = await _get_filters(chat_id) 41 | if name in _filters: 42 | return _filters[name] 43 | else: 44 | return False 45 | 46 | 47 | async def save_filter(chat_id: int, name: str, _filter: dict): 48 | name = name.lower().strip() 49 | _filters = await _get_filters(chat_id) 50 | _filters[name] = _filter 51 | await filtersdb.update_one( 52 | {"chat_id": chat_id}, 53 | { 54 | "$set": { 55 | "filters": _filters 56 | } 57 | }, 58 | upsert=True 59 | ) 60 | 61 | 62 | async def delete_filter(chat_id: int, name: str) -> bool: 63 | filtersd = await _get_filters(chat_id) 64 | name = name.lower().strip() 65 | if name in filtersd: 66 | del filtersd[name] 67 | await filtersdb.update_one( 68 | {"chat_id": chat_id}, 69 | { 70 | "$set": { 71 | "filters": filtersd 72 | } 73 | }, 74 | upsert=True 75 | ) 76 | return True 77 | return False -------------------------------------------------------------------------------- /PyrogramBot/db/gbansdb: -------------------------------------------------------------------------------- 1 | from PyrogramBot import db 2 | 3 | 4 | gbansdb = db.gban 5 | 6 | 7 | async def get_gbans_count() -> int: 8 | users = gbansdb.find({"user_id": {"$gt": 0}}) 9 | users = await users.to_list(length=100000) 10 | return len(users) 11 | 12 | 13 | async def is_gbanned_user(user_id: int) -> bool: 14 | user = await gbansdb.find_one({"user_id": user_id}) 15 | if not user: 16 | return False 17 | return True 18 | 19 | 20 | async def add_gban_user(user_id: int): 21 | is_gbanned = await is_gbanned_user(user_id) 22 | if is_gbanned: 23 | return 24 | return await gbansdb.insert_one({"user_id": user_id}) 25 | 26 | 27 | async def remove_gban_user(user_id: int): 28 | is_gbanned = await is_gbanned_user(user_id) 29 | if not is_gbanned: 30 | return 31 | return await gbansdb.delete_one({"user_id": user_id}) 32 | 33 | 34 | -------------------------------------------------------------------------------- /PyrogramBot/db/notesdb: -------------------------------------------------------------------------------- 1 | from PyrogramBot import db 2 | from typing import Dict, List, Union 3 | 4 | 5 | notesdb = db.notes 6 | 7 | 8 | async def get_notes_count() -> dict: 9 | chats = notesdb.find({"chat_id": {"$lt": 0}}) 10 | if not chats: 11 | return {} 12 | chats_count = 0 13 | notes_count = 0 14 | for chat in await chats.to_list(length=1000000000): 15 | notes_name = await get_note_names(chat['chat_id']) 16 | notes_count += len(notes_name) 17 | chats_count += 1 18 | return { 19 | "chats_count": chats_count, 20 | "notes_count": notes_count 21 | } 22 | 23 | 24 | async def _get_notes(chat_id: int) -> Dict[str, int]: 25 | _notes = await notesdb.find_one({"chat_id": chat_id}) 26 | if _notes: 27 | _notes = _notes["notes"] 28 | else: 29 | _notes = {} 30 | return _notes 31 | 32 | 33 | async def get_note_names(chat_id: int) -> List[str]: 34 | _notes = [] 35 | for note in await _get_notes(chat_id): 36 | _notes.append(note) 37 | return _notes 38 | 39 | 40 | async def get_note(chat_id: int, name: str) -> Union[bool, dict]: 41 | name = name.lower().strip() 42 | _notes = await _get_notes(chat_id) 43 | if name in _notes: 44 | return _notes[name] 45 | else: 46 | return False 47 | 48 | 49 | async def save_note(chat_id: int, name: str, note: dict): 50 | name = name.lower().strip() 51 | _notes = await _get_notes(chat_id) 52 | _notes[name] = note 53 | 54 | await notesdb.update_one( 55 | {"chat_id": chat_id}, 56 | { 57 | "$set": { 58 | "notes": _notes 59 | } 60 | }, 61 | upsert=True 62 | ) 63 | 64 | 65 | async def delete_note(chat_id: int, name: str) -> bool: 66 | notesd = await _get_notes(chat_id) 67 | name = name.lower().strip() 68 | if name in notesd: 69 | del notesd[name] 70 | await notesdb.update_one( 71 | {"chat_id": chat_id}, 72 | { 73 | "$set": { 74 | "notes": notesd 75 | } 76 | }, 77 | upsert=True 78 | ) 79 | return True 80 | return False -------------------------------------------------------------------------------- /PyrogramBot/db/warnsdb: -------------------------------------------------------------------------------- 1 | from PyrogramBot import db 2 | from typing import Dict, List, Union 3 | 4 | 5 | warnsdb = db.warns 6 | 7 | 8 | async def int_to_alpha(user_id: int) -> str: 9 | alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] 10 | text = "" 11 | user_id = str(user_id) 12 | for i in user_id: 13 | text += alphabet[int(i)] 14 | return text 15 | 16 | 17 | async def alpha_to_int(user_id_alphabet: str) -> int: 18 | alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] 19 | user_id = "" 20 | for i in user_id_alphabet: 21 | index = alphabet.index(i) 22 | user_id += str(index) 23 | user_id = int(user_id) 24 | return user_id 25 | 26 | 27 | async def get_warns_count() -> dict: 28 | chats = warnsdb.find({"chat_id": {"$lt": 0}}) 29 | if not chats: 30 | return {} 31 | chats_count = 0 32 | warns_count = 0 33 | for chat in await chats.to_list(length=100000000): 34 | for user in chat['warns']: 35 | warns_count += chat['warns'][user]['warns'] 36 | chats_count += 1 37 | return { 38 | "chats_count": chats_count, 39 | "warns_count": warns_count 40 | } 41 | 42 | 43 | async def get_warns(chat_id: int) -> Dict[str, int]: 44 | warns = await warnsdb.find_one({"chat_id": chat_id}) 45 | if warns: 46 | warns = warns['warns'] 47 | else: 48 | warns = {} 49 | return warns 50 | 51 | 52 | async def get_warn(chat_id: int, name: str) -> Union[bool, dict]: 53 | name = name.lower().strip() 54 | warns = await get_warns(chat_id) 55 | if name in warns: 56 | return warns[name] 57 | 58 | 59 | async def add_warn(chat_id: int, name: str, warn: dict): 60 | name = name.lower().strip() 61 | warns = await get_warns(chat_id) 62 | warns[name] = warn 63 | 64 | await warnsdb.update_one( 65 | {"chat_id": chat_id}, 66 | { 67 | "$set": { 68 | "warns": warns 69 | } 70 | }, 71 | upsert=True 72 | ) 73 | 74 | 75 | async def remove_warns(chat_id: int, name: str) -> bool: 76 | warnsd = await get_warns(chat_id) 77 | name = name.lower().strip() 78 | if name in warnsd: 79 | del warnsd[name] 80 | await warnsdb.update_one( 81 | {"chat_id": chat_id}, 82 | { 83 | "$set": { 84 | "warns": warnsd 85 | } 86 | }, 87 | upsert=True 88 | ) 89 | return True 90 | return False 91 | -------------------------------------------------------------------------------- /PyrogramBot/modules/__init__.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import importlib 3 | import sys 4 | 5 | from os.path import dirname, basename, isfile 6 | from PyrogramBot import log 7 | 8 | 9 | def __list_all_modules(): 10 | # This generates a list of modules in this 11 | # folder for the * in __main__ to work. 12 | mod_paths = glob.glob(dirname(__file__) + "/*.py") 13 | all_modules = [ 14 | basename(f)[:-3] 15 | for f in mod_paths 16 | if isfile(f) 17 | and f.endswith(".py") 18 | and not f.endswith("__init__.py") 19 | and not f.endswith("__main__.py") 20 | ] 21 | 22 | 23 | importlib.import_module("PyrogramBot.modules.__main__") 24 | ALL_MODULES = sorted(__list_all_modules()) 25 | log.info("Modules loaded: %s", str(ALL_MODULES)) 26 | __all__ = ALL_MODULES + ["ALL_MODULES"] 27 | -------------------------------------------------------------------------------- /PyrogramBot/modules/__main__.py: -------------------------------------------------------------------------------- 1 | # __main__.py exits the chat -------------------------------------------------------------------------------- /PyrogramBot/modules/eval.py: -------------------------------------------------------------------------------- 1 | import io 2 | import sys 3 | import traceback 4 | from PyrogramBot import app 5 | from PyrogramBot.config import OWNER_ID 6 | from PyrogramBot.utils.errors import capture_err 7 | from pyrogram import Client, filters 8 | 9 | __MODULE__ = "Eval" 10 | __HELP__ = "/eval [python code] - Evaluate the Given Code" 11 | 12 | 13 | @app.on_message(filters.user(OWNER_ID) & filters.command("eval")) 14 | @capture_err 15 | async def eval(client, message): 16 | status_message = await message.reply_text("Processing ...") 17 | cmd = message.text.split(" ", maxsplit=1)[1] 18 | 19 | reply_to_ = message 20 | if message.reply_to_message: 21 | reply_to_ = message.reply_to_message 22 | 23 | old_stderr = sys.stderr 24 | old_stdout = sys.stdout 25 | redirected_output = sys.stdout = io.StringIO() 26 | redirected_error = sys.stderr = io.StringIO() 27 | stdout, stderr, exc = None, None, None 28 | 29 | try: 30 | await aexec(cmd, client, message) 31 | except Exception: 32 | exc = traceback.format_exc() 33 | 34 | stdout = redirected_output.getvalue() 35 | stderr = redirected_error.getvalue() 36 | sys.stdout = old_stdout 37 | sys.stderr = old_stderr 38 | 39 | evaluation = "" 40 | if exc: 41 | evaluation = exc 42 | elif stderr: 43 | evaluation = stderr 44 | elif stdout: 45 | evaluation = stdout 46 | else: 47 | evaluation = "Success" 48 | 49 | final_output = "EVAL: " 50 | final_output += f"{cmd}\n\n" 51 | final_output += "OUTPUT:\n" 52 | final_output += f"{evaluation.strip()} \n" 53 | 54 | if len(final_output) > 4096: 55 | with io.BytesIO(str.encode(final_output)) as out_file: 56 | out_file.name = "eval.text" 57 | await reply_to_.reply_document( 58 | document=out_file, caption=cmd, disable_notification=True 59 | ) 60 | else: 61 | await reply_to_.reply_text(final_output) 62 | await status_message.delete() 63 | 64 | 65 | async def aexec(code, client, message): 66 | exec( 67 | "async def __aexec(client, message): " 68 | + "".join(f"\n {l_}" for l_ in code.split("\n")) 69 | ) 70 | return await locals()["__aexec"](client, message) 71 | -------------------------------------------------------------------------------- /PyrogramBot/modules/fun.py: -------------------------------------------------------------------------------- 1 | import pyjokes 2 | from quoters import Quote 3 | from pyrogram import filters 4 | from PyrogramBot import app 5 | 6 | __MODULE__ = "Fun" 7 | __HELP__ = """/quote - Get Quotes\n/joke - Get Jokes\n**Lastly:**\n**Press F** replying someone and check Yourself Okay!""" 8 | 9 | #Using External Modules like 'pyjokes' and 'quoters' 10 | 11 | # ShortCut for Long Codes with Repeating Functions 12 | 13 | jokes = pyjokes.get_joke # You can use this when you have to use the same thing over and over again sometimes, Like For Example, pyjokes.get_joke(language="hi", category="neutral"), pyjokes.get_joke(language="en", category="neutral"), Then Instead of writting 'pyjokes.get_joke' you use your pre-defined function 'jokes' in place of that. 14 | 15 | @app.on_message(filters.command(["joke"])) 16 | async def crackjoke(_, message): 17 | joke = jokes(language="en", category="neutral") 18 | await message.reply_text(joke) 19 | 20 | # Simple way for short codes with single use functions 21 | 22 | @app.on_message(filters.command(["quote"])) 23 | async def quoter(_, message): 24 | quote = Quote.print() 25 | await message.reply_text(quote) 26 | 27 | # Example Of Simple Regex Use From @SupMeta_Bot 28 | 29 | @app.on_message(filters.regex(["(?i)f"])) 30 | async def f(_, message): 31 | if (len(message.text) != 1): 32 | return 33 | if message.reply_to_message: 34 | await message.reply_to_message.reply_text("**Press F** To Pay Respect to This **LeJhand**", parse_mode="markdown") 35 | else: 36 | await message.reply_text("Pay Respect to Whom??, Reply to Someone's Message!") 37 | 38 | -------------------------------------------------------------------------------- /PyrogramBot/modules/ping.py: -------------------------------------------------------------------------------- 1 | from PyrogramBot import app, bot_start_time 2 | from PyrogramBot.__main__ import BOT_DC_ID 3 | from PyrogramBot.utils.errors import capture_err 4 | from psutil import cpu_percent, virtual_memory, disk_usage, boot_time 5 | 6 | from pyrogram import filters, __version__ 7 | from platform import python_version 8 | 9 | import time 10 | 11 | __MODULE__ = "Bot Status" 12 | __HELP__ = "/ping - Check Bot Ping and Uptime\n/status - Check Bot Stats" 13 | 14 | #Formatter 15 | 16 | def get_readable_time(seconds: int) -> str: 17 | count = 0 18 | ping_time = "" 19 | time_list = [] 20 | time_suffix_list = ["s", "m", "h", "days"] 21 | while count < 4: 22 | count += 1 23 | if count < 3: 24 | remainder, obtained = divmod(seconds, 60) 25 | else: 26 | remainder, obtained = divmod(seconds, 24) 27 | if seconds == 0 and remainder == 0: 28 | break 29 | time_list.append(int(obtained)) 30 | seconds = int(remainder) 31 | for i in range(len(time_list)): 32 | time_list[i] = str(time_list[i]) + time_suffix_list[i] 33 | if len(time_list) == 4: 34 | ping_time += time_list.pop() + ", " 35 | time_list.reverse() 36 | ping_time += ":".join(time_list) 37 | return ping_time 38 | 39 | #Uptime 40 | 41 | bot_uptime = int(time.time() - bot_start_time) 42 | 43 | # Ping 44 | 45 | @app.on_message(filters.command("ping")) 46 | @capture_err 47 | async def ping(_, message): 48 | start_time = time.time() 49 | pong = await message.reply_text("Calculating...") 50 | end_time = time.time() 51 | ping = round((end_time - start_time) * 1000, 3) 52 | await pong.edit_text( 53 | f"**Ping [DC-{BOT_DC_ID}]:** {ping}ms\n\n**Uptime:** {get_readable_time((bot_uptime))}.", parse_mode='markdown') 54 | 55 | # System Info Example 56 | 57 | @app.on_message(filters.command("status")) 58 | @capture_err 59 | async def status(_, message): 60 | ram = virtual_memory().percent 61 | cpu = cpu_percent().percent 62 | disk = disk_usage("/").percent 63 | text = "*>-------< System >-------<*\n\n" 64 | text += f"Uptime: `{get_readable_time((bot_uptime))}`\n" 65 | text += f"**CPU:** `{cpu}%`\n" 66 | text += f"**RAM:** `{ram}%`\n" 67 | text += f"**Disk:** `{disk}%`\n" 68 | text += f"**Python Version:** `{python_version}`\n" 69 | text += "**Library:** `Pyrogram`\n" 70 | text += f"**Pyrogram Version:** `{__version__}`" 71 | await message.reply_text(text, parse_mode="markdown") -------------------------------------------------------------------------------- /PyrogramBot/modules/start.py: -------------------------------------------------------------------------------- 1 | from PyrogramBot import app 2 | from PyrogramBot.__main__ import BOT_NAME 3 | from PyrogramBot.config import OWNER_ID 4 | 5 | from PyrogramBot.utils.errors import capture_err 6 | 7 | from pyrogram import filters 8 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 9 | 10 | __MODULE__ = "Basic" 11 | 12 | __HELP__ = """/start - Start the bot with Inline Buttons 13 | /help - Help Menu 14 | """ 15 | 16 | keyboard = InlineKeyboardMarkup( 17 | [ 18 | [ 19 | InlineKeyboardButton( 20 | text="Example Inline Link Button", 21 | url=f"https://github.com/swatv3nub/PyrogramBot", 22 | ), 23 | InlineKeyboardButton( 24 | text="Example Inline Query Button", 25 | callback_data="example", 26 | ) 27 | ] 28 | 29 | #Start 30 | 31 | @app.on_message(filters.command("start")) 32 | @capture_err 33 | async def start(_, message): 34 | start_text=f"Hello {message.from_user.mention}, I am {BOT_NAME}\n\nMy Onwer is [{OWNER_ID}](tg://user?id={OWNER_ID})" 35 | await message.reply_text(start_text, reply_markup=keyboard, parse_mode="markdown") 36 | 37 | 38 | @app.on_callback_query(filters.regex("example")) 39 | async def example(_, CallBackQuery): 40 | await app.answer_callback_query(CallbackQuery.id, "Surprise!", show_alert=True) 41 | 42 | 43 | #Help { in __main__.py } -------------------------------------------------------------------------------- /PyrogramBot/utils/errors.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | from functools import wraps 4 | from PyrogramBot import app, LOG_CHANNEL 5 | 6 | 7 | def split_limits(text): 8 | if len(text) < 2048: 9 | return [text] 10 | 11 | lines = text.splitlines(True) 12 | small_msg = '' 13 | result = [] 14 | for line in lines: 15 | if len(small_msg) + len(line) < 2048: 16 | small_msg += line 17 | else: 18 | result.append(small_msg) 19 | small_msg = line 20 | else: 21 | result.append(small_msg) 22 | 23 | return result 24 | 25 | 26 | def capture_err(func): 27 | @wraps(func) 28 | async def caperr(client, message, *args, **kwargs): 29 | try: 30 | return await func(client, message, *args, **kwargs) 31 | except Exception as err: 32 | exc_type, exc_obj, exc_tb = sys.exc_info() 33 | errors = traceback.format_exception( 34 | etype=exc_type, value=exc_obj, tb=exc_tb, 35 | ) 36 | error_feedback = split_limits( 37 | '**Logged Error**\n\n**Command User:** `{}` | **Chat:** `{}`\n\nCommand Used:```{}```\n\n**Incoming Error**```{}```\n'.format( 38 | 0 if not message.from_user else message.from_user.id, 39 | 0 if not message.chat else message.chat.id, 40 | message.text or message.caption, 41 | ''.join(errors), 42 | ), 43 | ) 44 | for x in error_feedback: 45 | await app.send_message( 46 | LOG_CHANNEL, 47 | x 48 | ) 49 | raise err 50 | return caperr 51 | -------------------------------------------------------------------------------- /PyrogramBot/utils/misc.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | from pyrogram.types import InlineKeyboardButton 3 | from PyrogramBot import LOAD_MODULES, NOLOAD_MODULES 4 | 5 | 6 | class EqInlineKeyboardButton(InlineKeyboardButton): 7 | def __eq__(self, other): 8 | return self.text == other.text 9 | 10 | def __lt__(self, other): 11 | return self.text < other.text 12 | 13 | def __gt__(self, other): 14 | return self.text > other.text 15 | 16 | 17 | def count_modules(page_n, module_dict, prefix, chat=None): 18 | if not chat: 19 | modules = sorted( 20 | [ 21 | EqInlineKeyboardButton( 22 | x.__MODULE__, 23 | callback_data="{}_module({})".format( 24 | prefix, x.__MODULE__.lower()), 25 | ) 26 | for x in module_dict.values() 27 | ] 28 | ) 29 | else: 30 | modules = sorted( 31 | [ 32 | EqInlineKeyboardButton( 33 | x.__MODULE__, 34 | callback_data="{}_module({},{})".format( 35 | prefix, chat, x.__MODULE__.lower() 36 | ), 37 | ) 38 | for x in module_dict.values() 39 | ] 40 | ) 41 | 42 | pairs = list(zip(modules[::3], modules[1::3], modules[2::3])) 43 | i = 0 44 | for m in pairs: 45 | for _ in m: 46 | i += 1 47 | if len(modules) - i == 1: 48 | pairs.append((modules[-1],)) 49 | elif len(modules) - i == 2: 50 | pairs.append( 51 | ( 52 | modules[-2], 53 | modules[-1], 54 | ) 55 | ) 56 | 57 | max_num_pages = ceil(len(pairs) / 7) 58 | modulo_page = page_n % max_num_pages 59 | 60 | # can only have a certain amount of buttons side by side 61 | if len(pairs) > 7: 62 | pairs = pairs[modulo_page * 7: 7 * (modulo_page + 1)] + [ 63 | ( 64 | EqInlineKeyboardButton( 65 | "<", 66 | callback_data="{}_prev({})".format( 67 | prefix, modulo_page 68 | ), 69 | ), 70 | EqInlineKeyboardButton( 71 | ">", 72 | callback_data="{}_next({})".format( 73 | prefix, modulo_page 74 | ), 75 | ), 76 | ) 77 | ] 78 | 79 | return pairs 80 | 81 | 82 | def is_module_loaded(name): 83 | return (not LOAD_MODULES or name in LOAD_MODULES) and name not in NOLOAD_MODULES 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyrogramBot 2 | 3 | [![forthebadge made-with-python](http://ForTheBadge.com/images/badges/made-with-python.svg)](https://www.python.org/) 4 | [![ForTheBadge built-with-love](http://ForTheBadge.com/images/badges/built-with-love.svg)](https://GitHub.com/swatv3nub/) 5 | 6 | 7 | ![Python Version](https://img.shields.io/badge/python-3.9.1-green?style=for-the-badge&logo=appveyor) 8 | ![Pyrogram Version](https://img.shields.io/badge/pyrogram-1.2.6-orange?style=for-the-badge&logo=appveyor) 9 | ![Database](https://img.shields.io/badge/database-mongo-orange?style=for-the-badge&logo=appveyor) 10 | ![Issues](https://img.shields.io/github/issues/swatv3nub/PyrogramBot?style=for-the-badge&logo=appveyor) 11 | ![Forks](https://img.shields.io/github/forks/swatv3nub/PyrogramBot?style=for-the-badge&logo=appveyor) 12 | ![Stars](https://img.shields.io/github/stars/swatv3nub/PyrogramBot?style=for-the-badge&logo=appveyor) 13 | ![LICENSE](https://img.shields.io/github/license/swatv3nub/PyrogramBot?style=for-the-badge&logo=appveyor) 14 | ![Contributors](https://img.shields.io/github/contributors/swatv3nub/PyrogramBot?style=for-the-badge&logo=appveyor) 15 | ![Repository Size](https://img.shields.io/github/repo-size/swatv3nub/PyrogramBot?style=for-the-badge&logo=appveyor) 16 | 17 | Simple Base for Users Who wants to make their own Telegram Bots in **[Pyrogram](http://github.com/Pyrogram/Pyrogram)** with or without **[Mongo Database](https://mongodb.com)** 18 | 19 | ## How to add modules? 20 | 21 | - Fork and add your modules in **[PyrogramBot/modules](https://github.com/swatv3nub/PyrogramBot/blob/Alpha/PyrogramBot/modules)** 22 | - Add Required ENV Variables in **[app.json](https://github.com/swatv3nub/PyrogramBot/blob/Alpha/app.json)** & in **[config.py](https://github.com/swatv3nub/PyrogramBot/blob/Alpha/PyrogramBot/config.py)** 23 | - Add required external requirements in **[requirements.txt](https://github.com/swatv3nub/PyrogramBot/blob/Alpha/requirements.txt)** 24 | 25 | ## Hosting 26 | 27 | ### Heroku 28 | 29 | - Heroku Deploy is Very Simple, Just Click the Button Below 30 | 31 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/swatv3nub/PyrogramBot) 32 | 33 | ### Local Deploy 34 | 35 | - `git clone https://github.com/swatv3nub/PyrogramBot` 36 | - `cd PyrogramBot` 37 | - `pip3 install -r requirements.txt` 38 | - Edit The `config.py` as per the instruction given there. 39 | - `python3 -m PyrogramBot` 40 | 41 | ## Report Bugs 42 | 43 | Report Bugs in [@PythonDevsChat](https://t.me/PythonDevsChat) 44 | 45 | Do Fork And Star The Repository If You Liked It. 46 | 47 | ## Credits 48 | - [Dan Tès](https://telegram.dog/haskell) for His [Pyrogram Library](https://github.com/Pyrogram/Pyrogram) 49 | - MaskedVirus - [Telegram](https://t.me/MaskedVirus) | [GitHub](https://github.com/swatv3nub) 50 | - TheHamkerCat - [Telegram](https://t.me/TheHamkerCat) | [GitHub](https://github.com/TheHamkerCat) 51 | 52 | 53 | ## Powered By 54 | 55 | [![Python Devs](https://img.shields.io/badge/python-devs-green?style=for-the-badge&logo=appveyor)](https://t.me/PythonDevs) 56 | 57 | [![TheCodents](https://img.shields.io/badge/The-Codents-green?style=for-the-badge&logo=appveyor)](https://t.me/TheCodents) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PyrogramBot", 3 | "description": "Bot written in Python using the Pyrogram Library - Sample", 4 | "keywords": [ 5 | "telegram", 6 | "python", 7 | "pyrogram" 8 | ], 9 | "repository": "https://github.com/swatv3nub/PyrogramBot", 10 | "success_url": "https://telegram.dog/ProjectHackfreaks", 11 | "website": "https://github.com/swatv3nub", 12 | "env": { 13 | "ENV": { 14 | "description": "Setting this to ANYTHING will enable webhooks when in env mode", 15 | "value": "ANYTHING" 16 | }, 17 | "API_ID": { 18 | "description": "Get this value from https://my.telegram.org", 19 | "required": true 20 | }, 21 | "API_HASH": { 22 | "description": "Get this value from https://my.telegram.org", 23 | "required": true 24 | }, 25 | "BOT_TOKEN": { 26 | "description": "Get this value from @BotFather" 27 | "required": true 28 | }, 29 | "OWNER_ID": { 30 | "description": "Your ID", 31 | "required": true 32 | } 33 | }, 34 | "buildpacks": [ 35 | { 36 | "url": "heroku/python" 37 | }], 38 | "formation": { 39 | "worker": { 40 | "quantity": 1, 41 | "size": "free" 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /heroku.yml.sample: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | TgCrypto 2 | aiohttp 3 | pyrogram 4 | python-dateutil 5 | pyjokes 6 | quoters 7 | psutil 8 | pykeyboard 9 | #motor 10 | dnspython 11 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.9.1 2 | --------------------------------------------------------------------------------