├── Dockerfile ├── LICENSE ├── Procfile ├── README.md ├── Script.py ├── app.json ├── app.py ├── bot.py ├── database ├── connections_mdb.py ├── filters_mdb.py ├── gfilters_mdb.py ├── ia_filterdb.py └── users_chats_db.py ├── docker-compose.yml ├── heroku.yml ├── info.py ├── logging.conf ├── plugins ├── __init__.py ├── banned.py ├── broadcast.py ├── channel.py ├── commands.py ├── connection.py ├── files_delete.py ├── filters.py ├── genlink.py ├── gfilters.py ├── index.py ├── inline.py ├── misc.py ├── p_ttishow.py ├── pm_filter.py ├── route.py └── sample.py ├── render.yaml ├── requirements.txt ├── runtime.txt ├── sample_info.py ├── start.sh └── utils.py /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | 3 | WORKDIR /Pm-Shortner 4 | 5 | COPY . /Pm-Shortner 6 | 7 | RUN pip install -r requirements.txt 8 | 9 | CMD ["python", "bot.py"] 10 | -------------------------------------------------------------------------------- /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 | worker: python3 bot.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | PM-Shortner-Bot Logo 3 |

4 |

5 | PM-Shortner-Bot 6 |

7 | 8 | ![Typing SVG](https://readme-typing-svg.herokuapp.com/?lines=𝑊𝑒𝑙𝑐𝑜𝑚𝑒+𝑇𝑜+Nothing-Bot;𝐴+𝑠𝑖𝑚𝑝𝑙𝑒+𝑎𝑛𝑑+𝑝𝑜𝑤𝑒𝑟𝑓𝑢𝑙+𝐵𝑜𝑡!;𝐼𝑛𝑑𝑒𝑥𝑒𝑠+𝐹𝑖𝑙𝑒𝑠+𝑎𝑏𝑜𝑣𝑒+2𝐺𝐵;𝐴+𝐵𝑜𝑡+𝑤𝑖𝑡ℎ+𝑑𝑜𝑢𝑏𝑙𝑒+𝑏𝑢𝑡𝑡𝑜𝑛!;𝑆𝑡𝑎𝑟𝑡+𝑚𝑒𝑠𝑠𝑎𝑔𝑒+𝑤𝑖𝑡ℎ+𝑝𝑖𝑐!;𝐴𝑛𝑑+𝑚𝑜𝑟𝑒+𝑓𝑒𝑎𝑡𝑢𝑟𝑒𝑠!) 9 |

10 | 11 | ## 𝐹𝑒𝑎𝑡𝑢𝑟𝑒𝑠 12 | - [x] 𝐼𝑀𝐷𝐵 𝑇𝑒𝑚𝑝𝑙𝑎𝑡𝑒 𝑆𝑒𝑡 13 | - [x] 𝐼𝑛𝑑𝑒𝑥𝑒𝑠 𝐹𝑖𝑙𝑒𝑠 𝑎𝑏𝑜𝑣𝑒 2𝐺𝐵 14 | - [x] 𝑃𝑟𝑒𝐷𝑉𝐷 𝑎𝑛𝑑 𝐶𝑎𝑚𝑅𝑖𝑝 𝐷𝑒𝑙𝑒𝑡𝑒 𝑀𝑜𝑑𝑒 15 | - [x] 𝑀𝑢𝑙𝑡𝑖𝑝𝑙𝑒 𝐹𝑖𝑙𝑒 𝐷𝑒𝑙𝑒𝑡𝑖𝑜𝑛 16 | - [x] 𝑆𝑒𝑡𝑡𝑖𝑛𝑔𝑠 𝑀𝑒𝑛𝑢 17 | - [x] 𝐹𝑜𝑟𝑐𝑒 𝑆𝑢𝑏𝑠𝑐𝑟𝑖𝑝𝑡𝑖𝑜𝑛 18 | - [x] 𝑊𝑒𝑙𝑐𝑜𝑚𝑒 𝑀𝑒𝑠𝑠𝑎𝑔𝑒 19 | - [x] 𝐴𝑢𝑡𝑜𝑚𝑎𝑡𝑖𝑐 𝐹𝑖𝑙𝑒 𝐹𝑖𝑙𝑡𝑒𝑟𝑖𝑛𝑔 20 | - [x] 𝐷𝑜𝑢𝑏𝑙𝑒 𝐹𝑖𝑙𝑡𝑒𝑟 𝐵𝑢𝑡𝑡𝑜𝑛 21 | - [x] 𝑆𝑖𝑛𝑔𝑙𝑒 𝐹𝑖𝑙𝑡𝑒𝑟 𝐵𝑢𝑡𝑡𝑜𝑛 22 | - [x] 𝐵𝑜𝑡 𝑃𝑀 𝐹𝑖𝑙𝑒 𝑆𝑒𝑛𝑑 𝑀𝑜𝑑𝑒 23 | - [x] 𝐴𝑢𝑡𝑜 𝐹𝑖𝑙𝑒 𝑆𝑒𝑛𝑑 24 | - [x] 𝐹𝑜𝑟𝑤𝑎𝑟𝑑 𝑅𝑒𝑠𝑡𝑟𝑖𝑐𝑡𝑖𝑜𝑛 25 | - [x] 𝐹𝑖𝑙𝑒 𝑃𝑟𝑜𝑡𝑒𝑐𝑡 26 | - [x] 𝑀𝑎𝑛𝑢𝑎𝑙 𝐹𝑖𝑙𝑒 𝐹𝑖𝑙𝑡𝑒𝑟𝑖𝑛𝑔 27 | - [x] 𝐺𝑙𝑜𝑏𝑎𝑙 𝐹𝑖𝑙𝑒 𝐹𝑖𝑙𝑡𝑒𝑟𝑖𝑛𝑔 28 | - [x] 𝐼𝑀𝐷𝐵 29 | - [x] 𝐴𝑑𝑚𝑖𝑛 𝐶𝑜𝑚𝑚𝑎𝑛𝑑𝑠 30 | - [x] 𝑈𝑠𝑒𝑟 𝐵𝑟𝑜𝑎𝑑𝑐𝑎𝑠𝑡 31 | - [x] 𝐺𝑟𝑜𝑢𝑝 𝐵𝑟𝑜𝑎𝑑𝑐𝑎𝑠𝑡 32 | - [x] 𝐼𝑛𝑑𝑒𝑥 33 | - [x] 𝐼𝑀𝐷𝐵 𝑠𝑒𝑎𝑟𝑐ℎ 34 | - [x] 𝐼𝑛𝑙𝑖𝑛𝑒 𝑆𝑒𝑎𝑟𝑐ℎ 35 | - [x] 𝑅𝑎𝑛𝑑𝑜𝑚 𝑝𝑖𝑐𝑠 36 | - [x] 𝑖𝑑𝑠 𝑎𝑛𝑑 𝑈𝑠𝑒𝑟 𝑖𝑛𝑓𝑜 37 | - [x] 𝑆𝑡𝑎𝑡𝑠 38 | - [x] 𝑈𝑠𝑒𝑟𝑠 39 | - [x] 𝐶ℎ𝑎𝑡𝑠 40 | - [x] 𝑈𝑠𝑒𝑟 𝐵𝑎𝑛 41 | - [x] 𝑈𝑠𝑒𝑟 𝑈𝑛𝑏𝑎𝑛 42 | - [x] 𝐶ℎ𝑎𝑡 𝐿𝑒𝑎𝑣𝑒 43 | - [x] 𝐶ℎ𝑎𝑡 𝐷𝑖𝑠𝑎𝑏𝑙𝑒 44 | - [x] 𝐶ℎ𝑎𝑛𝑛𝑒𝑙 45 | - [x] 𝑆𝑝𝑒𝑙𝑙𝑖𝑛𝑔 𝐶ℎ𝑒𝑐𝑘 𝐹𝑒𝑎𝑡𝑢𝑟𝑒 46 | - [x] 𝐹𝑖𝑙𝑒 𝑆𝑡𝑜𝑟𝑒 47 | - [x] 𝐴𝑢𝑡𝑜 𝐷𝑒𝑙𝑒𝑡𝑒 48 | - [x] 𝐴𝑛𝑑 𝑀𝑜𝑟𝑒... 49 | 50 | 𝑇𝑜 𝑘𝑛𝑜𝑤 𝑎𝑏𝑜𝑢𝑡 𝑎𝑙𝑙 𝑓𝑒𝑎𝑡𝑢𝑟𝑒𝑠, 𝐽𝑜𝑖𝑛 𝑜𝑢𝑟 ᴊᴏɪɴ ᴜᴘᴅᴀᴛᴇ ᴄʜᴀɴɴᴇʟ ❤️‍🔥. 51 | 52 | ## 𝐶𝑜𝑚𝑚𝑎𝑛𝑑𝑠 53 | ``` 54 | • /invite - 𝑡𝑜 𝑔𝑒𝑡 𝑡ℎ𝑒 𝑖𝑛𝑣𝑖𝑡𝑒 𝑙𝑖𝑛𝑘 𝑜𝑓 𝑎𝑛𝑦 𝑐ℎ𝑎𝑡 𝑤ℎ𝑖𝑐ℎ 𝑡ℎ𝑒 𝑏𝑜𝑡 𝑖𝑠 𝑎𝑑𝑚𝑖𝑛. 55 | • /logs - 𝑡𝑜 𝑔𝑒𝑡 𝑡ℎ𝑒 𝑟𝑒𝑠𝑐𝑒𝑛𝑡 𝑒𝑟𝑟𝑜𝑟𝑠 56 | • /stats - 𝑡𝑜 𝑔𝑒𝑡 𝑠𝑡𝑎𝑡𝑢𝑠 𝑜𝑓 𝑓𝑖𝑙𝑒𝑠 𝑖𝑛 𝑑𝑏. 57 | • /connections - 𝑇𝑜 𝑠𝑒𝑒 𝑎𝑙𝑙 𝑐𝑜𝑛𝑛𝑒𝑐𝑡𝑒𝑑 𝑔𝑟𝑜𝑢𝑝𝑠 58 | • /settings - 𝑇𝑜 𝑜𝑝𝑒𝑛 𝑠𝑒𝑡𝑡𝑖𝑛𝑔𝑠 𝑚𝑒𝑛𝑢 59 | • /filter - 𝑎𝑑𝑑 𝑚𝑎𝑛𝑢𝑎𝑙 𝑓𝑖𝑙𝑡𝑒𝑟𝑠 60 | • /filters - 𝑣𝑖𝑒𝑤 𝑓𝑖𝑙𝑡𝑒𝑟𝑠 61 | • /connect - 𝑐𝑜𝑛𝑛𝑒𝑐𝑡 𝑡𝑜 𝑃𝑀. 62 | • /disconnect - 𝑑𝑖𝑠𝑐𝑜𝑛𝑛𝑒𝑐𝑡 𝑓𝑟𝑜𝑚 𝑃𝑀 63 | • /del - 𝑑𝑒𝑙𝑒𝑡𝑒 𝑎 𝑓𝑖𝑙𝑡𝑒𝑟 64 | • /delall - 𝑑𝑒𝑙𝑒𝑡𝑒 𝑎𝑙𝑙 𝑓𝑖𝑙𝑡𝑒𝑟𝑠 65 | • /deleteall - 𝑑𝑒𝑙𝑒𝑡𝑒 𝑎𝑙𝑙 𝑖𝑛𝑑𝑒𝑥𝑒𝑑 𝑓𝑖𝑙𝑒𝑠. 66 | • /delete - 𝑑𝑒𝑙𝑒𝑡𝑒 𝑎 𝑠𝑝𝑒𝑐𝑖𝑓𝑖𝑐 𝑓𝑖𝑙𝑒 𝑓𝑟𝑜𝑚 𝑖𝑛𝑑𝑒𝑥. 67 | • /info - 𝑔𝑒𝑡 𝑢𝑠𝑒𝑟 𝑖𝑛𝑓𝑜 68 | • /id - 𝑔𝑒𝑡 𝑡𝑔 𝑖𝑑𝑠. 69 | • /imdb - 𝑓𝑒𝑡𝑐ℎ 𝑖𝑛𝑓𝑜 𝑓𝑟𝑜𝑚 𝑖𝑚𝑑𝑏. 70 | • /search - 𝑇𝑜 𝑠𝑒𝑎𝑟𝑐ℎ 𝑓𝑟𝑜𝑚 𝑣𝑎𝑟𝑖𝑜𝑢𝑠 𝑠𝑜𝑢𝑟𝑐𝑒𝑠 71 | • /start - 𝑇𝑜 𝑠𝑡𝑎𝑟𝑡 𝑡ℎ𝑒 𝑏𝑜𝑡 72 | • /setskip - 𝑇𝑜 𝑠𝑘𝑖𝑝 𝑛𝑢𝑚𝑏𝑒𝑟 𝑜𝑓 𝑚𝑒𝑠𝑠𝑎𝑔𝑒𝑠 𝑤ℎ𝑒𝑛 𝑖𝑛𝑑𝑒𝑥𝑖𝑛𝑔 𝑓𝑖𝑙𝑒𝑠 73 | • /users - 𝑡𝑜 𝑔𝑒𝑡 𝑙𝑖𝑠𝑡 𝑜𝑓 𝑚𝑦 𝑢𝑠𝑒𝑟𝑠 𝑎𝑛𝑑 𝑖𝑑𝑠. 74 | • /chats - 𝑡𝑜 𝑔𝑒𝑡 𝑙𝑖𝑠𝑡 𝑜𝑓 𝑡ℎ𝑒 𝑚𝑦 𝑐ℎ𝑎𝑡𝑠 𝑎𝑛𝑑 𝑖𝑑𝑠 75 | • /leave - 𝑡𝑜 𝑙𝑒𝑎𝑣𝑒 𝑓𝑟𝑜𝑚 𝑎 𝑐ℎ𝑎𝑡. 76 | • /disable - 𝑑𝑜 𝑑𝑖𝑠𝑎𝑏𝑙𝑒 𝑎 𝑐ℎ𝑎𝑡. 77 | • /enable - 𝑟𝑒-𝑒𝑛𝑎𝑏𝑙𝑒 𝑐ℎ𝑎𝑡. 78 | • /ban - 𝑡𝑜 𝑏𝑎𝑛 𝑎 𝑢𝑠𝑒𝑟. 79 | • /unban - 𝑡𝑜 𝑢𝑛𝑏𝑎𝑛 𝑎 𝑢𝑠𝑒𝑟. 80 | • /channel - 𝑡𝑜 𝑔𝑒𝑡 𝑙𝑖𝑠𝑡 𝑜𝑓 𝑡𝑜𝑡𝑎𝑙 𝑐𝑜𝑛𝑛𝑒𝑐𝑡𝑒𝑑 𝑐ℎ𝑎𝑛𝑛𝑒𝑙𝑠 81 | • /broadcast - 𝑡𝑜 𝑏𝑟𝑜𝑎𝑑𝑐𝑎𝑠𝑡 𝑎 𝑚𝑒𝑠𝑠𝑎𝑔𝑒 𝑡𝑜 𝑎𝑙𝑙 𝑢𝑠𝑒𝑟𝑠 82 | • /grp_broadcast - 𝑇𝑜 𝑏𝑟𝑜𝑎𝑑𝑐𝑎𝑠𝑡 𝑎 𝑚𝑒𝑠𝑠𝑎𝑔𝑒 𝑡𝑜 𝑎𝑙𝑙 𝑐𝑜𝑛𝑛𝑒𝑐𝑡𝑒𝑑 𝑔𝑟𝑜𝑢𝑝𝑠. 83 | • /batch - 𝑡𝑜 𝑐𝑟𝑒𝑎𝑡𝑒 𝑙𝑖𝑛𝑘 𝑓𝑜𝑟 𝑚𝑢𝑙𝑡𝑖𝑝𝑙𝑒 𝑝𝑜𝑠𝑡𝑠 84 | • /link - 𝑡𝑜 𝑐𝑟𝑒𝑎𝑡𝑒 𝑙𝑖𝑛𝑘 𝑓𝑜𝑟 𝑜𝑛𝑒 𝑝𝑜𝑠𝑡 85 | • /status - 𝑌𝑜𝑢𝑟 𝐻𝑒𝑟𝑜𝑘𝑢 𝐴𝑃𝐼 𝐾𝑒𝑦 𝑡𝑜 𝑐ℎ𝑒𝑐𝑘 𝑑𝑦𝑛𝑜, 𝑏𝑜𝑡 𝑢𝑝𝑡𝑖𝑚𝑒 𝑎𝑛𝑑 𝑏𝑜𝑡 𝑤𝑜𝑟𝑘𝑖𝑛𝑔 𝑑𝑎𝑦 𝑝𝑟𝑒𝑑𝑖𝑐𝑡𝑖𝑜𝑛. 86 | • /set_template - 𝑇𝑜 𝑠𝑒𝑡 𝑎 𝑐𝑢𝑠𝑡𝑜𝑚 𝐼𝑀𝐷𝑏 𝑡𝑒𝑚𝑝𝑙𝑎𝑡𝑒 𝑓𝑜𝑟 𝑖𝑛𝑑𝑖𝑣𝑖𝑑𝑢𝑎𝑙 𝑔𝑟𝑜𝑢𝑝𝑠 87 | • /gfilter - 𝑇𝑜 𝑎𝑑𝑑 𝑔𝑙𝑜𝑏𝑎𝑙 𝑓𝑖𝑙𝑡𝑒𝑟𝑠. 88 | • /gfilters - 𝑇𝑜 𝑣𝑖𝑒𝑤 𝑙𝑖𝑠𝑡 𝑜𝑓 𝑎𝑙𝑙 𝑔𝑙𝑜𝑏𝑎𝑙 𝑓𝑖𝑙𝑡𝑒𝑟𝑠. 89 | • /delg - 𝑇𝑜 𝑑𝑒𝑙𝑒𝑡𝑒 𝑎 𝑠𝑝𝑒𝑐𝑖𝑓𝑖𝑐 𝑔𝑙𝑜𝑏𝑎𝑙 𝑓𝑖𝑙𝑡𝑒𝑟. 90 | • /delallg - 𝑇𝑜 𝑑𝑒𝑙𝑒𝑡𝑒 𝑎𝑙𝑙 𝑔𝑙𝑜𝑏𝑎𝑙 𝑓𝑖𝑙𝑡𝑒𝑟𝑠 𝑓𝑟𝑜𝑚 𝑡ℎ𝑒 𝑏𝑜𝑡'𝑠 𝑑𝑎𝑡𝑎𝑏𝑎𝑠𝑒. 91 | • /deletefiles - 𝑇𝑜 𝑑𝑒𝑙𝑒𝑡𝑒 𝑃𝑟𝑒𝐷𝑉𝐷 𝑎𝑛𝑑 𝐶𝑎𝑚𝑅𝑖𝑝 𝐹𝑖𝑙𝑒𝑠 𝑓𝑟𝑜𝑚 𝑡ℎ𝑒 𝑏𝑜𝑡'𝑠 𝑑𝑎𝑡𝑎𝑏𝑎𝑠𝑒. 92 | ``` 93 | 94 | ## Variables 95 | 96 | ### Required Variables 97 | * `BOT_TOKEN`: Create a bot using [@BotFather](https://telegram.dog/BotFather), and get the Telegram API token. 98 | * `API_ID`: Get this value from [telegram.org](https://my.telegram.org/apps) 99 | * `API_HASH`: Get this value from [telegram.org](https://my.telegram.org/apps) 100 | * `CHANNELS`: Username or ID of channel or group. Separate multiple IDs by space 101 | * `ADMINS`: Username or ID of Admin. Separate multiple Admins by space 102 | * `DATABASE_URI`: [mongoDB](https://www.mongodb.com) URI. Get this value from [mongoDB](https://www.mongodb.com). For more help watch this [video](https://youtu.be/1G1XwEOnxxo) 103 | * `DATABASE_NAME`: Name of the database in [mongoDB](https://www.mongodb.com). 104 | * `LOG_CHANNEL` : A channel to log the activities of bot. Make sure bot is an admin in the channel. 105 | ### Optional Variables 106 | * `PICS`: Telegraph links of images to show in start message.( Multiple images can be used separated by space ) 107 | * `FILE_STORE_CHANNEL`: Channel from were file store links of posts should be made.Separate multiple IDs by space 108 | * Check [info.py](https://github.com/Singhsawan/PM-Shortner-Bot 109 | ) for more optional variables 110 | 111 | 112 |
Deploy To Heroku 113 |

114 |
115 | 116 | Deploy To Heroku 117 | 118 |

119 |
120 | 121 |
Deploy To VPS 122 |

123 |

124 | git clone https://github.com/Singhsawan/Pmshortner2
125 | # Install Packages
126 | pip3 install -U -r requirements.txt
127 | Edit info.py with variables as given below then run bot
128 | python3 bot.py
129 | 
130 |

131 |
132 | -------------------------------------------------------------------------------- /Script.py: -------------------------------------------------------------------------------- 1 | class script(object): 2 | START_TXT = """ʜᴇʟᴏ {} 3 | 4 | ɪ ᴀᴍ ᴘᴏᴡᴇʀ ғᴜʟʟ ᴀᴜᴛᴏ ғɪʟᴛᴇʀ ʙᴏᴛ 5 | ɪ ᴀᴍ ᴘᴍ sʜᴏʀᴛɴᴇʀ-ʟɪɴᴋ ʙᴏᴛ 6 | 7 | ᴍᴀɴᴛᴀɪɴᴇᴅ ʙʏ : ɴᴏᴛʜɪɴɢ ʙʀᴏ 😏""" 8 | 9 | 10 | HELP_TXT = """Hᴇʏ {} 11 | Hᴇʀᴇ Is Tʜᴇ Hᴇʟᴘ Fᴏʀ Mʏ Cᴏᴍᴍᴀɴᴅs.""" 12 | 13 | ABOUT_TXT = """ 14 | 🤖 ᴍʏ ɴᴀᴍᴇ : {} 15 | 👨‍💻 ᴄʀᴇᴀᴛᴏʀ : ɴᴏᴛʜɪɴɢ ʙʀᴏ 16 | 📚 ʟɪʙʀᴀʀʏ : ᴘʏʀᴏɢʀᴀᴍ 17 | 📝 ʟᴀɴɢᴜᴀɢᴇ : ᴘʏᴛʜᴏɴ 3 18 | ♻️ ᴅᴀᴛᴀ ʙᴀsᴇ : ᴍᴏɴɢᴏ ᴅʙ 19 | 📡 ʜᴏsᴛᴇᴅ ᴏɴ : Heroku 20 | 🥶 ʙᴜɪʟᴅ sᴛᴀᴛᴜs : ᴠ3.0 [sᴛᴀʙʟᴇ​]""" 21 | 22 | SOURCE_TXT = """ 23 | Tʜɪꜱ Bᴏᴛ Iꜱ Oᴘᴇɴ Sᴏᴜʀᴄᴇ Pʀᴏᴊᴇᴄᴛ. 24 | ꜱᴏᴜʀᴄᴇ : ʀᴇᴘᴏ ʟɪɴᴋ 🙃""" 25 | 26 | MANUELFILTER_TXT = """ʜᴇʟᴘ: ꜰɪʟᴛᴇʀꜱ 27 | - ꜰɪʟᴛᴇʀ ɪꜱ ᴀ ꜰᴇᴀᴛᴜʀᴇ ᴡᴇʀᴇ ᴜꜱᴇʀꜱ ᴄᴀɴ ꜱᴇᴛ ᴀᴜᴛᴏᴍᴀᴛᴇᴅ ʀᴇᴘʟɪᴇꜱ ꜰᴏʀ ᴀ ᴘᴀʀᴛɪᴄᴜʟᴀʀ ᴋᴇʏᴡᴏʀᴅ ᴀɴᴅ ɪ ᴡɪʟʟ ʀᴇꜱᴘᴏɴᴅ ᴡʜᴇɴᴇᴠᴇʀ ᴀ ᴋᴇʏᴡᴏʀᴅ ɪꜱ ꜰᴏᴜɴᴅ ɪɴ ᴛʜᴇ ᴍᴇꜱꜱᴀɢᴇ 28 | ɴᴏᴛᴇ: 29 | 1. ᴛʜɪꜱ ʙᴏᴛ ꜱʜᴏᴜʟᴅ ʜᴀᴠᴇ ᴀᴅᴍɪɴ ᴘʀɪᴠɪʟᴇɢᴇ. 30 | 2. ᴏɴʟʏ ᴀᴅᴍɪɴꜱ ᴄᴀɴ ᴀᴅᴅ ꜰɪʟᴛᴇʀꜱ ɪɴ ᴀ ᴄʜᴀᴛ. 31 | 3. ᴀʟᴇʀᴛ ʙᴜᴛᴛᴏɴꜱ ʜᴀᴠᴇ ᴀ ʟɪᴍɪᴛ ᴏꜰ 64 ᴄʜᴀʀᴀᴄᴛᴇʀꜱ. 32 | Cᴏᴍᴍᴀɴᴅs Aɴᴅ Usᴀɢᴇ: 33 | • /filter - ᴀᴅᴅ ᴀ ꜰɪʟᴛᴇʀ ɪɴ ᴀ ᴄʜᴀᴛ 34 | • /filters - ʟɪꜱᴛ ᴀʟʟ ᴛʜᴇ ꜰɪʟᴛᴇʀꜱ ᴏꜰ ᴀ ᴄʜᴀᴛ 35 | • /del - ᴅᴇʟᴇᴛᴇ ᴀ ꜱᴘᴇᴄɪꜰɪᴄ ꜰɪʟᴛᴇʀ ɪɴ ᴀ ᴄʜᴀᴛ 36 | • /delall - ᴅᴇʟᴇᴛᴇ ᴛʜᴇ ᴡʜᴏʟᴇ ꜰɪʟᴛᴇʀꜱ ɪɴ ᴀ ᴄʜᴀᴛ (ᴄʜᴀᴛ ᴏᴡɴᴇʀ ᴏɴʟʏ)""" 37 | 38 | BUTTON_TXT = """ʜᴇʟᴘ: ʙᴜᴛᴛᴏɴꜱ 39 | - ᴛʜɪꜱ ʙᴏᴛ ꜱᴜᴘᴘᴏʀᴛꜱ ʙᴏᴛʜ ᴜʀʟ ᴀɴᴅ ᴀʟᴇʀᴛ ɪɴʟɪɴᴇ ʙᴜᴛᴛᴏɴꜱ. 40 | ɴᴏᴛᴇ: 41 | 1. ᴛᴇʟᴇɢʀᴀᴍ ᴡɪʟʟ ɴᴏᴛ ᴀʟʟᴏᴡꜱ ʏᴏᴜ ᴛᴏ ꜱᴇɴᴅ ʙᴜᴛᴛᴏɴꜱ ᴡɪᴛʜᴏᴜᴛ ᴀɴʏ ᴄᴏɴᴛᴇɴᴛ, ꜱᴏ ᴄᴏɴᴛᴇɴᴛ ɪꜱ ᴍᴀɴᴅᴀᴛᴏʀʏ. 42 | 2. ᴛʜɪꜱ ʙᴏᴛ ꜱᴜᴘᴘᴏʀᴛꜱ ʙᴜᴛᴛᴏɴꜱ ᴡɪᴛʜ ᴀɴʏ ᴛᴇʟᴇɢʀᴀᴍ ᴍᴇᴅɪᴀ ᴛʏᴘᴇ. 43 | 3. ʙᴜᴛᴛᴏɴꜱ ꜱʜᴏᴜʟᴅ ʙᴇ ᴘʀᴏᴘᴇʀʟʏ ᴘᴀʀꜱᴇᴅ ᴀꜱ ᴍᴀʀᴋᴅᴏᴡɴ ꜰᴏʀᴍᴀᴛ 44 | ᴜʀʟ ʙᴜᴛᴛᴏɴꜱ: 45 | [Button Text](buttonurl:https://t.me/TeamHMT_Bots) 46 | ᴀʟᴇʀᴛ ʙᴜᴛᴛᴏɴꜱ: 47 | [Button Text](buttonalert:ᴛʜɪꜱ ɪꜱ ᴀɴ ᴀʟᴇʀᴛ ᴍᴇꜱꜱᴀɢᴇ)""" 48 | 49 | AUTOFILTER_TXT = """ʜᴇʟᴘ: ᴀᴜᴛᴏ ꜰɪʟᴛᴇʀ 50 | ɴᴏᴛᴇ: Fɪʟᴇ Iɴᴅᴇx 51 | 1. ᴍᴀᴋᴇ ᴍᴇ ᴛʜᴇ ᴀᴅᴍɪɴ ᴏꜰ ʏᴏᴜʀ ᴄʜᴀɴɴᴇʟ ɪꜰ ɪᴛ'ꜱ ᴘʀɪᴠᴀᴛᴇ. 52 | 2. ᴍᴀᴋᴇ ꜱᴜʀᴇ ᴛʜᴀᴛ ʏᴏᴜʀ ᴄʜᴀɴɴᴇʟ ᴅᴏᴇꜱ ɴᴏᴛ ᴄᴏɴᴛᴀɪɴꜱ ᴄᴀᴍʀɪᴘꜱ, ᴘᴏʀɴ ᴀɴᴅ ꜰᴀᴋᴇ ꜰɪʟᴇꜱ. 53 | 3. ꜰᴏʀᴡᴀʀᴅ ᴛʜᴇ ʟᴀꜱᴛ ᴍᴇꜱꜱᴀɢᴇ ᴛᴏ ᴍᴇ ᴡɪᴛʜ Qᴜᴏᴛᴇꜱ. ɪ'ʟʟ ᴀᴅᴅ ᴀʟʟ ᴛʜᴇ ꜰɪʟᴇꜱ ɪɴ ᴛʜᴀᴛ ᴄʜᴀɴɴᴇʟ ᴛᴏ ᴍʏ ᴅʙ. 54 | 55 | Nᴏᴛᴇ: AᴜᴛᴏFɪʟᴛᴇʀ 56 | 1. Aᴅᴅ ᴛʜᴇ ʙᴏᴛ ᴀs ᴀᴅᴍɪɴ ᴏɴ ʏᴏᴜʀ ɢʀᴏᴜᴘ. 57 | 2. Usᴇ /connect ᴀɴᴅ ᴄᴏɴɴᴇᴄᴛ ʏᴏᴜʀ ɢʀᴏᴜᴘ ᴛᴏ ᴛʜᴇ ʙᴏᴛ. 58 | 3. Usᴇ /settings ᴏɴ ʙᴏᴛ's PM ᴀɴᴅ ᴛᴜʀɴ ᴏɴ AᴜᴛᴏFɪʟᴛᴇʀ ᴏɴ ᴛʜᴇ sᴇᴛᴛɪɴɢs ᴍᴇɴᴜ.""" 59 | 60 | CONNECTION_TXT = """ʜᴇʟᴘ: ᴄᴏɴɴᴇᴄᴛɪᴏɴꜱ 61 | - ᴜꜱᴇᴅ ᴛᴏ ᴄᴏɴɴᴇᴄᴛ ʙᴏᴛ ᴛᴏ ᴘᴍ ꜰᴏʀ ᴍᴀɴᴀɢɪɴɢ ꜰɪʟᴛᴇʀꜱ 62 | - ɪᴛ ʜᴇʟᴘꜱ ᴛᴏ ᴀᴠᴏɪᴅ ꜱᴘᴀᴍᴍɪɴɢ ɪɴ ɢʀᴏᴜᴘꜱ. 63 | ɴᴏᴛᴇ: 64 | 1. ᴏɴʟʏ ᴀᴅᴍɪɴꜱ ᴄᴀɴ ᴀᴅᴅ ᴀ ᴄᴏɴɴᴇᴄᴛɪᴏɴ. 65 | 2. ꜱᴇɴᴅ /ᴄᴏɴɴᴇᴄᴛ ꜰᴏʀ ᴄᴏɴɴᴇᴄᴛɪɴɢ ᴍᴇ ᴛᴏ ʏᴏᴜʀ ᴘᴍ 66 | Cᴏᴍᴍᴀɴᴅs Aɴᴅ Usᴀɢᴇ: 67 | • /connect - ᴄᴏɴɴᴇᴄᴛ ᴀ ᴘᴀʀᴛɪᴄᴜʟᴀʀ ᴄʜᴀᴛ ᴛᴏ ʏᴏᴜʀ ᴘᴍ 68 | • /disconnect - ᴅɪꜱᴄᴏɴɴᴇᴄᴛ ꜰʀᴏᴍ ᴀ ᴄʜᴀᴛ 69 | • /connections - ʟɪꜱᴛ ᴀʟʟ ʏᴏᴜʀ ᴄᴏɴɴᴇᴄᴛɪᴏɴꜱ""" 70 | 71 | EXTRAMOD_TXT = """ʜᴇʟᴘ: Exᴛʀᴀ Mᴏᴅᴜʟᴇs 72 | ɴᴏᴛᴇ: 73 | ᴛʜᴇꜱᴇ ᴀʀᴇ ᴛʜᴇ ᴇxᴛʀᴀ ꜰᴇᴀᴛᴜʀᴇꜱ ᴏꜰ ᴛʜɪꜱ ʙᴏᴛ 74 | Cᴏᴍᴍᴀɴᴅs Aɴᴅ Usᴀɢᴇ: 75 | • /id - ɢᴇᴛ ɪᴅ ᴏꜰ ᴀ ꜱᴘᴇᴄɪꜰɪᴇᴅ ᴜꜱᴇʀ. 76 | • /info - ɢᴇᴛ ɪɴꜰᴏʀᴍᴀᴛɪᴏɴ ᴀʙᴏᴜᴛ ᴀ ᴜꜱᴇʀ. 77 | • /imdb - ɢᴇᴛ ᴛʜᴇ ꜰɪʟᴍ ɪɴꜰᴏʀᴍᴀᴛɪᴏɴ ꜰʀᴏᴍ ɪᴍᴅʙ ꜱᴏᴜʀᴄᴇ. 78 | • /search - ɢᴇᴛ ᴛʜᴇ ꜰɪʟᴍ ɪɴꜰᴏʀᴍᴀᴛɪᴏɴ ꜰʀᴏᴍ ᴠᴀʀɪᴏᴜꜱ ꜱᴏᴜʀᴄᴇꜱ.""" 79 | 80 | ADMIN_TXT = """ʜᴇʟᴘ: Aᴅᴍɪɴ Mᴏᴅs 81 | ɴᴏᴛᴇ: 82 | Tʜɪs Mᴏᴅᴜʟᴇ Oɴʟʏ Wᴏʀᴋs Fᴏʀ Mʏ Aᴅᴍɪɴs 83 | Cᴏᴍᴍᴀɴᴅs Aɴᴅ Usᴀɢᴇ: 84 | • /logs - ᴛᴏ ɢᴇᴛ ᴛʜᴇ ʀᴇᴄᴇɴᴛ ᴇʀʀᴏʀꜱ 85 | • /stats - ᴛᴏ ɢᴇᴛ ꜱᴛᴀᴛᴜꜱ ᴏꜰ ꜰɪʟᴇꜱ ɪɴ ᴅʙ. [Tʜɪs Cᴏᴍᴍᴀɴᴅ Cᴀɴ Bᴇ Usᴇᴅ Bʏ Aɴʏᴏɴᴇ] 86 | • /delete - ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴀ ꜱᴘᴇᴄɪꜰɪᴄ ꜰɪʟᴇ ꜰʀᴏᴍ ᴅʙ. 87 | • /users - ᴛᴏ ɢᴇᴛ ʟɪꜱᴛ ᴏꜰ ᴍʏ ᴜꜱᴇʀꜱ ᴀɴᴅ ɪᴅꜱ. 88 | • /chats - ᴛᴏ ɢᴇᴛ ʟɪꜱᴛ ᴏꜰ ᴍʏ ᴄʜᴀᴛꜱ ᴀɴᴅ ɪᴅꜱ 89 | • /leave - ᴛᴏ ʟᴇᴀᴠᴇ ꜰʀᴏᴍ ᴀ ᴄʜᴀᴛ. 90 | • /disable - ᴛᴏ ᴅɪꜱᴀʙʟᴇ ᴀ ᴄʜᴀᴛ. 91 | • /ban - ᴛᴏ ʙᴀɴ ᴀ ᴜꜱᴇʀ. 92 | • /unban - ᴛᴏ ᴜɴʙᴀɴ ᴀ ᴜꜱᴇʀ. 93 | • /channel - ᴛᴏ ɢᴇᴛ ʟɪꜱᴛ ᴏꜰ ᴛᴏᴛᴀʟ ᴄᴏɴɴᴇᴄᴛᴇᴅ ᴄʜᴀɴɴᴇʟꜱ 94 | • /broadcast - ᴛᴏ ʙʀᴏᴀᴅᴄᴀꜱᴛ ᴀ ᴍᴇꜱꜱᴀɢᴇ ᴛᴏ ᴀʟʟ ᴜꜱᴇʀꜱ 95 | • /grp_broadcast - Tᴏ ʙʀᴏᴀᴅᴄᴀsᴛ ᴀ ᴍᴇssᴀɢᴇ ᴛᴏ ᴀʟʟ ᴄᴏɴɴᴇᴄᴛᴇᴅ ɢʀᴏᴜᴘs. 96 | • /gfilter - ᴛᴏ ᴀᴅᴅ ɢʟᴏʙᴀʟ ғɪʟᴛᴇʀs 97 | • /gfilters - ᴛᴏ ᴠɪᴇᴡ ʟɪsᴛ ᴏғ ᴀʟʟ ɢʟᴏʙᴀʟ ғɪʟᴛᴇʀs 98 | • /delg - ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴀ sᴘᴇᴄɪғɪᴄ ɢʟᴏʙᴀʟ ғɪʟᴛᴇʀ 99 | • /request - Tᴏ sᴇɴᴅ ᴀ Mᴏᴠɪᴇ/Sᴇʀɪᴇs ʀᴇᴏ̨ᴜᴇsᴛ ᴛᴏ ʙᴏᴛ ᴀᴅᴍɪɴs. Oɴʟʏ ᴡᴏʀᴋs ᴏɴ sᴜᴘᴘᴏʀᴛ ɢʀᴏᴜᴘ. [Tʜɪs Cᴏᴍᴍᴀɴᴅ Cᴀɴ Bᴇ Usᴇᴅ Bʏ Aɴʏᴏɴᴇ] 100 | • /delallg - Tᴏ ᴅᴇʟᴇᴛᴇ ᴀʟʟ Gғɪʟᴛᴇʀs ғʀᴏᴍ ᴛʜᴇ ʙᴏᴛ's ᴅᴀᴛᴀʙᴀsᴇ. 101 | • /deletefiles - Tᴏ ᴅᴇʟᴇᴛᴇ CᴀᴍRɪᴘ ᴀɴᴅ PʀᴇDVD Fɪʟᴇs ғʀᴏᴍ ᴛʜᴇ ʙᴏᴛ's ᴅᴀᴛᴀʙᴀsᴇ.""" 102 | 103 | STATUS_TXT = """★ Tᴏᴛᴀʟ Fɪʟᴇs: {} 104 | ★ Tᴏᴛᴀʟ Usᴇʀs: {} 105 | ★ Tᴏᴛᴀʟ Cʜᴀᴛs: {} 106 | ★ Usᴇᴅ Sᴛᴏʀᴀɢᴇ: {} 107 | ★ Fʀᴇᴇ Sᴛᴏʀᴀɢᴇ: {}""" 108 | 109 | LOG_TEXT_G = """#NewGroup 110 | Gʀᴏᴜᴘ = {}({}) 111 | Tᴏᴛᴀʟ Mᴇᴍʙᴇʀs = {} 112 | Aᴅᴅᴇᴅ Bʏ - {}""" 113 | 114 | LOG_TEXT_P = """#NewUser 115 | ID - {} 116 | Nᴀᴍᴇ - {}""" 117 | 118 | ALRT_TXT = """ʜᴇʟʟᴏ {}, 119 | ᴛʜɪꜱ ɪꜱ ɴᴏᴛ ʏᴏᴜʀ ᴍᴏᴠɪᴇ ʀᴇQᴜᴇꜱᴛ, 120 | ʀᴇQᴜᴇꜱᴛ ʏᴏᴜʀ'ꜱ...""" 121 | 122 | OLD_ALRT_TXT = """ʜᴇʏ {}, 123 | ʏᴏᴜ ᴀʀᴇ ᴜꜱɪɴɢ ᴏɴᴇ ᴏꜰ ᴍʏ ᴏʟᴅ ᴍᴇꜱꜱᴀɢᴇꜱ, 124 | ᴘʟᴇᴀꜱᴇ ꜱᴇɴᴅ ᴛʜᴇ ʀᴇQᴜᴇꜱᴛ ᴀɢᴀɪɴ.""" 125 | 126 | CUDNT_FND = """ɪ ᴄᴏᴜʟᴅɴ'ᴛ ꜰɪɴᴅ ᴀɴʏᴛʜɪɴɢ ʀᴇʟᴀᴛᴇᴅ ᴛᴏ {} 127 | ᴅɪᴅ ʏᴏᴜ ᴍᴇᴀɴ ᴀɴʏ ᴏɴᴇ ᴏꜰ ᴛʜᴇꜱᴇ?""" 128 | 129 | I_CUDNT = """sᴏʀʀʏ ɴᴏ ꜰɪʟᴇs ᴡᴇʀᴇ ꜰᴏᴜɴᴅ ꜰᴏʀ ʏᴏᴜʀ ʀᴇǫᴜᴇꜱᴛ {} 😕 130 | 131 | ᴄʜᴇᴄᴋ ʏᴏᴜʀ sᴘᴇʟʟɪɴɢ ɪɴ ɢᴏᴏɢʟᴇ ᴀɴᴅ ᴛʀʏ ᴀɢᴀɪɴ 😃 132 | 133 | ᴍᴏᴠɪᴇ ʀᴇǫᴜᴇꜱᴛ ꜰᴏʀᴍᴀᴛ 👇 134 | 135 | ᴇxᴀᴍᴘʟᴇ : Uncharted or Uncharted 2022 or Uncharted En 136 | 137 | ꜱᴇʀɪᴇꜱ ʀᴇǫᴜᴇꜱᴛ ꜰᴏʀᴍᴀᴛ 👇 138 | 139 | ᴇxᴀᴍᴘʟᴇ : Loki S01 or Loki S01E04 or Lucifer S03E24 140 | 141 | 🚯 ᴅᴏɴᴛ ᴜꜱᴇ ➠ ':(!,./)""" 142 | 143 | I_CUD_NT = """ɪ ᴄᴏᴜʟᴅɴ'ᴛ ꜰɪɴᴅ ᴀɴʏ ᴍᴏᴠɪᴇ ʀᴇʟᴀᴛᴇᴅ ᴛᴏ {}. 144 | ᴘʟᴇᴀꜱᴇ ᴄʜᴇᴄᴋ ᴛʜᴇ ꜱᴘᴇʟʟɪɴɢ ᴏɴ ɢᴏᴏɢʟᴇ ᴏʀ ɪᴍᴅʙ...""" 145 | 146 | MVE_NT_FND = """ᴍᴏᴠɪᴇ ɴᴏᴛ ꜰᴏᴜɴᴅ ɪɴ ᴅᴀᴛᴀʙᴀꜱᴇ...""" 147 | 148 | TOP_ALRT_MSG = """Cʜᴇᴄᴋɪɴɢ Fᴏʀ Mᴏᴠɪᴇ Iɴ Dᴀᴛᴀʙᴀsᴇ...""" 149 | 150 | MELCOW_ENG = """Hᴇʟʟᴏ {} 😍, Aɴᴅ Wᴇʟᴄᴏᴍᴇ Tᴏ {} Gʀᴏᴜᴘ ❤️""" 151 | 152 | SHORTLINK_INFO = """ 153 | ──────「 Hᴏᴡ ᴛᴏ Eᴀʀɴ Mᴏɴᴇʏ 」────── 154 | 155 | Yᴏᴜ ᴄᴀɴ Eᴀʀɴ Mᴏɴᴇʏ Fʀᴏᴍ Tʜɪs Bᴏᴛ Uɴᴛɪʟ ᴛʜɪs ʙᴏᴛ ɪs ᴀʟɪᴠᴇ. 156 | 157 | Wᴀɴᴛ ᴛᴏ Kɴᴏᴡ Hᴏᴡ? Fᴏʟʟᴏᴡ Tʜᴇsᴇ Sᴛᴇᴘs:- 158 | 159 | sᴛᴇᴘ 𝟷 : ʏᴏᴜ ᴍᴜsᴛ ʜᴀᴠᴇ ᴀᴛʟᴇᴀsᴛ ᴏɴᴇ ɢʀᴏᴜᴘ ᴡɪᴛʜ ᴍɪɴɪᴍᴜᴍ 1𝟶𝟶 ᴍᴇᴍʙᴇʀs. 160 | 161 | sᴛᴇᴘ 𝟸 : ᴍᴀᴋᴇ ᴀᴄᴄᴏᴜɴᴛ ᴏɴ Aɴʏ Sʜᴏʀᴛᴇɴᴇʀ Wᴇʙsɪᴛᴇ. 162 | 163 | sᴛᴇᴘ 𝟹 : ꜰᴏʟʟᴏᴡ ᴛʜᴇsᴇ ɪɴꜱᴛʀᴜᴄᴛɪᴏɴꜱ Tᴏ ᴄᴏɴɴᴇᴄᴛ sʜᴏʀᴛᴇɴᴇʀ. 164 | 165 | ➣ Yᴏᴜ ᴄᴀɴ ᴄᴏɴɴᴇᴄᴛ ᴀs ᴍᴀɴʏ ɢʀᴏᴜᴘ ʏᴏᴜ ʜᴀᴠᴇ. 166 | 167 | Any Doubts or Not Connecting? Contact Me 168 | """ 169 | 170 | REQINFO = """ 171 | ⚠ ɪɴꜰᴏʀᴍᴀᴛɪᴏɴ ⚠ 172 | 173 | ᴀꜰᴛᴇʀ 5 ᴍɪɴᴜᴛᴇꜱ ᴛʜɪꜱ ᴍᴇꜱꜱᴀɢᴇ ᴡɪʟʟ ʙᴇ ᴀᴜᴛᴏᴍᴀᴛɪᴄᴀʟʟʏ ᴅᴇʟᴇᴛᴇᴅ 174 | 175 | ɪꜰ ʏᴏᴜ ᴅᴏ ɴᴏᴛ ꜱᴇᴇ ᴛʜᴇ ʀᴇǫᴜᴇsᴛᴇᴅ ᴍᴏᴠɪᴇ / sᴇʀɪᴇs ꜰɪʟᴇ, ʟᴏᴏᴋ ᴀᴛ ᴛʜᴇ ɴᴇxᴛ ᴘᴀɢᴇ""" 176 | 177 | SELECT = """ 178 | MOVIES ➢ Sᴇʟᴇᴄᴛ "Lᴀɴɢᴜᴀɢᴇs" 179 | 180 | SERIES ➢ Sᴇʟᴇᴄᴛ "Sᴇᴀsᴏɴs" 181 | 182 | Tɪᴘ: Sᴇʟᴇᴄᴛ "Lᴀɴɢᴜᴀɢᴇs" ᴏʀ "Sᴇᴀsᴏɴs" Bᴜᴛᴛᴏɴ ᴀɴᴅ Cʟɪᴄᴋ "Sᴇɴᴅ Aʟʟ" Tᴏ ɢᴇᴛ Aʟʟ Fɪʟᴇ Lɪɴᴋs ɪɴ ᴀ Sɪɴɢʟᴇ ᴄʟɪᴄᴋ""" 183 | 184 | SINFO = """ 185 | ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ 186 | ꜱᴇʀɪᴇꜱ ʀᴇǫᴜᴇꜱᴛ ꜰᴏʀᴍᴀᴛ 187 | ⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯ 188 | 189 | ɢᴏ ᴛᴏ ɢᴏᴏɢʟᴇ ➠ ᴛʏᴘᴇ ꜱᴇʀɪᴇꜱ ɴᴀᴍᴇ ➠ ᴄᴏᴘʏ ᴄᴏʀʀᴇᴄᴛ ɴᴀᴍᴇ ➠ ᴘᴀꜱᴛᴇ ᴛʜɪꜱ ɢʀᴏᴜᴘ 190 | 191 | ᴇxᴀᴍᴘʟᴇ : Loki S01E01 192 | 193 | 🚯 ᴅᴏɴᴛ ᴜꜱᴇ ➠ ':(!,./)""" 194 | 195 | NORSLTS = """ 196 | ★ #𝗡𝗼𝗥𝗲𝘀𝘂𝗹𝘁𝘀 ★ 197 | 198 | 𝗜𝗗 : {} 199 | 200 | 𝗡𝗮𝗺𝗲 : {} 201 | 202 | 𝗠𝗲𝘀𝘀𝗮𝗴𝗲 : {}""" 203 | 204 | CAPTION = """ 205 | {file_name} 206 | 207 | ⚙️ sɪᴢᴇ : {file_size}""" 208 | 209 | IMDB_TEMPLATE_TXT = """ 210 | Query: {query} 211 | IMDb Data: 212 | 213 | 🏷 Title: {title} 214 | 🎭 Genres: {genres} 215 | 📆 Year: {year} 216 | ⏱️ Result Shown in: {remaining_seconds} seconds 🔥 217 | 🌟 Rating: {rating} / 10""" 218 | 219 | ALL_FILTERS = """ 220 | Hᴇʏ {}, Tʜᴇsᴇ ᴀʀᴇ ᴍʏ ᴛʜʀᴇᴇ ᴛʏᴘᴇs ᴏғ ғɪʟᴛᴇʀs.""" 221 | 222 | GFILTER_TXT = """ 223 | Wᴇʟᴄᴏᴍᴇ ᴛᴏ Gʟᴏʙᴀʟ Fɪʟᴛᴇʀs. Gʟᴏʙᴀʟ Fɪʟᴛᴇʀs ᴀʀᴇ ᴛʜᴇ ғɪʟᴛᴇʀs sᴇᴛ ʙʏ ʙᴏᴛ ᴀᴅᴍɪɴs ᴡʜɪᴄʜ ᴡɪʟʟ ᴡᴏʀᴋ ᴏɴ ᴀʟʟ ɢʀᴏᴜᴘs. 224 | 225 | Aᴠᴀɪʟᴀʙʟᴇ ᴄᴏᴍᴍᴀɴᴅs: 226 | • /gfilter - Tᴏ ᴄʀᴇᴀᴛᴇ ᴀ ɢʟᴏʙᴀʟ ғɪʟᴛᴇʀ. 227 | • /gfilters - Tᴏ ᴠɪᴇᴡ ᴀʟʟ ɢʟᴏʙᴀʟ ғɪʟᴛᴇʀs. 228 | • /delg - Tᴏ ᴅᴇʟᴇᴛᴇ ᴀ ᴘᴀʀᴛɪᴄᴜʟᴀʀ ɢʟᴏʙᴀʟ ғɪʟᴛᴇʀ. 229 | • /delallg - ᴛᴏ ᴅᴇʟᴇᴛᴇ ᴀʟʟ ɢʟᴏʙᴀʟ ꜰɪʟᴛᴇʀꜱ.""" 230 | 231 | FILE_STORE_TXT = """ 232 | Fɪʟᴇ sᴛᴏʀᴇ ɪs ᴛʜᴇ ғᴇᴀᴛᴜʀᴇ ᴡʜɪᴄʜ ᴡɪʟʟ ᴄʀᴇᴀᴛᴇ ᴀ sʜᴀʀᴇᴀʙʟᴇ ʟɪɴᴋ ᴏғ ᴀ sɪɴɢʟᴇ ᴏʀ ᴍᴜʟᴛɪᴘʟᴇ ғɪʟᴇs. 233 | 234 | Aᴠᴀɪʟᴀʙʟᴇ ᴄᴏᴍᴍᴀɴᴅs: 235 | • /batch - Tᴏ ᴄʀᴇᴀᴛᴇ ᴀ ʙᴀᴛᴄʜ ʟɪɴᴋ ᴏғ ᴍᴜʟᴛɪᴘʟᴇ ғɪʟᴇs. 236 | • /link - Tᴏ ᴄʀᴇᴀᴛᴇ ᴀ sɪɴɢʟᴇ ғɪʟᴇ sᴛᴏʀᴇ ʟɪɴᴋ. 237 | • /pbatch - Jᴜsᴛ ʟɪᴋᴇ /batch, ʙᴜᴛ ᴛʜᴇ ғɪʟᴇs ᴡɪʟʟ ʙᴇ sᴇɴᴅ ᴡɪᴛʜ ғᴏʀᴡᴀʀᴅ ʀᴇsᴛʀɪᴄᴛɪᴏɴs. 238 | • /plink - Jᴜsᴛ ʟɪᴋᴇ /link, ʙᴜᴛ ᴛʜᴇ ғɪʟᴇ ᴡɪʟʟ ʙᴇ sᴇɴᴅ ᴡɪᴛʜ ғᴏʀᴡᴀʀᴅ ʀᴇsᴛʀɪᴄᴛɪᴏɴ.""" 239 | 240 | RESTART_TXT = """ 241 | Bᴏᴛ Rᴇsᴛᴀʀᴛᴇᴅ ! 242 | 243 | 📅 Dᴀᴛᴇ : {} 244 | ⏰ Tɪᴍᴇ : {} 245 | 🌐 Tɪᴍᴇᴢᴏɴᴇ : Asia/Kolkata 246 | 🛠️ Bᴜɪʟᴅ Sᴛᴀᴛᴜs: v2.7.1 [ Sᴛᴀʙʟᴇ ]""" 247 | 248 | LOGO = """𝑺𝒕𝒂𝒓𝒕𝒊𝒏𝒈.......🥵""" 249 | 250 | 251 | STICKER_TXT = """yᴏᴜ ᴄᴀɴ ᴜꜱᴇ ᴛʜɪꜱ ᴍᴏᴅᴜʟᴇ ᴛᴏ ꜰɪɴᴅᴀɴy ꜱᴛɪᴄᴋᴇʀꜱ ɪᴅ. 252 | • ᴜꜱᴀɢᴇ :ᴛᴏ ɢᴇᴛ ꜱᴛɪᴄᴋᴇʀ 253 | 254 | ⭕ ʜᴏᴡ ᴛᴏ ᴜꜱᴇ 255 | ◉ Reply To Any Sticker [/stickerid] 256 | 257 | /𝐬𝐭𝐢𝐜𝐤𝐞𝐫𝐢𝐝 𝐬𝐭𝐢𝐜𝐤𝐞𝐫 𝐢𝐝 258 | 259 | """ 260 | 261 | RULE_TXT = """♦ 𝗚𝗿𝗼𝘂𝗽 𝗥𝘂𝗹𝗲𝘀 ♦ 262 | 263 | ◈ Sᴇᴀʀᴄʜ Mᴏᴠɪᴇ Wɪᴛʜ Cᴏʀʀᴇᴄᴛ Sᴘᴇʟʟɪɴɢ: 264 | • ᴀᴠᴀᴛᴀʀ 𝟸𝟶𝟶𝟿 ✅ 265 | • ᴀᴠᴀᴛᴀʀ ʜɪɴᴅɪ ✅ 266 | • ᴀᴠᴀᴛᴀʀ ᴍᴏᴠɪᴇ ❌ 267 | • ᴀᴠᴀᴛᴀʀ ʜɪɴᴅɪ ᴅᴜʙʙᴇᴅ..❌ 268 | 269 | ◈ Sᴇᴀʀᴄʜ Wᴇʙ Sᴇʀɪᴇs Iɴ ᴛʜɪs Fᴏʀᴍᴀᴛ: 270 | • ᴠɪᴋɪɴɢs S𝟶𝟷 ✅ 271 | • ᴠɪᴋɪɴɢs S𝟶𝟷E𝟶𝟷 ✅ 272 | • ᴠɪᴋɪɴɢs S𝟶𝟷 ʜɪɴᴅɪ ✅ 273 | • ᴠɪᴋɪɴɢs S𝟶𝟷 ʜɪɴᴅɪ ᴅᴜʙʙ... ❌ 274 | • ᴠɪᴋɪɴɢs sᴇᴀsᴏɴ 𝟷 ❌ 275 | • ᴠɪᴋɪɴɢs ᴡᴇʙ sᴇʀɪᴇs ❌ 276 | 277 | ➙ ᴅᴏɴ'ᴛ ᴅᴏ ᴀɴʏ ꜱᴇʟꜰ ᴘʀᴏᴍᴏᴛɪᴏɴ. 278 | ➙ ᴅᴏɴ'ᴛ ꜱᴇɴᴅ ᴀɴʏ ᴋɪɴᴅ ᴏꜰ ᴘʜᴏᴛᴏ, ᴠɪᴅᴇᴏ, ᴅᴏᴄᴜᴍᴇɴᴛꜱ, ᴜʀʟ, ᴇᴛᴄ... 279 | ➙ ᴅᴏɴ'ᴛ ʀᴇǫᴜᴇꜱᴛ ᴀɴʏ ᴛʜɪɴɢꜱ ᴏᴛʜᴇʀ ᴛʜᴀɴ ᴍᴏᴠɪᴇꜱ, ꜱᴇʀɪᴇꜱ, ᴀɴɪᴍᴀᴛɪᴏɴ, ᴄᴀʀᴛᴏᴏɴ, ᴀɴɪᴍᴇ, ᴋ-ᴅʀᴀᴍᴀ ᴍᴀɴʏ ᴍᴏʀᴇ. 280 | 281 | 🔰 Nᴏᴛᴇ : ᴀʟʟ ᴍᴇꜱꜱᴀɢᴇꜱ ᴡɪʟʟ ʙᴇ ᴀᴜᴛᴏ-ᴅᴇʟᴇᴛᴇᴅ ᴀꜰᴛᴇʀ 𝟷𝟶 ᴍɪɴᴜᴛᴇꜱ ᴛᴏ ᴀᴠᴏɪᴅ ᴄᴏᴘʏʀɪɢʜᴛ ɪꜱꜱᴜᴇꜱ.""" 282 | 283 | 284 | 285 | SETTINGS_TXT = """ 286 | Hᴇʟᴘ : Sᴇᴛᴛɪɴɢꜱ 287 | 288 | ◈ sᴇᴛᴛɪɴɢs ɪs ᴍᴏsᴛ ɪᴍᴘᴏʀᴛᴀɴᴛ ғᴇᴀᴛᴜʀᴇ ɪɴ ᴛʜɪs ʙᴏᴛ. 289 | ◈ ʏᴏᴜ ᴄᴀɴ ᴇᴀsɪʟʏ ᴄᴜsᴛᴏᴍɪᴢᴇ ᴛʜɪs ʙᴏᴛ ғᴏʀ ʏᴏᴜʀ ɢʀᴏᴜᴘ. 290 | 291 | Nᴏᴛᴇ : 292 | 1. ᴏɴʟʏ ɢʀᴏᴜᴘ ᴀᴅᴍɪɴ ᴄᴀɴ ᴜsᴇ ᴛʜɪs ᴄᴏᴍᴍᴀɴᴅ ᴀɴᴅ ᴄʜᴀɴɢᴇ sᴇᴛᴛɪɴɢs. 293 | 2. ɪᴛ ᴡᴏʀᴋs ᴏɴʟʏ ᴡʜᴇɴ ʙᴏᴛ ᴀʟʀᴇᴀᴅʏ ᴄᴏɴɴᴇᴄᴛᴇᴅ ᴛᴏ ʏᴏᴜʀ ɢʀᴏᴜᴘ. 294 | 295 | Cᴏᴍᴍᴀɴᴅs Aɴᴅ Usᴀɢᴇ : 296 | • /connect - ᴄᴏɴɴᴇᴄᴛ ʏᴏᴜʀ ɢʀᴏᴜᴘ ᴛᴏ ʙᴏᴛ 297 | • /settings - ᴄʜᴀɴɢᴇ sᴇᴛᴛɪɴɢs ᴀs ʏᴏᴜʀ ᴡɪsʜ """ 298 | 299 | TELEGRAPH_TXT = """ Hᴇʟᴘ : Tᴇʟᴇɢʀᴀᴘʜ 300 | 301 | Nᴏᴛᴇ: ᴛʜɪꜱ ᴄᴏᴍᴍᴀɴᴅ ɪꜱ ᴀᴠᴀɪʟᴀʙʟᴇ ɪɴ ɢʀᴏᴜᴘꜱ ᴀɴᴅ ᴘᴍꜱ. ᴀʟꜱᴏ ᴄᴀɴ ʙᴇ ᴜꜱᴇ ʙʏ ᴇᴠᴇʀʏᴏɴᴇ. 302 | 303 | Cᴏᴍᴍᴀɴᴅs & Usᴀɢᴇ : 304 | • /telegraph - sᴇɴᴅ ᴍᴇ ᴘɪᴄᴛᴜʀᴇ ᴏʀ ᴠɪᴅᴇᴏ ᴜɴᴅᴇʀ 𝟻ᴍʙ""" 305 | 306 | FONT_TXT = """Hᴇʟᴘ : Fᴏɴᴛ 307 | 308 | Nᴏᴛᴇ: ʏᴏᴜ ᴄᴀɴ ᴜꜱᴇ ᴛʜɪꜱ ᴍᴏᴅᴇ ᴛᴏ ᴄʜᴀɴɢᴇ ʏᴏᴜʀ ꜰᴏɴᴛꜱ ꜱᴛʏʟᴇ, ᴊᴜꜱᴛ ꜱᴇɴᴅ ᴍᴇ ʟɪᴋᴇ ᴛʜɪꜱ ꜰᴏʀᴍᴀᴛ. 309 | 310 | /font TG_LINKS_CHANNEL""" 311 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ben-Url-Filter-Bot", 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/Singhsawan/PM-Shortner-Bot", 19 | "repository": "https://github.com/Singhsawan/PM-Shortner-Bot", 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": "eco" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from time import sleep, time 3 | from psutil import boot_time, disk_usage, net_io_counters 4 | from subprocess import check_output 5 | from os import path as ospath 6 | app = Flask(__name__) 7 | botStartTime = time() 8 | if ospath.exists('.git'): 9 | commit_date = check_output(["git log -1 --date=format:'%y/%m/%d %H:%M' --pretty=format:'%cd'"], shell=True).decode() 10 | else: 11 | commit_date = 'No UPSTREAM_REPO' 12 | 13 | @app.route('/status', methods=['GET']) 14 | def status(): 15 | bot_uptime = time() - botStartTime 16 | uptime = time() - boot_time() 17 | sent = net_io_counters().bytes_sent 18 | recv = net_io_counters().bytes_recv 19 | return { 20 | 'commit_date': commit_date, 21 | 'uptime': uptime, 22 | 'on_time': bot_uptime, 23 | 'free_disk': disk_usage('.').free, 24 | 'total_disk': disk_usage('.').total, 25 | 'network': { 26 | 'sent': sent, 27 | 'recv': recv, 28 | }, 29 | } 30 | @app.route('/') 31 | def hello_world(): 32 | return 'TGNVS' 33 | 34 | 35 | if __name__ == "__main__": 36 | app.run() 37 | -------------------------------------------------------------------------------- /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, LOG_CHANNEL, PORT 15 | from utils import temp 16 | from typing import Union, Optional, AsyncGenerator 17 | from pyrogram import types 18 | from Script import script 19 | from datetime import date, datetime 20 | import pytz 21 | from aiohttp import web 22 | from plugins import web_server 23 | 24 | class Bot(Client): 25 | 26 | def __init__(self): 27 | super().__init__( 28 | name=SESSION, 29 | api_id=API_ID, 30 | api_hash=API_HASH, 31 | bot_token=BOT_TOKEN, 32 | workers=50, 33 | plugins={"root": "plugins"}, 34 | sleep_threshold=5, 35 | ) 36 | 37 | async def start(self): 38 | b_users, b_chats = await db.get_banned() 39 | temp.BANNED_USERS = b_users 40 | temp.BANNED_CHATS = b_chats 41 | await super().start() 42 | await Media.ensure_indexes() 43 | me = await self.get_me() 44 | temp.ME = me.id 45 | temp.U_NAME = me.username 46 | temp.B_NAME = me.first_name 47 | self.username = '@' + me.username 48 | logging.info(f"{me.first_name} with for Pyrogram v{__version__} (Layer {layer}) started on {me.username}.") 49 | logging.info(LOG_STR) 50 | logging.info(script.LOGO) 51 | tz = pytz.timezone('Asia/Kolkata') 52 | today = date.today() 53 | now = datetime.now(tz) 54 | time = now.strftime("%H:%M:%S %p") 55 | await self.send_message(chat_id=LOG_CHANNEL, text=script.RESTART_TXT.format(today, time)) 56 | app = web.AppRunner(await web_server()) 57 | await app.setup() 58 | bind_address = "0.0.0.0" 59 | await web.TCPSite(app, bind_address, PORT).start() 60 | 61 | async def stop(self, *args): 62 | await super().stop() 63 | logging.info("Bot stopped. Bye.") 64 | 65 | async def iter_messages( 66 | self, 67 | chat_id: Union[int, str], 68 | limit: int, 69 | offset: int = 0, 70 | ) -> Optional[AsyncGenerator["types.Message", None]]: 71 | """Iterate through a chat sequentially. 72 | This convenience method does the same as repeatedly calling :meth:`~pyrogram.Client.get_messages` in a loop, thus saving 73 | you from the hassle of setting up boilerplate code. It is useful for getting the whole chat messages with a 74 | single call. 75 | Parameters: 76 | chat_id (``int`` | ``str``): 77 | Unique identifier (int) or username (str) of the target chat. 78 | For your personal cloud (Saved Messages) you can simply use "me" or "self". 79 | For a contact that exists in your Telegram address book you can use his phone number (str). 80 | 81 | limit (``int``): 82 | Identifier of the last message to be returned. 83 | 84 | offset (``int``, *optional*): 85 | Identifier of the first message to be returned. 86 | Defaults to 0. 87 | Returns: 88 | ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects. 89 | Example: 90 | .. code-block:: python 91 | for message in app.iter_messages("pyrogram", 1, 15000): 92 | print(message.text) 93 | """ 94 | current = offset 95 | while True: 96 | new_diff = min(200, limit - current) 97 | if new_diff <= 0: 98 | return 99 | messages = await self.get_messages(chat_id, list(range(current, current+new_diff+1))) 100 | for message in messages: 101 | yield message 102 | current += 1 103 | 104 | 105 | app = Bot() 106 | app.run() 107 | -------------------------------------------------------------------------------- /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 info import DATABASE_URI, DATABASE_NAME 3 | from pyrogram import enums 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/gfilters_mdb.py: -------------------------------------------------------------------------------- 1 | import pymongo 2 | from info import DATABASE_URI, DATABASE_NAME 3 | from pyrogram import enums 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_gfilter(gfilters, text, reply_text, btn, file, alert): 14 | mycol = mydb[str(gfilters)] 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_gfilter(gfilters, name): 32 | mycol = mydb[str(gfilters)] 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_gfilters(gfilters): 51 | mycol = mydb[str(gfilters)] 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_gfilter(message, text, gfilters): 65 | mycol = mydb[str(gfilters)] 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 gfilter anymore.", 73 | quote=True, 74 | parse_mode=enums.ParseMode.MARKDOWN 75 | ) 76 | else: 77 | await message.reply_text("Couldn't find that gfilter!", quote=True) 78 | 79 | async def del_allg(message, gfilters): 80 | if str(gfilters) not in mydb.list_collection_names(): 81 | await message.edit_text("Nothing to Remove !") 82 | return 83 | 84 | mycol = mydb[str(gfilters)] 85 | try: 86 | mycol.drop() 87 | await message.edit_text(f"All gfilters has been removed !") 88 | except: 89 | await message.edit_text("Couldn't remove all gfilters !") 90 | return 91 | 92 | async def count_gfilters(gfilters): 93 | mycol = mydb[str(gfilters)] 94 | 95 | count = mycol.count() 96 | return False if count == 0 else count 97 | 98 | 99 | async def gfilter_stats(): 100 | collections = mydb.list_collection_names() 101 | 102 | if "CONNECTION" in collections: 103 | collections.remove("CONNECTION") 104 | 105 | totalcount = 0 106 | for collection in collections: 107 | mycol = mydb[collection] 108 | count = mycol.count() 109 | totalcount += count 110 | 111 | totalcollections = len(collections) 112 | 113 | return totalcollections, totalcount 114 | -------------------------------------------------------------------------------- /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, MAX_B_TN 11 | from utils import get_settings, save_group_settings 12 | 13 | logger = logging.getLogger(__name__) 14 | logger.setLevel(logging.INFO) 15 | 16 | 17 | client = AsyncIOMotorClient(DATABASE_URI) 18 | db = client[DATABASE_NAME] 19 | instance = Instance.from_db(db) 20 | 21 | @instance.register 22 | class Media(Document): 23 | file_id = fields.StrField(attribute='_id') 24 | file_ref = fields.StrField(allow_none=True) 25 | file_name = fields.StrField(required=True) 26 | file_size = fields.IntField(required=True) 27 | file_type = fields.StrField(allow_none=True) 28 | mime_type = fields.StrField(allow_none=True) 29 | caption = fields.StrField(allow_none=True) 30 | 31 | class Meta: 32 | indexes = ('$file_name', ) 33 | collection_name = COLLECTION_NAME 34 | 35 | 36 | async def save_file(media): 37 | """Save file in database""" 38 | 39 | # TODO: Find better way to get same file_id for same media to avoid duplicates 40 | file_id, file_ref = unpack_new_file_id(media.file_id) 41 | file_name = re.sub(r"(_|\-|\.|\+)", " ", str(media.file_name)) 42 | try: 43 | file = Media( 44 | file_id=file_id, 45 | file_ref=file_ref, 46 | file_name=file_name, 47 | file_size=media.file_size, 48 | file_type=media.file_type, 49 | mime_type=media.mime_type, 50 | caption=media.caption.html if media.caption else None, 51 | ) 52 | except ValidationError: 53 | logger.exception('Error occurred while saving file in database') 54 | return False, 2 55 | else: 56 | try: 57 | await file.commit() 58 | except DuplicateKeyError: 59 | logger.warning( 60 | f'{getattr(media, "file_name", "NO_FILE")} is already saved in database' 61 | ) 62 | 63 | return False, 0 64 | else: 65 | logger.info(f'{getattr(media, "file_name", "NO_FILE")} is saved to database') 66 | return True, 1 67 | 68 | 69 | 70 | async def get_search_results(chat_id, query, file_type=None, max_results=10, offset=0, filter=False): 71 | """For given query return (results, next_offset)""" 72 | if chat_id is not None: 73 | settings = await get_settings(int(chat_id)) 74 | try: 75 | if settings['max_btn']: 76 | max_results = 10 77 | else: 78 | max_results = int(MAX_B_TN) 79 | except KeyError: 80 | await save_group_settings(int(chat_id), 'max_btn', False) 81 | settings = await get_settings(int(chat_id)) 82 | if settings['max_btn']: 83 | max_results = 10 84 | else: 85 | max_results = int(MAX_B_TN) 86 | query = query.strip() 87 | #if filter: 88 | #better ? 89 | #query = query.replace(' ', r'(\s|\.|\+|\-|_)') 90 | #raw_pattern = r'(\s|_|\-|\.|\+)' + query + r'(\s|_|\-|\.|\+)' 91 | if not query: 92 | raw_pattern = '.' 93 | elif ' ' not in query: 94 | raw_pattern = r'(\b|[\.\+\-_])' + query + r'(\b|[\.\+\-_])' 95 | else: 96 | raw_pattern = query.replace(' ', r'.*[\s\.\+\-_]') 97 | 98 | try: 99 | regex = re.compile(raw_pattern, flags=re.IGNORECASE) 100 | except: 101 | return [] 102 | 103 | if USE_CAPTION_FILTER: 104 | filter = {'$or': [{'file_name': regex}, {'caption': regex}]} 105 | else: 106 | filter = {'file_name': regex} 107 | 108 | if file_type: 109 | filter['file_type'] = file_type 110 | 111 | total_results = await Media.count_documents(filter) 112 | next_offset = offset + max_results 113 | 114 | if next_offset > total_results: 115 | next_offset = '' 116 | 117 | cursor = Media.find(filter) 118 | # Sort by recent 119 | cursor.sort('$natural', -1) 120 | # Slice files according to offset and max results 121 | cursor.skip(offset).limit(max_results) 122 | # Get list of files 123 | files = await cursor.to_list(length=max_results) 124 | 125 | return files, next_offset, total_results 126 | 127 | async def get_bad_files(query, file_type=None, filter=False): 128 | """For given query return (results, next_offset)""" 129 | query = query.strip() 130 | #if filter: 131 | #better ? 132 | #query = query.replace(' ', r'(\s|\.|\+|\-|_)') 133 | #raw_pattern = r'(\s|_|\-|\.|\+)' + query + r'(\s|_|\-|\.|\+)' 134 | if not query: 135 | raw_pattern = '.' 136 | elif ' ' not in query: 137 | raw_pattern = r'(\b|[\.\+\-_])' + query + r'(\b|[\.\+\-_])' 138 | else: 139 | raw_pattern = query.replace(' ', r'.*[\s\.\+\-_]') 140 | 141 | try: 142 | regex = re.compile(raw_pattern, flags=re.IGNORECASE) 143 | except: 144 | return [] 145 | 146 | if USE_CAPTION_FILTER: 147 | filter = {'$or': [{'file_name': regex}, {'caption': regex}]} 148 | else: 149 | filter = {'file_name': regex} 150 | 151 | if file_type: 152 | filter['file_type'] = file_type 153 | 154 | total_results = await Media.count_documents(filter) 155 | 156 | cursor = Media.find(filter) 157 | # Sort by recent 158 | cursor.sort('$natural', -1) 159 | # Get list of files 160 | files = await cursor.to_list(length=total_results) 161 | 162 | return files, total_results 163 | 164 | async def get_file_details(query): 165 | filter = {'file_id': query} 166 | cursor = Media.find(filter) 167 | filedetails = await cursor.to_list(length=1) 168 | return filedetails 169 | 170 | 171 | def encode_file_id(s: bytes) -> str: 172 | r = b"" 173 | n = 0 174 | 175 | for i in s + bytes([22]) + bytes([4]): 176 | if i == 0: 177 | n += 1 178 | else: 179 | if n: 180 | r += b"\x00" + bytes([n]) 181 | n = 0 182 | 183 | r += bytes([i]) 184 | 185 | return base64.urlsafe_b64encode(r).decode().rstrip("=") 186 | 187 | 188 | def encode_file_ref(file_ref: bytes) -> str: 189 | return base64.urlsafe_b64encode(file_ref).decode().rstrip("=") 190 | 191 | 192 | def unpack_new_file_id(new_file_id): 193 | """Return file_id, file_ref""" 194 | decoded = FileId.decode(new_file_id) 195 | file_id = encode_file_id( 196 | pack( 197 | "{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, broadcast_messages_group 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}") 43 | 44 | @Client.on_message(filters.command("grp_broadcast") & filters.user(ADMINS) & filters.reply) 45 | async def broadcast_group(bot, message): 46 | groups = await db.get_all_chats() 47 | b_msg = message.reply_to_message 48 | sts = await message.reply_text( 49 | text='Broadcasting your messages To Groups...' 50 | ) 51 | start_time = time.time() 52 | total_groups = await db.total_chat_count() 53 | done = 0 54 | failed =0 55 | 56 | success = 0 57 | async for group in groups: 58 | pti, sh = await broadcast_messages_group(int(group['id']), b_msg) 59 | if pti: 60 | success += 1 61 | elif sh == "Error": 62 | failed += 1 63 | done += 1 64 | if not done % 20: 65 | await sts.edit(f"Broadcast in progress:\n\nTotal Groups {total_groups}\nCompleted: {done} / {total_groups}\nSuccess: {success}") 66 | time_taken = datetime.timedelta(seconds=int(time.time()-start_time)) 67 | await sts.edit(f"Broadcast Completed:\nCompleted in {time_taken} seconds.\n\nTotal Groups {total_groups}\nCompleted: {done} / {total_groups}\nSuccess: {success}") 68 | 69 | -------------------------------------------------------------------------------- /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/connection.py: -------------------------------------------------------------------------------- 1 | from pyrogram import filters, Client, enums 2 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 3 | from database.connections_mdb import add_connection, all_connections, if_active, delete_connection 4 | from info import ADMINS 5 | import logging 6 | 7 | logger = logging.getLogger(__name__) 8 | logger.setLevel(logging.ERROR) 9 | 10 | 11 | @Client.on_message((filters.private | filters.group) & filters.command('connect')) 12 | async def addconnection(client, message): 13 | userid = message.from_user.id if message.from_user else None 14 | if not userid: 15 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 16 | chat_type = message.chat.type 17 | 18 | if chat_type == enums.ChatType.PRIVATE: 19 | try: 20 | cmd, group_id = message.text.split(" ", 1) 21 | except: 22 | await message.reply_text( 23 | "Enter in correct format!\n\n" 24 | "/connect groupid\n\n" 25 | "Get your Group id by adding this bot to your group and use /id", 26 | quote=True 27 | ) 28 | return 29 | 30 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 31 | group_id = message.chat.id 32 | 33 | try: 34 | st = await client.get_chat_member(group_id, userid) 35 | if ( 36 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 37 | and st.status != enums.ChatMemberStatus.OWNER 38 | and userid not in ADMINS 39 | ): 40 | await message.reply_text("You should be an admin in Given group!", quote=True) 41 | return 42 | except Exception as e: 43 | logger.exception(e) 44 | await message.reply_text( 45 | "Invalid Group ID!\n\nIf correct, Make sure I'm present in your group!!", 46 | quote=True, 47 | ) 48 | 49 | return 50 | try: 51 | st = await client.get_chat_member(group_id, "me") 52 | if st.status == enums.ChatMemberStatus.ADMINISTRATOR: 53 | ttl = await client.get_chat(group_id) 54 | title = ttl.title 55 | 56 | addcon = await add_connection(str(group_id), str(userid)) 57 | if addcon: 58 | await message.reply_text( 59 | f"Successfully connected to **{title}**\nNow manage your group from my pm !", 60 | quote=True, 61 | parse_mode=enums.ParseMode.MARKDOWN 62 | ) 63 | if chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 64 | await client.send_message( 65 | userid, 66 | f"Connected to **{title}** !", 67 | parse_mode=enums.ParseMode.MARKDOWN 68 | ) 69 | else: 70 | await message.reply_text( 71 | "You're already connected to this chat!", 72 | quote=True 73 | ) 74 | else: 75 | await message.reply_text("Add me as an admin in group", quote=True) 76 | except Exception as e: 77 | logger.exception(e) 78 | await message.reply_text('Some error occurred! Try again later.', quote=True) 79 | return 80 | 81 | 82 | @Client.on_message((filters.private | filters.group) & filters.command('disconnect')) 83 | async def deleteconnection(client, message): 84 | userid = message.from_user.id if message.from_user else None 85 | if not userid: 86 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 87 | chat_type = message.chat.type 88 | 89 | if chat_type == enums.ChatType.PRIVATE: 90 | await message.reply_text("Run /connections to view or disconnect from groups!", quote=True) 91 | 92 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 93 | group_id = message.chat.id 94 | 95 | st = await client.get_chat_member(group_id, userid) 96 | if ( 97 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 98 | and st.status != enums.ChatMemberStatus.OWNER 99 | and str(userid) not in ADMINS 100 | ): 101 | return 102 | 103 | delcon = await delete_connection(str(userid), str(group_id)) 104 | if delcon: 105 | await message.reply_text("Successfully disconnected from this chat", quote=True) 106 | else: 107 | await message.reply_text("This chat isn't connected to me!\nDo /connect to connect.", quote=True) 108 | 109 | 110 | @Client.on_message(filters.private & filters.command(["connections"])) 111 | async def connections(client, message): 112 | userid = message.from_user.id 113 | 114 | groupids = await all_connections(str(userid)) 115 | if groupids is None: 116 | await message.reply_text( 117 | "There are no active connections!! Connect to some groups first.", 118 | quote=True 119 | ) 120 | return 121 | buttons = [] 122 | for groupid in groupids: 123 | try: 124 | ttl = await client.get_chat(int(groupid)) 125 | title = ttl.title 126 | active = await if_active(str(userid), str(groupid)) 127 | act = " - ACTIVE" if active else "" 128 | buttons.append( 129 | [ 130 | InlineKeyboardButton( 131 | text=f"{title}{act}", callback_data=f"groupcb:{groupid}:{act}" 132 | ) 133 | ] 134 | ) 135 | except: 136 | pass 137 | if buttons: 138 | await message.reply_text( 139 | "Your connected group details ;\n\n", 140 | reply_markup=InlineKeyboardMarkup(buttons), 141 | quote=True 142 | ) 143 | else: 144 | await message.reply_text( 145 | "There are no active connections!! Connect to some groups first.", 146 | quote=True 147 | ) 148 | -------------------------------------------------------------------------------- /plugins/files_delete.py: -------------------------------------------------------------------------------- 1 | import re 2 | import logging 3 | from pyrogram import Client, filters 4 | from info import DELETE_CHANNELS 5 | from database.ia_filterdb import Media, unpack_new_file_id 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | media_filter = filters.document | filters.video | filters.audio 10 | 11 | 12 | @Client.on_message(filters.chat(DELETE_CHANNELS) & media_filter) 13 | async def deletemultiplemedia(bot, message): 14 | """Delete Multiple files from database""" 15 | 16 | for file_type in ("document", "video", "audio"): 17 | media = getattr(message, file_type, None) 18 | if media is not None: 19 | break 20 | else: 21 | return 22 | 23 | file_id, file_ref = unpack_new_file_id(media.file_id) 24 | 25 | result = await Media.collection.delete_one({ 26 | '_id': file_id, 27 | }) 28 | if result.deleted_count: 29 | logger.info('File is successfully deleted from database.') 30 | else: 31 | file_name = re.sub(r"(_|\-|\.|\+)", " ", str(media.file_name)) 32 | result = await Media.collection.delete_many({ 33 | 'file_name': file_name, 34 | 'file_size': media.file_size, 35 | 'mime_type': media.mime_type 36 | }) 37 | if result.deleted_count: 38 | logger.info('File is successfully deleted from database.') 39 | else: 40 | result = await Media.collection.delete_many({ 41 | 'file_name': media.file_name, 42 | 'file_size': media.file_size, 43 | 'mime_type': media.mime_type 44 | }) 45 | if result.deleted_count: 46 | logger.info('File is successfully deleted from database.') 47 | else: 48 | logger.info('File not found in database.') 49 | -------------------------------------------------------------------------------- /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 | userid = message.from_user.id 128 | grpid = await active_connection(str(userid)) 129 | if grpid is not None: 130 | grp_id = grpid 131 | try: 132 | chat = await client.get_chat(grpid) 133 | title = chat.title 134 | except: 135 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 136 | return 137 | else: 138 | await message.reply_text("I'm not connected to any groups!", quote=True) 139 | return 140 | 141 | elif chat_type in [enums.ChatType.GROUP, enums.ChatType.SUPERGROUP]: 142 | grp_id = message.chat.id 143 | title = message.chat.title 144 | 145 | else: 146 | return 147 | 148 | st = await client.get_chat_member(grp_id, userid) 149 | if ( 150 | st.status != enums.ChatMemberStatus.ADMINISTRATOR 151 | and st.status != enums.ChatMemberStatus.OWNER 152 | and str(userid) not in ADMINS 153 | ): 154 | return 155 | 156 | texts = await get_filters(grp_id) 157 | count = await count_filters(grp_id) 158 | if count: 159 | filterlist = f"Total number of filters in **{title}** : {count}\n\n" 160 | 161 | for text in texts: 162 | keywords = " × `{}`\n".format(text) 163 | 164 | filterlist += keywords 165 | 166 | if len(filterlist) > 4096: 167 | with io.BytesIO(str.encode(filterlist.replace("`", ""))) as keyword_file: 168 | keyword_file.name = "keywords.txt" 169 | await message.reply_document( 170 | document=keyword_file, 171 | quote=True 172 | ) 173 | return 174 | else: 175 | filterlist = f"There are no active filters in **{title}**" 176 | 177 | await message.reply_text( 178 | text=filterlist, 179 | quote=True, 180 | parse_mode=enums.ParseMode.MARKDOWN 181 | ) 182 | 183 | @Client.on_message(filters.command('del') & filters.incoming) 184 | async def deletefilter(client, message): 185 | userid = message.from_user.id if message.from_user else None 186 | if not userid: 187 | return await message.reply(f"You are anonymous admin. Use /connect {message.chat.id} in PM") 188 | chat_type = message.chat.type 189 | 190 | if chat_type == enums.ChatType.PRIVATE: 191 | grpid = await active_connection(str(userid)) 192 | if grpid is not None: 193 | grp_id = grpid 194 | try: 195 | chat = await client.get_chat(grpid) 196 | title = chat.title 197 | except: 198 | await message.reply_text("Make sure I'm present in your group!!", quote=True) 199 | return 200 | else: 201 | await message.reply_text("I'm not connected to any groups!", quote=True) 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 | st = await client.get_chat_member(grp_id, userid) 263 | if (st.status == enums.ChatMemberStatus.OWNER) or (str(userid) in ADMINS): 264 | await message.reply_text( 265 | f"This will delete all filters from '{title}'.\nDo you want to continue??", 266 | reply_markup=InlineKeyboardMarkup([ 267 | [InlineKeyboardButton(text="YES",callback_data="delallconfirm")], 268 | [InlineKeyboardButton(text="CANCEL",callback_data="delallcancel")] 269 | ]), 270 | quote=True 271 | ) 272 | 273 | -------------------------------------------------------------------------------- /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/gfilters.py: -------------------------------------------------------------------------------- 1 | import io 2 | from pyrogram import filters, Client, enums 3 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup 4 | from database.gfilters_mdb import( 5 | add_gfilter, 6 | get_gfilters, 7 | delete_gfilter, 8 | count_gfilters 9 | ) 10 | 11 | from database.connections_mdb import active_connection 12 | from utils import get_file_id, gfilterparser, split_quotes 13 | from info import ADMINS 14 | 15 | 16 | @Client.on_message(filters.command(['gfilter', 'addg']) & filters.incoming & filters.user(ADMINS)) 17 | async def addgfilter(client, message): 18 | args = message.text.html.split(None, 1) 19 | 20 | if len(args) < 2: 21 | await message.reply_text("Command Incomplete :(", quote=True) 22 | return 23 | 24 | extracted = split_quotes(args[1]) 25 | text = extracted[0].lower() 26 | 27 | if not message.reply_to_message and len(extracted) < 2: 28 | await message.reply_text("Add some content to save your filter!", quote=True) 29 | return 30 | 31 | if (len(extracted) >= 2) and not message.reply_to_message: 32 | reply_text, btn, alert = gfilterparser(extracted[1], text) 33 | fileid = None 34 | if not reply_text: 35 | await message.reply_text("You cannot have buttons alone, give some text to go with it!", quote=True) 36 | return 37 | 38 | elif message.reply_to_message and message.reply_to_message.reply_markup: 39 | try: 40 | rm = message.reply_to_message.reply_markup 41 | btn = rm.inline_keyboard 42 | msg = get_file_id(message.reply_to_message) 43 | if msg: 44 | fileid = msg.file_id 45 | reply_text = message.reply_to_message.caption.html 46 | else: 47 | reply_text = message.reply_to_message.text.html 48 | fileid = None 49 | alert = None 50 | except: 51 | reply_text = "" 52 | btn = "[]" 53 | fileid = None 54 | alert = None 55 | 56 | elif message.reply_to_message and message.reply_to_message.media: 57 | try: 58 | msg = get_file_id(message.reply_to_message) 59 | fileid = msg.file_id if msg else None 60 | reply_text, btn, alert = gfilterparser(extracted[1], text) if message.reply_to_message.sticker else gfilterparser(message.reply_to_message.caption.html, text) 61 | except: 62 | reply_text = "" 63 | btn = "[]" 64 | alert = None 65 | elif message.reply_to_message and message.reply_to_message.text: 66 | try: 67 | fileid = None 68 | reply_text, btn, alert = gfilterparser(message.reply_to_message.text.html, text) 69 | except: 70 | reply_text = "" 71 | btn = "[]" 72 | alert = None 73 | else: 74 | return 75 | 76 | await add_gfilter('gfilters', text, reply_text, btn, fileid, alert) 77 | 78 | await message.reply_text( 79 | f"GFilter for `{text}` added", 80 | quote=True, 81 | parse_mode=enums.ParseMode.MARKDOWN 82 | ) 83 | 84 | 85 | @Client.on_message(filters.command(['viewgfilters', 'gfilters']) & filters.incoming & filters.user(ADMINS)) 86 | async def get_all_gfilters(client, message): 87 | texts = await get_gfilters('gfilters') 88 | count = await count_gfilters('gfilters') 89 | if count: 90 | gfilterlist = f"Total number of gfilters : {count}\n\n" 91 | 92 | for text in texts: 93 | keywords = " × `{}`\n".format(text) 94 | 95 | gfilterlist += keywords 96 | 97 | if len(gfilterlist) > 4096: 98 | with io.BytesIO(str.encode(gfilterlist.replace("`", ""))) as keyword_file: 99 | keyword_file.name = "keywords.txt" 100 | await message.reply_document( 101 | document=keyword_file, 102 | quote=True 103 | ) 104 | return 105 | else: 106 | gfilterlist = f"There are no active gfilters." 107 | 108 | await message.reply_text( 109 | text=gfilterlist, 110 | quote=True, 111 | parse_mode=enums.ParseMode.MARKDOWN 112 | ) 113 | 114 | @Client.on_message(filters.command('delg') & filters.incoming & filters.user(ADMINS)) 115 | async def deletegfilter(client, message): 116 | try: 117 | cmd, text = message.text.split(" ", 1) 118 | except: 119 | await message.reply_text( 120 | "Mention the gfiltername which you wanna delete!\n\n" 121 | "/delg gfiltername\n\n" 122 | "Use /viewgfilters to view all available gfilters", 123 | quote=True 124 | ) 125 | return 126 | 127 | query = text.lower() 128 | 129 | await delete_gfilter(message, query, 'gfilters') 130 | 131 | @Client.on_message(filters.command('delallg') & filters.user(ADMINS)) 132 | async def delallgfilters(client, message): 133 | await message.reply_text( 134 | f"Do you want to continue??", 135 | reply_markup=InlineKeyboardMarkup([ 136 | [InlineKeyboardButton(text="YES",callback_data="gfiltersdeleteallconfirm")], 137 | [InlineKeyboardButton(text="CANCEL",callback_data="gfiltersdeleteallcancel")] 138 | ]), 139 | quote=True 140 | ) 141 | -------------------------------------------------------------------------------- /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 is_subscribed, get_size, temp 7 | from info import CACHE_TIME, AUTH_USERS, AUTH_CHANNEL, CUSTOM_FILE_CAPTION 8 | from database.connections_mdb import active_connection 9 | 10 | logger = logging.getLogger(__name__) 11 | cache_time = 0 if AUTH_USERS or AUTH_CHANNEL else CACHE_TIME 12 | 13 | async def inline_users(query: InlineQuery): 14 | if AUTH_USERS: 15 | if query.from_user and query.from_user.id in AUTH_USERS: 16 | return True 17 | else: 18 | return False 19 | if query.from_user and query.from_user.id not in temp.BANNED_USERS: 20 | return True 21 | return False 22 | 23 | @Client.on_inline_query() 24 | async def answer(bot, query): 25 | """Show search results for given inline query""" 26 | chat_id = await active_connection(str(query.from_user.id)) 27 | 28 | if not await inline_users(query): 29 | await query.answer(results=[], 30 | cache_time=0, 31 | switch_pm_text='okDa', 32 | switch_pm_parameter="hehe") 33 | return 34 | 35 | if AUTH_CHANNEL and not await is_subscribed(bot, query): 36 | await query.answer(results=[], 37 | cache_time=0, 38 | switch_pm_text='You have to subscribe my channel to use the bot', 39 | switch_pm_parameter="subscribe") 40 | return 41 | 42 | results = [] 43 | if '|' in query.query: 44 | string, file_type = query.query.split('|', maxsplit=1) 45 | string = string.strip() 46 | file_type = file_type.strip().lower() 47 | else: 48 | string = query.query.strip() 49 | file_type = None 50 | 51 | offset = int(query.offset or 0) 52 | reply_markup = get_reply_markup(query=string) 53 | files, next_offset, total = await get_search_results( 54 | chat_id, 55 | string, 56 | file_type=file_type, 57 | max_results=10, 58 | offset=offset) 59 | 60 | for file in files: 61 | title=file.file_name 62 | size=get_size(file.file_size) 63 | f_caption=file.caption 64 | if CUSTOM_FILE_CAPTION: 65 | try: 66 | 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) 67 | except Exception as e: 68 | logger.exception(e) 69 | f_caption=f_caption 70 | if f_caption is None: 71 | f_caption = f"{file.file_name}" 72 | results.append( 73 | InlineQueryResultCachedDocument( 74 | title=file.file_name, 75 | document_file_id=file.file_id, 76 | caption=f_caption, 77 | description=f'Size: {get_size(file.file_size)}\nType: {file.file_type}', 78 | reply_markup=reply_markup)) 79 | 80 | if results: 81 | switch_pm_text = f"{emoji.FILE_FOLDER} Results - {total}" 82 | if string: 83 | switch_pm_text += f" for {string}" 84 | try: 85 | await query.answer(results=results, 86 | is_personal = True, 87 | cache_time=cache_time, 88 | switch_pm_text=switch_pm_text, 89 | switch_pm_parameter="start", 90 | next_offset=str(next_offset)) 91 | except QueryIdInvalid: 92 | pass 93 | except Exception as e: 94 | logging.exception(str(e)) 95 | else: 96 | switch_pm_text = f'{emoji.CROSS_MARK} No results' 97 | if string: 98 | switch_pm_text += f' for "{string}"' 99 | 100 | await query.answer(results=[], 101 | is_personal = True, 102 | cache_time=cache_time, 103 | switch_pm_text=switch_pm_text, 104 | switch_pm_parameter="okay") 105 | 106 | 107 | def get_reply_markup(query): 108 | buttons = [ 109 | [ 110 | InlineKeyboardButton('Search again', switch_inline_query_current_chat=query) 111 | ] 112 | ] 113 | return InlineKeyboardMarkup(buttons) 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /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, CallbackQuery 3 | from pyrogram.errors.exceptions.bad_request_400 import MessageTooLong, PeerIdInvalid 4 | from info import ADMINS, LOG_CHANNEL, SUPPORT_CHAT, MELCOW_NEW_USERS, MELCOW_VID, CHNL_LNK, GRP_LNK 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 | import asyncio 11 | 12 | """-----------------------------------------https://t.me/GetTGLink/4179 --------------------------------------""" 13 | 14 | @Client.on_message(filters.new_chat_members & filters.group) 15 | async def save_group(bot, message): 16 | r_j_check = [u.id for u in message.new_chat_members] 17 | if temp.ME in r_j_check: 18 | if not await db.get_chat(message.chat.id): 19 | total=await bot.get_chat_members_count(message.chat.id) 20 | r_j = message.from_user.mention if message.from_user else "Anonymous" 21 | await bot.send_message(LOG_CHANNEL, script.LOG_TEXT_G.format(message.chat.title, message.chat.id, total, r_j)) 22 | await db.add_chat(message.chat.id, message.chat.title) 23 | if message.chat.id in temp.BANNED_CHATS: 24 | # Inspired from a boat of a banana tree 25 | buttons = [[ 26 | InlineKeyboardButton('Support', url=f'https://t.me/{SUPPORT_CHAT}') 27 | ]] 28 | reply_markup=InlineKeyboardMarkup(buttons) 29 | k = await message.reply( 30 | text='CHAT NOT ALLOWED 🐞\n\nMy admins has restricted me from working here ! If you want to know more about it contact support..', 31 | reply_markup=reply_markup, 32 | ) 33 | 34 | try: 35 | await k.pin() 36 | except: 37 | pass 38 | await bot.leave_chat(message.chat.id) 39 | return 40 | buttons = [[ 41 | InlineKeyboardButton('Sᴜᴘᴘᴏʀᴛ Gʀᴏᴜᴘ', url=GRP_LNK), 42 | InlineKeyboardButton('Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ', url=CHNL_LNK) 43 | ],[ 44 | InlineKeyboardButton("Bᴏᴛ Oᴡɴᴇʀ", url="t.me/J_shree_ram") 45 | ]] 46 | reply_markup=InlineKeyboardMarkup(buttons) 47 | await message.reply_text( 48 | text=f"Thankyou For Adding Me In {message.chat.title} ❣️\n\nIf you have any questions & doubts about using me contact support.", 49 | reply_markup=reply_markup) 50 | else: 51 | settings = await get_settings(message.chat.id) 52 | if settings["welcome"]: 53 | for u in message.new_chat_members: 54 | if (temp.MELCOW).get('welcome') is not None: 55 | try: 56 | await (temp.MELCOW['welcome']).delete() 57 | except: 58 | pass 59 | temp.MELCOW['welcome'] = await message.reply_video( 60 | video=(MELCOW_VID), 61 | caption=(script.MELCOW_ENG.format(u.mention, message.chat.title)), 62 | reply_markup=InlineKeyboardMarkup( 63 | [[ 64 | InlineKeyboardButton('Sᴜᴘᴘᴏʀᴛ Gʀᴏᴜᴘ', url=GRP_LNK), 65 | InlineKeyboardButton('Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ', url=CHNL_LNK) 66 | ],[ 67 | InlineKeyboardButton("Bᴏᴛ Oᴡɴᴇʀ", url="t.me/J_shree_ram") 68 | ]] 69 | ), 70 | parse_mode=enums.ParseMode.HTML 71 | ) 72 | 73 | if settings["auto_delete"]: 74 | await asyncio.sleep(600) 75 | await (temp.MELCOW['welcome']).delete() 76 | 77 | 78 | 79 | 80 | 81 | @Client.on_message(filters.command('leave') & filters.user(ADMINS)) 82 | async def leave_a_chat(bot, message): 83 | if len(message.command) == 1: 84 | return await message.reply('Give me a chat id') 85 | chat = message.command[1] 86 | try: 87 | chat = int(chat) 88 | except: 89 | chat = chat 90 | try: 91 | buttons = [[ 92 | InlineKeyboardButton('Support Group', url="https://t.me/+4nzja42ELQwzOWVl"), 93 | InlineKeyboardButton('Owner', url="https://t.me/J_shree_ram") 94 | ],[ 95 | InlineKeyboardButton('Use Me Here', url=f'https://t.me/{SUPPORT_CHAT}') 96 | ]] 97 | reply_markup=InlineKeyboardMarkup(buttons) 98 | await bot.send_message( 99 | chat_id=chat, 100 | text='Hello Friends, \nMy admin has told me to leave from group, so i go! If you wanna add me again contact my Support Group or My Owner', 101 | reply_markup=reply_markup, 102 | ) 103 | 104 | await bot.leave_chat(chat) 105 | await message.reply(f"left the chat `{chat}`") 106 | except Exception as e: 107 | await message.reply(f'Error - {e}') 108 | 109 | @Client.on_message(filters.command('disable') & filters.user(ADMINS)) 110 | async def disable_chat(bot, message): 111 | if len(message.command) == 1: 112 | return await message.reply('Give me a chat id') 113 | r = message.text.split(None) 114 | if len(r) > 2: 115 | reason = message.text.split(None, 2)[2] 116 | chat = message.text.split(None, 2)[1] 117 | else: 118 | chat = message.command[1] 119 | reason = "No reason Provided" 120 | try: 121 | chat_ = int(chat) 122 | except: 123 | return await message.reply('Give Me A Valid Chat ID') 124 | cha_t = await db.get_chat(int(chat_)) 125 | if not cha_t: 126 | return await message.reply("Chat Not Found In DB") 127 | if cha_t['is_disabled']: 128 | return await message.reply(f"This chat is already disabled:\nReason- {cha_t['reason']} ") 129 | await db.disable_chat(int(chat_), reason) 130 | temp.BANNED_CHATS.append(int(chat_)) 131 | await message.reply('Chat Successfully Disabled') 132 | try: 133 | buttons = [[ 134 | InlineKeyboardButton('Support', url=f'https://t.me/{SUPPORT_CHAT}') 135 | ]] 136 | reply_markup=InlineKeyboardMarkup(buttons) 137 | await bot.send_message( 138 | chat_id=chat_, 139 | text=f'Hello Friends, \nMy admin has told me to leave from group so i go! If you wanna add me again contact my support group. \nReason : {reason}', 140 | reply_markup=reply_markup) 141 | await bot.leave_chat(chat_) 142 | except Exception as e: 143 | await message.reply(f"Error - {e}") 144 | 145 | 146 | @Client.on_message(filters.command('enable') & filters.user(ADMINS)) 147 | async def re_enable_chat(bot, message): 148 | if len(message.command) == 1: 149 | return await message.reply('Give me a chat id') 150 | chat = message.command[1] 151 | try: 152 | chat_ = int(chat) 153 | except: 154 | return await message.reply('Give Me A Valid Chat ID') 155 | sts = await db.get_chat(int(chat)) 156 | if not sts: 157 | return await message.reply("Chat Not Found In DB !") 158 | if not sts.get('is_disabled'): 159 | return await message.reply('This chat is not yet disabled.') 160 | await db.re_enable_chat(int(chat_)) 161 | temp.BANNED_CHATS.remove(int(chat_)) 162 | await message.reply("Chat Successfully re-enabled") 163 | 164 | 165 | @Client.on_message(filters.command('stats') & filters.incoming) 166 | async def get_ststs(bot, message): 167 | rju = await message.reply('Fetching stats..') 168 | total_users = await db.total_users_count() 169 | totl_chats = await db.total_chat_count() 170 | files = await Media.count_documents() 171 | size = await db.get_db_size() 172 | free = 536870912 - size 173 | size = get_size(size) 174 | free = get_size(free) 175 | await rju.edit(script.STATUS_TXT.format(files, total_users, totl_chats, size, free)) 176 | 177 | 178 | @Client.on_message(filters.command('invite') & filters.user(ADMINS)) 179 | async def gen_invite(bot, message): 180 | if len(message.command) == 1: 181 | return await message.reply('Give me a chat id') 182 | chat = message.command[1] 183 | try: 184 | chat = int(chat) 185 | except: 186 | return await message.reply('Give Me A Valid Chat ID') 187 | try: 188 | link = await bot.create_chat_invite_link(chat) 189 | except ChatAdminRequired: 190 | return await message.reply("Invite Link Generation Failed, Iam Not Having Sufficient Rights") 191 | except Exception as e: 192 | return await message.reply(f'Error {e}') 193 | await message.reply(f'Here is your Invite Link {link.invite_link}') 194 | 195 | @Client.on_message(filters.command('ban') & filters.user(ADMINS)) 196 | async def ban_a_user(bot, message): 197 | # https://t.me/GetTGLink/4185 198 | if len(message.command) == 1: 199 | return await message.reply('Give me a user id / username') 200 | r = message.text.split(None) 201 | if len(r) > 2: 202 | reason = message.text.split(None, 2)[2] 203 | chat = message.text.split(None, 2)[1] 204 | else: 205 | chat = message.command[1] 206 | reason = "No reason Provided" 207 | try: 208 | chat = int(chat) 209 | except: 210 | pass 211 | try: 212 | k = await bot.get_users(chat) 213 | except PeerIdInvalid: 214 | return await message.reply("This is an invalid user, make sure ia have met him before.") 215 | except IndexError: 216 | return await message.reply("This might be a channel, make sure its a user.") 217 | except Exception as e: 218 | return await message.reply(f'Error - {e}') 219 | else: 220 | jar = await db.get_ban_status(k.id) 221 | if jar['is_banned']: 222 | return await message.reply(f"{k.mention} is already banned\nReason: {jar['ban_reason']}") 223 | await db.ban_user(k.id, reason) 224 | temp.BANNED_USERS.append(k.id) 225 | await message.reply(f"Successfully banned {k.mention}") 226 | 227 | 228 | 229 | @Client.on_message(filters.command('unban') & filters.user(ADMINS)) 230 | async def unban_a_user(bot, message): 231 | if len(message.command) == 1: 232 | return await message.reply('Give me a user id / username') 233 | r = message.text.split(None) 234 | if len(r) > 2: 235 | reason = message.text.split(None, 2)[2] 236 | chat = message.text.split(None, 2)[1] 237 | else: 238 | chat = message.command[1] 239 | reason = "No reason Provided" 240 | try: 241 | chat = int(chat) 242 | except: 243 | pass 244 | try: 245 | k = await bot.get_users(chat) 246 | except PeerIdInvalid: 247 | return await message.reply("This is an invalid user, make sure ia have met him before.") 248 | except IndexError: 249 | return await message.reply("Thismight be a channel, make sure its a user.") 250 | except Exception as e: 251 | return await message.reply(f'Error - {e}') 252 | else: 253 | jar = await db.get_ban_status(k.id) 254 | if not jar['is_banned']: 255 | return await message.reply(f"{k.mention} is not yet banned.") 256 | await db.remove_ban(k.id) 257 | temp.BANNED_USERS.remove(k.id) 258 | await message.reply(f"Successfully unbanned {k.mention}") 259 | 260 | 261 | 262 | @Client.on_message(filters.command('users') & filters.user(ADMINS)) 263 | async def list_users(bot, message): 264 | # https://t.me/GetTGLink/4184 265 | raju = await message.reply('Getting List Of Users') 266 | users = await db.get_all_users() 267 | out = "Users Saved In DB Are:\n\n" 268 | async for user in users: 269 | out += f"{user['name']}" 270 | if user['ban_status']['is_banned']: 271 | out += '( Banned User )' 272 | out += '\n' 273 | try: 274 | await raju.edit_text(out) 275 | except MessageTooLong: 276 | with open('users.txt', 'w+') as outfile: 277 | outfile.write(out) 278 | await message.reply_document('users.txt', caption="List Of Users") 279 | 280 | @Client.on_message(filters.command('chats') & filters.user(ADMINS)) 281 | async def list_chats(bot, message): 282 | raju = await message.reply('Getting List Of chats') 283 | chats = await db.get_all_chats() 284 | out = "Chats Saved In DB Are:\n\n" 285 | async for chat in chats: 286 | out += f"**Title:** `{chat['title']}`\n**- ID:** `{chat['id']}`" 287 | if chat['chat_status']['is_disabled']: 288 | out += '( Disabled Chat )' 289 | out += '\n' 290 | try: 291 | await raju.edit_text(out) 292 | except MessageTooLong: 293 | with open('chats.txt', 'w+') as outfile: 294 | outfile.write(out) 295 | await message.reply_document('chats.txt', caption="List Of Chats") 296 | -------------------------------------------------------------------------------- /plugins/route.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | 3 | routes = web.RouteTableDef() 4 | 5 | @routes.get("/", allow_head=True) 6 | async def root_route_handler(request): 7 | return web.json_response("BenFilterBot") 8 | -------------------------------------------------------------------------------- /plugins/sample.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # A Docker web service 3 | - type: web 4 | name: ben-url-filter-bot 5 | env: python 6 | startCommand: python3 bot.py 7 | buildCommand: pip3 install -U -r requirements.txt 8 | repo: https://github.com/Moviesbotz/ben-url-filter-bot.git # optional 9 | region: oregon # optional (defaults to oregon) 10 | plan: free # optional (defaults to starter) 11 | branch: web # optional (defaults to master) 12 | numInstances: 1 # optional (defaults to 1) 13 | healthCheckPath: / 14 | 15 | #End of yaml 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram 2 | tgcrypto 3 | pymongo[srv] 4 | motor 5 | marshmallow 6 | umongo 7 | requests 8 | bs4 9 | git+https://github.com/Joelkb/cinemagoer 10 | shortzy 11 | datetime 12 | pytz 13 | Flask 14 | gunicorn 15 | aiohttp 16 | 17 | 18 | pyrogram 19 | pyrofork 20 | tgcrypto 21 | pymongo[srv] 22 | motor 23 | marshmallow 24 | umongo 25 | requests 26 | bs4 27 | git+https://github.com/Joelkb/cinemagoer 28 | shortzy 29 | datetime 30 | pytz 31 | Flask 32 | gunicorn 33 | aiohttp 34 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.10.9 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 | gunicorn app:app & python3 bot.py 2 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from pyrogram.errors import ( 3 | InputUserDeactivated, 4 | UserNotParticipant, 5 | FloodWait, 6 | UserIsBlocked, 7 | PeerIdInvalid, 8 | ) 9 | from info import ( 10 | AUTH_CHANNEL, 11 | LONG_IMDB_DESCRIPTION, 12 | MAX_LIST_ELM, 13 | SHORTLINK_URL, 14 | SHORTLINK_API, 15 | IS_SHORTLINK, 16 | LOG_CHANNEL, 17 | TUTORIAL, 18 | GRP_LNK, 19 | CHNL_LNK, 20 | CUSTOM_FILE_CAPTION, 21 | SECOND_SHORTLINK_URL, 22 | SECOND_SHORTLINK_API, 23 | STREAM_URL, 24 | STREAM_BIN, 25 | IS_STREAM, 26 | ) 27 | from imdb import Cinemagoer 28 | import asyncio 29 | from pyrogram.types import Message, InlineKeyboardButton, InlineKeyboardMarkup 30 | from pyrogram.errors import FloodWait, UserIsBlocked, MessageNotModified, PeerIdInvalid 31 | from pyrogram import enums 32 | from typing import Union 33 | from Script import script 34 | import pytz 35 | import random 36 | import re 37 | import os 38 | from datetime import datetime, date 39 | import string 40 | from typing import List 41 | from database.users_chats_db import db 42 | from bs4 import BeautifulSoup 43 | import requests 44 | import aiohttp 45 | from shortzy import Shortzy 46 | import http.client 47 | import json 48 | 49 | logger = logging.getLogger(__name__) 50 | logger.setLevel(logging.INFO) 51 | 52 | BTN_URL_REGEX = re.compile( 53 | r"(\[([^\[]+?)\]\((buttonurl|buttonalert):(?:/{0,2})(.+?)(:same)?\))" 54 | ) 55 | 56 | imdb = Cinemagoer() 57 | TOKENS = {} 58 | VERIFIED = {} 59 | BANNED = {} 60 | SECOND_SHORTENER = {} 61 | SMART_OPEN = "“" 62 | SMART_CLOSE = "”" 63 | START_CHAR = ("'", '"', SMART_OPEN) 64 | 65 | 66 | # temp db for banned 67 | class temp(object): 68 | BANNED_USERS = [] 69 | BANNED_CHATS = [] 70 | ME = None 71 | CURRENT = int(os.environ.get("SKIP", 2)) 72 | CANCEL = False 73 | MELCOW = {} 74 | U_NAME = None 75 | B_NAME = None 76 | GETALL = {} 77 | SHORT = {} 78 | SETTINGS = {} 79 | 80 | 81 | async def is_subscribed(bot, query=None, userid=None): 82 | try: 83 | if userid == None and query != None: 84 | user = await bot.get_chat_member(AUTH_CHANNEL, query.from_user.id) 85 | else: 86 | user = await bot.get_chat_member(AUTH_CHANNEL, int(userid)) 87 | except UserNotParticipant: 88 | pass 89 | except Exception as e: 90 | logger.exception(e) 91 | else: 92 | if user.status != enums.ChatMemberStatus.BANNED: 93 | return True 94 | 95 | return False 96 | 97 | 98 | async def get_poster(query, bulk=False, id=False, file=None): 99 | if not id: 100 | # https://t.me/GetTGLink/4183 101 | query = (query.strip()).lower() 102 | title = query 103 | year = re.findall(r"[1-2]\d{3}$", query, re.IGNORECASE) 104 | if year: 105 | year = list_to_str(year[:1]) 106 | title = (query.replace(year, "")).strip() 107 | elif file is not None: 108 | year = re.findall(r"[1-2]\d{3}", file, re.IGNORECASE) 109 | if year: 110 | year = list_to_str(year[:1]) 111 | else: 112 | year = None 113 | movieid = imdb.search_movie(title.lower(), results=10) 114 | if not movieid: 115 | return None 116 | if year: 117 | filtered = list(filter(lambda k: str(k.get("year")) == str(year), movieid)) 118 | if not filtered: 119 | filtered = movieid 120 | else: 121 | filtered = movieid 122 | movieid = list( 123 | filter(lambda k: k.get("kind") in ["movie", "tv series"], filtered) 124 | ) 125 | if not movieid: 126 | movieid = filtered 127 | if bulk: 128 | return movieid 129 | movieid = movieid[0].movieID 130 | else: 131 | movieid = query 132 | movie = imdb.get_movie(movieid) 133 | if movie.get("original air date"): 134 | date = movie["original air date"] 135 | elif movie.get("year"): 136 | date = movie.get("year") 137 | else: 138 | date = "N/A" 139 | plot = "" 140 | if not LONG_IMDB_DESCRIPTION: 141 | plot = movie.get("plot") 142 | if plot and len(plot) > 0: 143 | plot = plot[0] 144 | else: 145 | plot = movie.get("plot outline") 146 | if plot and len(plot) > 800: 147 | plot = plot[0:800] + "..." 148 | 149 | return { 150 | "title": movie.get("title"), 151 | "votes": movie.get("votes"), 152 | "aka": list_to_str(movie.get("akas")), 153 | "seasons": movie.get("number of seasons"), 154 | "box_office": movie.get("box office"), 155 | "localized_title": movie.get("localized title"), 156 | "kind": movie.get("kind"), 157 | "imdb_id": f"tt{movie.get('imdbID')}", 158 | "cast": list_to_str(movie.get("cast")), 159 | "runtime": list_to_str(movie.get("runtimes")), 160 | "countries": list_to_str(movie.get("countries")), 161 | "certificates": list_to_str(movie.get("certificates")), 162 | "languages": list_to_str(movie.get("languages")), 163 | "director": list_to_str(movie.get("director")), 164 | "writer": list_to_str(movie.get("writer")), 165 | "producer": list_to_str(movie.get("producer")), 166 | "composer": list_to_str(movie.get("composer")), 167 | "cinematographer": list_to_str(movie.get("cinematographer")), 168 | "music_team": list_to_str(movie.get("music department")), 169 | "distributors": list_to_str(movie.get("distributors")), 170 | "release_date": date, 171 | "year": movie.get("year"), 172 | "genres": list_to_str(movie.get("genres")), 173 | "poster": movie.get("full-size cover url"), 174 | "plot": plot, 175 | "rating": str(movie.get("rating")), 176 | "url": f"https://www.imdb.com/title/tt{movieid}", 177 | } 178 | 179 | 180 | # https://github.com/odysseusmax/animated-lamp/blob/2ef4730eb2b5f0596ed6d03e7b05243d93e3415b/bot/utils/broadcast.py#L37 181 | 182 | 183 | async def broadcast_messages(user_id, message): 184 | try: 185 | await message.copy(chat_id=user_id) 186 | return True, "Success" 187 | except FloodWait as e: 188 | await asyncio.sleep(e.x) 189 | return await broadcast_messages(user_id, message) 190 | except InputUserDeactivated: 191 | await db.delete_user(int(user_id)) 192 | logging.info(f"{user_id}-Removed from Database, since deleted account.") 193 | return False, "Deleted" 194 | except UserIsBlocked: 195 | logging.info(f"{user_id} -Blocked the bot.") 196 | return False, "Blocked" 197 | except PeerIdInvalid: 198 | await db.delete_user(int(user_id)) 199 | logging.info(f"{user_id} - PeerIdInvalid") 200 | return False, "Error" 201 | except Exception as e: 202 | return False, "Error" 203 | 204 | 205 | async def broadcast_messages_group(chat_id, message): 206 | try: 207 | kd = await message.copy(chat_id=chat_id) 208 | try: 209 | await kd.pin() 210 | except: 211 | pass 212 | return True, "Success" 213 | except FloodWait as e: 214 | await asyncio.sleep(e.x) 215 | return await broadcast_messages_group(chat_id, message) 216 | except Exception as e: 217 | return False, "Error" 218 | 219 | 220 | async def search_gagala(text): 221 | usr_agent = { 222 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " 223 | "Chrome/61.0.3163.100 Safari/537.36" 224 | } 225 | text = text.replace(" ", "+") 226 | url = f"https://www.google.com/search?q={text}" 227 | response = requests.get(url, headers=usr_agent) 228 | response.raise_for_status() 229 | soup = BeautifulSoup(response.text, "html.parser") 230 | titles = soup.find_all("h3") 231 | return [title.getText() for title in titles] 232 | 233 | 234 | async def get_settings(group_id): 235 | settings = temp.SETTINGS.get(group_id) 236 | if not settings: 237 | settings = await db.get_settings(group_id) 238 | temp.SETTINGS[group_id] = settings 239 | return settings 240 | 241 | 242 | async def save_group_settings(group_id, key, value): 243 | current = await get_settings(group_id) 244 | current[key] = value 245 | temp.SETTINGS[group_id] = current 246 | await db.update_settings(group_id, current) 247 | 248 | 249 | def get_size(size): 250 | """Get size in readable format""" 251 | 252 | units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"] 253 | size = float(size) 254 | i = 0 255 | while size >= 1024.0 and i < len(units): 256 | i += 1 257 | size /= 1024.0 258 | return "%.2f %s" % (size, units[i]) 259 | 260 | 261 | def split_list(l, n): 262 | for i in range(0, len(l), n): 263 | yield l[i : i + n] 264 | 265 | 266 | def get_file_id(msg: Message): 267 | if msg.media: 268 | for message_type in ( 269 | "photo", 270 | "animation", 271 | "audio", 272 | "document", 273 | "video", 274 | "video_note", 275 | "voice", 276 | "sticker", 277 | ): 278 | obj = getattr(msg, message_type) 279 | if obj: 280 | setattr(obj, "message_type", message_type) 281 | return obj 282 | 283 | 284 | def extract_user(message: Message) -> Union[int, str]: 285 | """extracts the user from a message""" 286 | # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7 287 | user_id = None 288 | user_first_name = None 289 | if message.reply_to_message: 290 | user_id = message.reply_to_message.from_user.id 291 | user_first_name = message.reply_to_message.from_user.first_name 292 | 293 | elif len(message.command) > 1: 294 | if ( 295 | len(message.entities) > 1 296 | and message.entities[1].type == enums.MessageEntityType.TEXT_MENTION 297 | ): 298 | required_entity = message.entities[1] 299 | user_id = required_entity.user.id 300 | user_first_name = required_entity.user.first_name 301 | else: 302 | user_id = message.command[1] 303 | # don't want to make a request -_- 304 | user_first_name = user_id 305 | try: 306 | user_id = int(user_id) 307 | except ValueError: 308 | pass 309 | else: 310 | user_id = message.from_user.id 311 | user_first_name = message.from_user.first_name 312 | return (user_id, user_first_name) 313 | 314 | 315 | def list_to_str(k): 316 | if not k: 317 | return "N/A" 318 | elif len(k) == 1: 319 | return str(k[0]) 320 | elif MAX_LIST_ELM: 321 | k = k[: int(MAX_LIST_ELM)] 322 | return " ".join(f"{elem}, " for elem in k) 323 | else: 324 | return " ".join(f"{elem}, " for elem in k) 325 | 326 | 327 | def last_online(from_user): 328 | time = "" 329 | if from_user.is_bot: 330 | time += "🤖 Bot :(" 331 | elif from_user.status == enums.UserStatus.RECENTLY: 332 | time += "Recently" 333 | elif from_user.status == enums.UserStatus.LAST_WEEK: 334 | time += "Within the last week" 335 | elif from_user.status == enums.UserStatus.LAST_MONTH: 336 | time += "Within the last month" 337 | elif from_user.status == enums.UserStatus.LONG_AGO: 338 | time += "A long time ago :(" 339 | elif from_user.status == enums.UserStatus.ONLINE: 340 | time += "Currently Online" 341 | elif from_user.status == enums.UserStatus.OFFLINE: 342 | time += from_user.last_online_date.strftime("%a, %d %b %Y, %H:%M:%S") 343 | return time 344 | 345 | 346 | def split_quotes(text: str) -> List: 347 | if not any(text.startswith(char) for char in START_CHAR): 348 | return text.split(None, 1) 349 | counter = 1 # ignore first char -> is some kind of quote 350 | while counter < len(text): 351 | if text[counter] == "\\": 352 | counter += 1 353 | elif text[counter] == text[0] or ( 354 | text[0] == SMART_OPEN and text[counter] == SMART_CLOSE 355 | ): 356 | break 357 | counter += 1 358 | else: 359 | return text.split(None, 1) 360 | 361 | # 1 to avoid starting quote, and counter is exclusive so avoids ending 362 | key = remove_escapes(text[1:counter].strip()) 363 | # index will be in range, or `else` would have been executed and returned 364 | rest = text[counter + 1 :].strip() 365 | if not key: 366 | key = text[0] + text[0] 367 | return list(filter(None, [key, rest])) 368 | 369 | 370 | def gfilterparser(text, keyword): 371 | if "buttonalert" in text: 372 | text = text.replace("\n", "\\n").replace("\t", "\\t") 373 | buttons = [] 374 | note_data = "" 375 | prev = 0 376 | i = 0 377 | alerts = [] 378 | for match in BTN_URL_REGEX.finditer(text): 379 | # Check if btnurl is escaped 380 | n_escapes = 0 381 | to_check = match.start(1) - 1 382 | while to_check > 0 and text[to_check] == "\\": 383 | n_escapes += 1 384 | to_check -= 1 385 | 386 | # if even, not escaped -> create button 387 | if n_escapes % 2 == 0: 388 | note_data += text[prev : match.start(1)] 389 | prev = match.end(1) 390 | if match.group(3) == "buttonalert": 391 | # create a thruple with button label, url, and newline status 392 | if bool(match.group(5)) and buttons: 393 | buttons[-1].append( 394 | InlineKeyboardButton( 395 | text=match.group(2), 396 | callback_data=f"gfilteralert:{i}:{keyword}", 397 | ) 398 | ) 399 | else: 400 | buttons.append( 401 | [ 402 | InlineKeyboardButton( 403 | text=match.group(2), 404 | callback_data=f"gfilteralert:{i}:{keyword}", 405 | ) 406 | ] 407 | ) 408 | i += 1 409 | alerts.append(match.group(4)) 410 | elif bool(match.group(5)) and buttons: 411 | buttons[-1].append( 412 | InlineKeyboardButton( 413 | text=match.group(2), url=match.group(4).replace(" ", "") 414 | ) 415 | ) 416 | else: 417 | buttons.append( 418 | [ 419 | InlineKeyboardButton( 420 | text=match.group(2), url=match.group(4).replace(" ", "") 421 | ) 422 | ] 423 | ) 424 | 425 | else: 426 | note_data += text[prev:to_check] 427 | prev = match.start(1) - 1 428 | else: 429 | note_data += text[prev:] 430 | 431 | try: 432 | return note_data, buttons, alerts 433 | except: 434 | return note_data, buttons, None 435 | 436 | 437 | def parser(text, keyword): 438 | if "buttonalert" in text: 439 | text = text.replace("\n", "\\n").replace("\t", "\\t") 440 | buttons = [] 441 | note_data = "" 442 | prev = 0 443 | i = 0 444 | alerts = [] 445 | for match in BTN_URL_REGEX.finditer(text): 446 | # Check if btnurl is escaped 447 | n_escapes = 0 448 | to_check = match.start(1) - 1 449 | while to_check > 0 and text[to_check] == "\\": 450 | n_escapes += 1 451 | to_check -= 1 452 | 453 | # if even, not escaped -> create button 454 | if n_escapes % 2 == 0: 455 | note_data += text[prev : match.start(1)] 456 | prev = match.end(1) 457 | if match.group(3) == "buttonalert": 458 | # create a thruple with button label, url, and newline status 459 | if bool(match.group(5)) and buttons: 460 | buttons[-1].append( 461 | InlineKeyboardButton( 462 | text=match.group(2), 463 | callback_data=f"alertmessage:{i}:{keyword}", 464 | ) 465 | ) 466 | else: 467 | buttons.append( 468 | [ 469 | InlineKeyboardButton( 470 | text=match.group(2), 471 | callback_data=f"alertmessage:{i}:{keyword}", 472 | ) 473 | ] 474 | ) 475 | i += 1 476 | alerts.append(match.group(4)) 477 | elif bool(match.group(5)) and buttons: 478 | buttons[-1].append( 479 | InlineKeyboardButton( 480 | text=match.group(2), url=match.group(4).replace(" ", "") 481 | ) 482 | ) 483 | else: 484 | buttons.append( 485 | [ 486 | InlineKeyboardButton( 487 | text=match.group(2), url=match.group(4).replace(" ", "") 488 | ) 489 | ] 490 | ) 491 | 492 | else: 493 | note_data += text[prev:to_check] 494 | prev = match.start(1) - 1 495 | else: 496 | note_data += text[prev:] 497 | 498 | try: 499 | return note_data, buttons, alerts 500 | except: 501 | return note_data, buttons, None 502 | 503 | 504 | def remove_escapes(text: str) -> str: 505 | res = "" 506 | is_escaped = False 507 | for counter in range(len(text)): 508 | if is_escaped: 509 | res += text[counter] 510 | is_escaped = False 511 | elif text[counter] == "\\": 512 | is_escaped = True 513 | else: 514 | res += text[counter] 515 | return res 516 | 517 | 518 | def humanbytes(size): 519 | if not size: 520 | return "" 521 | power = 2**10 522 | n = 0 523 | Dic_powerN = {0: " ", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"} 524 | while size > power: 525 | size /= power 526 | n += 1 527 | return str(round(size, 2)) + " " + Dic_powerN[n] + "B" 528 | 529 | 530 | async def get_shortlink(chat_id, link, second=False): 531 | settings = await get_settings(chat_id) # fetching settings for group 532 | if "shortlink" in settings.keys(): 533 | URL = settings["shortlink"] 534 | API = settings["shortlink_api"] 535 | else: 536 | URL = SHORTLINK_URL 537 | API = SHORTLINK_API 538 | if ( 539 | URL.startswith("shorturllink") 540 | or URL.startswith("terabox.in") 541 | or URL.startswith("urlshorten.in") 542 | or second 543 | ): 544 | URL = SECOND_SHORTLINK_URL 545 | API = SECOND_SHORTLINK_API 546 | if URL == "api.shareus.io": 547 | # method 1: 548 | # https = link.split(":")[0] #splitting https or http from link 549 | # if "http" == https: #if https == "http": 550 | # https = "https" 551 | # link = link.replace("http", https) #replacing http to https 552 | # conn = http.client.HTTPSConnection("api.shareus.io") 553 | # payload = json.dumps({ 554 | # "api_key": "4c1YTBacB6PTuwogBiEIFvZN5TI3", 555 | # "monetization": True, 556 | # "destination": link, 557 | # "ad_page": 3, 558 | # "category": "Entertainment", 559 | # "tags": ["trendinglinks"], 560 | # "monetize_with_money": False, 561 | # "price": 0, 562 | # "currency": "INR", 563 | # "purchase_note":"" 564 | 565 | # }) 566 | # headers = { 567 | # 'Keep-Alive': '', 568 | # 'Content-Type': 'application/json' 569 | # } 570 | # conn.request("POST", "/generate_link", payload, headers) 571 | # res = conn.getresponse() 572 | # data = res.read().decode("utf-8") 573 | # parsed_data = json.loads(data) 574 | # if parsed_data["status"] == "success": 575 | # return parsed_data["link"] 576 | # method 2 577 | url = f"https://{URL}/easy_api" 578 | params = { 579 | "key": API, 580 | "link": link, 581 | } 582 | try: 583 | async with aiohttp.ClientSession() as session: 584 | async with session.get( 585 | url, params=params, raise_for_status=True, ssl=False 586 | ) as response: 587 | data = await response.text() 588 | return data 589 | except Exception as e: 590 | logger.error(e) 591 | return link 592 | else: 593 | shortzy = Shortzy(api_key=API, base_site=URL) 594 | link = await shortzy.convert(link) 595 | return link 596 | 597 | 598 | # async def get_shortlink(chat_id, link, second=False): 599 | # if not second: 600 | # settings = await get_settings(chat_id) #fetching settings for group 601 | # if 'shortlink' in settings.keys(): 602 | # URL = settings['shortlink'] 603 | # API = settings['shortlink_api'] 604 | # else: 605 | # URL = SHORTLINK_URL 606 | # API = SHORTLINK_API 607 | # if URL.startswith("shorturllink"): 608 | # URL = SECOND_SHORTLINK_URL 609 | # API = SECOND_SHORTLINK_API 610 | # # if 'shortlink_api' in settings.keys(): 611 | # # API = settings['shortlink_api'] 612 | # # elif URL.startswith("shorturllink"): 613 | # # URL = SECOND_SHORTLINK_URL 614 | # # else: 615 | # # API = SHORTLINK_API 616 | # https = link.split(":")[0] #splitting https or http from link 617 | # if "http" == https: #if https == "http": 618 | # https = "https" 619 | # link = link.replace("http", https) #replacing http to https 620 | # if URL == "api.shareus.in": 621 | # url = f'https://{URL}/shortLink' 622 | # params = { 623 | # "token": API, 624 | # "format": "json", 625 | # "link": link, 626 | # } 627 | # try: 628 | # async with aiohttp.ClientSession() as session: 629 | # async with session.get(url, params=params, raise_for_status=True, ssl=False) as response: 630 | # data = await response.json(content_type="text/html") 631 | # if data["status"] == "success": 632 | # return data["shortlink"] 633 | # else: 634 | # logger.error(f"Error: {data['message']}") 635 | # return f'https://{URL}/shortLink?token={API}&format=json&link={link}' 636 | # except Exception as e: 637 | # logger.error(e) 638 | # return f'https://{URL}/shortLink?token={API}&format=json&link={link}' 639 | # else: 640 | # url = f'https://{URL}/api' 641 | # params = { 642 | # "api": API, 643 | # "url": link, 644 | # } 645 | # try: 646 | # async with aiohttp.ClientSession() as session: 647 | # async with session.get(url, params=params, raise_for_status=True, ssl=False) as response: 648 | # data = await response.json() 649 | # if data["status"] == "success": 650 | # return data["shortenedUrl"] 651 | # else: 652 | # logger.error(f"Error: {data['message']}") 653 | # if URL == 'clicksfly.com': 654 | # return f'https://{URL}/api?api={API}&url={link}' 655 | # else: 656 | # return f'https://{URL}/api?api={API}&link={link}' 657 | # except Exception as e: 658 | # SECOND_SHORTENER[chat_id] = URL 659 | # logger.error(e) 660 | # await get_shortlink(chat_id, link, second=True) 661 | # # return f'https://{URL}/api?api={API}&link={link}' 662 | # else: 663 | # if SECOND_SHORTENER.get(chat_id).startswith('shorturllink'): 664 | # URL = SECOND_SHORTLINK_URL 665 | # API = SECOND_SHORTLINK_API 666 | # else: 667 | # URL = SHORTLINK_URL 668 | # API = SHORTLINK_API 669 | # url = f'https://{URL}/api' 670 | # params = { 671 | # "api": API, 672 | # "url": link, 673 | # } 674 | # try: 675 | # async with aiohttp.ClientSession() as session: 676 | # async with session.get(url, params=params, raise_for_status=True, ssl=False) as response: 677 | # data = await response.json() 678 | # if data["status"] == "success": 679 | # return data["shortenedUrl"] 680 | # else: 681 | # logger.error(f"Error: {data['message']}") 682 | # return f'https://{URL}/api?api={API}&link={link}' 683 | # except Exception as e: 684 | # logger.error(e) 685 | # return f'https://{URL}/api?api={API}&link={link}' 686 | # # settings = await get_settings(chat_id) #fetching settings for group 687 | # # if 'shortlink' in settings.keys(): 688 | # # URL = settings['shortlink'] 689 | # # API = settings['shortlink_api'] 690 | # # else: 691 | # # URL = SHORTLINK_URL 692 | # # API = SHORTLINK_API 693 | # # if URL.startswith("shorturllink"): 694 | # # URL = SECOND_SHORTLINK_URL 695 | # # API = SECOND_SHORTLINK_API 696 | # # # url = settings['url'] 697 | # # # api = settings['api'] 698 | # # shortzy = Shortzy(api_key=API, base_site=URL) 699 | 700 | # # link = await shortzy.convert(link) 701 | # # return link 702 | 703 | 704 | async def get_tutorial(chat_id): 705 | settings = await get_settings(chat_id) # fetching settings for group 706 | if "tutorial" in settings.keys(): 707 | TUTORIAL_URL = settings["tutorial"] 708 | else: 709 | TUTORIAL_URL = TUTORIAL 710 | return TUTORIAL_URL 711 | 712 | 713 | async def get_verify_shorted_link(link): 714 | API = SHORTLINK_API 715 | URL = SHORTLINK_URL 716 | https = link.split(":")[0] 717 | if "http" == https: 718 | https = "https" 719 | link = link.replace("http", https) 720 | 721 | if URL == "api.shareus.in": 722 | url = f"https://{URL}/shortLink" 723 | params = { 724 | "token": API, 725 | "format": "json", 726 | "link": link, 727 | } 728 | try: 729 | async with aiohttp.ClientSession() as session: 730 | async with session.get( 731 | url, params=params, raise_for_status=True, ssl=False 732 | ) as response: 733 | data = await response.json(content_type="text/html") 734 | if data["status"] == "success": 735 | return data["shortlink"] 736 | else: 737 | logger.error(f"Error: {data['message']}") 738 | return f"https://{URL}/shortLink?token={API}&format=json&link={link}" 739 | 740 | except Exception as e: 741 | logger.error(e) 742 | return f"https://{URL}/shortLink?token={API}&format=json&link={link}" 743 | else: 744 | url = f"https://{URL}/api" 745 | params = { 746 | "api": API, 747 | "url": link, 748 | } 749 | try: 750 | async with aiohttp.ClientSession() as session: 751 | async with session.get( 752 | url, params=params, raise_for_status=True, ssl=False 753 | ) as response: 754 | data = await response.json() 755 | if data["status"] == "success": 756 | return data["shortenedUrl"] 757 | else: 758 | logger.error(f"Error: {data['message']}") 759 | return f"https://{URL}/api?api={API}&link={link}" 760 | 761 | except Exception as e: 762 | logger.error(e) 763 | return f"{URL}/api?api={API}&link={link}" 764 | 765 | 766 | async def check_token(bot, userid, token): 767 | user = await bot.get_users(userid) 768 | if not await db.is_user_exist(user.id): 769 | await db.add_user(user.id, user.first_name) 770 | await bot.send_message( 771 | LOG_CHANNEL, script.LOG_TEXT_P.format(user.id, user.mention) 772 | ) 773 | if user.id in TOKENS.keys(): 774 | TKN = TOKENS[user.id] 775 | if token in TKN.keys(): 776 | is_used = TKN[token] 777 | if is_used == True: 778 | return False 779 | else: 780 | return True 781 | else: 782 | return False 783 | 784 | 785 | async def get_token(bot, userid, link): 786 | user = await bot.get_users(userid) 787 | if not await db.is_user_exist(user.id): 788 | await db.add_user(user.id, user.first_name) 789 | await bot.send_message( 790 | LOG_CHANNEL, script.LOG_TEXT_P.format(user.id, user.mention) 791 | ) 792 | token = "".join(random.choices(string.ascii_letters + string.digits, k=7)) 793 | TOKENS[user.id] = {token: False} 794 | link = f"{link}verify-{user.id}-{token}" 795 | shortened_verify_url = await get_verify_shorted_link(link) 796 | return str(shortened_verify_url) 797 | 798 | 799 | async def send_all(bot, userid, files, ident): 800 | if AUTH_CHANNEL and not await is_subscribed(bot=bot, userid=userid): 801 | try: 802 | invite_link = await bot.create_chat_invite_link(int(AUTH_CHANNEL)) 803 | except ChatAdminRequired: 804 | logger.error("Mᴀᴋᴇ sᴜʀᴇ Bᴏᴛ ɪs ᴀᴅᴍɪɴ ɪɴ Fᴏʀᴄᴇsᴜʙ ᴄʜᴀɴɴᴇʟ") 805 | return 806 | 807 | await bot.send_message( 808 | chat_id=userid, 809 | text="**Yᴏᴜ ᴀʀᴇ ɴᴏᴛ ɪɴ ᴏᴜʀ Bᴀᴄᴋ-ᴜᴘ ᴄʜᴀɴɴᴇʟ ɢɪᴠᴇɴ ʙᴇʟᴏᴡ sᴏ ʏᴏᴜ ᴅᴏɴ'ᴛ ɢᴇᴛ ᴛʜᴇ ᴍᴏᴠɪᴇ ғɪʟᴇ...\n\nIғ ʏᴏᴜ ᴡᴀɴᴛ ᴛʜᴇ ᴍᴏᴠɪᴇ ғɪʟᴇ, ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ '❆ Jᴏɪɴ Oᴜʀ Bᴀᴄᴋ-Uᴘ Cʜᴀɴɴᴇʟ ❆' ʙᴜᴛᴛᴏɴ ʙᴇʟᴏᴡ ᴀɴᴅ ᴊᴏɪɴ ᴏᴜʀ ʙᴀᴄᴋ-ᴜᴘ ᴄʜᴀɴɴᴇʟ, ᴛʜᴇɴ ᴄʟɪᴄᴋ ᴏɴ ᴛʜᴇ '↻ Tʀʏ Aɢᴀɪɴ' ʙᴜᴛᴛᴏɴ ʙᴇʟᴏᴡ...\n\nTʜᴇɴ ʏᴏᴜ ᴡɪʟʟ ɢᴇᴛ ᴛʜᴇ ᴍᴏᴠɪᴇ ғɪʟᴇs...**", 810 | reply_markup=InlineKeyboardMarkup(btn), 811 | parse_mode=enums.ParseMode.MARKDOWN, 812 | ) 813 | return "fsub" 814 | 815 | if IS_VERIFY and not await check_verification(bot, userid): 816 | btn = [ 817 | [ 818 | InlineKeyboardButton( 819 | "Vᴇʀɪғʏ", 820 | url=await get_token( 821 | bot, 822 | userid, 823 | f"https://telegram.me/{temp.U_NAME}?start=", 824 | "send_all", 825 | ), 826 | ), 827 | InlineKeyboardButton("Hᴏᴡ Tᴏ Vᴇʀɪғʏ", url=HOW_TO_VERIFY), 828 | ] 829 | ] 830 | await bot.send_message( 831 | chat_id=userid, 832 | text="Yᴏᴜ ᴀʀᴇ ɴᴏᴛ ᴠᴇʀɪғɪᴇᴅ!\nKɪɴᴅʟʏ ᴠᴇʀɪғʏ ᴛᴏ ᴄᴏɴᴛɪɴᴜᴇ Sᴏ ᴛʜᴀᴛ ʏᴏᴜ ᴄᴀɴ ɢᴇᴛ ᴀᴄᴄᴇss ᴛᴏ ᴜɴʟɪᴍɪᴛᴇᴅ ᴍᴏᴠɪᴇs ᴜɴᴛɪʟ 12 ʜᴏᴜʀs ғʀᴏᴍ ɴᴏᴡ !", 833 | protect_content=True if PROTECT_CONTENT else False, 834 | reply_markup=InlineKeyboardMarkup(btn), 835 | ) 836 | return "verify" 837 | 838 | for file in files: 839 | f_caption = file.caption 840 | title = file.file_name 841 | size = get_size(file.file_size) 842 | if CUSTOM_FILE_CAPTION: 843 | try: 844 | f_caption = CUSTOM_FILE_CAPTION.format( 845 | file_name="" if title is None else title, 846 | file_size="" if size is None else size, 847 | file_caption="" if f_caption is None else f_caption, 848 | ) 849 | except Exception as e: 850 | print(e) 851 | f_caption = f_caption 852 | if f_caption is None: 853 | f_caption = f"{title}" 854 | try: 855 | await bot.send_cached_media( 856 | chat_id=userid, 857 | file_id=file.file_id, 858 | caption=f_caption, 859 | protect_content=True if ident == "filep" else False, 860 | reply_markup=InlineKeyboardMarkup( 861 | [ 862 | [ 863 | InlineKeyboardButton("Sᴜᴘᴘᴏʀᴛ Gʀᴏᴜᴘ", url=GRP_LNK), 864 | InlineKeyboardButton("Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ", url=CHNL_LNK), 865 | ], 866 | [InlineKeyboardButton("Bᴏᴛ Oᴡɴᴇʀ", url="t.me/J_shree_ram")], 867 | ] 868 | ), 869 | ) 870 | except UserIsBlocked: 871 | logger.error(f"Usᴇʀ: {userid} ʙʟᴏᴄᴋᴇᴅ ᴛʜᴇ ʙᴏᴛ. Uɴʙʟᴏᴄᴋ ᴛʜᴇ ʙᴏᴛ!") 872 | return "Usᴇʀ ɪs ʙʟᴏᴄᴋᴇᴅ ᴛʜᴇ ʙᴏᴛ ! Uɴʙʟᴏᴄᴋ ᴛᴏ sᴇɴᴅ ғɪʟᴇs!" 873 | except PeerIdInvalid: 874 | logger.error("Eʀʀᴏʀ: Pᴇᴇʀ ID ɪɴᴠᴀʟɪᴅ !") 875 | return "Pᴇᴇʀ ID ɪɴᴠᴀʟɪᴅ !" 876 | except Exception as e: 877 | logger.error(f"Eʀʀᴏʀ: {e}") 878 | return f"Eʀʀᴏʀ: {e}" 879 | return "done" 880 | 881 | 882 | async def verify_user(bot, userid, token): 883 | user = await bot.get_users(userid) 884 | if not await db.is_user_exist(user.id): 885 | await db.add_user(user.id, user.first_name) 886 | await bot.send_message( 887 | LOG_CHANNEL, script.LOG_TEXT_P.format(user.id, user.mention) 888 | ) 889 | TOKENS[user.id] = {token: True} 890 | tz = pytz.timezone("Asia/Kolkata") 891 | today = date.today() 892 | VERIFIED[user.id] = str(today) 893 | 894 | 895 | async def check_verification(bot, userid): 896 | user = await bot.get_users(userid) 897 | if not await db.is_user_exist(user.id): 898 | await db.add_user(user.id, user.first_name) 899 | await bot.send_message( 900 | LOG_CHANNEL, script.LOG_TEXT_P.format(user.id, user.mention) 901 | ) 902 | tz = pytz.timezone("Asia/Kolkata") 903 | today = date.today() 904 | if user.id in VERIFIED.keys(): 905 | EXP = VERIFIED[user.id] 906 | years, month, day = EXP.split("-") 907 | comp = date(int(years), int(month), int(day)) 908 | if comp < today: 909 | return False 910 | else: 911 | return True 912 | else: 913 | return False 914 | 915 | 916 | async def send_all(bot, userid, files, ident, chat_id, user_name, query): 917 | settings = await get_settings(chat_id) 918 | if "is_shortlink" in settings.keys(): 919 | ENABLE_SHORTLINK = settings["is_shortlink"] 920 | else: 921 | await save_group_settings(message.chat.id, "is_shortlink", False) 922 | ENABLE_SHORTLINK = False 923 | try: 924 | if ENABLE_SHORTLINK: 925 | for file in files: 926 | title = file.file_name 927 | size = get_size(file.file_size) 928 | await bot.send_message( 929 | chat_id=userid, 930 | text=f"Hᴇʏ ᴛʜᴇʀᴇ {user_name} 👋🏽 \n\n✅ Sᴇᴄᴜʀᴇ ʟɪɴᴋ ᴛᴏ ʏᴏᴜʀ ғɪʟᴇ ʜᴀs sᴜᴄᴄᴇssғᴜʟʟʏ ʙᴇᴇɴ ɢᴇɴᴇʀᴀᴛᴇᴅ ᴘʟᴇᴀsᴇ ᴄʟɪᴄᴋ ᴅᴏᴡɴʟᴏᴀᴅ ʙᴜᴛᴛᴏɴ\n\n🗃️ Fɪʟᴇ Nᴀᴍᴇ : {title}\n🔖 Fɪʟᴇ Sɪᴢᴇ : {size}", 931 | reply_markup=InlineKeyboardMarkup( 932 | [ 933 | [ 934 | InlineKeyboardButton( 935 | "📤 Dᴏᴡɴʟᴏᴀᴅ 📥", 936 | url=await get_shortlink( 937 | chat_id, 938 | f"https://telegram.me/{temp.U_NAME}?start=files_{file.file_id}", 939 | ), 940 | ) 941 | ] 942 | ] 943 | ), 944 | ) 945 | else: 946 | for file in files: 947 | f_caption = file.caption 948 | title = file.file_name 949 | size = get_size(file.file_size) 950 | if CUSTOM_FILE_CAPTION: 951 | try: 952 | f_caption = CUSTOM_FILE_CAPTION.format( 953 | file_name="" if title is None else title, 954 | file_size="" if size is None else size, 955 | file_caption="" if f_caption is None else f_caption, 956 | ) 957 | except Exception as e: 958 | print(e) 959 | f_caption = f_caption 960 | if f_caption is None: 961 | f_caption = f"{title}" 962 | await bot.send_cached_media( 963 | chat_id=userid, 964 | file_id=file.file_id, 965 | caption=f_caption, 966 | protect_content=True if ident == "filep" else False, 967 | reply_markup=InlineKeyboardMarkup( 968 | [ 969 | [ 970 | InlineKeyboardButton("Sᴜᴘᴘᴏʀᴛ Gʀᴏᴜᴘ", url=GRP_LNK), 971 | InlineKeyboardButton("Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ", url=CHNL_LNK), 972 | ], 973 | [InlineKeyboardButton("Bᴏᴛ Oᴡɴᴇʀ", url="t.me/J_shree_ram")], 974 | ] 975 | ), 976 | ) 977 | except UserIsBlocked: 978 | await query.answer("Uɴʙʟᴏᴄᴋ ᴛʜᴇ ʙᴏᴛ ᴍᴀʜɴ !", show_alert=True) 979 | except PeerIdInvalid: 980 | await query.answer("Hᴇʏ, Sᴛᴀʀᴛ Bᴏᴛ Fɪʀsᴛ Aɴᴅ Cʟɪᴄᴋ Sᴇɴᴅ Aʟʟ", show_alert=True) 981 | except Exception as e: 982 | await query.answer("Hᴇʏ, Sᴛᴀʀᴛ Bᴏᴛ Fɪʀsᴛ Aɴᴅ Cʟɪᴄᴋ Sᴇɴᴅ Aʟʟ", show_alert=True) 983 | """if IS_SHORTLINK == True: 984 | for file in files: 985 | title = file.file_name 986 | size = get_size(file.file_size) 987 | await bot.send_message(chat_id=userid, text=f"Hᴇʏ ᴛʜᴇʀᴇ {user_name} 👋🏽 \n\n✅ Sᴇᴄᴜʀᴇ ʟɪɴᴋ ᴛᴏ ʏᴏᴜʀ ғɪʟᴇ ʜᴀs sᴜᴄᴄᴇssғᴜʟʟʏ ʙᴇᴇɴ ɢᴇɴᴇʀᴀᴛᴇᴅ ᴘʟᴇᴀsᴇ ᴄʟɪᴄᴋ ᴅᴏᴡɴʟᴏᴀᴅ ʙᴜᴛᴛᴏɴ\n\n🗃️ Fɪʟᴇ Nᴀᴍᴇ : {title}\n🔖 Fɪʟᴇ Sɪᴢᴇ : {size}", reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("📤 Dᴏᴡɴʟᴏᴀᴅ 📥", url=await get_shortlink(chat_id, f"https://telegram.me/{temp.U_NAME}?start=files_{file.file_id}"))]]) 988 | ) 989 | else: 990 | for file in files: 991 | f_caption = file.caption 992 | title = file.file_name 993 | size = get_size(file.file_size) 994 | if CUSTOM_FILE_CAPTION: 995 | try: 996 | f_caption = CUSTOM_FILE_CAPTION.format(file_name='' if title is None else title, 997 | file_size='' if size is None else size, 998 | file_caption='' if f_caption is None else f_caption) 999 | except Exception as e: 1000 | print(e) 1001 | f_caption = f_caption 1002 | if f_caption is None: 1003 | f_caption = f"{title}" 1004 | await bot.send_cached_media( 1005 | chat_id=userid, 1006 | file_id=file.file_id, 1007 | caption=f_caption, 1008 | protect_content=True if ident == "filep" else False, 1009 | reply_markup=InlineKeyboardMarkup( 1010 | [ 1011 | [ 1012 | InlineKeyboardButton('Sᴜᴘᴘᴏʀᴛ Gʀᴏᴜᴘ', url=GRP_LNK), 1013 | InlineKeyboardButton('Uᴘᴅᴀᴛᴇs Cʜᴀɴɴᴇʟ', url=CHNL_LNK) 1014 | ],[ 1015 | InlineKeyboardButton("Bᴏᴛ Oᴡɴᴇʀ", url="t.me/J_shree_ram") 1016 | ] 1017 | ] 1018 | ) 1019 | )""" 1020 | 1021 | def get_media_from_message(message: "Message"): 1022 | media_types = ( 1023 | "audio", 1024 | "document", 1025 | "photo", 1026 | "sticker", 1027 | "animation", 1028 | "video", 1029 | "voice", 1030 | "video_note", 1031 | ) 1032 | for attr in media_types: 1033 | if media := getattr(message, attr, None): 1034 | return media 1035 | 1036 | def get_name(media_msg: Message) -> str: 1037 | media = get_media_from_message(media_msg) 1038 | return getattr(media, "file_name", "None") 1039 | 1040 | def get_hash(media_msg: Message) -> str: 1041 | media = get_media_from_message(media_msg) 1042 | return getattr(media, "file_unique_id", "")[:6] 1043 | 1044 | 1045 | async def get_cap(settings, remaining_seconds, files, query, total_results, search): 1046 | 1047 | cap = f"Hᴇʏ {query.from_user.mention}, Fᴏᴜɴᴅ {total_results} Rᴇsᴜʟᴛs ғᴏʀ Yᴏᴜʀ Qᴜᴇʀʏ {search}\n\n" 1048 | for file in files: 1049 | cap += f"♻️ [{get_size(file.file_size)}] {' '.join(filter(lambda x: not x.startswith('[') and not x.startswith('@') and not x.startswith('www.'), file.file_name.split()))}\n\n" 1050 | return cap 1051 | 1052 | 1053 | --------------------------------------------------------------------------------