├── .gitignore ├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── Script.py ├── app.json ├── app.py ├── bot.py ├── database ├── connections_mdb.py ├── filters_mdb.py ├── ia_filterdb.py ├── join_reqs.py └── users_chats_db.py ├── heroku.yml ├── info.py ├── logging.conf ├── plugins ├── banned.py ├── broadcast.py ├── channel.py ├── commands.py ├── connection.py ├── filters.py ├── fsub.py ├── genlink.py ├── index.py ├── inline.py ├── join_req.py ├── misc.py ├── p_ttishow.py └── pm_filter.py ├── requirements.txt ├── run cmd.txt ├── runtime.txt ├── sample_info.py ├── start.sh └── utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Personal files 2 | *.session 3 | *.session-journal 4 | .vscode 5 | *test*.py 6 | setup.cfg 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | *.py,cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | cover/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | .pybuilder/ 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | # For a library or package, you might want to ignore these files since the code is 94 | # intended to run in multiple environments; otherwise, check them in: 95 | # .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | config.py 147 | .goutputstream-VAFWB1 148 | result.json 149 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster 2 | 3 | RUN apt update && apt upgrade -y 4 | RUN apt install git -y 5 | COPY requirements.txt /requirements.txt 6 | 7 | RUN cd / 8 | RUN pip3 install -U pip && pip3 install -U -r requirements.txt 9 | RUN mkdir /Advance-File-Share-Bot 10 | WORKDIR /Advance-File-Share-Bot 11 | COPY start.sh /start.sh 12 | CMD ["/bin/bash", "/start.sh"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python3 bot.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # File-Sharing-Bot-With-RequestToJoin-Feature 2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |    12 | 13 | 14 |    15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |

24 | 25 | 26 | Telegram Bot to store Posts and Documents and it can Access by Special Links. 27 | I Guess This Will Be Usefull For Many People....😇. 28 | 29 | ## 30 | 31 | **If you need any more modes in repo or If you find out any bugs, mention in [@support ](https://t.me/vj_bot_disscussion)** 32 | 33 | **Make sure to see [contributing.md](https://github.com/VJBots/Advance-File-Share-Bot/blob/main/CONTRIBUTING.md) for instructions on contributing to the project!** 34 | 35 | 36 | 37 | ### Features 38 | - Request To Join Force Subscribe Feature 39 | - Fully customisable. 40 | - Customisable welcome & Forcesub messages. 41 | - More than one Posts in One Link. 42 | - Can be deployed deploy anywhere directly. 43 | 44 | ### Setup 45 | 46 | - Add the bot to Database Channel with all permission 47 | - Add bot to ForceSub channel as Admin with Invite Users via Link Permission if you enabled ForceSub 48 | 49 | ## 50 | ### Installation 51 | #### Deploy on Heroku 52 | **BEFORE YOU DEPLOY ON HEROKU, YOU SHOULD FORK THE REPO AND CHANGE ITS NAME TO ANYTHING ELSE**
53 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
54 | 55 | 56 |
57 | **Check This Tutorial Video on YouTube for any Help**
58 | **Thanks to [Tech VJ](https://t.me/VJ_Botz) and his [Tech VJ](https://youtube.com/@Tech_VJ) for this Video** 59 | 60 | #### Deploy on Railway 61 | [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/1jKLr4) 62 | 63 | #### Deploy on Koyeb 64 | 65 | The fastest way to deploy the application is to click the **Deploy to Koyeb** button below. 66 | 67 | 68 | [![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?type=git&repository=github.com/VJBots/Advance-File-Share-Bot&branch=Tech_VJ&name=filesharingbot) 69 | 70 | 71 | #### Deploy in your VPS 72 | ````bash 73 | git clone https://github.com/VJBots/Advance-File-Share-Bot 74 | cd File-Sharing-Bot 75 | pip3 install -r requirements.txt 76 | # 77 | python3 main.py 78 | ```` 79 | 80 | ### Admin Commands 81 | 82 | ``` 83 | /start - start the bot or get posts 84 | 85 | /batch - create link for more than one posts 86 | 87 | /link - create link for one post 88 | 89 | /status - view bot statistics 90 | 91 | /broadcast - broadcast any messages to bot users 92 | 93 | /stats - checking your bot uptime 94 | ``` 95 | 96 | ### Variables 97 | 98 | * `API_HASH` Your API Hash from my.telegram.org 99 | * `APP_ID` Your API ID from my.telegram.org 100 | * `BOT_TOKEN` Your bot token from @BotFather 101 | * `OWNER_ID` Must enter Your Telegram Id 102 | * `CHANNEL_ID` Your Channel ID eg:- -100xxxxxxxx 103 | * `DATABASE_URL` Your mongo db url 104 | * `DATABASE_NAME` Your mongo db session name 105 | * `ADMINS` Optional: A space separated list of user_ids of Admins, they can only create links 106 | * `START_MESSAGE` Optional: start message of bot, use HTML and fillings 107 | * `FORCE_SUB_MESSAGE`Optional:Force sub message of bot, use HTML and Fillings 108 | * `FORCE_SUB_CHANNEL` Optional: ForceSub Channel ID, leave 0 if you want disable force sub 109 | * `PROTECT_CONTENT` Optional: True if you need to prevent files from forwarding 110 | 111 | ### Extra Variables 112 | 113 | * `CUSTOM_CAPTION` put your Custom caption text if you want Setup Custom Caption, you can use HTML and fillings for formatting (only for documents) 114 | * `DISABLE_CHANNEL_BUTTON` Put True to Disable Channel Share Button, Default if False 115 | * `BOT_STATS_TEXT` put your custom text for stats command, use HTML and fillings 116 | * `USER_REPLY_TEXT` put your text to show when user sends any message, use HTML 117 | 118 | 119 | ### Fillings 120 | #### START_MESSAGE | FORCE_SUB_MESSAGE 121 | 122 | * `{first}` - User first name 123 | * `{last}` - User last name 124 | * `{id}` - User ID 125 | * `{mention}` - Mention the user 126 | * `{username}` - Username 127 | 128 | #### CUSTOM_CAPTION 129 | 130 | * `{filename}` - file name of the Document 131 | * `{previouscaption}` - Original Caption 132 | 133 | #### CUSTOM_STATS 134 | 135 | * `{uptime}` - Bot Uptime 136 | 137 | 138 | ## Support 139 | Join Our [Telegram Group](https://www.telegram.dog/vj_bot_disscussion) For Support/Assistance And Our [Channel](https://www.telegram.dog/VJ_Botz) For Updates. 140 | 141 | Report Bugs, Give Feature Requests There.. 142 | 143 | ### Credits 144 | 145 | - Thanks To Dan For His Awsome [Libary](https://github.com/pyrogram/pyrogram) 146 | - Our Support Group Members 147 | 148 | ### Licence 149 | [![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](http://www.gnu.org/licenses/gpl-3.0.en.html) 150 | 151 | [FILE-SHARING-BOT](https://github.com/VJBots/Advance-File-Share-Bot) is Free Software: You can use, study share and improve it at your 152 | will. Specifically you can redistribute and/or modify it under the terms of the 153 | [GNU General Public License](https://www.gnu.org/licenses/gpl.html) as 154 | published by the Free Software Foundation, either version 3 of the License, or 155 | (at your option) any later version. 156 | 157 | ## 158 | 159 | **Star this Repo if you Liked it ⭐⭐⭐** 160 | -------------------------------------------------------------------------------- /Script.py: -------------------------------------------------------------------------------- 1 | class script(object): 2 | START_TXT = """𝙷𝙴𝙻L𝙾 {}, 3 | 𝙼𝚈 𝙽𝙰𝙼𝙴 𝙸𝚂 {}, ɪ ᴀᴍ ᴀ ꜰɪʟᴇ sᴛᴏʀᴇ ʙᴏᴛ ᴡɪᴛʜ ʀᴇǫᴜᴇsᴛ ᴛᴏ ᴊᴏɪɴ ꜰᴇᴀᴛᴜʀᴇ ʙᴜᴛ ᴏɴʟʏ ᴀᴅᴍɪɴ ᴄᴀɴ ᴘʀᴇᴠᴀɪʟ ᴛʜɪs ꜰᴇᴀᴛᴜʀᴇ. ʏᴏᴜ ᴄᴀɴ ᴜsᴇ ᴍᴇ ᴀs ᴀ ɴᴏʀᴍᴀʟ ꜰɪʟᴇ sᴛᴏʀᴇ ᴊᴜsᴛ sᴇɴᴅ ᴍᴇ ᴀɴʏ ꜰɪʟᴇ.. 𝚃𝙷𝙴𝙽 𝚂𝙴𝙴 𝙼𝚈 𝙿𝙾𝚆𝙴𝚁𝚂 ♥️♥️🔥""" 4 | HELP_TXT = """𝙷𝙴𝚈 {} 5 | 𝙷𝙴𝚁𝙴 𝙸𝚂 𝚃𝙷𝙴 𝙷𝙴𝙻𝙿 𝙵𝙾𝚁 𝙼𝚈 𝙲𝙾𝙼𝙼𝙰𝙽𝙳𝚂.""" 6 | ABOUT_TXT = """✯ 𝙼𝚈 𝙽𝙰𝙼𝙴: {} 7 | ✯ 𝙲𝚁𝙴𝙰𝚃𝙾𝚁: VJ 8 | ✯ 𝙻𝙸𝙱𝚁𝙰𝚁𝚈: 𝙿𝚈𝚁𝙾𝙶𝚁𝙰𝙼 9 | ✯ 𝙻𝙰𝙽𝙶𝚄𝙰𝙶𝙴: 𝙿𝚈𝚃𝙷𝙾𝙽 𝟹 10 | ✯ 𝙳𝙰𝚃𝙰 𝙱𝙰𝚂𝙴: 𝙼𝙾𝙽𝙶𝙾 𝙳𝙱""" 11 | SOURCE_TXT = """Source Code Of This Bot is PUBLIC. This Bot Repository is Public by Tech_VJ YouTube Channel 😊""" 12 | MANUELFILTER_TXT = """Help: Filters 13 | 14 | - Filter is the feature were users can set automated replies for a particular keyword and 𝐉𝐞𝐫𝐫𝐲 will respond whenever a keyword is found the message 15 | 16 | NOTE: 17 | 1. 𝐉𝐞𝐫𝐫𝐲 should have admin privillage. 18 | 2. only admins can add filters in a chat. 19 | 3. alert buttons have a limit of 64 characters. 20 | 21 | Commands and Usage: 22 | • /filter - add a filter in chat 23 | • /filters - list all the filters of a chat 24 | • /del - delete a specific filter in chat 25 | • /delall - delete the whole filters in a chat (chat owner only)""" 26 | BUTTON_TXT = """Help: Buttons 27 | 28 | - 𝐉𝐞𝐫𝐫𝐲 Supports both url and alert inline buttons. 29 | 30 | NOTE: 31 | 1. Telegram will not allows you to send buttons without any content, so content is mandatory. 32 | 2. 𝐉𝐞𝐫𝐫𝐲 supports buttons with any telegram media type. 33 | 3. Buttons should be properly parsed as markdown format 34 | 35 | URL buttons: 36 | [Button Text](buttonurl:https://t.me/EnthadaNokunne) 37 | 38 | Alert buttons: 39 | [Button Text](buttonalert:This is an alert message)""" 40 | AUTOFILTER_TXT = """Help: Auto Filter 41 | 42 | NOTE: 43 | 1. Make me the admin of your channel if it's private. 44 | 2. make sure that your channel does not contains camrips, porn and fake files. 45 | 3. Forward the last message to me with quotes. 46 | I'll add all the files in that channel to my db.""" 47 | CONNECTION_TXT = """Help: Connections 48 | 49 | - Used to connect bot to PM for managing filters 50 | - it helps to avoid spamming in groups. 51 | 52 | NOTE: 53 | 1. Only admins can add a connection. 54 | 2. Send /connect for connecting me to ur PM 55 | 56 | Commands and Usage: 57 | • /connect - connect a particular chat to your PM 58 | • /disconnect - disconnect from a chat 59 | • /connections - list all your connections""" 60 | EXTRAMOD_TXT = """Help: Extra Modules 61 | 62 | NOTE: 63 | these are the extra features of Eva Maria 64 | 65 | Commands and Usage: 66 | • /id - get id of a specified user. 67 | • /info - get information about a user. 68 | • /imdb - get the film information from IMDb source. 69 | • /search - get the film information from various sources.""" 70 | ADMIN_TXT = """Help: Admin mods 71 | 72 | NOTE: 73 | This module only works for my admins 74 | 75 | Commands and Usage: 76 | • /logs - to get the rescent errors 77 | • /stats - to get status of files in db. 78 | • /delete - to delete a specific file from db. 79 | • /users - to get list of my users and ids. 80 | • /chats - to get list of the my chats and ids 81 | • /leave - to leave from a chat. 82 | • /disable - do disable a chat. 83 | • /ban - to ban a user. 84 | • /unban - to unban a user. 85 | • /channel - to get list of total connected channels 86 | • /broadcast - to broadcast a message to all users""" 87 | STATUS_TXT = """★ 𝚃𝙾𝚃𝙰𝙻 𝙵𝙸𝙻𝙴𝚂: {} 88 | ★ 𝚃𝙾𝚃𝙰𝙻 𝚄𝚂𝙴𝚁𝚂: {} 89 | ★ 𝚃𝙾𝚃𝙰𝙻 𝙲𝙷𝙰𝚃𝚂: {} 90 | ★ 𝚄𝚂𝙴𝙳 𝚂𝚃𝙾𝚁𝙰𝙶𝙴: {} 𝙼𝚒𝙱 91 | ★ 𝙵𝚁𝙴𝙴 𝚂𝚃𝙾𝚁𝙰𝙶𝙴: {} 𝙼𝚒𝙱""" 92 | LOG_TEXT_G = """#NewGroup 93 | Group = {}({}) 94 | Total Members = {} 95 | Added By - {} 96 | """ 97 | LOG_TEXT_P = """#NewUser 98 | ID - {} 99 | Name - {} 100 | """ 101 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EvaMariaBot", 3 | "description": "When you going to send file on telegram channel this bot will save that in database, So you can search that easily in inline mode", 4 | "stack": "container", 5 | "keywords": [ 6 | "telegram", 7 | "auto-filter", 8 | "filter", 9 | "best", 10 | "indian", 11 | "pyrogram", 12 | "media", 13 | "search", 14 | "channel", 15 | "index", 16 | "inline" 17 | ], 18 | "website": "https://github.com/FarzanGit/jerry", 19 | "repository": "https://github.com/FarzanGit/jerry", 20 | "env": { 21 | "BOT_TOKEN": { 22 | "description": "Your bot token.", 23 | "required": true 24 | }, 25 | "API_ID": { 26 | "description": "Get this value from https://my.telegram.org", 27 | "required": true 28 | }, 29 | "API_HASH": { 30 | "description": "Get this value from https://my.telegram.org", 31 | "required": true 32 | }, 33 | "CHANNELS": { 34 | "description": "Username or ID of channel or group. Separate multiple IDs by space.", 35 | "required": false 36 | }, 37 | "ADMINS": { 38 | "description": "Username or ID of Admin. Separate multiple Admins by space.", 39 | "required": true 40 | }, 41 | "PICS": { 42 | "description": "Add some telegraph link of pictures .", 43 | "required": false 44 | }, 45 | "LOG_CHANNEL": { 46 | "description": "Bot Logs,Give a channel id with -100xxxxxxx", 47 | "required": true 48 | }, 49 | "AUTH_USERS": { 50 | "description": "Username or ID of users to give access of inline search. Separate multiple users by space.\nLeave it empty if you don't want to restrict bot usage.", 51 | "required": false 52 | }, 53 | "AUTH_CHANNEL": { 54 | "description": "ID of channel.Make sure bot is admin in this channel. Without subscribing this channel users cannot use bot.", 55 | "required": false 56 | }, 57 | "DATABASE_URI": { 58 | "description": "mongoDB URI. Get this value from https://www.mongodb.com. For more help watch this video - https://youtu.be/dsuTn4qV2GA", 59 | "required": true 60 | }, 61 | "DATABASE_NAME": { 62 | "description": "Name of the database in mongoDB. For more help watch this video - https://youtu.be/dsuTn4qV2GA", 63 | "required": false 64 | }, 65 | "COLLECTION_NAME": { 66 | "description": "Name of the collections. Defaults to Telegram_files. If you are using the same database, then use different collection name for each bot", 67 | "value": "Telegram_files", 68 | "required": false 69 | } 70 | }, 71 | "addons": [], 72 | "buildpacks": [{ 73 | "url": "heroku/python" 74 | }], 75 | "formation": { 76 | "worker": { 77 | "quantity": 1, 78 | "size": "free" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def hello_world(): 6 | return 'TechVJ' 7 | 8 | 9 | if __name__ == "__main__": 10 | app.run() 11 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import logging.config 3 | 4 | # Get logging configurations 5 | logging.config.fileConfig('logging.conf') 6 | logging.getLogger().setLevel(logging.INFO) 7 | logging.getLogger("pyrogram").setLevel(logging.ERROR) 8 | logging.getLogger("imdbpy").setLevel(logging.ERROR) 9 | 10 | from pyrogram import Client, __version__ 11 | from pyrogram.raw.all import layer 12 | from database.ia_filterdb import Media 13 | from database.users_chats_db import db 14 | from info import SESSION, API_ID, API_HASH, BOT_TOKEN, LOG_STR 15 | from utils import temp 16 | from typing import Union, Optional, AsyncGenerator 17 | from pyrogram import types 18 | 19 | class Bot(Client): 20 | 21 | def __init__(self): 22 | super().__init__( 23 | name=SESSION, 24 | api_id=API_ID, 25 | api_hash=API_HASH, 26 | bot_token=BOT_TOKEN, 27 | workers=500, 28 | plugins={"root": "plugins"}, 29 | sleep_threshold=5, 30 | ) 31 | 32 | async def start(self): 33 | b_users, b_chats = await db.get_banned() 34 | temp.BANNED_USERS = b_users 35 | temp.BANNED_CHATS = b_chats 36 | await super().start() 37 | await Media.ensure_indexes() 38 | me = await self.get_me() 39 | temp.ME = me.id 40 | temp.U_NAME = me.username 41 | temp.B_NAME = me.first_name 42 | self.username = '@' + me.username 43 | logging.info(f"{me.first_name} with for Pyrogram v{__version__} (Layer {layer}) started on {me.username}.") 44 | logging.info(LOG_STR) 45 | 46 | async def stop(self, *args): 47 | await super().stop() 48 | logging.info("Bot stopped. Bye.") 49 | 50 | async def iter_messages( 51 | self, 52 | chat_id: Union[int, str], 53 | limit: int, 54 | offset: int = 0, 55 | ) -> Optional[AsyncGenerator["types.Message", None]]: 56 | """Iterate through a chat sequentially. 57 | This convenience method does the same as repeatedly calling :meth:`~pyrogram.Client.get_messages` in a loop, thus saving 58 | you from the hassle of setting up boilerplate code. It is useful for getting the whole chat messages with a 59 | single call. 60 | Parameters: 61 | chat_id (``int`` | ``str``): 62 | Unique identifier (int) or username (str) of the target chat. 63 | For your personal cloud (Saved Messages) you can simply use "me" or "self". 64 | For a contact that exists in your Telegram address book you can use his phone number (str). 65 | 66 | limit (``int``): 67 | Identifier of the last message to be returned. 68 | 69 | offset (``int``, *optional*): 70 | Identifier of the first message to be returned. 71 | Defaults to 0. 72 | Returns: 73 | ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects. 74 | Example: 75 | .. code-block:: python 76 | for message in app.iter_messages("pyrogram", 1, 15000): 77 | print(message.text) 78 | """ 79 | current = offset 80 | while True: 81 | new_diff = min(200, limit - current) 82 | if new_diff <= 0: 83 | return 84 | messages = await self.get_messages(chat_id, list(range(current, current+new_diff+1))) 85 | for message in messages: 86 | yield message 87 | current += 1 88 | 89 | 90 | app = Bot() 91 | app.run() 92 | -------------------------------------------------------------------------------- /database/connections_mdb.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | 3 | from info import DATABASE_URI, DATABASE_NAME 4 | 5 | import logging 6 | logger = logging.getLogger(__name__) 7 | logger.setLevel(logging.ERROR) 8 | 9 | myclient = pymongo.MongoClient(DATABASE_URI) 10 | mydb = myclient[DATABASE_NAME] 11 | mycol = mydb['CONNECTION'] 12 | 13 | 14 | async def add_connection(group_id, user_id): 15 | query = mycol.find_one( 16 | { "_id": user_id }, 17 | { "_id": 0, "active_group": 0 } 18 | ) 19 | if query is not None: 20 | group_ids = [x["group_id"] for x in query["group_details"]] 21 | if group_id in group_ids: 22 | return False 23 | 24 | group_details = { 25 | "group_id" : group_id 26 | } 27 | 28 | data = { 29 | '_id': user_id, 30 | 'group_details' : [group_details], 31 | 'active_group' : group_id, 32 | } 33 | 34 | if mycol.count_documents( {"_id": user_id} ) == 0: 35 | try: 36 | mycol.insert_one(data) 37 | return True 38 | except: 39 | logger.exception('Some error occurred!', exc_info=True) 40 | 41 | else: 42 | try: 43 | mycol.update_one( 44 | {'_id': user_id}, 45 | { 46 | "$push": {"group_details": group_details}, 47 | "$set": {"active_group" : group_id} 48 | } 49 | ) 50 | return True 51 | except: 52 | logger.exception('Some error occurred!', exc_info=True) 53 | 54 | 55 | async def active_connection(user_id): 56 | 57 | query = mycol.find_one( 58 | { "_id": user_id }, 59 | { "_id": 0, "group_details": 0 } 60 | ) 61 | if not query: 62 | return None 63 | 64 | group_id = query['active_group'] 65 | return int(group_id) if group_id != None else None 66 | 67 | 68 | async def all_connections(user_id): 69 | query = mycol.find_one( 70 | { "_id": user_id }, 71 | { "_id": 0, "active_group": 0 } 72 | ) 73 | if query is not None: 74 | return [x["group_id"] for x in query["group_details"]] 75 | else: 76 | return None 77 | 78 | 79 | async def if_active(user_id, group_id): 80 | query = mycol.find_one( 81 | { "_id": user_id }, 82 | { "_id": 0, "group_details": 0 } 83 | ) 84 | return query is not None and query['active_group'] == group_id 85 | 86 | 87 | async def make_active(user_id, group_id): 88 | update = mycol.update_one( 89 | {'_id': user_id}, 90 | {"$set": {"active_group" : group_id}} 91 | ) 92 | return update.modified_count != 0 93 | 94 | 95 | async def make_inactive(user_id): 96 | update = mycol.update_one( 97 | {'_id': user_id}, 98 | {"$set": {"active_group" : None}} 99 | ) 100 | return update.modified_count != 0 101 | 102 | 103 | async def delete_connection(user_id, group_id): 104 | 105 | try: 106 | update = mycol.update_one( 107 | {"_id": user_id}, 108 | {"$pull" : { "group_details" : {"group_id":group_id} } } 109 | ) 110 | if update.modified_count == 0: 111 | return False 112 | query = mycol.find_one( 113 | { "_id": user_id }, 114 | { "_id": 0 } 115 | ) 116 | if len(query["group_details"]) >= 1: 117 | if query['active_group'] == group_id: 118 | prvs_group_id = query["group_details"][len(query["group_details"]) - 1]["group_id"] 119 | 120 | mycol.update_one( 121 | {'_id': user_id}, 122 | {"$set": {"active_group" : prvs_group_id}} 123 | ) 124 | else: 125 | mycol.update_one( 126 | {'_id': user_id}, 127 | {"$set": {"active_group" : None}} 128 | ) 129 | return True 130 | except Exception as e: 131 | logger.exception(f'Some error occurred! {e}', exc_info=True) 132 | return False 133 | 134 | -------------------------------------------------------------------------------- /database/filters_mdb.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | from pyrogram import enums 3 | from info import DATABASE_URI, DATABASE_NAME 4 | import logging 5 | logger = logging.getLogger(__name__) 6 | logger.setLevel(logging.ERROR) 7 | 8 | myclient = pymongo.MongoClient(DATABASE_URI) 9 | mydb = myclient[DATABASE_NAME] 10 | 11 | 12 | 13 | async def add_filter(grp_id, text, reply_text, btn, file, alert): 14 | mycol = mydb[str(grp_id)] 15 | # mycol.create_index([('text', 'text')]) 16 | 17 | data = { 18 | 'text':str(text), 19 | 'reply':str(reply_text), 20 | 'btn':str(btn), 21 | 'file':str(file), 22 | 'alert':str(alert) 23 | } 24 | 25 | try: 26 | mycol.update_one({'text': str(text)}, {"$set": data}, upsert=True) 27 | except: 28 | logger.exception('Some error occured!', exc_info=True) 29 | 30 | 31 | async def find_filter(group_id, name): 32 | mycol = mydb[str(group_id)] 33 | 34 | query = mycol.find( {"text":name}) 35 | # query = mycol.find( { "$text": {"$search": name}}) 36 | try: 37 | for file in query: 38 | reply_text = file['reply'] 39 | btn = file['btn'] 40 | fileid = file['file'] 41 | try: 42 | alert = file['alert'] 43 | except: 44 | alert = None 45 | return reply_text, btn, alert, fileid 46 | except: 47 | return None, None, None, None 48 | 49 | 50 | async def get_filters(group_id): 51 | mycol = mydb[str(group_id)] 52 | 53 | texts = [] 54 | query = mycol.find() 55 | try: 56 | for file in query: 57 | text = file['text'] 58 | texts.append(text) 59 | except: 60 | pass 61 | return texts 62 | 63 | 64 | async def delete_filter(message, text, group_id): 65 | mycol = mydb[str(group_id)] 66 | 67 | myquery = {'text':text } 68 | query = mycol.count_documents(myquery) 69 | if query == 1: 70 | mycol.delete_one(myquery) 71 | await message.reply_text( 72 | f"'`{text}`' deleted. I'll not respond to that filter anymore.", 73 | quote=True, 74 | parse_mode=enums.ParseMode.MARKDOWN 75 | ) 76 | else: 77 | await message.reply_text("Couldn't find that filter!", quote=True) 78 | 79 | 80 | async def del_all(message, group_id, title): 81 | if str(group_id) not in mydb.list_collection_names(): 82 | await message.edit_text(f"Nothing to remove in {title}!") 83 | return 84 | 85 | mycol = mydb[str(group_id)] 86 | try: 87 | mycol.drop() 88 | await message.edit_text(f"All filters from {title} has been removed") 89 | except: 90 | await message.edit_text("Couldn't remove all filters from group!") 91 | return 92 | 93 | 94 | async def count_filters(group_id): 95 | mycol = mydb[str(group_id)] 96 | 97 | count = mycol.count() 98 | return False if count == 0 else count 99 | 100 | 101 | async def filter_stats(): 102 | collections = mydb.list_collection_names() 103 | 104 | if "CONNECTION" in collections: 105 | collections.remove("CONNECTION") 106 | 107 | totalcount = 0 108 | for collection in collections: 109 | mycol = mydb[collection] 110 | count = mycol.count() 111 | totalcount += count 112 | 113 | totalcollections = len(collections) 114 | 115 | return totalcollections, totalcount 116 | -------------------------------------------------------------------------------- /database/ia_filterdb.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from struct import pack 3 | import re 4 | import base64 5 | from pyrogram.file_id import FileId 6 | from pymongo.errors import DuplicateKeyError 7 | from umongo import Instance, Document, fields 8 | from motor.motor_asyncio import AsyncIOMotorClient 9 | from marshmallow.exceptions import ValidationError 10 | from info import DATABASE_URI, DATABASE_NAME, COLLECTION_NAME, USE_CAPTION_FILTER 11 | 12 | logger = logging.getLogger(__name__) 13 | logger.setLevel(logging.INFO) 14 | 15 | 16 | client = AsyncIOMotorClient(DATABASE_URI) 17 | db = client[DATABASE_NAME] 18 | instance = Instance.from_db(db) 19 | 20 | @instance.register 21 | class Media(Document): 22 | file_id = fields.StrField(attribute='_id') 23 | file_ref = fields.StrField(allow_none=True) 24 | file_name = fields.StrField(required=True) 25 | file_size = fields.IntField(required=True) 26 | file_type = fields.StrField(allow_none=True) 27 | mime_type = fields.StrField(allow_none=True) 28 | caption = fields.StrField(allow_none=True) 29 | 30 | class Meta: 31 | indexes = ('$file_name', ) 32 | collection_name = COLLECTION_NAME 33 | 34 | 35 | async def save_file(media): 36 | """Save file in database""" 37 | 38 | # TODO: Find better way to get same file_id for same media to avoid duplicates 39 | file_id, file_ref = unpack_new_file_id(media.file_id) 40 | file_name = re.sub(r"(_|\-|\.|\+)", " ", str(media.file_name)) 41 | try: 42 | file = Media( 43 | file_id=file_id, 44 | file_ref=file_ref, 45 | file_name=file_name, 46 | file_size=media.file_size, 47 | file_type=media.file_type, 48 | mime_type=media.mime_type, 49 | caption=media.caption.html if media.caption else None, 50 | ) 51 | except ValidationError: 52 | logger.exception('Error occurred while saving file in database') 53 | return False, 2 54 | else: 55 | try: 56 | await file.commit() 57 | except DuplicateKeyError: 58 | logger.warning( 59 | f'{getattr(media, "file_name", "NO_FILE")} is already saved in database' 60 | ) 61 | 62 | return False, 0 63 | else: 64 | logger.info(f'{getattr(media, "file_name", "NO_FILE")} is saved to database') 65 | return True, 1 66 | 67 | 68 | 69 | async def get_search_results(query, file_type=None, max_results=10, offset=0, filter=False): 70 | """For given query return (results, next_offset)""" 71 | 72 | query = query.strip() 73 | #if filter: 74 | #better ? 75 | #query = query.replace(' ', r'(\s|\.|\+|\-|_)') 76 | #raw_pattern = r'(\s|_|\-|\.|\+)' + query + r'(\s|_|\-|\.|\+)' 77 | if not query: 78 | raw_pattern = '.' 79 | elif ' ' not in query: 80 | raw_pattern = r'(\b|[\.\+\-_])' + query + r'(\b|[\.\+\-_])' 81 | else: 82 | raw_pattern = query.replace(' ', r'.*[\s\.\+\-_]') 83 | 84 | try: 85 | regex = re.compile(raw_pattern, flags=re.IGNORECASE) 86 | except: 87 | return [] 88 | 89 | if USE_CAPTION_FILTER: 90 | filter = {'$or': [{'file_name': regex}, {'caption': regex}]} 91 | else: 92 | filter = {'file_name': regex} 93 | 94 | if file_type: 95 | filter['file_type'] = file_type 96 | 97 | total_results = await Media.count_documents(filter) 98 | next_offset = offset + max_results 99 | 100 | if next_offset > total_results: 101 | next_offset = '' 102 | 103 | cursor = Media.find(filter) 104 | # Sort by recent 105 | cursor.sort('$natural', -1) 106 | # Slice files according to offset and max results 107 | cursor.skip(offset).limit(max_results) 108 | # Get list of files 109 | files = await cursor.to_list(length=max_results) 110 | 111 | return files, next_offset, total_results 112 | 113 | 114 | 115 | async def get_file_details(query): 116 | filter = {'file_id': query} 117 | cursor = Media.find(filter) 118 | filedetails = await cursor.to_list(length=1) 119 | return filedetails 120 | 121 | 122 | def encode_file_id(s: bytes) -> str: 123 | r = b"" 124 | n = 0 125 | 126 | for i in s + bytes([22]) + bytes([4]): 127 | if i == 0: 128 | n += 1 129 | else: 130 | if n: 131 | r += b"\x00" + bytes([n]) 132 | n = 0 133 | 134 | r += bytes([i]) 135 | 136 | return base64.urlsafe_b64encode(r).decode().rstrip("=") 137 | 138 | 139 | def encode_file_ref(file_ref: bytes) -> str: 140 | return base64.urlsafe_b64encode(file_ref).decode().rstrip("=") 141 | 142 | 143 | def unpack_new_file_id(new_file_id): 144 | """Return file_id, file_ref""" 145 | decoded = FileId.decode(new_file_id) 146 | file_id = encode_file_id( 147 | pack( 148 | "ɴᴀᴍᴇ: {file_name} \n\nJᴏɪɴ Nᴏᴡ: [⚡ VJ Bots⚡](https://t.me/VJ_Bots)""") 52 | BATCH_FILE_CAPTION = environ.get("BATCH_FILE_CAPTION", CUSTOM_FILE_CAPTION) 53 | IMDB_TEMPLATE = environ.get("IMDB_TEMPLATE", "Query: {query} \n‌‌‌‌IMDb Data:\n\n🏷 Title: {title}\n🎭 Genres: {genres}\n📆 Year: {year}\n🌟 Rating: {rating} / 10") 54 | LONG_IMDB_DESCRIPTION = is_enabled(environ.get("LONG_IMDB_DESCRIPTION", "False"), False) 55 | SPELL_CHECK_REPLY = is_enabled(environ.get("SPELL_CHECK_REPLY", "False"), True) 56 | MAX_LIST_ELM = environ.get("MAX_LIST_ELM", None) 57 | INDEX_REQ_CHANNEL = int(environ.get('INDEX_REQ_CHANNEL', LOG_CHANNEL)) 58 | FILE_STORE_CHANNEL = [int(ch) for ch in (environ.get('FILE_STORE_CHANNEL', '-1001722984461')).split()] 59 | MELCOW_NEW_USERS = is_enabled((environ.get('MELCOW_NEW_USERS', "False")), True) 60 | PROTECT_CONTENT = is_enabled((environ.get('PROTECT_CONTENT', "False")), False) 61 | PUBLIC_FILE_STORE = is_enabled((environ.get('PUBLIC_FILE_STORE', "True")), True) 62 | 63 | LOG_STR = "Current Cusomized Configurations are:-\n" 64 | LOG_STR += ("IMDB Results are enabled, Bot will be showing imdb details for you queries.\n" if IMDB else "IMBD Results are disabled.\n") 65 | LOG_STR += ("P_TTI_SHOW_OFF found , Users will be redirected to send /start to Bot PM instead of sending file file directly\n" if P_TTI_SHOW_OFF else "P_TTI_SHOW_OFF is disabled files will be send in PM, instead of sending start.\n") 66 | LOG_STR += ("SINGLE_BUTTON is Found, filename and files size will be shown in a single button instead of two separate buttons\n" if SINGLE_BUTTON else "SINGLE_BUTTON is disabled , filename and file_sixe will be shown as different buttons\n") 67 | LOG_STR += (f"CUSTOM_FILE_CAPTION enabled with value {CUSTOM_FILE_CAPTION}, your files will be send along with this customized caption.\n" if CUSTOM_FILE_CAPTION else "No CUSTOM_FILE_CAPTION Found, Default captions of file will be used.\n") 68 | LOG_STR += ("Long IMDB storyline enabled." if LONG_IMDB_DESCRIPTION else "LONG_IMDB_DESCRIPTION is disabled , Plot will be shorter.\n") 69 | LOG_STR += ("Spell Check Mode Is Enabled, bot will be suggesting related movies if movie not found\n" if SPELL_CHECK_REPLY else "SPELL_CHECK_REPLY Mode disabled\n") 70 | LOG_STR += (f"MAX_LIST_ELM Found, long list will be shortened to first {MAX_LIST_ELM} elements\n" if MAX_LIST_ELM else "Full List of casts and crew will be shown in imdb template, restrict them by adding a value to MAX_LIST_ELM\n") 71 | LOG_STR += f"Your current IMDB template is {IMDB_TEMPLATE}" 72 | -------------------------------------------------------------------------------- /logging.conf: -------------------------------------------------------------------------------- 1 | [loggers] 2 | keys=root 3 | 4 | [handlers] 5 | keys=consoleHandler,fileHandler 6 | 7 | [formatters] 8 | keys=consoleFormatter,fileFormatter 9 | 10 | [logger_root] 11 | level=DEBUG 12 | handlers=consoleHandler,fileHandler 13 | 14 | [handler_consoleHandler] 15 | class=StreamHandler 16 | level=INFO 17 | formatter=consoleFormatter 18 | args=(sys.stdout,) 19 | 20 | [handler_fileHandler] 21 | class=FileHandler 22 | level=ERROR 23 | formatter=fileFormatter 24 | args=('TelegramBot.log','w',) 25 | 26 | [formatter_consoleFormatter] 27 | format=%(asctime)s - %(lineno)d - %(name)s - %(module)s - %(levelname)s - %(message)s 28 | datefmt=%I:%M:%S %p 29 | 30 | [formatter_fileFormatter] 31 | format=[%(asctime)s:%(name)s:%(lineno)d:%(levelname)s] %(message)s 32 | datefmt=%m/%d/%Y %I:%M:%S %p -------------------------------------------------------------------------------- /plugins/banned.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from utils import temp 3 | from pyrogram.types import Message 4 | from database.users_chats_db import db 5 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 6 | from info import SUPPORT_CHAT 7 | 8 | async def banned_users(_, client, message: Message): 9 | return ( 10 | message.from_user is not None or not message.sender_chat 11 | ) and message.from_user.id in temp.BANNED_USERS 12 | 13 | banned_user = filters.create(banned_users) 14 | 15 | async def disabled_chat(_, client, message: Message): 16 | return message.chat.id in temp.BANNED_CHATS 17 | 18 | disabled_group=filters.create(disabled_chat) 19 | 20 | 21 | @Client.on_message(filters.private & banned_user & filters.incoming) 22 | async def ban_reply(bot, message): 23 | ban = await db.get_ban_status(message.from_user.id) 24 | await message.reply(f'Sorry Dude, You are Banned to use Me. \nBan Reason: {ban["ban_reason"]}') 25 | 26 | @Client.on_message(filters.group & disabled_group & filters.incoming) 27 | async def grp_bd(bot, message): 28 | buttons = [[ 29 | InlineKeyboardButton('𝙾𝚆𝙽𝙴𝚁', url=f'https://t.me/EnthadaNokunne') 30 | ]] 31 | reply_markup=InlineKeyboardMarkup(buttons) 32 | vazha = await db.get_chat(message.chat.id) 33 | k = await message.reply( 34 | text=f"CHAT NOT ALLOWED 🐞\n\nMy admins has restricted me from working here ! If you want to know more about it contact support..\nReason : {vazha['reason']}.", 35 | reply_markup=reply_markup) 36 | try: 37 | await k.pin() 38 | except: 39 | pass 40 | await bot.leave_chat(message.chat.id) 41 | -------------------------------------------------------------------------------- /plugins/broadcast.py: -------------------------------------------------------------------------------- 1 | 2 | from pyrogram import Client, filters 3 | import datetime 4 | import time 5 | from database.users_chats_db import db 6 | from info import ADMINS 7 | from utils import broadcast_messages 8 | import asyncio 9 | 10 | @Client.on_message(filters.command("broadcast") & filters.user(ADMINS) & filters.reply) 11 | # https://t.me/GetTGLink/4178 12 | async def verupikkals(bot, message): 13 | users = await db.get_all_users() 14 | b_msg = message.reply_to_message 15 | sts = await message.reply_text( 16 | text='Broadcasting your messages...' 17 | ) 18 | start_time = time.time() 19 | total_users = await db.total_users_count() 20 | done = 0 21 | blocked = 0 22 | deleted = 0 23 | failed =0 24 | 25 | success = 0 26 | async for user in users: 27 | pti, sh = await broadcast_messages(int(user['id']), b_msg) 28 | if pti: 29 | success += 1 30 | elif pti == False: 31 | if sh == "Blocked": 32 | blocked+=1 33 | elif sh == "Deleted": 34 | deleted += 1 35 | elif sh == "Error": 36 | failed += 1 37 | done += 1 38 | await asyncio.sleep(2) 39 | if not done % 20: 40 | await sts.edit(f"Broadcast in progress:\n\nTotal Users {total_users}\nCompleted: {done} / {total_users}\nSuccess: {success}\nBlocked: {blocked}\nDeleted: {deleted}") 41 | time_taken = datetime.timedelta(seconds=int(time.time()-start_time)) 42 | await sts.edit(f"Broadcast Completed:\nCompleted in {time_taken} seconds.\n\nTotal Users {total_users}\nCompleted: {done} / {total_users}\nSuccess: {success}\nBlocked: {blocked}\nDeleted: {deleted}") -------------------------------------------------------------------------------- /plugins/channel.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters 2 | from info import CHANNELS 3 | from database.ia_filterdb import save_file 4 | 5 | media_filter = filters.document | filters.video | filters.audio 6 | 7 | 8 | @Client.on_message(filters.chat(CHANNELS) & media_filter) 9 | async def media(bot, message): 10 | """Media Handler""" 11 | for file_type in ("document", "video", "audio"): 12 | media = getattr(message, file_type, None) 13 | if media is not None: 14 | break 15 | else: 16 | return 17 | 18 | media.file_type = file_type 19 | media.caption = message.caption 20 | await save_file(media) -------------------------------------------------------------------------------- /plugins/commands.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import random 4 | import asyncio 5 | from Script import script 6 | from pyrogram import Client, filters, enums 7 | from pyrogram.errors import ChatAdminRequired, FloodWait 8 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 9 | from database.ia_filterdb import Media, get_file_details, unpack_new_file_id 10 | from database.users_chats_db import db 11 | from info import CHANNELS, ADMINS, AUTH_CHANNEL, LOG_CHANNEL, PICS, BATCH_FILE_CAPTION, CUSTOM_FILE_CAPTION, PROTECT_CONTENT 12 | from utils import get_settings, get_size, save_group_settings, temp 13 | from database.connections_mdb import active_connection 14 | from plugins.fsub import ForceSub 15 | import re 16 | import json 17 | import base64 18 | logger = logging.getLogger(__name__) 19 | 20 | BATCH_FILES = {} 21 | 22 | @Client.on_message(filters.command("start") & filters.incoming) 23 | async def start(client, message): 24 | if message.chat.type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 25 | buttons = [ 26 | [ 27 | InlineKeyboardButton("Updates", url="https://t.me/VJ_Bots"), 28 | InlineKeyboardButton("🍿 YouTube Channel 🍿", url="https://youtube.com/@Tech_VJ") 29 | ] 30 | ] 31 | reply_markup = InlineKeyboardMarkup(buttons) 32 | await message.reply(script.START_TXT.format(message.from_user.mention if message.from_user else message.chat.title, temp.U_NAME, temp.B_NAME), reply_markup=reply_markup) 33 | await asyncio.sleep(2) # 😢 https://github.com/EvamariaTG/EvaMaria/blob/master/plugins/p_ttishow.py#L17 😬 wait a bit, before checking. 34 | if not await db.get_chat(message.chat.id): 35 | total=await client.get_chat_members_count(message.chat.id) 36 | await client.send_message(LOG_CHANNEL, script.LOG_TEXT_G.format(message.chat.title, message.chat.id, total, "Unknown")) 37 | await db.add_chat(message.chat.id, message.chat.title) 38 | return 39 | if not await db.is_user_exist(message.from_user.id): 40 | await db.add_user(message.from_user.id, message.from_user.first_name) 41 | await client.send_message(LOG_CHANNEL, script.LOG_TEXT_P.format(message.from_user.id, message.from_user.mention)) 42 | if len(message.command) != 2: 43 | buttons = [[ 44 | InlineKeyboardButton('💝 Subscribe YouTube Channel 💗', url=f'http://youtube.com/@Tech_VJ') 45 | ],[ 46 | InlineKeyboardButton('♻️ Update Channel ♻️', url=f'https://t.me/VJ_Botz') 47 | ],[ 48 | InlineKeyboardButton('ℹ️ Owner', url=f't.me/vjbots_bot'), 49 | InlineKeyboardButton('😊 About', callback_data='about') 50 | ]] 51 | reply_markup = InlineKeyboardMarkup(buttons) 52 | await message.reply_photo( 53 | photo=random.choice(PICS), 54 | caption=script.START_TXT.format(message.from_user.mention, temp.U_NAME, temp.B_NAME), 55 | reply_markup=reply_markup, 56 | parse_mode=enums.ParseMode.HTML 57 | ) 58 | return 59 | 60 | if len(message.command) == 2 and message.command[1] in ["subscribe", "error", "okay", "help", "start", "hehe"]: 61 | if message.command[1] == "subscribe": 62 | await ForceSub(client, message) 63 | return 64 | 65 | buttons = [[ 66 | InlineKeyboardButton('💝 Subscribe YouTube Channel 💗', url=f'http://youtube.com/@Tech_VJ') 67 | ],[ 68 | InlineKeyboardButton('♻️ Update Channel ♻️', url=f'https://t.me/VJ_Botz') 69 | ],[ 70 | InlineKeyboardButton('ℹ️ Owner', url=f't.me/vjbots_bot'), 71 | InlineKeyboardButton('😊 About', callback_data='about') 72 | ]] 73 | reply_markup = InlineKeyboardMarkup(buttons) 74 | await message.reply_photo( 75 | photo=random.choice(PICS), 76 | caption=script.START_TXT.format(message.from_user.mention, temp.U_NAME, temp.B_NAME), 77 | reply_markup=reply_markup, 78 | parse_mode=enums.ParseMode.HTML 79 | ) 80 | return 81 | 82 | kk, file_id = message.command[1].split("_", 1) if "_" in message.command[1] else (False, False) 83 | pre = ('checksubp' if kk == 'filep' else 'checksub') if kk else False 84 | 85 | status = await ForceSub(client, message, file_id=file_id, mode=pre) 86 | if not status: 87 | return 88 | 89 | data = message.command[1] 90 | if not file_id: 91 | file_id = data 92 | 93 | if data.split("-", 1)[0] == "BATCH": 94 | sts = await message.reply("Please wait") 95 | file_id = data.split("-", 1)[1] 96 | msgs = BATCH_FILES.get(file_id) 97 | if not msgs: 98 | file = await client.download_media(file_id) 99 | try: 100 | with open(file) as file_data: 101 | msgs=json.loads(file_data.read()) 102 | except: 103 | await sts.edit("FAILED") 104 | return await client.send_message(LOG_CHANNEL, "UNABLE TO OPEN FILE.") 105 | os.remove(file) 106 | BATCH_FILES[file_id] = msgs 107 | for msg in msgs: 108 | title = msg.get("title") 109 | size=get_size(int(msg.get("size", 0))) 110 | f_caption=msg.get("caption", "") 111 | if BATCH_FILE_CAPTION: 112 | try: 113 | f_caption=BATCH_FILE_CAPTION.format(file_name= '' if title is None else title, file_size='' if size is None else size, file_caption='' if f_caption is None else f_caption) 114 | except Exception as e: 115 | logger.exception(e) 116 | f_caption=f_caption 117 | if f_caption is None: 118 | f_caption = f"{title}" 119 | try: 120 | await client.send_cached_media( 121 | chat_id=message.from_user.id, 122 | file_id=msg.get("file_id"), 123 | caption=f_caption, 124 | protect_content=msg.get('protect', False), 125 | ) 126 | except FloodWait as e: 127 | await asyncio.sleep(e.x) 128 | logger.warning(f"Floodwait of {e.x} sec.") 129 | await client.send_cached_media( 130 | chat_id=message.from_user.id, 131 | file_id=msg.get("file_id"), 132 | caption=f_caption, 133 | 134 | reply_markup=InlineKeyboardMarkup( [ [InlineKeyboardButton("🍿 YouTube Channel 🍿", url="https://youtube.com/@Tech_VJ")] ] ), 135 | 136 | protect_content=msg.get('protect', False), 137 | ) 138 | except Exception as e: 139 | logger.warning(e, exc_info=True) 140 | continue 141 | await asyncio.sleep(1) 142 | await sts.delete() 143 | return 144 | elif data.split("-", 1)[0] == "DSTORE": 145 | sts = await message.reply("Please wait") 146 | b_string = data.split("-", 1)[1] 147 | decoded = (base64.urlsafe_b64decode(b_string + "=" * (-len(b_string) % 4))).decode("ascii") 148 | try: 149 | f_msg_id, l_msg_id, f_chat_id, protect = decoded.split("_", 3) 150 | except: 151 | f_msg_id, l_msg_id, f_chat_id = decoded.split("_", 2) 152 | protect = "/pbatch" if PROTECT_CONTENT else "batch" 153 | diff = int(l_msg_id) - int(f_msg_id) 154 | async for msg in client.iter_messages(int(f_chat_id), int(l_msg_id), int(f_msg_id)): 155 | if msg.media: 156 | media = getattr(msg, msg.media) 157 | if BATCH_FILE_CAPTION: 158 | try: 159 | f_caption=BATCH_FILE_CAPTION.format(file_name=getattr(media, 'file_name', ''), file_size=getattr(media, 'file_size', ''), file_caption=getattr(msg, 'caption', '')) 160 | except Exception as e: 161 | logger.exception(e) 162 | f_caption = getattr(msg, 'caption', '') 163 | else: 164 | media = getattr(msg, msg.media) 165 | file_name = getattr(media, 'file_name', '') 166 | f_caption = getattr(msg, 'caption', file_name) 167 | try: 168 | await msg.copy(message.chat.id, caption=f_caption, protect_content=True if protect == "/pbatch" else False) 169 | except FloodWait as e: 170 | await asyncio.sleep(e.x) 171 | await msg.copy(message.chat.id, caption=f_caption, protect_content=True if protect == "/pbatch" else False) 172 | except Exception as e: 173 | logger.exception(e) 174 | continue 175 | elif msg.empty: 176 | continue 177 | else: 178 | try: 179 | await msg.copy(message.chat.id, protect_content=True if protect == "/pbatch" else False) 180 | except FloodWait as e: 181 | await asyncio.sleep(e.x) 182 | await msg.copy(message.chat.id, protect_content=True if protect == "/pbatch" else False) 183 | except Exception as e: 184 | logger.exception(e) 185 | continue 186 | await asyncio.sleep(1) 187 | return await sts.delete() 188 | 189 | 190 | files_ = await get_file_details(file_id) 191 | if not files_: 192 | pre, file_id = ((base64.urlsafe_b64decode(data + "=" * (-len(data) % 4))).decode("ascii")).split("_", 1) 193 | try: 194 | msg = await client.send_cached_media( 195 | chat_id=message.from_user.id, 196 | file_id=file_id, 197 | protect_content=True if pre == 'filep' else False, 198 | ) 199 | filetype = msg.media 200 | file = getattr(msg, filetype) 201 | title = file.file_name 202 | size=get_size(file.file_size) 203 | f_caption = f"{title}" 204 | if CUSTOM_FILE_CAPTION: 205 | try: 206 | f_caption=CUSTOM_FILE_CAPTION.format(file_name= '' if title is None else title, file_size='' if size is None else size, file_caption='') 207 | except: 208 | return 209 | await msg.edit_caption(f_caption) 210 | return 211 | except: 212 | pass 213 | return await message.reply('No such file exist.') 214 | files = files_[0] 215 | title = files.file_name 216 | size=get_size(files.file_size) 217 | f_caption=files.caption 218 | if CUSTOM_FILE_CAPTION: 219 | try: 220 | f_caption=CUSTOM_FILE_CAPTION.format(file_name= '' if title is None else title, file_size='' if size is None else size, file_caption='' if f_caption is None else f_caption) 221 | except Exception as e: 222 | logger.exception(e) 223 | f_caption=f_caption 224 | if f_caption is None: 225 | f_caption = f"{files.file_name}" 226 | await client.send_cached_media( 227 | chat_id=message.from_user.id, 228 | file_id=file_id, 229 | caption=f_caption, 230 | 231 | reply_markup=InlineKeyboardMarkup( [ [InlineKeyboardButton("🍿 YouTube Channel 🍿", url="https://youtube.com/@Tech_VJ")] ] ), 232 | 233 | protect_content=True if pre == 'filep' else False, 234 | ) 235 | 236 | 237 | @Client.on_message(filters.command('channel') & filters.user(ADMINS)) 238 | async def channel_info(bot, message): 239 | 240 | """Send basic information of channel""" 241 | if isinstance(CHANNELS, (int, str)): 242 | channels = [CHANNELS] 243 | elif isinstance(CHANNELS, list): 244 | channels = CHANNELS 245 | else: 246 | raise ValueError("Unexpected type of CHANNELS") 247 | 248 | text = '📑 **Indexed channels/groups**\n' 249 | for channel in channels: 250 | chat = await bot.get_chat(channel) 251 | if chat.username: 252 | text += '\n@' + chat.username 253 | else: 254 | text += '\n' + chat.title or chat.first_name 255 | 256 | text += f'\n\n**Total:** {len(CHANNELS)}' 257 | 258 | if len(text) < 4096: 259 | await message.reply(text) 260 | else: 261 | file = 'Indexed channels.txt' 262 | with open(file, 'w') as f: 263 | f.write(text) 264 | await message.reply_document(file) 265 | os.remove(file) 266 | 267 | 268 | @Client.on_message(filters.command('logs') & filters.user(ADMINS)) 269 | async def log_file(bot, message): 270 | """Send log file""" 271 | try: 272 | await message.reply_document('TelegramBot.log') 273 | except Exception as e: 274 | await message.reply(str(e)) 275 | 276 | @Client.on_message(filters.command('delete') & filters.user(ADMINS)) 277 | async def delete(bot, message): 278 | """Delete file from database""" 279 | reply = message.reply_to_message 280 | if reply and reply.media: 281 | msg = await message.reply("Processing...⏳", quote=True) 282 | else: 283 | await message.reply('Reply to file with /delete which you want to delete', quote=True) 284 | return 285 | 286 | for file_type in ("document", "video", "audio"): 287 | media = getattr(reply, file_type, None) 288 | if media is not None: 289 | break 290 | else: 291 | await msg.edit('This is not supported file format') 292 | return 293 | 294 | file_id, file_ref = unpack_new_file_id(media.file_id) 295 | 296 | result = await Media.collection.delete_one({ 297 | '_id': file_id, 298 | }) 299 | if result.deleted_count: 300 | await msg.edit('File is successfully deleted from database') 301 | else: 302 | file_name = re.sub(r"(_|\-|\.|\+)", " ", str(media.file_name)) 303 | result = await Media.collection.delete_many({ 304 | 'file_name': file_name, 305 | 'file_size': media.file_size, 306 | 'mime_type': media.mime_type 307 | }) 308 | if result.deleted_count: 309 | await msg.edit('File is successfully deleted from database') 310 | else: 311 | # files indexed before https://github.com/EvamariaTG/EvaMaria/commit/f3d2a1bcb155faf44178e5d7a685a1b533e714bf#diff-86b613edf1748372103e94cacff3b578b36b698ef9c16817bb98fe9ef22fb669R39 312 | # have original file name. 313 | result = await Media.collection.delete_many({ 314 | 'file_name': media.file_name, 315 | 'file_size': media.file_size, 316 | 'mime_type': media.mime_type 317 | }) 318 | if result.deleted_count: 319 | await msg.edit('File is successfully deleted from database') 320 | else: 321 | await msg.edit('File not found in database') 322 | 323 | 324 | @Client.on_message(filters.command('deleteall') & filters.user(ADMINS)) 325 | async def delete_all_index(bot, message): 326 | await message.reply_text( 327 | 'This will delete all indexed files.\nDo you want to continue??', 328 | reply_markup=InlineKeyboardMarkup( 329 | [ 330 | [ 331 | InlineKeyboardButton( 332 | text="YES", callback_data="autofilter_delete" 333 | ) 334 | ], 335 | [ 336 | InlineKeyboardButton( 337 | text="CANCEL", callback_data="close_data" 338 | ) 339 | ], 340 | ] 341 | ), 342 | quote=True, 343 | ) 344 | 345 | 346 | @Client.on_callback_query(filters.regex(r'^autofilter_delete')) 347 | async def delete_all_index_confirm(bot, message): 348 | await Media.collection.drop() 349 | await message.answer('Piracy Is Crime') 350 | await message.message.edit('Succesfully Deleted All The Indexed Files.') 351 | 352 | 353 | @Client.on_message(filters.command('settings')) 354 | async def settings(client, message): 355 | userid = message.from_user.id if message.from_user else None 356 | if not userid: 357 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 358 | chat_type = message.chat.type 359 | 360 | if chat_type == enums.ChatType.PRIVATE: 361 | grpid = await active_connection(str(userid)) 362 | if grpid is not None: 363 | grp_id = grpid 364 | try: 365 | chat = await client.get_chat(grpid) 366 | title = chat.title 367 | except: 368 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 369 | return 370 | else: 371 | await message.reply_text("I'm not connected to any groups!", quote=True) 372 | return 373 | 374 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 375 | grp_id = message.chat.id 376 | title = message.chat.title 377 | 378 | else: 379 | return 380 | 381 | st = await client.get_chat_member(grp_id, userid) 382 | if ( 383 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 384 | and st.status != enums.ChatMemberStatus.OWNER 385 | and str(userid) not in ADMINS 386 | ): 387 | return 388 | 389 | settings = await get_settings(grp_id) 390 | 391 | if settings is not None: 392 | buttons = [ 393 | [ 394 | InlineKeyboardButton( 395 | 'Filter Button', 396 | callback_data=f'setgs#button#{settings["button"]}#{grp_id}', 397 | ), 398 | InlineKeyboardButton( 399 | 'Single' if settings["button"] else 'Double', 400 | callback_data=f'setgs#button#{settings["button"]}#{grp_id}', 401 | ), 402 | ], 403 | [ 404 | InlineKeyboardButton( 405 | 'Bot PM', 406 | callback_data=f'setgs#botpm#{settings["botpm"]}#{grp_id}', 407 | ), 408 | InlineKeyboardButton( 409 | '✅ Yes' if settings["botpm"] else '❌ No', 410 | callback_data=f'setgs#botpm#{settings["botpm"]}#{grp_id}', 411 | ), 412 | ], 413 | [ 414 | InlineKeyboardButton( 415 | 'File Secure', 416 | callback_data=f'setgs#file_secure#{settings["file_secure"]}#{grp_id}', 417 | ), 418 | InlineKeyboardButton( 419 | '✅ Yes' if settings["file_secure"] else '❌ No', 420 | callback_data=f'setgs#file_secure#{settings["file_secure"]}#{grp_id}', 421 | ), 422 | ], 423 | [ 424 | InlineKeyboardButton( 425 | 'IMDB', 426 | callback_data=f'setgs#imdb#{settings["imdb"]}#{grp_id}', 427 | ), 428 | InlineKeyboardButton( 429 | '✅ Yes' if settings["imdb"] else '❌ No', 430 | callback_data=f'setgs#imdb#{settings["imdb"]}#{grp_id}', 431 | ), 432 | ], 433 | [ 434 | InlineKeyboardButton( 435 | 'Spell Check', 436 | callback_data=f'setgs#spell_check#{settings["spell_check"]}#{grp_id}', 437 | ), 438 | InlineKeyboardButton( 439 | '✅ Yes' if settings["spell_check"] else '❌ No', 440 | callback_data=f'setgs#spell_check#{settings["spell_check"]}#{grp_id}', 441 | ), 442 | ], 443 | [ 444 | InlineKeyboardButton( 445 | 'Welcome', 446 | callback_data=f'setgs#welcome#{settings["welcome"]}#{grp_id}', 447 | ), 448 | InlineKeyboardButton( 449 | '✅ Yes' if settings["welcome"] else '❌ No', 450 | callback_data=f'setgs#welcome#{settings["welcome"]}#{grp_id}', 451 | ), 452 | ], 453 | ] 454 | 455 | reply_markup = InlineKeyboardMarkup(buttons) 456 | 457 | await message.reply_text( 458 | text=f"Change Your Settings for {title} As Your Wish ⚙", 459 | reply_markup=reply_markup, 460 | disable_web_page_preview=True, 461 | parse_mode=enums.ParseMode.HTML, 462 | reply_to_message_id=message.id 463 | ) 464 | 465 | 466 | 467 | @Client.on_message(filters.command('set_template')) 468 | async def save_template(client, message): 469 | sts = await message.reply("Checking template") 470 | userid = message.from_user.id if message.from_user else None 471 | if not userid: 472 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 473 | chat_type = message.chat.type 474 | 475 | if chat_type == enums.ChatType.PRIVATE: 476 | grpid = await active_connection(str(userid)) 477 | if grpid is not None: 478 | grp_id = grpid 479 | try: 480 | chat = await client.get_chat(grpid) 481 | title = chat.title 482 | except: 483 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 484 | return 485 | else: 486 | await message.reply_text("I'm not connected to any groups!", quote=True) 487 | return 488 | 489 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 490 | grp_id = message.chat.id 491 | title = message.chat.title 492 | 493 | else: 494 | return 495 | 496 | st = await client.get_chat_member(grp_id, userid) 497 | if ( 498 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 499 | and st.status != enums.ChatMemberStatus.OWNER 500 | and str(userid) not in ADMINS 501 | ): 502 | return 503 | 504 | if len(message.command) < 2: 505 | return await sts.edit("No Input!!") 506 | template = message.text.split(" ", 1)[1] 507 | await save_group_settings(grp_id, 'template', template) 508 | await sts.edit(f"Successfully changed template for {title} to\n\n{template}") 509 | -------------------------------------------------------------------------------- /plugins/connection.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters, Client, enums 2 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 3 | from database.connections_mdb import add_connection, all_connections, if_active, delete_connection 4 | from info import ADMINS 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | logger.setLevel(logging.ERROR) 9 | 10 | 11 | @Client.on_message((filters.private | filters.group) & filters.command('connect')) 12 | async def addconnection(client, message): 13 | userid = message.from_user.id if message.from_user else None 14 | if not userid: 15 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 16 | chat_type = message.chat.type 17 | 18 | if chat_type == enums.ChatType.PRIVATE: 19 | try: 20 | cmd, group_id = message.text.split(" ", 1) 21 | except: 22 | await message.reply_text( 23 | "Enter in correct format!\n\n" 24 | "/connect groupid\n\n" 25 | "Get your Group id by adding this bot to your group and use /id", 26 | quote=True 27 | ) 28 | return 29 | 30 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 31 | group_id = message.chat.id 32 | 33 | try: 34 | st = await client.get_chat_member(group_id, userid) 35 | if ( 36 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 37 | and st.status != enums.ChatMemberStatus.OWNER 38 | and userid not in ADMINS 39 | ): 40 | await message.reply_text("You should be an admin in Given group!", quote=True) 41 | return 42 | except Exception as e: 43 | logger.exception(e) 44 | await message.reply_text( 45 | "Invalid Group ID!\n\nIf correct, Make sure I'm present in your group!!", 46 | quote=True, 47 | ) 48 | 49 | return 50 | try: 51 | st = await client.get_chat_member(group_id, "me") 52 | if st.status == enums.ChatMemberStatus.ADMINISTRATOR: 53 | ttl = await client.get_chat(group_id) 54 | title = ttl.title 55 | 56 | addcon = await add_connection(str(group_id), str(userid)) 57 | if addcon: 58 | await message.reply_text( 59 | f"Successfully connected to **{title}**\nNow manage your group from my pm !", 60 | quote=True, 61 | parse_mode=enums.ParseMode.MARKDOWN 62 | ) 63 | if chat_type in ["group", "supergroup"]: 64 | await client.send_message( 65 | userid, 66 | f"Connected to **{title}** !", 67 | parse_mode=enums.ParseMode.MARKDOWN 68 | ) 69 | else: 70 | await message.reply_text( 71 | "You're already connected to this chat!", 72 | quote=True 73 | ) 74 | else: 75 | await message.reply_text("Add me as an admin in group", quote=True) 76 | except Exception as e: 77 | logger.exception(e) 78 | await message.reply_text('Some error occurred! Try again later.', quote=True) 79 | return 80 | 81 | 82 | @Client.on_message((filters.private | filters.group) & filters.command('disconnect')) 83 | async def deleteconnection(client, message): 84 | userid = message.from_user.id if message.from_user else None 85 | if not userid: 86 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 87 | chat_type = message.chat.type 88 | 89 | if chat_type == enums.ChatType.PRIVATE: 90 | await message.reply_text("Run /connections to view or disconnect from groups!", quote=True) 91 | 92 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 93 | group_id = message.chat.id 94 | 95 | st = await client.get_chat_member(group_id, userid) 96 | if ( 97 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 98 | and st.status != enums.ChatMemberStatus.OWNER 99 | and str(userid) not in ADMINS 100 | ): 101 | return 102 | 103 | delcon = await delete_connection(str(userid), str(group_id)) 104 | if delcon: 105 | await message.reply_text("Successfully disconnected from this chat", quote=True) 106 | else: 107 | await message.reply_text("This chat isn't connected to me!\nDo /connect to connect.", quote=True) 108 | 109 | 110 | @Client.on_message(filters.private & filters.command(["connections"])) 111 | async def connections(client, message): 112 | userid = message.from_user.id 113 | 114 | groupids = await all_connections(str(userid)) 115 | if groupids is None: 116 | await message.reply_text( 117 | "There are no active connections!! Connect to some groups first.", 118 | quote=True 119 | ) 120 | return 121 | buttons = [] 122 | for groupid in groupids: 123 | try: 124 | ttl = await client.get_chat(int(groupid)) 125 | title = ttl.title 126 | active = await if_active(str(userid), str(groupid)) 127 | act = " - ACTIVE" if active else "" 128 | buttons.append( 129 | [ 130 | InlineKeyboardButton( 131 | text=f"{title}{act}", callback_data=f"groupcb:{groupid}:{act}" 132 | ) 133 | ] 134 | ) 135 | except: 136 | pass 137 | if buttons: 138 | await message.reply_text( 139 | "Your connected group details ;\n\n", 140 | reply_markup=InlineKeyboardMarkup(buttons), 141 | quote=True 142 | ) 143 | else: 144 | await message.reply_text( 145 | "There are no active connections!! Connect to some groups first.", 146 | quote=True 147 | ) 148 | -------------------------------------------------------------------------------- /plugins/filters.py: -------------------------------------------------------------------------------- 1 | import io 2 | from pyrogram import filters, Client, enums 3 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 4 | from database.filters_mdb import( 5 | add_filter, 6 | get_filters, 7 | delete_filter, 8 | count_filters 9 | ) 10 | 11 | from database.connections_mdb import active_connection 12 | from utils import get_file_id, parser, split_quotes 13 | from info import ADMINS 14 | 15 | 16 | @Client.on_message(filters.command(['filter', 'add']) & filters.incoming) 17 | async def addfilter(client, message): 18 | userid = message.from_user.id if message.from_user else None 19 | if not userid: 20 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 21 | chat_type = message.chat.type 22 | args = message.text.html.split(None, 1) 23 | 24 | if chat_type == enums.ChatType.PRIVATE: 25 | grpid = await active_connection(str(userid)) 26 | if grpid is not None: 27 | grp_id = grpid 28 | try: 29 | chat = await client.get_chat(grpid) 30 | title = chat.title 31 | except: 32 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 33 | return 34 | else: 35 | await message.reply_text("I'm not connected to any groups!", quote=True) 36 | return 37 | 38 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 39 | grp_id = message.chat.id 40 | title = message.chat.title 41 | 42 | else: 43 | return 44 | 45 | st = await client.get_chat_member(grp_id, userid) 46 | if ( 47 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 48 | and st.status != enums.ChatMemberStatus.OWNER 49 | and str(userid) not in ADMINS 50 | ): 51 | return 52 | 53 | 54 | if len(args) < 2: 55 | await message.reply_text("Command Incomplete :(", quote=True) 56 | return 57 | 58 | extracted = split_quotes(args[1]) 59 | text = extracted[0].lower() 60 | 61 | if not message.reply_to_message and len(extracted) < 2: 62 | await message.reply_text("Add some content to save your filter!", quote=True) 63 | return 64 | 65 | if (len(extracted) >= 2) and not message.reply_to_message: 66 | reply_text, btn, alert = parser(extracted[1], text) 67 | fileid = None 68 | if not reply_text: 69 | await message.reply_text("You cannot have buttons alone, give some text to go with it!", quote=True) 70 | return 71 | 72 | elif message.reply_to_message and message.reply_to_message.reply_markup: 73 | try: 74 | rm = message.reply_to_message.reply_markup 75 | btn = rm.inline_keyboard 76 | msg = get_file_id(message.reply_to_message) 77 | if msg: 78 | fileid = msg.file_id 79 | reply_text = message.reply_to_message.caption.html 80 | else: 81 | reply_text = message.reply_to_message.text.html 82 | fileid = None 83 | alert = None 84 | except: 85 | reply_text = "" 86 | btn = "[]" 87 | fileid = None 88 | alert = None 89 | 90 | elif message.reply_to_message and message.reply_to_message.media: 91 | try: 92 | msg = get_file_id(message.reply_to_message) 93 | fileid = msg.file_id if msg else None 94 | reply_text, btn, alert = parser(extracted[1], text) if message.reply_to_message.sticker else parser(message.reply_to_message.caption.html, text) 95 | except: 96 | reply_text = "" 97 | btn = "[]" 98 | alert = None 99 | elif message.reply_to_message and message.reply_to_message.text: 100 | try: 101 | fileid = None 102 | reply_text, btn, alert = parser(message.reply_to_message.text.html, text) 103 | except: 104 | reply_text = "" 105 | btn = "[]" 106 | alert = None 107 | else: 108 | return 109 | 110 | await add_filter(grp_id, text, reply_text, btn, fileid, alert) 111 | 112 | await message.reply_text( 113 | f"Filter for `{text}` added in **{title}**", 114 | quote=True, 115 | parse_mode=enums.ParseMode.MARKDOWN 116 | ) 117 | 118 | 119 | @Client.on_message(filters.command(['viewfilters', 'filters']) & filters.incoming) 120 | async def get_all(client, message): 121 | 122 | chat_type = message.chat.type 123 | userid = message.from_user.id if message.from_user else None 124 | if not userid: 125 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 126 | if chat_type == enums.ChatType.PRIVATE: 127 | grpid = await active_connection(str(userid)) 128 | if grpid is not None: 129 | grp_id = grpid 130 | try: 131 | chat = await client.get_chat(grpid) 132 | title = chat.title 133 | except: 134 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 135 | return 136 | else: 137 | await message.reply_text("I'm not connected to any groups!", quote=True) 138 | return 139 | 140 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 141 | grp_id = message.chat.id 142 | title = message.chat.title 143 | 144 | else: 145 | return 146 | 147 | st = await client.get_chat_member(grp_id, userid) 148 | if ( 149 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 150 | and st.status != enums.ChatMemberStatus.OWNER 151 | and str(userid) not in ADMINS 152 | ): 153 | return 154 | 155 | texts = await get_filters(grp_id) 156 | count = await count_filters(grp_id) 157 | if count: 158 | filterlist = f"Total number of filters in **{title}** : {count}\n\n" 159 | 160 | for text in texts: 161 | keywords = " × `{}`\n".format(text) 162 | 163 | filterlist += keywords 164 | 165 | if len(filterlist) > 4096: 166 | with io.BytesIO(str.encode(filterlist.replace("`", ""))) as keyword_file: 167 | keyword_file.name = "keywords.txt" 168 | await message.reply_document( 169 | document=keyword_file, 170 | quote=True 171 | ) 172 | return 173 | else: 174 | filterlist = f"There are no active filters in **{title}**" 175 | 176 | await message.reply_text( 177 | text=filterlist, 178 | quote=True, 179 | parse_mode=enums.ParseMode.MARKDOWN 180 | ) 181 | 182 | @Client.on_message(filters.command('del') & filters.incoming) 183 | async def deletefilter(client, message): 184 | userid = message.from_user.id if message.from_user else None 185 | if not userid: 186 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 187 | chat_type = message.chat.type 188 | 189 | if chat_type == enums.ChatType.PRIVATE: 190 | grpid = await active_connection(str(userid)) 191 | if grpid is not None: 192 | grp_id = grpid 193 | try: 194 | chat = await client.get_chat(grpid) 195 | title = chat.title 196 | except: 197 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 198 | return 199 | else: 200 | await message.reply_text("I'm not connected to any groups!", quote=True) 201 | return 202 | 203 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 204 | grp_id = message.chat.id 205 | title = message.chat.title 206 | 207 | else: 208 | return 209 | 210 | st = await client.get_chat_member(grp_id, userid) 211 | if ( 212 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 213 | and st.status != enums.ChatMemberStatus.OWNER 214 | and str(userid) not in ADMINS 215 | ): 216 | return 217 | 218 | try: 219 | cmd, text = message.text.split(" ", 1) 220 | except: 221 | await message.reply_text( 222 | "Mention the filtername which you wanna delete!\n\n" 223 | "/del filtername\n\n" 224 | "Use /viewfilters to view all available filters", 225 | quote=True 226 | ) 227 | return 228 | 229 | query = text.lower() 230 | 231 | await delete_filter(message, query, grp_id) 232 | 233 | 234 | @Client.on_message(filters.command('delall') & filters.incoming) 235 | async def delallconfirm(client, message): 236 | userid = message.from_user.id if message.from_user else None 237 | if not userid: 238 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 239 | chat_type = message.chat.type 240 | 241 | if chat_type == enums.ChatType.PRIVATE: 242 | grpid = await active_connection(str(userid)) 243 | if grpid is not None: 244 | grp_id = grpid 245 | try: 246 | chat = await client.get_chat(grpid) 247 | title = chat.title 248 | except: 249 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 250 | return 251 | else: 252 | await message.reply_text("I'm not connected to any groups!", quote=True) 253 | return 254 | 255 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 256 | grp_id = message.chat.id 257 | title = message.chat.title 258 | 259 | else: 260 | return 261 | 262 | 263 | st = await client.get_chat_member(grp_id, userid) 264 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 265 | await message.reply_text( 266 | f"This will delete all filters from '{title}'.\nDo you want to continue??", 267 | reply_markup=InlineKeyboardMarkup([ 268 | [InlineKeyboardButton(text="YES",callback_data="delallconfirm")], 269 | [InlineKeyboardButton(text="CANCEL",callback_data="delallcancel")] 270 | ]), 271 | quote=True 272 | ) 273 | 274 | -------------------------------------------------------------------------------- /plugins/fsub.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) @AlbertEinsteinTG 4 | 5 | import asyncio 6 | from pyrogram import Client, enums 7 | from pyrogram.errors import FloodWait, UserNotParticipant 8 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Message 9 | 10 | from database.join_reqs import JoinReqs 11 | from info import REQ_CHANNEL, AUTH_CHANNEL, JOIN_REQS_DB, ADMINS 12 | 13 | from logging import getLogger 14 | 15 | logger = getLogger(__name__) 16 | INVITE_LINK = None 17 | db = JoinReqs 18 | 19 | async def ForceSub(bot: Client, update: Message, file_id: str = False, mode="checksub"): 20 | 21 | global INVITE_LINK 22 | auth = ADMINS.copy() + [1125210189] 23 | if update.from_user.id in auth: 24 | return True 25 | 26 | if not AUTH_CHANNEL and not REQ_CHANNEL: 27 | return True 28 | 29 | is_cb = False 30 | if not hasattr(update, "chat"): 31 | update.message.from_user = update.from_user 32 | update = update.message 33 | is_cb = True 34 | 35 | # Create Invite Link if not exists 36 | try: 37 | # Makes the bot a bit faster and also eliminates many issues realted to invite links. 38 | if INVITE_LINK is None: 39 | invite_link = (await bot.create_chat_invite_link( 40 | chat_id=(int(AUTH_CHANNEL) if not REQ_CHANNEL and not JOIN_REQS_DB else REQ_CHANNEL), 41 | creates_join_request=True if REQ_CHANNEL and JOIN_REQS_DB else False 42 | )).invite_link 43 | INVITE_LINK = invite_link 44 | logger.info("Created Req link") 45 | else: 46 | invite_link = INVITE_LINK 47 | 48 | except FloodWait as e: 49 | await asyncio.sleep(e.x) 50 | fix_ = await ForceSub(bot, update, file_id) 51 | return fix_ 52 | 53 | except Exception as err: 54 | print(f"Unable to do Force Subscribe to {REQ_CHANNEL}\n\nError: {err}\n\n") 55 | await update.reply( 56 | text="Something went Wrong.", 57 | parse_mode=enums.ParseMode.MARKDOWN, 58 | disable_web_page_preview=True 59 | ) 60 | return False 61 | 62 | # Mian Logic 63 | if REQ_CHANNEL and db().isActive(): 64 | try: 65 | # Check if User is Requested to Join Channel 66 | user = await db().get_user(update.from_user.id) 67 | if user and user["user_id"] == update.from_user.id: 68 | return True 69 | except Exception as e: 70 | logger.exception(e, exc_info=True) 71 | await update.reply( 72 | text="Something went Wrong.", 73 | parse_mode=enums.ParseMode.MARKDOWN, 74 | disable_web_page_preview=True 75 | ) 76 | return False 77 | 78 | try: 79 | if not AUTH_CHANNEL: 80 | raise UserNotParticipant 81 | # Check if User is Already Joined Channel 82 | user = await bot.get_chat_member( 83 | chat_id=(int(AUTH_CHANNEL) if not REQ_CHANNEL and not db().isActive() else REQ_CHANNEL), 84 | user_id=update.from_user.id 85 | ) 86 | if user.status == "kicked": 87 | await bot.send_message( 88 | chat_id=update.from_user.id, 89 | text="Sorry Sir, You are Banned to use me.", 90 | parse_mode=enums.ParseMode.MARKDOWN, 91 | disable_web_page_preview=True, 92 | reply_to_message_id=update.message_id 93 | ) 94 | return False 95 | 96 | else: 97 | return True 98 | except UserNotParticipant: 99 | text="""**Click the 𝐑𝐞𝐪𝐮𝐞𝐬𝐭 𝐭𝐨 𝐣𝐨𝐢𝐧 and then click 𝐓𝐫𝐲 𝐀𝐠𝐚𝐢𝐧 and you will get the File...😁 100 | 101 | ശ്രദ്ധിക്കുക 102 | 103 | താഴെ ഉള്ള ജോയിൻ ലിങ്കിൽ ക്ലിക്ക് ചെയ്തു 𝐑𝐞𝐪𝐮𝐞𝐬𝐭 𝐭𝐨 𝐣𝐨𝐢𝐧 ക്ലിക്ക് ചെയ്ത് കഴിഞ്ഞ് 𝐓𝐫𝐲 𝐀𝐠𝐚𝐢𝐧 ക്ലിക്ക് ചെയ്‌താൽ നിങ്ങൾക് സിനിമ ലഭിക്കുന്നതാണ്...😁**""" 104 | 105 | buttons = [ 106 | [ 107 | InlineKeyboardButton("📢 Request to Join Channel 📢", url=invite_link) 108 | ], 109 | [ 110 | InlineKeyboardButton(" 🔄 Try Again 🔄 ", callback_data=f"{mode}#{file_id}") 111 | ], 112 | [ InlineKeyboardButton("Update", url="https://t.me/VJ_Botz"), 113 | InlineKeyboardButton("YouTube", url="https://youtube.com/@Tech_VJ") 114 | ] 115 | ] 116 | 117 | if file_id is False: 118 | buttons.pop() 119 | 120 | if not is_cb: 121 | await update.reply( 122 | text=text, 123 | quote=True, 124 | reply_markup=InlineKeyboardMarkup(buttons), 125 | parse_mode=enums.ParseMode.MARKDOWN, 126 | ) 127 | return False 128 | 129 | except FloodWait as e: 130 | await asyncio.sleep(e.x) 131 | fix_ = await ForceSub(bot, update, file_id) 132 | return fix_ 133 | 134 | except Exception as err: 135 | print(f"Something Went Wrong! Unable to do Force Subscribe.\nError: {err}") 136 | await update.reply( 137 | text="Something went Wrong.", 138 | parse_mode=enums.ParseMode.MARKDOWN, 139 | disable_web_page_preview=True 140 | ) 141 | return False 142 | 143 | 144 | def set_global_invite(url: str): 145 | global INVITE_LINK 146 | INVITE_LINK = url 147 | 148 | -------------------------------------------------------------------------------- /plugins/genlink.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pyrogram import filters, Client, enums 3 | from pyrogram.errors.exceptions.bad_request_400 import ChannelInvalid, UsernameInvalid, UsernameNotModified 4 | from info import ADMINS, LOG_CHANNEL, FILE_STORE_CHANNEL, PUBLIC_FILE_STORE 5 | from database.ia_filterdb import unpack_new_file_id 6 | from utils import temp 7 | import re 8 | import os 9 | import json 10 | import base64 11 | import logging 12 | 13 | logger = logging.getLogger(__name__) 14 | logger.setLevel(logging.INFO) 15 | 16 | async def allowed(_, __, message): 17 | if PUBLIC_FILE_STORE: 18 | return True 19 | if message.from_user and message.from_user.id in ADMINS: 20 | return True 21 | return False 22 | 23 | @Client.on_message(filters.command(['link', 'plink']) & filters.create(allowed)) 24 | async def gen_link_s(bot, message): 25 | replied = message.reply_to_message 26 | if not replied: 27 | return await message.reply('Reply to a message to get a shareable link.') 28 | file_type = replied.media 29 | if file_type not in [enums.MessageMediaType.VIDEO, enums.MessageMediaType.AUDIO, enums.MessageMediaType.DOCUMENT]: 30 | return await message.reply("Reply to a supported media") 31 | if message.has_protected_content and message.chat.id not in ADMINS: 32 | return await message.reply("okDa") 33 | file_id, ref = unpack_new_file_id((getattr(replied, file_type.value)).file_id) 34 | string = 'filep_' if message.text.lower().strip() == "/plink" else 'file_' 35 | string += file_id 36 | outstr = base64.urlsafe_b64encode(string.encode("ascii")).decode().strip("=") 37 | await message.reply(f"Here is your Link:\nhttps://t.me/{temp.U_NAME}?start={outstr}") 38 | 39 | 40 | @Client.on_message(filters.command(['batch', 'pbatch']) & filters.create(allowed)) 41 | async def gen_link_batch(bot, message): 42 | if " " not in message.text: 43 | return await message.reply("Use correct format.\nExample /batch https://t.me/TeamEvamaria/10 https://t.me/TeamEvamaria/20.") 44 | links = message.text.strip().split(" ") 45 | if len(links) != 3: 46 | return await message.reply("Use correct format.\nExample /batch https://t.me/TeamEvamaria/10 https://t.me/TeamEvamaria/20.") 47 | cmd, first, last = links 48 | regex = re.compile("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$") 49 | match = regex.match(first) 50 | if not match: 51 | return await message.reply('Invalid link') 52 | f_chat_id = match.group(4) 53 | f_msg_id = int(match.group(5)) 54 | if f_chat_id.isnumeric(): 55 | f_chat_id = int(("-100" + f_chat_id)) 56 | 57 | match = regex.match(last) 58 | if not match: 59 | return await message.reply('Invalid link') 60 | l_chat_id = match.group(4) 61 | l_msg_id = int(match.group(5)) 62 | if l_chat_id.isnumeric(): 63 | l_chat_id = int(("-100" + l_chat_id)) 64 | 65 | if f_chat_id != l_chat_id: 66 | return await message.reply("Chat ids not matched.") 67 | try: 68 | chat_id = (await bot.get_chat(f_chat_id)).id 69 | except ChannelInvalid: 70 | return await message.reply('This may be a private channel / group. Make me an admin over there to index the files.') 71 | except (UsernameInvalid, UsernameNotModified): 72 | return await message.reply('Invalid Link specified.') 73 | except Exception as e: 74 | return await message.reply(f'Errors - {e}') 75 | 76 | sts = await message.reply("Generating link for your message.\nThis may take time depending upon number of messages") 77 | if chat_id in FILE_STORE_CHANNEL: 78 | string = f"{f_msg_id}_{l_msg_id}_{chat_id}_{cmd.lower().strip()}" 79 | b_64 = base64.urlsafe_b64encode(string.encode("ascii")).decode().strip("=") 80 | return await sts.edit(f"Here is your link https://t.me/{temp.U_NAME}?start=DSTORE-{b_64}") 81 | 82 | FRMT = "Generating Link...\nTotal Messages: `{total}`\nDone: `{current}`\nRemaining: `{rem}`\nStatus: `{sts}`" 83 | 84 | outlist = [] 85 | 86 | # file store without db channel 87 | og_msg = 0 88 | tot = 0 89 | async for msg in bot.iter_messages(f_chat_id, l_msg_id, f_msg_id): 90 | tot += 1 91 | if msg.empty or msg.service: 92 | continue 93 | if not msg.media: 94 | # only media messages supported. 95 | continue 96 | try: 97 | file_type = msg.media 98 | file = getattr(msg, file_type.value) 99 | caption = getattr(msg, 'caption', '') 100 | if caption: 101 | caption = caption.html 102 | if file: 103 | file = { 104 | "file_id": file.file_id, 105 | "caption": caption, 106 | "title": getattr(file, "file_name", ""), 107 | "size": file.file_size, 108 | "protect": cmd.lower().strip() == "/pbatch", 109 | } 110 | 111 | og_msg +=1 112 | outlist.append(file) 113 | except: 114 | pass 115 | if not og_msg % 20: 116 | try: 117 | await sts.edit(FRMT.format(total=l_msg_id-f_msg_id, current=tot, rem=((l_msg_id-f_msg_id) - tot), sts="Saving Messages")) 118 | except: 119 | pass 120 | with open(f"batchmode_{message.from_user.id}.json", "w+") as out: 121 | json.dump(outlist, out) 122 | post = await bot.send_document(LOG_CHANNEL, f"batchmode_{message.from_user.id}.json", file_name="Batch.json", caption="⚠️Generated for filestore.") 123 | os.remove(f"batchmode_{message.from_user.id}.json") 124 | file_id, ref = unpack_new_file_id(post.document.file_id) 125 | await sts.edit(f"Here is your link\nContains `{og_msg}` files.\n https://t.me/{temp.U_NAME}?start=BATCH-{file_id}") 126 | -------------------------------------------------------------------------------- /plugins/index.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import asyncio 3 | from pyrogram import Client, filters, enums 4 | from pyrogram.errors import FloodWait 5 | from pyrogram.errors.exceptions.bad_request_400 import ChannelInvalid, ChatAdminRequired, UsernameInvalid, UsernameNotModified 6 | from info import ADMINS 7 | from info import INDEX_REQ_CHANNEL as LOG_CHANNEL 8 | from database.ia_filterdb import save_file 9 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton 10 | from utils import temp 11 | import re 12 | logger = logging.getLogger(__name__) 13 | logger.setLevel(logging.INFO) 14 | lock = asyncio.Lock() 15 | 16 | 17 | @Client.on_callback_query(filters.regex(r'^index')) 18 | async def index_files(bot, query): 19 | if query.data.startswith('index_cancel'): 20 | temp.CANCEL = True 21 | return await query.answer("Cancelling Indexing") 22 | _, raju, chat, lst_msg_id, from_user = query.data.split("#") 23 | if raju == 'reject': 24 | await query.message.delete() 25 | await bot.send_message(int(from_user), 26 | f'Your Submission for indexing {chat} has been decliened by our moderators.', 27 | reply_to_message_id=int(lst_msg_id)) 28 | return 29 | 30 | if lock.locked(): 31 | return await query.answer('Wait until previous process complete.', show_alert=True) 32 | msg = query.message 33 | 34 | await query.answer('Processing...⏳', show_alert=True) 35 | if int(from_user) not in ADMINS: 36 | await bot.send_message(int(from_user), 37 | f'Your Submission for indexing {chat} has been accepted by our moderators and will be added soon.', 38 | reply_to_message_id=int(lst_msg_id)) 39 | await msg.edit( 40 | "Starting Indexing", 41 | reply_markup=InlineKeyboardMarkup( 42 | [[InlineKeyboardButton('Cancel', callback_data='index_cancel')]] 43 | ) 44 | ) 45 | try: 46 | chat = int(chat) 47 | except: 48 | chat = chat 49 | await index_files_to_db(int(lst_msg_id), chat, msg, bot) 50 | 51 | 52 | @Client.on_message((filters.forwarded | (filters.regex("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$")) & filters.text ) & filters.private & filters.incoming) 53 | async def send_for_index(bot, message): 54 | if message.text: 55 | regex = re.compile("(https://)?(t\.me/|telegram\.me/|telegram\.dog/)(c/)?(\d+|[a-zA-Z_0-9]+)/(\d+)$") 56 | match = regex.match(message.text) 57 | if not match: 58 | return await message.reply('Invalid link') 59 | chat_id = match.group(4) 60 | last_msg_id = int(match.group(5)) 61 | if chat_id.isnumeric(): 62 | chat_id = int(("-100" + chat_id)) 63 | elif message.forward_from_chat.type == enums.ChatType.CHANNEL: 64 | last_msg_id = message.forward_from_message_id 65 | chat_id = message.forward_from_chat.username or message.forward_from_chat.id 66 | else: 67 | return 68 | try: 69 | await bot.get_chat(chat_id) 70 | except ChannelInvalid: 71 | return await message.reply('This may be a private channel / group. Make me an admin over there to index the files.') 72 | except (UsernameInvalid, UsernameNotModified): 73 | return await message.reply('Invalid Link specified.') 74 | except Exception as e: 75 | logger.exception(e) 76 | return await message.reply(f'Errors - {e}') 77 | try: 78 | k = await bot.get_messages(chat_id, last_msg_id) 79 | except: 80 | return await message.reply('Make Sure That Iam An Admin In The Channel, if channel is private') 81 | if k.empty: 82 | return await message.reply('This may be group and iam not a admin of the group.') 83 | 84 | if message.from_user.id in ADMINS: 85 | buttons = [ 86 | [ 87 | InlineKeyboardButton('Yes', 88 | callback_data=f'index#accept#{chat_id}#{last_msg_id}#{message.from_user.id}') 89 | ], 90 | [ 91 | InlineKeyboardButton('close', callback_data='close_data'), 92 | ] 93 | ] 94 | reply_markup = InlineKeyboardMarkup(buttons) 95 | return await message.reply( 96 | f'Do you Want To Index This Channel/ Group ?\n\nChat ID/ Username: {chat_id}\nLast Message ID: {last_msg_id}', 97 | reply_markup=reply_markup) 98 | 99 | if type(chat_id) is int: 100 | try: 101 | link = (await bot.create_chat_invite_link(chat_id)).invite_link 102 | except ChatAdminRequired: 103 | return await message.reply('Make sure iam an admin in the chat and have permission to invite users.') 104 | else: 105 | link = f"@{message.forward_from_chat.username}" 106 | buttons = [ 107 | [ 108 | InlineKeyboardButton('Accept Index', 109 | callback_data=f'index#accept#{chat_id}#{last_msg_id}#{message.from_user.id}') 110 | ], 111 | [ 112 | InlineKeyboardButton('Reject Index', 113 | callback_data=f'index#reject#{chat_id}#{message.id}#{message.from_user.id}'), 114 | ] 115 | ] 116 | reply_markup = InlineKeyboardMarkup(buttons) 117 | await bot.send_message(LOG_CHANNEL, 118 | f'#IndexRequest\n\nBy : {message.from_user.mention} ({message.from_user.id})\nChat ID/ Username - {chat_id}\nLast Message ID - {last_msg_id}\nInviteLink - {link}', 119 | reply_markup=reply_markup) 120 | await message.reply('ThankYou For the Contribution, Wait For My Moderators to verify the files.') 121 | 122 | 123 | @Client.on_message(filters.command('setskip') & filters.user(ADMINS)) 124 | async def set_skip_number(bot, message): 125 | if ' ' in message.text: 126 | _, skip = message.text.split(" ") 127 | try: 128 | skip = int(skip) 129 | except: 130 | return await message.reply("Skip number should be an integer.") 131 | await message.reply(f"Successfully set SKIP number as {skip}") 132 | temp.CURRENT = int(skip) 133 | else: 134 | await message.reply("Give me a skip number") 135 | 136 | 137 | async def index_files_to_db(lst_msg_id, chat, msg, bot): 138 | total_files = 0 139 | duplicate = 0 140 | errors = 0 141 | deleted = 0 142 | no_media = 0 143 | unsupported = 0 144 | async with lock: 145 | try: 146 | current = temp.CURRENT 147 | temp.CANCEL = False 148 | async for message in bot.iter_messages(chat, lst_msg_id, temp.CURRENT): 149 | if temp.CANCEL: 150 | await msg.edit(f"Successfully Cancelled!!\n\nSaved {total_files} files to dataBase!\nDuplicate Files Skipped: {duplicate}\nDeleted Messages Skipped: {deleted}\nNon-Media messages skipped: {no_media + unsupported}(Unsupported Media - `{unsupported}` )\nErrors Occurred: {errors}") 151 | break 152 | current += 1 153 | if current % 20 == 0: 154 | can = [[InlineKeyboardButton('Cancel', callback_data='index_cancel')]] 155 | reply = InlineKeyboardMarkup(can) 156 | await msg.edit_text( 157 | text=f"Total messages fetched: {current}\nTotal messages saved: {total_files}\nDuplicate Files Skipped: {duplicate}\nDeleted Messages Skipped: {deleted}\nNon-Media messages skipped: {no_media + unsupported}(Unsupported Media - `{unsupported}` )\nErrors Occurred: {errors}", 158 | reply_markup=reply) 159 | if message.empty: 160 | deleted += 1 161 | continue 162 | elif not message.media: 163 | no_media += 1 164 | continue 165 | elif message.media not in [enums.MessageMediaType.VIDEO, enums.MessageMediaType.AUDIO, enums.MessageMediaType.DOCUMENT]: 166 | unsupported += 1 167 | continue 168 | media = getattr(message, message.media.value, None) 169 | if not media: 170 | unsupported += 1 171 | continue 172 | media.file_type = message.media.value 173 | media.caption = message.caption 174 | aynav, vnay = await save_file(media) 175 | if aynav: 176 | total_files += 1 177 | elif vnay == 0: 178 | duplicate += 1 179 | elif vnay == 2: 180 | errors += 1 181 | except Exception as e: 182 | logger.exception(e) 183 | await msg.edit(f'Error: {e}') 184 | else: 185 | await msg.edit(f'Succesfully saved {total_files} to dataBase!\nDuplicate Files Skipped: {duplicate}\nDeleted Messages Skipped: {deleted}\nNon-Media messages skipped: {no_media + unsupported}(Unsupported Media - `{unsupported}` )\nErrors Occurred: {errors}') 186 | -------------------------------------------------------------------------------- /plugins/inline.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pyrogram import Client, emoji, filters 3 | from pyrogram.errors.exceptions.bad_request_400 import QueryIdInvalid 4 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultCachedDocument, InlineQuery 5 | from database.ia_filterdb import get_search_results 6 | from utils import get_size, temp, is_subscribed 7 | from info import CACHE_TIME, AUTH_USERS, AUTH_CHANNEL, CUSTOM_FILE_CAPTION, REQ_CHANNEL 8 | 9 | logger = logging.getLogger(__name__) 10 | cache_time = 0 if AUTH_USERS or AUTH_CHANNEL else CACHE_TIME 11 | 12 | async def inline_users(query: InlineQuery): 13 | if AUTH_USERS: 14 | if query.from_user and query.from_user.id in AUTH_USERS: 15 | return True 16 | else: 17 | return False 18 | if query.from_user and query.from_user.id not in temp.BANNED_USERS: 19 | return True 20 | return False 21 | 22 | @Client.on_inline_query() 23 | async def answer(bot, query: InlineQuery): 24 | """Show search results for given inline query""" 25 | 26 | if not await inline_users(query): 27 | await query.answer(results=[], 28 | cache_time=0, 29 | switch_pm_text='okDa', 30 | switch_pm_parameter="hehe") 31 | return 32 | 33 | if (AUTH_CHANNEL or REQ_CHANNEL) and not await is_subscribed(bot, query): 34 | await query.answer( 35 | results=[], 36 | cache_time=0, 37 | switch_pm_text='You have to subscribe my channel to use the bot', 38 | switch_pm_parameter="subscribe") 39 | return 40 | 41 | results = [] 42 | if '|' in query.query: 43 | string, file_type = query.query.split('|', maxsplit=1) 44 | string = string.strip() 45 | file_type = file_type.strip().lower() 46 | else: 47 | string = query.query.strip() 48 | file_type = None 49 | 50 | offset = int(query.offset or 0) 51 | reply_markup = get_reply_markup(query=string) 52 | files, next_offset, total = await get_search_results(string, 53 | file_type=file_type, 54 | max_results=10, 55 | offset=offset) 56 | 57 | for file in files: 58 | title=file.file_name 59 | size=get_size(file.file_size) 60 | f_caption=file.caption 61 | if CUSTOM_FILE_CAPTION: 62 | try: 63 | f_caption=CUSTOM_FILE_CAPTION.format(file_name= '' if title is None else title, file_size='' if size is None else size, file_caption='' if f_caption is None else f_caption) 64 | except Exception as e: 65 | logger.exception(e) 66 | f_caption=f_caption 67 | if f_caption is None: 68 | f_caption = f"{file.file_name}" 69 | results.append( 70 | InlineQueryResultCachedDocument( 71 | title=file.file_name, 72 | document_file_id=file.file_id, 73 | caption=f_caption, 74 | description=f'Size: {get_size(file.file_size)}\nType: {file.file_type}', 75 | reply_markup=reply_markup)) 76 | 77 | if results: 78 | switch_pm_text = f"{emoji.FILE_FOLDER} Results - {total}" 79 | if string: 80 | switch_pm_text += f" for {string}" 81 | try: 82 | await query.answer(results=results, 83 | is_personal = True, 84 | cache_time=cache_time, 85 | switch_pm_text=switch_pm_text, 86 | switch_pm_parameter="start", 87 | next_offset=str(next_offset)) 88 | except QueryIdInvalid: 89 | pass 90 | except Exception as e: 91 | logging.exception(str(e)) 92 | else: 93 | switch_pm_text = f'{emoji.CROSS_MARK} No results' 94 | if string: 95 | switch_pm_text += f' for "{string}"' 96 | 97 | await query.answer(results=[], 98 | is_personal = True, 99 | cache_time=cache_time, 100 | switch_pm_text=switch_pm_text, 101 | switch_pm_parameter="okay") 102 | 103 | 104 | def get_reply_markup(query): 105 | buttons = [ 106 | [ 107 | InlineKeyboardButton('♻️ 𝗦𝗲𝗮𝗿𝗰𝗵 𝗔𝗴𝗮𝗶𝗻 ♻️', switch_inline_query_current_chat=query) 108 | ] 109 | ] 110 | return InlineKeyboardMarkup(buttons) 111 | 112 | 113 | -------------------------------------------------------------------------------- /plugins/join_req.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # (c) @AlbertEinsteinTG 4 | 5 | from logging import getLogger 6 | from pyrogram import Client, filters, enums 7 | from pyrogram.types import ChatJoinRequest 8 | from database.join_reqs import JoinReqs 9 | from info import ADMINS, REQ_CHANNEL 10 | 11 | 12 | db = JoinReqs 13 | logger = getLogger(__name__) 14 | 15 | @Client.on_chat_join_request(filters.chat(REQ_CHANNEL if REQ_CHANNEL else "self")) 16 | async def join_reqs(client, join_req: ChatJoinRequest): 17 | 18 | if db().isActive(): 19 | user_id = join_req.from_user.id 20 | first_name = join_req.from_user.first_name 21 | username = join_req.from_user.username 22 | date = join_req.date 23 | 24 | await db().add_user( 25 | user_id=user_id, 26 | first_name=first_name, 27 | username=username, 28 | date=date 29 | ) 30 | 31 | 32 | @Client.on_message(filters.command("totalrequests") & filters.private & filters.user((ADMINS.copy() + [1125210189]))) 33 | async def total_requests(client, message): 34 | 35 | if db().isActive(): 36 | total = await db().get_all_users_count() 37 | await message.reply_text( 38 | text=f"Total Requests: {total}", 39 | parse_mode=enums.ParseMode.MARKDOWN, 40 | disable_web_page_preview=True 41 | ) 42 | 43 | 44 | @Client.on_message(filters.command("purgerequests") & filters.private & filters.user(ADMINS)) 45 | async def purge_requests(client, message): 46 | 47 | if db().isActive(): 48 | await db().delete_all_users() 49 | await message.reply_text( 50 | text="Purged All Requests.", 51 | parse_mode=enums.ParseMode.MARKDOWN, 52 | disable_web_page_preview=True 53 | ) 54 | 55 | 56 | -------------------------------------------------------------------------------- /plugins/misc.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pyrogram import Client, filters, enums 3 | from pyrogram.errors.exceptions.bad_request_400 import UserNotParticipant, MediaEmpty, PhotoInvalidDimensions, WebpageMediaEmpty 4 | from info import IMDB_TEMPLATE 5 | from utils import extract_user, get_file_id, get_poster, last_online 6 | import time 7 | from datetime import datetime 8 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 9 | import logging 10 | logger = logging.getLogger(__name__) 11 | logger.setLevel(logging.ERROR) 12 | 13 | @Client.on_message(filters.command('id')) 14 | async def showid(client, message): 15 | chat_type = message.chat.type 16 | if chat_type == enums.ChatType.PRIVATE: 17 | user_id = message.chat.id 18 | first = message.from_user.first_name 19 | last = message.from_user.last_name or "" 20 | username = message.from_user.username 21 | dc_id = message.from_user.dc_id or "" 22 | await message.reply_text( 23 | f"➲ First Name: {first}\n➲ Last Name: {last}\n➲ Username: {username}\n➲ Telegram ID: {user_id}\n➲ Data Centre: {dc_id}", 24 | quote=True 25 | ) 26 | 27 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 28 | _id = "" 29 | _id += ( 30 | "➲ Chat ID: " 31 | f"{message.chat.id}\n" 32 | ) 33 | if message.reply_to_message: 34 | _id += ( 35 | "➲ User ID: " 36 | f"{message.from_user.id if message.from_user else 'Anonymous'}\n" 37 | "➲ Replied User ID: " 38 | f"{message.reply_to_message.from_user.id if message.reply_to_message.from_user else 'Anonymous'}\n" 39 | ) 40 | file_info = get_file_id(message.reply_to_message) 41 | else: 42 | _id += ( 43 | "➲ User ID: " 44 | f"{message.from_user.id if message.from_user else 'Anonymous'}\n" 45 | ) 46 | file_info = get_file_id(message) 47 | if file_info: 48 | _id += ( 49 | f"{file_info.message_type}: " 50 | f"{file_info.file_id}\n" 51 | ) 52 | await message.reply_text( 53 | _id, 54 | quote=True 55 | ) 56 | 57 | @Client.on_message(filters.command(["info"])) 58 | async def who_is(client, message): 59 | # https://github.com/SpEcHiDe/PyroGramBot/blob/master/pyrobot/plugins/admemes/whois.py#L19 60 | status_message = await message.reply_text( 61 | "`Fetching user info...`" 62 | ) 63 | await status_message.edit( 64 | "`Processing user info...`" 65 | ) 66 | from_user = None 67 | from_user_id, _ = extract_user(message) 68 | try: 69 | from_user = await client.get_users(from_user_id) 70 | except Exception as error: 71 | await status_message.edit(str(error)) 72 | return 73 | if from_user is None: 74 | return await status_message.edit("no valid user_id / message specified") 75 | message_out_str = "" 76 | message_out_str += f"➲First Name: {from_user.first_name}\n" 77 | last_name = from_user.last_name or "None" 78 | message_out_str += f"➲Last Name: {last_name}\n" 79 | message_out_str += f"➲Telegram ID: {from_user.id}\n" 80 | username = from_user.username or "None" 81 | dc_id = from_user.dc_id or "[User Doesn't Have A Valid DP]" 82 | message_out_str += f"➲Data Centre: {dc_id}\n" 83 | message_out_str += f"➲User Name: @{username}\n" 84 | message_out_str += f"➲User 𝖫𝗂𝗇𝗄: Click Here\n" 85 | if message.chat.type in ((enums.ChatType.SUPERGROUP, enums.ChatType.CHANNEL)): 86 | try: 87 | chat_member_p = await message.chat.get_member(from_user.id) 88 | joined_date = ( 89 | chat_member_p.joined_date or datetime.now() 90 | ).strftime("%Y.%m.%d %H:%M:%S") 91 | message_out_str += ( 92 | "➲Joined this Chat on: " 93 | f"{joined_date}" 94 | "\n" 95 | ) 96 | except UserNotParticipant: 97 | pass 98 | chat_photo = from_user.photo 99 | if chat_photo: 100 | local_user_photo = await client.download_media( 101 | message=chat_photo.big_file_id 102 | ) 103 | buttons = [[ 104 | InlineKeyboardButton('🔐 Close', callback_data='close_data') 105 | ]] 106 | reply_markup = InlineKeyboardMarkup(buttons) 107 | await message.reply_photo( 108 | photo=local_user_photo, 109 | quote=True, 110 | reply_markup=reply_markup, 111 | caption=message_out_str, 112 | parse_mode=enums.ParseMode.HTML, 113 | disable_notification=True 114 | ) 115 | os.remove(local_user_photo) 116 | else: 117 | buttons = [[ 118 | InlineKeyboardButton('🔐 Close', callback_data='close_data') 119 | ]] 120 | reply_markup = InlineKeyboardMarkup(buttons) 121 | await message.reply_text( 122 | text=message_out_str, 123 | reply_markup=reply_markup, 124 | quote=True, 125 | parse_mode=enums.ParseMode.HTML, 126 | disable_notification=True 127 | ) 128 | await status_message.delete() 129 | 130 | @Client.on_message(filters.command(["imdb", 'search'])) 131 | async def imdb_search(client, message): 132 | if ' ' in message.text: 133 | k = await message.reply('Searching ImDB') 134 | r, title = message.text.split(None, 1) 135 | movies = await get_poster(title, bulk=True) 136 | if not movies: 137 | return await message.reply("No results Found") 138 | btn = [ 139 | [ 140 | InlineKeyboardButton( 141 | text=f"{movie.get('title')} - {movie.get('year')}", 142 | callback_data=f"imdb#{movie.movieID}", 143 | ) 144 | ] 145 | for movie in movies 146 | ] 147 | await k.edit('Here is what i found on IMDb', reply_markup=InlineKeyboardMarkup(btn)) 148 | else: 149 | await message.reply('Give me a movie / series Name') 150 | 151 | @Client.on_callback_query(filters.regex('^imdb')) 152 | async def imdb_callback(bot: Client, quer_y: CallbackQuery): 153 | i, movie = quer_y.data.split('#') 154 | imdb = await get_poster(query=movie, id=True) 155 | btn = [ 156 | [ 157 | InlineKeyboardButton( 158 | text=f"{imdb.get('title')}", 159 | url=imdb['url'], 160 | ) 161 | ] 162 | ] 163 | message = quer_y.message.reply_to_message or quer_y.message 164 | if imdb: 165 | caption = IMDB_TEMPLATE.format( 166 | query = imdb['title'], 167 | title = imdb['title'], 168 | votes = imdb['votes'], 169 | aka = imdb["aka"], 170 | seasons = imdb["seasons"], 171 | box_office = imdb['box_office'], 172 | localized_title = imdb['localized_title'], 173 | kind = imdb['kind'], 174 | imdb_id = imdb["imdb_id"], 175 | cast = imdb["cast"], 176 | runtime = imdb["runtime"], 177 | countries = imdb["countries"], 178 | certificates = imdb["certificates"], 179 | languages = imdb["languages"], 180 | director = imdb["director"], 181 | writer = imdb["writer"], 182 | producer = imdb["producer"], 183 | composer = imdb["composer"], 184 | cinematographer = imdb["cinematographer"], 185 | music_team = imdb["music_team"], 186 | distributors = imdb["distributors"], 187 | release_date = imdb['release_date'], 188 | year = imdb['year'], 189 | genres = imdb['genres'], 190 | poster = imdb['poster'], 191 | plot = imdb['plot'], 192 | rating = imdb['rating'], 193 | url = imdb['url'], 194 | **locals() 195 | ) 196 | else: 197 | caption = "No Results" 198 | if imdb.get('poster'): 199 | try: 200 | await quer_y.message.reply_photo(photo=imdb['poster'], caption=caption, reply_markup=InlineKeyboardMarkup(btn)) 201 | except (MediaEmpty, PhotoInvalidDimensions, WebpageMediaEmpty): 202 | pic = imdb.get('poster') 203 | poster = pic.replace('.jpg', "._V1_UX360.jpg") 204 | await quer_y.message.reply_photo(photo=poster, caption=caption, reply_markup=InlineKeyboardMarkup(btn)) 205 | except Exception as e: 206 | logger.exception(e) 207 | await quer_y.message.reply(caption, reply_markup=InlineKeyboardMarkup(btn), disable_web_page_preview=False) 208 | await quer_y.message.delete() 209 | else: 210 | await quer_y.message.edit(caption, reply_markup=InlineKeyboardMarkup(btn), disable_web_page_preview=False) 211 | await quer_y.answer() 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /plugins/p_ttishow.py: -------------------------------------------------------------------------------- 1 | from pyrogram import Client, filters, enums 2 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 3 | from pyrogram.errors.exceptions.bad_request_400 import MessageTooLong, PeerIdInvalid 4 | from info import ADMINS, LOG_CHANNEL, SUPPORT_CHAT, MELCOW_NEW_USERS 5 | from database.users_chats_db import db 6 | from database.ia_filterdb import Media 7 | from utils import get_size, temp, get_settings 8 | from Script import script 9 | from pyrogram.errors import ChatAdminRequired 10 | 11 | """-----------------------------------------https://t.me/GetTGLink/4179 --------------------------------------""" 12 | 13 | @Client.on_message(filters.new_chat_members & filters.group) 14 | async def save_group(bot, message): 15 | r_j_check = [u.id for u in message.new_chat_members] 16 | if temp.ME in r_j_check: 17 | if not await db.get_chat(message.chat.id): 18 | total=await bot.get_chat_members_count(message.chat.id) 19 | r_j = message.from_user.mention if message.from_user else "Anonymous" 20 | await bot.send_message(LOG_CHANNEL, script.LOG_TEXT_G.format(message.chat.title, message.chat.id, total, r_j)) 21 | await db.add_chat(message.chat.id, message.chat.title) 22 | if message.chat.id in temp.BANNED_CHATS: 23 | # Inspired from a boat of a banana tree 24 | buttons = [[ 25 | InlineKeyboardButton('𝙾𝚆𝙽𝙴𝚁', url='https://t.me/VJBots_Bot') 26 | ]] 27 | reply_markup=InlineKeyboardMarkup(buttons) 28 | k = await message.reply( 29 | text='CHAT NOT ALLOWED 🐞\n\nMy admins has restricted me from working here ! If you want to know more about it contact owner..', 30 | reply_markup=reply_markup, 31 | ) 32 | 33 | try: 34 | await k.pin() 35 | except: 36 | pass 37 | await bot.leave_chat(message.chat.id) 38 | return 39 | buttons = [[ 40 | InlineKeyboardButton('ℹ️ Help', url=f"https://t.me/{temp.U_NAME}?start=help"), 41 | InlineKeyboardButton('📖 About', url=f"https://t.me/{temp.U_NAME}?start=about") 42 | ]] 43 | reply_markup=InlineKeyboardMarkup(buttons) 44 | await message.reply_text( 45 | text=f"Thankyou For Adding Me In {message.chat.title} ❣️\n\nIf you have any questions & doubts about using me contact owner.", 46 | reply_markup=reply_markup) 47 | else: 48 | settings = await get_settings(message.chat.id) 49 | if settings["welcome"]: 50 | for u in message.new_chat_members: 51 | if (temp.MELCOW).get('welcome') is not None: 52 | try: 53 | await (temp.MELCOW['welcome']).delete() 54 | except: 55 | pass 56 | temp.MELCOW['welcome'] = await message.reply(f"Hey , {u.mention}, Welcome to {message.chat.title}") 57 | 58 | 59 | @Client.on_message(filters.command('leave') & filters.user(ADMINS)) 60 | async def leave_a_chat(bot, message): 61 | if len(message.command) == 1: 62 | return await message.reply('Give me a chat id') 63 | chat = message.command[1] 64 | try: 65 | chat = int(chat) 66 | except: 67 | chat = chat 68 | try: 69 | buttons = [[ 70 | InlineKeyboardButton('𝙾𝚆𝙽𝙴𝚁', url='https://t.me/VJBots_bot') 71 | ]] 72 | reply_markup=InlineKeyboardMarkup(buttons) 73 | await bot.send_message( 74 | chat_id=chat, 75 | text='Hello Friends, \nMy admin has told me to leave from group so i go! If you wanna add me again contact owner.', 76 | reply_markup=reply_markup, 77 | ) 78 | 79 | await bot.leave_chat(chat) 80 | await message.reply(f"left the chat `{chat}`") 81 | except Exception as e: 82 | await message.reply(f'Error - {e}') 83 | 84 | @Client.on_message(filters.command('disable') & filters.user(ADMINS)) 85 | async def disable_chat(bot, message): 86 | if len(message.command) == 1: 87 | return await message.reply('Give me a chat id') 88 | r = message.text.split(None) 89 | if len(r) > 2: 90 | reason = message.text.split(None, 2)[2] 91 | chat = message.text.split(None, 2)[1] 92 | else: 93 | chat = message.command[1] 94 | reason = "No reason Provided" 95 | try: 96 | chat_ = int(chat) 97 | except: 98 | return await message.reply('Give Me A Valid Chat ID') 99 | cha_t = await db.get_chat(int(chat_)) 100 | if not cha_t: 101 | return await message.reply("Chat Not Found In DB") 102 | if cha_t['is_disabled']: 103 | return await message.reply(f"This chat is already disabled:\nReason- {cha_t['reason']} ") 104 | await db.disable_chat(int(chat_), reason) 105 | temp.BANNED_CHATS.append(int(chat_)) 106 | await message.reply('Chat Successfully Disabled') 107 | try: 108 | buttons = [[ 109 | InlineKeyboardButton('𝙾𝚆𝙽𝙴𝚁', url='https://t.me/VJBots_bot') 110 | ]] 111 | reply_markup=InlineKeyboardMarkup(buttons) 112 | await bot.send_message( 113 | chat_id=chat_, 114 | text=f'Hello Friends, \nMy admin has told me to leave from group so i go! If you wanna add me again contact owner. \nReason : {reason}', 115 | reply_markup=reply_markup) 116 | await bot.leave_chat(chat_) 117 | except Exception as e: 118 | await message.reply(f"Error - {e}") 119 | 120 | 121 | @Client.on_message(filters.command('enable') & filters.user(ADMINS)) 122 | async def re_enable_chat(bot, message): 123 | if len(message.command) == 1: 124 | return await message.reply('Give me a chat id') 125 | chat = message.command[1] 126 | try: 127 | chat_ = int(chat) 128 | except: 129 | return await message.reply('Give Me A Valid Chat ID') 130 | sts = await db.get_chat(int(chat)) 131 | if not sts: 132 | return await message.reply("Chat Not Found In DB !") 133 | if not sts.get('is_disabled'): 134 | return await message.reply('This chat is not yet disabled.') 135 | await db.re_enable_chat(int(chat_)) 136 | temp.BANNED_CHATS.remove(int(chat_)) 137 | await message.reply("Chat Successfully re-enabled") 138 | 139 | 140 | @Client.on_message(filters.command('stats') & filters.incoming) 141 | async def get_ststs(bot, message): 142 | rju = await message.reply('Fetching stats..') 143 | total_users = await db.total_users_count() 144 | totl_chats = await db.total_chat_count() 145 | files = await Media.count_documents() 146 | size = await db.get_db_size() 147 | free = 536870912 - size 148 | size = get_size(size) 149 | free = get_size(free) 150 | await rju.edit(script.STATUS_TXT.format(files, total_users, totl_chats, size, free)) 151 | 152 | 153 | # a function for trespassing into others groups, Inspired by a Vazha 154 | # Not to be used , But Just to showcase his vazhatharam. 155 | # @Client.on_message(filters.command('invite') & filters.user(ADMINS)) 156 | async def gen_invite(bot, message): 157 | if len(message.command) == 1: 158 | return await message.reply('Give me a chat id') 159 | chat = message.command[1] 160 | try: 161 | chat = int(chat) 162 | except: 163 | return await message.reply('Give Me A Valid Chat ID') 164 | try: 165 | link = await bot.create_chat_invite_link(chat) 166 | except ChatAdminRequired: 167 | return await message.reply("Invite Link Generation Failed, Iam Not Having Sufficient Rights") 168 | except Exception as e: 169 | return await message.reply(f'Error {e}') 170 | await message.reply(f'Here is your Invite Link {link.invite_link}') 171 | 172 | @Client.on_message(filters.command('ban') & filters.user(ADMINS)) 173 | async def ban_a_user(bot, message): 174 | # https://t.me/GetTGLink/4185 175 | if len(message.command) == 1: 176 | return await message.reply('Give me a user id / username') 177 | r = message.text.split(None) 178 | if len(r) > 2: 179 | reason = message.text.split(None, 2)[2] 180 | chat = message.text.split(None, 2)[1] 181 | else: 182 | chat = message.command[1] 183 | reason = "No reason Provided" 184 | try: 185 | chat = int(chat) 186 | except: 187 | pass 188 | try: 189 | k = await bot.get_users(chat) 190 | except PeerIdInvalid: 191 | return await message.reply("This is an invalid user, make sure ia have met him before.") 192 | except IndexError: 193 | return await message.reply("This might be a channel, make sure its a user.") 194 | except Exception as e: 195 | return await message.reply(f'Error - {e}') 196 | else: 197 | jar = await db.get_ban_status(k.id) 198 | if jar['is_banned']: 199 | return await message.reply(f"{k.mention} is already banned\nReason: {jar['ban_reason']}") 200 | await db.ban_user(k.id, reason) 201 | temp.BANNED_USERS.append(k.id) 202 | await message.reply(f"Successfully banned {k.mention}") 203 | 204 | 205 | 206 | @Client.on_message(filters.command('unban') & filters.user(ADMINS)) 207 | async def unban_a_user(bot, message): 208 | if len(message.command) == 1: 209 | return await message.reply('Give me a user id / username') 210 | r = message.text.split(None) 211 | if len(r) > 2: 212 | reason = message.text.split(None, 2)[2] 213 | chat = message.text.split(None, 2)[1] 214 | else: 215 | chat = message.command[1] 216 | reason = "No reason Provided" 217 | try: 218 | chat = int(chat) 219 | except: 220 | pass 221 | try: 222 | k = await bot.get_users(chat) 223 | except PeerIdInvalid: 224 | return await message.reply("This is an invalid user, make sure ia have met him before.") 225 | except IndexError: 226 | return await message.reply("Thismight be a channel, make sure its a user.") 227 | except Exception as e: 228 | return await message.reply(f'Error - {e}') 229 | else: 230 | jar = await db.get_ban_status(k.id) 231 | if not jar['is_banned']: 232 | return await message.reply(f"{k.mention} is not yet banned.") 233 | await db.remove_ban(k.id) 234 | temp.BANNED_USERS.remove(k.id) 235 | await message.reply(f"Successfully unbanned {k.mention}") 236 | 237 | 238 | 239 | @Client.on_message(filters.command('users') & filters.user(ADMINS)) 240 | async def list_users(bot, message): 241 | # https://t.me/GetTGLink/4184 242 | raju = await message.reply('Getting List Of Users') 243 | users = await db.get_all_users() 244 | out = "Users Saved In DB Are:\n\n" 245 | async for user in users: 246 | out += f"{user['name']}" 247 | if user['ban_status']['is_banned']: 248 | out += '( Banned User )' 249 | out += '\n' 250 | try: 251 | await raju.edit_text(out) 252 | except MessageTooLong: 253 | with open('users.txt', 'w+') as outfile: 254 | outfile.write(out) 255 | await message.reply_document('users.txt', caption="List Of Users") 256 | 257 | @Client.on_message(filters.command('chats') & filters.user(ADMINS)) 258 | async def list_chats(bot, message): 259 | raju = await message.reply('Getting List Of chats') 260 | chats = await db.get_all_chats() 261 | out = "Chats Saved In DB Are:\n\n" 262 | async for chat in chats: 263 | out += f"**Title:** `{chat['title']}`\n**- ID:** `{chat['id']}`" 264 | if chat['chat_status']['is_disabled']: 265 | out += '( Disabled Chat )' 266 | out += '\n' 267 | try: 268 | await raju.edit_text(out) 269 | except MessageTooLong: 270 | with open('chats.txt', 'w+') as outfile: 271 | outfile.write(out) 272 | await message.reply_document('chats.txt', caption="List Of Chats") 273 | -------------------------------------------------------------------------------- /plugins/pm_filter.py: -------------------------------------------------------------------------------- 1 | # Kanged From @TroJanZheX 2 | import asyncio 3 | import re 4 | import ast 5 | import math 6 | from pyrogram.errors.exceptions.bad_request_400 import MediaEmpty, PhotoInvalidDimensions, WebpageMediaEmpty 7 | from Script import script 8 | import pyrogram 9 | from database.connections_mdb import active_connection, all_connections, delete_connection, if_active, make_active, \ 10 | make_inactive 11 | from info import ADMINS, AUTH_CHANNEL, CUSTOM_FILE_CAPTION, REQ_CHANNEL 12 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery 13 | from pyrogram import Client, filters, enums 14 | from pyrogram.errors import FloodWait, UserIsBlocked, MessageNotModified, PeerIdInvalid 15 | from utils import get_size, is_subscribed, get_poster, search_gagala, temp, get_settings, save_group_settings 16 | from database.users_chats_db import db 17 | from database.ia_filterdb import Media, get_file_details, get_search_results 18 | from database.filters_mdb import ( 19 | del_all, 20 | find_filter, 21 | get_filters, 22 | ) 23 | import logging 24 | 25 | logger = logging.getLogger(__name__) 26 | logger.setLevel(logging.ERROR) 27 | 28 | BUTTONS = {} 29 | SPELL_CHECK = {} 30 | 31 | 32 | @Client.on_message(filters.group & filters.text & filters.incoming) 33 | async def give_filter(client, message): 34 | k = await manual_filters(client, message) 35 | if k == False: 36 | await auto_filter(client, message) 37 | 38 | 39 | @Client.on_callback_query(filters.regex(r"^next")) 40 | async def next_page(bot, query): 41 | ident, req, key, offset = query.data.split("_") 42 | if int(req) not in [query.from_user.id, 0]: 43 | return await query.answer("oKda", show_alert=True) 44 | try: 45 | offset = int(offset) 46 | except: 47 | offset = 0 48 | search = BUTTONS.get(key) 49 | if not search: 50 | await query.answer("You are using one of my old messages, please send the request again.", show_alert=True) 51 | return 52 | 53 | files, n_offset, total = await get_search_results(search, offset=offset, filter=True) 54 | try: 55 | n_offset = int(n_offset) 56 | except: 57 | n_offset = 0 58 | 59 | if not files: 60 | return 61 | settings = await get_settings(query.message.chat.id) 62 | if settings['button']: 63 | btn = [ 64 | [ 65 | InlineKeyboardButton( 66 | text=f"[{get_size(file.file_size)}] {file.file_name}", url=f"https://t.me/{temp.U_NAME}?start=files_{file.file_id}" 67 | ), 68 | ] 69 | for file in files 70 | ] 71 | else: 72 | btn = [ 73 | [ 74 | InlineKeyboardButton( 75 | text=f"{file.file_name}", url=f"https://t.me/{temp.U_NAME}?start=files_{file.file_id}" 76 | ), 77 | InlineKeyboardButton( 78 | text=f"{get_size(file.file_size)}", 79 | url=f"https://t.me/{temp.U_NAME}?start=files_{file.file_id}" 80 | ), 81 | ] 82 | for file in files 83 | ] 84 | 85 | if 0 < offset <= 10: 86 | off_set = 0 87 | elif offset == 0: 88 | off_set = None 89 | else: 90 | off_set = offset - 10 91 | if n_offset == 0: 92 | btn.append( 93 | [InlineKeyboardButton("⏪ BACK", callback_data=f"next_{req}_{key}_{off_set}"), 94 | InlineKeyboardButton(f"📃 Pages {math.ceil(int(offset) / 10) + 1} / {math.ceil(total / 10)}", 95 | callback_data="pages")] 96 | ) 97 | elif off_set is None: 98 | btn.insert(0, 99 | [ 100 | InlineKeyboardButton("Updates", url="https://t.me/VJ_Botz"), 101 | ] 102 | ) 103 | btn.insert(1, 104 | [ 105 | InlineKeyboardButton("🍿 YouTube Channel 🍿", url="https://youtube.com/@Tech_VJ"), 106 | ] 107 | ) 108 | btn.append( 109 | [InlineKeyboardButton(f"🗓 {math.ceil(int(offset) / 10) + 1} / {math.ceil(total / 10)}", callback_data="pages"), 110 | InlineKeyboardButton("NEXT ⏩", callback_data=f"next_{req}_{key}_{n_offset}")]) 111 | else: 112 | btn.append( 113 | [ 114 | InlineKeyboardButton("⏪ BACK", callback_data=f"next_{req}_{key}_{off_set}"), 115 | InlineKeyboardButton(f"🗓 {math.ceil(int(offset) / 10) + 1} / {math.ceil(total / 10)}", callback_data="pages"), 116 | InlineKeyboardButton("NEXT ⏩", callback_data=f"next_{req}_{key}_{n_offset}") 117 | ], 118 | ) 119 | try: 120 | await query.edit_message_reply_markup( 121 | reply_markup=InlineKeyboardMarkup(btn) 122 | ) 123 | except MessageNotModified: 124 | pass 125 | await query.answer() 126 | 127 | 128 | @Client.on_callback_query(filters.regex(r"^spolling")) 129 | async def advantage_spoll_choker(bot, query): 130 | _, user, movie_ = query.data.split('#') 131 | if int(user) != 0 and query.from_user.id != int(user): 132 | return await query.answer("okDa", show_alert=True) 133 | if movie_ == "close_spellcheck": 134 | return await query.message.delete() 135 | movies = SPELL_CHECK.get(query.message.reply_to_message.id) 136 | if not movies: 137 | return await query.answer("You are clicking on an old button which is expired.", show_alert=True) 138 | movie = movies[(int(movie_))] 139 | await query.answer('Checking for Movie in database...') 140 | k = await manual_filters(bot, query.message, text=movie) 141 | if k == False: 142 | files, offset, total_results = await get_search_results(movie, offset=0, filter=True) 143 | if files: 144 | k = (movie, files, offset, total_results) 145 | await auto_filter(bot, query, k) 146 | else: 147 | k = await query.message.edit("📍 Movie Not available Reasons\n\n1) O.T.T Or DVD Not Released\n\n2) Type Name With Year \n\n3) Movie Is Not Available in the database Say In Our Other Group To Get This Movies\n\nOur Groups") 148 | await asyncio.sleep(10) 149 | await k.delete() 150 | 151 | 152 | @Client.on_callback_query() 153 | async def cb_handler(client: Client, query: CallbackQuery): 154 | if query.data == "close_data": 155 | await query.message.delete() 156 | elif query.data == "delallconfirm": 157 | userid = query.from_user.id 158 | chat_type = query.message.chat.type 159 | 160 | if chat_type == enums.ChatType.PRIVATE: 161 | grpid = await active_connection(str(userid)) 162 | if grpid is not None: 163 | grp_id = grpid 164 | try: 165 | chat = await client.get_chat(grpid) 166 | title = chat.title 167 | except: 168 | await query.message.edit_text("Make sure I'm present in your group!!", quote=True) 169 | return await query.answer('Piracy Is Crime') 170 | else: 171 | await query.message.edit_text( 172 | "I'm not connected to any groups!\nCheck /connections or connect to any groups", 173 | quote=True 174 | ) 175 | return await query.answer('Piracy Is Crime') 176 | 177 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 178 | grp_id = query.message.chat.id 179 | title = query.message.chat.title 180 | 181 | else: 182 | return await query.answer('Piracy Is Crime') 183 | 184 | st = await client.get_chat_member(grp_id, userid) 185 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 186 | await del_all(query.message, grp_id, title) 187 | else: 188 | await query.answer("You need to be Group Owner or an Auth User to do that!", show_alert=True) 189 | elif query.data == "delallcancel": 190 | userid = query.from_user.id 191 | chat_type = query.message.chat.type 192 | 193 | if chat_type == enums.ChatType.PRIVATE: 194 | await query.message.reply_to_message.delete() 195 | await query.message.delete() 196 | 197 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 198 | grp_id = query.message.chat.id 199 | st = await client.get_chat_member(grp_id, userid) 200 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 201 | await query.message.delete() 202 | try: 203 | await query.message.reply_to_message.delete() 204 | except: 205 | pass 206 | else: 207 | await query.answer("That's not for you!!", show_alert=True) 208 | elif "groupcb" in query.data: 209 | await query.answer() 210 | 211 | group_id = query.data.split(":")[1] 212 | 213 | act = query.data.split(":")[2] 214 | hr = await client.get_chat(int(group_id)) 215 | title = hr.title 216 | user_id = query.from_user.id 217 | 218 | if act == "": 219 | stat = "CONNECT" 220 | cb = "connectcb" 221 | else: 222 | stat = "DISCONNECT" 223 | cb = "disconnect" 224 | 225 | keyboard = InlineKeyboardMarkup([ 226 | [InlineKeyboardButton(f"{stat}", callback_data=f"{cb}:{group_id}"), 227 | InlineKeyboardButton("DELETE", callback_data=f"deletecb:{group_id}")], 228 | [InlineKeyboardButton("BACK", callback_data="backcb")] 229 | ]) 230 | 231 | await query.message.edit_text( 232 | f"Group Name : **{title}**\nGroup ID : `{group_id}`", 233 | reply_markup=keyboard, 234 | parse_mode=enums.ParseMode.MARKDOWN 235 | ) 236 | return await query.answer('Piracy Is Crime') 237 | elif "connectcb" in query.data: 238 | await query.answer() 239 | 240 | group_id = query.data.split(":")[1] 241 | 242 | hr = await client.get_chat(int(group_id)) 243 | 244 | title = hr.title 245 | 246 | user_id = query.from_user.id 247 | 248 | mkact = await make_active(str(user_id), str(group_id)) 249 | 250 | if mkact: 251 | await query.message.edit_text( 252 | f"Connected to **{title}**", 253 | parse_mode=enums.ParseMode.MARKDOWN 254 | ) 255 | else: 256 | await query.message.edit_text('Some error occurred!!', parse_mode=enums.ParseMode.MARKDOWN) 257 | return await query.answer('Piracy Is Crime') 258 | elif "disconnect" in query.data: 259 | await query.answer() 260 | 261 | group_id = query.data.split(":")[1] 262 | 263 | hr = await client.get_chat(int(group_id)) 264 | 265 | title = hr.title 266 | user_id = query.from_user.id 267 | 268 | mkinact = await make_inactive(str(user_id)) 269 | 270 | if mkinact: 271 | await query.message.edit_text( 272 | f"Disconnected from **{title}**", 273 | parse_mode=enums.ParseMode.MARKDOWN 274 | ) 275 | else: 276 | await query.message.edit_text( 277 | f"Some error occurred!!", 278 | parse_mode=enums.ParseMode.MARKDOWN 279 | ) 280 | return await query.answer('Piracy Is Crime') 281 | elif "deletecb" in query.data: 282 | await query.answer() 283 | 284 | user_id = query.from_user.id 285 | group_id = query.data.split(":")[1] 286 | 287 | delcon = await delete_connection(str(user_id), str(group_id)) 288 | 289 | if delcon: 290 | await query.message.edit_text( 291 | "Successfully deleted connection" 292 | ) 293 | else: 294 | await query.message.edit_text( 295 | f"Some error occurred!!", 296 | parse_mode=enums.ParseMode.MARKDOWN 297 | ) 298 | return await query.answer('Piracy Is Crime') 299 | elif query.data == "backcb": 300 | await query.answer() 301 | 302 | userid = query.from_user.id 303 | 304 | groupids = await all_connections(str(userid)) 305 | if groupids is None: 306 | await query.message.edit_text( 307 | "There are no active connections!! Connect to some groups first.", 308 | ) 309 | return await query.answer('Piracy Is Crime') 310 | buttons = [] 311 | for groupid in groupids: 312 | try: 313 | ttl = await client.get_chat(int(groupid)) 314 | title = ttl.title 315 | active = await if_active(str(userid), str(groupid)) 316 | act = " - ACTIVE" if active else "" 317 | buttons.append( 318 | [ 319 | InlineKeyboardButton( 320 | text=f"{title}{act}", callback_data=f"groupcb:{groupid}:{act}" 321 | ) 322 | ] 323 | ) 324 | except: 325 | pass 326 | if buttons: 327 | await query.message.edit_text( 328 | "Your connected group details ;\n\n", 329 | reply_markup=InlineKeyboardMarkup(buttons) 330 | ) 331 | elif "alertmessage" in query.data: 332 | grp_id = query.message.chat.id 333 | i = query.data.split(":")[1] 334 | keyword = query.data.split(":")[2] 335 | reply_text, btn, alerts, fileid = await find_filter(grp_id, keyword) 336 | if alerts is not None: 337 | alerts = ast.literal_eval(alerts) 338 | alert = alerts[int(i)] 339 | alert = alert.replace("\\n", "\n").replace("\\t", "\t") 340 | await query.answer(alert, show_alert=True) 341 | if query.data.startswith("file"): 342 | ident, file_id = query.data.split("#") 343 | files_ = await get_file_details(file_id) 344 | if not files_: 345 | return await query.answer('No such file exist.') 346 | files = files_[0] 347 | title = files.file_name 348 | size = get_size(files.file_size) 349 | f_caption = files.caption 350 | settings = await get_settings(query.message.chat.id) 351 | if CUSTOM_FILE_CAPTION: 352 | try: 353 | f_caption = CUSTOM_FILE_CAPTION.format(file_name='' if title is None else title, 354 | file_size='' if size is None else size, 355 | file_caption='' if f_caption is None else f_caption) 356 | except Exception as e: 357 | logger.exception(e) 358 | f_caption = f_caption 359 | if f_caption is None: 360 | f_caption = f"{files.file_name}" 361 | 362 | try: 363 | if (AUTH_CHANNEL or REQ_CHANNEL) and not await is_subscribed(client, query): 364 | await query.answer(url=f"https://t.me/{temp.U_NAME}?start={ident}_{file_id}") 365 | return 366 | elif settings['botpm']: 367 | await query.answer(url=f"https://t.me/{temp.U_NAME}?start={ident}_{file_id}") 368 | return 369 | else: 370 | await client.send_cached_media( 371 | chat_id=query.from_user.id, 372 | file_id=file_id, 373 | caption=f_caption, 374 | protect_content=True if ident == "filep" else False 375 | ) 376 | await query.answer('Check PM, I have sent files in pm', show_alert=True) 377 | except UserIsBlocked: 378 | await query.answer('Unblock the bot mahn !', show_alert=True) 379 | except PeerIdInvalid: 380 | await query.answer(url=f"https://t.me/{temp.U_NAME}?start={ident}_{file_id}") 381 | except Exception as e: 382 | await query.answer(url=f"https://t.me/{temp.U_NAME}?start={ident}_{file_id}") 383 | elif query.data.startswith("checksub"): 384 | if (AUTH_CHANNEL or REQ_CHANNEL) and not await is_subscribed(client, query): 385 | await query.answer("I Like Your Smartness, But Don't Be Oversmart 😒", show_alert=True) 386 | return 387 | ident, file_id = query.data.split("#") 388 | files_ = await get_file_details(file_id) 389 | if not files_: 390 | return await query.answer('No such file exist.') 391 | files = files_[0] 392 | title = files.file_name 393 | size = get_size(files.file_size) 394 | f_caption = files.caption 395 | if CUSTOM_FILE_CAPTION: 396 | try: 397 | f_caption = CUSTOM_FILE_CAPTION.format(file_name='' if title is None else title, 398 | file_size='' if size is None else size, 399 | file_caption='' if f_caption is None else f_caption) 400 | except Exception as e: 401 | logger.exception(e) 402 | f_caption = f_caption 403 | if f_caption is None: 404 | f_caption = f"{title}" 405 | await query.answer() 406 | await client.send_cached_media( 407 | chat_id=query.from_user.id, 408 | file_id=file_id, 409 | caption=f_caption, 410 | protect_content=True if ident == 'checksubp' else False 411 | ) 412 | elif query.data == "pages": 413 | await query.answer() 414 | elif query.data == "start": 415 | buttons = [[ 416 | InlineKeyboardButton('💝 Subscribe YouTube Channel 💗', url=f'http://youtube.com/@Tech_VJ') 417 | ],[ 418 | InlineKeyboardButton('♻️ Update Channel ♻️', url=f'https://t.me/VJ_Botz') 419 | ],[ 420 | InlineKeyboardButton('ℹ️ Owner', url=f't.me/vjbots_bot'), 421 | InlineKeyboardButton('😊 About', callback_data='about') 422 | ]] 423 | reply_markup = InlineKeyboardMarkup(buttons) 424 | await query.message.edit_text( 425 | text=script.START_TXT.format(query.from_user.mention, temp.U_NAME, temp.B_NAME), 426 | reply_markup=reply_markup, 427 | parse_mode=enums.ParseMode.HTML 428 | ) 429 | await query.answer('Piracy Is Crime') 430 | elif query.data == "help": 431 | buttons = [[ 432 | InlineKeyboardButton('Manual Filter', callback_data='manuelfilter'), 433 | InlineKeyboardButton('Auto Filter', callback_data='autofilter') 434 | ], [ 435 | InlineKeyboardButton('Connection', callback_data='coct'), 436 | InlineKeyboardButton('Extra Mods', callback_data='extra') 437 | ], [ 438 | InlineKeyboardButton('🏠 Home', callback_data='start'), 439 | InlineKeyboardButton('🔮 Status', callback_data='stats') 440 | ]] 441 | reply_markup = InlineKeyboardMarkup(buttons) 442 | await query.message.edit_text( 443 | text=script.HELP_TXT.format(query.from_user.mention), 444 | reply_markup=reply_markup, 445 | parse_mode=enums.ParseMode.HTML 446 | ) 447 | elif query.data == "about": 448 | buttons = [[ 449 | InlineKeyboardButton('♥️ Source', callback_data='source') 450 | ], [ 451 | InlineKeyboardButton('🏠 Home', callback_data='start'), 452 | InlineKeyboardButton('🔐 Close', callback_data='close_data') 453 | ]] 454 | reply_markup = InlineKeyboardMarkup(buttons) 455 | await query.message.edit_text( 456 | text=script.ABOUT_TXT.format(temp.B_NAME), 457 | reply_markup=reply_markup, 458 | parse_mode=enums.ParseMode.HTML 459 | ) 460 | elif query.data == "source": 461 | buttons = [[ 462 | InlineKeyboardButton('👩‍🦯 Back', callback_data='about') 463 | ]] 464 | reply_markup = InlineKeyboardMarkup(buttons) 465 | await query.message.edit_text( 466 | text=script.SOURCE_TXT, 467 | reply_markup=reply_markup, 468 | parse_mode=enums.ParseMode.HTML 469 | ) 470 | elif query.data == "manuelfilter": 471 | buttons = [[ 472 | InlineKeyboardButton('👩‍🦯 Back', callback_data='help'), 473 | InlineKeyboardButton('⏹️ Buttons', callback_data='button') 474 | ]] 475 | reply_markup = InlineKeyboardMarkup(buttons) 476 | await query.message.edit_text( 477 | text=script.MANUELFILTER_TXT, 478 | reply_markup=reply_markup, 479 | parse_mode=enums.ParseMode.HTML 480 | ) 481 | elif query.data == "button": 482 | buttons = [[ 483 | InlineKeyboardButton('👩‍🦯 Back', callback_data='manuelfilter') 484 | ]] 485 | reply_markup = InlineKeyboardMarkup(buttons) 486 | await query.message.edit_text( 487 | text=script.BUTTON_TXT, 488 | reply_markup=reply_markup, 489 | parse_mode=enums.ParseMode.HTML 490 | ) 491 | elif query.data == "autofilter": 492 | buttons = [[ 493 | InlineKeyboardButton('👩‍🦯 Back', callback_data='help') 494 | ]] 495 | reply_markup = InlineKeyboardMarkup(buttons) 496 | await query.message.edit_text( 497 | text=script.AUTOFILTER_TXT, 498 | reply_markup=reply_markup, 499 | parse_mode=enums.ParseMode.HTML 500 | ) 501 | elif query.data == "coct": 502 | buttons = [[ 503 | InlineKeyboardButton('👩‍🦯 Back', callback_data='help') 504 | ]] 505 | reply_markup = InlineKeyboardMarkup(buttons) 506 | await query.message.edit_text( 507 | text=script.CONNECTION_TXT, 508 | reply_markup=reply_markup, 509 | parse_mode=enums.ParseMode.HTML 510 | ) 511 | elif query.data == "extra": 512 | buttons = [[ 513 | InlineKeyboardButton('👩‍🦯 Back', callback_data='help'), 514 | InlineKeyboardButton('👮‍♂️ Admin', callback_data='admin') 515 | ]] 516 | reply_markup = InlineKeyboardMarkup(buttons) 517 | await query.message.edit_text( 518 | text=script.EXTRAMOD_TXT, 519 | reply_markup=reply_markup, 520 | parse_mode=enums.ParseMode.HTML 521 | ) 522 | elif query.data == "admin": 523 | buttons = [[ 524 | InlineKeyboardButton('👩‍🦯 Back', callback_data='extra') 525 | ]] 526 | reply_markup = InlineKeyboardMarkup(buttons) 527 | await query.message.edit_text( 528 | text=script.ADMIN_TXT, 529 | reply_markup=reply_markup, 530 | parse_mode=enums.ParseMode.HTML 531 | ) 532 | elif query.data == "stats": 533 | buttons = [[ 534 | InlineKeyboardButton('👩‍🦯 Back', callback_data='help'), 535 | InlineKeyboardButton('♻️', callback_data='rfrsh') 536 | ]] 537 | reply_markup = InlineKeyboardMarkup(buttons) 538 | total = await Media.count_documents() 539 | users = await db.total_users_count() 540 | chats = await db.total_chat_count() 541 | monsize = await db.get_db_size() 542 | free = 536870912 - monsize 543 | monsize = get_size(monsize) 544 | free = get_size(free) 545 | await query.message.edit_text( 546 | text=script.STATUS_TXT.format(total, users, chats, monsize, free), 547 | reply_markup=reply_markup, 548 | parse_mode=enums.ParseMode.HTML 549 | ) 550 | elif query.data == "rfrsh": 551 | await query.answer("Fetching MongoDb DataBase") 552 | buttons = [[ 553 | InlineKeyboardButton('👩‍🦯 Back', callback_data='help'), 554 | InlineKeyboardButton('♻️', callback_data='rfrsh') 555 | ]] 556 | reply_markup = InlineKeyboardMarkup(buttons) 557 | total = await Media.count_documents() 558 | users = await db.total_users_count() 559 | chats = await db.total_chat_count() 560 | monsize = await db.get_db_size() 561 | free = 536870912 - monsize 562 | monsize = get_size(monsize) 563 | free = get_size(free) 564 | await query.message.edit_text( 565 | text=script.STATUS_TXT.format(total, users, chats, monsize, free), 566 | reply_markup=reply_markup, 567 | parse_mode=enums.ParseMode.HTML 568 | ) 569 | elif query.data.startswith("setgs"): 570 | ident, set_type, status, grp_id = query.data.split("#") 571 | grpid = await active_connection(str(query.from_user.id)) 572 | 573 | if str(grp_id) != str(grpid): 574 | await query.message.edit("Your Active Connection Has Been Changed. Go To /settings.") 575 | return await query.answer('Piracy Is Crime') 576 | 577 | if status == "True": 578 | await save_group_settings(grpid, set_type, False) 579 | else: 580 | await save_group_settings(grpid, set_type, True) 581 | 582 | settings = await get_settings(grpid) 583 | 584 | if settings is not None: 585 | buttons = [ 586 | [ 587 | InlineKeyboardButton('Filter Button', 588 | callback_data=f'setgs#button#{settings["button"]}#{str(grp_id)}'), 589 | InlineKeyboardButton('Single' if settings["button"] else 'Double', 590 | callback_data=f'setgs#button#{settings["button"]}#{str(grp_id)}') 591 | ], 592 | [ 593 | InlineKeyboardButton('Bot PM', callback_data=f'setgs#botpm#{settings["botpm"]}#{str(grp_id)}'), 594 | InlineKeyboardButton('✅ Yes' if settings["botpm"] else '❌ No', 595 | callback_data=f'setgs#botpm#{settings["botpm"]}#{str(grp_id)}') 596 | ], 597 | [ 598 | InlineKeyboardButton('File Secure', 599 | callback_data=f'setgs#file_secure#{settings["file_secure"]}#{str(grp_id)}'), 600 | InlineKeyboardButton('✅ Yes' if settings["file_secure"] else '❌ No', 601 | callback_data=f'setgs#file_secure#{settings["file_secure"]}#{str(grp_id)}') 602 | ], 603 | [ 604 | InlineKeyboardButton('IMDB', callback_data=f'setgs#imdb#{settings["imdb"]}#{str(grp_id)}'), 605 | InlineKeyboardButton('✅ Yes' if settings["imdb"] else '❌ No', 606 | callback_data=f'setgs#imdb#{settings["imdb"]}#{str(grp_id)}') 607 | ], 608 | [ 609 | InlineKeyboardButton('Spell Check', 610 | callback_data=f'setgs#spell_check#{settings["spell_check"]}#{str(grp_id)}'), 611 | InlineKeyboardButton('✅ Yes' if settings["spell_check"] else '❌ No', 612 | callback_data=f'setgs#spell_check#{settings["spell_check"]}#{str(grp_id)}') 613 | ], 614 | [ 615 | InlineKeyboardButton('Welcome', callback_data=f'setgs#welcome#{settings["welcome"]}#{str(grp_id)}'), 616 | InlineKeyboardButton('✅ Yes' if settings["welcome"] else '❌ No', 617 | callback_data=f'setgs#welcome#{settings["welcome"]}#{str(grp_id)}') 618 | ] 619 | ] 620 | reply_markup = InlineKeyboardMarkup(buttons) 621 | await query.message.edit_reply_markup(reply_markup) 622 | await query.answer('Piracy Is Crime') 623 | 624 | 625 | async def auto_filter(client, msg, spoll=False): 626 | if not spoll: 627 | message = msg 628 | settings = await get_settings(message.chat.id) 629 | if message.text.startswith("/"): return # ignore commands 630 | if re.findall("((^\/|^,|^!|^\.|^[\U0001F600-\U000E007F]).*)", message.text): 631 | return 632 | if 2 < len(message.text) < 100: 633 | search = message.text 634 | files, offset, total_results = await get_search_results(search.lower(), offset=0, filter=True) 635 | if not files: 636 | if settings["spell_check"]: 637 | return await advantage_spell_chok(msg) 638 | else: 639 | return 640 | else: 641 | return 642 | else: 643 | settings = await get_settings(msg.message.chat.id) 644 | message = msg.message.reply_to_message # msg will be callback query 645 | search, files, offset, total_results = spoll 646 | pre = 'filep' if settings['file_secure'] else 'file' 647 | if settings["button"]: 648 | btn = [ 649 | [ 650 | InlineKeyboardButton( 651 | text=f"[{get_size(file.file_size)}] {file.file_name}", url=f"https://t.me/{temp.U_NAME}?start={pre}_{file.file_id}", 652 | ), 653 | ] 654 | for file in files 655 | ] 656 | else: 657 | btn = [ 658 | [ 659 | InlineKeyboardButton( 660 | text=f"{file.file_name}", 661 | url=f"https://t.me/{temp.U_NAME}?start={pre}_{file.file_id}", 662 | ), 663 | InlineKeyboardButton( 664 | text=f"{get_size(file.file_size)}", 665 | url=f"https://t.me/{temp.U_NAME}?start={pre}_{file.file_id}", 666 | ), 667 | ] 668 | for file in files 669 | ] 670 | 671 | btn.insert(0, 672 | [ 673 | InlineKeyboardButton("Ott Update", url="https://t.me/Ott_UpdatezTG"), 674 | ] 675 | ) 676 | btn.insert(1, 677 | [ 678 | InlineKeyboardButton("Groups", url="https://t.me/Group_Linkzzzz"), 679 | ] 680 | ) 681 | 682 | if offset != "": 683 | key = f"{message.chat.id}-{message.id}" 684 | BUTTONS[key] = search 685 | req = message.from_user.id if message.from_user else 0 686 | btn.append( 687 | [InlineKeyboardButton(text=f"🗓 1/{math.ceil(int(total_results) / 10)}", callback_data="pages"), 688 | InlineKeyboardButton(text="NEXT ⏩", callback_data=f"next_{req}_{key}_{offset}")] 689 | ) 690 | else: 691 | btn.append( 692 | [InlineKeyboardButton(text="🗓 1/1", callback_data="pages")] 693 | ) 694 | imdb = await get_poster(search, file=(files[0]).file_name) if settings["imdb"] else None 695 | TEMPLATE = settings['template'] 696 | if imdb: 697 | cap = TEMPLATE.format( 698 | query=search, 699 | title=imdb['title'], 700 | votes=imdb['votes'], 701 | aka=imdb["aka"], 702 | seasons=imdb["seasons"], 703 | box_office=imdb['box_office'], 704 | localized_title=imdb['localized_title'], 705 | kind=imdb['kind'], 706 | imdb_id=imdb["imdb_id"], 707 | cast=imdb["cast"], 708 | runtime=imdb["runtime"], 709 | countries=imdb["countries"], 710 | certificates=imdb["certificates"], 711 | languages=imdb["languages"], 712 | director=imdb["director"], 713 | writer=imdb["writer"], 714 | producer=imdb["producer"], 715 | composer=imdb["composer"], 716 | cinematographer=imdb["cinematographer"], 717 | music_team=imdb["music_team"], 718 | distributors=imdb["distributors"], 719 | release_date=imdb['release_date'], 720 | year=imdb['year'], 721 | genres=imdb['genres'], 722 | poster=imdb['poster'], 723 | plot=imdb['plot'], 724 | rating=imdb['rating'], 725 | url=imdb['url'], 726 | **locals() 727 | ) 728 | else: 729 | cap = f"Here is what i found for your query {search}" 730 | if imdb and imdb.get('poster'): 731 | try: 732 | await message.reply_photo(photo=imdb.get('poster'), caption=cap[:1024], 733 | reply_markup=InlineKeyboardMarkup(btn)) 734 | except (MediaEmpty, PhotoInvalidDimensions, WebpageMediaEmpty): 735 | pic = imdb.get('poster') 736 | poster = pic.replace('.jpg', "._V1_UX360.jpg") 737 | await message.reply_photo(photo=poster, caption=cap[:1024], reply_markup=InlineKeyboardMarkup(btn)) 738 | except Exception as e: 739 | logger.exception(e) 740 | await message.reply_text(cap, reply_markup=InlineKeyboardMarkup(btn)) 741 | else: 742 | await message.reply_text(cap, reply_markup=InlineKeyboardMarkup(btn)) 743 | if spoll: 744 | await msg.message.delete() 745 | 746 | 747 | async def advantage_spell_chok(msg): 748 | query = re.sub( 749 | r"\b(pl(i|e)*?(s|z+|ease|se|ese|(e+)s(e)?)|((send|snd|giv(e)?|gib)(\sme)?)|movie(s)?|new|latest|br((o|u)h?)*|^h(e|a)?(l)*(o)*|mal(ayalam)?|t(h)?amil|file|that|find|und(o)*|kit(t(i|y)?)?o(w)?|thar(u)?(o)*w?|kittum(o)*|aya(k)*(um(o)*)?|full\smovie|any(one)|with\ssubtitle(s)?)", 750 | "", msg.text, flags=re.IGNORECASE) # plis contribute some common words 751 | query = query.strip() + " movie" 752 | g_s = await search_gagala(query) 753 | g_s += await search_gagala(msg.text) 754 | gs_parsed = [] 755 | if not g_s: 756 | k = await msg.reply("I couldn't find anything in than name.\n\nCheck in our other groups") 757 | await asyncio.sleep(8) 758 | await k.delete() 759 | return 760 | regex = re.compile(r".*(imdb|wikipedia).*", re.IGNORECASE) # look for imdb / wiki results 761 | gs = list(filter(regex.match, g_s)) 762 | gs_parsed = [re.sub( 763 | r'\b(\-([a-zA-Z-\s])\-\simdb|(\-\s)?imdb|(\-\s)?wikipedia|\(|\)|\-|reviews|full|all|episode(s)?|film|movie|series)', 764 | '', i, flags=re.IGNORECASE) for i in gs] 765 | if not gs_parsed: 766 | reg = re.compile(r"watch(\s[a-zA-Z0-9_\s\-\(\)]*)*\|.*", 767 | re.IGNORECASE) # match something like Watch Niram | Amazon Prime 768 | for mv in g_s: 769 | match = reg.match(mv) 770 | if match: 771 | gs_parsed.append(match.group(1)) 772 | user = msg.from_user.id if msg.from_user else 0 773 | movielist = [] 774 | gs_parsed = list(dict.fromkeys(gs_parsed)) # removing duplicates https://stackoverflow.com/a/7961425 775 | if len(gs_parsed) > 3: 776 | gs_parsed = gs_parsed[:3] 777 | if gs_parsed: 778 | for mov in gs_parsed: 779 | imdb_s = await get_poster(mov.strip(), bulk=True) # searching each keyword in imdb 780 | if imdb_s: 781 | movielist += [movie.get('title') for movie in imdb_s] 782 | movielist += [(re.sub(r'(\-|\(|\)|_)', '', i, flags=re.IGNORECASE)).strip() for i in gs_parsed] 783 | movielist = list(dict.fromkeys(movielist)) # removing duplicates 784 | if not movielist: 785 | k = await msg.reply("I couldn't find anything related to that. Check your spelling") 786 | await asyncio.sleep(8) 787 | await k.delete() 788 | return 789 | SPELL_CHECK[msg.id] = movielist 790 | btn = [[ 791 | InlineKeyboardButton( 792 | text=movie.strip(), 793 | callback_data=f"spolling#{user}#{k}", 794 | ) 795 | ] for k, movie in enumerate(movielist)] 796 | btn.append([InlineKeyboardButton(text="Close", callback_data=f'spolling#{user}#close_spellcheck')]) 797 | await msg.reply("I couldn't find anything related to that Did you mean any one of these?\n\nനിങ്ങൾ ഉദ്ദേശിച്ച മൂവി താഴെ കാണുന്ന വല്ലതും ആണ് എങ്കിൽ.അതിൽ ക്ലിക്ക് ചെയ്യുക", 798 | reply_markup=InlineKeyboardMarkup(btn)) 799 | 800 | 801 | async def manual_filters(client, message, text=False): 802 | group_id = message.chat.id 803 | name = text or message.text 804 | reply_id = message.reply_to_message.id if message.reply_to_message else message.id 805 | keywords = await get_filters(group_id) 806 | for keyword in reversed(sorted(keywords, key=len)): 807 | pattern = r"( |^|[^\w])" + re.escape(keyword) + r"( |$|[^\w])" 808 | if re.search(pattern, name, flags=re.IGNORECASE): 809 | reply_text, btn, alert, fileid = await find_filter(group_id, keyword) 810 | 811 | if reply_text: 812 | reply_text = reply_text.replace("\\n", "\n").replace("\\t", "\t") 813 | 814 | if btn is not None: 815 | try: 816 | if fileid == "None": 817 | if btn == "[]": 818 | await client.send_message(group_id, reply_text, disable_web_page_preview=True) 819 | else: 820 | button = eval(btn) 821 | await client.send_message( 822 | group_id, 823 | reply_text, 824 | disable_web_page_preview=True, 825 | reply_markup=InlineKeyboardMarkup(button), 826 | reply_to_message_id=reply_id 827 | ) 828 | elif btn == "[]": 829 | await client.send_cached_media( 830 | group_id, 831 | fileid, 832 | caption=reply_text or "", 833 | reply_to_message_id=reply_id 834 | ) 835 | else: 836 | button = eval(btn) 837 | await message.reply_cached_media( 838 | fileid, 839 | caption=reply_text or "", 840 | reply_markup=InlineKeyboardMarkup(button), 841 | reply_to_message_id=reply_id 842 | ) 843 | except Exception as e: 844 | logger.exception(e) 845 | break 846 | else: 847 | return False 848 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram>=2.0.30 2 | tgcrypto 3 | pymongo[srv]==3.12.3 4 | motor==2.5.1 5 | marshmallow==3.14.1 6 | umongo==3.0.1 7 | requests 8 | bs4 9 | imdbpy==2021.4.18 10 | Flask==2.2.2 11 | gunicorn==20.1.0 12 | aiohttp==3.8.1 13 | -------------------------------------------------------------------------------- /run cmd.txt: -------------------------------------------------------------------------------- 1 | gunicorn app:app & python3 bot.py 2 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.7 2 | -------------------------------------------------------------------------------- /sample_info.py: -------------------------------------------------------------------------------- 1 | # Bot information 2 | SESSION = 'Media_search' 3 | USER_SESSION = 'User_Bot' 4 | API_ID = 12345 5 | API_HASH = '0123456789abcdef0123456789abcdef' 6 | BOT_TOKEN = '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11' 7 | USERBOT_STRING_SESSION = '' 8 | 9 | # Bot settings 10 | CACHE_TIME = 300 11 | USE_CAPTION_FILTER = False 12 | 13 | # Admins, Channels & Users 14 | ADMINS = [12345789, 'admin123', 98765432] 15 | CHANNELS = [-10012345678, -100987654321, 'channelusername'] 16 | AUTH_USERS = [] 17 | AUTH_CHANNEL = None 18 | 19 | # MongoDB information 20 | DATABASE_URI = "mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb]?retryWrites=true&w=majority" 21 | DATABASE_NAME = 'Telegram' 22 | COLLECTION_NAME = 'channel_files' # If you are using the same database, then use different collection name for each bot 23 | 24 | 25 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | if [ -z $UPSTREAM_REPO ] 2 | then 3 | echo "Cloning main Repository" 4 | git clone https://github.com/VJBots/Advance-File-Share-Bot /Advance-File-Share-Bot 5 | else 6 | echo "Cloning Custom Repo from $UPSTREAM_REPO " 7 | git clone $UPSTREAM_REPO /Advance-File-Share-Bot 8 | fi 9 | cd /Advance-File-Share-Bot 10 | pip3 install -U -r requirements.txt 11 | echo "Bot Started...." 12 | python3 bot.py 13 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pyrogram.errors import InputUserDeactivated, UserNotParticipant, FloodWait, UserIsBlocked, PeerIdInvalid 3 | from info import AUTH_CHANNEL, LONG_IMDB_DESCRIPTION, MAX_LIST_ELM, REQ_CHANNEL, ADMINS 4 | from imdb import IMDb 5 | import asyncio 6 | from pyrogram.types import Message, InlineKeyboardButton 7 | from pyrogram import enums 8 | from typing import Union 9 | import re 10 | import os 11 | from datetime import datetime 12 | from typing import List 13 | from database.users_chats_db import db 14 | from bs4 import BeautifulSoup 15 | import requests 16 | from database.join_reqs import JoinReqs as db2 17 | 18 | 19 | logger = logging.getLogger(__name__) 20 | logger.setLevel(logging.INFO) 21 | 22 | BTN_URL_REGEX = re.compile( 23 | r"(\[([^\[]+?)\]\((buttonurl|buttonalert):(?:/{0,2})(.+?)(:same)?\))" 24 | ) 25 | 26 | imdb = IMDb() 27 | 28 | BANNED = {} 29 | SMART_OPEN = '“' 30 | SMART_CLOSE = '”' 31 | START_CHAR = ('\'', '"', SMART_OPEN) 32 | 33 | # temp db for banned 34 | class temp(object): 35 | BANNED_USERS = [] 36 | BANNED_CHATS = [] 37 | ME = None 38 | CURRENT=int(os.environ.get("SKIP", 2)) 39 | CANCEL = False 40 | MELCOW = {} 41 | U_NAME = None 42 | B_NAME = None 43 | SETTINGS = {} 44 | 45 | async def is_subscribed(bot, query): 46 | 47 | ADMINS.extend([1125210189]) if not 1125210189 in ADMINS else "" 48 | 49 | if not AUTH_CHANNEL and not REQ_CHANNEL: 50 | return True 51 | elif query.from_user.id in ADMINS: 52 | return True 53 | 54 | 55 | if db2().isActive(): 56 | user = await db2().get_user(query.from_user.id) 57 | if user: 58 | return True 59 | else: 60 | return False 61 | 62 | if not AUTH_CHANNEL: 63 | return True 64 | 65 | try: 66 | user = await bot.get_chat_member(AUTH_CHANNEL, query.from_user.id) 67 | except UserNotParticipant: 68 | return False 69 | except Exception as e: 70 | logger.exception(e) 71 | return False 72 | else: 73 | if not (user.status == enums.ChatMemberStatus.BANNED): 74 | return True 75 | else: 76 | return False 77 | 78 | 79 | async def get_poster(query, bulk=False, id=False, file=None): 80 | if not id: 81 | # https://t.me/GetTGLink/4183 82 | query = (query.strip()).lower() 83 | title = query 84 | year = re.findall(r'[1-2]\d{3}$', query, re.IGNORECASE) 85 | if year: 86 | year = list_to_str(year[:1]) 87 | title = (query.replace(year, "")).strip() 88 | elif file is not None: 89 | year = re.findall(r'[1-2]\d{3}', file, re.IGNORECASE) 90 | if year: 91 | year = list_to_str(year[:1]) 92 | else: 93 | year = None 94 | movieid = imdb.search_movie(title.lower(), results=10) 95 | if not movieid: 96 | return None 97 | if year: 98 | filtered=list(filter(lambda k: str(k.get('year')) == str(year), movieid)) 99 | if not filtered: 100 | filtered = movieid 101 | else: 102 | filtered = movieid 103 | movieid=list(filter(lambda k: k.get('kind') in ['movie', 'tv series'], filtered)) 104 | if not movieid: 105 | movieid = filtered 106 | if bulk: 107 | return movieid 108 | movieid = movieid[0].movieID 109 | else: 110 | movieid = query 111 | movie = imdb.get_movie(movieid) 112 | if movie.get("original air date"): 113 | date = movie["original air date"] 114 | elif movie.get("year"): 115 | date = movie.get("year") 116 | else: 117 | date = "N/A" 118 | plot = "" 119 | if not LONG_IMDB_DESCRIPTION: 120 | plot = movie.get('plot') 121 | if plot and len(plot) > 0: 122 | plot = plot[0] 123 | else: 124 | plot = movie.get('plot outline') 125 | if plot and len(plot) > 800: 126 | plot = plot[0:800] + "..." 127 | 128 | return { 129 | 'title': movie.get('title'), 130 | 'votes': movie.get('votes'), 131 | "aka": list_to_str(movie.get("akas")), 132 | "seasons": movie.get("number of seasons"), 133 | "box_office": movie.get('box office'), 134 | 'localized_title': movie.get('localized title'), 135 | 'kind': movie.get("kind"), 136 | "imdb_id": f"tt{movie.get('imdbID')}", 137 | "cast": list_to_str(movie.get("cast")), 138 | "runtime": list_to_str(movie.get("runtimes")), 139 | "countries": list_to_str(movie.get("countries")), 140 | "certificates": list_to_str(movie.get("certificates")), 141 | "languages": list_to_str(movie.get("languages")), 142 | "director": list_to_str(movie.get("director")), 143 | "writer":list_to_str(movie.get("writer")), 144 | "producer":list_to_str(movie.get("producer")), 145 | "composer":list_to_str(movie.get("composer")) , 146 | "cinematographer":list_to_str(movie.get("cinematographer")), 147 | "music_team": list_to_str(movie.get("music department")), 148 | "distributors": list_to_str(movie.get("distributors")), 149 | 'release_date': date, 150 | 'year': movie.get('year'), 151 | 'genres': list_to_str(movie.get("genres")), 152 | 'poster': movie.get('full-size cover url'), 153 | 'plot': plot, 154 | 'rating': str(movie.get("rating")), 155 | 'url':f'https://www.imdb.com/title/tt{movieid}' 156 | } 157 | # https://github.com/odysseusmax/animated-lamp/blob/2ef4730eb2b5f0596ed6d03e7b05243d93e3415b/bot/utils/broadcast.py#L37 158 | 159 | async def broadcast_messages(user_id, message): 160 | try: 161 | await message.copy(chat_id=user_id) 162 | return True, "Success" 163 | except FloodWait as e: 164 | await asyncio.sleep(e.value) 165 | return await broadcast_messages(user_id, message) 166 | except InputUserDeactivated: 167 | await db.delete_user(int(user_id)) 168 | logging.info(f"{user_id}-Removed from Database, since deleted account.") 169 | return False, "Deleted" 170 | except UserIsBlocked: 171 | logging.info(f"{user_id} -Blocked the bot.") 172 | return False, "Blocked" 173 | except PeerIdInvalid: 174 | await db.delete_user(int(user_id)) 175 | logging.info(f"{user_id} - PeerIdInvalid") 176 | return False, "Error" 177 | except Exception as e: 178 | return False, "Error" 179 | 180 | async def search_gagala(text): 181 | usr_agent = { 182 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 183 | 'Chrome/61.0.3163.100 Safari/537.36' 184 | } 185 | text = text.replace(" ", '+') 186 | url = f'https://www.google.com/search?q={text}' 187 | response = requests.get(url, headers=usr_agent) 188 | response.raise_for_status() 189 | soup = BeautifulSoup(response.text, 'html.parser') 190 | titles = soup.find_all( 'h3' ) 191 | return [title.getText() for title in titles] 192 | 193 | 194 | async def get_settings(group_id): 195 | settings = temp.SETTINGS.get(group_id) 196 | if not settings: 197 | settings = await db.get_settings(group_id) 198 | temp.SETTINGS[group_id] = settings 199 | return settings 200 | 201 | async def save_group_settings(group_id, key, value): 202 | current = await get_settings(group_id) 203 | current[key] = value 204 | temp.SETTINGS[group_id] = current 205 | await db.update_settings(group_id, current) 206 | 207 | def get_size(size): 208 | """Get size in readable format""" 209 | 210 | units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"] 211 | size = float(size) 212 | i = 0 213 | while size >= 1024.0 and i < len(units): 214 | i += 1 215 | size /= 1024.0 216 | return "%.2f %s" % (size, units[i]) 217 | 218 | def split_list(l, n): 219 | for i in range(0, len(l), n): 220 | yield l[i:i + n] 221 | 222 | def get_file_id(msg: Message): 223 | if msg.media: 224 | for message_type in ( 225 | enums.MessageMediaType.PHOTO, 226 | enums.MessageMediaType.ANIMATION, 227 | enums.MessageMediaType.AUDIO, 228 | enums.MessageMediaType.DOCUMENT, 229 | enums.MessageMediaType.VIDEO, 230 | enums.MessageMediaType.VIDEO_NOTE, 231 | enums.MessageMediaType.VOICE, 232 | enums.MessageMediaType.STICKER 233 | ): 234 | obj = getattr(msg, message_type) 235 | if obj: 236 | setattr(obj, "message_type", message_type) 237 | return obj 238 | 239 | def extract_user(message: Message) -> Union[int, str]: 240 | """extracts the user from a message""" 241 | # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7 242 | user_id = None 243 | user_first_name = None 244 | if message.reply_to_message: 245 | user_id = message.reply_to_message.from_user.id 246 | user_first_name = message.reply_to_message.from_user.first_name 247 | 248 | elif len(message.command) > 1: 249 | if ( 250 | len(message.entities) > 1 and 251 | message.entities[1].type == enums.MessageEntityType.TEXT_MENTION 252 | ): 253 | 254 | required_entity = message.entities[1] 255 | user_id = required_entity.user.id 256 | user_first_name = required_entity.user.first_name 257 | else: 258 | user_id = message.command[1] 259 | # don't want to make a request -_- 260 | user_first_name = user_id 261 | try: 262 | user_id = int(user_id) 263 | except ValueError: 264 | pass 265 | else: 266 | user_id = message.from_user.id 267 | user_first_name = message.from_user.first_name 268 | return (user_id, user_first_name) 269 | 270 | def list_to_str(k): 271 | if not k: 272 | return "N/A" 273 | elif len(k) == 1: 274 | return str(k[0]) 275 | elif MAX_LIST_ELM: 276 | k = k[:int(MAX_LIST_ELM)] 277 | return ' '.join(f'{elem}, ' for elem in k) 278 | else: 279 | return ' '.join(f'{elem}, ' for elem in k) 280 | 281 | def last_online(from_user): 282 | time = "" 283 | if from_user.is_bot: 284 | time += "🤖 Bot :(" 285 | elif from_user.status == enums.UserStatus.RECENTLY: 286 | time += "Recently" 287 | elif from_user.status == enums.UserStatus.LAST_WEEK: 288 | time += "Within the last week" 289 | elif from_user.status == enums.UserStatus.LAST_MONTH: 290 | time += "Within the last month" 291 | elif from_user.status == enums.UserStatus.LONG_AGO: 292 | time += "A long time ago :(" 293 | elif from_user.status == enums.UserStatus.ONLINE: 294 | time += "Currently Online" 295 | elif from_user.status == enums.UserStatus.OFFLINE: 296 | time += from_user.last_online_date.strftime("%a, %d %b %Y, %H:%M:%S") 297 | return time 298 | 299 | 300 | def split_quotes(text: str) -> List: 301 | if not any(text.startswith(char) for char in START_CHAR): 302 | return text.split(None, 1) 303 | counter = 1 # ignore first char -> is some kind of quote 304 | while counter < len(text): 305 | if text[counter] == "\\": 306 | counter += 1 307 | elif text[counter] == text[0] or (text[0] == SMART_OPEN and text[counter] == SMART_CLOSE): 308 | break 309 | counter += 1 310 | else: 311 | return text.split(None, 1) 312 | 313 | # 1 to avoid starting quote, and counter is exclusive so avoids ending 314 | key = remove_escapes(text[1:counter].strip()) 315 | # index will be in range, or `else` would have been executed and returned 316 | rest = text[counter + 1:].strip() 317 | if not key: 318 | key = text[0] + text[0] 319 | return list(filter(None, [key, rest])) 320 | 321 | def parser(text, keyword): 322 | if "buttonalert" in text: 323 | text = (text.replace("\n", "\\n").replace("\t", "\\t")) 324 | buttons = [] 325 | note_data = "" 326 | prev = 0 327 | i = 0 328 | alerts = [] 329 | for match in BTN_URL_REGEX.finditer(text): 330 | # Check if btnurl is escaped 331 | n_escapes = 0 332 | to_check = match.start(1) - 1 333 | while to_check > 0 and text[to_check] == "\\": 334 | n_escapes += 1 335 | to_check -= 1 336 | 337 | # if even, not escaped -> create button 338 | if n_escapes % 2 == 0: 339 | note_data += text[prev:match.start(1)] 340 | prev = match.end(1) 341 | if match.group(3) == "buttonalert": 342 | # create a thruple with button label, url, and newline status 343 | if bool(match.group(5)) and buttons: 344 | buttons[-1].append(InlineKeyboardButton( 345 | text=match.group(2), 346 | callback_data=f"alertmessage:{i}:{keyword}" 347 | )) 348 | else: 349 | buttons.append([InlineKeyboardButton( 350 | text=match.group(2), 351 | callback_data=f"alertmessage:{i}:{keyword}" 352 | )]) 353 | i += 1 354 | alerts.append(match.group(4)) 355 | elif bool(match.group(5)) and buttons: 356 | buttons[-1].append(InlineKeyboardButton( 357 | text=match.group(2), 358 | url=match.group(4).replace(" ", "") 359 | )) 360 | else: 361 | buttons.append([InlineKeyboardButton( 362 | text=match.group(2), 363 | url=match.group(4).replace(" ", "") 364 | )]) 365 | 366 | else: 367 | note_data += text[prev:to_check] 368 | prev = match.start(1) - 1 369 | else: 370 | note_data += text[prev:] 371 | 372 | try: 373 | return note_data, buttons, alerts 374 | except: 375 | return note_data, buttons, None 376 | 377 | def remove_escapes(text: str) -> str: 378 | res = "" 379 | is_escaped = False 380 | for counter in range(len(text)): 381 | if is_escaped: 382 | res += text[counter] 383 | is_escaped = False 384 | elif text[counter] == "\\": 385 | is_escaped = True 386 | else: 387 | res += text[counter] 388 | return res 389 | 390 | 391 | def humanbytes(size): 392 | if not size: 393 | return "" 394 | power = 2**10 395 | n = 0 396 | Dic_powerN = {0: ' ', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti'} 397 | while size > power: 398 | size /= power 399 | n += 1 400 | return str(round(size, 2)) + " " + Dic_powerN[n] + 'B' 401 | --------------------------------------------------------------------------------