├── .deepsource.toml ├── .dockerignore ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── app.json ├── assets └── arial.ttf ├── heroku.yml ├── nezuko ├── __init__.py ├── __main__.py ├── core │ ├── decorators │ │ ├── errors.py │ │ ├── misc.py │ │ └── permissions.py │ ├── filters.py │ ├── keyboard.py │ ├── sections.py │ ├── tasks.py │ └── types │ │ ├── InlineQueryResult.py │ │ └── __init__.py ├── modules │ ├── __init__.py │ ├── __main__.py │ ├── admin.py │ ├── admin_misc.py │ ├── animals.py │ ├── anime.py │ ├── anime_picks.py │ ├── antiservice.py │ ├── autocorrect.py │ ├── blacklist.py │ ├── blacklist_chat.py │ ├── carbon.py │ ├── cat.py │ ├── chat_watcher.py │ ├── chatbot.py │ ├── couple.py │ ├── cricinfo.py │ ├── crypto.py │ ├── dice.py │ ├── filters.py │ ├── flood.py │ ├── fun.py │ ├── global_stats.py │ ├── greetings.py │ ├── imdb.py │ ├── img_pdf.py │ ├── info.py │ ├── inline.py │ ├── karma.py │ ├── locks.py │ ├── misc.py │ ├── mongo_backup.py │ ├── music.py │ ├── notes.py │ ├── parse_preview.py │ ├── paste.py │ ├── pipes.py │ ├── proxy.py │ ├── quotly.py │ ├── reddit.py │ ├── regex.py │ ├── repo.py │ ├── reverse.py │ ├── rss.py │ ├── stickers.py │ ├── sudo.py │ ├── sudoers.py │ ├── telegraph.py │ ├── tts.py │ ├── urltools.py │ └── webss.py └── utils │ ├── __init__.py │ ├── constants.py │ ├── dbfunctions.py │ ├── downloader.py │ ├── files.py │ ├── filter_groups.py │ ├── formatter.py │ ├── fun_strings.py │ ├── functions.py │ ├── http.py │ ├── inlinefuncs.py │ ├── json_prettify.py │ ├── misc.py │ ├── pastebin.py │ ├── read_lines.py │ ├── rss.py │ ├── runs.txt │ └── stickerset.py ├── requirements.txt ├── sample_config.env └── sample_config.py /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "python" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | runtime_version = "3.x.x" 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .webm 2 | *.mp3 3 | *.mp4 4 | *.webp 5 | *.weba 6 | *.part 7 | *.ytdl 8 | final.png 9 | background.png 10 | temp.png 11 | # python 12 | venv/ 13 | __pycache__/ 14 | # tgbot 15 | config.py 16 | *.session 17 | *.session-journal 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.env 2 | *.m4a 3 | *.apk 4 | spam_mod.py 5 | *.c 6 | dump 7 | exec 8 | *.out 9 | __pycache__ 10 | *.opus 11 | unknown_errors.txt 12 | *.jpg 13 | token.py 14 | *.raw 15 | images/ 16 | # Byte-compiled / optimized / DLL files 17 | __pycache__/ 18 | *.py[cod] 19 | *.png 20 | *.mp4 21 | *.webm 22 | *$py.class 23 | *.mp3 24 | . 25 | # C extensions 26 | *.so 27 | config.py 28 | # Distribution / packaging 29 | .Python 30 | build/ 31 | develop-eggs/ 32 | dist/ 33 | downloads/ 34 | eggs/ 35 | .eggs/ 36 | lib/ 37 | lib64/ 38 | parts/ 39 | sdist/ 40 | var/ 41 | wheels/ 42 | pip-wheel-metadata/ 43 | share/python-wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # PyInstaller 50 | # Usually these files are written by a python script from a template 51 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 52 | *.manifest 53 | *.spec 54 | 55 | # Installer logs 56 | pip-log.txt 57 | pip-delete-this-directory.txt 58 | 59 | # Unit test / coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .nox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | *.py,cover 70 | .hypothesis/ 71 | .pytest_cache/ 72 | 73 | # Translations 74 | *.mo 75 | *.pot 76 | 77 | # Django stuff: 78 | *.log 79 | local_settings.py 80 | db.sqlite3 81 | db.sqlite3-journal 82 | 83 | # Flask stuff: 84 | instance/ 85 | .webassets-cache 86 | 87 | # Scrapy stuff: 88 | .scrapy 89 | 90 | # Sphinx documentation 91 | docs/_build/ 92 | 93 | # PyBuilder 94 | target/ 95 | 96 | # Jupyter Notebook 97 | .ipynb_checkpoints 98 | 99 | # IPython 100 | profile_default/ 101 | ipython_config.py 102 | 103 | # pyenv 104 | .python-version 105 | 106 | # pipenv 107 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 108 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 109 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 110 | # install all needed dependencies. 111 | #Pipfile.lock 112 | 113 | # celery beat schedule file 114 | celerybeat-schedule 115 | 116 | # SageMath parsed files 117 | *.sage.py 118 | 119 | # Environments 120 | .env 121 | .venv 122 | env/ 123 | venv/ 124 | ENV/ 125 | env.bak/ 126 | venv.bak/ 127 | 128 | # Spyder project settings 129 | .spyderproject 130 | .spyproject 131 | 132 | # Rope project settings 133 | .ropeproject 134 | 135 | # mkdocs documentation 136 | /site 137 | 138 | # mypy 139 | .mypy_cache/ 140 | .dmypy.json 141 | dmypy.json 142 | 143 | # Pyre type checker 144 | .pyre/ 145 | *.ini 146 | *.pyc 147 | *.session 148 | *.session-journal 149 | 150 | # vim 151 | [._]*.sw[a-p] 152 | 153 | #others 154 | neofetch.txt 155 | error.log 156 | permissions.json 157 | .vscode 158 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at thehamkercat@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ 93 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM archlinux/archlinux:latest 2 | 3 | # Install dependencies 4 | RUN pacman -Syu --noconfirm && pacman -S --noconfirm git wget libxml2 libxslt zip python-pip ffmpeg 5 | 6 | # Downloading mongodb tools 7 | RUN wget https://fastdl.mongodb.org/tools/db/mongodb-database-tools-ubuntu2004-x86_64-100.5.2.tgz && tar -xf mongodb*.tgz && \ 8 | mv mongodb-database-tools-ubuntu2004-x86_64-100.5.2/bin/* /bin/ && \ 9 | rm -rf mongodb-database-tools-ubuntu2004-x86_64-100.5.2* 10 | 11 | # Changing working directory and it's permission 12 | WORKDIR /app 13 | RUN chmod 777 /app 14 | 15 | # Install python dependencies 16 | COPY requirements.txt . 17 | RUN pip3 install --no-cache-dir -U -r requirements.txt 18 | 19 | # Copy files to the working directory 20 | COPY . . 21 | 22 | # Run the application 23 | CMD ["python3","-m","nezuko"] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 TheHamkerCat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: python3 -m nezuko 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | ✨ NezukoBot ✨ 3 |

4 | 5 |

:warning: THIS PROJECT IS DEAD. DO NOT USE IT, MIGHT RUN INTO UNKNOWN PROBLEMS.

6 | 7 |

8 | Telegram Group Manager Bot Written In Python Using Pyrogram. 9 |

10 |

11 | :exclamation: Please star this project before using it. 12 |

13 | 14 |

15 | LICENSE 16 | Contributors 17 | Repository Size
18 | Python Version 19 | Issues 20 | Forks 21 | Stars 22 |

23 | 24 |

25 | Ready to use method 26 |

27 | 28 |

29 | A ready-to-use running instance of this bot can be found on Telegram
30 | NezukoBot 31 |

32 | 33 |

34 | ⇝ Requirements ⇜ 35 |

36 | 37 |

38 | Python3.9 | 39 | Telegram API Key | 40 | Telegram Bot Token | 41 | MongoDB URI 42 |

43 | 44 |

45 | ⇝ Install Locally Or On A VPS ⇜ 46 |

47 | 48 | ```console 49 | git clone https://github.com/rozari0/NezukoBot 50 | cd NezukoBot 51 | pip3 install -U -r requirements.txt 52 | cp sample_config.env config.env 53 | ``` 54 | 55 |

56 | Edit config.env with your own values 57 |

58 | 59 |

60 | ⇝ Run Directly ⇜ 61 |

62 | 63 | ```console 64 | python3 -m nezuko 65 | ``` 66 | 67 |

68 | ⇝ Docker ⇜ 69 |

70 | 71 | ```console 72 | git clone https://github.com/rozari0/NezukoBot 73 | cd NezukoBot 74 | cp sample_config.env config.env 75 | ``` 76 | 77 |

78 | Edit config.env with your own values 79 |

80 | 81 | ```console 82 | sudo docker build . -t nezuko 83 | sudo docker run nezuko 84 | ``` 85 | 86 |

87 | ⇝ Write new modules ⇜ 88 |

89 | 90 | ```py 91 | # Add license text here, get it from below 92 | 93 | from nezuko import app # This is bot's client 94 | from pyrogram import filters # pyrogram filters 95 | ... 96 | 97 | 98 | # For /help menu 99 | __MODULE__ = "Module Name" 100 | __HELP__ = "Module help message" 101 | 102 | 103 | @app.on_message(filters.command("start")) 104 | async def some_function(_, message): 105 | await message.reply_text("I'm already up!!") 106 | 107 | # Many useful functions are in, nezuko/utils/, nezuko, and nezuko/core/ 108 | ``` 109 | 110 |

111 | And put that file in nezuko/modules/, restart and test your bot. 112 |

113 | 114 | # Credits 115 | 116 | Thanks to: 117 | - [`TheHamkerCat`](https://github.com/TheHamkerCat) for build up of this bot from scratch and ARQ api. :3 118 | - [`delivrance`](https://github.com/delivrance) for pyrogram. 119 | - [`rozari0`](https://github.com/rozari0) for some extra features & fixes. 120 | 121 | And many more people who aren't mentioned here, but can be found in [Contributors](https://github.com/rozari0/NezukoBot/graphs/contributors). 122 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Nezuko", 3 | "description": "Telegram Group Management Bot Written In Python Using Pyrogram.", 4 | "repository": "https://github.com/rozari0/NezukoBot", 5 | "logo": "https://upload.wikimedia.org/wikipedia/commons/c/c3/Python-logo-notext.svg", 6 | "keywords": [ 7 | "python3", 8 | "telegram", 9 | "bot", 10 | "youtube", 11 | "telegram-bot", 12 | "pyrogram" 13 | ], 14 | "repository": "https://github.com/rozari0/NezukoBot", 15 | "stack": "container", 16 | "env": { 17 | "BOT_TOKEN": { 18 | "description": "Obtain a Telegram bot token by contacting @BotFather", 19 | "required": true 20 | }, 21 | "API_ID": { 22 | "description": "API_ID of your Telegram Account my.telegram.org/apps", 23 | "required": true 24 | }, 25 | "API_HASH": { 26 | "description": "API_HASH of your Telegram Account my.telegram.org/apps", 27 | "required": true 28 | }, 29 | "SUDO_USERS_ID": { 30 | "description": "Sudo users have full access to everythin, don't trust anyone... ex:- 123456 654311 123456", 31 | "required": true 32 | }, 33 | "LOG_GROUP_ID": { 34 | "description": "For logs channel to note down important bot level events, recommend to make this public. ex: '-123456'", 35 | "required": true 36 | }, 37 | "GBAN_LOG_GROUP_ID": { 38 | "description": "gban logs. ex: '-123456'", 39 | "required": true 40 | }, 41 | "WELCOME_DELAY_KICK_SEC": { 42 | "value": "300", 43 | "required": true 44 | }, 45 | "MONGO_URL": { 46 | "description": "Get From Here. https://telegra.ph/How-To-get-Mongodb-URI-04-06", 47 | "required": true 48 | }, 49 | "ARQ_API_URL": { 50 | "description": "For Music Downloading And Many More Things... Don't change this value", 51 | "value": "https://thearq.tech", 52 | "required": true 53 | }, 54 | "MESSAGE_DUMP_CHAT": { 55 | "description": "Chat_id of the group where useless things will go", 56 | "required": true 57 | }, 58 | "ARQ_API_KEY": { 59 | "description": "Get this from @ARQRobot.", 60 | "required": true 61 | }, 62 | "RSS_DELAY": { 63 | "description": "Delay in which RSS will send updates in chat", 64 | "value": "300", 65 | "required": true 66 | }, 67 | "OWNER_ID":{ 68 | "description": "Owner id of bot", 69 | "required":true 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /assets/arial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rozari0/NezukoBot/e60d2ab0ea08ed9dc940a8aca21930cfe65e7c81/assets/arial.ttf -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | docker: 3 | worker: Dockerfile 4 | -------------------------------------------------------------------------------- /nezuko/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa F405 2 | """ 3 | MIT License 4 | 5 | Copyright (c) 2021 TheHamkerCat 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | import asyncio 26 | import time 27 | from inspect import getfullargspec 28 | from os import path 29 | 30 | from aiohttp import ClientSession 31 | from motor.motor_asyncio import AsyncIOMotorClient as MongoClient 32 | from pyrogram import Client 33 | from pyrogram.types import Message 34 | from pyromod import listen 35 | from Python_ARQ import ARQ 36 | from telegraph import Telegraph 37 | 38 | from sample_config import * 39 | 40 | GBAN_LOG_GROUP_ID = GBAN_LOG_GROUP_ID 41 | SUDOERS = SUDO_USERS_ID 42 | WELCOME_DELAY_KICK_SEC = WELCOME_DELAY_KICK_SEC 43 | LOG_GROUP_ID = LOG_GROUP_ID 44 | MESSAGE_DUMP_CHAT = MESSAGE_DUMP_CHAT 45 | MOD_LOAD = [] 46 | MOD_NOLOAD = [] 47 | bot_start_time = time.time() 48 | 49 | # MongoDB client 50 | print("[INFO]: INITIALIZING DATABASE") 51 | mongo_client = MongoClient(MONGO_URL) 52 | db = mongo_client.nezuko 53 | 54 | 55 | async def load_sudoers(): 56 | global SUDOERS 57 | print("[INFO]: LOADING SUDOERS") 58 | sudoersdb = db.sudoers 59 | sudoers = await sudoersdb.find_one({"sudo": "sudo"}) 60 | sudoers = [] if not sudoers else sudoers["sudoers"] 61 | for user_id in SUDOERS: 62 | if user_id not in sudoers: 63 | sudoers.append(user_id) 64 | await sudoersdb.update_one( 65 | {"sudo": "sudo"}, 66 | {"$set": {"sudoers": sudoers}}, 67 | upsert=True, 68 | ) 69 | SUDOERS = (SUDOERS + sudoers) if sudoers else SUDOERS 70 | print("[INFO]: LOADED SUDOERS") 71 | 72 | 73 | loop = asyncio.get_event_loop() 74 | loop.run_until_complete(load_sudoers()) 75 | 76 | aiohttpsession = ClientSession() 77 | 78 | arq = ARQ(ARQ_API_URL, ARQ_API_KEY, aiohttpsession) 79 | 80 | app = Client("nezuko", bot_token=BOT_TOKEN, api_id=API_ID, api_hash=API_HASH) 81 | 82 | print("[INFO]: STARTING BOT CLIENT") 83 | app.start() 84 | 85 | print("[INFO]: GATHERING PROFILE INFO") 86 | x = app.get_me() 87 | 88 | 89 | BOT_ID = x.id 90 | BOT_NAME = x.first_name + (x.last_name or "") 91 | BOT_USERNAME = x.username 92 | BOT_MENTION = x.mention 93 | BOT_DC_ID = x.dc_id 94 | 95 | telegraph = Telegraph() 96 | telegraph.create_account(short_name=BOT_USERNAME) 97 | 98 | 99 | async def eor(msg: Message, **kwargs): 100 | func = ( 101 | (msg.edit_text if msg.from_user.is_self else msg.reply) 102 | if msg.from_user 103 | else msg.reply 104 | ) 105 | spec = getfullargspec(func.__wrapped__).args 106 | return await func(**{k: v for k, v in kwargs.items() if k in spec}) 107 | -------------------------------------------------------------------------------- /nezuko/core/decorators/errors.py: -------------------------------------------------------------------------------- 1 | """ WRITTEN BY @pokurt, https://github.com/pokurt""" 2 | 3 | import sys 4 | import traceback 5 | from functools import wraps 6 | 7 | from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden 8 | 9 | from nezuko import LOG_GROUP_ID, app 10 | 11 | 12 | def split_limits(text): 13 | if len(text) < 2048: 14 | return [text] 15 | 16 | lines = text.splitlines(True) 17 | small_msg = "" 18 | result = [] 19 | for line in lines: 20 | if len(small_msg) + len(line) < 2048: 21 | small_msg += line 22 | else: 23 | result.append(small_msg) 24 | small_msg = line 25 | result.append(small_msg) 26 | 27 | return result 28 | 29 | 30 | def capture_err(func): 31 | @wraps(func) 32 | async def capture(client, message, *args, **kwargs): 33 | try: 34 | return await func(client, message, *args, **kwargs) 35 | except ChatWriteForbidden: 36 | await app.leave_chat(message.chat.id) 37 | return 38 | except Exception as err: 39 | exc_type, exc_obj, exc_tb = sys.exc_info() 40 | errors = traceback.format_exception( 41 | etype=exc_type, 42 | value=exc_obj, 43 | tb=exc_tb, 44 | ) 45 | error_feedback = split_limits( 46 | "**ERROR** | `{}` | `{}`\n\n```{}```\n\n```{}```\n".format( 47 | 0 if not message.from_user else message.from_user.id, 48 | 0 if not message.chat else message.chat.id, 49 | message.text or message.caption, 50 | "".join(errors), 51 | ), 52 | ) 53 | for x in error_feedback: 54 | await app.send_message(LOG_GROUP_ID, x) 55 | raise err 56 | 57 | return capture 58 | -------------------------------------------------------------------------------- /nezuko/core/decorators/misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from functools import wraps 25 | from time import time 26 | 27 | 28 | def exec_time(func): 29 | @wraps(func) 30 | async def _time_it(*args, **kwargs): 31 | t1 = time() 32 | try: 33 | return await func(*args, **kwargs) 34 | finally: 35 | t2 = time() 36 | total = t2 - t1 37 | total = round(total, 3) 38 | print(f"{func.__name__} Took: {total} Seconds") 39 | 40 | return _time_it 41 | -------------------------------------------------------------------------------- /nezuko/core/decorators/permissions.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from functools import wraps 25 | from traceback import format_exc as err 26 | 27 | from pyrogram.errors.exceptions.forbidden_403 import ChatWriteForbidden 28 | from pyrogram.types import Message 29 | 30 | from nezuko import SUDOERS, app 31 | from nezuko.modules.admin import member_permissions 32 | 33 | 34 | async def authorised(func, subFunc2, client, message, *args, **kwargs): 35 | chatID = message.chat.id 36 | try: 37 | await func(client, message, *args, **kwargs) 38 | except ChatWriteForbidden: 39 | await app.leave_chat(chatID) 40 | except Exception as e: 41 | try: 42 | await message.reply_text(str(e.MESSAGE)) 43 | except AttributeError: 44 | await message.reply_text(str(e)) 45 | e = err() 46 | print(e) 47 | return subFunc2 48 | 49 | 50 | async def unauthorised(message: Message, permission, subFunc2): 51 | chatID = message.chat.id 52 | text = ( 53 | "You don't have the required permission to perform this action." 54 | + f"\n**Permission:** __{permission}__" 55 | ) 56 | try: 57 | await message.reply_text(text) 58 | except ChatWriteForbidden: 59 | await app.leave_chat(chatID) 60 | return subFunc2 61 | 62 | 63 | def adminsOnly(permission): 64 | def subFunc(func): 65 | @wraps(func) 66 | async def subFunc2(client, message: Message, *args, **kwargs): 67 | chatID = message.chat.id 68 | if not message.from_user: 69 | # For anonymous admins 70 | if ( 71 | message.sender_chat 72 | and message.sender_chat.id == message.chat.id 73 | ): 74 | return await authorised( 75 | func, 76 | subFunc2, 77 | client, 78 | message, 79 | *args, 80 | **kwargs, 81 | ) 82 | return await unauthorised(message, permission, subFunc2) 83 | # For admins and sudo users 84 | userID = message.from_user.id 85 | permissions = await member_permissions(chatID, userID) 86 | if userID not in SUDOERS and permission not in permissions: 87 | return await unauthorised(message, permission, subFunc2) 88 | return await authorised( 89 | func, subFunc2, client, message, *args, **kwargs 90 | ) 91 | 92 | return subFunc2 93 | 94 | return subFunc 95 | -------------------------------------------------------------------------------- /nezuko/core/filters.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pyrogram import filters as filters_ 25 | from pyrogram.types import Message 26 | 27 | from nezuko import SUDOERS 28 | from nezuko import USERBOT_ID as OWNER_ID 29 | from nezuko.utils.functions import get_urls_from_text 30 | 31 | 32 | def url(_, __, message: Message) -> bool: 33 | # Can't use entities to check for url because 34 | # monospace removes url entity 35 | 36 | # TODO Fix detection of those urls which 37 | # doesn't have schema, ex-facebook.com 38 | 39 | text = message.text or message.caption 40 | if not text: 41 | return False 42 | return bool(get_urls_from_text(text)) 43 | 44 | 45 | async def admin(_, __, message: Message) -> bool: 46 | if message.chat.type not in ["group", "supergroup"]: 47 | return False 48 | if not message.from_user: 49 | return bool(message.sender_chat) 50 | # Calling iter_chat_members again and again 51 | # doesn't cause floodwait, that's why i'm using it here. 52 | return message.from_user.id in [ 53 | member.user.id 54 | async for member in message._client.get_chat_members( 55 | message.chat.id, filter="administrators" 56 | ) 57 | ] 58 | 59 | 60 | def entities(_, __, message: Message) -> bool: 61 | return bool(message.entities) 62 | 63 | 64 | def anonymous(_, __, message: Message) -> bool: 65 | return bool(message.sender_chat) 66 | 67 | 68 | def sudoers(_, __, message: Message) -> bool: 69 | if not message.from_user: 70 | return False 71 | return message.from_user.id in SUDOERS 72 | 73 | 74 | def owner(_, __, message: Message) -> bool: 75 | if not message.from_user: 76 | return False 77 | return message.from_user.id == OWNER_ID 78 | 79 | 80 | class Filters: 81 | pass 82 | 83 | 84 | filters = Filters 85 | filters.url = filters_.create(url) 86 | filters.admin = filters_.create(admin) 87 | filters.entities = filters_.create(entities) 88 | filters.anonymous = filters_.create(anonymous) 89 | filters.sudoers = filters_.create(sudoers) 90 | filters.owner = filters_.create(owner) 91 | -------------------------------------------------------------------------------- /nezuko/core/keyboard.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pykeyboard import InlineKeyboard 25 | from pyrogram.types import InlineKeyboardButton as Ikb 26 | 27 | from nezuko.utils.functions import get_urls_from_text as is_url 28 | 29 | 30 | def keyboard(buttons_list, row_width: int = 2): 31 | """ 32 | Buttons builder, pass buttons in a list and it will 33 | return pyrogram.types.IKB object 34 | Ex: keyboard([["click here", "https://google.com"]]) 35 | if theres, a url, it will make url button, else callback button 36 | """ 37 | buttons = InlineKeyboard(row_width=row_width) 38 | data = [ 39 | ( 40 | Ikb(text=str(i[0]), callback_data=str(i[1])) 41 | if not is_url(i[1]) 42 | else Ikb(text=str(i[0]), url=str(i[1])) 43 | ) 44 | for i in buttons_list 45 | ] 46 | buttons.add(*data) 47 | return buttons 48 | 49 | 50 | def ikb(data: dict, row_width: int = 2): 51 | """ 52 | Converts a dict to pyrogram buttons 53 | Ex: dict_to_keyboard({"click here": "this is callback data"}) 54 | """ 55 | return keyboard(data.items(), row_width=row_width) 56 | -------------------------------------------------------------------------------- /nezuko/core/sections.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | n = "\n" 26 | w = " " 27 | 28 | 29 | bold = lambda x: f"**{x}:** " 30 | bold_ul = lambda x: f"**--{x}:**-- " 31 | 32 | mono = lambda x: f"`{x}`{n}" 33 | 34 | 35 | def section( 36 | title: str, 37 | body: dict, 38 | indent: int = 2, 39 | underline: bool = False, 40 | ) -> str: 41 | 42 | text = (bold_ul(title) + n) if underline else bold(title) + n 43 | 44 | for key, value in body.items(): 45 | text += ( 46 | indent * w 47 | + bold(key) 48 | + ((value[0] + n) if isinstance(value, list) else mono(value)) 49 | ) 50 | return text 51 | -------------------------------------------------------------------------------- /nezuko/core/tasks.py: -------------------------------------------------------------------------------- 1 | from asyncio import Lock, create_task 2 | from time import time 3 | 4 | from pyrogram import filters 5 | from pyrogram.types import Message 6 | 7 | from nezuko import BOT_ID, SUDOERS 8 | from nezuko.core.sections import bold, section, w 9 | 10 | tasks = {} 11 | TASKS_LOCK = Lock() 12 | arrow = lambda x: (x.text if x else "") + "\n`→`" 13 | 14 | 15 | def all_tasks(): 16 | return tasks 17 | 18 | 19 | async def add_task( 20 | taskFunc, 21 | task_name, 22 | *args, 23 | **kwargs, 24 | ): 25 | 26 | async with TASKS_LOCK: 27 | global tasks 28 | 29 | task_id = (list(tasks.keys())[-1] + 1) if tasks else 0 30 | 31 | task = create_task( 32 | taskFunc(*args, **kwargs), 33 | name=task_name, 34 | ) 35 | tasks[task_id] = task, int(time()) 36 | return task, task_id 37 | 38 | 39 | async def rm_task(task_id=None): 40 | global tasks 41 | 42 | async with TASKS_LOCK: 43 | for key, value in list(tasks.items()): 44 | if value[0].done() or value[0].cancelled(): 45 | del tasks[key] 46 | 47 | if (task_id is not None) and (task_id in tasks): 48 | task = tasks[task_id][0] 49 | 50 | if not task.done(): 51 | task.cancel() 52 | 53 | del tasks[task_id] 54 | 55 | 56 | async def _get_tasks_text(): 57 | await rm_task() # Clean completed tasks 58 | if not tasks: 59 | return f"{arrow('')} No pending task" 60 | 61 | text = bold("Tasks") + "\n" 62 | 63 | for i, task in enumerate(list(tasks.items())): 64 | indent = w * 4 65 | 66 | t, started = task[1] 67 | elapsed = round(time() - started) 68 | info = t._repr_info() 69 | 70 | id = task[0] 71 | text += section( 72 | f"{indent}Task {i}", 73 | body={ 74 | "Name": t.get_name(), 75 | "Task ID": id, 76 | "Status": info[0].capitalize(), 77 | "Origin": info[2].split("/")[-1].replace(">", ""), 78 | "Running since": f"{elapsed}s", 79 | }, 80 | indent=8, 81 | ) 82 | return text 83 | -------------------------------------------------------------------------------- /nezuko/core/types/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from .InlineQueryResult import ( 4 | InlineQueryResultAudio, 5 | InlineQueryResultCachedDocument, 6 | ) 7 | -------------------------------------------------------------------------------- /nezuko/modules/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import glob 25 | import importlib 26 | import sys 27 | from os.path import basename, dirname, isfile 28 | 29 | from nezuko import MOD_LOAD, MOD_NOLOAD 30 | 31 | 32 | def __list_all_modules(): 33 | # This generates a list of modules in this 34 | # folder for the * in __main__ to work. 35 | mod_paths = glob.glob(dirname(__file__) + "/*.py") 36 | all_modules = [ 37 | basename(f)[:-3] 38 | for f in mod_paths 39 | if isfile(f) 40 | and f.endswith(".py") 41 | and not f.endswith("__init__.py") 42 | and not f.endswith("__main__.py") 43 | ] 44 | 45 | if MOD_LOAD or MOD_NOLOAD: 46 | to_load = MOD_LOAD 47 | if to_load: 48 | if not all( 49 | any(mod == module_name for module_name in all_modules) 50 | for mod in to_load 51 | ): 52 | sys.exit() 53 | 54 | else: 55 | to_load = all_modules 56 | 57 | if MOD_NOLOAD: 58 | return [item for item in to_load if item not in MOD_NOLOAD] 59 | 60 | return to_load 61 | 62 | return all_modules 63 | 64 | 65 | print("[INFO]: IMPORTING MODULES") 66 | importlib.import_module("nezuko.modules.__main__") 67 | ALL_MODULES = sorted(__list_all_modules()) 68 | __all__ = ALL_MODULES + ["ALL_MODULES"] 69 | -------------------------------------------------------------------------------- /nezuko/modules/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | -------------------------------------------------------------------------------- /nezuko/modules/admin_misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import os 25 | 26 | from pyrogram import filters 27 | 28 | from nezuko import app 29 | from nezuko.core.decorators.permissions import adminsOnly 30 | 31 | __MODULE__ = "Admin Miscs" 32 | __HELP__ = """ 33 | /set_chat_title - Change The Name Of A Group/Channel. 34 | /set_chat_photo - Change The PFP Of A Group/Channel. 35 | /set_user_title - Change The Administrator Title Of An Admin. 36 | """ 37 | 38 | 39 | @app.on_message(filters.command("set_chat_title") & ~filters.private) 40 | @adminsOnly("can_change_info") 41 | async def set_chat_title(_, message): 42 | if len(message.command) < 2: 43 | return await message.reply_text("**Usage:**\n/set_chat_title NEW NAME") 44 | old_title = message.chat.title 45 | new_title = message.text.split(None, 1)[1] 46 | await message.chat.set_title(new_title) 47 | await message.reply_text( 48 | f"Successfully Changed Group Title From {old_title} To {new_title}" 49 | ) 50 | 51 | 52 | @app.on_message(filters.command("set_user_title") & ~filters.private) 53 | @adminsOnly("can_change_info") 54 | async def set_user_title(_, message): 55 | if not message.reply_to_message: 56 | return await message.reply_text( 57 | "Reply to user's message to set his admin title" 58 | ) 59 | if not message.reply_to_message.from_user: 60 | return await message.reply_text( 61 | "I can't change admin title of an unknown entity" 62 | ) 63 | chat_id = message.chat.id 64 | from_user = message.reply_to_message.from_user 65 | if len(message.command) < 2: 66 | return await message.reply_text( 67 | "**Usage:**\n/set_user_title NEW ADMINISTRATOR TITLE" 68 | ) 69 | title = message.text.split(None, 1)[1] 70 | await app.set_administrator_title(chat_id, from_user.id, title) 71 | await message.reply_text( 72 | f"Successfully Changed {from_user.mention}'s Admin Title To {title}" 73 | ) 74 | 75 | 76 | @app.on_message(filters.command("set_chat_photo") & ~filters.private) 77 | @adminsOnly("can_change_info") 78 | async def set_chat_photo(_, message): 79 | reply = message.reply_to_message 80 | 81 | if not reply: 82 | return await message.reply_text( 83 | "Reply to a photo to set it as chat_photo" 84 | ) 85 | 86 | file = reply.document or reply.photo 87 | if not file: 88 | return await message.reply_text( 89 | "Reply to a photo or document to set it as chat_photo" 90 | ) 91 | 92 | if file.file_size > 5000000: 93 | return await message.reply("File size too large.") 94 | 95 | photo = await reply.download() 96 | await message.chat.set_photo(photo) 97 | await message.reply_text("Successfully Changed Group Photo") 98 | os.remove(photo) 99 | -------------------------------------------------------------------------------- /nezuko/modules/animals.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 rozari0 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | from pyrogram import filters 26 | 27 | from nezuko import app 28 | from nezuko.core.decorators.errors import capture_err 29 | from nezuko.utils.http import get, resp_get 30 | 31 | __MODULE__ = "Animals" 32 | __HELP__ = """/catfacts - To Get Facts About Cat. 33 | /dogfacts - To Get Facts About Dog. 34 | /animalfacts - To Get Facts About Animal. 35 | """ 36 | 37 | 38 | @app.on_message(filters.command("catfacts")) 39 | @capture_err 40 | async def catfacts(client, message): 41 | """ 42 | Get cat facts 43 | """ 44 | message = await message.reply_text("`Getting cat facts...`") 45 | resp = await get("https://cat-fact.herokuapp.com/facts/random") 46 | return await message.edit(resp["text"]) 47 | 48 | 49 | @app.on_message(filters.command("animalfacts")) 50 | @capture_err 51 | async def animalfacts(client, message): 52 | somerandomvariable = await get("https://axoltlapi.herokuapp.com/") 53 | return await message.reply_photo( 54 | somerandomvariable["url"], caption=somerandomvariable["facts"] 55 | ) 56 | 57 | 58 | @app.on_message(filters.command("dogfacts")) 59 | @capture_err 60 | async def dogfacts(client, message): 61 | somerandomvariable = await get( 62 | "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?number=1" 63 | ) 64 | return await message.reply_text(somerandomvariable[0]["fact"]) 65 | -------------------------------------------------------------------------------- /nezuko/modules/antiservice.py: -------------------------------------------------------------------------------- 1 | # Written By [MaskedVirus | swatv3nub] for William and Ryūga 2 | # Kang With Proper Credits 3 | 4 | from pyrogram import filters 5 | 6 | from nezuko import app 7 | from nezuko.core.decorators.permissions import adminsOnly 8 | from nezuko.utils.dbfunctions import ( 9 | antiservice_off, 10 | antiservice_on, 11 | is_antiservice_on, 12 | ) 13 | 14 | __MODULE__ = "AntiService" 15 | __HELP__ = """ 16 | Plugin to delete service messages in a chat! 17 | 18 | /antiservice [enable|disable] 19 | """ 20 | 21 | 22 | @app.on_message(filters.command("antiservice") & ~filters.private) 23 | @adminsOnly("can_change_info") 24 | async def anti_service(_, message): 25 | if len(message.command) != 2: 26 | return await message.reply_text( 27 | "Usage: /antiservice [enable | disable]" 28 | ) 29 | status = message.text.split(None, 1)[1].strip() 30 | status = status.lower() 31 | chat_id = message.chat.id 32 | if status == "enable": 33 | await antiservice_on(chat_id) 34 | await message.reply_text( 35 | "Enabled AntiService System. I will Delete Service Messages from Now on." 36 | ) 37 | elif status == "disable": 38 | await antiservice_off(chat_id) 39 | await message.reply_text( 40 | "Disabled AntiService System. I won't Be Deleting Service Message from Now on." 41 | ) 42 | else: 43 | await message.reply_text( 44 | "Unknown Suffix, Use /antiservice [enable|disable]" 45 | ) 46 | 47 | 48 | @app.on_message(filters.service, group=11) 49 | async def delete_service(_, message): 50 | chat_id = message.chat.id 51 | try: 52 | if await is_antiservice_on(chat_id): 53 | return await message.delete() 54 | except Exception: 55 | pass 56 | -------------------------------------------------------------------------------- /nezuko/modules/autocorrect.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import Message 3 | 4 | from nezuko import SUDOERS, app, arq 5 | from nezuko.utils.filter_groups import autocorrect_group 6 | 7 | 8 | @app.on_message(filters.command("autocorrect")) 9 | async def autocorrect_bot(_, message: Message): 10 | if not message.reply_to_message: 11 | return await message.reply_text("Reply to a text message.") 12 | reply = message.reply_to_message 13 | text = reply.text or reply.caption 14 | if not text: 15 | return await message.reply_text("Reply to a text message.") 16 | data = await arq.spellcheck(text) 17 | if not data.ok: 18 | return await message.reply_text("Something wrong happened.") 19 | result = data.result 20 | await message.reply_text(result.corrected or "Empty") 21 | -------------------------------------------------------------------------------- /nezuko/modules/blacklist.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import re 25 | from time import time 26 | 27 | from pyrogram import filters 28 | from pyrogram.types import ChatPermissions 29 | 30 | from nezuko import SUDOERS, app 31 | from nezuko.core.decorators.errors import capture_err 32 | from nezuko.core.decorators.permissions import adminsOnly 33 | from nezuko.modules.admin import list_admins 34 | from nezuko.utils.dbfunctions import ( 35 | delete_blacklist_filter, 36 | get_blacklisted_words, 37 | save_blacklist_filter, 38 | ) 39 | from nezuko.utils.filter_groups import blacklist_filters_group 40 | 41 | __MODULE__ = "Blacklist" 42 | __HELP__ = """ 43 | /blacklisted - Get All The Blacklisted Words In The Chat. 44 | /blacklist [WORD|SENTENCE] - Blacklist A Word Or A Sentence. 45 | /whitelist [WORD|SENTENCE] - Whitelist A Word Or A Sentence. 46 | """ 47 | 48 | 49 | @app.on_message(filters.command("blacklist") & ~filters.private) 50 | @adminsOnly("can_restrict_members") 51 | async def save_filters(_, message): 52 | if len(message.command) < 2: 53 | return await message.reply_text("Usage:\n/blacklist [WORD|SENTENCE]") 54 | word = message.text.split(None, 1)[1].strip() 55 | if not word: 56 | return await message.reply_text( 57 | "**Usage**\n__/blacklist [WORD|SENTENCE]__" 58 | ) 59 | chat_id = message.chat.id 60 | await save_blacklist_filter(chat_id, word) 61 | await message.reply_text(f"__**Blacklisted {word}.**__") 62 | 63 | 64 | @app.on_message(filters.command("blacklisted") & ~filters.private) 65 | @capture_err 66 | async def get_filterss(_, message): 67 | data = await get_blacklisted_words(message.chat.id) 68 | if not data: 69 | await message.reply_text("**No blacklisted words in this chat.**") 70 | else: 71 | msg = f"List of blacklisted words in {message.chat.title}\n" 72 | for word in data: 73 | msg += f"**-** `{word}`\n" 74 | await message.reply_text(msg) 75 | 76 | 77 | @app.on_message(filters.command("whitelist") & ~filters.private) 78 | @adminsOnly("can_restrict_members") 79 | async def del_filter(_, message): 80 | if len(message.command) < 2: 81 | return await message.reply_text("Usage:\n/whitelist [WORD|SENTENCE]") 82 | word = message.text.split(None, 1)[1].strip() 83 | if not word: 84 | return await message.reply_text("Usage:\n/whitelist [WORD|SENTENCE]") 85 | chat_id = message.chat.id 86 | deleted = await delete_blacklist_filter(chat_id, word) 87 | if deleted: 88 | return await message.reply_text(f"**Whitelisted {word}.**") 89 | await message.reply_text("**No such blacklist filter.**") 90 | 91 | 92 | @app.on_message(filters.text & ~filters.private, group=blacklist_filters_group) 93 | @capture_err 94 | async def blacklist_filters_re(_, message): 95 | text = message.text.lower().strip() 96 | if not text: 97 | return 98 | chat_id = message.chat.id 99 | user = message.from_user 100 | if not user: 101 | return 102 | if user.id in SUDOERS: 103 | return 104 | list_of_filters = await get_blacklisted_words(chat_id) 105 | for word in list_of_filters: 106 | pattern = r"( |^|[^\w])" + re.escape(word) + r"( |$|[^\w])" 107 | if re.search(pattern, text, flags=re.IGNORECASE): 108 | if user.id in await list_admins(chat_id): 109 | return 110 | try: 111 | await message.chat.restrict_member( 112 | user.id, 113 | ChatPermissions(), 114 | until_date=(time() + 3600), 115 | ) 116 | except Exception: 117 | return 118 | return await app.send_message( 119 | chat_id, 120 | f"Muted {user.mention} [`{user.id}`] for 1 hour " 121 | + f"due to a blacklist match on {word}.", 122 | ) 123 | -------------------------------------------------------------------------------- /nezuko/modules/blacklist_chat.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import Message 3 | 4 | from nezuko import SUDOERS, app 5 | from nezuko.core.decorators.errors import capture_err 6 | from nezuko.utils.dbfunctions import ( 7 | blacklist_chat, 8 | blacklisted_chats, 9 | whitelist_chat, 10 | ) 11 | 12 | __MODULE__ = "Blacklist Chat" 13 | __HELP__ = """ 14 | **THIS MODULE IS ONLY FOR DEVS** 15 | 16 | Use this module to make the bot leave some chats 17 | in which you don't want it to be in. 18 | 19 | /blacklist_chat [CHAT_ID] - Blacklist a chat. 20 | /whitelist_chat [CHAT_ID] - Whitelist a chat. 21 | /blacklisted - Show blacklisted chats. 22 | """ 23 | 24 | 25 | @app.on_message(filters.command("blacklist_chat") & filters.user(SUDOERS)) 26 | @capture_err 27 | async def blacklist_chat_func(_, message: Message): 28 | if len(message.command) != 2: 29 | return await message.reply_text( 30 | "**Usage:**\n/blacklist_chat [CHAT_ID]" 31 | ) 32 | chat_id = int(message.text.strip().split()[1]) 33 | if chat_id in await blacklisted_chats(): 34 | return await message.reply_text("Chat is already blacklisted.") 35 | blacklisted = await blacklist_chat(chat_id) 36 | if blacklisted: 37 | return await message.reply_text( 38 | "Chat has been successfully blacklisted" 39 | ) 40 | await message.reply_text("Something wrong happened, check logs.") 41 | 42 | 43 | @app.on_message(filters.command("whitelist_chat") & filters.user(SUDOERS)) 44 | @capture_err 45 | async def whitelist_chat_func(_, message: Message): 46 | if len(message.command) != 2: 47 | return await message.reply_text( 48 | "**Usage:**\n/whitelist_chat [CHAT_ID]" 49 | ) 50 | chat_id = int(message.text.strip().split()[1]) 51 | if chat_id not in await blacklisted_chats(): 52 | return await message.reply_text("Chat is already whitelisted.") 53 | whitelisted = await whitelist_chat(chat_id) 54 | if whitelisted: 55 | return await message.reply_text( 56 | "Chat has been successfully whitelisted" 57 | ) 58 | await message.reply_text("Something wrong happened, check logs.") 59 | 60 | 61 | @app.on_message(filters.command("blacklisted_chats") & filters.user(SUDOERS)) 62 | @capture_err 63 | async def blacklisted_chats_func(_, message: Message): 64 | text = "" 65 | for count, chat_id in enumerate(await blacklisted_chats(), 1): 66 | try: 67 | title = (await app.get_chat(chat_id)).title 68 | except Exception: 69 | title = "Private" 70 | text += f"**{count}. {title}** [`{chat_id}`]\n" 71 | await message.reply_text(text) 72 | -------------------------------------------------------------------------------- /nezuko/modules/carbon.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | from pyrogram import filters 26 | 27 | from nezuko import app 28 | from nezuko.core.decorators.errors import capture_err 29 | from nezuko.utils.functions import make_carbon 30 | 31 | 32 | @app.on_message(filters.command("carbon")) 33 | @capture_err 34 | async def carbon_func(_, message): 35 | if not message.reply_to_message: 36 | return await message.reply_text( 37 | "Reply to a text message to make carbon." 38 | ) 39 | if not message.reply_to_message.text: 40 | return await message.reply_text( 41 | "Reply to a text message to make carbon." 42 | ) 43 | m = await message.reply_text("Preparing Carbon") 44 | carbon = await make_carbon(message.reply_to_message.text) 45 | await m.edit("Uploading") 46 | await app.send_document(message.chat.id, carbon) 47 | await m.delete() 48 | carbon.close() 49 | -------------------------------------------------------------------------------- /nezuko/modules/cat.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 rozari0 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | from pyrogram import filters 26 | 27 | from nezuko import app 28 | from nezuko.core.decorators.errors import capture_err 29 | from nezuko.utils.http import get, resp_get 30 | 31 | __MODULE__ = "Cats" 32 | __HELP__ = """/randomcat - To Get Random Photo of Cat. 33 | /catfacts - To Get Facts About Cat. 34 | """ 35 | 36 | 37 | @app.on_message(filters.command("randomcat")) 38 | @capture_err 39 | async def randomcat(_, message): 40 | cat = await get("https://aws.random.cat/meow") 41 | await message.reply_photo(cat.get("file")) 42 | -------------------------------------------------------------------------------- /nezuko/modules/chat_watcher.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from nezuko import app 25 | from nezuko.utils.dbfunctions import ( 26 | add_served_chat, 27 | add_served_user, 28 | blacklisted_chats, 29 | ) 30 | from nezuko.utils.filter_groups import chat_watcher_group 31 | 32 | 33 | @app.on_message(group=chat_watcher_group) 34 | async def chat_watcher_func(_, message): 35 | if message.from_user: 36 | user_id = message.from_user.id 37 | await add_served_user(user_id) 38 | 39 | chat_id = message.chat.id 40 | blacklisted_chats_list = await blacklisted_chats() 41 | 42 | if not chat_id: 43 | return 44 | 45 | if chat_id in blacklisted_chats_list: 46 | return await app.leave_chat(chat_id) 47 | 48 | await add_served_chat(chat_id) 49 | -------------------------------------------------------------------------------- /nezuko/modules/chatbot.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from asyncio import gather, sleep 25 | 26 | from pyrogram import enums, filters 27 | from pyrogram.types import Message 28 | 29 | from nezuko import BOT_ID, SUDOERS, app, arq, eor 30 | from nezuko.core.decorators.errors import capture_err 31 | from nezuko.utils.filter_groups import chatbot_group 32 | 33 | __MODULE__ = "ChatBot" 34 | __HELP__ = """ 35 | /chatbot [ENABLE|DISABLE] To Enable Or Disable ChatBot In Your Chat.""" 36 | 37 | active_chats_bot = [] 38 | 39 | 40 | async def chat_bot_toggle(db, message: Message): 41 | status = message.text.split(None, 1)[1].lower() 42 | chat_id = message.chat.id 43 | if status == "enable": 44 | if chat_id not in db: 45 | db.append(chat_id) 46 | text = "Chatbot Enabled!" 47 | return await eor(message, text=text) 48 | await eor(message, text="ChatBot Is Already Enabled.") 49 | elif status == "disable": 50 | if chat_id in db: 51 | db.remove(chat_id) 52 | return await eor(message, text="Chatbot Disabled!") 53 | await eor(message, text="ChatBot Is Already Disabled.") 54 | else: 55 | await eor(message, text="**Usage:**\n/chatbot [ENABLE|DISABLE]") 56 | 57 | 58 | # Enabled | Disable Chatbot 59 | 60 | 61 | @app.on_message(filters.command("chatbot")) 62 | @capture_err 63 | async def chatbot_status(_, message: Message): 64 | if len(message.command) != 2: 65 | return await eor(message, text="**Usage:**\n/chatbot [ENABLE|DISABLE]") 66 | await chat_bot_toggle(active_chats_bot, message) 67 | 68 | 69 | async def lunaQuery(query: str, user_id: int): 70 | luna = await arq.luna(query, user_id) 71 | return luna.result 72 | 73 | 74 | async def type_and_send(message: Message): 75 | chat_id = message.chat.id 76 | user_id = message.from_user.id if message.from_user else 0 77 | query = message.text.strip() 78 | await message._client.send_chat_action(chat_id, enums.ChatAction.TYPING) 79 | response, _ = await gather(lunaQuery(query, user_id), sleep(3)) 80 | await message.reply_text(response) 81 | await message._client.send_chat_action(chat_id, enums.ChatAction.CANCEL) 82 | 83 | 84 | @app.on_message( 85 | filters.text 86 | & filters.reply 87 | & ~filters.bot 88 | & ~filters.via_bot 89 | & ~filters.forwarded, 90 | group=chatbot_group, 91 | ) 92 | @capture_err 93 | async def chatbot_talk(_, message: Message): 94 | if message.chat.id not in active_chats_bot: 95 | return 96 | if not message.reply_to_message: 97 | return 98 | if not message.reply_to_message.from_user: 99 | return 100 | if message.reply_to_message.from_user.id != BOT_ID: 101 | return 102 | await type_and_send(message) 103 | -------------------------------------------------------------------------------- /nezuko/modules/couple.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import random 25 | from datetime import datetime 26 | 27 | from pyrogram import filters 28 | 29 | from nezuko import app 30 | from nezuko.core.decorators.errors import capture_err 31 | from nezuko.utils.dbfunctions import get_couple, save_couple 32 | 33 | __MODULE__ = "Shippering" 34 | __HELP__ = "/detect_gay - To Choose Couple Of The Day" 35 | 36 | 37 | # Date and time 38 | def dt(): 39 | now = datetime.now() 40 | dt_string = now.strftime("%d/%m/%Y %H:%M") 41 | return dt_string.split(" ") 42 | 43 | 44 | def dt_tom(): 45 | return ( 46 | str(int(dt()[0].split("/")[0]) + 1) 47 | + "/" 48 | + dt()[0].split("/")[1] 49 | + "/" 50 | + dt()[0].split("/")[2] 51 | ) 52 | 53 | 54 | today = str(dt()[0]) 55 | tomorrow = str(dt_tom()) 56 | 57 | 58 | @app.on_message(filters.command(["detect_gay", "couple"]) & ~filters.private) 59 | @capture_err 60 | async def couple(_, message): 61 | try: 62 | chat_id = message.chat.id 63 | is_selected = await get_couple(chat_id, today) 64 | if not is_selected: 65 | list_of_users = [] 66 | async for i in app.get_chat_members(message.chat.id): 67 | if not i.user.is_bot: 68 | list_of_users.append(i.user.id) 69 | if len(list_of_users) < 2: 70 | return await message.reply_text("Not enough users") 71 | c1_id = random.choice(list_of_users) 72 | c2_id = random.choice(list_of_users) 73 | while c1_id == c2_id: 74 | c1_id = random.choice(list_of_users) 75 | c1_mention = (await app.get_users(c1_id)).mention 76 | c2_mention = (await app.get_users(c2_id)).mention 77 | 78 | couple_selection_message = f"""**Couple of the day:** 79 | {c1_mention} + {c2_mention} = ❤️ 80 | 81 | __New couple of the day may be chosen at 12AM {tomorrow}__""" 82 | await app.send_message( 83 | message.chat.id, text=couple_selection_message 84 | ) 85 | couple = {"c1_id": c1_id, "c2_id": c2_id} 86 | await save_couple(chat_id, today, couple) 87 | 88 | else: 89 | c1_id = int(is_selected["c1_id"]) 90 | c2_id = int(is_selected["c2_id"]) 91 | c1_name = (await app.get_users(c1_id)).first_name 92 | c2_name = (await app.get_users(c2_id)).first_name 93 | couple_selection_message = f"""Couple of the day: 94 | [{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = ❤️ 95 | 96 | __New couple of the day may be chosen at 12AM {tomorrow}__""" 97 | await app.send_message( 98 | message.chat.id, text=couple_selection_message 99 | ) 100 | except Exception as e: 101 | print(e) 102 | await message.reply_text(e) 103 | -------------------------------------------------------------------------------- /nezuko/modules/cricinfo.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 rozari0 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | from bs4 import BeautifulSoup 26 | from pyrogram import filters 27 | from pyrogram.enums import ParseMode 28 | 29 | from nezuko import app 30 | from nezuko.core.decorators.errors import capture_err 31 | from nezuko.utils.http import get, resp_get 32 | 33 | __MODULE__ = "CricInfo" 34 | __HELP__ = """ 35 | /cricinfo - Get the latest score of the match. 36 | """ 37 | 38 | 39 | @app.on_message(filters.command("cricinfo")) 40 | @capture_err 41 | async def catfacts(client, message): 42 | score_page = "http://static.cricinfo.com/rss/livescores.xml" 43 | page = await get(score_page) 44 | soup = BeautifulSoup(page, "html.parser") 45 | result = soup.find_all("description") 46 | Sed = "".join( 47 | "" + match.get_text() + "\n\n" for match in result 48 | ) 49 | return await message.reply(Sed, parse_mode=ParseMode.HTML) 50 | -------------------------------------------------------------------------------- /nezuko/modules/crypto.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | 3 | from nezuko import app 4 | from nezuko.core.decorators.errors import capture_err 5 | from nezuko.core.keyboard import ikb 6 | from nezuko.core.sections import section 7 | from nezuko.utils.http import get 8 | 9 | __MODULE__ = "Crypto" 10 | __HELP__ = """ 11 | /crypto [currency] 12 | Get Real Time value from currency given. 13 | """ 14 | 15 | 16 | @app.on_message(filters.command("crypto")) 17 | @capture_err 18 | async def crypto(_, message): 19 | if len(message.command) < 2: 20 | return await message.reply("/crypto [currency]") 21 | 22 | currency = message.text.split(None, 1)[1].lower() 23 | 24 | btn = ikb( 25 | {"Available Currencies": "https://plotcryptoprice.herokuapp.com"}, 26 | ) 27 | 28 | m = await message.reply("`Processing...`") 29 | 30 | try: 31 | r = await get( 32 | "https://x.wazirx.com/wazirx-falcon/api/v2.0/crypto_rates", 33 | timeout=5, 34 | ) 35 | except Exception: 36 | return await m.edit("[ERROR]: Something went wrong.") 37 | 38 | if currency not in r: 39 | return await m.edit( 40 | "[ERROR]: INVALID CURRENCY", 41 | reply_markup=btn, 42 | ) 43 | 44 | body = {i.upper(): j for i, j in r.get(currency).items()} 45 | 46 | text = section( 47 | "Current Crypto Rates For " + currency.upper(), 48 | body, 49 | ) 50 | await m.edit(text, reply_markup=btn) 51 | -------------------------------------------------------------------------------- /nezuko/modules/dice.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import Message 3 | 4 | from nezuko import SUDOERS, app 5 | 6 | __MODULE__ = "Dice" 7 | __HELP__ = """ 8 | /dice 9 | Roll a dice. 10 | """ 11 | 12 | 13 | @app.on_message(filters.command("dice")) 14 | async def throw_dice(client, message: Message): 15 | six = (message.from_user.id in SUDOERS) if message.from_user else False 16 | 17 | c = message.chat.id 18 | if not six: 19 | return await client.send_dice(c, "🎲") 20 | 21 | m = await client.send_dice(c, "🎲") 22 | 23 | while m.dice.value != 6: 24 | await m.delete() 25 | m = await client.send_dice(c, "🎲") 26 | -------------------------------------------------------------------------------- /nezuko/modules/filters.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import re 25 | 26 | from pyrogram import filters 27 | 28 | from nezuko import app 29 | from nezuko.core.decorators.errors import capture_err 30 | from nezuko.core.decorators.permissions import adminsOnly 31 | from nezuko.core.keyboard import ikb 32 | from nezuko.utils.dbfunctions import ( 33 | delete_filter, 34 | get_filter, 35 | get_filters_names, 36 | save_filter, 37 | ) 38 | from nezuko.utils.filter_groups import chat_filters_group 39 | from nezuko.utils.functions import extract_text_and_keyb 40 | 41 | __MODULE__ = "Filters" 42 | __HELP__ = """/filters To Get All The Filters In The Chat. 43 | /filter [FILTER_NAME] To Save A Filter (Can be a sticker or text). 44 | /stop [FILTER_NAME] To Stop A Filter. 45 | 46 | 47 | You can use markdown or html to save text too. 48 | 49 | Checkout /markdownhelp to know more about formattings and other syntax. 50 | """ 51 | 52 | 53 | @app.on_message(filters.command("filter") & ~filters.private) 54 | @adminsOnly("can_change_info") 55 | async def save_filters(_, message): 56 | if len(message.command) < 2 or not message.reply_to_message: 57 | return await message.reply_text( 58 | "**Usage:**\nReply to a text or sticker with /filter [FILTER_NAME] to save it." 59 | ) 60 | if ( 61 | not message.reply_to_message.text 62 | and not message.reply_to_message.sticker 63 | ): 64 | return await message.reply_text( 65 | "__**You can only save text or stickers in filters.**__" 66 | ) 67 | name = message.text.split(None, 1)[1].strip() 68 | if not name: 69 | return await message.reply_text( 70 | "**Usage:**\n__/filter [FILTER_NAME]__" 71 | ) 72 | chat_id = message.chat.id 73 | _type = "text" if message.reply_to_message.text else "sticker" 74 | _filter = { 75 | "type": _type, 76 | "data": message.reply_to_message.text.markdown 77 | if _type == "text" 78 | else message.reply_to_message.sticker.file_id, 79 | } 80 | await save_filter(chat_id, name, _filter) 81 | await message.reply_text(f"__**Saved filter {name}.**__") 82 | 83 | 84 | @app.on_message(filters.command("filters") & ~filters.private) 85 | @capture_err 86 | async def get_filterss(_, message): 87 | _filters = await get_filters_names(message.chat.id) 88 | if not _filters: 89 | return await message.reply_text("**No filters in this chat.**") 90 | _filters.sort() 91 | msg = f"List of filters in {message.chat.title}\n" 92 | for _filter in _filters: 93 | msg += f"**-** `{_filter}`\n" 94 | await message.reply_text(msg) 95 | 96 | 97 | @app.on_message(filters.command("stop") & ~filters.private) 98 | @adminsOnly("can_change_info") 99 | async def del_filter(_, message): 100 | if len(message.command) < 2: 101 | return await message.reply_text("**Usage:**\n__/stop [FILTER_NAME]__") 102 | name = message.text.split(None, 1)[1].strip() 103 | if not name: 104 | return await message.reply_text("**Usage:**\n__/stop [FILTER_NAME]__") 105 | chat_id = message.chat.id 106 | deleted = await delete_filter(chat_id, name) 107 | if deleted: 108 | await message.reply_text(f"**Deleted filter {name}.**") 109 | else: 110 | await message.reply_text("**No such filter.**") 111 | 112 | 113 | @app.on_message( 114 | filters.text & ~filters.private & ~filters.via_bot & ~filters.forwarded, 115 | group=chat_filters_group, 116 | ) 117 | @capture_err 118 | async def filters_re(_, message): 119 | text = message.text.lower().strip() 120 | if not text: 121 | return 122 | chat_id = message.chat.id 123 | list_of_filters = await get_filters_names(chat_id) 124 | for word in list_of_filters: 125 | pattern = r"( |^|[^\w])" + re.escape(word) + r"( |$|[^\w])" 126 | if re.search(pattern, text, flags=re.IGNORECASE): 127 | _filter = await get_filter(chat_id, word) 128 | data_type = _filter["type"] 129 | data = _filter["data"] 130 | if data_type == "text": 131 | keyb = None 132 | if re.findall(r"\[.+\,.+\]", data): 133 | keyboard = extract_text_and_keyb(ikb, data) 134 | if keyboard: 135 | data, keyb = keyboard 136 | 137 | if message.reply_to_message: 138 | await message.reply_to_message.reply_text( 139 | data, 140 | reply_markup=keyb, 141 | disable_web_page_preview=True, 142 | ) 143 | 144 | if text.startswith("~"): 145 | await message.delete() 146 | return 147 | 148 | return await message.reply_text( 149 | data, 150 | reply_markup=keyb, 151 | disable_web_page_preview=True, 152 | ) 153 | if message.reply_to_message: 154 | await message.reply_to_message.reply_sticker(data) 155 | 156 | if text.startswith("~"): 157 | await message.delete() 158 | return 159 | return await message.reply_sticker(data) 160 | -------------------------------------------------------------------------------- /nezuko/modules/flood.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from asyncio import get_running_loop, sleep 25 | from time import time 26 | 27 | from pyrogram import filters 28 | from pyrogram.types import ( 29 | CallbackQuery, 30 | ChatPermissions, 31 | InlineKeyboardButton, 32 | InlineKeyboardMarkup, 33 | Message, 34 | ) 35 | 36 | from nezuko import SUDOERS, app 37 | from nezuko.core.decorators.errors import capture_err 38 | from nezuko.core.decorators.permissions import adminsOnly 39 | from nezuko.modules.admin import list_admins, member_permissions 40 | from nezuko.utils.dbfunctions import flood_off, flood_on, is_flood_on 41 | from nezuko.utils.filter_groups import flood_group 42 | 43 | __MODULE__ = "Flood" 44 | __HELP__ = """ 45 | Anti-Flood system, the one who sends more than 10 messages in a row, gets muted for an hour (Except for admins). 46 | 47 | /flood [ENABLE|DISABLE] - Turn flood detection on or off 48 | """ 49 | 50 | 51 | DB = {} # TODO Use mongodb instead of a fucking dict. 52 | 53 | 54 | def reset_flood(chat_id, user_id=0): 55 | for user in DB[chat_id].keys(): 56 | if user != user_id: 57 | DB[chat_id][user] = 0 58 | 59 | 60 | @app.on_message( 61 | ~filters.service 62 | & ~filters.me 63 | & ~filters.private 64 | & ~filters.channel 65 | & ~filters.bot, 66 | group=flood_group, 67 | ) 68 | @capture_err 69 | async def flood_control_func(_, message: Message): 70 | if not message.chat: 71 | return 72 | chat_id = message.chat.id 73 | if not (await is_flood_on(chat_id)): 74 | return 75 | # Initialize db if not already. 76 | if chat_id not in DB: 77 | DB[chat_id] = {} 78 | 79 | if not message.from_user: 80 | reset_flood(chat_id) 81 | return 82 | 83 | user_id = message.from_user.id 84 | mention = message.from_user.mention 85 | 86 | if user_id not in DB[chat_id]: 87 | DB[chat_id][user_id] = 0 88 | 89 | # Reset floodb of current chat if some other user sends a message 90 | reset_flood(chat_id, user_id) 91 | 92 | # Ignore devs and admins 93 | mods = (await list_admins(chat_id)) + SUDOERS 94 | if user_id in mods: 95 | return 96 | 97 | # Mute if user sends more than 10 messages in a row 98 | if DB[chat_id][user_id] >= 10: 99 | DB[chat_id][user_id] = 0 100 | try: 101 | await message.chat.restrict_member( 102 | user_id, 103 | permissions=ChatPermissions(), 104 | until_date=int(time() + 3600), 105 | ) 106 | except Exception: 107 | return 108 | keyboard = InlineKeyboardMarkup( 109 | [ 110 | [ 111 | InlineKeyboardButton( 112 | text="🚨 Unmute 🚨", 113 | callback_data=f"unmute_{user_id}", 114 | ) 115 | ] 116 | ] 117 | ) 118 | m = await message.reply_text( 119 | f"Imagine flooding the chat in front of me, Muted {mention} for an hour!", 120 | reply_markup=keyboard, 121 | ) 122 | 123 | async def delete(): 124 | await sleep(3600) 125 | try: 126 | await m.delete() 127 | except Exception: 128 | pass 129 | 130 | loop = get_running_loop() 131 | return loop.create_task(delete()) 132 | DB[chat_id][user_id] += 1 133 | 134 | 135 | @app.on_callback_query(filters.regex("unmute_")) 136 | async def flood_callback_func(_, cq: CallbackQuery): 137 | from_user = cq.from_user 138 | permissions = await member_permissions(cq.message.chat.id, from_user.id) 139 | permission = "can_restrict_members" 140 | if permission not in permissions: 141 | return await cq.answer( 142 | "You don't have enough permissions to perform this action.\n" 143 | + f"Permission needed: {permission}", 144 | show_alert=True, 145 | ) 146 | user_id = cq.data.split("_")[1] 147 | await cq.message.chat.unban_member(user_id) 148 | text = cq.message.text.markdown 149 | text = f"~~{text}~~\n\n" 150 | text += f"__User unmuted by {from_user.mention}__" 151 | await cq.message.edit(text) 152 | 153 | 154 | @app.on_message(filters.command("flood") & ~filters.private) 155 | @adminsOnly("can_change_info") 156 | async def flood_toggle(_, message: Message): 157 | if len(message.command) != 2: 158 | return await message.reply_text("Usage: /flood [ENABLE|DISABLE]") 159 | status = message.text.split(None, 1)[1].strip() 160 | status = status.lower() 161 | chat_id = message.chat.id 162 | if status == "enable": 163 | await flood_on(chat_id) 164 | await message.reply_text("Enabled Flood Checker.") 165 | elif status == "disable": 166 | await flood_off(chat_id) 167 | await message.reply_text("Disabled Flood Checker.") 168 | else: 169 | await message.reply_text("Unknown Suffix, Use /flood [ENABLE|DISABLE]") 170 | -------------------------------------------------------------------------------- /nezuko/modules/fun.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from pyrogram import filters 4 | from pyrogram.types import Message 5 | 6 | import nezuko.utils.fun_strings as fun_strings 7 | from nezuko import BOT_ID, BOT_NAME, BOT_USERNAME, SUDOERS, app, arq 8 | from nezuko.core.decorators.errors import capture_err 9 | 10 | __MODULE__ = "Fun" 11 | __HELP__ = """ 12 | /weebify - To weebify a message. 13 | /wish - To get succession rate! (Just for fun-.-) 14 | /slap - To slap someone. 15 | 16 | """ 17 | 18 | 19 | normiefont = [ 20 | "a", 21 | "b", 22 | "c", 23 | "d", 24 | "e", 25 | "f", 26 | "g", 27 | "h", 28 | "i", 29 | "j", 30 | "k", 31 | "l", 32 | "m", 33 | "n", 34 | "o", 35 | "p", 36 | "q", 37 | "r", 38 | "s", 39 | "t", 40 | "u", 41 | "v", 42 | "w", 43 | "x", 44 | "y", 45 | "z", 46 | ] 47 | weebyfont = [ 48 | "卂", 49 | "乃", 50 | "匚", 51 | "刀", 52 | "乇", 53 | "下", 54 | "厶", 55 | "卄", 56 | "工", 57 | "丁", 58 | "长", 59 | "乚", 60 | "从", 61 | "𠘨", 62 | "口", 63 | "尸", 64 | "㔿", 65 | "尺", 66 | "丂", 67 | "丅", 68 | "凵", 69 | "リ", 70 | "山", 71 | "乂", 72 | "丫", 73 | "乙", 74 | ] 75 | 76 | 77 | def weebifytext(text): 78 | string = text.lower().replace(" ", " ") 79 | for normiecharacter in string: 80 | if normiecharacter in normiefont: 81 | weebycharacter = weebyfont[normiefont.index(normiecharacter)] 82 | string = string.replace(normiecharacter, weebycharacter) 83 | return string 84 | 85 | 86 | @app.on_message(filters.command(["weebify", f"weebify@{BOT_USERNAME}"])) 87 | @capture_err 88 | async def weebify(client, message: Message): 89 | if message.reply_to_message: 90 | return await message.reply_text( 91 | weebifytext(message.reply_to_message.text) 92 | ) 93 | if len(message.command) < 2: 94 | return await message.reply_text( 95 | "reply **/weebify** To a message for weebify or use **/weebify Your Text**" 96 | ) 97 | message.command.pop(0) 98 | return await message.reply_text(weebifytext(" ".join(message.command))) 99 | 100 | 101 | @app.on_message( 102 | filters.command(["slap", f"slap@{BOT_USERNAME}"]) & ~filters.private 103 | ) 104 | @capture_err 105 | async def slap(client, message: Message): 106 | if message.reply_to_message: 107 | try: 108 | if message.reply_to_message.from_user.id == BOT_ID: 109 | return await message.reply_text( 110 | "Stop slapping me. REEEEEEEEEEEEEE." 111 | ) 112 | except: 113 | return await message.reply_text( 114 | "You Cann't Slap an Anon Admin. :p" 115 | ) 116 | else: 117 | try: 118 | user1 = message.from_user.first_name 119 | except: 120 | user1 = message.chat.title 121 | try: 122 | user2 = message.reply_to_message.from_user.first_name 123 | except: 124 | user2 = message.chat.title 125 | temp = random.choice(fun_strings.SLAP_TEMPLATES) 126 | item = random.choice(fun_strings.ITEMS) 127 | hit = random.choice(fun_strings.HIT) 128 | throw = random.choice(fun_strings.THROW) 129 | reply = temp.format( 130 | user1=user1, user2=user2, item=item, hits=hit, throws=throw 131 | ) 132 | return await message.reply_text( 133 | reply, reply_to_message_id=message.reply_to_message.id 134 | ) 135 | else: 136 | user1 = BOT_NAME 137 | try: 138 | user2 = message.from_user.first_name 139 | except: 140 | user2 = message.chat.title 141 | temp = random.choice(fun_strings.SLAP_TEMPLATES) 142 | item = random.choice(fun_strings.ITEMS) 143 | hit = random.choice(fun_strings.HIT) 144 | throw = random.choice(fun_strings.THROW) 145 | reply = temp.format( 146 | user1=user1, user2=user2, item=item, hits=hit, throws=throw 147 | ) 148 | return await message.reply_text(reply) 149 | 150 | 151 | @app.on_message(filters.command(["wish", f"wish@{BOT_USERNAME}"])) 152 | @capture_err 153 | async def wish(client, message: Message): 154 | if message.reply_to_message: 155 | return await message.reply_text( 156 | f"Your Wish **{message.reply_to_message.text}** Has {random.randint(1,99)}% Succession Rate!" 157 | ) 158 | if len(message.command) < 2: 159 | return await message.reply_text( 160 | "reply **/wish** To a message for wish or use **/wish Your Wish**" 161 | ) 162 | message.command.pop(0) 163 | return await message.reply_text( 164 | f"Your Wish **{' '.join(message.command)}** Has {random.randint(1,99)}% Succession Rate!" 165 | ) 166 | -------------------------------------------------------------------------------- /nezuko/modules/global_stats.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import asyncio 25 | 26 | from pyrogram import filters 27 | from pyrogram.errors import FloodWait 28 | 29 | from nezuko import BOT_ID, BOT_NAME, SUDOERS, app 30 | from nezuko.core.decorators.errors import capture_err 31 | from nezuko.modules import ALL_MODULES 32 | from nezuko.utils.dbfunctions import ( 33 | get_blacklist_filters_count, 34 | get_filters_count, 35 | get_gbans_count, 36 | get_karmas_count, 37 | get_notes_count, 38 | get_rss_feeds_count, 39 | get_served_chats, 40 | get_served_users, 41 | get_warns_count, 42 | remove_served_chat, 43 | ) 44 | from nezuko.utils.http import get 45 | from nezuko.utils.inlinefuncs import keywords_list 46 | 47 | 48 | @app.on_message(filters.command("clean_db") & filters.user(SUDOERS)) 49 | @capture_err 50 | async def clean_db(_, message): 51 | served_chats = [int(i["chat_id"]) for i in (await get_served_chats())] 52 | m = await message.reply( 53 | f"__**Cleaning database, Might take around {len(served_chats)*2} seconds.**__", 54 | ) 55 | for served_chat in served_chats: 56 | try: 57 | await app.get_chat_members(served_chat, BOT_ID) 58 | await asyncio.sleep(2) 59 | except FloodWait as e: 60 | await asyncio.sleep(int(e.x)) 61 | except Exception: 62 | await remove_served_chat(served_chat) 63 | served_chats.remove(served_chat) 64 | await m.edit("**Database Cleaned.**") 65 | 66 | 67 | @app.on_message(filters.command("gstats") & filters.user(SUDOERS)) 68 | @capture_err 69 | async def global_stats(_, message): 70 | m = await app.send_message( 71 | message.chat.id, 72 | text="__**Analysing Stats...**__", 73 | disable_web_page_preview=True, 74 | ) 75 | 76 | # For bot served chat and users count 77 | served_chats = len(await get_served_chats()) 78 | served_users = len(await get_served_users()) 79 | # Gbans count 80 | gbans = await get_gbans_count() 81 | _notes = await get_notes_count() 82 | notes_count = _notes["notes_count"] 83 | notes_chats_count = _notes["chats_count"] 84 | 85 | # Filters count across chats 86 | _filters = await get_filters_count() 87 | filters_count = _filters["filters_count"] 88 | filters_chats_count = _filters["chats_count"] 89 | 90 | # Blacklisted filters count across chats 91 | _filters = await get_blacklist_filters_count() 92 | blacklist_filters_count = _filters["filters_count"] 93 | blacklist_filters_chats_count = _filters["chats_count"] 94 | 95 | # Warns count across chats 96 | _warns = await get_warns_count() 97 | warns_count = _warns["warns_count"] 98 | warns_chats_count = _warns["chats_count"] 99 | 100 | # Karmas count across chats 101 | _karmas = await get_karmas_count() 102 | karmas_count = _karmas["karmas_count"] 103 | karmas_chats_count = _karmas["chats_count"] 104 | 105 | # Contributors/Developers count and commits on github 106 | url = "https://api.github.com/repos/rozari0/nezukobot/contributors" 107 | rurl = "https://github.com/rozari0/nezukobot" 108 | developers = await get(url) 109 | commits = sum(developer["contributions"] for developer in developers) 110 | developers = len(developers) 111 | 112 | # Rss feeds 113 | rss_count = await get_rss_feeds_count() 114 | # Modules info 115 | modules_count = len(ALL_MODULES) 116 | 117 | # Userbot info 118 | groups_ub = channels_ub = bots_ub = privates_ub = total_ub = 0 119 | 120 | msg = f""" 121 | **Global Stats of {BOT_NAME}**: 122 | **{modules_count}** Modules Loaded. 123 | **{len(keywords_list)}** Inline Modules Loaded. 124 | **{rss_count}** Active RSS Feeds. 125 | **{gbans}** Globally banned users. 126 | **{filters_count}** Filters, Across **{filters_chats_count}** chats. 127 | **{blacklist_filters_count}** Blacklist Filters, Across **{blacklist_filters_chats_count}** chats. 128 | **{notes_count}** Notes, Across **{notes_chats_count}** chats. 129 | **{warns_count}** Warns, Across **{warns_chats_count}** chats. 130 | **{karmas_count}** Karma, Across **{karmas_chats_count}** chats. 131 | **{served_users}** Users, Across **{served_chats}** chats. 132 | **{developers}** Developers And **{commits}** Commits On **[Github]({rurl})**. 133 | 134 | """ 135 | await m.edit(msg, disable_web_page_preview=True) 136 | -------------------------------------------------------------------------------- /nezuko/modules/imdb.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 rozari0 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from imdb import IMDb 25 | from pyrogram import filters 26 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 27 | 28 | from nezuko import app 29 | 30 | ia = IMDb() 31 | 32 | 33 | @app.on_message(filters.command("imdb")) 34 | async def imdb(client, message): 35 | """ 36 | .imdb 37 | """ 38 | if len(message.command) > 1: 39 | movie = " ".join(message.command[1:]) 40 | else: 41 | return await message.reply_text("Please enter a movie name") 42 | try: 43 | movie = ia.get_movie(ia.search_movie(movie)[0].movieID) 44 | except: 45 | return await message.edit("Movie not found") 46 | 47 | reply_markup = InlineKeyboardMarkup( 48 | [ 49 | [ 50 | InlineKeyboardButton( 51 | "More Info", 52 | url=f"https://www.imdb.com/title/tt{movie.movieID}", 53 | ) 54 | ] 55 | ] 56 | ) 57 | _writers, _directors, _casts = [], [], [] 58 | try: 59 | for _i in movie["writer"]: 60 | _writers.append(_i["name"]) 61 | except: 62 | pass 63 | try: 64 | for _i in movie["director"]: 65 | _directors.append(_i["name"]) 66 | except: 67 | pass 68 | try: 69 | for _i in movie["cast"]: 70 | _casts.append(_i["name"]) 71 | _casts = _casts[:4] if len(_casts) >= 5 else _casts 72 | except: 73 | pass 74 | caption = f"{movie['kind'].capitalize()}\n======\n\ 75 | Title: {movie['title']}\n\ 76 | Year: {movie['year']}\n\ 77 | Rating: {movie['rating'] if 'rating' in movie else 'Not Found'}\n\ 78 | Genre: {', '.join(movie['genres'])}\n\ 79 | Runtime: {movie['runtime'][0] if 'runtime' in movie else 'Not Found'}\n\ 80 | Writers: {', '.join(_writers)}\n\ 81 | Directors: {', '.join(_directors)}\n\ 82 | Actors: {', '.join(_casts)}\n\ 83 | Language: {movie['language'] if 'language' in movie else 'Not Found'}\n\ 84 | Country: {movie['country'] if 'country' in movie else 'Not Found'}\n\ 85 | " 86 | try: 87 | m = await message.reply_photo( 88 | movie["full-size cover url"], 89 | caption=caption, 90 | reply_markup=reply_markup, 91 | ) 92 | except KeyError: 93 | m = await message.reply_photo( 94 | movie["cover url"], caption=caption, reply_markup=reply_markup 95 | ) 96 | 97 | return await m.reply_text( 98 | f"Plot: {movie['plot outline'] if 'plot outline' in movie else 'Not available'}" 99 | ) 100 | -------------------------------------------------------------------------------- /nezuko/modules/img_pdf.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including witout limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from io import BytesIO 25 | from os import path, remove 26 | from time import time 27 | 28 | import img2pdf 29 | from PIL import Image 30 | from pyrogram import filters 31 | from pyrogram.types import Message 32 | 33 | from nezuko import app 34 | from nezuko.core.decorators.errors import capture_err 35 | from nezuko.core.sections import section 36 | 37 | 38 | async def convert( 39 | main_message: Message, 40 | reply_messages, 41 | status_message: Message, 42 | start_time: float, 43 | ): 44 | m = status_message 45 | 46 | documents = [] 47 | 48 | for message in reply_messages: 49 | if not message.document: 50 | return await m.edit("Not document, ABORTED!") 51 | 52 | if message.document.mime_type.split("/")[0] != "image": 53 | return await m.edit("Invalid mime type!") 54 | 55 | if message.document.file_size > 5000000: 56 | return await m.edit("Size too large, ABORTED!") 57 | documents.append(await message.download()) 58 | 59 | for img_path in documents: 60 | img = Image.open(img_path).convert("RGB") 61 | img.save(img_path, "JPEG", quality=100) 62 | 63 | pdf = BytesIO(img2pdf.convert(documents)) 64 | pdf.name = "wbb.pdf" 65 | 66 | if len(main_message.command) >= 2: 67 | pdf.name = main_message.text.split(None, 1)[1] 68 | 69 | elapsed = round(time() - start_time, 2) 70 | 71 | await main_message.reply_document( 72 | document=pdf, 73 | caption=section( 74 | "IMG2PDF", 75 | body={ 76 | "Title": pdf.name, 77 | "Size": f"{pdf.__sizeof__() / (10**6)}MB", 78 | "Pages": len(documents), 79 | "Took": f"{elapsed}s", 80 | }, 81 | ), 82 | ) 83 | 84 | await m.delete() 85 | pdf.close() 86 | for file in documents: 87 | if path.exists(file): 88 | remove(file) 89 | 90 | 91 | @app.on_message(filters.command("pdf")) 92 | @capture_err 93 | async def img_to_pdf(_, message: Message): 94 | reply = message.reply_to_message 95 | if not reply: 96 | return await message.reply( 97 | "Reply to an image (as document) or group of images." 98 | ) 99 | 100 | m = await message.reply_text("Converting..") 101 | start_time = time() 102 | 103 | if reply.media_group_id: 104 | messages = await app.get_media_group( 105 | message.chat.id, 106 | reply.id, 107 | ) 108 | return await convert(message, messages, m, start_time) 109 | 110 | return await convert(message, [reply], m, start_time) 111 | -------------------------------------------------------------------------------- /nezuko/modules/info.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import os 25 | 26 | from pyrogram import filters 27 | from pyrogram.types import Message 28 | 29 | from nezuko import SUDOERS, app 30 | from nezuko.core.sections import section 31 | from nezuko.utils.dbfunctions import is_gbanned_user, user_global_karma 32 | 33 | __MODULE__ = "Info" 34 | __HELP__ = """ 35 | /info [USERNAME|ID] - Get info about a user. 36 | /chat_info [USERNAME|ID] - Get info about a chat. 37 | """ 38 | 39 | 40 | async def get_user_info(user, already=False): 41 | if not already: 42 | user = await app.get_users(user) 43 | if not user.first_name: 44 | return ["Deleted account", None] 45 | user_id = user.id 46 | username = user.username 47 | first_name = user.first_name 48 | mention = user.mention("Link") 49 | dc_id = user.dc_id 50 | photo_id = user.photo.big_file_id if user.photo else None 51 | is_gbanned = await is_gbanned_user(user_id) 52 | is_sudo = user_id in SUDOERS 53 | karma = await user_global_karma(user_id) 54 | body = { 55 | "ID": user_id, 56 | "DC": dc_id, 57 | "Name": [first_name], 58 | "Username": [("@" + username) if username else "Null"], 59 | "Mention": [mention], 60 | "Sudo": is_sudo, 61 | "Karma": karma, 62 | "Gbanned": is_gbanned, 63 | } 64 | caption = section("User info", body) 65 | return [caption, photo_id] 66 | 67 | 68 | async def get_chat_info(chat, already=False): 69 | if not already: 70 | chat = await app.get_chat(chat) 71 | chat_id = chat.id 72 | username = chat.username 73 | title = chat.title 74 | type_ = chat.type 75 | is_scam = chat.is_scam 76 | description = chat.description 77 | members = chat.members_count 78 | is_restricted = chat.is_restricted 79 | link = f"[Link](t.me/{username})" if username else "Null" 80 | dc_id = chat.dc_id 81 | photo_id = chat.photo.big_file_id if chat.photo else None 82 | body = { 83 | "ID": chat_id, 84 | "DC": dc_id, 85 | "Type": type_, 86 | "Name": [title], 87 | "Username": [("@" + username) if username else "Null"], 88 | "Mention": [link], 89 | "Members": members, 90 | "Scam": is_scam, 91 | "Restricted": is_restricted, 92 | "Description": [description], 93 | } 94 | caption = section("Chat info", body) 95 | return [caption, photo_id] 96 | 97 | 98 | @app.on_message(filters.command("info")) 99 | async def info_func(_, message: Message): 100 | if message.reply_to_message: 101 | user = message.reply_to_message.from_user.id 102 | elif len(message.command) == 1: 103 | user = message.from_user.id 104 | else: 105 | user = message.text.split(None, 1)[1] 106 | 107 | m = await message.reply_text("Processing") 108 | 109 | try: 110 | info_caption, photo_id = await get_user_info(user) 111 | except Exception as e: 112 | return await m.edit(str(e)) 113 | 114 | if not photo_id: 115 | return await m.edit(info_caption, disable_web_page_preview=True) 116 | photo = await app.download_media(photo_id) 117 | 118 | await message.reply_photo(photo, caption=info_caption, quote=False) 119 | await m.delete() 120 | os.remove(photo) 121 | 122 | 123 | @app.on_message(filters.command("chat_info")) 124 | async def chat_info_func(_, message: Message): 125 | try: 126 | if len(message.command) > 2: 127 | return await message.reply_text( 128 | "**Usage:**/chat_info [USERNAME|ID]" 129 | ) 130 | 131 | if len(message.command) == 1: 132 | chat = message.chat.id 133 | elif len(message.command) == 2: 134 | chat = message.text.split(None, 1)[1] 135 | 136 | m = await message.reply_text("Processing") 137 | 138 | info_caption, photo_id = await get_chat_info(chat) 139 | if not photo_id: 140 | return await m.edit(info_caption, disable_web_page_preview=True) 141 | 142 | photo = await app.download_media(photo_id) 143 | await message.reply_photo(photo, caption=info_caption, quote=False) 144 | 145 | await m.delete() 146 | os.remove(photo) 147 | except Exception as e: 148 | await m.edit(e) 149 | -------------------------------------------------------------------------------- /nezuko/modules/locks.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pyrogram import filters 25 | from pyrogram.errors.exceptions.bad_request_400 import ChatNotModified 26 | from pyrogram.types import ChatPermissions 27 | 28 | from nezuko import SUDOERS, app 29 | from nezuko.core.decorators.errors import capture_err 30 | from nezuko.core.decorators.permissions import adminsOnly 31 | from nezuko.modules.admin import current_chat_permissions, list_admins 32 | from nezuko.utils.functions import get_urls_from_text 33 | 34 | __MODULE__ = "Locks" 35 | __HELP__ = """ 36 | Commands: /lock | /unlock | /locks [No Parameters Required] 37 | 38 | Parameters: 39 | messages | stickers | gifs | media | games | polls 40 | 41 | inline | url | group_info | user_add | pin 42 | 43 | You can only pass the "all" parameter with /lock, not with /unlock 44 | 45 | Example: 46 | /lock all 47 | """ 48 | 49 | incorrect_parameters = "Incorrect Parameters, Check Locks Section In Help." 50 | # Using disable_preview as a switch for url checker 51 | # That way we won't need an additional db to check 52 | # If url lock is enabled/disabled for a chat 53 | data = { 54 | "messages": "can_send_messages", 55 | "stickers": "can_send_stickers", 56 | "gifs": "can_send_animations", 57 | "media": "can_send_media_messages", 58 | "games": "can_send_games", 59 | "inline": "can_use_inline_bots", 60 | "url": "can_add_web_page_previews", 61 | "polls": "can_send_polls", 62 | "group_info": "can_change_info", 63 | "useradd": "can_invite_users", 64 | "pin": "can_pin_messages", 65 | } 66 | 67 | 68 | async def tg_lock(message, permissions: list, perm: str, lock: bool): 69 | if lock: 70 | if perm not in permissions: 71 | return await message.reply_text("Already locked.") 72 | permissions.remove(perm) 73 | elif perm in permissions: 74 | return await message.reply_text("Already Unlocked.") 75 | else: 76 | permissions.append(perm) 77 | 78 | permissions = {perm: True for perm in list(set(permissions))} 79 | 80 | try: 81 | await app.set_chat_permissions( 82 | message.chat.id, ChatPermissions(**permissions) 83 | ) 84 | except ChatNotModified: 85 | return await message.reply_text( 86 | "To unlock this, you have to unlock 'messages' first." 87 | ) 88 | 89 | await message.reply_text(("Locked." if lock else "Unlocked.")) 90 | 91 | 92 | @app.on_message(filters.command(["lock", "unlock"]) & ~filters.private) 93 | @adminsOnly("can_restrict_members") 94 | async def locks_func(_, message): 95 | if len(message.command) != 2: 96 | return await message.reply_text(incorrect_parameters) 97 | 98 | chat_id = message.chat.id 99 | parameter = message.text.strip().split(None, 1)[1].lower() 100 | state = message.command[0].lower() 101 | 102 | if parameter not in data and parameter != "all": 103 | return await message.reply_text(incorrect_parameters) 104 | 105 | permissions = await current_chat_permissions(chat_id) 106 | 107 | if parameter in data: 108 | await tg_lock( 109 | message, 110 | permissions, 111 | data[parameter], 112 | bool(state == "lock"), 113 | ) 114 | elif parameter == "all" and state == "lock": 115 | await app.set_chat_permissions(chat_id, ChatPermissions()) 116 | await message.reply_text(f"Locked Everything in {message.chat.title}") 117 | 118 | elif parameter == "all" and state == "unlock": 119 | await app.set_chat_permissions( 120 | chat_id, 121 | ChatPermissions( 122 | can_send_messages=True, 123 | can_send_media_messages=True, 124 | can_send_stickers=True, 125 | can_send_animations=True, 126 | can_invite_users=True, 127 | can_send_games=True, 128 | can_use_inline_bots=True, 129 | can_send_polls=True, 130 | can_add_web_page_previews=True, 131 | ), 132 | ) 133 | await message.reply(f"Unlocked Everything in {message.chat.title}") 134 | 135 | 136 | @app.on_message(filters.command("locks") & ~filters.private) 137 | @capture_err 138 | async def locktypes(_, message): 139 | permissions = await current_chat_permissions(message.chat.id) 140 | 141 | if not permissions: 142 | return await message.reply_text("No Permissions.") 143 | 144 | perms = "".join(f"__**{i}**__\n" for i in permissions) 145 | await message.reply_text(perms) 146 | 147 | 148 | @app.on_message(filters.text & ~filters.private, group=69) 149 | async def url_detector(_, message): 150 | user = message.from_user 151 | chat_id = message.chat.id 152 | text = message.text.lower().strip() 153 | 154 | if not text or not user: 155 | return 156 | if user.id in (SUDOERS + (await list_admins(chat_id))): 157 | return 158 | 159 | check = get_urls_from_text(text) 160 | if check: 161 | permissions = await current_chat_permissions(chat_id) 162 | if "can_add_web_page_previews" not in permissions: 163 | try: 164 | await message.delete() 165 | except Exception: 166 | await message.reply_text( 167 | "This message contains a URL, " 168 | + "but i don't have enough permissions to delete it" 169 | ) 170 | -------------------------------------------------------------------------------- /nezuko/modules/mongo_backup.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from os import remove 25 | from os import system as execute 26 | 27 | from pyrogram import filters 28 | from pyrogram.types import Message 29 | 30 | from nezuko import MONGO_URL, SUDOERS, app 31 | 32 | 33 | @app.on_message( 34 | filters.command("backup") & filters.user(SUDOERS) & filters.private 35 | ) 36 | async def backup(_, message: Message): 37 | m = await message.reply("Backing up data...") 38 | 39 | code = execute(f'mongodump --uri "{MONGO_URL}"') 40 | if int(code) != 0: 41 | return await m.edit( 42 | "Looks like you don't have mongo-database-tools installed " 43 | + "grab it from mongodb.com/try/download/database-tools" 44 | ) 45 | 46 | code = execute("zip backup.zip -r9 dump/*") 47 | if int(code) != 0: 48 | return await m.edit( 49 | "Looks like you don't have `zip` package installed, BACKUP FAILED!" 50 | ) 51 | 52 | await message.reply_document("backup.zip") 53 | await m.delete() 54 | remove("backup.zip") 55 | -------------------------------------------------------------------------------- /nezuko/modules/music.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from __future__ import unicode_literals 25 | 26 | import os 27 | from asyncio import get_running_loop 28 | from functools import partial 29 | from io import BytesIO 30 | from urllib.parse import urlparse 31 | 32 | import ffmpeg 33 | import youtube_dl 34 | from pyrogram import filters 35 | 36 | from nezuko import aiohttpsession as session 37 | from nezuko import app, arq 38 | from nezuko.core.decorators.errors import capture_err 39 | from nezuko.utils.pastebin import paste 40 | 41 | __MODULE__ = "Music" 42 | __HELP__ = """ 43 | /ytmusic [link] To Download Music From Various Websites Including Youtube. [SUDOERS] 44 | /saavn [query] To Download Music From Saavn. 45 | /lyrics [query] To Get Lyrics Of A Song. 46 | """ 47 | 48 | is_downloading = False 49 | 50 | 51 | def get_file_extension_from_url(url): 52 | url_path = urlparse(url).path 53 | basename = os.path.basename(url_path) 54 | return basename.split(".")[-1] 55 | 56 | 57 | def download_youtube_audio(url: str): 58 | global is_downloading 59 | with youtube_dl.YoutubeDL( 60 | { 61 | "format": "bestaudio", 62 | "writethumbnail": True, 63 | "quiet": True, 64 | } 65 | ) as ydl: 66 | info_dict = ydl.extract_info(url, download=False) 67 | if int(float(info_dict["duration"])) > 600: 68 | is_downloading = False 69 | return [] 70 | ydl.process_info(info_dict) 71 | audio_file = ydl.prepare_filename(info_dict) 72 | basename = audio_file.rsplit(".", 1)[-2] 73 | if info_dict["ext"] == "webm": 74 | audio_file_opus = basename + ".opus" 75 | ffmpeg.input(audio_file).output( 76 | audio_file_opus, codec="copy", loglevel="error" 77 | ).overwrite_output().run() 78 | os.remove(audio_file) 79 | audio_file = audio_file_opus 80 | thumbnail_url = info_dict["thumbnail"] 81 | thumbnail_file = ( 82 | basename + "." + get_file_extension_from_url(thumbnail_url) 83 | ) 84 | title = info_dict["title"] 85 | try: 86 | performer = info_dict["artist"] 87 | except: 88 | performer = info_dict["uploader"] 89 | duration = int(float(info_dict["duration"])) 90 | return [title, performer, duration, audio_file, thumbnail_file] 91 | 92 | 93 | @app.on_message(filters.command("ytmusic")) 94 | @capture_err 95 | async def music(_, message): 96 | global is_downloading 97 | if len(message.command) != 2: 98 | return await message.reply_text("/ytmusic needs a link as argument") 99 | url = message.text.split(None, 1)[1] 100 | if is_downloading: 101 | return await message.reply_text( 102 | "Another download is in progress, try again after sometime." 103 | ) 104 | is_downloading = True 105 | m = await message.reply_text( 106 | f"Downloading {url}", disable_web_page_preview=True 107 | ) 108 | try: 109 | loop = get_running_loop() 110 | music = await loop.run_in_executor( 111 | None, partial(download_youtube_audio, url) 112 | ) 113 | if not music: 114 | await m.edit("Too Long, Can't Download.") 115 | ( 116 | title, 117 | performer, 118 | duration, 119 | audio_file, 120 | thumbnail_file, 121 | ) = music 122 | except Exception as e: 123 | is_downloading = False 124 | return await m.edit(str(e)) 125 | await message.reply_audio( 126 | audio_file, 127 | duration=duration, 128 | performer=performer, 129 | title=title, 130 | thumb=thumbnail_file, 131 | ) 132 | await m.delete() 133 | os.remove(audio_file) 134 | os.remove(thumbnail_file) 135 | is_downloading = False 136 | 137 | 138 | # Funtion To Download Song 139 | async def download_song(url): 140 | async with session.get(url) as resp: 141 | song = await resp.read() 142 | song = BytesIO(song) 143 | song.name = "a.mp3" 144 | return song 145 | 146 | 147 | # Jiosaavn Music 148 | 149 | 150 | @app.on_message(filters.command("saavn")) 151 | @capture_err 152 | async def jssong(_, message): 153 | global is_downloading 154 | if len(message.command) < 2: 155 | return await message.reply_text("/saavn requires an argument.") 156 | if is_downloading: 157 | return await message.reply_text( 158 | "Another download is in progress, try again after sometime." 159 | ) 160 | is_downloading = True 161 | text = message.text.split(None, 1)[1] 162 | m = await message.reply_text("Searching...") 163 | try: 164 | songs = await arq.saavn(text) 165 | if not songs.ok: 166 | await m.edit(songs.result) 167 | is_downloading = False 168 | return 169 | sname = songs.result[0].song 170 | slink = songs.result[0].media_url 171 | ssingers = songs.result[0].singers 172 | sduration = songs.result[0].duration 173 | await m.edit("Downloading") 174 | song = await download_song(slink) 175 | await m.edit("Uploading") 176 | await message.reply_audio( 177 | audio=song, 178 | title=sname, 179 | performer=ssingers, 180 | duration=sduration, 181 | ) 182 | await m.delete() 183 | except Exception as e: 184 | is_downloading = False 185 | return await m.edit(str(e)) 186 | is_downloading = False 187 | song.close() 188 | 189 | 190 | # Lyrics 191 | 192 | 193 | @app.on_message(filters.command("lyrics")) 194 | async def lyrics_func(_, message): 195 | if len(message.command) < 2: 196 | return await message.reply_text("**Usage:**\n/lyrics [QUERY]") 197 | m = await message.reply_text("**Searching**") 198 | query = message.text.strip().split(None, 1)[1] 199 | song = await arq.lyrics(query) 200 | lyrics = song.result 201 | if len(lyrics) < 4095: 202 | return await m.edit(f"__{lyrics}__") 203 | lyrics = await paste(lyrics) 204 | await m.edit(f"**LYRICS_TOO_LONG:** [URL]({lyrics})") 205 | -------------------------------------------------------------------------------- /nezuko/modules/notes.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from re import findall 25 | 26 | from pyrogram import filters 27 | 28 | from nezuko import LOG_GROUP_ID as USERBOT_ID 29 | from nezuko import SUDOERS, app, eor 30 | from nezuko.core.decorators.errors import capture_err 31 | from nezuko.core.decorators.permissions import adminsOnly 32 | from nezuko.core.keyboard import ikb 33 | from nezuko.utils.dbfunctions import ( 34 | delete_note, 35 | get_note, 36 | get_note_names, 37 | save_note, 38 | ) 39 | from nezuko.utils.functions import extract_text_and_keyb 40 | 41 | __MODULE__ = "Notes" 42 | __HELP__ = """/notes To Get All The Notes In The Chat. 43 | 44 | /save [NOTE_NAME] To Save A Note (Can be a sticker or text). 45 | 46 | #NOTE_NAME To Get A Note. 47 | 48 | /delete [NOTE_NAME] To Delete A Note. 49 | 50 | Checkout /markdownhelp to know more about formattings and other syntax. 51 | """ 52 | 53 | USERBOT_PREFIX = "." 54 | 55 | 56 | @app.on_message(filters.command("save") & ~filters.private) 57 | @adminsOnly("can_change_info") 58 | async def save_notee(_, message): 59 | if len(message.command) < 2 or not message.reply_to_message: 60 | await eor( 61 | message, 62 | text="**Usage:**\nReply to a text or sticker with /save [NOTE_NAME] to save it.", 63 | ) 64 | 65 | elif ( 66 | not message.reply_to_message.text 67 | and not message.reply_to_message.sticker 68 | ): 69 | await eor( 70 | message, 71 | text="__**You can only save text or stickers in notes.**__", 72 | ) 73 | else: 74 | name = message.text.split(None, 1)[1].strip() 75 | if not name: 76 | return await eor(message, text="**Usage**\n__/save [NOTE_NAME]__") 77 | _type = "text" if message.reply_to_message.text else "sticker" 78 | note = { 79 | "type": _type, 80 | "data": message.reply_to_message.text.markdown 81 | if _type == "text" 82 | else message.reply_to_message.sticker.file_id, 83 | } 84 | prefix = message.text.split()[0][0] 85 | chat_id = message.chat.id if prefix != USERBOT_PREFIX else USERBOT_ID 86 | await save_note(chat_id, name, note) 87 | await eor(message, text=f"__**Saved note {name}.**__") 88 | 89 | 90 | @app.on_message(filters.command("notes") & ~filters.private) 91 | @capture_err 92 | async def get_notes(_, message): 93 | prefix = message.text.split()[0][0] 94 | is_ubot = bool(prefix == USERBOT_PREFIX) 95 | chat_id = USERBOT_ID if is_ubot else message.chat.id 96 | 97 | _notes = await get_note_names(chat_id) 98 | 99 | if not _notes: 100 | return await eor(message, text="**No notes in this chat.**") 101 | _notes.sort() 102 | msg = f"List of notes in {'USERBOT' if is_ubot else message.chat.title}\n" 103 | for note in _notes: 104 | msg += f"**-** `{note}`\n" 105 | await eor(message, text=msg) 106 | 107 | 108 | async def get_one_note_userbot(_, message): 109 | if len(message.text.split()) < 2: 110 | return await eor(message, text="Invalid arguments") 111 | 112 | name = message.text.split(None, 1)[1] 113 | 114 | _note = await get_note(USERBOT_ID, name) 115 | if not _note: 116 | return await eor(message, text="No such note.") 117 | 118 | if _note["type"] == "text": 119 | data = _note["data"] 120 | await eor( 121 | message, 122 | text=data, 123 | disable_web_page_preview=True, 124 | ) 125 | else: 126 | await message.reply_sticker(_note["data"]) 127 | 128 | 129 | @app.on_message(filters.regex(r"^#.+") & filters.text & ~filters.private) 130 | @capture_err 131 | async def get_one_note(_, message): 132 | name = message.text.replace("#", "", 1) 133 | if not name: 134 | return 135 | _note = await get_note(message.chat.id, name) 136 | if not _note: 137 | return 138 | if _note["type"] == "text": 139 | data = _note["data"] 140 | keyb = None 141 | if findall(r"\[.+\,.+\]", data): 142 | keyboard = extract_text_and_keyb(ikb, data) 143 | if keyboard: 144 | data, keyb = keyboard 145 | await message.reply_text( 146 | data, 147 | reply_markup=keyb, 148 | disable_web_page_preview=True, 149 | ) 150 | else: 151 | await message.reply_sticker(_note["data"]) 152 | 153 | 154 | @app.on_message(filters.command("delete") & ~filters.private) 155 | @adminsOnly("can_change_info") 156 | async def del_note(_, message): 157 | if len(message.command) < 2: 158 | return await eor(message, text="**Usage**\n__/delete [NOTE_NAME]__") 159 | name = message.text.split(None, 1)[1].strip() 160 | if not name: 161 | return await eor(message, text="**Usage**\n__/delete [NOTE_NAME]__") 162 | 163 | prefix = message.text.split()[0][0] 164 | is_ubot = bool(prefix == USERBOT_PREFIX) 165 | chat_id = USERBOT_ID if is_ubot else message.chat.id 166 | 167 | deleted = await delete_note(chat_id, name) 168 | if deleted: 169 | await eor(message, text=f"**Deleted note {name} successfully.**") 170 | else: 171 | await eor(message, text="**No such note.**") 172 | -------------------------------------------------------------------------------- /nezuko/modules/parse_preview.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep 2 | 3 | from pyrogram import filters 4 | from pyrogram.types import Message 5 | 6 | from nezuko import SUDOERS, app, eor 7 | from nezuko.core.sections import section 8 | 9 | 10 | @app.on_message( 11 | filters.command("parse_preview") & filters.user(SUDOERS), 12 | ) 13 | async def parse(_, message: Message): 14 | r = message.reply_to_message 15 | has_wpp = False 16 | 17 | m_ = await eor(message, text="Parsing...") 18 | if not r: 19 | return await m_.edit("Reply to a message with a webpage") 20 | 21 | if not r.web_page: 22 | text = r.text or r.caption 23 | if text: 24 | m = await app.send_message(m_.chat.id, text) 25 | await sleep(1) 26 | await m.delete() 27 | if m.web_page: 28 | r = m 29 | has_wpp = True 30 | else: 31 | has_wpp = True 32 | 33 | if not has_wpp: 34 | return await m_.edit( 35 | "Replied message has no webpage preview.", 36 | ) 37 | 38 | wpp = r.web_page 39 | 40 | body = { 41 | "Title": [wpp.title or "Null"], 42 | "Description": [ 43 | (wpp.description[:50] + "...") if wpp.description else "Null" 44 | ], 45 | "URL": [wpp.display_url or "Null"], 46 | "Author": [wpp.author or "Null"], 47 | "Site Name": [wpp.site_name or "Null"], 48 | "Type": wpp.type or "Null", 49 | } 50 | 51 | text = section("Preview", body) 52 | 53 | t = wpp.type 54 | 55 | if t == "photo": 56 | media = wpp.photo 57 | func = app.send_photo 58 | elif t == "audio": 59 | media = wpp.audio 60 | func = app.send_audio 61 | elif t == "video": 62 | media = wpp.video 63 | func = app.send_video 64 | elif t == "document": 65 | media = wpp.document 66 | func = app.send_document 67 | else: 68 | media = None 69 | func = None 70 | 71 | if media and func: 72 | await m_.delete() 73 | return await func( 74 | m_.chat.id, 75 | media.file_id, 76 | caption=text, 77 | ) 78 | 79 | await m_.edit(text, disable_web_page_preview=True) 80 | -------------------------------------------------------------------------------- /nezuko/modules/paste.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import os 25 | import re 26 | 27 | import aiofiles 28 | from pyrogram import filters 29 | from pyrogram.types import Message 30 | 31 | from nezuko import SUDOERS, app, eor 32 | from nezuko.core.decorators.errors import capture_err 33 | from nezuko.core.keyboard import ikb 34 | from nezuko.utils.pastebin import paste 35 | 36 | __MODULE__ = "Paste" 37 | __HELP__ = "/paste - To Paste Replied Text Or Document To A Pastebin" 38 | pattern = re.compile(r"^text/|json$|yaml$|xml$|toml$|x-sh$|x-shellscript$") 39 | 40 | 41 | @app.on_message(filters.command("paste")) 42 | @capture_err 43 | async def paste_func(_, message: Message): 44 | if not message.reply_to_message: 45 | return await eor(message, text="Reply To A Message With /paste") 46 | r = message.reply_to_message 47 | 48 | if not r.text and not r.document: 49 | return await eor( 50 | message, text="Only text and documents are supported." 51 | ) 52 | 53 | m = await eor(message, text="Pasting...") 54 | 55 | if r.text: 56 | content = str(r.text) 57 | elif r.document: 58 | if r.document.file_size > 40000: 59 | return await m.edit("You can only paste files smaller than 40KB.") 60 | 61 | if not pattern.search(r.document.mime_type): 62 | return await m.edit("Only text files can be pasted.") 63 | 64 | doc = await message.reply_to_message.download() 65 | 66 | async with aiofiles.open(doc, mode="r") as f: 67 | content = await f.read() 68 | 69 | os.remove(doc) 70 | 71 | link = await paste(content) 72 | kb = ikb({"Paste Link": link}) 73 | try: 74 | if m.from_user.is_bot: 75 | await message.reply_photo( 76 | photo=link, 77 | quote=False, 78 | reply_markup=kb, 79 | ) 80 | else: 81 | await message.reply_photo( 82 | photo=link, 83 | quote=False, 84 | caption=f"**Paste Link:** [Here]({link})", 85 | ) 86 | await m.delete() 87 | except Exception: 88 | await m.edit("Here's your paste", reply_markup=kb) 89 | -------------------------------------------------------------------------------- /nezuko/modules/pipes.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import asyncio 25 | 26 | from pyrogram import filters 27 | from pyrogram.types import Message 28 | 29 | from nezuko import BOT_ID, SUDOERS, app 30 | from nezuko.core.decorators.errors import capture_err 31 | 32 | __MODULE__ = "Pipes" 33 | __HELP__ = """ 34 | **THIS MODULE IS ONLY FOR DEVS** 35 | 36 | Use this module to create a pipe that will forward messages of one chat/channel to another. 37 | 38 | 39 | /activate_pipe [FROM_CHAT_ID] [TO_CHAT_ID] [BOT|USERBOT] 40 | 41 | Active a pipe. 42 | 43 | choose 'BOT' or 'USERBOT' according to your needs, 44 | this will decide which client will fetch the 45 | message from 'FROM_CHAT'. 46 | 47 | 48 | /deactivate_pipe [FROM_CHAT_ID] 49 | Deactivete a pipe. 50 | 51 | 52 | /show_pipes 53 | Show all the active pipes. 54 | 55 | **NOTE:** 56 | These pipes are only temporary, and will be destroyed 57 | on restart. 58 | """ 59 | pipes_list_bot = {} 60 | pipes_list_userbot = {} 61 | 62 | 63 | @app.on_message(~filters.me, group=500) 64 | @capture_err 65 | async def pipes_worker_bot(_, message: Message): 66 | chat_id = message.chat.id 67 | if chat_id in pipes_list_bot: 68 | await message.forward(pipes_list_bot[chat_id]) 69 | 70 | 71 | @app.on_message(filters.command("activate_pipe") & filters.user(SUDOERS)) 72 | @capture_err 73 | async def activate_pipe_func(_, message: Message): 74 | global pipes_list_bot, pipes_list_userbot 75 | 76 | if len(message.command) != 4: 77 | return await message.reply( 78 | "**Usage:**\n/activate_pipe [FROM_CHAT_ID] [TO_CHAT_ID] [BOT|USERBOT]" 79 | ) 80 | 81 | text = message.text.strip().split() 82 | 83 | from_chat = int(text[1]) 84 | to_chat = int(text[2]) 85 | fetcher = text[3].lower() 86 | 87 | if fetcher not in ["bot", "userbot"]: 88 | return await message.reply("Wrong fetcher, see help menu.") 89 | 90 | if from_chat in pipes_list_bot or from_chat in pipes_list_userbot: 91 | return await message.reply_text("This pipe is already active.") 92 | 93 | dict_ = pipes_list_bot 94 | if fetcher == "userbot": 95 | dict_ = pipes_list_userbot 96 | 97 | dict_[from_chat] = to_chat 98 | await message.reply_text("Activated pipe.") 99 | 100 | 101 | @app.on_message(filters.command("deactivate_pipe") & filters.user(SUDOERS)) 102 | @capture_err 103 | async def deactivate_pipe_func(_, message: Message): 104 | global pipes_list_bot, pipes_list_userbot 105 | 106 | if len(message.command) != 2: 107 | await message.reply_text("**Usage:**\n/deactivate_pipe [FROM_CHAT_ID]") 108 | return 109 | text = message.text.strip().split() 110 | from_chat = int(text[1]) 111 | 112 | if from_chat not in pipes_list_bot and from_chat not in pipes_list_userbot: 113 | await message.reply_text("This pipe is already inactive.") 114 | 115 | dict_ = pipes_list_bot 116 | if from_chat in pipes_list_userbot: 117 | dict_ = pipes_list_userbot 118 | 119 | del dict_[from_chat] 120 | await message.reply_text("Deactivated pipe.") 121 | 122 | 123 | @app.on_message(filters.command("pipes") & filters.user(SUDOERS)) 124 | @capture_err 125 | async def show_pipes_func(_, message: Message): 126 | pipes_list_bot.update(pipes_list_userbot) 127 | if not pipes_list_bot: 128 | return await message.reply_text("No pipe is active.") 129 | 130 | text = "".join( 131 | ( 132 | f"**Pipe:** `{count}`\n**From:** `{pipe[0]}`\n" 133 | + f"**To:** `{pipe[1]}`\n\n" 134 | ) 135 | for count, pipe in enumerate(pipes_list_bot.items(), 1) 136 | ) 137 | await message.reply_text(text) 138 | -------------------------------------------------------------------------------- /nezuko/modules/proxy.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | from asyncio import get_event_loop, sleep 26 | 27 | from pyrogram import filters 28 | from pyrogram.types import CallbackQuery, Message 29 | 30 | from nezuko import app, arq 31 | from nezuko.core.keyboard import ikb 32 | 33 | __MODULE__ = "Proxy" 34 | __HELP__ = ( 35 | "/proxy - Get socks5 proxy which you can" 36 | + " use with telegram or other things" 37 | ) 38 | 39 | proxies = [] 40 | 41 | 42 | async def get_proxies(): 43 | global proxies 44 | proxies = (await arq.proxy()).result 45 | 46 | 47 | loop = get_event_loop() 48 | loop.create_task(get_proxies()) 49 | 50 | 51 | def url_from_proxy(proxy: str) -> str: 52 | creds, proxy = proxy.split("//")[1:][0].split("@") 53 | user, passwd = creds.split(":") 54 | host, port = proxy.split(":") 55 | return ( 56 | f"https://t.me/socks?server={host}&port=" 57 | + f"{port}&user={user}&pass={passwd}" 58 | ) 59 | 60 | 61 | @app.on_message(filters.command("proxy")) 62 | async def proxy_func(_, message: Message): 63 | if len(proxies) == 0: 64 | await sleep(0.5) 65 | location = proxies[0].location 66 | proxy = proxies[0].proxy 67 | url = url_from_proxy(proxy) 68 | keyb = ikb( 69 | { 70 | "←": "proxy_arq_-1", 71 | "→": "proxy_arq_1", 72 | "Connect": url, 73 | } 74 | ) 75 | await message.reply_text( 76 | f""" 77 | **Proxy:** {proxy} 78 | **Location**: {location} 79 | 80 | **POWERED BY [ARQ](http://t.me/ARQUpdates)**""", 81 | reply_markup=keyb, 82 | disable_web_page_preview=True, 83 | ) 84 | 85 | 86 | @app.on_callback_query(filters.regex(r"proxy_arq_")) 87 | async def proxy_callback_func(_, cq: CallbackQuery): 88 | data = cq.data 89 | index = int(data.split("_")[-1]) 90 | location = proxies[index].location 91 | proxy = proxies[index].proxy 92 | url = url_from_proxy(proxy) 93 | keyb = ikb( 94 | { 95 | "←": f"proxy_arq_{index-1}", 96 | "→": f"proxy_arq_{index+1}", 97 | "Connect": url, 98 | } 99 | ) 100 | await cq.message.edit( 101 | f""" 102 | **Proxy:** {proxy} 103 | **Location**: {location} 104 | 105 | **POWERED BY [ARQ](http://t.me/ARQUpdates)**""", 106 | reply_markup=keyb, 107 | disable_web_page_preview=True, 108 | ) 109 | -------------------------------------------------------------------------------- /nezuko/modules/quotly.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from io import BytesIO 25 | from traceback import format_exc 26 | 27 | from pyrogram import filters 28 | from pyrogram.types import Message 29 | 30 | from nezuko import SUDOERS, app, arq 31 | from nezuko.core.decorators.errors import capture_err 32 | 33 | __MODULE__ = "Quotly" 34 | __HELP__ = """ 35 | /q - To quote a message. 36 | /q [INTEGER] - To quote more than 1 messages. 37 | /q r - to quote a message with it's reply 38 | 39 | Use .q to quote using userbot 40 | """ 41 | 42 | 43 | async def quotify(messages: list): 44 | response = await arq.quotly(messages) 45 | if not response.ok: 46 | return [False, response.result] 47 | sticker = response.result 48 | sticker = BytesIO(sticker) 49 | sticker.name = "sticker.webp" 50 | return [True, sticker] 51 | 52 | 53 | def getArg(message: Message) -> str: 54 | return message.text.strip().split(None, 1)[1].strip() 55 | 56 | 57 | def isArgInt(message: Message) -> list: 58 | count = getArg(message) 59 | try: 60 | count = int(count) 61 | return [True, count] 62 | except ValueError: 63 | return [False, 0] 64 | 65 | 66 | @app.on_message(filters.command("q") & ~filters.private) 67 | @capture_err 68 | async def quotly_func(client, message: Message): 69 | if not message.reply_to_message: 70 | return await message.reply_text("Reply to a message to quote it.") 71 | if not message.reply_to_message.text: 72 | return await message.reply_text( 73 | "Replied message has no text, can't quote it." 74 | ) 75 | m = await message.reply_text("Quoting Messages") 76 | if len(message.command) < 2: 77 | messages = [message.reply_to_message] 78 | 79 | elif len(message.command) == 2: 80 | arg = isArgInt(message) 81 | if arg[0]: 82 | if arg[1] < 2 or arg[1] > 10: 83 | return await m.edit("Argument must be between 2-10.") 84 | 85 | count = arg[1] 86 | 87 | # Fetching 5 extra messages so tha twe can ignore media 88 | # messages and still end up with correct offset 89 | messages = [ 90 | i 91 | for i in await client.get_messages( 92 | message.chat.id, 93 | range( 94 | message.reply_to_message.id, 95 | message.reply_to_message.id + (count + 5), 96 | ), 97 | replies=0, 98 | ) 99 | if not i.empty and not i.media 100 | ] 101 | messages = messages[:count] 102 | else: 103 | if getArg(message) != "r": 104 | return await m.edit( 105 | "Incorrect Argument, Pass **'r'** or **'INT'**, **EX:** __/q 2__" 106 | ) 107 | reply_message = await client.get_messages( 108 | message.chat.id, 109 | message.reply_to_message.id, 110 | replies=1, 111 | ) 112 | messages = [reply_message] 113 | else: 114 | return await m.edit( 115 | "Incorrect argument, check quotly module in help section." 116 | ) 117 | try: 118 | if not message: 119 | return await m.edit("Something went wrong.") 120 | 121 | sticker = await quotify(messages) 122 | if not sticker[0]: 123 | await message.reply_text(sticker[1]) 124 | return await m.delete() 125 | sticker = sticker[1] 126 | await message.reply_sticker(sticker) 127 | await m.delete() 128 | sticker.close() 129 | except Exception as e: 130 | await m.edit( 131 | "Something went wrong while quoting messages," 132 | + " This error usually happens when there's a " 133 | + " message containing something other than text," 134 | + " or one of the messages in-between are deleted." 135 | ) 136 | e = format_exc() 137 | print(e) 138 | -------------------------------------------------------------------------------- /nezuko/modules/reddit.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pyrogram import filters 25 | 26 | from nezuko import app, arq 27 | from nezuko.core.decorators.errors import capture_err 28 | from nezuko.utils.dbfunctions import get_nsfw_status 29 | 30 | __MODULE__ = "Reddit" 31 | __HELP__ = "/reddit [query] - results something from reddit" 32 | 33 | 34 | @app.on_message(filters.command("reddit")) 35 | @capture_err 36 | async def reddit(_, message): 37 | if len(message.command) != 2: 38 | return await message.reply_text("/reddit needs an argument") 39 | subreddit = message.text.split(None, 1)[1] 40 | m = await message.reply_text("Searching") 41 | reddit = await arq.reddit(subreddit) 42 | if not reddit.ok: 43 | return await m.edit(reddit.result) 44 | reddit = reddit.result 45 | nsfw = reddit.nsfw 46 | sreddit = reddit.subreddit 47 | title = reddit.title 48 | image = reddit.url 49 | link = reddit.postLink 50 | if nsfw: 51 | if await get_nsfw_status(message.chat.id) == False: 52 | return await m.edit( 53 | "NSFW content is disabled in this chat! Enable it using /nsfw" 54 | ) 55 | caption = f""" 56 | **Title:** `{title}` 57 | **Subreddit:** {sreddit} 58 | **PostLink:** {link}""" 59 | try: 60 | await message.reply_photo(photo=image, caption=caption) 61 | await m.delete() 62 | except Exception as e: 63 | await m.edit(e.MESSAGE) 64 | -------------------------------------------------------------------------------- /nezuko/modules/regex.py: -------------------------------------------------------------------------------- 1 | # https://github.com/PaulSonOfLars/tgbot/blob/master/tg_bot/modules/sed.py 2 | import re 3 | import sre_constants 4 | 5 | from pyrogram import filters 6 | 7 | from nezuko import app 8 | from nezuko.utils.filter_groups import regex_group 9 | 10 | __MODULE__ = "Sed" 11 | __HELP__ = "**Usage:**\ns/foo/bar" 12 | 13 | 14 | DELIMITERS = ("/", ":", "|", "_") 15 | 16 | 17 | @app.on_message( 18 | filters.regex(r"s([{}]).*?\1.*".format("".join(DELIMITERS))), 19 | group=regex_group, 20 | ) 21 | async def sed(_, message): 22 | if not message.text: 23 | return 24 | sed_result = separate_sed(message.text) 25 | if message.reply_to_message: 26 | if message.reply_to_message.text: 27 | to_fix = message.reply_to_message.text 28 | elif message.reply_to_message.caption: 29 | to_fix = message.reply_to_message.caption 30 | else: 31 | return 32 | try: 33 | repl, repl_with, flags = sed_result 34 | except Exception: 35 | return 36 | 37 | if not repl: 38 | return await message.reply_text( 39 | "You're trying to replace... " "nothing with something?" 40 | ) 41 | 42 | try: 43 | 44 | if infinite_checker(repl): 45 | return await message.reply_text("Nice try -_-") 46 | 47 | if "i" in flags and "g" in flags: 48 | text = re.sub(repl, repl_with, to_fix, flags=re.I).strip() 49 | elif "i" in flags: 50 | text = re.sub( 51 | repl, repl_with, to_fix, count=1, flags=re.I 52 | ).strip() 53 | elif "g" in flags: 54 | text = re.sub(repl, repl_with, to_fix).strip() 55 | else: 56 | text = re.sub(repl, repl_with, to_fix, count=1).strip() 57 | except sre_constants.error: 58 | return 59 | 60 | # empty string errors -_- 61 | if len(text) >= 4096: 62 | await message.reply_text( 63 | "The result of the sed command was too long for \ 64 | telegram!" 65 | ) 66 | elif text: 67 | await message.reply_to_message.reply_text(text) 68 | 69 | 70 | def infinite_checker(repl): 71 | regex = [ 72 | r"\((.{1,}[\+\*]){1,}\)[\+\*].", 73 | r"[\(\[].{1,}\{\d(,)?\}[\)\]]\{\d(,)?\}", 74 | r"\(.{1,}\)\{.{1,}(,)?\}\(.*\)(\+|\* |\{.*\})", 75 | ] 76 | for match in regex: 77 | status = re.search(match, repl) 78 | return bool(status) 79 | 80 | 81 | def separate_sed(sed_string): 82 | if ( 83 | len(sed_string) < 3 84 | or sed_string[1] not in DELIMITERS 85 | or sed_string.count(sed_string[1]) < 2 86 | ): 87 | return 88 | 89 | delim = sed_string[1] 90 | start = counter = 2 91 | while counter < len(sed_string): 92 | if sed_string[counter] == "\\": 93 | counter += 1 94 | 95 | elif sed_string[counter] == delim: 96 | replace = sed_string[start:counter] 97 | counter += 1 98 | start = counter 99 | break 100 | 101 | counter += 1 102 | 103 | else: 104 | return None 105 | while counter < len(sed_string): 106 | if ( 107 | sed_string[counter] == "\\" 108 | and counter + 1 < len(sed_string) 109 | and sed_string[counter + 1] == delim 110 | ): 111 | sed_string = sed_string[:counter] + sed_string[counter + 1 :] 112 | 113 | elif sed_string[counter] == delim: 114 | replace_with = sed_string[start:counter] 115 | counter += 1 116 | break 117 | 118 | counter += 1 119 | else: 120 | return replace, sed_string[start:], "" 121 | 122 | flags = sed_string[counter:] if counter < len(sed_string) else "" 123 | return replace, replace_with, flags.lower() 124 | -------------------------------------------------------------------------------- /nezuko/modules/repo.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pyrogram import filters 25 | 26 | from nezuko import app 27 | from nezuko.core.decorators.errors import capture_err 28 | from nezuko.utils.http import get 29 | 30 | __MODULE__ = "Repo" 31 | __HELP__ = "/repo - To Get My Github Repository Link " "And Support Group Link" 32 | 33 | 34 | @app.on_message(filters.command("repo")) 35 | @capture_err 36 | async def repo(_, message): 37 | users = await get( 38 | "https://api.github.com/repos/rozari0/NezukoBot/contributors" 39 | ) 40 | list_of_users = "".join( 41 | f"**{count}.** [{user['login']}]({user['html_url']})\n" 42 | for count, user in enumerate(users, start=1) 43 | ) 44 | 45 | text = f"""[Github](https://github.com/rozari0/NezukoBot) | [Group](t.me/thecrowclub) 46 | ```---------------- 47 | | Contributors | 48 | ----------------``` 49 | {list_of_users}""" 50 | await app.send_message( 51 | message.chat.id, text=text, disable_web_page_preview=True 52 | ) 53 | -------------------------------------------------------------------------------- /nezuko/modules/reverse.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import os 25 | from asyncio import gather, get_running_loop 26 | from base64 import b64decode 27 | from io import BytesIO 28 | from random import randint 29 | 30 | import aiofiles 31 | import requests 32 | from bs4 import BeautifulSoup 33 | from pyrogram import filters 34 | from pyrogram.types import InputMediaPhoto, Message 35 | 36 | from nezuko import MESSAGE_DUMP_CHAT, SUDOERS, app, eor 37 | from nezuko.core.decorators.errors import capture_err 38 | from nezuko.utils.functions import get_file_id_from_message 39 | from nezuko.utils.http import get 40 | 41 | 42 | async def get_soup(url: str, headers): 43 | html = await get(url, headers=headers) 44 | return BeautifulSoup(html, "html.parser") 45 | 46 | 47 | @app.on_message(filters.command("reverse")) 48 | @capture_err 49 | async def reverse_image_search(client, message: Message): 50 | if not message.reply_to_message: 51 | return await eor( 52 | message, text="Reply to a message to reverse search it." 53 | ) 54 | reply = message.reply_to_message 55 | if ( 56 | not reply.document 57 | and not reply.photo 58 | and not reply.sticker 59 | and not reply.animation 60 | and not reply.video 61 | ): 62 | return await eor( 63 | message, 64 | text="Reply to an image/document/sticker/animation to reverse search it.", 65 | ) 66 | m = await eor(message, text="Searching...") 67 | file_id = get_file_id_from_message(reply) 68 | if not file_id: 69 | return await m.edit("Can't reverse that") 70 | image = await client.download_media(file_id, f"{randint(1000, 10000)}.jpg") 71 | async with aiofiles.open(image, "rb") as f: 72 | if image: 73 | search_url = "http://www.google.com/searchbyimage/upload" 74 | multipart = { 75 | "encoded_image": (image, await f.read()), 76 | "image_content": "", 77 | } 78 | 79 | def post_non_blocking(): 80 | return requests.post( 81 | search_url, files=multipart, allow_redirects=False 82 | ) 83 | 84 | loop = get_running_loop() 85 | response = await loop.run_in_executor(None, post_non_blocking) 86 | location = response.headers.get("Location") 87 | os.remove(image) 88 | else: 89 | return await m.edit("Something wrong happened.") 90 | headers = { 91 | "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0" 92 | } 93 | 94 | try: 95 | soup = await get_soup(location, headers=headers) 96 | div = soup.find_all("div", {"class": "r5a77d"})[0] 97 | text = div.find("a").text 98 | text = f"**Result**: [{text}]({location})" 99 | except Exception: 100 | return await m.edit( 101 | f"**Result**: [Link]({location})", 102 | disable_web_page_preview=True, 103 | ) 104 | 105 | # Pass if no images detected 106 | try: 107 | url = "https://google.com" + soup.find_all( 108 | "a", {"class": "ekf0x hSQtef"} 109 | )[0].get("href") 110 | 111 | soup = await get_soup(url, headers=headers) 112 | 113 | media = [] 114 | for img in soup.find_all("img"): 115 | if len(media) == 2: 116 | break 117 | 118 | if img.get("src"): 119 | img = img.get("src") 120 | if "image/gif" in img: 121 | continue 122 | 123 | img = BytesIO(b64decode(img)) 124 | img.name = "img.png" 125 | media.append(img) 126 | elif img.get("data-src"): 127 | img = img.get("data-src") 128 | media.append(img) 129 | 130 | # Cache images, so we can use file_ids 131 | tasks = [client.send_photo(MESSAGE_DUMP_CHAT, img) for img in media] 132 | messages = await gather(*tasks) 133 | 134 | await message.reply_media_group( 135 | [ 136 | InputMediaPhoto( 137 | i.photo.file_id, 138 | caption=text, 139 | ) 140 | for i in messages 141 | ] 142 | ) 143 | except Exception: 144 | pass 145 | 146 | await m.edit( 147 | text, 148 | disable_web_page_preview=True, 149 | ) 150 | -------------------------------------------------------------------------------- /nezuko/modules/rss.py: -------------------------------------------------------------------------------- 1 | from asyncio import get_event_loop, sleep 2 | 3 | from feedparser import parse 4 | from pyrogram import filters 5 | from pyrogram.types import Message 6 | 7 | from nezuko import RSS_DELAY, app 8 | from nezuko.core.decorators.errors import capture_err 9 | from nezuko.utils.dbfunctions import ( 10 | add_rss_feed, 11 | get_rss_feeds, 12 | is_rss_active, 13 | remove_rss_feed, 14 | update_rss_feed, 15 | ) 16 | from nezuko.utils.functions import get_http_status_code, get_urls_from_text 17 | from nezuko.utils.rss import Feed 18 | 19 | __MODULE__ = "RSS" 20 | __HELP__ = f""" 21 | /add_feed [URL] - Add a feed to chat 22 | /rm_feed - Remove feed from chat 23 | 24 | **Note:** 25 | - This will check for updates every {RSS_DELAY//60} minutes. 26 | - You can only add one feed per chat. 27 | - Currently RSS and ATOM feeds are supported. 28 | """ 29 | 30 | 31 | async def rss_worker(): 32 | print("[INFO]: RSS WORKER STARTED") 33 | while True: 34 | feeds = await get_rss_feeds() 35 | if not feeds: 36 | await sleep(RSS_DELAY) 37 | continue 38 | 39 | loop = get_event_loop() 40 | 41 | for _feed in feeds: 42 | try: 43 | chat = _feed["chat_id"] 44 | url = _feed["url"] 45 | last_title = _feed.get("last_title") 46 | 47 | parsed = await loop.run_in_executor(None, parse, url) 48 | feed = Feed(parsed) 49 | 50 | if feed.title == last_title: 51 | continue 52 | 53 | await app.send_message( 54 | chat, feed.parsed(), disable_web_page_preview=True 55 | ) 56 | await update_rss_feed(chat, feed.title) 57 | except Exception as e: 58 | print(str(e), f"RSS {chat}") 59 | await sleep(RSS_DELAY) 60 | 61 | 62 | loop = get_event_loop() 63 | loop.create_task(rss_worker()) 64 | 65 | 66 | @app.on_message(filters.command("add_feed")) 67 | @capture_err 68 | async def add_feed_func(_, m: Message): 69 | if len(m.command) != 2: 70 | return await m.reply("Read 'RSS' section in help menu.") 71 | url = m.text.split(None, 1)[1].strip() 72 | 73 | if not url: 74 | return await m.reply("[ERROR]: Invalid Argument") 75 | 76 | urls = get_urls_from_text(url) 77 | if not urls: 78 | return await m.reply("[ERROR]: Invalid URL") 79 | 80 | url = urls[0] 81 | status = await get_http_status_code(url) 82 | if status != 200: 83 | return await m.reply("[ERROR]: Invalid Url") 84 | 85 | ns = "[ERROR]: This feed isn't supported." 86 | try: 87 | loop = get_event_loop() 88 | parsed = await loop.run_in_executor(None, parse, url) 89 | feed = Feed(parsed) 90 | except Exception: 91 | return await m.reply(ns) 92 | if not feed: 93 | return await m.reply(ns) 94 | 95 | chat_id = m.chat.id 96 | if await is_rss_active(chat_id): 97 | return await m.reply("[ERROR]: You already have an RSS feed enabled.") 98 | try: 99 | await m.reply(feed.parsed(), disable_web_page_preview=True) 100 | except Exception: 101 | return await m.reply(ns) 102 | await add_rss_feed(chat_id, feed.url, feed.title) 103 | 104 | 105 | @app.on_message(filters.command("rm_feed")) 106 | async def rm_feed_func(_, m: Message): 107 | if await is_rss_active(m.chat.id): 108 | await remove_rss_feed(m.chat.id) 109 | await m.reply("Removed RSS Feed") 110 | else: 111 | await m.reply("There are no active RSS Feeds in this chat.") 112 | -------------------------------------------------------------------------------- /nezuko/modules/sudo.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pyrogram import filters 25 | from pyrogram.types import Message 26 | 27 | from nezuko import BOT_ID, SUDOERS, app, eor 28 | from nezuko.core.decorators.errors import capture_err 29 | from nezuko.utils.dbfunctions import add_sudo, get_sudoers, remove_sudo 30 | from nezuko.utils.functions import restart 31 | 32 | __MODULE__ = "Sudo" 33 | __HELP__ = """ 34 | **THIS MODULE IS ONLY FOR DEVS** 35 | 36 | .useradd - To Add A User In Sudoers. 37 | .userdel - To Remove A User From Sudoers. 38 | .sudoers - To List Sudo Users. 39 | 40 | **NOTE:** 41 | 42 | Never add anyone to sudoers unless you trust them, 43 | sudo users can do anything with your account, they 44 | can even delete your account. 45 | """ 46 | 47 | 48 | @app.on_message(filters.command("useradd") & filters.user(SUDOERS)) 49 | @capture_err 50 | async def useradd(_, message: Message): 51 | if not message.reply_to_message: 52 | return await eor( 53 | message, 54 | text="Reply to someone's message to add him to sudoers.", 55 | ) 56 | user_id = message.reply_to_message.from_user.id 57 | umention = (await app.get_users(user_id)).mention 58 | sudoers = await get_sudoers() 59 | if user_id in sudoers: 60 | return await eor(message, text=f"{umention} is already in sudoers.") 61 | if user_id == BOT_ID: 62 | return await eor( 63 | message, text="You can't add assistant bot in sudoers." 64 | ) 65 | added = await add_sudo(user_id) 66 | if added: 67 | await eor( 68 | message, 69 | text=f"Successfully added {umention} in sudoers, Bot will be restarted now.", 70 | ) 71 | return await restart(None) 72 | await eor(message, text="Something wrong happened, check logs.") 73 | 74 | 75 | @app.on_message(filters.command("userdel") & filters.user(SUDOERS)) 76 | @capture_err 77 | async def userdel(_, message: Message): 78 | if not message.reply_to_message: 79 | return await eor( 80 | message, 81 | text="Reply to someone's message to remove him to sudoers.", 82 | ) 83 | user_id = message.reply_to_message.from_user.id 84 | umention = (await app.get_users(user_id)).mention 85 | if user_id not in await get_sudoers(): 86 | return await eor(message, text=f"{umention} is not in sudoers.") 87 | removed = await remove_sudo(user_id) 88 | if removed: 89 | await eor( 90 | message, 91 | text=f"Successfully removed {umention} from sudoers, Bot will be restarted now.", 92 | ) 93 | return await restart(None) 94 | await eor(message, text="Something wrong happened, check logs.") 95 | 96 | 97 | @app.on_message(filters.command("sudoers") & filters.user(SUDOERS)) 98 | @capture_err 99 | async def sudoers_list(_, message: Message): 100 | sudoers = await get_sudoers() 101 | text = "" 102 | for count, user_id in enumerate(sudoers, 1): 103 | user = await app.get_users(user_id) 104 | user = user.first_name if not user.mention else user.mention 105 | text += f"{count}. {user}\n" 106 | await eor(message, text=text) 107 | -------------------------------------------------------------------------------- /nezuko/modules/telegraph.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters 2 | from pyrogram.types import Message 3 | 4 | from nezuko import app, telegraph 5 | from nezuko.core.decorators.errors import capture_err 6 | 7 | __MODULE__ = "Telegraph" 8 | __HELP__ = "/telegraph [Page name]: Paste styled text on telegraph." 9 | 10 | 11 | @app.on_message(filters.command("telegraph")) 12 | @capture_err 13 | async def paste(_, message: Message): 14 | reply = message.reply_to_message 15 | 16 | if not reply or not reply.text: 17 | return await message.reply("Reply to a text message") 18 | 19 | if len(message.command) < 2: 20 | return await message.reply("**Usage:**\n /telegraph [Page name]") 21 | 22 | page_name = message.text.split(None, 1)[1] 23 | page = telegraph.create_page( 24 | page_name, html_content=(reply.text.html).replace("\n", "
") 25 | ) 26 | return await message.reply( 27 | f"**Posted:** {page['url']}", 28 | disable_web_page_preview=True, 29 | ) 30 | -------------------------------------------------------------------------------- /nezuko/modules/tts.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import traceback 25 | from asyncio import get_running_loop 26 | from io import BytesIO 27 | 28 | from googletrans import Translator 29 | from gtts import gTTS 30 | from pyrogram import filters 31 | from pyrogram.types import Message 32 | 33 | from nezuko import app 34 | 35 | 36 | def convert(text): 37 | audio = BytesIO() 38 | i = Translator().translate(text, dest="en") 39 | lang = i.src 40 | tts = gTTS(text, lang=lang) 41 | audio.name = lang + ".mp3" 42 | tts.write_to_fp(audio) 43 | return audio 44 | 45 | 46 | @app.on_message(filters.command("tts")) 47 | async def text_to_speech(_, message: Message): 48 | if not message.reply_to_message: 49 | return await message.reply_text("Reply to some text ffs.") 50 | if not message.reply_to_message.text: 51 | return await message.reply_text("Reply to some text ffs.") 52 | m = await message.reply_text("Processing") 53 | text = message.reply_to_message.text 54 | try: 55 | loop = get_running_loop() 56 | audio = await loop.run_in_executor(None, convert, text) 57 | await message.reply_audio(audio) 58 | await m.delete() 59 | audio.close() 60 | except Exception as e: 61 | await m.edit(e) 62 | e = traceback.format_exc() 63 | print(e) 64 | -------------------------------------------------------------------------------- /nezuko/modules/urltools.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 rozari0 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from pyrogram import filters 25 | 26 | from nezuko import app 27 | from nezuko.core.decorators.errors import capture_err 28 | from nezuko.utils.http import get, resp_get 29 | 30 | __MODULE__ = "Url Tools" 31 | __HELP__ = """/short - To Short a url. Use **/short url coustom** to get coustom link. 32 | /unshort - To unshort a url.""" 33 | 34 | 35 | @app.on_message(filters.command("short")) 36 | @capture_err 37 | async def short(_, message): 38 | if len(message.command) < 2: 39 | return await message.reply_text( 40 | "**/short sho.rt/url** To short a url." 41 | ) 42 | url = message.command[1] 43 | if not url.startswith("http"): 44 | url = "http://" + url 45 | try: 46 | short = message.command[2] 47 | shortRequest = await get( 48 | f"https://api.1pt.co/addURL?long={url}&short={short}" 49 | ) 50 | short = shortRequest["short"] 51 | return await message.reply_text( 52 | f"**URL After Short: `https://1pt.co/{short}`**" 53 | ) 54 | except IndexError: 55 | shortRequest = await get(f"https://api.1pt.co/addURL?long={url}") 56 | short = shortRequest["short"] 57 | return await message.reply_text( 58 | f"**URL After Short: `https://1pt.co/{short}`**" 59 | ) 60 | except Exception as e: 61 | return await message.reply_text(f"**{e}**") 62 | 63 | 64 | @app.on_message(filters.command("unshort")) 65 | @capture_err 66 | async def unshort(_, message): 67 | if len(message.command) < 2: 68 | return await message.reply_text("**/unshort url** To unshort a url.") 69 | url = message.command[1] 70 | if not url.startswith("http"): 71 | url = "http://" + url 72 | try: 73 | mainurl = await resp_get(url) 74 | return await message.reply_text( 75 | f"**URL After Unshort: `{mainurl.url}`**" 76 | ) 77 | except Exception as e: 78 | return await message.reply_text(f"**{e}**") 79 | -------------------------------------------------------------------------------- /nezuko/modules/webss.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from asyncio import gather 25 | from base64 import b64decode 26 | from io import BytesIO 27 | 28 | from pyrogram import filters 29 | from pyrogram.types import Message 30 | 31 | from nezuko import SUDOERS, app, eor 32 | from nezuko.core.decorators.errors import capture_err 33 | from nezuko.utils.http import post 34 | 35 | 36 | async def take_screenshot(url: str, full: bool = False): 37 | url = "https://" + url if not url.startswith("http") else url 38 | payload = { 39 | "url": url, 40 | "width": 1920, 41 | "height": 1080, 42 | "scale": 1, 43 | "format": "jpeg", 44 | } 45 | if full: 46 | payload["full"] = True 47 | data = await post( 48 | "https://webscreenshot.vercel.app/api", 49 | data=payload, 50 | ) 51 | if "image" not in data: 52 | return None 53 | b = data["image"].replace("data:image/jpeg;base64,", "") 54 | file = BytesIO(b64decode(b)) 55 | file.name = "webss.jpg" 56 | return file 57 | 58 | 59 | @app.on_message(filters.command(["webss", "ss"])) 60 | @capture_err 61 | async def take_ss(_, message: Message): 62 | if len(message.command) < 2: 63 | return await eor(message, text="Give A Url To Fetch Screenshot.") 64 | 65 | if len(message.command) == 2: 66 | url = message.text.split(None, 1)[1] 67 | full = False 68 | elif len(message.command) == 3: 69 | url = message.text.split(None, 2)[1] 70 | full = message.text.split(None, 2)[2].lower().strip() in [ 71 | "yes", 72 | "y", 73 | "1", 74 | "true", 75 | ] 76 | else: 77 | return await eor(message, text="Invalid Command.") 78 | 79 | m = await eor(message, text="Capturing screenshot...") 80 | 81 | try: 82 | photo = await take_screenshot(url, full) 83 | if not photo: 84 | return await m.edit("Failed To Take Screenshot") 85 | 86 | m = await m.edit("Uploading...") 87 | 88 | if not full: 89 | # Full size images have problem with reply_photo, that's why 90 | # we need to only use reply_photo if we're not using full size 91 | await gather( 92 | *[message.reply_document(photo), message.reply_photo(photo)] 93 | ) 94 | else: 95 | await message.reply_document(photo) 96 | await m.delete() 97 | except Exception as e: 98 | await m.edit(str(e)) 99 | -------------------------------------------------------------------------------- /nezuko/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from .misc import paginate_modules # noqa: F401 25 | from .read_lines import random_line # noqa: F401 26 | -------------------------------------------------------------------------------- /nezuko/utils/constants.py: -------------------------------------------------------------------------------- 1 | # New file 2 | 3 | from pyrogram.filters import command 4 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message 5 | 6 | from nezuko import BOT_USERNAME, app 7 | 8 | MARKDOWN = """ 9 | Read the below text carefully to find out how formatting works! 10 | 11 | Supported Fillings: 12 | 13 | {name} - This will mention the user with their name. 14 | {chat} - This will fill with the current chat name. 15 | 16 | NOTE: Fillings only works in greetings module. 17 | 18 | 19 | Supported formatting: 20 | 21 | **Bold** : Creates bold text. 22 | ~~strike~~: Creates striked text. 23 | __italic__: Creates italic text. 24 | --underline--: Creates underline text. 25 | `code words`: Creates code words text. 26 | [hyperlink](google.com): Creates hyperlink text. 27 | Note: You can use both markdown & html tags. 28 | 29 | 30 | Button formatting: 31 | 32 | -> text ~ [button text, button link] 33 | 34 | 35 | Example: 36 | 37 | example button with markdown formatting ~ [button text, https://google.com] 38 | """ 39 | 40 | 41 | @app.on_message(command("markdownhelp")) 42 | async def mkdwnhelp(_, m: Message): 43 | keyb = InlineKeyboardMarkup( 44 | [ 45 | [ 46 | InlineKeyboardButton( 47 | text="Click Here!", 48 | url=f"http://t.me/{BOT_USERNAME}?start=mkdwn_help", 49 | ) 50 | ] 51 | ] 52 | ) 53 | if m.chat.type != "private": 54 | await m.reply( 55 | "Click on the below button to get markdown usage syntax in pm!", 56 | reply_markup=keyb, 57 | ) 58 | else: 59 | await m.reply( 60 | MARKDOWN, parse_mode="html", disable_web_page_preview=True 61 | ) 62 | return 63 | -------------------------------------------------------------------------------- /nezuko/utils/downloader.py: -------------------------------------------------------------------------------- 1 | from os.path import abspath as absolute_path 2 | from time import time 3 | 4 | import aiofiles 5 | 6 | from nezuko import aiohttpsession as session 7 | from nezuko.core.tasks import add_task 8 | 9 | 10 | def ensure_status(status_code: int): 11 | if status_code < 200 or status_code >= 300: 12 | raise Exception(f"HttpProcessingError: {status_code}") 13 | 14 | 15 | async def download_url( 16 | url, 17 | file_path, 18 | chunk_size, 19 | ): 20 | file_path = file_path or url.split("/")[-1][:20] 21 | 22 | async with session.get(url) as response: 23 | ensure_status(response.status) 24 | 25 | async with aiofiles.open(file_path, "wb") as f: 26 | 27 | # Save content in file using aiohttp streamReader. 28 | async for chunk in response.content.iter_chunked(chunk_size): 29 | await f.write(chunk) 30 | 31 | return absolute_path(file_path) 32 | 33 | 34 | async def download( 35 | url: str, 36 | file_path: str = None, 37 | chunk_size: int = 1000000, # 1MB chunk 38 | task_id: int = int(time()), 39 | ): 40 | """ 41 | :url: url where the file is located 42 | :file_path: path/to/file 43 | :chunk_size: size of a single chunk 44 | 45 | Returns: 46 | (asyncio.Task, task_id), With which you can await 47 | the task, track task progress or cancel it. 48 | """ 49 | # Create a task and add it to main tasks dict 50 | # So we can cancel it using .cancelTask 51 | 52 | task, task_id = await add_task( 53 | download_url, 54 | "Downloader", 55 | url=url, 56 | file_path=file_path, 57 | chunk_size=chunk_size, 58 | ) 59 | 60 | return task, task_id 61 | -------------------------------------------------------------------------------- /nezuko/utils/files.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | import math 25 | import os 26 | 27 | from PIL import Image 28 | from pyrogram import Client, raw 29 | from pyrogram.file_id import FileId 30 | 31 | STICKER_DIMENSIONS = (512, 512) 32 | 33 | 34 | async def resize_file_to_sticker_size(file_path: str) -> str: 35 | im = Image.open(file_path) 36 | if (im.width, im.height) < STICKER_DIMENSIONS: 37 | size1 = im.width 38 | size2 = im.height 39 | if im.width > im.height: 40 | scale = STICKER_DIMENSIONS[0] / size1 41 | size1new = STICKER_DIMENSIONS[0] 42 | size2new = size2 * scale 43 | else: 44 | scale = STICKER_DIMENSIONS[1] / size2 45 | size1new = size1 * scale 46 | size2new = STICKER_DIMENSIONS[1] 47 | size1new = math.floor(size1new) 48 | size2new = math.floor(size2new) 49 | sizenew = (size1new, size2new) 50 | im = im.resize(sizenew) 51 | else: 52 | im.thumbnail(STICKER_DIMENSIONS) 53 | try: 54 | os.remove(file_path) 55 | file_path = f"{file_path}.png" 56 | return file_path 57 | finally: 58 | im.save(file_path) 59 | 60 | 61 | async def upload_document( 62 | client: Client, file_path: str, chat_id: int 63 | ) -> raw.base.InputDocument: 64 | media = await client.invoke( 65 | raw.functions.messages.UploadMedia( 66 | peer=await client.resolve_peer(chat_id), 67 | media=raw.types.InputMediaUploadedDocument( 68 | mime_type=client.guess_mime_type(file_path) 69 | or "application/zip", 70 | file=await client.save_file(file_path), 71 | attributes=[ 72 | raw.types.DocumentAttributeFilename( 73 | file_name=os.path.basename(file_path) 74 | ) 75 | ], 76 | ), 77 | ) 78 | ) 79 | return raw.types.InputDocument( 80 | id=media.document.id, 81 | access_hash=media.document.access_hash, 82 | file_reference=media.document.file_reference, 83 | ) 84 | 85 | 86 | async def get_document_from_file_id( 87 | file_id: str, 88 | ) -> raw.base.InputDocument: 89 | decoded = FileId.decode(file_id) 90 | return raw.types.InputDocument( 91 | id=decoded.media_id, 92 | access_hash=decoded.access_hash, 93 | file_reference=decoded.file_reference, 94 | ) 95 | -------------------------------------------------------------------------------- /nezuko/utils/filter_groups.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | chat_filters_group = 1 25 | chatbot_group = 2 26 | karma_positive_group = 3 27 | karma_negative_group = 4 28 | regex_group = 5 29 | welcome_captcha_group = 6 30 | antiflood_group = 7 31 | blacklist_filters_group = 8 32 | taglog_group = 9 33 | chat_watcher_group = 10 34 | flood_group = 11 35 | autocorrect_group = 12 36 | -------------------------------------------------------------------------------- /nezuko/utils/formatter.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | def get_readable_time(seconds: int) -> str: 27 | count = 0 28 | ping_time = "" 29 | time_list = [] 30 | time_suffix_list = ["s", "m", "h", "days"] 31 | while count < 4: 32 | count += 1 33 | remainder, result = ( 34 | divmod(seconds, 60) if count < 3 else divmod(seconds, 24) 35 | ) 36 | if seconds == 0 and remainder == 0: 37 | break 38 | time_list.append(int(result)) 39 | seconds = int(remainder) 40 | 41 | for i, _ in enumerate(time_list): 42 | time_list[i] = str(time_list[i]) + time_suffix_list[i] 43 | 44 | if len(time_list) == 4: 45 | ping_time += time_list.pop() + ", " 46 | 47 | time_list.reverse() 48 | ping_time += ":".join(time_list) 49 | return ping_time 50 | 51 | 52 | # Convert seconds to mm:ss 53 | async def convert_seconds_to_minutes(seconds: int): 54 | seconds = int(seconds) 55 | seconds %= 24 * 3600 56 | seconds %= 3600 57 | minutes = seconds // 60 58 | seconds %= 60 59 | return "%02d:%02d" % (minutes, seconds) 60 | -------------------------------------------------------------------------------- /nezuko/utils/http.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from asyncio import gather 25 | 26 | from nezuko import aiohttpsession as session 27 | 28 | 29 | async def get(url: str, *args, **kwargs): 30 | async with session.get(url, *args, **kwargs) as resp: 31 | try: 32 | data = await resp.json() 33 | except Exception: 34 | data = await resp.text() 35 | return data 36 | 37 | 38 | async def head(url: str, *args, **kwargs): 39 | async with session.head(url, *args, **kwargs) as resp: 40 | try: 41 | data = await resp.json() 42 | except Exception: 43 | data = await resp.text() 44 | return data 45 | 46 | 47 | async def post(url: str, *args, **kwargs): 48 | async with session.post(url, *args, **kwargs) as resp: 49 | try: 50 | data = await resp.json() 51 | except Exception: 52 | data = await resp.text() 53 | return data 54 | 55 | 56 | async def multiget(url: str, times: int, *args, **kwargs): 57 | return await gather(*[get(url, *args, **kwargs) for _ in range(times)]) 58 | 59 | 60 | async def multihead(url: str, times: int, *args, **kwargs): 61 | return await gather(*[head(url, *args, **kwargs) for _ in range(times)]) 62 | 63 | 64 | async def multipost(url: str, times: int, *args, **kwargs): 65 | return await gather(*[post(url, *args, **kwargs) for _ in range(times)]) 66 | 67 | 68 | async def resp_get(url: str, *args, **kwargs): 69 | return await session.get(url, *args, **kwargs) 70 | 71 | 72 | async def resp_post(url: str, *args, **kwargs): 73 | return await session.post(url, *args, **kwargs) 74 | -------------------------------------------------------------------------------- /nezuko/utils/json_prettify.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | 26 | async def json_object_prettify(objecc): 27 | dicc = objecc.__dict__ 28 | return "".join( 29 | f"**{key}:** `{value}`\n" 30 | for key, value in dicc.items() 31 | if key not in ["pinned_message", "photo", "_", "_client"] 32 | ) 33 | 34 | 35 | async def json_prettify(data): 36 | output = "" 37 | try: 38 | for key, value in data.items(): 39 | output += f"**{str(key).capitalize()}:** `{value}`\n" 40 | except Exception: 41 | for datas in data: 42 | for key, value in datas.items(): 43 | output += f"**{str(key).capitalize()}:** `{value}`\n" 44 | output += "------------------------\n" 45 | return output 46 | -------------------------------------------------------------------------------- /nezuko/utils/misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from math import ceil 25 | 26 | from pyrogram.types import InlineKeyboardButton 27 | 28 | from nezuko import MOD_LOAD, MOD_NOLOAD 29 | 30 | 31 | class EqInlineKeyboardButton(InlineKeyboardButton): 32 | def __eq__(self, other): 33 | return self.text == other.text 34 | 35 | def __lt__(self, other): 36 | return self.text < other.text 37 | 38 | def __gt__(self, other): 39 | return self.text > other.text 40 | 41 | 42 | def paginate_modules(page_n, module_dict, prefix, chat=None): 43 | if not chat: 44 | modules = sorted( 45 | [ 46 | EqInlineKeyboardButton( 47 | x.__MODULE__, 48 | callback_data="{}_module({})".format( 49 | prefix, x.__MODULE__.lower() 50 | ), 51 | ) 52 | for x in module_dict.values() 53 | ] 54 | ) 55 | else: 56 | modules = sorted( 57 | [ 58 | EqInlineKeyboardButton( 59 | x.__MODULE__, 60 | callback_data="{}_module({},{})".format( 61 | prefix, chat, x.__MODULE__.lower() 62 | ), 63 | ) 64 | for x in module_dict.values() 65 | ] 66 | ) 67 | 68 | pairs = list(zip(modules[::3], modules[1::3], modules[2::3])) 69 | i = 0 70 | for m in pairs: 71 | for _ in m: 72 | i += 1 73 | if len(modules) - i == 1: 74 | pairs.append((modules[-1],)) 75 | elif len(modules) - i == 2: 76 | pairs.append( 77 | ( 78 | modules[-2], 79 | modules[-1], 80 | ) 81 | ) 82 | 83 | COLUMN_SIZE = 4 84 | 85 | max_num_pages = ceil(len(pairs) / COLUMN_SIZE) 86 | modulo_page = page_n % max_num_pages 87 | 88 | # can only have a certain amount of buttons side by side 89 | if len(pairs) > COLUMN_SIZE: 90 | pairs = pairs[ 91 | modulo_page * COLUMN_SIZE : COLUMN_SIZE * (modulo_page + 1) 92 | ] + [ 93 | ( 94 | EqInlineKeyboardButton( 95 | "❮", 96 | callback_data="{}_prev({})".format(prefix, modulo_page), 97 | ), 98 | EqInlineKeyboardButton( 99 | "Back", 100 | callback_data="{}_home({})".format(prefix, modulo_page), 101 | ), 102 | EqInlineKeyboardButton( 103 | "❯", 104 | callback_data="{}_next({})".format(prefix, modulo_page), 105 | ), 106 | ) 107 | ] 108 | 109 | return pairs 110 | 111 | 112 | def is_module_loaded(name): 113 | return (not MOD_LOAD or name in MOD_LOAD) and name not in MOD_NOLOAD 114 | -------------------------------------------------------------------------------- /nezuko/utils/pastebin.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from nezuko.utils.http import post 25 | 26 | BASE = "https://batbin.me/" 27 | 28 | 29 | async def paste(content: str): 30 | resp = await post(f"{BASE}api/v2/paste", data=content) 31 | if not resp["success"]: 32 | return 33 | return BASE + resp["message"] 34 | -------------------------------------------------------------------------------- /nezuko/utils/read_lines.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2021 TheHamkerCat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | from random import choice 25 | 26 | 27 | async def random_line(fname): 28 | with open(fname) as f: 29 | data = f.read().splitlines() 30 | return choice(data) 31 | -------------------------------------------------------------------------------- /nezuko/utils/rss.py: -------------------------------------------------------------------------------- 1 | class Feed: 2 | def __init__(self, feed): 3 | if not feed.get("entries"): 4 | return 5 | entry = feed["entries"][0] 6 | self.title = entry.get("title") or "" 7 | 8 | # We need title to check latest post 9 | if not self.title: 10 | return 11 | 12 | self.link = entry.get("link") or "" 13 | self.published = entry.get("published") or "" 14 | self.updated = entry.get("updated") or "" 15 | self.author = entry.get("author") 16 | self.summary = entry.get("summary") or "" 17 | 18 | def parsed(self): 19 | text = f"**Title:** [{self.title.strip()}]({self.link or 'https://google.com'})\n" 20 | if self.author: 21 | text += f"**Author:** {self.author}\n" 22 | if self.published: 23 | text += f"**Published:** `{self.published}`\n" 24 | if self.updated: 25 | text += f"**Last Updated:** `{self.updated}`\n" 26 | 27 | if self.summary and " raw.base.messages.StickerSet: 32 | try: 33 | return await client.invoke( 34 | raw.functions.messages.GetStickerSet( 35 | stickerset=raw.types.InputStickerSetShortName(short_name=name), 36 | hash=0, 37 | ) 38 | ) 39 | except errors.exceptions.not_acceptable_406.StickersetInvalid: 40 | return None 41 | 42 | 43 | # Known errors: (I don't see a reason to catch them as we, for sure, won't face them right now): 44 | # errors.exceptions.bad_request_400.PackShortNameInvalid -> pack name needs to end with _by_botname 45 | # errors.exceptions.bad_request_400.ShortnameOccupyFailed -> pack's name is already in use 46 | 47 | 48 | async def create_sticker_set( 49 | client: Client, 50 | owner: int, 51 | title: str, 52 | short_name: str, 53 | stickers: List[raw.base.InputStickerSetItem], 54 | ) -> raw.base.messages.StickerSet: 55 | return await client.invoke( 56 | raw.functions.stickers.CreateStickerSet( 57 | user_id=await client.resolve_peer(owner), 58 | title=title, 59 | short_name=short_name, 60 | stickers=stickers, 61 | ) 62 | ) 63 | 64 | 65 | async def add_sticker_to_set( 66 | client: Client, 67 | stickerset: raw.base.messages.StickerSet, 68 | sticker: raw.base.InputStickerSetItem, 69 | ) -> raw.base.messages.StickerSet: 70 | return await client.invoke( 71 | raw.functions.stickers.AddStickerToSet( 72 | stickerset=raw.types.InputStickerSetShortName( 73 | short_name=stickerset.set.short_name 74 | ), 75 | sticker=sticker, 76 | ) 77 | ) 78 | 79 | 80 | async def create_sticker( 81 | sticker: raw.base.InputDocument, emoji: str 82 | ) -> raw.base.InputStickerSetItem: 83 | return raw.types.InputStickerSetItem(document=sticker, emoji=emoji) 84 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiofiles 2 | ffmpeg-python 3 | gTTS 4 | dnspython 5 | future 6 | googletrans==4.0.0-rc1 7 | motor 8 | pillow 9 | psutil 10 | pykeyboard 11 | pyrogram 12 | python-arq 13 | requests 14 | git+https://github.com/bisohns/search-engine-parser.git 15 | speedtest-cli 16 | TgCrypto 17 | uvloop 18 | youtube_dl 19 | bs4 20 | python-dotenv 21 | feedparser 22 | pyromod 23 | fuzzysearch 24 | img2pdf 25 | telegraph 26 | jikanpy 27 | cinemagoer 28 | -------------------------------------------------------------------------------- /sample_config.env: -------------------------------------------------------------------------------- 1 | # Only for Docker Deployment 2 | 3 | BOT_TOKEN=your_bot_token # Get it from @botfather 4 | 5 | API_ID=your_api_id 6 | 7 | API_HASH=your_api_hash 8 | 9 | SUDO_USERS_ID=682966383 # Sudo users have full access to everythin, don't trust anyone 10 | 11 | LOG_GROUP_ID=-125639839 12 | 13 | GBAN_LOG_GROUP_ID=-173738999 14 | 15 | MESSAGE_DUMP_CHAT=-173738999 16 | 17 | WELCOME_DELAY_KICK_SEC=300 # Edit if u want 18 | 19 | MONGO_URL=your_mongodb_url 20 | 21 | ARQ_API_URL=https://thearq.tech # Leave it like this 22 | 23 | ARQ_API_KEY=your_api_key # Get it from @ARQRobot 24 | 25 | RSS_DELAY=300 # In seconds 26 | 27 | UPSTREAM_REPO = "https://github.com/rozari0/NezukoBot.git" 28 | -------------------------------------------------------------------------------- /sample_config.py: -------------------------------------------------------------------------------- 1 | from os import environ, path 2 | 3 | from dotenv import load_dotenv 4 | 5 | if path.exists("config.env"): 6 | load_dotenv("config.env") 7 | 8 | BOT_TOKEN = environ.get("BOT_TOKEN", None) 9 | API_ID = int(environ.get("API_ID", 6)) 10 | API_HASH = environ.get("API_HASH", "eb06d4abfb49dc3eeb1aeb98ae0f581e") 11 | SUDO_USERS_ID = [int(x) for x in environ.get("SUDO_USERS_ID", "").split()] 12 | LOG_GROUP_ID = int(environ.get("LOG_GROUP_ID", None)) 13 | GBAN_LOG_GROUP_ID = int(environ.get("GBAN_LOG_GROUP_ID", None)) 14 | MESSAGE_DUMP_CHAT = int(environ.get("MESSAGE_DUMP_CHAT", None)) 15 | WELCOME_DELAY_KICK_SEC = int(environ.get("WELCOME_DELAY_KICK_SEC", None)) 16 | MONGO_URL = environ.get("MONGO_URL", None) 17 | ARQ_API_URL = environ.get("ARQ_API_URL", None) 18 | ARQ_API_KEY = environ.get("ARQ_API_KEY", None) 19 | RSS_DELAY = int(environ.get("RSS_DELAY", None)) 20 | UPSTREAM_REPO = environ.get( 21 | "UPSTREAM_REPO", "https://github.com/rozari0/NezukoBot.git" 22 | ) 23 | --------------------------------------------------------------------------------