├── 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 |
2 |
3 |
5 | PM-Shortner-Bot
6 |
7 |
8 | 
9 |
114 |
115 |
116 |
117 |
118 |
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 |
ᴀᴅᴅ ᴀ ꜰɪʟᴛᴇʀ ɪɴ ᴀ ᴄʜᴀᴛ
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 | "/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 |
--------------------------------------------------------------------------------