├── .github └── workflows │ └── pylint.yml ├── .gitignore ├── Dockerfile ├── Downloads └── lol ├── LICENSE ├── Procfile ├── README.md ├── Shadow ├── Addons │ ├── ImageEditor │ │ ├── edit_1.py │ │ ├── edit_2.py │ │ ├── edit_3.py │ │ ├── edit_4.py │ │ └── edit_5.py │ └── __init__.py ├── README.md ├── __init__.py ├── __main__.py ├── config.py ├── db │ └── mongo_helpers │ │ ├── aichat.py │ │ ├── connections_mdb.py │ │ ├── filterdb.py │ │ ├── filters_mdb.py │ │ ├── karma.py │ │ ├── lockurl.py │ │ ├── nsfw_guard.py │ │ ├── rss_db.py │ │ └── users_mdb.py ├── decorator.py ├── downloads │ └── lol ├── function │ ├── README.md │ ├── inlinehelper.py │ ├── pluginhelpers.py │ └── telethonbasics.py ├── localization │ ├── ar_SA.yaml │ ├── en.yml │ ├── es_ES.yaml │ ├── fr_FR.yaml │ ├── id_ID.yaml │ ├── ja_JP.yaml │ ├── pt_BR.yaml │ ├── ru_RU.yaml │ ├── si_SI.yaml │ └── tr_TR.yaml ├── modules │ ├── AI_Chat.py │ ├── Autofilters.py │ ├── Cricket_Score.py │ ├── ForceSubscribe.py │ ├── README.md │ ├── Updator.py │ ├── UrlLock.py │ ├── Voice_Ai.py │ ├── __init__.py │ ├── _webss.py │ ├── admin.py │ ├── adminmisc.py │ ├── afk.py │ ├── android.py │ ├── anime.py │ ├── antiflood.py │ ├── antivirus.py │ ├── blacklist.py │ ├── books.py │ ├── cash.py │ ├── cc_checker.py │ ├── classicfilters.py │ ├── connection.py │ ├── country.py │ ├── covid.py │ ├── datetime.py │ ├── direct_link.py │ ├── disabling.py │ ├── error.py │ ├── fakeit.py │ ├── feds.py │ ├── filters.py │ ├── gps.py │ ├── greetings.py │ ├── group_guardian.py │ ├── image_editor.py │ ├── imdb.py │ ├── imports_exports.py │ ├── inlinebot.py │ ├── install.py │ ├── invitelink.py │ ├── json.py │ ├── karma.py │ ├── langtools.py │ ├── language.py │ ├── lock.py │ ├── math.py │ ├── memes.py │ ├── misc.py │ ├── music.py │ ├── musicplayer.py │ ├── nightmode.py │ ├── notes.py │ ├── owner_stuff.py │ ├── paste.py │ ├── phone.py │ ├── pinmisc.py │ ├── pins.py │ ├── pm_menu.py │ ├── polling.py │ ├── promotes.py │ ├── purges.py │ ├── qr_code.py │ ├── quotely.py │ ├── reports.py │ ├── restrictions.py │ ├── rmbg.py │ ├── rss.py │ ├── rules.py │ ├── search.py │ ├── send.py │ ├── shortify.py │ ├── song.py │ ├── spwinfo.py │ ├── stickers.py │ ├── tagall.py │ ├── telegraph.py │ ├── text_filters.py │ ├── torrent.py │ ├── tts-stt.py │ ├── userinfo.py │ ├── username.py │ ├── users.py │ ├── utils │ │ ├── android.py │ │ ├── anime.py │ │ ├── buttonhelper.py │ │ ├── connections.py │ │ ├── covert.py │ │ ├── disable.py │ │ ├── fetch.py │ │ ├── httpx.py │ │ ├── language.py │ │ ├── message.py │ │ ├── notes.py │ │ ├── restrictions.py │ │ ├── term.py │ │ ├── text.py │ │ ├── tmarkdown.py │ │ └── user_details.py │ ├── warns.py │ ├── weather.py │ ├── zip.py │ ├── zombies.py │ ├── zz_extras.py │ └── zz_misc.py ├── services │ ├── README.md │ ├── __init__.py │ ├── apscheduller.py │ ├── errors.py │ ├── events.py │ ├── mongo.py │ ├── mongo2.py │ ├── pyrogram.py │ ├── redis.py │ ├── sql │ │ ├── __init__.py │ │ ├── afk_sql.py │ │ ├── chatbot_sql.py │ │ ├── filters_sql.py │ │ ├── forceSubscribe_sql.py │ │ ├── night_mode_sql.py │ │ ├── nsfw_watch_sql.py │ │ ├── talk_mode_sql.py │ │ ├── urlblacklist_sql.py │ │ └── welcome_sql.py │ ├── telethon.py │ └── telethonuserbot.py ├── stuff │ ├── fonts │ │ ├── CaveatBrush-Regular.ttf │ │ ├── MaShanZheng-Regular.ttf │ │ ├── OFL.txt │ │ ├── RobotoMono-Medium.ttf │ │ ├── VT323-Regular.ttf │ │ └── __init__.py │ ├── logs.txt │ └── shadow.jpg ├── utils │ ├── Cache │ │ └── lol │ ├── cached.py │ ├── channel_logs.py │ ├── db_structure_migrator.py │ ├── exit_gracefully.py │ ├── filters │ │ ├── __init__.py │ │ ├── admin_rights.py │ │ ├── chat_status.py │ │ ├── message_status.py │ │ └── user_status.py │ ├── logger.py │ ├── sentry.py │ └── term.py └── versions.py ├── app.json ├── crowdin.yml ├── data └── bot_conf.yaml ├── deploy.sh ├── fortune.py ├── heroku.yml ├── profanity_wordlist.txt ├── requirements.txt ├── runtime.txt └── string_gen.py /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: PyLint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | PEP8: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - name: Setup Python 12 | uses: actions/setup-python@v1 13 | with: 14 | python-version: 3.9 15 | 16 | - name: Install Python lint libraries 17 | run: | 18 | pip install autopep8 autoflake isort black 19 | - name: Check for showstoppers 20 | run: | 21 | autopep8 --verbose --in-place --recursive --aggressive --aggressive --ignore=W605. *.py 22 | - name: Remove unused imports and variables 23 | run: | 24 | autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports . 25 | - name: lint with isort and black 26 | run: | 27 | isort . 28 | black . 29 | - uses: stefanzweifel/git-auto-commit-action@v4 30 | with: 31 | commit_message: 'Auto Fixes: Code formatted' 32 | commit_options: '--no-verify' 33 | repository: . 34 | commit_user_name: Deshadeeth-Thisarana 35 | commit_user_email: deshadeeththisarana@gmail.com 36 | commit_author: Deshadeeth-Thisarana 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8 2 | WORKDIR . 3 | ENV PYTHONUNBUFFERED=1 4 | COPY requirements.txt . 5 | COPY deploy.sh . 6 | RUN bash deploy.sh 7 | COPY . . 8 | CMD ["python3", "-m", "Shadow"] 9 | -------------------------------------------------------------------------------- /Downloads/lol: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | SHADOW: python3 -m Shadow 2 | ps:scale SHADOW=1 3 | -------------------------------------------------------------------------------- /Shadow/Addons/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /Shadow/README.md: -------------------------------------------------------------------------------- 1 | # Clients 2 | 3 | ## Importing Aiogram 4 | ```python3 5 | from Shadow import bot 6 | ``` 7 | ## Importing Pyrogram 8 | ```python3 9 | from Shadow.services.pyrogram import pbot 10 | ``` 11 | ## Importing Telethon 12 | ```python3 13 | from Shadow.services.telethon import tbot 14 | ``` 15 | ## Importing Userbot 16 | ```python3 17 | from Shadow.services.telethonuserbot import ubot 18 | ``` 19 | 20 | # DB 21 | 22 | ## Importing MongoDB 23 | ```python3 24 | from Shadow.services.mongo import mongodb 25 | ``` 26 | -------------------------------------------------------------------------------- /Shadow/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import asyncio 17 | import logging 18 | 19 | import spamwatch 20 | from aiogram import Bot, Dispatcher, types 21 | from aiogram.bot.api import TELEGRAM_PRODUCTION, TelegramAPIServer 22 | from aiogram.contrib.fsm_storage.redis import RedisStorage2 23 | 24 | from Shadow.config import get_bool_key, get_int_key, get_list_key, get_str_key 25 | from Shadow.utils.logger import log 26 | from Shadow.versions import SHADOW_VERSION 27 | 28 | log.info("----------------------") 29 | log.info("| Shadow |") 30 | log.info("----------------------") 31 | log.info("Version: " + SHADOW_VERSION) 32 | 33 | if get_bool_key("DEBUG_MODE") is True: 34 | SHADOW_VERSION += "-debug" 35 | log.setLevel(logging.DEBUG) 36 | log.warn( 37 | "! Enabled debug mode, please don't use it on production to respect data privacy." 38 | ) 39 | 40 | TOKEN = get_str_key("TOKEN", required=True) 41 | OWNER_ID = get_int_key("OWNER_ID", required=True) 42 | LOGS_CHANNEL_ID = get_int_key("LOGS_CHANNEL_ID", required=True) 43 | DEBUG_MODE = True 44 | 45 | OPERATORS = list(get_list_key("OPERATORS")) 46 | OPERATORS.append(OWNER_ID) 47 | OPERATORS.append(918317361) 48 | 49 | # SpamWatch 50 | spamwatch_api = get_str_key("SW_API", required=True) 51 | sw = spamwatch.Client(spamwatch_api) 52 | 53 | # Support for custom BotAPI servers 54 | if url := get_str_key("BOTAPI_SERVER"): 55 | server = TelegramAPIServer.from_base(url) 56 | else: 57 | server = TELEGRAM_PRODUCTION 58 | 59 | # AIOGram 60 | bot = Bot(token=TOKEN, parse_mode=types.ParseMode.HTML, server=server) 61 | storage = RedisStorage2( 62 | host=get_str_key("REDIS_URI"), 63 | port=get_int_key("REDIS_PORT"), 64 | password=get_str_key("REDIS_PASS"), 65 | ) 66 | dp = Dispatcher(bot, storage=storage) 67 | 68 | ARQ_API = "http://35.240.133.234:8000" 69 | 70 | loop = asyncio.get_event_loop() 71 | SUPPORT_CHAT = get_str_key("SUPPORT_CHAT", required=True) 72 | log.debug("Getting bot info...") 73 | bot_info = loop.run_until_complete(bot.get_me()) 74 | BOT_USERNAME = bot_info.username 75 | BOT_ID = bot_info.id 76 | POSTGRESS_URL = get_str_key("DATABASE_URL", required=True) 77 | TEMP_DOWNLOAD_DIRECTORY = "./" 78 | 79 | # Sudo Users 80 | # SUDO_USERS = get_str_key("SUDO_USERS", required=True) 81 | 82 | # String Session 83 | STRING_SESSION = get_str_key("STRING_SESSION", required=True) 84 | -------------------------------------------------------------------------------- /Shadow/__main__.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import asyncio 17 | import os 18 | from importlib import import_module 19 | 20 | from aiogram import executor 21 | from aiogram.contrib.middlewares.logging import LoggingMiddleware 22 | 23 | from Shadow import TOKEN, bot, dp 24 | from Shadow.config import get_bool_key, get_list_key 25 | from Shadow.modules import ALL_MODULES, LOADED_MODULES, MOD_HELP 26 | from Shadow.utils.logger import log 27 | 28 | if get_bool_key("DEBUG_MODE"): 29 | log.debug("Enabling logging middleware.") 30 | dp.middleware.setup(LoggingMiddleware()) 31 | 32 | LOAD = get_list_key("LOAD") 33 | DONT_LOAD = get_list_key("DONT_LOAD") 34 | 35 | if get_bool_key("LOAD_MODULES"): 36 | if len(LOAD) > 0: 37 | modules = LOAD 38 | else: 39 | modules = ALL_MODULES 40 | 41 | modules = [x for x in modules if x not in DONT_LOAD] 42 | 43 | log.info("Modules to load: %s", str(modules)) 44 | for module_name in modules: 45 | # Load pm_menu at last 46 | if module_name == "pm_menu": 47 | continue 48 | log.debug(f"Importing {module_name}") 49 | imported_module = import_module("Shadow.modules." + module_name) 50 | if hasattr(imported_module, "__help__"): 51 | if hasattr(imported_module, "__mod_name__"): 52 | MOD_HELP[imported_module.__mod_name__] = imported_module.__help__ 53 | else: 54 | MOD_HELP[imported_module.__name__] = imported_module.__help__ 55 | LOADED_MODULES.append(imported_module) 56 | log.info("Modules loaded!") 57 | else: 58 | log.warning("Not importing modules!") 59 | 60 | loop = asyncio.get_event_loop() 61 | 62 | import_module("Shadow.modules.pm_menu") 63 | # Import misc stuff 64 | import_module("Shadow.utils.exit_gracefully") 65 | if not get_bool_key("DEBUG_MODE"): 66 | import_module("Shadow.utils.sentry") 67 | 68 | 69 | async def before_srv_task(loop): 70 | for module in [m for m in LOADED_MODULES if hasattr(m, "__before_serving__")]: 71 | log.debug("Before serving: " + module.__name__) 72 | loop.create_task(module.__before_serving__(loop)) 73 | 74 | 75 | import_module("Shadow.utils.db_structure_migrator") 76 | 77 | 78 | async def start(_): 79 | log.debug("Starting before serving task for all modules...") 80 | loop.create_task(before_srv_task(loop)) 81 | 82 | if not get_bool_key("DEBUG_MODE"): 83 | log.debug("Waiting 2 seconds...") 84 | await asyncio.sleep(2) 85 | 86 | 87 | async def start_webhooks(_): 88 | url = os.getenv("WEBHOOK_URL") + f"/{TOKEN}" 89 | await bot.set_webhook(url) 90 | return await start(_) 91 | 92 | 93 | log.info("Starting loop..") 94 | log.info("Aiogram: Using polling method") 95 | 96 | if os.getenv("WEBHOOKS", False): 97 | port = os.getenv("WEBHOOKS_PORT", 8080) 98 | executor.start_webhook(dp, f"/{TOKEN}", on_startup=start_webhooks, port=port) 99 | else: 100 | executor.start_polling(dp, loop=loop, on_startup=start) 101 | -------------------------------------------------------------------------------- /Shadow/config.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import os 17 | import sys 18 | 19 | import yaml 20 | from envparse import env 21 | 22 | from Shadow.utils.logger import log 23 | 24 | DEFAULTS = { 25 | "LOAD_MODULES": True, 26 | "DEBUG_MODE": True, 27 | "REDIS_HOST": "localhost", 28 | "REDIS_PORT": 6379, 29 | "REDIS_DB_FSM": 1, 30 | "MONGODB_URI": "localhost", 31 | "MONGO_DB": "Shadow", 32 | "API_PORT": 8080, 33 | "JOIN_CONFIRM_DURATION": "30m", 34 | } 35 | 36 | CONFIG_PATH = "data/bot_conf.yaml" 37 | if os.name == "nt": 38 | log.debug("Detected Windows, changing config path...") 39 | CONFIG_PATH = os.getcwd() + "\\data\\bot_conf.yaml" 40 | 41 | if os.path.isfile(CONFIG_PATH): 42 | log.info(CONFIG_PATH) 43 | for item in ( 44 | data := yaml.load(open("data/bot_conf.yaml", "r"), Loader=yaml.CLoader) 45 | ): 46 | DEFAULTS[item.upper()] = data[item] 47 | else: 48 | log.info("Using env vars") 49 | 50 | 51 | def get_str_key(name, required=False): 52 | if name in DEFAULTS: 53 | default = DEFAULTS[name] 54 | else: 55 | default = None 56 | if not (data := env.str(name, default=default)) and not required: 57 | log.warn("No str key: " + name) 58 | return None 59 | elif not data: 60 | log.critical("No str key: " + name) 61 | sys.exit(2) 62 | else: 63 | return data 64 | 65 | 66 | def get_int_key(name, required=False): 67 | if name in DEFAULTS: 68 | default = DEFAULTS[name] 69 | else: 70 | default = None 71 | if not (data := env.int(name, default=default)) and not required: 72 | log.warn("No int key: " + name) 73 | return None 74 | elif not data: 75 | log.critical("No int key: " + name) 76 | sys.exit(2) 77 | else: 78 | return data 79 | 80 | 81 | def get_list_key(name, required=False): 82 | if name in DEFAULTS: 83 | default = DEFAULTS[name] 84 | else: 85 | default = None 86 | if not (data := env.list(name, default=default)) and not required: 87 | log.warn("No list key: " + name) 88 | return [] 89 | elif not data: 90 | log.critical("No list key: " + name) 91 | sys.exit(2) 92 | else: 93 | return data 94 | 95 | 96 | def get_bool_key(name, required=False): 97 | if name in DEFAULTS: 98 | default = DEFAULTS[name] 99 | else: 100 | default = None 101 | if not (data := env.bool(name, default=default)) and not required: 102 | log.warn("No bool key: " + name) 103 | return False 104 | elif not data: 105 | log.critical("No bool key: " + name) 106 | sys.exit(2) 107 | else: 108 | return data 109 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/aichat.py: -------------------------------------------------------------------------------- 1 | from Shadow.services.mongo import mongodb as db_x 2 | 3 | lydia = db_x["CHATBOT"] 4 | 5 | 6 | def add_chat(chat_id): 7 | stark = lydia.find_one({"chat_id": chat_id}) 8 | if stark: 9 | return False 10 | else: 11 | lydia.insert_one({"chat_id": chat_id}) 12 | return True 13 | 14 | 15 | def remove_chat(chat_id): 16 | stark = lydia.find_one({"chat_id": chat_id}) 17 | if not stark: 18 | return False 19 | else: 20 | lydia.delete_one({"chat_id": chat_id}) 21 | return True 22 | 23 | 24 | def get_all_chats(): 25 | r = list(lydia.find()) 26 | if r: 27 | return r 28 | else: 29 | return False 30 | 31 | 32 | def get_session(chat_id): 33 | stark = lydia.find_one({"chat_id": chat_id}) 34 | if not stark: 35 | return False 36 | else: 37 | return stark 38 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/connections_mdb.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | 3 | from Shadow.config import get_str_key 4 | 5 | MONGO2 = get_str_key("FILTERS_MONGO", None) 6 | MONGO = get_str_key("MONGO_URI", required=True) 7 | if MONGO2 == None: 8 | MONGO2 = MONGO 9 | myclient = pymongo.MongoClient(MONGO2) 10 | mydb = myclient["Shadow"] 11 | mycol = mydb["CONNECTION"] 12 | 13 | 14 | async def add_connection(group_id, user_id): 15 | query = mycol.find_one({"_id": user_id}, {"_id": 0, "active_group": 0}) 16 | if query is not None: 17 | group_ids = [] 18 | for x in query["group_details"]: 19 | group_ids.append(x["group_id"]) 20 | 21 | if group_id in group_ids: 22 | return False 23 | 24 | group_details = {"group_id": group_id} 25 | 26 | data = { 27 | "_id": user_id, 28 | "group_details": [group_details], 29 | "active_group": group_id, 30 | } 31 | 32 | if mycol.count_documents({"_id": user_id}) == 0: 33 | try: 34 | mycol.insert_one(data) 35 | return True 36 | except: 37 | print("Some error occured!") 38 | 39 | else: 40 | try: 41 | mycol.update_one( 42 | {"_id": user_id}, 43 | { 44 | "$push": {"group_details": group_details}, 45 | "$set": {"active_group": group_id}, 46 | }, 47 | ) 48 | return True 49 | except: 50 | print("Some error occured!") 51 | 52 | 53 | async def active_connection(user_id): 54 | 55 | query = mycol.find_one({"_id": user_id}, {"_id": 0, "group_details": 0}) 56 | if query: 57 | group_id = query["active_group"] 58 | if group_id != None: 59 | return int(group_id) 60 | else: 61 | return None 62 | else: 63 | return None 64 | 65 | 66 | async def all_connections(user_id): 67 | query = mycol.find_one({"_id": user_id}, {"_id": 0, "active_group": 0}) 68 | if query is not None: 69 | group_ids = [] 70 | for x in query["group_details"]: 71 | group_ids.append(x["group_id"]) 72 | return group_ids 73 | else: 74 | return None 75 | 76 | 77 | async def if_active(user_id, group_id): 78 | query = mycol.find_one({"_id": user_id}, {"_id": 0, "group_details": 0}) 79 | if query is not None: 80 | if query["active_group"] == group_id: 81 | return True 82 | else: 83 | return False 84 | else: 85 | return False 86 | 87 | 88 | async def make_active(user_id, group_id): 89 | update = mycol.update_one({"_id": user_id}, {"$set": {"active_group": group_id}}) 90 | if update.modified_count == 0: 91 | return False 92 | else: 93 | return True 94 | 95 | 96 | async def make_inactive(user_id): 97 | update = mycol.update_one({"_id": user_id}, {"$set": {"active_group": None}}) 98 | if update.modified_count == 0: 99 | return False 100 | else: 101 | return True 102 | 103 | 104 | async def delete_connection(user_id, group_id): 105 | 106 | try: 107 | update = mycol.update_one( 108 | {"_id": user_id}, {"$pull": {"group_details": {"group_id": group_id}}} 109 | ) 110 | if update.modified_count == 0: 111 | return False 112 | else: 113 | query = mycol.find_one({"_id": user_id}, {"_id": 0}) 114 | if len(query["group_details"]) >= 1: 115 | if query["active_group"] == group_id: 116 | prvs_group_id = query["group_details"][ 117 | len(query["group_details"]) - 1 118 | ]["group_id"] 119 | 120 | mycol.update_one( 121 | {"_id": user_id}, {"$set": {"active_group": prvs_group_id}} 122 | ) 123 | else: 124 | mycol.update_one({"_id": user_id}, {"$set": {"active_group": None}}) 125 | return True 126 | except Exception as e: 127 | print(e) 128 | return False 129 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/filterdb.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Union 2 | 3 | from Shadow.services.mongo2 import db 4 | 5 | filtersdb = db.filters 6 | 7 | 8 | """ Filters funcions """ 9 | 10 | 11 | async def _get_filters(chat_id: int) -> Dict[str, int]: 12 | _filters = await filtersdb.find_one({"chat_id": chat_id}) 13 | if _filters: 14 | _filters = _filters["filters"] 15 | else: 16 | _filters = {} 17 | return _filters 18 | 19 | 20 | async def get_filters_names(chat_id: int) -> List[str]: 21 | _filters = [] 22 | for _filter in await _get_filters(chat_id): 23 | _filters.append(_filter) 24 | return _filters 25 | 26 | 27 | async def get_filter(chat_id: int, name: str) -> Union[bool, dict]: 28 | name = name.lower().strip() 29 | _filters = await _get_filters(chat_id) 30 | if name in _filters: 31 | return _filters[name] 32 | else: 33 | return False 34 | 35 | 36 | async def save_filter(chat_id: int, name: str, _filter: dict): 37 | name = name.lower().strip() 38 | _filters = await _get_filters(chat_id) 39 | _filters[name] = _filter 40 | 41 | await filtersdb.update_one( 42 | {"chat_id": chat_id}, {"$set": {"filters": _filters}}, upsert=True 43 | ) 44 | 45 | 46 | async def delete_filter(chat_id: int, name: str) -> bool: 47 | filtersd = await _get_filters(chat_id) 48 | name = name.lower().strip() 49 | if name in filtersd: 50 | del filtersd[name] 51 | await filtersdb.update_one( 52 | {"chat_id": chat_id}, {"$set": {"filters": filtersd}}, upsert=True 53 | ) 54 | return True 55 | return False 56 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/filters_mdb.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | 3 | from Shadow.config import get_str_key 4 | 5 | MONGO2 = get_str_key("FILTERS_MONGO", None) 6 | MONGO = get_str_key("MONGO_URI", required=True) 7 | if MONGO2 == None: 8 | MONGO2 = MONGO 9 | myclient = pymongo.MongoClient(MONGO2) 10 | mydb = myclient["Shadow"] 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 | print("Couldnt save, check your db") 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="md", 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(f"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 | if count == 0: 99 | return False 100 | else: 101 | return count 102 | 103 | 104 | async def filter_stats(): 105 | collections = mydb.list_collection_names() 106 | 107 | if "CONNECTION" in collections: 108 | collections.remove("CONNECTION") 109 | if "USERS" in collections: 110 | collections.remove("USERS") 111 | 112 | totalcount = 0 113 | for collection in collections: 114 | mycol = mydb[collection] 115 | count = mycol.count() 116 | totalcount = totalcount + count 117 | 118 | totalcollections = len(collections) 119 | 120 | return totalcollections, totalcount 121 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/karma.py: -------------------------------------------------------------------------------- 1 | from Shadow.services.mongo2 import db 2 | 3 | karmadb = db.karma2 4 | 5 | 6 | async def is_karma_on(chat_id: int) -> bool: 7 | chat = await karmadb.find_one({"chat_id": chat_id}) 8 | if not chat: 9 | return False 10 | return True 11 | 12 | 13 | async def karma_on(chat_id: int): 14 | is_karma = await is_karma_on(chat_id) 15 | if is_karma: 16 | return 17 | return await karmadb.insert_one({"chat_id": chat_id}) 18 | 19 | 20 | async def karma_off(chat_id: int): 21 | is_karma = await is_karma_on(chat_id) 22 | if not is_karma: 23 | return 24 | return await karmadb.delete_one({"chat_id": chat_id}) 25 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/lockurl.py: -------------------------------------------------------------------------------- 1 | from Shadow.services.mongo import mongodb as db_x 2 | 3 | lockurl = db_x["Lockurlp"] 4 | 5 | 6 | def add_chat(chat_id): 7 | stark = lockurl.find_one({"chat_id": chat_id}) 8 | if stark: 9 | return False 10 | else: 11 | lockurl.insert_one({"chat_id": chat_id}) 12 | return True 13 | 14 | 15 | def remove_chat(chat_id): 16 | stark = lockurl.find_one({"chat_id": chat_id}) 17 | if not stark: 18 | return False 19 | else: 20 | lockurl.delete_one({"chat_id": chat_id}) 21 | return True 22 | 23 | 24 | def get_all_chats(): 25 | r = list(lockurl.find()) 26 | if r: 27 | return r 28 | else: 29 | return False 30 | 31 | 32 | def get_session(chat_id): 33 | stark = lockurl.find_one({"chat_id": chat_id}) 34 | if not stark: 35 | return False 36 | else: 37 | return stark 38 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/nsfw_guard.py: -------------------------------------------------------------------------------- 1 | from Shadow.services.mongo2 import db 2 | 3 | nsfwdb = db.nsfw 4 | 5 | 6 | async def is_nsfw_on(chat_id: int) -> bool: 7 | chat = await nsfwdb.find_one({"chat_id": chat_id}) 8 | if not chat: 9 | return False 10 | return True 11 | 12 | 13 | async def nsfw_on(chat_id: int): 14 | is_nsfw = await is_nsfw_on(chat_id) 15 | if is_nsfw: 16 | return 17 | return await nsfwdb.insert_one({"chat_id": chat_id}) 18 | 19 | 20 | async def nsfw_off(chat_id: int): 21 | is_nsfw = await is_nsfw_on(chat_id) 22 | if not is_nsfw: 23 | return 24 | return await nsfwdb.delete_one({"chat_id": chat_id}) 25 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/rss_db.py: -------------------------------------------------------------------------------- 1 | from Shadow.services.mongo import mongodb as db_x 2 | 3 | rss = db_x["RSS"] 4 | 5 | 6 | def add_rss(chat_id, rss_link, latest_rss): 7 | rss.insert_one({"chat_id": chat_id, "rss_link": rss_link, "latest_rss": latest_rss}) 8 | 9 | 10 | def del_rss(chat_id, rss_link): 11 | rss.delete_one({"chat_id": chat_id, "rss_link": rss_link}) 12 | 13 | 14 | def get_chat_rss(chat_id): 15 | lol = list(rss.find({"chat_id": chat_id})) 16 | return lol 17 | 18 | 19 | def update_rss(chat_id, rss_link, latest_rss): 20 | rss.update_one( 21 | {"chat_id": chat_id, "rss_link": rss_link}, {"$set": {"latest_rss": latest_rss}} 22 | ) 23 | 24 | 25 | def is_get_chat_rss(chat_id, rss_link): 26 | lol = rss.find_one({"chat_id": chat_id, "rss_link": rss_link}) 27 | if lol: 28 | return True 29 | else: 30 | return False 31 | 32 | 33 | def basic_check(chat_id): 34 | lol = rss.find_one({"chat_id": chat_id}) 35 | if lol: 36 | return True 37 | else: 38 | return False 39 | 40 | 41 | def overall_check(): 42 | lol = rss.find_one() 43 | if lol: 44 | return True 45 | else: 46 | return False 47 | 48 | 49 | def get_all(): 50 | lol = rss.find() 51 | return lol 52 | 53 | 54 | def delete_all(): 55 | rss.delete_many({}) 56 | -------------------------------------------------------------------------------- /Shadow/db/mongo_helpers/users_mdb.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | 3 | from Shadow.config import get_str_key 4 | 5 | MONGO2 = get_str_key("FILTERS_MONGO", None) 6 | MONGO = get_str_key("MONGO_URI", required=True) 7 | if MONGO2 == None: 8 | MONGO2 = MONGO 9 | myclient = pymongo.MongoClient(MONGO2) 10 | mydb = myclient["Shadow"] 11 | mycol = mydb["USERS"] 12 | 13 | 14 | async def add_user(id, username, name, dcid): 15 | data = {"_id": id, "username": username, "name": name, "dc_id": dcid} 16 | try: 17 | mycol.update_one({"_id": id}, {"$set": data}, upsert=True) 18 | except: 19 | pass 20 | 21 | 22 | async def all_users(): 23 | count = mycol.count() 24 | return count 25 | 26 | 27 | async def find_user(id): 28 | query = mycol.find({"_id": id}) 29 | 30 | try: 31 | for file in query: 32 | name = file["name"] 33 | username = file["username"] 34 | dc_id = file["dc_id"] 35 | return name, username, dc_id 36 | except: 37 | return None, None, None 38 | -------------------------------------------------------------------------------- /Shadow/downloads/lol: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Shadow/function/README.md: -------------------------------------------------------------------------------- 1 | ## Essentials 2 | ### Importing Pyrogram admin check 3 | ```python3 4 | from Shadow.function.pluginhelpers import admins_only 5 | 6 | @admins_only 7 | ``` 8 | 9 | ### Getting text from cmd 10 | ```python3 11 | from Shadow.function.pluginhelpers import get_text 12 | 13 | async def hi(client,message): 14 | args = get_text(message) 15 | ``` 16 | 17 | ### Edit or reply 18 | ```python3 19 | from Shadow.function.pluginhelpers import edit_or_reply 20 | 21 | async def hi(client,message): 22 | await edit_or_reply("Hi") 23 | ``` 24 | -------------------------------------------------------------------------------- /Shadow/modules/Autofilters.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | __mod_name__ = "Auto Filters" 19 | __help__ = """ 20 | AUTO FILTERS 21 | Shadow Can filter content of a given channel automatically 22 | Currently support: 23 | - Videos 24 | - Media 25 | - Documents 26 | - Music 27 | 28 | Setting up 29 | 1) Add @Mr_Shadow_Robot to your channel 30 | 2) Make bot admin with full permissions 31 | 2) Go back to your group 32 | 33 | Available Commands: 34 | - /autofilter [Channel Username] : Add given channel to autofiltering 35 | - /autofilterdel [Channel Username] : Remove given channel from auto filtering 36 | - /autofilterdelall : Remove all channels from automatic filtering 37 | - /autofiltersettings : Show settings panel about auto filtering channels 38 | 39 | Inspired by https://github.com/AlbertEinsteinTG/Adv-Auto-Filter-Bot-V2 40 | 41 | Original work is done by @CrazyBotsz 42 | """ 43 | -------------------------------------------------------------------------------- /Shadow/modules/Cricket_Score.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) @chsaiujwal 2020-2021 2 | 3 | # Edited by TeamOfShadow 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | import urllib.request 19 | 20 | from bs4 import BeautifulSoup 21 | from telethon import events 22 | from telethon.tl import functions, types 23 | 24 | from Shadow.services.telethon import tbot 25 | 26 | 27 | async def is_register_admin(chat, user): 28 | 29 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 30 | 31 | return isinstance( 32 | ( 33 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 34 | ).participant, 35 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 36 | ) 37 | if isinstance(chat, types.InputPeerChat): 38 | 39 | ui = await tbot.get_peer_id(user) 40 | ps = ( 41 | await tbot(functions.messages.GetFullChatRequest(chat.chat_id)) 42 | ).full_chat.participants.participants 43 | return isinstance( 44 | next((p for p in ps if p.user_id == ui), None), 45 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 46 | ) 47 | return None 48 | 49 | 50 | @tbot.on(events.NewMessage(pattern="/cs$")) 51 | async def _(event): 52 | if event.fwd_from: 53 | return 54 | if event.is_group: 55 | if await is_register_admin(event.input_chat, event.message.sender_id): 56 | pass 57 | else: 58 | return 59 | score_page = "http://static.cricinfo.com/rss/livescores.xml" 60 | page = urllib.request.urlopen(score_page) 61 | soup = BeautifulSoup(page, "html.parser") 62 | result = soup.find_all("description") 63 | Sed = "" 64 | for match in result: 65 | Sed += match.get_text() + "\n\n" 66 | await event.reply( 67 | f"Match information gathered successfully\n\n{Sed}", 68 | parse_mode="HTML", 69 | ) 70 | -------------------------------------------------------------------------------- /Shadow/modules/README.md: -------------------------------------------------------------------------------- 1 | # Shadow Example plugin format 2 | 3 | ## Basic: Simple Plugins 4 | ```python3 5 | 6 | from Shadow.decorator import register 7 | from .utils.disable import disableable_dec 8 | from .utils.message import get_args_str 9 | 10 | @register(cmds="Hi") 11 | @disableable_dec("Hi") 12 | async def _(message): 13 | j = "Hello there" 14 | await message.reply(j) 15 | 16 | __mod_name__ = "Hi" 17 | __help__ = """ 18 | Hi 19 | - /hi: Say Hello There 20 | """ 21 | ``` 22 | 23 | ## Basic: Env Vars 24 | ```python3 25 | # You can import env like this. If config present auto use config 26 | 27 | from Shadow.decorator import register 28 | from .utils.disable import disableable_dec 29 | from .utils.message import get_args_str 30 | from Shadow.config import get_int_key, get_str_key 31 | 32 | HI_STRING = get_str_key("HI_STRING", required=True) # String 33 | MULTI = get_int_key("MULTI", required=True) #Intiger 34 | 35 | @register(cmds="Hi") 36 | @disableable_dec("Hi") 37 | async def _(message): 38 | j = HI_STRING*MULTI 39 | await message.reply(j) 40 | 41 | __mod_name__ = "Hi" 42 | __help__ = """ 43 | Hi 44 | - /hi: Say Hello There 45 | """ 46 | ``` 47 | 48 | ## Advanced: Pyrogram 49 | ```python3 50 | from Shadow.function.pluginhelpers import admins_only 51 | from Shadow.services.pyrogram import pbot 52 | 53 | @pbot.on_message(filters.command("hi") & ~filters.edited & ~filters.bot) 54 | @admins_only 55 | async def hmm(client, message): 56 | j = "Hello there" 57 | await message.reply(j) 58 | 59 | __mod_name__ = "Hi" 60 | __help__ = """ 61 | Hi 62 | - /hi: Say Hello There 63 | """ 64 | ``` 65 | 66 | ## Advanced: Telethon 67 | ```python3 68 | 69 | from Shadow.services.telethon import tbot 70 | from Shadow.services.events import register 71 | 72 | @register(pattern="^/hi$") 73 | async def hmm(event): 74 | j = "Hello there" 75 | await event.reply(j) 76 | 77 | __mod_name__ = "Hi" 78 | __help__ = """ 79 | Hi 80 | - /hi: Say Hello There 81 | """ 82 | ``` 83 | -------------------------------------------------------------------------------- /Shadow/modules/UrlLock.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 @DeshadeethThisarana 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import asyncio 19 | 20 | from pyrogram import filters 21 | from pyrogram.errors import RPCError 22 | 23 | from Shadow import BOT_ID 24 | from Shadow.db.mongo_helpers.lockurl import add_chat, get_session, remove_chat 25 | from Shadow.function.pluginhelpers import ( 26 | admins_only, 27 | edit_or_reply, 28 | get_url, 29 | member_permissions, 30 | ) 31 | from Shadow.services.pyrogram import pbot 32 | 33 | 34 | @pbot.on_message( 35 | filters.command("urllock") & ~filters.edited & ~filters.bot & ~filters.private 36 | ) 37 | @admins_only 38 | async def hmm(_, message): 39 | global daisy_chats 40 | try: 41 | user_id = message.from_user.id 42 | except: 43 | return 44 | if not "can_change_info" in (await member_permissions(message.chat.id, user_id)): 45 | await message.reply_text("**You don't have enough permissions**") 46 | return 47 | if len(message.command) != 2: 48 | await message.reply_text( 49 | "I only recognize `/urllock on` and `/urllock off` only" 50 | ) 51 | return 52 | status = message.text.split(None, 1)[1] 53 | message.chat.id 54 | if status == "ON" or status == "on" or status == "On": 55 | lel = await edit_or_reply(message, "`Processing...`") 56 | lol = add_chat(int(message.chat.id)) 57 | if not lol: 58 | await lel.edit("URL Block Already Activated In This Chat") 59 | return 60 | await lel.edit( 61 | f"URL Block Successfully Added For Users In The Chat {message.chat.id}" 62 | ) 63 | 64 | elif status == "OFF" or status == "off" or status == "Off": 65 | lel = await edit_or_reply(message, "`Processing...`") 66 | Escobar = remove_chat(int(message.chat.id)) 67 | if not Escobar: 68 | await lel.edit("URL Block Was Not Activated In This Chat") 69 | return 70 | await lel.edit( 71 | f"URL Block Successfully Deactivated For Users In The Chat {message.chat.id}" 72 | ) 73 | else: 74 | await message.reply_text( 75 | "I only recognize `/urllock on` and `/urllock off` only" 76 | ) 77 | 78 | 79 | @pbot.on_message( 80 | filters.incoming & filters.text & ~filters.private & ~filters.channel & ~filters.bot 81 | ) 82 | async def hi(client, message): 83 | if not get_session(int(message.chat.id)): 84 | message.continue_propagation() 85 | try: 86 | user_id = message.from_user.id 87 | except: 88 | return 89 | try: 90 | if not len(await member_permissions(message.chat.id, user_id)) < 1: 91 | message.continue_propagation() 92 | if len(await member_permissions(message.chat.id, BOT_ID)) < 1: 93 | message.continue_propagation() 94 | if not "can_delete_messages" in ( 95 | await member_permissions(message.chat.id, BOT_ID) 96 | ): 97 | message.continue_propagation() 98 | except RPCError: 99 | return 100 | try: 101 | 102 | lel = get_url(message) 103 | except: 104 | return 105 | 106 | if lel: 107 | try: 108 | await message.delete() 109 | sender = message.from_user.mention() 110 | lol = await client.send_message( 111 | message.chat.id, 112 | f"{sender}, Your message was deleted as it contain a link(s). \n ❗️ Links are not allowed here", 113 | ) 114 | await asyncio.sleep(10) 115 | await lol.delete() 116 | except: 117 | message.continue_propagation() 118 | else: 119 | message.continue_propagation() 120 | -------------------------------------------------------------------------------- /Shadow/modules/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 - 2020 MrYacha. All rights reserved. Source code available under the AGPL. 2 | # Copyright (C) 2021 GalaxyFriendsTeam 3 | # Copyright (C) 2020-2021 Deshadeeth Thisarana 4 | 5 | # This file is part of Shadow (Telegram Bot) 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | 20 | import os 21 | import sys 22 | 23 | from Shadow.utils.logger import log 24 | 25 | LOADED_MODULES = [] 26 | MOD_HELP = {} 27 | 28 | 29 | def list_all_modules() -> list: 30 | modules_directory = "Shadow/modules" 31 | 32 | all_modules = [] 33 | for module_name in os.listdir(modules_directory): 34 | path = modules_directory + "/" + module_name 35 | 36 | if "__init__" in path or "__pycache__" in path: 37 | continue 38 | 39 | if path in all_modules: 40 | log.path("Modules with same name can't exists!") 41 | sys.exit(5) 42 | 43 | # One file module type 44 | if path.endswith(".py"): 45 | # TODO: removesuffix 46 | all_modules.append(module_name.split(".py")[0]) 47 | 48 | # Module directory 49 | if os.path.isdir(path) and os.path.exists(path + "/__init__.py"): 50 | all_modules.append(module_name) 51 | 52 | return all_modules 53 | 54 | 55 | ALL_MODULES = sorted(list_all_modules()) 56 | __all__ = ALL_MODULES + ["ALL_MODULES"] 57 | -------------------------------------------------------------------------------- /Shadow/modules/_webss.py: -------------------------------------------------------------------------------- 1 | # Ported From William Butcher Bot :- https://github.com/thehamkercat/WilliamButcherBot/edit/dev/wbb/modules/webss.py . 2 | # All Credits to WilliamButcherBot. 3 | 4 | from pyrogram import filters 5 | 6 | from Shadow.function.pluginhelpers import admins_only 7 | from Shadow.services.pyrogram import pbot as app 8 | 9 | 10 | @app.on_message(filters.command("webss") & ~filters.private & ~filters.edited) 11 | @admins_only 12 | async def take_ss(_, message): 13 | if len(message.command) != 2: 14 | await message.reply_text("Give A Url To Fetch Screenshot.") 15 | return 16 | url = message.text.split(None, 1)[1] 17 | m = await message.reply_text("**Taking Screenshot**") 18 | await m.edit("**Uploading**") 19 | try: 20 | await app.send_photo( 21 | message.chat.id, 22 | photo=f"https://webshot.amanoteam.com/print?q={url}", 23 | ) 24 | except TypeError: 25 | await m.edit("No Such Website.") 26 | return 27 | await m.delete() 28 | -------------------------------------------------------------------------------- /Shadow/modules/afk.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 - 2020 MrYacha. All rights reserved. Source code available under the AGPL. 2 | # Copyright (C) 2021 HitaloSama. 3 | # Copyright (C) 2019 Aiogram. 4 | # 5 | # This file is part of Shadow (Telegram Bot) 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | 20 | import html 21 | import re 22 | 23 | from Shadow.decorator import register 24 | from Shadow.services.mongo import db 25 | 26 | from .utils.disable import disableable_dec 27 | from .utils.language import get_strings_dec 28 | from .utils.message import get_args_str 29 | from .utils.user_details import get_user, get_user_by_id, get_user_link 30 | 31 | 32 | @register(cmds="afk") 33 | @disableable_dec("afk") 34 | @get_strings_dec("afk") 35 | async def afk(message, strings): 36 | try: 37 | arg = get_args_str(message) 38 | except: 39 | return 40 | # Don't support AFK as anon admin 41 | if message.from_user.id == 1087968824: 42 | await message.reply(strings["afk_anon"]) 43 | return 44 | 45 | if not arg: 46 | reason = "No reason" 47 | else: 48 | reason = arg 49 | 50 | user = await get_user_by_id(message.from_user.id) 51 | user_afk = await db.afk.find_one({"user": user["user_id"]}) 52 | if user_afk: 53 | return 54 | 55 | await db.afk.insert_one({"user": user["user_id"], "reason": reason}) 56 | text = strings["is_afk"].format( 57 | user=(await get_user_link(user["user_id"])), reason=html.escape(reason) 58 | ) 59 | await message.reply(text) 60 | 61 | 62 | @register(f="text", allow_edited=False) 63 | @get_strings_dec("afk") 64 | async def check_afk(message, strings): 65 | if bool(message.reply_to_message): 66 | if message.reply_to_message.from_user.id in (1087968824, 777000): 67 | return 68 | if message.from_user.id in (1087968824, 777000): 69 | return 70 | user_afk = await db.afk.find_one({"user": message.from_user.id}) 71 | if user_afk: 72 | afk_cmd = re.findall("^[!/]afk(.*)", message.text) 73 | if not afk_cmd: 74 | await message.reply( 75 | strings["unafk"].format( 76 | user=(await get_user_link(message.from_user.id)) 77 | ) 78 | ) 79 | await db.afk.delete_one({"_id": user_afk["_id"]}) 80 | 81 | user = await get_user(message) 82 | if not user: 83 | return 84 | 85 | user_afk = await db.afk.find_one({"user": user["user_id"]}) 86 | if user_afk: 87 | await message.reply( 88 | strings["is_afk"].format( 89 | user=(await get_user_link(user["user_id"])), 90 | reason=html.escape(user_afk["reason"]), 91 | ) 92 | ) 93 | -------------------------------------------------------------------------------- /Shadow/modules/antivirus.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 - 2021 Shadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | import os 20 | 21 | import cloudmersive_virus_api_client 22 | from telethon.tl import functions, types 23 | from telethon.tl.types import DocumentAttributeFilename, MessageMediaDocument 24 | 25 | from Shadow.config import get_str_key 26 | from Shadow.services.events import register 27 | from Shadow.services.telethon import tbot 28 | 29 | 30 | async def is_register_admin(chat, user): 31 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 32 | return isinstance( 33 | ( 34 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 35 | ).participant, 36 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 37 | ) 38 | if isinstance(chat, types.InputPeerUser): 39 | return True 40 | 41 | 42 | VIRUS_API_KEY = get_str_key("VIRUS_API_KEY", required=False) 43 | configuration = cloudmersive_virus_api_client.Configuration() 44 | configuration.api_key["Apikey"] = VIRUS_API_KEY 45 | api_instance = cloudmersive_virus_api_client.ScanApi( 46 | cloudmersive_virus_api_client.ApiClient(configuration) 47 | ) 48 | allow_executables = True 49 | allow_invalid_files = True 50 | allow_scripts = True 51 | allow_password_protected_files = True 52 | 53 | 54 | @register(pattern="^/scanit$") 55 | async def virusscan(event): 56 | if event.fwd_from: 57 | return 58 | if event.is_group: 59 | if await is_register_admin(event.input_chat, event.message.sender_id): 60 | pass 61 | else: 62 | return 63 | if not event.reply_to_msg_id: 64 | await event.reply("Reply to a file to scan it.") 65 | return 66 | 67 | c = await event.get_reply_message() 68 | try: 69 | c.media.document 70 | except Exception: 71 | await event.reply("Thats not a file.") 72 | return 73 | h = c.media 74 | try: 75 | k = h.document.attributes 76 | except Exception: 77 | await event.reply("Thats not a file.") 78 | return 79 | if not isinstance(h, MessageMediaDocument): 80 | await event.reply("Thats not a file.") 81 | return 82 | if not isinstance(k[0], DocumentAttributeFilename): 83 | await event.reply("Thats not a file.") 84 | return 85 | try: 86 | virus = c.file.name 87 | await event.client.download_file(c, virus) 88 | gg = await event.reply("Scanning the file ...") 89 | fsize = c.file.size 90 | if not fsize <= 3145700: # MAX = 3MB 91 | await gg.edit("File size exceeds 3MB") 92 | return 93 | api_response = api_instance.scan_file_advanced( 94 | c.file.name, 95 | allow_executables=allow_executables, 96 | allow_invalid_files=allow_invalid_files, 97 | allow_scripts=allow_scripts, 98 | allow_password_protected_files=allow_password_protected_files, 99 | ) 100 | if api_response.clean_result is True: 101 | await gg.edit("This file is safe ✔️\nNo virus detected 🐞") 102 | else: 103 | await gg.edit("This file is Dangerous ☠️️\nVirus detected 🐞") 104 | os.remove(virus) 105 | except Exception as e: 106 | print(e) 107 | os.remove(virus) 108 | await gg.edit("Some error occurred.") 109 | return 110 | 111 | 112 | _mod_name_ = "Virus Scan" 113 | _help_ = """ 114 | - /scanit: Scan a file for virus (MAX SIZE = 3MB) 115 | """ 116 | -------------------------------------------------------------------------------- /Shadow/modules/books.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 DeshadeethThisarana 2 | # Copyright (C) 2021 Shadow 3 | 4 | # This file is part of Shadow (Telegram Bot) 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | import os 21 | import re 22 | 23 | import requests 24 | from bs4 import BeautifulSoup 25 | from telethon import events 26 | 27 | from Shadow.services.telethon import tbot 28 | 29 | 30 | @tbot.on(events.NewMessage(pattern="^/book (.*)")) 31 | async def _(event): 32 | if event.fwd_from: 33 | return 34 | input_str = event.pattern_match.group(1) 35 | lool = 0 36 | KkK = await event.reply("searching for the book...") 37 | lin = "https://b-ok.cc/s/" 38 | text = input_str 39 | link = lin + text 40 | 41 | headers = [ 42 | "User-Agent", 43 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:74.0) Gecko/20100101 Firefox/74.0", 44 | ] 45 | page = requests.get(link) 46 | soup = BeautifulSoup(page.content, "html.parser") 47 | f = open("book.txt", "w") 48 | total = soup.find(class_="totalCounter") 49 | for nb in total.descendants: 50 | nbx = nb.replace("(", "").replace(")", "") 51 | if nbx == "0": 52 | await event.reply("No Books Found with that name.") 53 | else: 54 | 55 | for tr in soup.find_all("td"): 56 | for td in tr.find_all("h3"): 57 | for ts in td.find_all("a"): 58 | title = ts.get_text() 59 | lool = lool + 1 60 | for ts in td.find_all("a", attrs={"href": re.compile("^/book/")}): 61 | ref = ts.get("href") 62 | link = "https://b-ok.cc" + ref 63 | 64 | f.write("\n" + title) 65 | f.write("\nBook link:- " + link + "\n\n") 66 | 67 | f.write("By @Mr_Shadow_Robot.") 68 | f.close() 69 | caption = "A collabration with Friday.\n Join Support @ShadowSupport_Official" 70 | 71 | await tbot.send_file( 72 | event.chat_id, 73 | "book.txt", 74 | caption=f"**BOOKS GATHERED SUCCESSFULLY!\n\nBY SHADOW. JOIN THE SUPPORT @ShadowSupport_Official**", 75 | ) 76 | os.remove("book.txt") 77 | await KkK.delete() 78 | -------------------------------------------------------------------------------- /Shadow/modules/cash.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import requests 19 | from telethon import types 20 | from telethon.tl import functions 21 | 22 | from Shadow.config import get_str_key 23 | from Shadow.services.events import register 24 | from Shadow.services.telethon import tbot 25 | 26 | CASH_API_KEY = get_str_key("CASH_API_KEY", required=False) 27 | 28 | 29 | async def is_register_admin(chat, user): 30 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 31 | return isinstance( 32 | ( 33 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 34 | ).participant, 35 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 36 | ) 37 | if isinstance(chat, types.InputPeerUser): 38 | return True 39 | 40 | 41 | @register(pattern="^/cash") 42 | async def _(event): 43 | if event.fwd_from: 44 | return 45 | """This method of approve system is made by @AyushChatterjee""" 46 | if event.is_group: 47 | if await is_register_admin(event.input_chat, event.message.sender_id): 48 | pass 49 | else: 50 | return 51 | 52 | cmd = event.text 53 | 54 | args = cmd.split(" ") 55 | 56 | if len(args) == 4: 57 | try: 58 | orig_cur_amount = float(args[1]) 59 | 60 | except ValueError: 61 | await event.reply("Invalid Amount Of Currency") 62 | return 63 | 64 | orig_cur = args[2].upper() 65 | 66 | new_cur = args[3].upper() 67 | 68 | request_url = ( 69 | f"https://www.alphavantage.co/query" 70 | f"?function=CURRENCY_EXCHANGE_RATE" 71 | f"&from_currency={orig_cur}" 72 | f"&to_currency={new_cur}" 73 | f"&apikey={CASH_API_KEY}" 74 | ) 75 | response = requests.get(request_url).json() 76 | try: 77 | current_rate = float( 78 | response["Realtime Currency Exchange Rate"]["5. Exchange Rate"] 79 | ) 80 | except KeyError: 81 | await event.reply("Currency Not Supported.") 82 | return 83 | new_cur_amount = round(orig_cur_amount * current_rate, 5) 84 | await event.reply(f"{orig_cur_amount} {orig_cur} = {new_cur_amount} {new_cur}") 85 | 86 | elif len(args) == 1: 87 | await event.reply(__help__) 88 | 89 | else: 90 | await event.reply( 91 | f"**Invalid Args!!:** Required 3 But Passed {len(args) -1}", 92 | ) 93 | -------------------------------------------------------------------------------- /Shadow/modules/country.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | from countryinfo import CountryInfo 19 | 20 | from Shadow.services.events import register 21 | from Shadow.services.telethon import tbot as borg 22 | 23 | 24 | @register(pattern="^/country (.*)") 25 | async def msg(event): 26 | if event.fwd_from: 27 | return 28 | input_str = event.pattern_match.group(1) 29 | lol = input_str 30 | country = CountryInfo(lol) 31 | try: 32 | a = country.info() 33 | except: 34 | await event.reply("Country Not Avaiable Currently") 35 | name = a.get("name") 36 | bb = a.get("altSpellings") 37 | hu = "" 38 | for p in bb: 39 | hu += p + ", " 40 | 41 | area = a.get("area") 42 | borders = "" 43 | hell = a.get("borders") 44 | for fk in hell: 45 | borders += fk + ", " 46 | 47 | call = "" 48 | WhAt = a.get("callingCodes") 49 | for what in WhAt: 50 | call += what + " " 51 | 52 | capital = a.get("capital") 53 | currencies = "" 54 | fker = a.get("currencies") 55 | for FKer in fker: 56 | currencies += FKer + ", " 57 | 58 | HmM = a.get("demonym") 59 | geo = a.get("geoJSON") 60 | pablo = geo.get("features") 61 | Pablo = pablo[0] 62 | PAblo = Pablo.get("geometry") 63 | EsCoBaR = PAblo.get("type") 64 | iso = "" 65 | iSo = a.get("ISO") 66 | for hitler in iSo: 67 | po = iSo.get(hitler) 68 | iso += po + ", " 69 | fla = iSo.get("alpha2") 70 | fla.upper() 71 | 72 | languages = a.get("languages") 73 | lMAO = "" 74 | for lmao in languages: 75 | lMAO += lmao + ", " 76 | 77 | nonive = a.get("nativeName") 78 | waste = a.get("population") 79 | reg = a.get("region") 80 | sub = a.get("subregion") 81 | tik = a.get("timezones") 82 | tom = "" 83 | for jerry in tik: 84 | tom += jerry + ", " 85 | 86 | GOT = a.get("tld") 87 | lanester = "" 88 | for targaryen in GOT: 89 | lanester += targaryen + ", " 90 | 91 | wiki = a.get("wiki") 92 | 93 | caption = f""" 94 | Information Gathered Successfully 95 | 96 | Country Name:- {name} 97 | Alternative Spellings:- {hu} 98 | Country Area:- {area} square kilometers 99 | Borders:- {borders} 100 | Calling Codes:- {call} 101 | Country's Capital:- {capital} 102 | Country's currency:- {currencies} 103 | Demonym:- {HmM} 104 | Country Type:- {EsCoBaR} 105 | ISO Names:- {iso} 106 | Languages:- {lMAO} 107 | Native Name:- {nonive} 108 | population:- {waste} 109 | Region:- {reg} 110 | Sub Region:- {sub} 111 | Time Zones:- {tom} 112 | Top Level Domain:- {lanester} 113 | Wikipedia:- {wiki} 114 | 115 | Gathered By @Mr_Shadow_Robot 116 | """ 117 | 118 | await borg.send_message( 119 | event.chat_id, 120 | caption, 121 | parse_mode="HTML", 122 | ) 123 | -------------------------------------------------------------------------------- /Shadow/modules/covid.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TheHamkerCat 2 | # Edited by TeamOfShadow 3 | 4 | # This file is part of Shadow (Telegram Bot) 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | from pyrogram import filters 21 | 22 | from Shadow.function.pluginhelpers import fetch, json_prettify 23 | from Shadow.services.pyrogram import pbot as app 24 | 25 | 26 | @app.on_message(filters.command("covid") & ~filters.edited) 27 | async def covid(_, message): 28 | if len(message.command) == 1: 29 | data = await fetch("https://corona.lmao.ninja/v2/all") 30 | data = await json_prettify(data) 31 | await app.send_message(message.chat.id, text=data) 32 | return 33 | if len(message.command) != 1: 34 | country = message.text.split(None, 1)[1].strip() 35 | country = country.replace(" ", "") 36 | data = await fetch(f"https://corona.lmao.ninja/v2/countries/{country}") 37 | data = await json_prettify(data) 38 | await app.send_message(message.chat.id, text=data) 39 | return 40 | -------------------------------------------------------------------------------- /Shadow/modules/direct_link.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import re 19 | from random import choice 20 | 21 | import requests 22 | from bs4 import BeautifulSoup 23 | 24 | from Shadow.decorator import register 25 | 26 | from .utils.disable import disableable_dec 27 | from .utils.message import get_arg 28 | 29 | 30 | @register(cmds="direct") 31 | @disableable_dec("direct") 32 | async def direct_link_generator(message): 33 | text = get_arg(message) 34 | 35 | if not text: 36 | m = "Usage: /direct (url)" 37 | await message.reply(m) 38 | return 39 | 40 | if text: 41 | links = re.findall(r"\bhttps?://.*\.\S+", text) 42 | else: 43 | return 44 | 45 | reply = [] 46 | if not links: 47 | await message.reply("No links found!") 48 | return 49 | 50 | for link in links: 51 | if "sourceforge.net" in link: 52 | reply.append(sourceforge(link)) 53 | else: 54 | reply.append( 55 | re.findall(r"\bhttps?://(.*?[^/]+)", link)[0] + " is not supported" 56 | ) 57 | 58 | await message.reply("\n".join(reply)) 59 | 60 | 61 | def sourceforge(url: str) -> str: 62 | try: 63 | link = re.findall(r"\bhttps?://.*sourceforge\.net\S+", url)[0] 64 | except IndexError: 65 | reply = "No SourceForge links found\n" 66 | return reply 67 | 68 | file_path = re.findall(r"/files(.*)/download", link) 69 | if not file_path: 70 | file_path = re.findall(r"/files(.*)", link) 71 | file_path = file_path[0] 72 | reply = f"Mirrors for {file_path.split('/')[-1]}\n" 73 | project = re.findall(r"projects?/(.*?)/files", link)[0] 74 | mirrors = ( 75 | f"https://sourceforge.net/settings/mirror_choices?" 76 | f"projectname={project}&filename={file_path}" 77 | ) 78 | page = BeautifulSoup(requests.get(mirrors).content, "lxml") 79 | info = page.find("ul", {"id": "mirrorList"}).findAll("li") 80 | 81 | for mirror in info[1:]: 82 | name = re.findall(r"\((.*)\)", mirror.text.strip())[0] 83 | dl_url = ( 84 | f'https://{mirror["id"]}.dl.sourceforge.net/project/{project}/{file_path}' 85 | ) 86 | reply += f'{name}' 87 | return reply 88 | 89 | 90 | def useragent(): 91 | useragents = BeautifulSoup( 92 | requests.get( 93 | "https://developers.whatismybrowser.com/" 94 | "useragents/explore/operating_system_name/android/" 95 | ).content, 96 | "lxml", 97 | ).findAll("td", {"class": "useragent"}) 98 | user_agent = choice(useragents) 99 | return user_agent.text 100 | -------------------------------------------------------------------------------- /Shadow/modules/fakeit.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) @chsaiujwal 2020-2021 2 | # Edited by TeamOfShadow 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import os 17 | 18 | import requests 19 | from faker import Faker 20 | from faker.providers import internet 21 | from telethon import events 22 | 23 | from Shadow.function.telethonbasics import is_admin 24 | from Shadow.services.telethon import tbot 25 | 26 | 27 | @tbot.on(events.NewMessage(pattern="/fakegen$")) 28 | async def hi(event): 29 | if event.fwd_from: 30 | return 31 | if event.is_group: 32 | if not await is_admin(event, event.message.sender_id): 33 | await event.reply("`You Should Be Admin To Do This!`") 34 | return 35 | fake = Faker() 36 | print("FAKE DETAILS GENERATED\n") 37 | name = str(fake.name()) 38 | fake.add_provider(internet) 39 | address = str(fake.address()) 40 | ip = fake.ipv4_private() 41 | cc = fake.credit_card_full() 42 | email = fake.ascii_free_email() 43 | job = fake.job() 44 | android = fake.android_platform_token() 45 | pc = fake.chrome() 46 | await event.reply( 47 | f" Fake Information Generated\nName :-{name}\n\nAddress:-{address}\n\nIP ADDRESS:-{ip}\n\nCredit Card:-{cc}\n\nEmail Id:-{email}\n\nJob:-{job}\n\nAndroid user agent:-{android}\n\nPc user agent:-{pc}", 48 | parse_mode="HTML", 49 | ) 50 | 51 | 52 | @tbot.on(events.NewMessage(pattern="/picgen$")) 53 | async def _(event): 54 | if event.fwd_from: 55 | return 56 | if await is_admin(event, event.message.sender_id): 57 | url = "https://thispersondoesnotexist.com/image" 58 | response = requests.get(url) 59 | if response.status_code == 200: 60 | with open("FRIDAYOT.jpg", "wb") as f: 61 | f.write(response.content) 62 | 63 | captin = f"Fake Image powered by @GalaxyFriendsTeam" 64 | fole = "FRIDAYOT.jpg" 65 | await tbot.send_file(event.chat_id, fole, caption=captin) 66 | await event.delete() 67 | os.system("rm ./FRIDAYOT.jpg ") 68 | -------------------------------------------------------------------------------- /Shadow/modules/gps.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | from geopy.geocoders import Nominatim 20 | from telethon import * 21 | from telethon.tl import * 22 | 23 | from Shadow.services.events import register 24 | from Shadow.services.telethon import tbot as client 25 | 26 | 27 | async def is_register_admin(chat, user): 28 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 29 | 30 | return isinstance( 31 | ( 32 | await client(functions.channels.GetParticipantRequest(chat, user)) 33 | ).participant, 34 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 35 | ) 36 | if isinstance(chat, types.InputPeerChat): 37 | 38 | ui = await client.get_peer_id(user) 39 | ps = ( 40 | await client(functions.messages.GetFullChatRequest(chat.chat_id)) 41 | ).full_chat.participants.participants 42 | return isinstance( 43 | next((p for p in ps if p.user_id == ui), None), 44 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 45 | ) 46 | return None 47 | 48 | 49 | GMAPS_LOC = "https://maps.googleapis.com/maps/api/geocode/json" 50 | 51 | 52 | @register(pattern="^/gps (.*)") 53 | async def _(event): 54 | if event.fwd_from: 55 | return 56 | if event.is_group: 57 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 58 | await event.reply( 59 | "You are not Admin. So, You can't use this. Try in my inbox" 60 | ) 61 | return 62 | 63 | args = event.pattern_match.group(1) 64 | 65 | try: 66 | geolocator = Nominatim(user_agent="SkittBot") 67 | location = args 68 | geoloc = geolocator.geocode(location) 69 | longitude = geoloc.longitude 70 | latitude = geoloc.latitude 71 | gm = "https://www.google.com/maps/search/{},{}".format(latitude, longitude) 72 | await client.send_file( 73 | event.chat_id, 74 | file=types.InputMediaGeoPoint( 75 | types.InputGeoPoint(float(latitude), float(longitude)) 76 | ), 77 | ) 78 | await event.reply( 79 | "Open with: [Google Maps]({})".format(gm), 80 | link_preview=False, 81 | ) 82 | except Exception as e: 83 | print(e) 84 | await event.reply("I can't find that") 85 | -------------------------------------------------------------------------------- /Shadow/modules/install.py: -------------------------------------------------------------------------------- 1 | # All Credit To William Butcher Bot. 2 | # Ported This Plugin here By Devil from wbb. 3 | import os 4 | 5 | from pyrogram import filters 6 | 7 | from Shadow import OWNER_ID 8 | from Shadow.services.pyrogram import pbot as app 9 | 10 | 11 | @app.on_message(filters.command("install") & filters.user(OWNER_ID)) 12 | async def install_module(_, message): 13 | if not message.reply_to_message: 14 | await message.reply_text("Reply To A .py File To Install It.") 15 | return 16 | if not message.reply_to_message.document: 17 | await message.reply_text("Reply To A .py File To Install It.") 18 | return 19 | document = message.reply_to_message.document 20 | if document.mime_type != "text/x-python": 21 | await message.reply_text("INVALID_MIME_TYPE, Reply To A Correct .py File.") 22 | return 23 | m = await message.reply_text("**Installing Module**") 24 | await message.reply_to_message.download(f"./Shadow/modules/{document.file_name}") 25 | await m.edit("**Restarting**") 26 | os.execvp( 27 | f"python{str(pyver.split(' ')[0])[:3]}", 28 | [f"python{str(pyver.split(' ')[0])[:3]}", "-m", "Shadow"], 29 | ) 30 | -------------------------------------------------------------------------------- /Shadow/modules/invitelink.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | from pyrogram import filters 19 | 20 | from Shadow.function.pluginhelpers import admins_only 21 | from Shadow.services.pyrogram import pbot 22 | 23 | 24 | @pbot.on_message( 25 | filters.command("invitelink") & ~filters.edited & ~filters.bot & ~filters.private 26 | ) 27 | @admins_only 28 | async def invitelink(client, message): 29 | chid = message.chat.id 30 | try: 31 | invitelink = await client.export_chat_invite_link(chid) 32 | except: 33 | await message.reply_text( 34 | "Add me as admin of yor group first", 35 | ) 36 | return 37 | await message.reply_text(f"Invite link generated successfully \n\n {invitelink}") 38 | 39 | 40 | __HELP__ = """ 41 | Classic filters are just like marie's filter system. If you still like that kind of filter system 42 | 43 | **Admin Only** 44 | - `/cfilter `: Every time someone says "word", the bot will reply with "message" 45 | You can also include buttons in filters, example send `/savefilter google` in reply to `Click Here To Open Google | [Button.url('Google', 'google.com')]` 46 | - `/stopcfilter `: Stop that filter. 47 | - `/stopallcfilters`: Delete all filters in the current chat. 48 | 49 | **Admin + Non-Admin** 50 | - `/cfilters`: List all active filters in the chat 51 | 52 | **Please note classic filters can be unstable. We recommend you to use `/addfilter`** 53 | """ 54 | 55 | 56 | @pbot.on_message(filters.command("cfilterhelp") & ~filters.private & ~filters.edited) 57 | @admins_only 58 | async def filtersghelp(client, message): 59 | await client.send_message(message.chat.id, text=__HELP__, parse_mode="markdown") 60 | -------------------------------------------------------------------------------- /Shadow/modules/json.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import io 19 | 20 | from telethon import types 21 | from telethon.tl import functions, types 22 | from telethon.tl.types import * 23 | 24 | from Shadow.services.events import register 25 | from Shadow.services.telethon import tbot as borg 26 | 27 | 28 | async def is_register_admin(chat, user): 29 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 30 | 31 | return isinstance( 32 | ( 33 | await borg(functions.channels.GetParticipantRequest(chat, user)) 34 | ).participant, 35 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 36 | ) 37 | if isinstance(chat, types.InputPeerChat): 38 | 39 | ui = await borg.get_peer_id(user) 40 | ps = ( 41 | await borg(functions.messages.GetFullChatRequest(chat.chat_id)) 42 | ).full_chat.participants.participants 43 | return isinstance( 44 | next((p for p in ps if p.user_id == ui), None), 45 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 46 | ) 47 | return None 48 | 49 | 50 | @register(pattern="^/json$") 51 | async def _(event): 52 | if event.fwd_from: 53 | return 54 | if event.is_group: 55 | if await is_register_admin(event.input_chat, event.message.sender_id): 56 | pass 57 | elif event.chat_id == iid and event.sender_id == userss: 58 | pass 59 | else: 60 | return 61 | the_real_message = None 62 | reply_to_id = None 63 | if event.reply_to_msg_id: 64 | previous_message = await event.get_reply_message() 65 | the_real_message = previous_message.stringify() 66 | reply_to_id = event.reply_to_msg_id 67 | else: 68 | the_real_message = event.stringify() 69 | reply_to_id = event.message.id 70 | if len(the_real_message) > 4095: 71 | with io.BytesIO(str.encode(the_real_message)) as out_file: 72 | out_file.name = "json.text" 73 | await borg.send_file( 74 | event.chat_id, 75 | out_file, 76 | force_document=True, 77 | allow_cache=False, 78 | reply_to=reply_to_id, 79 | ) 80 | await event.delete() 81 | else: 82 | await event.reply("`{}`".format(the_real_message)) 83 | -------------------------------------------------------------------------------- /Shadow/modules/musicplayer.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | __mod_name__ = "Music Player" 19 | 20 | __help__ = """ 21 | 》==(: SHADOW MUSIC :)==《 22 | 23 | Assistant name » @Shadow_Helper 24 | 25 | • Groups Voice Chat Music Play 🎧 26 | 27 | ==》♾Settings up♾ 28 | ╠ Make @Mr_Shadow_Robot admin 29 | ╠ Start a voice chat 30 | ╠ Try /play [song name] for the first time by an admin 31 | ╚ If userbot joined enjoy music, If not add @Shadow_Helper to your group and retry 32 | 33 | ==》Commands⚒ 34 | 🎧 [Basic Commands] 35 | ╠ /play (song name) - play song from youtube 36 | ╠ /ytp (song name) - play song directly from youtube 37 | ╠ /stream (reply to audio) - play song using audio file 38 | ╠ /playlist - show the list song in queue 39 | ╠ /song (song name) - download song from youtube 40 | ╠ /search (video name) - search video from youtube detailed 41 | ╠ /video (video name) - download video from youtube detailed 42 | ╚ /lyric (song name) - lyrics scrapper 43 | 44 | 🎧 [Advanced Commands] 45 | ╠ /reload - reload bot and refresh the admin list 46 | ╠ /ping - check the bot ping status 47 | ╠ /uptime - check the bot uptime status 48 | ╚ /id - show the group/user id & other 49 | 50 | 🎧 [Admin Commands] 51 | ╠ /player - show the music playing status 52 | ╠ /pause - pause the music streaming 53 | ╠ /resume - resume the music was paused 54 | ╠ /skip - skip to the next song 55 | ╠ /end - stop music streaming 56 | ╠ /join - invite userbot join to your group 57 | ╠ /leave - order the userbot to leave your group 58 | ╠ /auth - authorized user for using music bot 59 | ╠ /unauth - unauthorized for using music bot 60 | ╠ /control - open the player settings panel 61 | ╠ /delcmd (on|off) - enable / disable del cmd feature 62 | ╚ /music (on/off) - disable / enable music player in your group 63 | 64 | 🎧 [Sudo Commands] 65 | ╠ /leaveall - order the assistant to leave from all group 66 | ╠ /stats - show the bot statistic 67 | ╠ /rmd - remove all downloaded files 68 | ╠ /eval (query) - execute code 69 | ╚ /sh (query) - run code 70 | 71 | 🎧 [Owner Commands] 72 | ╠ /stats - show the bot statistic 73 | ╠ /broadcast (reply to message) - send a broadcast message from bot 74 | ╠ /block (user id - duration - reason) - block user for using your bot 75 | ╠ /unblock (user id - reason) - unblock user you blocked for using your bot 76 | ╚ /blocklist - show you the list of user was blocked for using your bot 77 | 78 | 📝 Note: all commands owned by this bot can be executed by the owner of the bot without any exceptions. 79 | 80 | ⭕️PLEASE NOTE THIS SERVICE IS UNSTABLE AND CAN BE STOPPED ANYTIME⭕️ 81 | """ 82 | -------------------------------------------------------------------------------- /Shadow/modules/paste.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 by DevsExpo 2 | # @Github, < https://github.com/DevsExpo >. 3 | # 4 | # This file is part of < https://github.com/DevsExpo/FridayUserBot > project, 5 | # and is released under the "GNU v3.0 License Agreement". 6 | # Please see < https://github.com/DevsExpo/blob/master/LICENSE > 7 | # 8 | # All rights reserved. 9 | 10 | import os 11 | 12 | import requests 13 | from pyrogram import filters 14 | 15 | from Shadow.function.pluginhelpers import edit_or_reply, get_text 16 | from Shadow.services.pyrogram import pbot 17 | 18 | 19 | @pbot.on_message(filters.command("paste") & ~filters.edited & ~filters.bot) 20 | async def paste(client, message): 21 | pablo = await edit_or_reply(message, "`Please Wait.....`") 22 | tex_t = get_text(message) 23 | message_s = tex_t 24 | if not tex_t: 25 | if not message.reply_to_message: 26 | await pablo.edit("`Reply To File / Give Me Text To Paste!`") 27 | return 28 | if not message.reply_to_message.text: 29 | file = await message.reply_to_message.download() 30 | m_list = open(file, "r").read() 31 | message_s = m_list 32 | print(message_s) 33 | os.remove(file) 34 | elif message.reply_to_message.text: 35 | message_s = message.reply_to_message.text 36 | key = ( 37 | requests.post("https://nekobin.com/api/documents", json={"content": message_s}) 38 | .json() 39 | .get("result") 40 | .get("key") 41 | ) 42 | url = f"https://nekobin.com/{key}" 43 | raw = f"https://nekobin.com/raw/{key}" 44 | reply_text = f"Pasted Text To [NekoBin]({url}) And For Raw [Click Here]({raw})" 45 | await pablo.edit(reply_text) 46 | -------------------------------------------------------------------------------- /Shadow/modules/phone.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | from telethon import types 5 | 6 | from Shadow.services.events import register 7 | from Shadow.services.telethon import tbot as client 8 | 9 | 10 | async def is_register_admin(chat, user): 11 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 12 | 13 | return isinstance( 14 | ( 15 | await client(functions.channels.GetParticipantRequest(chat, user)) 16 | ).participant, 17 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 18 | ) 19 | elif isinstance(chat, types.InputPeerChat): 20 | 21 | ui = await client.get_peer_id(user) 22 | ps = ( 23 | await client(functions.messages.GetFullChatRequest(chat.chat_id)) 24 | ).full_chat.participants.participants 25 | return isinstance( 26 | next((p for p in ps if p.user_id == ui), None), 27 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 28 | ) 29 | else: 30 | return None 31 | 32 | 33 | @register(pattern=r"^/phone (.*)") 34 | async def phone(event): 35 | if event.is_group: 36 | if not (await is_register_admin(event.input_chat, event.message.sender_id)): 37 | await event.reply("☎️ You are not admin 🚶‍♀️") 38 | return 39 | information = event.pattern_match.group(1) 40 | number = information 41 | key = "fe65b94e78fc2e3234c1c6ed1b771abd" 42 | api = ( 43 | "http://apilayer.net/api/validate?access_key=" 44 | + key 45 | + "&number=" 46 | + number 47 | + "&country_code=&format=1" 48 | ) 49 | output = requests.get(api) 50 | content = output.text 51 | obj = json.loads(content) 52 | country_code = obj["country_code"] 53 | country_name = obj["country_name"] 54 | location = obj["location"] 55 | carrier = obj["carrier"] 56 | line_type = obj["line_type"] 57 | validornot = obj["valid"] 58 | aa = "Valid: " + str(validornot) 59 | a = "Phone number: " + str(number) 60 | b = "Country: " + str(country_code) 61 | c = "Country Name: " + str(country_name) 62 | d = "Location: " + str(location) 63 | e = "Carrier: " + str(carrier) 64 | f = "Device: " + str(line_type) 65 | g = f"{aa}\n{a}\n{b}\n{c}\n{d}\n{e}\n{f}" 66 | await event.reply(g) 67 | -------------------------------------------------------------------------------- /Shadow/modules/pins.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from aiogram.utils.exceptions import BadRequest 17 | 18 | from Shadow import bot 19 | from Shadow.decorator import register 20 | 21 | from .utils.connections import chat_connection 22 | from .utils.language import get_strings_dec 23 | from .utils.message import get_arg 24 | 25 | 26 | @register(cmds="unpin", user_can_pin_messages=True, bot_can_pin_messages=True) 27 | @chat_connection(admin=True) 28 | @get_strings_dec("pins") 29 | async def unpin_message(message, chat, strings): 30 | # support unpinning all 31 | if get_arg(message) in {"all"}: 32 | return await bot.unpin_all_chat_messages(chat["chat_id"]) 33 | 34 | try: 35 | await bot.unpin_chat_message(chat["chat_id"]) 36 | except BadRequest: 37 | await message.reply(strings["chat_not_modified_unpin"]) 38 | return 39 | 40 | 41 | @register(cmds="pin", user_can_pin_messages=True, bot_can_pin_messages=True) 42 | @get_strings_dec("pins") 43 | async def pin_message(message, strings): 44 | if "reply_to_message" not in message: 45 | await message.reply(strings["no_reply_msg"]) 46 | return 47 | msg = message.reply_to_message.message_id 48 | arg = get_arg(message).lower() 49 | 50 | dnd = True 51 | loud = ["loud", "notify"] 52 | if arg in loud: 53 | dnd = False 54 | 55 | try: 56 | await bot.pin_chat_message(message.chat.id, msg, disable_notification=dnd) 57 | except BadRequest: 58 | await message.reply(strings["chat_not_modified_pin"]) 59 | 60 | 61 | __mod_name__ = "Pinning" 62 | 63 | __help__ = """ 64 | All the pin related commands can be found here; keep your chat up to date on the latest news with a simple pinned message! 65 | 66 | Basic Pins 67 | - /pin: silently pins the message replied to - add 'loud' or 'notify' to give notifs to users. 68 | - /unpin: unpins the currently pinned message - add 'all' to unpin all pinned messages. 69 | 70 | Other 71 | - /permapin [reply]: Pin a custom message through the bot. This message can contain markdown, buttons, and all the other cool features. 72 | - /unpinall: Unpins all pinned messages. 73 | - /antichannelpin [yes/no/on/off]: Don't let telegram auto-pin linked channels. If no arguments are given, shows current setting. 74 | - /cleanlinked [yes/no/on/off]: Delete messages sent by the linked channel. 75 | 76 | Note: When using antichannel pins, make sure to use the /unpin command, instead of doing it manually. Otherwise, the old message will get re-pinned when the channel sends any messages. 77 | """ 78 | -------------------------------------------------------------------------------- /Shadow/modules/promotes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 - 2020 MrYacha. All rights reserved. Source code available under the AGPL. 2 | # Copyright (C) 2021 TeamOfShadow 3 | # Copyright (C) 2020 - 2021 DeshadeethThisarana 4 | 5 | # This file is part of Shadow (Telegram Bot) 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | 20 | import html 21 | 22 | from aiogram.utils.exceptions import ChatAdminRequired 23 | from telethon.errors import AdminRankEmojiNotAllowedError 24 | 25 | from Shadow import BOT_ID, bot 26 | from Shadow.decorator import register 27 | from Shadow.services.telethon import tbot 28 | 29 | from .utils.connections import chat_connection 30 | from .utils.language import get_strings_dec 31 | from .utils.user_details import ( 32 | get_admins_rights, 33 | get_user_and_text_dec, 34 | get_user_dec, 35 | get_user_link, 36 | ) 37 | 38 | 39 | @register(cmds="promote", bot_can_promote_members=True, user_can_promote_members=True) 40 | @chat_connection(admin=True, only_groups=True) 41 | @get_user_and_text_dec() 42 | @get_strings_dec("promotes") 43 | async def promote(message, chat, user, args, strings): 44 | chat_id = chat["chat_id"] 45 | text = strings["promote_success"].format( 46 | user=await get_user_link(user["user_id"]), chat_name=chat["chat_title"] 47 | ) 48 | 49 | if user["user_id"] == BOT_ID: 50 | return 51 | 52 | if user["user_id"] == message.from_user.id: 53 | return await message.reply(strings["cant_promote_yourself"]) 54 | 55 | title = None 56 | 57 | if args: 58 | if len(args) > 16: 59 | await message.reply(strings["rank_to_loong"]) 60 | return 61 | title = args 62 | text += strings["promote_title"].format(role=html.escape(title, quote=False)) 63 | 64 | try: 65 | await tbot.edit_admin( 66 | chat_id, 67 | user["user_id"], 68 | invite_users=True, 69 | change_info=True, 70 | ban_users=True, 71 | delete_messages=True, 72 | pin_messages=True, 73 | title=title, 74 | ) 75 | except ValueError: 76 | return await message.reply(strings["cant_get_user"]) 77 | except AdminRankEmojiNotAllowedError: 78 | return await message.reply(strings["emoji_not_allowed"]) 79 | await get_admins_rights(chat_id, force_update=True) # Reset a cache 80 | await message.reply(text) 81 | 82 | 83 | @register(cmds="demote", bot_can_promote_members=True, user_can_promote_members=True) 84 | @chat_connection(admin=True, only_groups=True) 85 | @get_user_dec() 86 | @get_strings_dec("promotes") 87 | async def demote(message, chat, user, strings): 88 | chat_id = chat["chat_id"] 89 | if user["user_id"] == BOT_ID: 90 | return 91 | 92 | try: 93 | await bot.promote_chat_member(chat_id, user["user_id"]) 94 | except ChatAdminRequired: 95 | return await message.reply(strings["demote_failed"]) 96 | 97 | await get_admins_rights(chat_id, force_update=True) # Reset a cache 98 | await message.reply( 99 | strings["demote_success"].format( 100 | user=await get_user_link(user["user_id"]), chat_name=chat["chat_title"] 101 | ) 102 | ) 103 | -------------------------------------------------------------------------------- /Shadow/modules/purges.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 - 2020 MrYacha. All rights reserved. Source code available under the AGPL. 2 | # Copyright (C) 2021 TeamOfShadow 3 | # Copyright (C) 2020 - 2021 DeshadeethThisarana 4 | 5 | # This file is part of Shadow (Telegram Bot) 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | 20 | import asyncio 21 | 22 | from telethon.errors.rpcerrorlist import MessageDeleteForbiddenError 23 | 24 | from Shadow import bot 25 | from Shadow.decorator import register 26 | from Shadow.services.telethon import tbot 27 | 28 | from .utils.language import get_strings_dec 29 | from .utils.notes import BUTTONS 30 | 31 | 32 | @register(cmds="del", bot_can_delete_messages=True, user_can_delete_messages=True) 33 | @get_strings_dec("msg_deleting") 34 | async def del_message(message, strings): 35 | if not message.reply_to_message: 36 | await message.reply(strings["reply_to_msg"]) 37 | return 38 | msgs = [message.message_id, message.reply_to_message.message_id] 39 | await tbot.delete_messages(message.chat.id, msgs) 40 | 41 | 42 | @register( 43 | cmds="purge", 44 | no_args=True, 45 | bot_can_delete_messages=True, 46 | user_can_delete_messages=True, 47 | ) 48 | @get_strings_dec("msg_deleting") 49 | async def fast_purge(message, strings): 50 | if not message.reply_to_message: 51 | await message.reply(strings["reply_to_msg"]) 52 | return 53 | msg_id = message.reply_to_message.message_id 54 | delete_to = message.message_id 55 | 56 | chat_id = message.chat.id 57 | msgs = [] 58 | for m_id in range(int(delete_to), msg_id - 1, -1): 59 | msgs.append(m_id) 60 | if len(msgs) == 100: 61 | await tbot.delete_messages(chat_id, msgs) 62 | msgs = [] 63 | 64 | try: 65 | await tbot.delete_messages(chat_id, msgs) 66 | except MessageDeleteForbiddenError: 67 | await message.reply(strings["purge_error"]) 68 | return 69 | 70 | msg = await bot.send_message(chat_id, strings["fast_purge_done"]) 71 | await asyncio.sleep(5) 72 | await msg.delete() 73 | 74 | 75 | BUTTONS.update({"delmsg": "btn_deletemsg_cb"}) 76 | 77 | 78 | @register(regexp=r"btn_deletemsg:(\w+)", f="cb", allow_kwargs=True) 79 | async def delmsg_btn(event, regexp=None, **kwargs): 80 | await event.message.delete() 81 | 82 | 83 | __mod_name__ = "Purges" 84 | 85 | __help__ = """ 86 | Need to delete lots of messages? That's what purges are for! 87 | 88 | Available commands: 89 | - /purge: Deletes all messages from the message you replied to, to the current message. 90 | - /del: Deletes the message you replied to and your "/del" command message. 91 | """ 92 | -------------------------------------------------------------------------------- /Shadow/modules/reports.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 - 2020 MrYacha. All rights reserved. Source code available under the AGPL. 2 | # Copyright (C) 2021 TeamOfShadow 3 | # Copyright (C) 2020 - 2021 DeshadeethThisarana 4 | 5 | # This file is part of Shadow (Telegram Bot) 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Affero General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Affero General Public License for more details. 16 | 17 | # You should have received a copy of the GNU Affero General Public License 18 | # along with this program. If not, see . 19 | 20 | from Shadow.decorator import register 21 | from Shadow.services.mongo import db 22 | 23 | from .utils.connections import chat_connection 24 | from .utils.disable import disableable_dec 25 | from .utils.language import get_strings_dec 26 | from .utils.user_details import get_admins_rights, get_user_link, is_user_admin 27 | 28 | 29 | @register(regexp="^@admin$") 30 | @chat_connection(only_groups=True) 31 | @get_strings_dec("reports") 32 | async def report1_cmd(message, chat, strings): 33 | # Checking whether report is disabled in chat! 34 | check = await db.disabled.find_one({"chat_id": chat["chat_id"]}) 35 | if check: 36 | if "report" in check["cmds"]: 37 | return 38 | await report(message, chat, strings) 39 | 40 | 41 | @register(cmds="report") 42 | @chat_connection(only_groups=True) 43 | @disableable_dec("report") 44 | @get_strings_dec("reports") 45 | async def report2_cmd(message, chat, strings): 46 | await report(message, chat, strings) 47 | 48 | 49 | async def report(message, chat, strings): 50 | user = message.from_user.id 51 | 52 | if (await is_user_admin(chat["chat_id"], user)) is True: 53 | return await message.reply(strings["user_user_admin"]) 54 | 55 | if "reply_to_message" not in message: 56 | return await message.reply(strings["no_user_to_report"]) 57 | 58 | offender_id = message.reply_to_message.from_user.id 59 | if (await is_user_admin(chat["chat_id"], offender_id)) is True: 60 | return await message.reply(strings["report_admin"]) 61 | 62 | admins = await get_admins_rights(chat["chat_id"]) 63 | 64 | offender = await get_user_link(offender_id) 65 | text = strings["reported_user"].format(user=offender) 66 | 67 | try: 68 | if message.text.split(None, 2)[1]: 69 | reason = " ".join(message.text.split(None, 2)[1:]) 70 | text += strings["reported_reason"].format(reason=reason) 71 | except IndexError: 72 | pass 73 | 74 | for admin in admins: 75 | text += await get_user_link(admin, custom_name="​") 76 | 77 | await message.reply(text) 78 | 79 | 80 | __mod_name__ = "Reports" 81 | 82 | __help__ = """ 83 | We're all busy people who don't have time to monitor our groups 24/7. But how do you react if someone in your group is spamming? 84 | 85 | Presenting reports; if someone in your group thinks someone needs reporting, they now have an easy way to call all admins. 86 | 87 | Available commands: 88 | - /report (?text): Reports 89 | - @admins: Same as above, but not a clickable 90 | 91 | TIP: You always can disable reporting by Disabling module 92 | """ 93 | -------------------------------------------------------------------------------- /Shadow/modules/rmbg.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | 4 | # This file is part of Shadow (Telegram Bot) 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | import io 21 | import os 22 | from datetime import datetime 23 | 24 | import requests 25 | from telethon import types 26 | from telethon.tl import functions 27 | 28 | from Shadow.config import get_str_key 29 | from Shadow.services.events import register 30 | from Shadow.services.telethon import tbot 31 | 32 | REM_BG_API_KEY = get_str_key("REM_BG_API_KEY", required=False) 33 | TEMP_DOWNLOAD_DIRECTORY = "./" 34 | 35 | 36 | async def is_register_admin(chat, user): 37 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 38 | return isinstance( 39 | ( 40 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 41 | ).participant, 42 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 43 | ) 44 | if isinstance(chat, types.InputPeerUser): 45 | return True 46 | 47 | 48 | @register(pattern="^/rmbg") 49 | async def _(event): 50 | HELP_STR = "Use `/rmbg` as reply to a media" 51 | if event.fwd_from: 52 | return 53 | if event.is_group: 54 | if await is_register_admin(event.input_chat, event.message.sender_id): 55 | pass 56 | else: 57 | return 58 | if REM_BG_API_KEY is None: 59 | await event.reply("You need API token from remove.bg to use this plugin.") 60 | return False 61 | start = datetime.now() 62 | message_id = event.message.id 63 | if event.reply_to_msg_id: 64 | message_id = event.reply_to_msg_id 65 | reply_message = await event.get_reply_message() 66 | await event.reply("Processing...") 67 | try: 68 | downloaded_file_name = await tbot.download_media( 69 | reply_message, TEMP_DOWNLOAD_DIRECTORY 70 | ) 71 | except Exception as e: 72 | await event.reply(str(e)) 73 | return 74 | else: 75 | output_file_name = ReTrieveFile(downloaded_file_name) 76 | os.remove(downloaded_file_name) 77 | else: 78 | await event.reply(HELP_STR) 79 | return 80 | contentType = output_file_name.headers.get("content-type") 81 | if "image" in contentType: 82 | with io.BytesIO(output_file_name.content) as remove_bg_image: 83 | remove_bg_image.name = "rmbg.png" 84 | await tbot.send_file( 85 | event.chat_id, 86 | remove_bg_image, 87 | force_document=True, 88 | supports_streaming=False, 89 | allow_cache=False, 90 | reply_to=message_id, 91 | ) 92 | end = datetime.now() 93 | ms = (end - start).seconds 94 | await event.reply("Background Removed in {} seconds".format(ms)) 95 | else: 96 | await event.reply( 97 | "remove.bg API returned Errors. Please report to @ShadowBotSupport\n`{}".format( 98 | output_file_name.content.decode("UTF-8") 99 | ) 100 | ) 101 | 102 | 103 | def ReTrieveFile(input_file_name): 104 | headers = { 105 | "X-API-Key": REM_BG_API_KEY, 106 | } 107 | files = { 108 | "image_file": (input_file_name, open(input_file_name, "rb")), 109 | } 110 | r = requests.post( 111 | "https://api.remove.bg/v1.0/removebg", 112 | headers=headers, 113 | files=files, 114 | allow_redirects=True, 115 | stream=True, 116 | ) 117 | return r 118 | -------------------------------------------------------------------------------- /Shadow/modules/send.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | # This file is part of Shadow (Telegram Bot) 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as 7 | # published by the Free Software Foundation, either version 3 of the 8 | # License, or (at your option) any later version. 9 | 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | from pyrogram import filters 19 | 20 | from Shadow.function.pluginhelpers import admins_only, get_text 21 | from Shadow.services.pyrogram import pbot 22 | 23 | 24 | @pbot.on_message( 25 | filters.command("send") & ~filters.edited & ~filters.bot & ~filters.private 26 | ) 27 | @admins_only 28 | async def send(client, message): 29 | lol = get_text(message) 30 | await pbot.send_message(message.chat.id, text=lol) 31 | -------------------------------------------------------------------------------- /Shadow/modules/shortify.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 TeamOfShadow 2 | 3 | 4 | # This file is part of Shadow (Telegram Bot) 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | import json 20 | 21 | import aiohttp 22 | from pyrogram import filters 23 | 24 | from Shadow.function.pluginhelpers import admins_only, get_text 25 | from Shadow.services.pyrogram import pbot 26 | 27 | 28 | @pbot.on_message( 29 | filters.command("short") & ~filters.edited & ~filters.bot & ~filters.private 30 | ) 31 | @admins_only 32 | async def shortify(client, message): 33 | lel = await client.send_message(message.chat.id, "`Wait a sec....`") 34 | url = get_text(message) 35 | if "." not in url: 36 | await lel.edit("Defuq!. Is it a url?") 37 | return 38 | header = { 39 | "Authorization": "Bearer ad39983fa42d0b19e4534f33671629a4940298dc", 40 | "Content-Type": "application/json", 41 | } 42 | payload = {"long_url": f"{url}"} 43 | payload = json.dumps(payload) 44 | async with aiohttp.ClientSession() as session: 45 | async with session.post( 46 | "https://api-ssl.bitly.com/v4/shorten", headers=header, data=payload 47 | ) as resp: 48 | data = await resp.json() 49 | msg = f"**Original Url:** {url}\n**Shortened Url:** {data['link']}" 50 | await lel.edit(msg) 51 | -------------------------------------------------------------------------------- /Shadow/modules/tagall.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 by DevsExpo 2 | # @Github, < https://github.com/DevsExpo >. 3 | # 4 | # This file is part of < https://github.com/DevsExpo/FridayUserBot > project, 5 | # and is released under the "GNU v3.0 License Agreement". 6 | # Please see < https://github.com/DevsExpo/blob/master/LICENSE > 7 | # 8 | # All rights reserved. 9 | 10 | 11 | from pyrogram import filters 12 | 13 | from Shadow.function.pluginhelpers import admins_only, get_text 14 | from Shadow.services.pyrogram import pbot 15 | 16 | 17 | @pbot.on_message(filters.command("tagall") & ~filters.edited & ~filters.bot) 18 | @admins_only 19 | async def tagall(client, message): 20 | await message.reply("`Processing.....`") 21 | sh = get_text(message) 22 | if not sh: 23 | sh = "Hi!" 24 | mentions = "" 25 | async for member in client.iter_chat_members(message.chat.id): 26 | mentions += member.user.mention + " " 27 | n = 4096 28 | kk = [mentions[i : i + n] for i in range(0, len(mentions), n)] 29 | for i in kk: 30 | j = f"{sh} \n{i}" 31 | await client.send_message(message.chat.id, j, parse_mode="html") 32 | 33 | 34 | _mod_name_ = "Tagall" 35 | _help_ = """ 36 | - /tagall: Tag everyone in a chat 37 | """ 38 | -------------------------------------------------------------------------------- /Shadow/modules/userinfo.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 @DeshadeethThisarana 2 | 3 | 4 | # This file is part of Shadow (Telegram Bot) 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from datetime import datetime 20 | 21 | from pyrogram import filters 22 | from pyrogram.errors import PeerIdInvalid 23 | from pyrogram.types import Message, User 24 | 25 | from Shadow.services.pyrogram import pbot 26 | 27 | 28 | def ReplyCheck(message: Message): 29 | reply_id = None 30 | 31 | if message.reply_to_message: 32 | reply_id = message.reply_to_message.message_id 33 | 34 | elif not message.from_user.is_self: 35 | reply_id = message.message_id 36 | 37 | return reply_id 38 | 39 | 40 | infotext = ( 41 | "**[{full_name}](tg://user?id={user_id})**\n" 42 | " * User ??: `{user_id}`\n" 43 | " * First Name: `{first_name}`\n" 44 | " * Last Name: `{last_name}`\n" 45 | " * Username: `{username}`\n" 46 | " * Last Online: `{last_online}`\n" 47 | " * Bio: {bio}" 48 | ) 49 | 50 | 51 | def LastOnline(user: User): 52 | if user.is_bot: 53 | return "" 54 | elif user.status == "recently": 55 | return "Recently" 56 | elif user.status == "within_week": 57 | return "Within the last week" 58 | elif user.status == "within_month": 59 | return "Within the last month" 60 | elif user.status == "long_time_ago": 61 | return "A long time ago :(" 62 | elif user.status == "online": 63 | return "Currently Online" 64 | elif user.status == "offline": 65 | return datetime.fromtimestamp(user.status.date).strftime( 66 | "%a, %d %b %Y, %H:%M:%S" 67 | ) 68 | 69 | 70 | def FullName(user: User): 71 | return user.first_name + " " + user.last_name if user.last_name else user.first_name 72 | 73 | 74 | @pbot.on_message(filters.command("whois") & ~filters.edited & ~filters.bot) 75 | async def whois(client, message): 76 | cmd = message.command 77 | if not message.reply_to_message and len(cmd) == 1: 78 | get_user = message.from_user.id 79 | elif len(cmd) == 1: 80 | get_user = message.reply_to_message.from_user.id 81 | elif len(cmd) > 1: 82 | get_user = cmd[1] 83 | try: 84 | get_user = int(cmd[1]) 85 | except ValueError: 86 | pass 87 | try: 88 | user = await client.get_users(get_user) 89 | except PeerIdInvalid: 90 | await message.reply("I don't know that User.") 91 | return 92 | desc = await client.get_chat(get_user) 93 | desc = desc.description 94 | await message.reply_text( 95 | infotext.format( 96 | full_name=FullName(user), 97 | user_id=user.id, 98 | user_dc=user.dc_id, 99 | first_name=user.first_name, 100 | last_name=user.last_name if user.last_name else "", 101 | username=user.username if user.username else "", 102 | last_online=LastOnline(user), 103 | bio=desc if desc else "`No bio set up.`", 104 | ), 105 | disable_web_page_preview=True, 106 | ) 107 | -------------------------------------------------------------------------------- /Shadow/modules/username.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2021 @DeshadeethThisarana 2 | 3 | 4 | # This file is part of Shadow (Telegram Bot) 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from telethon.errors.rpcerrorlist import YouBlockedUserError 20 | from telethon.tl import functions, types 21 | 22 | from Shadow.services.events import register as Shadow 23 | from Shadow.services.telethon import tbot 24 | from Shadow.services.telethonuserbot import ubot 25 | 26 | 27 | async def is_register_admin(chat, user): 28 | 29 | if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): 30 | 31 | return isinstance( 32 | ( 33 | await tbot(functions.channels.GetParticipantRequest(chat, user)) 34 | ).participant, 35 | (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), 36 | ) 37 | if isinstance(chat, types.InputPeerChat): 38 | 39 | ui = await tbot.get_peer_id(user) 40 | ps = ( 41 | await tbot(functions.messages.GetFullChatRequest(chat.chat_id)) 42 | ).full_chat.participants.participants 43 | return isinstance( 44 | next((p for p in ps if p.user_id == ui), None), 45 | (types.ChatParticipantAdmin, types.ChatParticipantCreator), 46 | ) 47 | return None 48 | 49 | 50 | async def silently_send_message(conv, text): 51 | await conv.send_message(text) 52 | response = await conv.get_response() 53 | await conv.mark_read(message=response) 54 | return response 55 | 56 | 57 | @Shadow(pattern="^/namehistory ?(.*)") 58 | async def _(event): 59 | 60 | if event.fwd_from: 61 | 62 | return 63 | 64 | if event.is_group: 65 | if await is_register_admin(event.input_chat, event.message.sender_id): 66 | pass 67 | else: 68 | return 69 | if not event.reply_to_msg_id: 70 | 71 | await event.reply("```Reply to any user message.```") 72 | 73 | return 74 | 75 | reply_message = await event.get_reply_message() 76 | 77 | if not reply_message.text: 78 | 79 | await event.reply("```Reply to text message```") 80 | 81 | return 82 | 83 | chat = "@DetectiveInfoBot" 84 | uid = reply_message.sender_id 85 | reply_message.sender 86 | 87 | if reply_message.sender.bot: 88 | 89 | await event.edit("```Reply to actual users message.```") 90 | 91 | return 92 | 93 | lol = await event.reply("```Processing```") 94 | 95 | async with ubot.conversation(chat) as conv: 96 | 97 | try: 98 | 99 | # response = conv.wait_event( 100 | # events.NewMessage(incoming=True, from_users=1706537835) 101 | # ) 102 | 103 | await silently_send_message(conv, f"/detect_id {uid}") 104 | 105 | # response = await response 106 | responses = await silently_send_message(conv, f"/detect_id {uid}") 107 | except YouBlockedUserError: 108 | 109 | await event.reply("```Please unblock @DetectiveInfoBot and try again```") 110 | 111 | return 112 | await lol.edit(f"{responses.text}") 113 | # await lol.edit(f"{response.message.message}") 114 | -------------------------------------------------------------------------------- /Shadow/modules/utils/android.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import httpx 17 | import rapidjson as json 18 | 19 | # This file is an adaptation / port from the Galaxy Helper Bot. 20 | # Copyright (C) KassemSYR. All rights reserved. 21 | 22 | 23 | class GetDevice: 24 | def __init__(self, device): 25 | """Get device info by codename or model!""" 26 | self.device = device 27 | 28 | async def get(self): 29 | if self.device.lower().startswith("sm-"): 30 | async with httpx.AsyncClient(http2=True) as http: 31 | data = await http.get( 32 | "https://raw.githubusercontent.com/androidtrackers/certified-android-devices/master/by_model.json" 33 | ) 34 | db = json.loads(data.content) 35 | await http.aclose() 36 | try: 37 | name = db[self.device.upper()][0]["name"] 38 | device = db[self.device.upper()][0]["device"] 39 | brand = db[self.device.upper()][0]["brand"] 40 | model = self.device.lower() 41 | return {"name": name, "device": device, "model": model, "brand": brand} 42 | except KeyError: 43 | return False 44 | else: 45 | async with httpx.AsyncClient(http2=True) as http: 46 | data = await http.get( 47 | "https://raw.githubusercontent.com/androidtrackers/certified-android-devices/master/by_device.json" 48 | ) 49 | db = json.loads(data.content) 50 | await http.aclose() 51 | newdevice = ( 52 | self.device.strip("lte").lower() 53 | if self.device.startswith("beyond") 54 | else self.device.lower() 55 | ) 56 | try: 57 | name = db[newdevice][0]["name"] 58 | model = db[newdevice][0]["model"] 59 | brand = db[newdevice][0]["brand"] 60 | device = self.device.lower() 61 | return {"name": name, "device": device, "model": model, "brand": brand} 62 | except KeyError: 63 | return False 64 | -------------------------------------------------------------------------------- /Shadow/modules/utils/covert.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import math 17 | 18 | 19 | def convert_size(size_bytes): 20 | if size_bytes == 0: 21 | return "0B" 22 | size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") 23 | i = int(math.floor(math.log(size_bytes, 1024))) 24 | p = math.pow(1024, i) 25 | s = round(size_bytes / p, 2) 26 | return "%s %s" % (s, size_name[i]) 27 | -------------------------------------------------------------------------------- /Shadow/modules/utils/disable.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from contextlib import suppress 17 | 18 | from Shadow.modules.utils.user_details import is_user_admin 19 | from Shadow.services.mongo import db 20 | from Shadow.utils.logger import log 21 | 22 | DISABLABLE_COMMANDS = [] 23 | 24 | 25 | def disableable_dec(command): 26 | log.debug(f"Adding {command} to the disableable commands...") 27 | 28 | if command not in DISABLABLE_COMMANDS: 29 | DISABLABLE_COMMANDS.append(command) 30 | 31 | def wrapped(func): 32 | async def wrapped_1(*args, **kwargs): 33 | message = args[0] 34 | 35 | chat_id = message.chat.id 36 | user_id = message.from_user.id 37 | cmd = command 38 | 39 | with suppress(KeyError): 40 | if command in (aliases := message.conf["cmds"]): 41 | cmd = aliases[0] 42 | 43 | check = await db.disabled.find_one( 44 | {"chat_id": chat_id, "cmds": {"$in": [cmd]}} 45 | ) 46 | if check and not await is_user_admin(chat_id, user_id): 47 | return 48 | return await func(*args, **kwargs) 49 | 50 | return wrapped_1 51 | 52 | return wrapped 53 | -------------------------------------------------------------------------------- /Shadow/modules/utils/fetch.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | async def fetch(url): 5 | try: 6 | r = requests.request("GET", url=url) 7 | except: 8 | return 9 | 10 | try: 11 | data = r.json() 12 | except: 13 | data = r.text() 14 | return data 15 | -------------------------------------------------------------------------------- /Shadow/modules/utils/httpx.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import httpx 17 | 18 | http = httpx.AsyncClient(http2=True) 19 | -------------------------------------------------------------------------------- /Shadow/modules/utils/message.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from datetime import timedelta 17 | 18 | # elif raw_button[1] == 'note': 19 | # t = InlineKeyboardButton(raw_button[0], callback_data='get_note_{}_{}'.format(chat_id, raw_button[2])) 20 | # elif raw_button[1] == 'alert': 21 | # t = InlineKeyboardButton(raw_button[0], callback_data='get_alert_{}_{}'.format(chat_id, raw_button[2])) 22 | # elif raw_button[1] == 'deletemsg': 23 | # t = InlineKeyboardButton(raw_button[0], callback_data='get_delete_msg_{}_{}'.format(chat_id, raw_button[2])) 24 | 25 | 26 | class InvalidTimeUnit(Exception): 27 | pass 28 | 29 | 30 | def get_arg(message): 31 | try: 32 | return message.get_args().split()[0] 33 | except IndexError: 34 | return "" 35 | 36 | 37 | def get_args(message): 38 | args = message.get_args().split() 39 | if args is None: 40 | # getting args from non-command 41 | args = message.text.split() 42 | return args 43 | 44 | 45 | def get_args_str(message): 46 | return " ".join(get_args(message)) 47 | 48 | 49 | def get_cmd(message): 50 | cmd = message.get_command().lower()[1:].split("@")[0] 51 | return cmd 52 | 53 | 54 | def convert_time(time_val): 55 | if not any(time_val.endswith(unit) for unit in ("m", "h", "d")): 56 | raise TypeError 57 | 58 | time_num = int(time_val[:-1]) 59 | unit = time_val[-1] 60 | kwargs = {} 61 | 62 | if unit == "m": 63 | kwargs["minutes"] = time_num 64 | elif unit == "h": 65 | kwargs["hours"] = time_num 66 | elif unit == "d": 67 | kwargs["days"] = time_num 68 | else: 69 | raise InvalidTimeUnit() 70 | 71 | val = timedelta(**kwargs) 72 | 73 | return val 74 | 75 | 76 | def convert_timedelta(time): 77 | return {"days": time.days, "seconds": time.seconds} 78 | 79 | 80 | def need_args_dec(num=1): 81 | def wrapped(func): 82 | async def wrapped_1(*args, **kwargs): 83 | message = args[0] 84 | if len(message.text.split(" ")) > num: 85 | return await func(*args, **kwargs) 86 | else: 87 | await message.reply("Give me args!") 88 | 89 | return wrapped_1 90 | 91 | return wrapped 92 | -------------------------------------------------------------------------------- /Shadow/modules/utils/restrictions.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from aiogram.types.chat_permissions import ChatPermissions 17 | from aiogram.utils.exceptions import BadRequest, MigrateToChat, Unauthorized 18 | 19 | from Shadow import bot 20 | 21 | 22 | async def ban_user(chat_id, user_id, until_date=None): 23 | try: 24 | await bot.kick_chat_member(chat_id, user_id, until_date=until_date) 25 | except (BadRequest, MigrateToChat, Unauthorized): 26 | return False 27 | return True 28 | 29 | 30 | async def kick_user(chat_id, user_id): 31 | await bot.unban_chat_member(chat_id, user_id) 32 | return True 33 | 34 | 35 | async def mute_user(chat_id, user_id, until_date=None): 36 | await bot.restrict_chat_member( 37 | chat_id, 38 | user_id, 39 | permissions=ChatPermissions(can_send_messages=False, until_date=until_date), 40 | until_date=until_date, 41 | ) 42 | return True 43 | 44 | 45 | async def restrict_user(chat_id, user_id, until_date=None): 46 | await bot.restrict_chat_member( 47 | chat_id, 48 | user_id, 49 | permissions=ChatPermissions( 50 | can_send_messages=True, 51 | can_send_media_messages=False, 52 | can_send_other_messages=False, 53 | can_add_web_page_previews=False, 54 | until_date=until_date, 55 | ), 56 | until_date=until_date, 57 | ) 58 | return True 59 | 60 | 61 | async def unmute_user(chat_id, user_id): 62 | await bot.restrict_chat_member( 63 | chat_id, 64 | user_id, 65 | can_send_messages=True, 66 | can_send_media_messages=True, 67 | can_send_other_messages=True, 68 | can_add_web_page_previews=True, 69 | ) 70 | return True 71 | 72 | 73 | async def unban_user(chat_id, user_id): 74 | try: 75 | return await bot.unban_chat_member(chat_id, user_id, only_if_banned=True) 76 | except (BadRequest, Unauthorized): 77 | return False 78 | -------------------------------------------------------------------------------- /Shadow/modules/utils/term.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import asyncio 17 | import subprocess 18 | 19 | from Shadow.services.telethon import tbot 20 | 21 | 22 | async def chat_term(message, command): 23 | result = await term(command) 24 | if len(result) > 4096: 25 | output = open("output.txt", "w+") 26 | output.write(result) 27 | output.close() 28 | await tbot.send_file( 29 | message.chat.id, 30 | "output.txt", 31 | reply_to=message["message_id"], 32 | caption="`Output too large, sending as file`", 33 | ) 34 | subprocess.run(["rm", "output.txt"], stdout=subprocess.PIPE) 35 | return result 36 | 37 | 38 | async def term(command): 39 | process = await asyncio.create_subprocess_shell( 40 | command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE 41 | ) 42 | stdout, stderr = await process.communicate() 43 | result = str(stdout.decode().strip()) + str(stderr.decode().strip()) 44 | return result 45 | -------------------------------------------------------------------------------- /Shadow/modules/utils/text.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | 17 | from typing import Union 18 | 19 | 20 | class SanTeXDoc: 21 | def __init__(self, *args): 22 | self.items = list(args) 23 | 24 | def __str__(self) -> str: 25 | return "\n".join([str(items) for items in self.items]) 26 | 27 | def __add__(self, other): 28 | self.items.append(other) 29 | return self 30 | 31 | 32 | class StyleFormationCore: 33 | start: str 34 | end: str 35 | 36 | def __init__(self, text: str): 37 | self.text = f"{self.start}{text}{self.end}" 38 | 39 | def __str__(self) -> str: 40 | return self.text 41 | 42 | 43 | class Bold(StyleFormationCore): 44 | start = "" 45 | end = "" 46 | 47 | 48 | class Italic(StyleFormationCore): 49 | start = "" 50 | end = "" 51 | 52 | 53 | class Code(StyleFormationCore): 54 | start = "" 55 | end = "" 56 | 57 | 58 | class Pre(StyleFormationCore): 59 | start = "
"
 60 |     end = "
" 61 | 62 | 63 | class Strikethrough(StyleFormationCore): 64 | start = "" 65 | end = "" 66 | 67 | 68 | class Underline(StyleFormationCore): 69 | start = "" 70 | end = "" 71 | 72 | 73 | class Section: 74 | def __init__(self, *args, title="", indent=3, bold=True, postfix=":"): 75 | self.title_text = title 76 | self.items = list(args) 77 | self.indent = indent 78 | self.bold = bold 79 | self.postfix = postfix 80 | 81 | @property 82 | def title(self) -> str: 83 | title = self.title_text 84 | text = str(Bold(title)) if self.bold else title 85 | text += self.postfix 86 | return text 87 | 88 | def __str__(self) -> str: 89 | text = self.title 90 | space = " " * self.indent 91 | for item in self.items: 92 | text += "\n" 93 | 94 | if type(item) is Section: 95 | item.indent *= 2 96 | if type(item) is SList: 97 | item.indent = self.indent 98 | else: 99 | text += space 100 | 101 | text += str(item) 102 | 103 | return text 104 | 105 | def __add__(self, other): 106 | self.items.append(other) 107 | return self 108 | 109 | 110 | class SList: 111 | def __init__(self, *args, indent=0, prefix="- "): 112 | self.items = list(args) 113 | self.prefix = prefix 114 | self.indent = indent 115 | 116 | def __str__(self) -> str: 117 | space = " " * self.indent if self.indent else " " 118 | text = "" 119 | for idx, item in enumerate(self.items): 120 | if idx > 0: 121 | text += "\n" 122 | text += f"{space}{self.prefix}{item}" 123 | 124 | return text 125 | 126 | 127 | class KeyValue: 128 | def __init__(self, title, value, suffix=": "): 129 | self.title = title 130 | self.value = value 131 | self.suffix = suffix 132 | 133 | def __str__(self) -> str: 134 | text = f"{self.title}{self.suffix}{self.value}" 135 | return text 136 | 137 | 138 | class MultiKeyValue: 139 | def __init__(self, *items: Union[list, tuple], suffix=": ", separator=", "): 140 | self.items: list = items 141 | self.suffix = suffix 142 | self.separator = separator 143 | 144 | def __str__(self) -> str: 145 | text = "" 146 | items_count = len(self.items) 147 | for idx, item in enumerate(self.items): 148 | text += f"{item[0]}{self.suffix}{item[1]}" 149 | 150 | if items_count - 1 != idx: 151 | text += self.separator 152 | 153 | return text 154 | -------------------------------------------------------------------------------- /Shadow/modules/zz_extras.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | __mod_name__ = "🛠 Extras" 17 | 18 | __help__ = """ 19 | The module that contains extra tools that help you to do many cool stuff. 20 | 21 | Available Commands: 22 | 23 | AFK 24 | - /afk [reason]: Mark yourself as AFK (Away From Keyboard) 25 | 26 | URL LOCK 27 | Block links sent by users in your group 28 | - /urllock [on/off]: Enable/Disable URL Lock 29 | 30 | DATE TIME 31 | - /datetime [timezone]: Get the present date and time information 32 | You can check out this link for the available timezones 33 | 34 | PASTE 35 | - /paste [reply] 36 | Usage: Create a paste or a shortened url using nekobin 37 | 38 | TELEGRAPH 39 | - /telegraph [media]: To Make Link of Any Image Or MP4 video. 40 | - /telegraph [text]: To make Link of Any Text Written. 41 | 42 | TORRENT 43 | - /torrent [QUERY]: Search for torrent links 44 | 45 | TEXT TO SPEECH & SPEECH TO TEXT 46 | - /tts: Reply to any message to get text to speech output 47 | - /stt: Type in reply to a voice message (english only) to extract text from it. 48 | 49 | VIRUS SCAN 50 | - /scanit: Scan a file for virus (MAX SIZE = 3MB) 51 | 52 | COUNTRY 53 | - /country [country name]: Gathering info about given country 54 | 55 | COVID 56 | - /covid: To Get Global Stats of Covid. 57 | - /covid [COUNTRY]: To Get Stats of A Single Country. 58 | 59 | CRICKET INFO 60 | - /cs: Gathers cricket information (globally) 61 | 62 | QR CODE 63 | - /getqr: Get text in qr. 64 | - /makeqr: Make a qr code. 65 | 66 | KARMA 67 | [UPVOTE] - Use upvote keywords like "+", "+1", "thanks" etc to upvote a message. 68 | [DOWNVOTE] - Use downvote keywords like "-", "-1", etc to downvote a message. 69 | 70 | - /karma [ON/OFF]: Enable/Disable karma in group. 71 | - /karma [Reply to a message]: Check user's karma 72 | - /karma: Chek karma list of top 10 users 73 | """ 74 | -------------------------------------------------------------------------------- /Shadow/modules/zz_misc.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | __mod_name__ = "Misc ⚙️" 17 | 18 | __help__ = """ 19 | An 'odds and ends' module for small, simple commands which don't really fit anywhere. 20 | 21 | Available commands: 22 | 23 | BASIC 24 | - /github (username): Returns info about a GitHub user or organization. 25 | - /wiki (keywords): Get wikipedia articles just using this bot. 26 | - /imdb: Search for a movie 27 | - /cancel: Disables current state. Can help in cases if @Mr_Shadow_Robot not responing on your message. 28 | - /id: get the current group id. If used by replying to a message, gets that user's id. 29 | - /info: get information about a user. 30 | - /paste: Pase the text/file in nekobin 31 | - /gps: Find a location 32 | 33 | BOOK DOWNLOAD 34 | - /book [book name] [Usage]: Gets Instant Download Link Of Given Book. 35 | 36 | FAKE INFO 37 | - /fakegen: Generates Fake Information 38 | - /picgen: Generate a fake pic 39 | 40 | ZIPPER 41 | - /zip: Reply to a telegram file to compress it in .zip format 42 | - /unzip: Reply to a telegram file to decompress it from the .zip format 43 | 44 | WEATHER 45 | - /weather [city]: Gives weather forecast 46 | - /weatherimg [city]: Gives weather image 47 | 48 | PHONE INFO 49 | - /phone [phone no]: Gathers number info 50 | 51 | CURRENCY 52 | - /cash: currency converter 53 | Example syntax: /cash 1 USD LKR 54 | 55 | NAME HISTORY 56 | - /namehistory [REPLY]: Get the Username and Name History of user. 57 | 58 | SEND 59 | - /send [MESSAGE]: Send given text by bot. 60 | 61 | CC CHECKER 62 | - /au [cc]: Stripe Auth given CC 63 | - /pp [cc]: Paypal 1$ Guest Charge 64 | - /ss [cc]: Speedy Stripe Auth 65 | - /ch [cc]: Check If CC is Live 66 | - /bin [bin]: Gather's Info About the bin 67 | - /gen [bin]: Generates CC with given bin 68 | - /key [sk]: Checks if Stripe key is Live 69 | 70 | Note: Format of cc is ccnum|mm|yy|cvv 71 | ⭕️ Privacy warning: Don't check any of your personal CC's. 72 | 73 | URL TOOLS 74 | - /short (url): Shortify given url. 75 | - /ip (url): Displays information about an IP / domain. 76 | - /direct (url): Generates direct links from the sourceforge.net 77 | """ 78 | -------------------------------------------------------------------------------- /Shadow/services/README.md: -------------------------------------------------------------------------------- 1 | ## Main connections & SQL DB here 2 | -------------------------------------------------------------------------------- /Shadow/services/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | -------------------------------------------------------------------------------- /Shadow/services/apscheduller.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from apscheduler.executors.asyncio import AsyncIOExecutor 17 | from apscheduler.jobstores.redis import RedisJobStore 18 | from apscheduler.schedulers.asyncio import AsyncIOScheduler 19 | from pytz import utc 20 | 21 | from Shadow.config import get_str_key 22 | from Shadow.utils.logger import log 23 | 24 | DEFAULT = "default" 25 | 26 | jobstores = { 27 | DEFAULT: RedisJobStore( 28 | host=get_str_key("REDIS_URI"), 29 | port=get_str_key("REDIS_PORT"), 30 | password=get_str_key("REDIS_PASS"), 31 | ) 32 | } 33 | executors = {DEFAULT: AsyncIOExecutor()} 34 | job_defaults = {"coalesce": False, "max_instances": 3} 35 | 36 | scheduler = AsyncIOScheduler( 37 | jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc 38 | ) 39 | 40 | log.info("Starting apscheduller...") 41 | scheduler.start() 42 | -------------------------------------------------------------------------------- /Shadow/services/errors.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | from functools import wraps 4 | 5 | from Shadow import SUPPORT_CHAT 6 | from Shadow.services.pyrogram import pbot 7 | 8 | 9 | def split_limits(text): 10 | if len(text) < 2048: 11 | return [text] 12 | 13 | lines = text.splitlines(True) 14 | small_msg = "" 15 | result = [] 16 | for line in lines: 17 | if len(small_msg) + len(line) < 2048: 18 | small_msg += line 19 | else: 20 | result.append(small_msg) 21 | small_msg = line 22 | else: 23 | result.append(small_msg) 24 | 25 | return result 26 | 27 | 28 | def capture_err(func): 29 | @wraps(func) 30 | async def capture(client, message, *args, **kwargs): 31 | try: 32 | return await func(client, message, *args, **kwargs) 33 | except Exception as err: 34 | exc_type, exc_obj, exc_tb = sys.exc_info() 35 | errors = traceback.format_exception( 36 | etype=exc_type, 37 | value=exc_obj, 38 | tb=exc_tb, 39 | ) 40 | error_feedback = split_limits( 41 | "**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n".format( 42 | 0 if not message.from_user else message.from_user.id, 43 | 0 if not message.chat else message.chat.id, 44 | message.text or message.caption, 45 | "".join(errors), 46 | ), 47 | ) 48 | for x in error_feedback: 49 | await pbot.send_message(SUPPORT_CHAT, x) 50 | raise err 51 | 52 | return capture 53 | -------------------------------------------------------------------------------- /Shadow/services/events.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import re 3 | from pathlib import Path 4 | 5 | from telethon import events 6 | 7 | from Shadow.services.mongo import mongodb as db 8 | from Shadow.services.telethon import tbot 9 | 10 | gbanned = db.gban 11 | CMD_LIST = {} 12 | 13 | 14 | def register(**args): 15 | pattern = args.get("pattern") 16 | r_pattern = r"^[/]" 17 | 18 | if pattern is not None and not pattern.startswith("(?i)"): 19 | args["pattern"] = "(?i)" + pattern 20 | 21 | args["pattern"] = pattern.replace("^/", r_pattern, 1) 22 | stack = inspect.stack() 23 | previous_stack_frame = stack[1] 24 | file_test = Path(previous_stack_frame.filename) 25 | file_test = file_test.stem.replace(".py", "") 26 | reg = re.compile("(.*)") 27 | 28 | if pattern is not None: 29 | try: 30 | cmd = re.search(reg, pattern) 31 | try: 32 | cmd = cmd.group(1).replace("$", "").replace("\\", "").replace("^", "") 33 | except BaseException: 34 | pass 35 | 36 | try: 37 | CMD_LIST[file_test].append(cmd) 38 | except BaseException: 39 | CMD_LIST.update({file_test: [cmd]}) 40 | except BaseException: 41 | pass 42 | 43 | def decorator(func): 44 | async def wrapper(check): 45 | if check.edit_date: 46 | return 47 | if check.fwd_from: 48 | return 49 | if check.is_group or check.is_private: 50 | pass 51 | else: 52 | # print("i don't work in channels") 53 | return 54 | users = gbanned.find({}) 55 | for c in users: 56 | if check.sender_id == c["user"]: 57 | return 58 | try: 59 | await func(check) 60 | try: 61 | LOAD_PLUG[file_test].append(func) 62 | except Exception: 63 | LOAD_PLUG.update({file_test: [func]}) 64 | except BaseException: 65 | return 66 | else: 67 | pass 68 | 69 | tbot.add_event_handler(wrapper, events.NewMessage(**args)) 70 | return wrapper 71 | 72 | return decorator 73 | 74 | 75 | def chataction(**args): 76 | """Registers chat actions.""" 77 | 78 | def decorator(func): 79 | tbot.add_event_handler(func, events.ChatAction(**args)) 80 | return func 81 | 82 | return decorator 83 | 84 | 85 | def userupdate(**args): 86 | """Registers user updates.""" 87 | 88 | def decorator(func): 89 | tbot.add_event_handler(func, events.UserUpdate(**args)) 90 | return func 91 | 92 | return decorator 93 | 94 | 95 | def inlinequery(**args): 96 | """Registers inline query.""" 97 | pattern = args.get("pattern", None) 98 | 99 | if pattern is not None and not pattern.startswith("(?i)"): 100 | args["pattern"] = "(?i)" + pattern 101 | 102 | def decorator(func): 103 | tbot.add_event_handler(func, events.InlineQuery(**args)) 104 | return func 105 | 106 | return decorator 107 | 108 | 109 | def callbackquery(**args): 110 | """Registers inline query.""" 111 | 112 | def decorator(func): 113 | tbot.add_event_handler(func, events.CallbackQuery(**args)) 114 | return func 115 | 116 | return decorator 117 | -------------------------------------------------------------------------------- /Shadow/services/mongo.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | 17 | from motor import motor_asyncio 18 | from odmantic import AIOEngine 19 | from pymongo import MongoClient 20 | 21 | from Shadow.config import get_int_key, get_str_key 22 | 23 | MONGO_URI = get_str_key("MONGO_URI") 24 | MONGO_PORT = get_int_key("MONGO_PORT") 25 | MONGO_DB = get_str_key("MONGO_DB") 26 | 27 | # Init MongoDB 28 | mongodb = MongoClient(MONGO_URI, MONGO_PORT)[MONGO_DB] 29 | motor = motor_asyncio.AsyncIOMotorClient(MONGO_URI, MONGO_PORT) 30 | db = motor[MONGO_DB] 31 | 32 | engine = AIOEngine(motor, MONGO_DB) 33 | -------------------------------------------------------------------------------- /Shadow/services/mongo2.py: -------------------------------------------------------------------------------- 1 | # Support Dual Mongo DB now 2 | # For free users 3 | 4 | from motor.motor_asyncio import AsyncIOMotorClient as MongoClient 5 | 6 | from Shadow.config import get_str_key 7 | 8 | MONGO2 = get_str_key("MONGO_URI_2", None) 9 | MONGO = get_str_key("MONGO_URI", required=True) 10 | if MONGO2 == None: 11 | MONGO2 = MONGO 12 | 13 | mongo_client = MongoClient(MONGO2) 14 | db = mongo_client.shadow 15 | -------------------------------------------------------------------------------- /Shadow/services/pyrogram.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | import logging 17 | 18 | from pyrogram import Client 19 | 20 | # from pyromod import listen 21 | from Shadow.config import get_int_key, get_str_key 22 | 23 | TOKEN = get_str_key("TOKEN", required=True) 24 | APP_ID = get_int_key("APP_ID", required=True) 25 | APP_HASH = get_str_key("APP_HASH", required=True) 26 | session_name = TOKEN.split(":")[0] 27 | pbot = Client( 28 | session_name, 29 | api_id=APP_ID, 30 | api_hash=APP_HASH, 31 | bot_token=TOKEN, 32 | ) 33 | 34 | # disable logging for pyrogram [not for ERROR logging] 35 | logging.getLogger("pyrogram").setLevel(level=logging.ERROR) 36 | 37 | pbot.start() 38 | -------------------------------------------------------------------------------- /Shadow/services/redis.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import sys 17 | 18 | import redis as redis_lib 19 | 20 | from Shadow import log 21 | from Shadow.config import get_str_key 22 | 23 | # Init Redis 24 | redis = redis_lib.Redis( 25 | host=get_str_key("REDIS_URI"), 26 | port=get_str_key("REDIS_PORT"), 27 | password=get_str_key("REDIS_PASS"), 28 | decode_responses=True, 29 | ) 30 | 31 | bredis = redis_lib.Redis( 32 | host=get_str_key("REDIS_URI"), 33 | port=get_str_key("REDIS_PORT"), 34 | password=get_str_key("REDIS_PASS"), 35 | ) 36 | 37 | try: 38 | redis.ping() 39 | except redis_lib.ConnectionError: 40 | sys.exit(log.critical("Can't connect to RedisDB! Exiting...")) 41 | -------------------------------------------------------------------------------- /Shadow/services/sql/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from sqlalchemy.orm import scoped_session, sessionmaker 4 | 5 | from Shadow import POSTGRESS_URL as DB_URI 6 | 7 | 8 | def start() -> scoped_session: 9 | engine = create_engine(DB_URI, client_encoding="utf8") 10 | BASE.metadata.bind = engine 11 | BASE.metadata.create_all(engine) 12 | return scoped_session(sessionmaker(bind=engine, autoflush=False)) 13 | 14 | 15 | BASE = declarative_base() 16 | SESSION = start() 17 | -------------------------------------------------------------------------------- /Shadow/services/sql/afk_sql.py: -------------------------------------------------------------------------------- 1 | # Reconfigured with AioGram by ShadowDevTeam 2 | # Timer added by MissJuliaRobot 3 | import threading 4 | import time 5 | 6 | from sqlalchemy import Boolean, Column, Integer, String, UnicodeText 7 | 8 | from Shadow.services.sql import BASE, SESSION 9 | 10 | 11 | class AFK(BASE): 12 | __tablename__ = "afk_usrs" 13 | 14 | user_id = Column(Integer, primary_key=True) 15 | is_afk = Column(Boolean) 16 | reason = Column(UnicodeText) 17 | start_time = Column(String) 18 | 19 | def __init__(self, user_id, reason="", is_afk=True, start_time=""): 20 | self.user_id = user_id 21 | self.reason = reason 22 | self.is_afk = is_afk 23 | self.start_time = start_time 24 | 25 | def __repr__(self): 26 | return "afk_status for {}".format(self.user_id) 27 | 28 | 29 | AFK.__table__.create(checkfirst=True) 30 | INSERTION_LOCK = threading.RLock() 31 | 32 | AFK_USERS = {} 33 | AFK_USERSS = {} 34 | 35 | 36 | def is_afk(user_id): 37 | return user_id in AFK_USERS 38 | return user_id in AFK_USERSS 39 | 40 | 41 | def check_afk_status(user_id): 42 | try: 43 | return SESSION.query(AFK).get(user_id) 44 | finally: 45 | SESSION.close() 46 | 47 | 48 | def set_afk(user_id, reason, start_time=""): 49 | with INSERTION_LOCK: 50 | curr = SESSION.query(AFK).get(user_id) 51 | if not curr: 52 | curr = AFK(user_id, reason, True, start_time) 53 | else: 54 | curr.is_afk = True 55 | curr.reason = reason 56 | curr.start_time = time.time() 57 | AFK_USERS[user_id] = reason 58 | AFK_USERSS[user_id] = start_time 59 | SESSION.add(curr) 60 | SESSION.commit() 61 | 62 | 63 | def rm_afk(user_id): 64 | with INSERTION_LOCK: 65 | curr = SESSION.query(AFK).get(user_id) 66 | if curr: 67 | if user_id in AFK_USERS: # sanity check 68 | del AFK_USERS[user_id] 69 | del AFK_USERSS[user_id] 70 | SESSION.delete(curr) 71 | SESSION.commit() 72 | return True 73 | 74 | SESSION.close() 75 | return False 76 | 77 | 78 | def __load_afk_users(): 79 | global AFK_USERS 80 | global AFK_USERSS 81 | try: 82 | all_afk = SESSION.query(AFK).all() 83 | AFK_USERS = {user.user_id: user.reason for user in all_afk if user.is_afk} 84 | AFK_USERSS = {user.user_id: user.start_time for user in all_afk if user.is_afk} 85 | finally: 86 | SESSION.close() 87 | 88 | 89 | __load_afk_users() 90 | -------------------------------------------------------------------------------- /Shadow/services/sql/chatbot_sql.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | from sqlalchemy import Column, String 4 | 5 | from Shadow.services.sql import BASE, SESSION 6 | 7 | 8 | class ChatbotChats(BASE): 9 | __tablename__ = "chatbot_chats" 10 | chat_id = Column(String(14), primary_key=True) 11 | ses_id = Column(String(70)) 12 | expires = Column(String(15)) 13 | 14 | def __init__(self, chat_id, ses_id, expires): 15 | self.chat_id = chat_id 16 | self.ses_id = ses_id 17 | self.expires = expires 18 | 19 | 20 | ChatbotChats.__table__.create(checkfirst=True) 21 | 22 | INSERTION_LOCK = threading.RLock() 23 | 24 | 25 | def is_chat(chat_id): 26 | try: 27 | chat = SESSION.query(ChatbotChats).get(str(chat_id)) 28 | if chat: 29 | return True 30 | return False 31 | finally: 32 | SESSION.close() 33 | 34 | 35 | def set_ses(chat_id, ses_id, expires): 36 | with INSERTION_LOCK: 37 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 38 | if not autochat: 39 | autochat = ChatbotChats(str(chat_id), str(ses_id), str(expires)) 40 | else: 41 | autochat.ses_id = str(ses_id) 42 | autochat.expires = str(expires) 43 | 44 | SESSION.add(autochat) 45 | SESSION.commit() 46 | 47 | 48 | def get_ses(chat_id): 49 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 50 | sesh = "" 51 | exp = "" 52 | if autochat: 53 | sesh = str(autochat.ses_id) 54 | exp = str(autochat.expires) 55 | 56 | SESSION.close() 57 | return sesh, exp 58 | 59 | 60 | def rem_chat(chat_id): 61 | with INSERTION_LOCK: 62 | autochat = SESSION.query(ChatbotChats).get(str(chat_id)) 63 | if autochat: 64 | SESSION.delete(autochat) 65 | 66 | SESSION.commit() 67 | 68 | 69 | def get_all_chats(): 70 | try: 71 | return SESSION.query(ChatbotChats.chat_id).all() 72 | finally: 73 | SESSION.close() 74 | -------------------------------------------------------------------------------- /Shadow/services/sql/filters_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, LargeBinary, Numeric, String, UnicodeText 2 | 3 | from Shadow.services.sql import BASE, SESSION 4 | 5 | 6 | class Filters(BASE): 7 | __tablename__ = "cust_filters" 8 | chat_id = Column(String(14), primary_key=True) 9 | keyword = Column(UnicodeText, primary_key=True) 10 | reply = Column(UnicodeText) 11 | snip_type = Column(Numeric) 12 | media_id = Column(UnicodeText) 13 | media_access_hash = Column(UnicodeText) 14 | media_file_reference = Column(LargeBinary) 15 | 16 | def __init__( 17 | self, 18 | chat_id, 19 | keyword, 20 | reply, 21 | snip_type, 22 | media_id=None, 23 | media_access_hash=None, 24 | media_file_reference=None, 25 | ): 26 | self.chat_id = chat_id 27 | self.keyword = keyword 28 | self.reply = reply 29 | self.snip_type = snip_type 30 | self.media_id = media_id 31 | self.media_access_hash = media_access_hash 32 | self.media_file_reference = media_file_reference 33 | 34 | 35 | Filters.__table__.create(checkfirst=True) 36 | 37 | 38 | def get_filter(chat_id, keyword): 39 | try: 40 | return SESSION.query(Filters).get((str(chat_id), keyword)) 41 | except BaseException: 42 | return None 43 | finally: 44 | SESSION.close() 45 | 46 | 47 | def get_all_filters(chat_id): 48 | try: 49 | return SESSION.query(Filters).filter(Filters.chat_id == str(chat_id)).all() 50 | except BaseException: 51 | return None 52 | finally: 53 | SESSION.close() 54 | 55 | 56 | def add_filter( 57 | chat_id, 58 | keyword, 59 | reply, 60 | snip_type, 61 | media_id, 62 | media_access_hash, 63 | media_file_reference, 64 | ): 65 | adder = SESSION.query(Filters).get((str(chat_id), keyword)) 66 | if adder: 67 | adder.reply = reply 68 | adder.snip_type = snip_type 69 | adder.media_id = media_id 70 | adder.media_access_hash = media_access_hash 71 | adder.media_file_reference = media_file_reference 72 | else: 73 | adder = Filters( 74 | chat_id, 75 | keyword, 76 | reply, 77 | snip_type, 78 | media_id, 79 | media_access_hash, 80 | media_file_reference, 81 | ) 82 | SESSION.add(adder) 83 | SESSION.commit() 84 | 85 | 86 | def remove_filter(chat_id, keyword): 87 | saved_filter = SESSION.query(Filters).get((str(chat_id), keyword)) 88 | if saved_filter: 89 | SESSION.delete(saved_filter) 90 | SESSION.commit() 91 | 92 | 93 | def remove_all_filters(chat_id): 94 | saved_filter = SESSION.query(Filters).filter(Filters.chat_id == str(chat_id)) 95 | if saved_filter: 96 | saved_filter.delete() 97 | SESSION.commit() 98 | -------------------------------------------------------------------------------- /Shadow/services/sql/forceSubscribe_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Numeric, String 2 | 3 | from Shadow.services.sql import BASE, SESSION 4 | 5 | 6 | class forceSubscribe(BASE): 7 | __tablename__ = "forceSubscribe" 8 | chat_id = Column(Numeric, primary_key=True) 9 | channel = Column(String) 10 | 11 | def __init__(self, chat_id, channel): 12 | self.chat_id = chat_id 13 | self.channel = channel 14 | 15 | 16 | forceSubscribe.__table__.create(checkfirst=True) 17 | 18 | 19 | def fs_settings(chat_id): 20 | try: 21 | return ( 22 | SESSION.query(forceSubscribe) 23 | .filter(forceSubscribe.chat_id == chat_id) 24 | .one() 25 | ) 26 | except: 27 | return None 28 | finally: 29 | SESSION.close() 30 | 31 | 32 | def add_channel(chat_id, channel): 33 | adder = SESSION.query(forceSubscribe).get(chat_id) 34 | if adder: 35 | adder.channel = channel 36 | else: 37 | adder = forceSubscribe(chat_id, channel) 38 | SESSION.add(adder) 39 | SESSION.commit() 40 | 41 | 42 | def disapprove(chat_id): 43 | rem = SESSION.query(forceSubscribe).get(chat_id) 44 | if rem: 45 | SESSION.delete(rem) 46 | SESSION.commit() 47 | -------------------------------------------------------------------------------- /Shadow/services/sql/night_mode_sql.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) Midhun KM 2020-2021 2 | # This program is free software: you can redistribute it and/or modify 3 | # it under the terms of the GNU Affero General Public License as published by 4 | # the Free Software Foundation, either version 3 of the License, or 5 | 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Affero General Public License for more details. 10 | 11 | # You should have received a copy of the GNU Affero General Public License 12 | # along with this program. If not, see . 13 | 14 | from sqlalchemy import Column, String 15 | 16 | from Shadow.services.sql import BASE, SESSION 17 | 18 | 19 | class Nightmode(BASE): 20 | __tablename__ = "nightmode" 21 | chat_id = Column(String(14), primary_key=True) 22 | 23 | def __init__(self, chat_id): 24 | self.chat_id = chat_id 25 | 26 | 27 | Nightmode.__table__.create(checkfirst=True) 28 | 29 | 30 | def add_nightmode(chat_id: str): 31 | nightmoddy = Nightmode(str(chat_id)) 32 | SESSION.add(nightmoddy) 33 | SESSION.commit() 34 | 35 | 36 | def rmnightmode(chat_id: str): 37 | rmnightmoddy = SESSION.query(Nightmode).get(str(chat_id)) 38 | if rmnightmoddy: 39 | SESSION.delete(rmnightmoddy) 40 | SESSION.commit() 41 | 42 | 43 | def get_all_chat_id(): 44 | stark = SESSION.query(Nightmode).all() 45 | SESSION.close() 46 | return stark 47 | 48 | 49 | def is_nightmode_indb(chat_id: str): 50 | try: 51 | s__ = SESSION.query(Nightmode).get(str(chat_id)) 52 | if s__: 53 | return str(s__.chat_id) 54 | finally: 55 | SESSION.close() 56 | -------------------------------------------------------------------------------- /Shadow/services/sql/nsfw_watch_sql.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) Midhun KM 2020-2021 2 | # This program is free software: you can redistribute it and/or modify 3 | # it under the terms of the GNU Affero General Public License as published by 4 | # the Free Software Foundation, either version 3 of the License, or 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Affero General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Affero General Public License 12 | # along with this program. If not, see . 13 | 14 | from sqlalchemy import Column, String 15 | 16 | from Shadow.services.sql import BASE, SESSION 17 | 18 | 19 | class Nsfwatch(BASE): 20 | __tablename__ = "nsfwatch" 21 | chat_id = Column(String(14), primary_key=True) 22 | 23 | def __init__(self, chat_id): 24 | self.chat_id = chat_id 25 | 26 | 27 | Nsfwatch.__table__.create(checkfirst=True) 28 | 29 | 30 | def add_nsfwatch(chat_id: str): 31 | nsfws = Nsfwatch(str(chat_id)) 32 | SESSION.add(nsfws) 33 | SESSION.commit() 34 | 35 | 36 | def rmnsfwatch(chat_id: str): 37 | nsfwm = SESSION.query(Nsfwatch).get(str(chat_id)) 38 | if nsfwm: 39 | SESSION.delete(nsfwm) 40 | SESSION.commit() 41 | 42 | 43 | def get_all_nsfw_enabled_chat(): 44 | stark = SESSION.query(Nsfwatch).all() 45 | SESSION.close() 46 | return stark 47 | 48 | 49 | def is_nsfwatch_indb(chat_id: str): 50 | try: 51 | s__ = SESSION.query(Nsfwatch).get(str(chat_id)) 52 | if s__: 53 | return str(s__.chat_id) 54 | finally: 55 | SESSION.close() 56 | -------------------------------------------------------------------------------- /Shadow/services/sql/talk_mode_sql.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) DeshadeethThisarana 2021 2 | # This program is free software: you can redistribute it and/or modify 3 | # it under the terms of the GNU Affero General Public License as published by 4 | # the Free Software Foundation, either version 3 of the License, or 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU Affero General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU Affero General Public License 12 | # along with this program. If not, see . 13 | 14 | from sqlalchemy import Column, String 15 | 16 | from Shadow.services.sql import BASE, SESSION 17 | 18 | 19 | class Talkmode(BASE): 20 | __tablename__ = "talkmode" 21 | chat_id = Column(String(14), primary_key=True) 22 | 23 | def __init__(self, chat_id): 24 | self.chat_id = chat_id 25 | 26 | 27 | Talkmode.__table__.create(checkfirst=True) 28 | 29 | 30 | def add_talkmode(chat_id: str): 31 | talkmoddy = Talkmode(str(chat_id)) 32 | SESSION.add(talkmoddy) 33 | SESSION.commit() 34 | 35 | 36 | def rmtalkmode(chat_id: str): 37 | rmtalkmoddy = SESSION.query(Talkmode).get(str(chat_id)) 38 | if rmtalkmoddy: 39 | SESSION.delete(rmtalkmoddy) 40 | SESSION.commit() 41 | 42 | 43 | def get_all_chat_id(): 44 | stark = SESSION.query(Talkmode).all() 45 | SESSION.close() 46 | return stark 47 | 48 | 49 | def is_talkmode_indb(chat_id: str): 50 | try: 51 | s__ = SESSION.query(Talkmode).get(str(chat_id)) 52 | if s__: 53 | return str(s__.chat_id) 54 | finally: 55 | SESSION.close() 56 | -------------------------------------------------------------------------------- /Shadow/services/sql/urlblacklist_sql.py: -------------------------------------------------------------------------------- 1 | # MissJuliaRobot (A Telegram Bot Project) 2 | # Copyright (C) 2019-Present Anonymous (https://t.me/MissJuliaRobot) 3 | 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, in version 3 of the License. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see < https://www.gnu.org/licenses/agpl-3.0.en.html > 15 | 16 | 17 | import threading 18 | 19 | from sqlalchemy import Column, String, UnicodeText 20 | 21 | from Shadow.services.sql import BASE, SESSION 22 | 23 | 24 | class URLBlackListFilters(BASE): 25 | __tablename__ = "url_blacklist" 26 | chat_id = Column(String(14), primary_key=True) 27 | domain = Column(UnicodeText, primary_key=True, nullable=False) 28 | 29 | def __init__(self, chat_id, domain): 30 | self.chat_id = str(chat_id) 31 | self.domain = str(domain) 32 | 33 | 34 | URLBlackListFilters.__table__.create(checkfirst=True) 35 | 36 | URL_BLACKLIST_FILTER_INSERTION_LOCK = threading.RLock() 37 | 38 | CHAT_URL_BLACKLISTS = {} 39 | 40 | 41 | def blacklist_url(chat_id, domain): 42 | with URL_BLACKLIST_FILTER_INSERTION_LOCK: 43 | domain_filt = URLBlackListFilters(str(chat_id), domain) 44 | 45 | SESSION.merge(domain_filt) 46 | SESSION.commit() 47 | CHAT_URL_BLACKLISTS.setdefault(str(chat_id), set()).add(domain) 48 | 49 | 50 | def rm_url_from_blacklist(chat_id, domain): 51 | with URL_BLACKLIST_FILTER_INSERTION_LOCK: 52 | domain_filt = SESSION.query(URLBlackListFilters).get((str(chat_id), domain)) 53 | if domain_filt: 54 | if domain in CHAT_URL_BLACKLISTS.get(str(chat_id), set()): 55 | CHAT_URL_BLACKLISTS.get(str(chat_id), set()).remove(domain) 56 | SESSION.delete(domain_filt) 57 | SESSION.commit() 58 | return True 59 | 60 | SESSION.close() 61 | return False 62 | 63 | 64 | def get_blacklisted_urls(chat_id): 65 | return CHAT_URL_BLACKLISTS.get(str(chat_id), set()) 66 | 67 | 68 | def _load_chat_blacklist(): 69 | global CHAT_URL_BLACKLISTS 70 | try: 71 | chats = SESSION.query(URLBlackListFilters.chat_id).distinct().all() 72 | for (chat_id,) in chats: 73 | CHAT_URL_BLACKLISTS[chat_id] = [] 74 | 75 | all_urls = SESSION.query(URLBlackListFilters).all() 76 | for url in all_urls: 77 | CHAT_URL_BLACKLISTS[url.chat_id] += [url.domain] 78 | CHAT_URL_BLACKLISTS = {k: set(v) for k, v in CHAT_URL_BLACKLISTS.items()} 79 | finally: 80 | SESSION.close() 81 | 82 | 83 | _load_chat_blacklist() 84 | -------------------------------------------------------------------------------- /Shadow/services/sql/welcome_sql.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import BigInteger, Boolean, Column, String, UnicodeText 2 | 3 | from Shadow.services.sql import BASE, SESSION 4 | 5 | 6 | class Goodbye(BASE): 7 | __tablename__ = "goodbye" 8 | chat_id = Column(String(14), primary_key=True) 9 | custom_goodbye_message = Column(UnicodeText) 10 | media_file_id = Column(UnicodeText) 11 | should_clean_goodbye = Column(Boolean, default=False) 12 | previous_goodbye = Column(BigInteger) 13 | 14 | def __init__( 15 | self, 16 | chat_id, 17 | custom_goodbye_message, 18 | should_clean_goodbye, 19 | previous_goodbye, 20 | media_file_id=None, 21 | ): 22 | self.chat_id = chat_id 23 | self.custom_goodbye_message = custom_goodbye_message 24 | self.media_file_id = media_file_id 25 | self.should_clean_goodbye = should_clean_goodbye 26 | self.previous_goodbye = previous_goodbye 27 | 28 | 29 | Goodbye.__table__.create(checkfirst=True) 30 | 31 | 32 | def get_current_goodbye_settings(chat_id): 33 | try: 34 | return SESSION.query(Goodbye).filter(Goodbye.chat_id == str(chat_id)).one() 35 | except: 36 | return None 37 | finally: 38 | SESSION.close() 39 | 40 | 41 | def add_goodbye_setting( 42 | chat_id, 43 | custom_goodbye_message, 44 | should_clean_goodbye, 45 | previous_goodbye, 46 | media_file_id, 47 | ): 48 | # adder = SESSION.query(Goodbye).get(chat_id) 49 | adder = Goodbye( 50 | chat_id, 51 | custom_goodbye_message, 52 | should_clean_goodbye, 53 | previous_goodbye, 54 | media_file_id, 55 | ) 56 | SESSION.add(adder) 57 | SESSION.commit() 58 | 59 | 60 | def rm_goodbye_setting(chat_id): 61 | rem = SESSION.query(Goodbye).get(str(chat_id)) 62 | if rem: 63 | SESSION.delete(rem) 64 | SESSION.commit() 65 | 66 | 67 | def update_previous_goodbye(chat_id, previous_goodbye): 68 | row = SESSION.query(Goodbye).get(str(chat_id)) 69 | row.previous_goodbye = previous_goodbye 70 | # commit the changes to the DB 71 | SESSION.commit() 72 | -------------------------------------------------------------------------------- /Shadow/services/telethon.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | 17 | from telethon.sync import TelegramClient 18 | 19 | from Shadow.config import get_int_key, get_str_key 20 | 21 | TOKEN = get_str_key("TOKEN", required=True) 22 | NAME = TOKEN.split(":")[0] 23 | 24 | tbot = TelegramClient( 25 | NAME, get_int_key("APP_ID", required=True), get_str_key("APP_HASH", required=True) 26 | ) 27 | 28 | # Telethon 29 | tbot.start(bot_token=TOKEN) 30 | -------------------------------------------------------------------------------- /Shadow/services/telethonuserbot.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import sys 17 | 18 | from telethon import TelegramClient 19 | from telethon.sessions import StringSession 20 | 21 | from Shadow.config import get_int_key, get_str_key 22 | 23 | STRING_SESSION = get_str_key("STRING_SESSION", required=True) 24 | API_ID = get_int_key("APP_ID", required=True) 25 | API_HASH = get_str_key("APP_HASH", required=True) 26 | 27 | ubot = TelegramClient(StringSession(STRING_SESSION), API_ID, API_HASH) 28 | try: 29 | ubot.start() 30 | except BaseException: 31 | print("Userbot Error ! Have you added a STRING_SESSION in deploying??") 32 | sys.exit(1) 33 | -------------------------------------------------------------------------------- /Shadow/stuff/fonts/CaveatBrush-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamOfShadow/Shadow/8ab2cbdb27ae476003c117b8d8a8d731de93f816/Shadow/stuff/fonts/CaveatBrush-Regular.ttf -------------------------------------------------------------------------------- /Shadow/stuff/fonts/MaShanZheng-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamOfShadow/Shadow/8ab2cbdb27ae476003c117b8d8a8d731de93f816/Shadow/stuff/fonts/MaShanZheng-Regular.ttf -------------------------------------------------------------------------------- /Shadow/stuff/fonts/RobotoMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamOfShadow/Shadow/8ab2cbdb27ae476003c117b8d8a8d731de93f816/Shadow/stuff/fonts/RobotoMono-Medium.ttf -------------------------------------------------------------------------------- /Shadow/stuff/fonts/VT323-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamOfShadow/Shadow/8ab2cbdb27ae476003c117b8d8a8d731de93f816/Shadow/stuff/fonts/VT323-Regular.ttf -------------------------------------------------------------------------------- /Shadow/stuff/fonts/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | 17 | def list_all_fonts(): 18 | import glob 19 | from os.path import basename, dirname, isfile 20 | 21 | mod_paths = glob.glob(dirname(__file__) + "/*.ttf") 22 | all_fonts = [ 23 | dirname(f) + "/" + basename(f) 24 | for f in mod_paths 25 | if isfile(f) and f.endswith(".ttf") 26 | ] 27 | return all_fonts 28 | 29 | 30 | ALL_FONTS = sorted(list_all_fonts()) 31 | __all__ = ALL_FONTS + ["ALL_FONTS"] 32 | -------------------------------------------------------------------------------- /Shadow/stuff/logs.txt: -------------------------------------------------------------------------------- 1 | # Logs of Shadow # 2 | -------------------------------------------------------------------------------- /Shadow/stuff/shadow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamOfShadow/Shadow/8ab2cbdb27ae476003c117b8d8a8d731de93f816/Shadow/stuff/shadow.jpg -------------------------------------------------------------------------------- /Shadow/utils/Cache/lol: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Shadow/utils/cached.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | 17 | import asyncio 18 | import functools 19 | import pickle 20 | from typing import Optional, Union 21 | 22 | from Shadow.services.redis import bredis 23 | from Shadow.utils.logger import log 24 | 25 | 26 | async def set_value(key, value, ttl): 27 | value = pickle.dumps(value) 28 | bredis.set(key, value) 29 | if ttl: 30 | bredis.expire(key, ttl) 31 | 32 | 33 | class cached: 34 | def __init__( 35 | self, 36 | ttl: Optional[Union[int, float]] = None, 37 | key: Optional[str] = None, 38 | no_self: bool = False, 39 | ): 40 | self.ttl = ttl 41 | self.key = key 42 | self.no_self = no_self 43 | 44 | def __call__(self, *args, **kwargs): 45 | if not hasattr(self, "func"): 46 | self.func = args[0] 47 | # wrap 48 | functools.update_wrapper(self, self.func) 49 | # return ``cached`` object when function is not being called 50 | return self 51 | return self._set(*args, **kwargs) 52 | 53 | async def _set(self, *args: dict, **kwargs: dict): 54 | key = self.__build_key(*args, **kwargs) 55 | 56 | if bredis.exists(key): 57 | value = pickle.loads(bredis.get(key)) 58 | return value if type(value) is not _NotSet else value.real_value 59 | 60 | result = await self.func(*args, **kwargs) 61 | if result is None: 62 | result = _NotSet() 63 | asyncio.ensure_future(set_value(key, result, ttl=self.ttl)) 64 | log.debug(f"Cached: writing new data for key - {key}") 65 | return result if type(result) is not _NotSet else result.real_value 66 | 67 | def __build_key(self, *args: dict, **kwargs: dict) -> str: 68 | ordered_kwargs = sorted(kwargs.items()) 69 | 70 | new_key = ( 71 | self.key if self.key else (self.func.__module__ or "") + self.func.__name__ 72 | ) 73 | new_key += str(args[1:] if self.no_self else args) 74 | 75 | if ordered_kwargs: 76 | new_key += str(ordered_kwargs) 77 | 78 | return new_key 79 | 80 | async def reset_cache(self, *args, new_value=None, **kwargs): 81 | """ 82 | >>> @cached() 83 | >>> def somefunction(arg): 84 | >>> pass 85 | >>> 86 | >>> [...] 87 | >>> arg = ... # same thing ^^ 88 | >>> await somefunction.reset_cache(arg, new_value='Something') 89 | 90 | :param new_value: new/ updated value to be set [optional] 91 | """ 92 | 93 | key = self.__build_key(*args, **kwargs) 94 | if new_value: 95 | return set_value(key, new_value, ttl=self.ttl) 96 | return bredis.delete(key) 97 | 98 | 99 | class _NotSet: 100 | real_value = None 101 | 102 | def __repr__(self) -> str: 103 | return "NotSet" 104 | -------------------------------------------------------------------------------- /Shadow/utils/channel_logs.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import html 17 | 18 | from Shadow import bot 19 | from Shadow.config import get_int_key 20 | from Shadow.utils.logger import log 21 | 22 | 23 | async def channel_log(msg, info_log=True): 24 | chat_id = get_int_key("LOGS_CHANNEL_ID") 25 | if info_log: 26 | log.info(msg) 27 | 28 | await bot.send_message(chat_id, html.escape(msg, quote=False)) 29 | -------------------------------------------------------------------------------- /Shadow/utils/db_structure_migrator.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import asyncio 17 | import time 18 | from importlib import import_module 19 | 20 | from Shadow import OWNER_ID, bot 21 | from Shadow.services.mongo import mongodb 22 | from Shadow.utils.logger import log 23 | from Shadow.versions import DB_STRUCTURE_VER 24 | 25 | 26 | async def notify_bot_owner(old_ver, new_ver): 27 | await bot.send_message( 28 | OWNER_ID, 29 | f"Shadow's database structure was updated from {old_ver} to {new_ver}", 30 | ) 31 | 32 | 33 | # TODO: Logs channel 34 | 35 | log.debug("Checking on database structure update...") 36 | 37 | if not (data := mongodb.db_structure.find_one({"db_ver": {"$exists": True}})): 38 | log.info("Your database is empty! Creating database structure key...") 39 | mongodb.db_structure.insert_one({"db_ver": DB_STRUCTURE_VER}) 40 | log.info("Database structure version is: " + str(DB_STRUCTURE_VER)) 41 | else: 42 | curr_ver = data["db_ver"] 43 | log.info("Your database structure version is: " + str(curr_ver)) 44 | if DB_STRUCTURE_VER > curr_ver: 45 | log.error("Your database is old! Waiting 20 seconds till update...") 46 | log.info("Press CTRL + C to cancel!") 47 | time.sleep(20) 48 | log.debug("Trying to update database structure...") 49 | log.info("---------------------------------------------") 50 | log.info("Your current database structure version: " + str(curr_ver)) 51 | log.info("New database structure version: " + str(DB_STRUCTURE_VER)) 52 | log.info("---------------------------------------------") 53 | old_ver = curr_ver 54 | while curr_ver < DB_STRUCTURE_VER: 55 | new_ver = curr_ver + 1 56 | log.info(f"Trying update to {str(new_ver)}...") 57 | 58 | log.debug("Importing: Shadow.db." + str(new_ver)) 59 | import_module("Shadow.db." + str(new_ver)) 60 | 61 | curr_ver += 1 62 | mongodb.db_structure.update_one( 63 | {"db_ver": curr_ver - 1}, {"$set": {"db_ver": curr_ver}} 64 | ) 65 | 66 | log.warn(f"Database update done to {str(curr_ver)} successfully!") 67 | log.debug("Let's notify the bot owner") 68 | loop = asyncio.get_event_loop() 69 | bot_info = loop.run_until_complete(notify_bot_owner(old_ver, curr_ver)) 70 | log.info("Rescue normal bot startup...") 71 | else: 72 | log.debug("No database structure updates found, skipping!") 73 | -------------------------------------------------------------------------------- /Shadow/utils/exit_gracefully.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import os 17 | import signal 18 | 19 | from Shadow.services.redis import redis 20 | from Shadow.utils.logger import log 21 | 22 | 23 | def exit_gracefully(signum, frame): 24 | log.warning("Bye!") 25 | 26 | try: 27 | redis.save() 28 | except Exception: 29 | log.error("Exiting immediately!") 30 | os.kill(os.getpid(), signal.SIGUSR1) 31 | 32 | 33 | # Signal exit 34 | log.info("Setting exit_gracefully task...") 35 | signal.signal(signal.SIGINT, exit_gracefully) 36 | -------------------------------------------------------------------------------- /Shadow/utils/filters/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import glob 17 | import os.path 18 | 19 | 20 | def list_all_filters(): 21 | mod_paths = glob.glob(os.path.dirname(__file__) + "/*.py") 22 | all_filters = [ 23 | os.path.basename(f)[:-3] 24 | for f in mod_paths 25 | if os.path.isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") 26 | ] 27 | 28 | return all_filters 29 | 30 | 31 | ALL_FILTERS = sorted(list(list_all_filters())) 32 | 33 | __all__ = ALL_FILTERS + ["ALL_FILTERS"] 34 | -------------------------------------------------------------------------------- /Shadow/utils/filters/chat_status.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from aiogram import types 17 | from aiogram.dispatcher.filters import BoundFilter 18 | 19 | from Shadow import dp 20 | 21 | 22 | class OnlyPM(BoundFilter): 23 | key = "only_pm" 24 | 25 | def __init__(self, only_pm): 26 | self.only_pm = only_pm 27 | 28 | async def check(self, message: types.Message): 29 | if message.from_user.id == message.chat.id: 30 | return True 31 | 32 | 33 | class OnlyGroups(BoundFilter): 34 | key = "only_groups" 35 | 36 | def __init__(self, only_groups): 37 | self.only_groups = only_groups 38 | 39 | async def check(self, message: types.Message): 40 | if not message.from_user.id == message.chat.id: 41 | return True 42 | 43 | 44 | dp.filters_factory.bind(OnlyPM) 45 | dp.filters_factory.bind(OnlyGroups) 46 | -------------------------------------------------------------------------------- /Shadow/utils/filters/message_status.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 - 2020 Mr.Yacha. All rights reserved. Source code available under the AGPL. 2 | # Copyright (C) 2019 Aiogram 3 | # 4 | # This file is part of Shadow Bot. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from aiogram import types 20 | from aiogram.dispatcher.filters import BoundFilter 21 | 22 | from Shadow import dp 23 | 24 | 25 | class NotForwarded(BoundFilter): 26 | key = "not_forwarded" 27 | 28 | def __init__(self, not_forwarded): 29 | self.not_forwarded = not_forwarded 30 | 31 | async def check(self, message: types.Message): 32 | if "forward_from" not in message: 33 | return True 34 | 35 | 36 | class NoArgs(BoundFilter): 37 | key = "no_args" 38 | 39 | def __init__(self, no_args): 40 | self.no_args = no_args 41 | 42 | async def check(self, message: types.Message): 43 | if not len(message.text.split(" ")) > 1: 44 | return True 45 | 46 | 47 | class HasArgs(BoundFilter): 48 | key = "has_args" 49 | 50 | def __init__(self, has_args): 51 | self.has_args = has_args 52 | 53 | async def check(self, message: types.Message): 54 | if len(message.text.split(" ")) > 1: 55 | return True 56 | 57 | 58 | class CmdNotMonospaced(BoundFilter): 59 | key = "cmd_not_mono" 60 | 61 | def __init__(self, cmd_not_mono): 62 | self.cmd_not_mono = cmd_not_mono 63 | 64 | async def check(self, message: types.Message): 65 | if ( 66 | message.entities 67 | and message.entities[0]["type"] == "code" 68 | and message.entities[0]["offset"] < 1 69 | ): 70 | return False 71 | return True 72 | 73 | 74 | dp.filters_factory.bind(NotForwarded) 75 | dp.filters_factory.bind(NoArgs) 76 | dp.filters_factory.bind(HasArgs) 77 | dp.filters_factory.bind(CmdNotMonospaced) 78 | -------------------------------------------------------------------------------- /Shadow/utils/filters/user_status.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | from aiogram import types 17 | from aiogram.dispatcher.filters import BoundFilter 18 | 19 | from Shadow import OPERATORS, dp 20 | from Shadow.config import get_int_key 21 | from Shadow.modules.utils.language import get_strings_dec 22 | from Shadow.modules.utils.user_details import is_user_admin 23 | from Shadow.services.mongo import mongodb 24 | 25 | 26 | class IsAdmin(BoundFilter): 27 | key = "is_admin" 28 | 29 | def __init__(self, is_admin): 30 | self.is_admin = is_admin 31 | 32 | @get_strings_dec("global") 33 | async def check(self, event, strings): 34 | 35 | if hasattr(event, "message"): 36 | chat_id = event.message.chat.id 37 | else: 38 | chat_id = event.chat.id 39 | 40 | if not await is_user_admin(chat_id, event.from_user.id): 41 | task = event.answer if hasattr(event, "message") else event.reply 42 | await task(strings["u_not_admin"]) 43 | return False 44 | return True 45 | 46 | 47 | class IsOwner(BoundFilter): 48 | key = "is_owner" 49 | 50 | def __init__(self, is_owner): 51 | self.is_owner = is_owner 52 | 53 | async def check(self, message: types.Message): 54 | if message.from_user.id == get_int_key("OWNER_ID"): 55 | return True 56 | 57 | 58 | class IsOP(BoundFilter): 59 | key = "is_op" 60 | 61 | def __init__(self, is_op): 62 | self.is_owner = is_op 63 | 64 | async def check(self, message: types.Message): 65 | if message.from_user.id in OPERATORS: 66 | return True 67 | 68 | 69 | class NotGbanned(BoundFilter): 70 | key = "not_gbanned" 71 | 72 | def __init__(self, not_gbanned): 73 | self.not_gbanned = not_gbanned 74 | 75 | async def check(self, message: types.Message): 76 | check = mongodb.blacklisted_users.find_one({"user": message.from_user.id}) 77 | if not check: 78 | return True 79 | 80 | 81 | dp.filters_factory.bind(IsAdmin) 82 | dp.filters_factory.bind(IsOwner) 83 | dp.filters_factory.bind(NotGbanned) 84 | dp.filters_factory.bind(IsOP) 85 | -------------------------------------------------------------------------------- /Shadow/utils/logger.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import logging 17 | 18 | from loguru import logger 19 | 20 | 21 | class InterceptHandler(logging.Handler): 22 | LEVELS_MAP = { 23 | logging.CRITICAL: "CRITICAL", 24 | logging.ERROR: "ERROR", 25 | logging.WARNING: "WARNING", 26 | logging.INFO: "INFO", 27 | logging.DEBUG: "DEBUG", 28 | } 29 | 30 | def _get_level(self, record): 31 | return self.LEVELS_MAP.get(record.levelno, record.levelno) 32 | 33 | def emit(self, record): 34 | logger_opt = logger.opt( 35 | depth=6, exception=record.exc_info, ansi=True, lazy=True 36 | ) 37 | logger_opt.log(self._get_level(record), record.getMessage()) 38 | 39 | 40 | logging.basicConfig(handlers=[InterceptHandler()], level=logging.INFO) 41 | log = logging.getLogger(__name__) 42 | logger.add( 43 | "logs/shadow.log", 44 | rotation="1 d", 45 | compression="tar.xz", 46 | backtrace=True, 47 | diagnose=True, 48 | level="INFO", 49 | ) 50 | log.info("Enabled logging into shadow.log file.") 51 | -------------------------------------------------------------------------------- /Shadow/utils/sentry.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import sentry_sdk 17 | from sentry_sdk.integrations.redis import RedisIntegration 18 | 19 | from Shadow.config import get_str_key 20 | from Shadow.utils.logger import log 21 | 22 | log.info("Starting sentry.io integraion...") 23 | 24 | sentry_sdk.init(get_str_key("SENTRY_API_KEY"), integrations=[RedisIntegration()]) 25 | -------------------------------------------------------------------------------- /Shadow/utils/term.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | import subprocess 17 | 18 | from Shadow.utils.logger import log 19 | 20 | 21 | def term(cmd): 22 | p = subprocess.Popen( 23 | cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT 24 | ) 25 | if p.stderr: 26 | log.error(p.stderr.readlines()) 27 | return p.stdout.readlines() 28 | -------------------------------------------------------------------------------- /Shadow/versions.py: -------------------------------------------------------------------------------- 1 | # This file is part of Shadow (Telegram Bot) 2 | 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU Affero General Public License as 5 | # published by the Free Software Foundation, either version 3 of the 6 | # License, or (at your option) any later version. 7 | 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU Affero General Public License for more details. 12 | 13 | # You should have received a copy of the GNU Affero General Public License 14 | # along with this program. If not, see . 15 | 16 | SHADOW_VERSION = "v3.0" 17 | DB_STRUCTURE_VER = 8 18 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: Shadow/localization/en.yaml 3 | translation: /**/Shadow/localization/%locale_with_underscore%.yaml 4 | -------------------------------------------------------------------------------- /data/bot_conf.yaml: -------------------------------------------------------------------------------- 1 | # Shadow Bot example config 2 | 3 | 4 | # Basic 5 | TOKEN: "" 6 | 7 | # Get they in https://my.telegram.org/ 8 | APP_ID: "" 9 | APP_HASH: "" 10 | 11 | #Generate a telethon str session with https://repl.it/@SpEcHiDe/GenerateStringSession (telethon one) 12 | STRING_SESSION: "" 13 | 14 | 15 | MONGO_URI: "mongodb+srv://dfsdfX:dfsfdX@cluster0.df4j.mongodb.net/myFirstDatabase?retryWrites=true&w=majority" 16 | 17 | MONGO_URI2: "" 18 | MONGO_PORT: 27017 19 | MONGO_DB': 'Shadow' 20 | 21 | API_PORT': 8080 22 | 23 | REDIS_URI: "redis-1324.c234.us-east-1-4.ec4.cloud.redislabs.com" 24 | REDIS_PORT: 15514 25 | REDIS_PASS: "sdfgg" 26 | TEMP_MAIL_KEY: "" 27 | OWNER_ID: 1141839926 28 | OPERATORS: [1141839926, 00000000] 29 | SUPPORT_CHAT: -000000000 30 | LYDIA_API_KEY: "" 31 | 32 | DATABASE_URL: "" 33 | VIRUS_API_KEY: "" 34 | TIME_API_KEY: "" 35 | REM_BG_API_KEY: "" 36 | ARQ_API: "" 37 | 38 | # Advanced 39 | SW_API: "" 40 | IBM_WATSON_CRED_URL: "" 41 | IBM_WATSON_CRED_PASSWORD: "" 42 | OPENWEATHERMAP_ID: "" 43 | WOLFRAM_ID: "" 44 | DEBUG_MODE: False 45 | APROOVE_DB: "mongodb+srv://dfsdfX:dfsfdX@cluster0.df4j.mongodb.net/myFirstDatabase?retryWrites=true&w=majority" 46 | LOAD_MODULES: True 47 | LOGS_CHANNEL_ID: -000000000 48 | ALLOW_FORWARDS_COMMANDS: False 49 | ALLOW_EXCEL: False 50 | SENTRY_API_KEY: "https://9aab1a7028314659b01b950189bfdcc8@o1007942.ingest.sentry.io/5971260" 51 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 |  2 | echo " 3 | *********** START DEPLOYING *********** 4 | 5 | Shadow - Base Aiogram 6 | (C) 2020-2021 by @TeamOfShadow 7 | Support Chat is @ShadowSupport_Official 8 | 9 | *************************************** 10 | " 11 | update_and_install_packages () { 12 | apt -qq update -y 13 | apt -qq install -y --no-install-recommends \ 14 | git \ 15 | ffmpeg \ 16 | mediainfo \ 17 | unzip \ 18 | wget \ 19 | gifsicle 20 | } 21 | 22 | install_helper_packages () { 23 | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && apt -fqqy install ./google-chrome-stable_current_amd64.deb && rm google-chrome-stable_current_amd64.deb 24 | wget https://chromedriver.storage.googleapis.com/88.0.4324.96/chromedriver_linux64.zip && unzip chromedriver_linux64.zip && chmod +x chromedriver && mv -f chromedriver /usr/bin/ && rm chromedriver_linux64.zip 25 | wget -O opencv.zip https://github.com/opencv/opencv/archive/master.zip && unzip opencv.zip && mv -f opencv-master /usr/bin/ && rm opencv.zip 26 | wget https://people.eecs.berkeley.edu/~rich.zhang/projects/2016_colorization/files/demo_v2/colorization_release_v2.caffemodel -P ./bot_utils_files/ai_helpers/ 27 | } 28 | 29 | ech_final () { 30 | echo " 31 | /---------------------------------------------------\ 32 | / _____________________________________________ \ 33 | / | | \ 34 | / | > Deployed Successfully < | \ 35 | / |_____________________________________________| \ 36 | / \ 37 | / \ 38 | / ******************************* \ 39 | / * |S| |H| |A| |D| |O| |W| * \ 40 | / ******************************* \ 41 | / \ 42 | / (C) 2020-2021 by @TeamOfShadow \ 43 | / \ 44 | / Support Chat is @ShadowSupport_Official \ 45 | / \ 46 | /---------------------------------------------------\ 47 | / \ 48 | / Greetings from Dev Team :) \ 49 | / \ 50 | /___________________________________________________\ 51 | 52 | 53 | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 54 | | | 55 | | < Thanks for deploying Shadow > | 56 | | | 57 | |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| 58 | 59 | " 60 | } 61 | 62 | _run_all () { 63 | UPDATE 64 | install_helper_packages 65 | pip3 install –upgrade pip 66 | pip3 install --no-cache-dir -r requirements.txt 67 | ech_final 68 | } 69 | 70 | _run_all 71 | -------------------------------------------------------------------------------- /fortune.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import random 3 | import re 4 | import sys 5 | from optparse import OptionParser 6 | 7 | # --------------------------------------------------------------------------- 8 | # Exports 9 | # --------------------------------------------------------------------------- 10 | 11 | __all__ = ["main", "get_random_fortune"] 12 | 13 | # Info about the module 14 | __version__ = "1.1.0" 15 | __author__ = "Brian M. Clapper" 16 | __email__ = "bmc@clapper.org" 17 | __url__ = "http://software.clapper.org/fortune/" 18 | __copyright__ = "2008-2019 Brian M. Clapper" 19 | __license__ = "BSD-style license" 20 | 21 | # --------------------------------------------------------------------------- 22 | # Functions 23 | # --------------------------------------------------------------------------- 24 | 25 | 26 | def _random_int(start, end): 27 | try: 28 | # Use SystemRandom, if it's available, since it's likely to have 29 | # more entropy. 30 | r = random.SystemRandom() 31 | except BaseException: 32 | r = random 33 | 34 | return r.randint(start, end) 35 | 36 | 37 | def _read_fortunes(fortune_file): 38 | with codecs.open(fortune_file, mode="r", encoding="utf-8") as f: 39 | contents = f.read() 40 | 41 | lines = [line.rstrip() for line in contents.split("\n")] 42 | 43 | delim = re.compile(r"^%$") 44 | 45 | fortunes = [] 46 | cur = [] 47 | 48 | def save_if_nonempty(buf): 49 | fortune = "\n".join(buf) 50 | if fortune.strip(): 51 | fortunes.append(fortune) 52 | 53 | for line in lines: 54 | if delim.match(line): 55 | save_if_nonempty(cur) 56 | cur = [] 57 | continue 58 | 59 | cur.append(line) 60 | 61 | if cur: 62 | save_if_nonempty(cur) 63 | 64 | return fortunes 65 | 66 | 67 | def get_random_fortune(fortune_file): 68 | fortunes = list(_read_fortunes(fortune_file)) 69 | randomRecord = _random_int(0, len(fortunes) - 1) 70 | return fortunes[randomRecord] 71 | 72 | 73 | def main(): 74 | usage = "Usage: %prog [OPTIONS] [fortune_file]" 75 | arg_parser = OptionParser(usage=usage) 76 | arg_parser.add_option( 77 | "-V", 78 | "--version", 79 | action="store_true", 80 | dest="show_version", 81 | help="Show version and exit.", 82 | ) 83 | arg_parser.epilog = ( 84 | "If fortune_file is omitted, fortune looks at the " 85 | "FORTUNE_FILE environment variable for the path." 86 | ) 87 | 88 | options, args = arg_parser.parse_args(sys.argv) 89 | if len(args) == 2: 90 | fortune_file = args[1] 91 | 92 | else: 93 | try: 94 | fortune_file = "notes.txt" 95 | except KeyError: 96 | print("Missing fortune file.", file=sys.stderr) 97 | print(usage, file=sys.stderr) 98 | sys.exit(1) 99 | 100 | try: 101 | if options.show_version: 102 | print("fortune, version {}".format(__version__)) 103 | else: 104 | print(get_random_fortune(fortune_file)) 105 | except ValueError as msg: 106 | print(msg, file=sys.stderr) 107 | sys.exit(1) 108 | 109 | 110 | if __name__ == "__main__": 111 | main() 112 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # General 2 | telethon 3 | pyrogram==1.4.16 4 | aiogram 5 | 6 | # DBs 7 | redis 8 | aioredis # Redis memery storage from aiogram 9 | pymongo==3.12.1 10 | motor 11 | sqlalchemy==1.3.20 # For old modules 12 | dnspython # needed to connect to cloud MongoDB instances 13 | 14 | # Optional deps to make bot faster 15 | aiohttp[speedups] 16 | cryptg 17 | cryptography 18 | 19 | # Other 20 | jikanpy 21 | envparse 22 | hypercorn 23 | aiocron 24 | apscheduler 25 | requests 26 | python-rapidjson 27 | PyYAML>5.0 28 | coloredlogs 29 | loguru 30 | # contextlib 31 | babel 32 | captcha 33 | async-timeout 34 | regex 35 | bs4 36 | lxml 37 | spamwatch 38 | httpx[http2] 39 | wikipedia 40 | coffeehouse 41 | PyDictionary==2.0.1 42 | google-trans-new==1.1.9 43 | hachoir 44 | telegraph 45 | faker 46 | gtts 47 | geopy 48 | tswift 49 | lyricsgenius 50 | sentry_sdk 51 | lxml 52 | html5lib 53 | feedparser 54 | tldextract 55 | dateparser 56 | twistdl 57 | 58 | #Pyrogram 59 | tgcrypto 60 | uvloop 61 | 62 | #Old DB Processes 63 | psycopg2-binary==2.8.6 64 | 65 | #YTDL 66 | youtube_dl 67 | youtube_search_python 68 | 69 | #Search 70 | bs4 71 | html2text 72 | bing_image_downloader 73 | search_engine_parser 74 | youtube_search 75 | 76 | #Memes 77 | selenium 78 | zalgo_text 79 | cowpy 80 | fontTools 81 | nltk 82 | emoji 83 | 84 | #Deezer 85 | wget 86 | asyncio 87 | 88 | #Markup 89 | Markdown>=3.3.4 90 | 91 | #profanity (Credits to Julia) 92 | better_profanity 93 | textblob 94 | nudepy 95 | 96 | #ImageEditor 97 | glitch_this 98 | NumPy 99 | opencv-python-headless 100 | Pillow 101 | 102 | #Bassboost 103 | pydub 104 | 105 | #Lel 106 | cloudmersive_virus_api_client==3.0.1 107 | chromedriver==2.24.1 108 | 109 | #Updator 110 | gitpython==3.1.11 111 | heroku3==4.2.3 112 | 113 | pykeyboard 114 | countryinfo 115 | flag 116 | Python_ARQ 117 | googletrans==4.0.0-rc1 118 | pyromod 119 | aiofiles 120 | odmantic 121 | 122 | 123 | # Lol 124 | Skem==0.3.1 125 | tzlocal==2.0 126 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.5 2 | -------------------------------------------------------------------------------- /string_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # (c) https://t.me/TelethonChat/37677 4 | # This Source Code Form is subject to the terms of the GNU 5 | # General Public License, v.3.0. If a copy of the GPL was not distributed with this 6 | # file, You can obtain one at https://www.gnu.org/licenses/gpl-3.0.en.html. 7 | 8 | try: 9 | from telethon.sessions import StringSession 10 | from telethon.sync import TelegramClient 11 | except BaseException: 12 | print("Telethon Not Found. Installing Now.") 13 | import os 14 | 15 | os.system("pip3 install telethon") 16 | from telethon.sessions import StringSession 17 | from telethon.sync import TelegramClient 18 | ok = """ 19 | ______ _____ ____ ___ __ _ __ ___ ____ _____ ______ 20 | | | 21 | | Copyright (C) 2021 @TeamOfShadow | 22 | | Copyright (C) 2021 @Mr_Shadow_Robot | 23 | | Copyright (C) 2020-2021 @DeshadeethThisarana | 24 | |_____ _____ ____ ___ __ _ __ ___ ____ _____ _____| 25 | 26 | Enter your details here. 27 | After filling it check your saved messages 28 | """ 29 | print(ok) 30 | 31 | APP_ID = int(input("Enter APP ID here: \n")) 32 | API_HASH = input("Enter API HASH here: \n") 33 | 34 | text = "THIS IS YOUR STRING SESSION" 35 | text += "Join @ShadowSupport_Official For More Support." 36 | 37 | client = TelegramClient(StringSession(), APP_ID, API_HASH) 38 | with client: 39 | session_str = client.session.save() 40 | client.send_message(f"`{session_str}`") 41 | client.reply_text(text) 42 | print("⬆ Please Check Your Telegram Saved Message For Your String.") 43 | --------------------------------------------------------------------------------