kalanakt commited on
Commit
e9dd2b9
1 Parent(s): 5ce72d3

Upload 15 files

Browse files
Files changed (15) hide show
  1. .gitignore +148 -0
  2. Dockerfile +10 -0
  3. LICENSE +339 -0
  4. README.md +1 -10
  5. Script.py +143 -0
  6. app.json +81 -0
  7. bot.py +92 -0
  8. heroku.yml +3 -0
  9. info.py +77 -0
  10. logging.conf +32 -0
  11. requirements.txt +12 -0
  12. runtime.txt +1 -0
  13. sample_info.py +24 -0
  14. start.sh +12 -0
  15. utils.py +512 -0
.gitignore ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Personal files
2
+ *.session
3
+ *.session-journal
4
+ .vscode
5
+ *test*.py
6
+ setup.cfg
7
+
8
+ # Byte-compiled / optimized / DLL files
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+
13
+ # C extensions
14
+ *.so
15
+
16
+ # Distribution / packaging
17
+ .Python
18
+ build/
19
+ develop-eggs/
20
+ dist/
21
+ downloads/
22
+ eggs/
23
+ .eggs/
24
+ lib/
25
+ lib64/
26
+ parts/
27
+ sdist/
28
+ var/
29
+ wheels/
30
+ share/python-wheels/
31
+ *.egg-info/
32
+ .installed.cfg
33
+ *.egg
34
+ MANIFEST
35
+
36
+ # PyInstaller
37
+ # Usually these files are written by a python script from a template
38
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
39
+ *.manifest
40
+ *.spec
41
+
42
+ # Installer logs
43
+ pip-log.txt
44
+ pip-delete-this-directory.txt
45
+
46
+ # Unit test / coverage reports
47
+ htmlcov/
48
+ .tox/
49
+ .nox/
50
+ .coverage
51
+ .coverage.*
52
+ .cache
53
+ nosetests.xml
54
+ coverage.xml
55
+ *.cover
56
+ *.py,cover
57
+ .hypothesis/
58
+ .pytest_cache/
59
+ cover/
60
+
61
+ # Translations
62
+ *.mo
63
+ *.pot
64
+
65
+ # Django stuff:
66
+ *.log
67
+ local_settings.py
68
+ db.sqlite3
69
+ db.sqlite3-journal
70
+
71
+ # Flask stuff:
72
+ instance/
73
+ .webassets-cache
74
+
75
+ # Scrapy stuff:
76
+ .scrapy
77
+
78
+ # Sphinx documentation
79
+ docs/_build/
80
+
81
+ # PyBuilder
82
+ .pybuilder/
83
+ target/
84
+
85
+ # Jupyter Notebook
86
+ .ipynb_checkpoints
87
+
88
+ # IPython
89
+ profile_default/
90
+ ipython_config.py
91
+
92
+ # pyenv
93
+ # For a library or package, you might want to ignore these files since the code is
94
+ # intended to run in multiple environments; otherwise, check them in:
95
+ # .python-version
96
+
97
+ # pipenv
98
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
99
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
100
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
101
+ # install all needed dependencies.
102
+ #Pipfile.lock
103
+
104
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
105
+ __pypackages__/
106
+
107
+ # Celery stuff
108
+ celerybeat-schedule
109
+ celerybeat.pid
110
+
111
+ # SageMath parsed files
112
+ *.sage.py
113
+
114
+ # Environments
115
+ .env
116
+ .venv
117
+ env/
118
+ venv/
119
+ ENV/
120
+ env.bak/
121
+ venv.bak/
122
+
123
+ # Spyder project settings
124
+ .spyderproject
125
+ .spyproject
126
+
127
+ # Rope project settings
128
+ .ropeproject
129
+
130
+ # mkdocs documentation
131
+ /site
132
+
133
+ # mypy
134
+ .mypy_cache/
135
+ .dmypy.json
136
+ dmypy.json
137
+
138
+ # Pyre type checker
139
+ .pyre/
140
+
141
+ # pytype static type analyzer
142
+ .pytype/
143
+
144
+ # Cython debug symbols
145
+ cython_debug/
146
+ config.py
147
+ .goutputstream-VAFWB1
148
+ result.json
Dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.8-slim-buster
2
+ WORKDIR /app
3
+ RUN apt update && apt upgrade -y
4
+ RUN apt install git -y
5
+ COPY requirements.txt requirements.txt
6
+ RUN pip3 install -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ CMD python3 bot.py
LICENSE ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
README.md CHANGED
@@ -1,10 +1 @@
1
- ---
2
- title: Baesuzy
3
- emoji: 🔥
4
- colorFrom: green
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ develop
 
 
 
 
 
 
 
 
 
Script.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class script(object):
2
+ START_TXT = """𝙷𝙴𝙻𝙾 {},
3
+ 𝙼𝚈 𝙽𝙰𝙼𝙴 𝙸𝚂 <a href=https://t.me/{}>{}</a>, 𝙸 𝙲𝙰𝙽 𝙿𝚁𝙾𝚅𝙸𝙳𝙴 𝙼𝙾𝚅𝙸𝙴𝚂, 𝙹𝚄𝚂𝚃 𝙰𝙳𝙳 𝙼𝙴 𝚃𝙾 𝚈𝙾𝚄𝚁 𝙶𝚁𝙾𝚄𝙿 𝙰𝙽𝙳 𝙴𝙽𝙹𝙾𝚈 😍"""
4
+ HELP_TXT = """𝙷𝙴𝚈 {}
5
+ 𝙷𝙴𝚁𝙴 𝙸𝚂 𝚃𝙷𝙴 𝙷𝙴𝙻𝙿 𝙵𝙾𝚁 𝙼𝚈 𝙲𝙾𝙼𝙼𝙰𝙽𝙳𝚂."""
6
+ ABOUT_TXT = """✯ 𝙼𝚈 𝙽𝙰𝙼𝙴: {}
7
+ ✯ 𝙲𝚁𝙴𝙰𝚃𝙾𝚁: <a href=https://t.me/TeamEvamaria>Team Eva Maria</a>
8
+ ✯ 𝙻𝙸𝙱𝚁𝙰𝚁𝚈: 𝙿𝚈𝚁𝙾𝙶𝚁𝙰𝙼
9
+ ✯ 𝙻𝙰𝙽𝙶𝚄𝙰𝙶𝙴: 𝙿𝚈𝚃𝙷𝙾𝙽 𝟹
10
+ ✯ 𝙳𝙰𝚃𝙰 𝙱𝙰𝚂𝙴: 𝙼𝙾𝙽𝙶𝙾 𝙳𝙱
11
+ ✯ 𝙱𝙾𝚃 𝚂𝙴𝚁𝚅𝙴𝚁: 𝙷𝙴𝚁𝙾𝙺𝚄
12
+ ✯ 𝙱𝚄𝙸𝙻𝙳 𝚂𝚃𝙰𝚃𝚄𝚂: v1.0.1 [ 𝙱𝙴𝚃𝙰 ]"""
13
+ SOURCE_TXT = """<b>NOTE:</b>
14
+ - Eva Maria is a open source project.
15
+ - Source - https://github.com/EvamariaTG/EvaMaria
16
+
17
+ <b>DEVS:</b>
18
+ - <a href=https://t.me/TeamEvamaria>Team Eva Maria</a>"""
19
+ MANUELFILTER_TXT = """Help: <b>Filters</b>
20
+
21
+ - Filter is the feature were users can set automated replies for a particular keyword and EvaMaria will respond whenever a keyword is found the message
22
+
23
+ <b>NOTE:</b>
24
+ 1. eva maria should have admin privillage.
25
+ 2. only admins can add filters in a chat.
26
+ 3. alert buttons have a limit of 64 characters.
27
+
28
+ <b>Commands and Usage:</b>
29
+ • /filter - <code>add a filter in chat</code>
30
+ • /filters - <code>list all the filters of a chat</code>
31
+ • /del - <code>delete a specific filter in chat</code>
32
+ • /delall - <code>delete the whole filters in a chat (chat owner only)</code>"""
33
+ BUTTON_TXT = """Help: <b>Buttons</b>
34
+
35
+ - Eva Maria Supports both url and alert inline buttons.
36
+
37
+ <b>NOTE:</b>
38
+ 1. Telegram will not allows you to send buttons without any content, so content is mandatory.
39
+ 2. Eva Maria supports buttons with any telegram media type.
40
+ 3. Buttons should be properly parsed as markdown format
41
+
42
+ <b>URL buttons:</b>
43
+ <code>[Button Text](buttonurl:https://t.me/EvaMariaBot)</code>
44
+
45
+ <b>Alert buttons:</b>
46
+ <code>[Button Text](buttonalert:This is an alert message)</code>"""
47
+ AUTOFILTER_TXT = """Help: <b>Auto Filter</b>
48
+
49
+ <b>NOTE:</b>
50
+ 1. Make me the admin of your channel if it's private.
51
+ 2. make sure that your channel does not contains camrips, porn and fake files.
52
+ 3. Forward the last message to me with quotes.
53
+ I'll add all the files in that channel to my db."""
54
+ CONNECTION_TXT = """Help: <b>Connections</b>
55
+
56
+ - Used to connect bot to PM for managing filters
57
+ - it helps to avoid spamming in groups.
58
+
59
+ <b>NOTE:</b>
60
+ 1. Only admins can add a connection.
61
+ 2. Send <code>/connect</code> for connecting me to ur PM
62
+
63
+ <b>Commands and Usage:</b>
64
+ • /connect - <code>connect a particular chat to your PM</code>
65
+ • /disconnect - <code>disconnect from a chat</code>
66
+ • /connections - <code>list all your connections</code>"""
67
+ EXTRAMOD_TXT = """Help: <b>Extra Modules</b>
68
+
69
+ <b>NOTE:</b>
70
+ these are the extra features of Eva Maria
71
+
72
+ <b>Commands and Usage:</b>
73
+ • /id - <code>get id of a specified user.</code>
74
+ • /info - <code>get information about a user.</code>
75
+ • /imdb - <code>get the film information from IMDb source.</code>
76
+ • /search - <code>get the film information from various sources.</code>"""
77
+ ADMIN_TXT = """Help: <b>Admin mods</b>
78
+
79
+ <b>NOTE:</b>
80
+ This module only works for my admins
81
+
82
+ <b>Commands and Usage:</b>
83
+ • /logs - <code>to get the rescent errors</code>
84
+ • /stats - <code>to get status of files in db.</code>
85
+ • /delete - <code>to delete a specific file from db.</code>
86
+ • /users - <code>to get list of my users and ids.</code>
87
+ • /chats - <code>to get list of the my chats and ids </code>
88
+ • /leave - <code>to leave from a chat.</code>
89
+ • /disable - <code>do disable a chat.</code>
90
+ • /ban - <code>to ban a user.</code>
91
+ • /unban - <code>to unban a user.</code>
92
+ • /channel - <code>to get list of total connected channels</code>
93
+ • /broadcast - <code>to broadcast a message to all users</code>"""
94
+ STATUS_TXT = """★ 𝚃𝙾𝚃𝙰𝙻 𝙵𝙸𝙻𝙴𝚂: <code>{}</code>
95
+ ★ 𝚃𝙾𝚃𝙰𝙻 𝚄𝚂𝙴𝚁𝚂: <code>{}</code>
96
+ ★ 𝚃𝙾𝚃𝙰𝙻 𝙲𝙷𝙰𝚃𝚂: <code>{}</code>
97
+ ★ 𝚄𝚂𝙴𝙳 𝚂𝚃𝙾𝚁𝙰𝙶𝙴: <code>{}</code> 𝙼𝚒𝙱
98
+ ★ 𝙵𝚁𝙴𝙴 𝚂𝚃𝙾𝚁𝙰𝙶𝙴: <code>{}</code> 𝙼𝚒𝙱"""
99
+ LOG_TEXT_G = """#NewGroup
100
+ Group = {}(<code>{}</code>)
101
+ Total Members = <code>{}</code>
102
+ Added By - {}
103
+ """
104
+ LOG_TEXT_P = """#NewUser
105
+ ID - <code>{}</code>
106
+ Name - {}
107
+ """
108
+ POST_TEXT = """
109
+ 𝗕𝗮𝗲 𝗦𝘂𝘇𝘆 𝗕𝗼𝘁 𝗨𝗽𝗱𝗮𝘁𝗲
110
+
111
+ 🔹 New Features :
112
+
113
+ - Remove the Old Added Verification method now no need to verify yourself for every request. you Only need to Verify yourself at once. (for 24 hours full access)
114
+ And you need to update verification expire within 24 hours.
115
+
116
+ - Now, You Can Use Bot In Both Privet Chat And Groups
117
+
118
+ - Added Advance TV Series Filters.
119
+
120
+ - Added button to get all files on the regular page
121
+
122
+ - When sending files bot Automatically sends similar files to a user
123
+
124
+ - Added Delete Timer ( You need to forward files to your `save message chat` before downloading )
125
+
126
+ - Added `/notification` Feature. it's update you new movie / Tv series ( For get notification you should turned on notification | go to bot and send /notification command ) [ Update 7/26 ]
127
+
128
+ Add Bae Suzy To Your Group & Enjoy
129
+
130
+ @SpaciousUniverseBot | @TMWAD
131
+
132
+ Bot Stats:
133
+
134
+ » Today Sended Files: {}
135
+ » New Users: {}
136
+ » Newly Added Files: {}
137
+
138
+ • Total Users: {}
139
+ • Total Files: {}
140
+
141
+ Updated Time: {}
142
+
143
+ """
app.json ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "EvaMariaBot",
3
+ "description": "When you going to send file on telegram channel this bot will save that in database, So you can search that easily in inline mode",
4
+ "stack": "container",
5
+ "keywords": [
6
+ "telegram",
7
+ "auto-filter",
8
+ "filter",
9
+ "best",
10
+ "indian",
11
+ "pyrogram",
12
+ "media",
13
+ "search",
14
+ "channel",
15
+ "index",
16
+ "inline"
17
+ ],
18
+ "website": "https://github.com/EvamariaTG/EvaMaria",
19
+ "repository": "https://github.com/EvamariaTG/EvaMaria",
20
+ "env": {
21
+ "BOT_TOKEN": {
22
+ "description": "Your bot token.",
23
+ "required": true
24
+ },
25
+ "API_ID": {
26
+ "description": "Get this value from https://my.telegram.org",
27
+ "required": true
28
+ },
29
+ "API_HASH": {
30
+ "description": "Get this value from https://my.telegram.org",
31
+ "required": true
32
+ },
33
+ "CHANNELS": {
34
+ "description": "Username or ID of channel or group. Separate multiple IDs by space.",
35
+ "required": false
36
+ },
37
+ "ADMINS": {
38
+ "description": "Username or ID of Admin. Separate multiple Admins by space.",
39
+ "required": true
40
+ },
41
+ "PICS": {
42
+ "description": "Add some telegraph link of pictures .",
43
+ "required": false
44
+ },
45
+ "LOG_CHANNEL": {
46
+ "description": "Bot Logs,Give a channel id with -100xxxxxxx",
47
+ "required": true
48
+ },
49
+ "AUTH_USERS": {
50
+ "description": "Username or ID of users to give access of inline search. Separate multiple users by space.\nLeave it empty if you don't want to restrict bot usage.",
51
+ "required": false
52
+ },
53
+ "AUTH_CHANNEL": {
54
+ "description": "ID of channel.Make sure bot is admin in this channel. Without subscribing this channel users cannot use bot.",
55
+ "required": false
56
+ },
57
+ "DATABASE_URI": {
58
+ "description": "mongoDB URI. Get this value from https://www.mongodb.com. For more help watch this video - https://youtu.be/dsuTn4qV2GA",
59
+ "required": true
60
+ },
61
+ "DATABASE_NAME": {
62
+ "description": "Name of the database in mongoDB. For more help watch this video - https://youtu.be/dsuTn4qV2GA",
63
+ "required": false
64
+ },
65
+ "COLLECTION_NAME": {
66
+ "description": "Name of the collections. Defaults to Telegram_files. If you are using the same database, then use different collection name for each bot",
67
+ "value": "Telegram_files",
68
+ "required": false
69
+ }
70
+ },
71
+ "addons": [],
72
+ "buildpacks": [{
73
+ "url": "heroku/python"
74
+ }],
75
+ "formation": {
76
+ "worker": {
77
+ "quantity": 1,
78
+ "size": "free"
79
+ }
80
+ }
81
+ }
bot.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pyrogram import types
2
+ from typing import Union, Optional, AsyncGenerator
3
+ from utils import temp
4
+ from info import SESSION, API_ID, API_HASH, BOT_TOKEN, LOG_STR
5
+ from database.users_chats_db import db
6
+ from database.ia_filterdb import Media
7
+ from pyrogram.raw.all import layer
8
+ from pyrogram import Client, __version__
9
+ import logging
10
+ import logging.config
11
+
12
+ # Get logging configurations
13
+ logging.config.fileConfig('logging.conf')
14
+ logging.getLogger().setLevel(logging.INFO)
15
+ logging.getLogger("pyrogram").setLevel(logging.ERROR)
16
+ logging.getLogger("imdbpy").setLevel(logging.ERROR)
17
+
18
+
19
+ class Bot(Client):
20
+
21
+ def __init__(self):
22
+ super().__init__(
23
+ name=SESSION,
24
+ api_id=API_ID,
25
+ api_hash=API_HASH,
26
+ bot_token=BOT_TOKEN,
27
+ workers=50,
28
+ plugins={"root": "plugins"},
29
+ sleep_threshold=5,
30
+ )
31
+
32
+ async def start(self):
33
+ b_users, b_chats = await db.get_banned()
34
+ temp.BANNED_USERS = b_users
35
+ temp.BANNED_CHATS = b_chats
36
+ await super().start()
37
+ await Media.ensure_indexes()
38
+ me = await self.get_me()
39
+ temp.ME = me.id
40
+ temp.U_NAME = me.username
41
+ temp.B_NAME = me.first_name
42
+ self.username = f'@{me.username}'
43
+ logging.info(
44
+ f"{me.first_name} with for Pyrogram v{__version__} (Layer {layer}) started on {me.username}.")
45
+ logging.info(LOG_STR)
46
+
47
+ async def iter_messages(
48
+ self,
49
+ chat_id: Union[int, str],
50
+ limit: int,
51
+ offset: int = 0,
52
+ ) -> Optional[AsyncGenerator["types.Message", None]]:
53
+ """Iterate through a chat sequentially.
54
+ This convenience method does the same as repeatedly calling :meth:`~pyrogram.Client.get_messages` in a loop, thus saving
55
+ you from the hassle of setting up boilerplate code. It is useful for getting the whole chat messages with a
56
+ single call.
57
+ Parameters:
58
+ chat_id (``int`` | ``str``):
59
+ Unique identifier (int) or username (str) of the target chat.
60
+ For your personal cloud (Saved Messages) you can simply use "me" or "self".
61
+ For a contact that exists in your Telegram address book you can use his phone number (str).
62
+
63
+ limit (``int``):
64
+ Identifier of the last message to be returned.
65
+
66
+ offset (``int``, *optional*):
67
+ Identifier of the first message to be returned.
68
+ Defaults to 0.
69
+ Returns:
70
+ ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects.
71
+ Example:
72
+ .. code-block:: python
73
+ for message in app.iter_messages("pyrogram", 1, 15000):
74
+ print(message.text)
75
+ """
76
+ current = offset
77
+ while True:
78
+ new_diff = min(200, limit - current)
79
+ if new_diff <= 0:
80
+ return
81
+ messages = await self.get_messages(chat_id, list(range(current, current+new_diff+1)))
82
+ for message in messages:
83
+ yield message
84
+ current += 1
85
+
86
+ async def stop(self, *args):
87
+ await super().stop()
88
+ logging.info("Bot stopped. Bye.")
89
+
90
+
91
+ app = Bot()
92
+ app.run()
heroku.yml ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ build:
2
+ docker:
3
+ worker: Dockerfile
info.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from os import environ
3
+
4
+ id_pattern = re.compile(r'^.\d+$')
5
+
6
+
7
+ def is_enabled(value, default):
8
+ if value.lower() in ["true", "yes", "1", "enable", "y"]:
9
+ return True
10
+ elif value.lower() in ["false", "no", "0", "disable", "n"]:
11
+ return False
12
+ else:
13
+ return default
14
+
15
+
16
+ # Bot information
17
+ SESSION = environ.get('SESSION', 'Media_search')
18
+ API_ID = int(environ['API_ID'])
19
+ API_HASH = environ['API_HASH']
20
+ BOT_TOKEN = environ['BOT_TOKEN']
21
+
22
+ # Bot settings
23
+ CACHE_TIME = int(environ.get('CACHE_TIME', 300))
24
+ USE_CAPTION_FILTER = bool(environ.get('USE_CAPTION_FILTER', False))
25
+ PICS = (environ.get('PICS', 'https://telegra.ph/file/7e56d907542396289fee4.jpg https://telegra.ph/file/9aa8dd372f4739fe02d85.jpg https://telegra.ph/file/adffc5ce502f5578e2806.jpg https://telegra.ph/file/6937b60bc2617597b92fd.jpg https://telegra.ph/file/09a7abaab340143f9c7e7.jpg https://telegra.ph/file/5a82c4a59bd04d415af1c.jpg https://telegra.ph/file/323986d3bd9c4c1b3cb26.jpg https://telegra.ph/file/b8a82dcb89fb296f92ca0.jpg https://telegra.ph/file/31adab039a85ed88e22b0.jpg https://telegra.ph/file/c0e0f4c3ed53ac8438f34.jpg https://telegra.ph/file/eede835fb3c37e07c9cee.jpg https://telegra.ph/file/e17d2d068f71a9867d554.jpg https://telegra.ph/file/8fb1ae7d995e8735a7c25.jpg https://telegra.ph/file/8fed19586b4aa019ec215.jpg https://telegra.ph/file/8e6c923abd6139083e1de.jpg https://telegra.ph/file/0049d801d29e83d68b001.jpg')).split()
26
+
27
+ # Admins, Channels & Users
28
+ ADMINS = [int(admin) if id_pattern.search(admin)
29
+ else admin for admin in environ.get('ADMINS', '').split()]
30
+ CHANNELS = [int(ch) if id_pattern.search(
31
+ ch) else ch for ch in environ.get('CHANNELS', '0').split()]
32
+ auth_users = [int(user) if id_pattern.search(
33
+ user) else user for user in environ.get('AUTH_USERS', '').split()]
34
+ AUTH_USERS = (auth_users + ADMINS) if auth_users else []
35
+ auth_channel = environ.get('AUTH_CHANNEL')
36
+ AUTH_CHANNEL = int(auth_channel) if auth_channel and id_pattern.search(
37
+ auth_channel) else None
38
+ auth_grp = environ.get('AUTH_GROUP')
39
+ AUTH_GROUPS = [int(ch) for ch in auth_grp.split()] if auth_grp else None
40
+
41
+ # MongoDB information
42
+ DATABASE_URI = environ.get('DATABASE_URI', "")
43
+ DATABASE_NAME = environ.get('DATABASE_NAME', "Rajappan")
44
+ COLLECTION_NAME = environ.get('COLLECTION_NAME', 'Telegram_files')
45
+
46
+ # Others
47
+ LOG_CHANNEL = int(environ.get('LOG_CHANNEL', 0))
48
+ SUPPORT_CHAT = environ.get('SUPPORT_CHAT', 'TeamEvamaria')
49
+ P_TTI_SHOW_OFF = is_enabled((environ.get('P_TTI_SHOW_OFF', "False")), False)
50
+ IMDB = is_enabled((environ.get('IMDB', "True")), True)
51
+ SINGLE_BUTTON = is_enabled((environ.get('SINGLE_BUTTON', "False")), False)
52
+ CUSTOM_FILE_CAPTION = environ.get("CUSTOM_FILE_CAPTION", None)
53
+ BATCH_FILE_CAPTION = environ.get("BATCH_FILE_CAPTION", CUSTOM_FILE_CAPTION)
54
+ IMDB_TEMPLATE = environ.get(
55
+ "IMDB_TEMPLATE", "🏷 Title: <a href={url}>{title}</a>\n🎭 Genres: {genres}\n📆 Year: <a href={url}/releaseinfo>{year}</a>\n🌟 Rating: <a href={url}/ratings>{rating}</a> / 10 /n/n Add Custome Template")
56
+ LONG_IMDB_DESCRIPTION = is_enabled(
57
+ environ.get("LONG_IMDB_DESCRIPTION", "False"), False)
58
+ SPELL_CHECK_REPLY = is_enabled(environ.get("SPELL_CHECK_REPLY", "True"), True)
59
+ MAX_LIST_ELM = environ.get("MAX_LIST_ELM", None)
60
+ INDEX_REQ_CHANNEL = int(environ.get('INDEX_REQ_CHANNEL', LOG_CHANNEL))
61
+ FILE_STORE_CHANNEL = [int(ch) for ch in (
62
+ environ.get('FILE_STORE_CHANNEL', '')).split()]
63
+ MELCOW_NEW_USERS = is_enabled((environ.get('MELCOW_NEW_USERS', "True")), True)
64
+ PROTECT_CONTENT = is_enabled((environ.get('PROTECT_CONTENT', "False")), False)
65
+ PUBLIC_FILE_STORE = is_enabled(
66
+ (environ.get('PUBLIC_FILE_STORE', "True")), True)
67
+
68
+ LOG_STR = "Current Cusomized Configurations are:-\n" + \
69
+ (("IMDB Results are enabled, Bot will be showing imdb details for you queries.\n" if IMDB else "IMBD Results are disabled.\n"))
70
+ LOG_STR += ("P_TTI_SHOW_OFF found , Users will be redirected to send /start to Bot PM instead of sending file file directly\n" if P_TTI_SHOW_OFF else "P_TTI_SHOW_OFF is disabled files will be send in PM, instead of sending start.\n")
71
+ LOG_STR += ("SINGLE_BUTTON is Found, filename and files size will be shown in a single button instead of two separate buttons\n" if SINGLE_BUTTON else "SINGLE_BUTTON is disabled , filename and file_sixe will be shown as different buttons\n")
72
+ LOG_STR += (f"CUSTOM_FILE_CAPTION enabled with value {CUSTOM_FILE_CAPTION}, your files will be send along with this customized caption.\n" if CUSTOM_FILE_CAPTION else "No CUSTOM_FILE_CAPTION Found, Default captions of file will be used.\n")
73
+ LOG_STR += ("Long IMDB storyline enabled." if LONG_IMDB_DESCRIPTION else "LONG_IMDB_DESCRIPTION is disabled , Plot will be shorter.\n")
74
+ LOG_STR += ("Spell Check Mode Is Enabled, bot will be suggesting related movies if movie not found\n" if SPELL_CHECK_REPLY else "SPELL_CHECK_REPLY Mode disabled\n")
75
+ LOG_STR += (
76
+ f"MAX_LIST_ELM Found, long list will be shortened to first {MAX_LIST_ELM} elements\n" if MAX_LIST_ELM else "Full List of casts and crew will be shown in imdb template, restrict them by adding a value to MAX_LIST_ELM\n")
77
+ LOG_STR += f"Your current IMDB template is {IMDB_TEMPLATE}"
logging.conf ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [loggers]
2
+ keys=root
3
+
4
+ [handlers]
5
+ keys=consoleHandler,fileHandler
6
+
7
+ [formatters]
8
+ keys=consoleFormatter,fileFormatter
9
+
10
+ [logger_root]
11
+ level=DEBUG
12
+ handlers=consoleHandler,fileHandler
13
+
14
+ [handler_consoleHandler]
15
+ class=StreamHandler
16
+ level=INFO
17
+ formatter=consoleFormatter
18
+ args=(sys.stdout,)
19
+
20
+ [handler_fileHandler]
21
+ class=FileHandler
22
+ level=ERROR
23
+ formatter=fileFormatter
24
+ args=('TelegramBot.log','w',)
25
+
26
+ [formatter_consoleFormatter]
27
+ format=%(asctime)s - %(lineno)d - %(name)s - %(module)s - %(levelname)s - %(message)s
28
+ datefmt=%I:%M:%S %p
29
+
30
+ [formatter_fileFormatter]
31
+ format=[%(asctime)s:%(name)s:%(lineno)d:%(levelname)s] %(message)s
32
+ datefmt=%m/%d/%Y %I:%M:%S %p
requirements.txt ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pyrogram
2
+ tgcrypto
3
+ pymongo[srv]==3.12.3
4
+ motor==2.5.1
5
+ marshmallow==3.14.1
6
+ umongo==3.0.1
7
+ requests
8
+ bs4
9
+ git+https://github.com/cinemagoer/cinemagoer
10
+ aiohttp
11
+ pyshorteners
12
+ Pillow
runtime.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python-3.10.5
sample_info.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
start.sh ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if [ -z $UPSTREAM_REPO ]
2
+ then
3
+ echo "Cloning main Repository"
4
+ git clone https://github.com/kalanakt/baesuzy.git /EvaMaria
5
+ else
6
+ echo "Cloning Custom Repo from $UPSTREAM_REPO "
7
+ git clone $UPSTREAM_REPO /EvaMaria
8
+ fi
9
+ cd /EvaMaria
10
+ pip3 install -U -r requirements.txt
11
+ echo "Starting Bot...."
12
+ python3 bot.py
utils.py ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from pyrogram.errors import InputUserDeactivated, UserNotParticipant, FloodWait, UserIsBlocked, PeerIdInvalid
3
+ from info import AUTH_CHANNEL, LONG_IMDB_DESCRIPTION, MAX_LIST_ELM
4
+ from imdb import Cinemagoer
5
+ import asyncio
6
+ from pyrogram.types import Message
7
+ from typing import Union
8
+ import re
9
+ import os
10
+ from datetime import datetime
11
+ from typing import List
12
+ from pyrogram.types import InlineKeyboardButton
13
+ from database.users_chats_db import db
14
+ from database.tvseriesfilters import find_tvseries_filter
15
+ from bs4 import BeautifulSoup
16
+ import requests
17
+ import json
18
+ import aiohttp
19
+ from database.ia_filterdb import get_search_results
20
+ from database.notification import remove_notification
21
+ import pyshorteners
22
+
23
+ shortner = pyshorteners.Shortener()
24
+
25
+ logger = logging.getLogger(__name__)
26
+ logger.setLevel(logging.INFO)
27
+
28
+ BTN_URL_REGEX = re.compile(
29
+ r"(\[([^\[]+?)\]\((buttonurl|buttonalert):(?:/{0,2})(.+?)(:same)?\))"
30
+ )
31
+
32
+ imdb = Cinemagoer()
33
+
34
+ BANNED = {}
35
+ SMART_OPEN = '“'
36
+ SMART_CLOSE = '”'
37
+ START_CHAR = ('\'', '"', SMART_OPEN)
38
+ btns = []
39
+
40
+ # temp db for banned
41
+
42
+
43
+ class temp(object):
44
+ BANNED_USERS = []
45
+ BANNED_CHATS = []
46
+ ME = None
47
+ CURRENT = int(os.environ.get("SKIP", 2))
48
+ CANCEL = False
49
+ MELCOW = {}
50
+ U_NAME = None
51
+ B_NAME = None
52
+ SETTINGS = {}
53
+
54
+
55
+ async def is_subscribed(bot, query):
56
+ try:
57
+ user = await bot.get_chat_member(AUTH_CHANNEL, query.from_user.id)
58
+ except UserNotParticipant:
59
+ pass
60
+ except Exception as e:
61
+ logger.exception(e)
62
+ else:
63
+ if user.status != 'kicked':
64
+ return True
65
+
66
+ return False
67
+
68
+
69
+ async def get_poster(query, bulk=False, id=False, file=None):
70
+ if not id:
71
+ query = query.strip().lower()
72
+ title = query
73
+ year = re.findall('[1-2]\d{3}$', query, re.IGNORECASE)
74
+ if year:
75
+ year = list_to_str(year[:1])
76
+ title = query.replace(year, "").strip()
77
+ elif file is not None:
78
+ year = re.findall('[1-2]\d{3}', file, re.IGNORECASE)
79
+ if year:
80
+ year = list_to_str(year[:1])
81
+ else:
82
+ year = None
83
+ try:
84
+ movieid = imdb.search_movie(title.lower(), results=10)
85
+ except Exception:
86
+ return None
87
+ if not movieid:
88
+ return None
89
+ if year:
90
+ filtered = list(filter(lambda k: str(
91
+ k.get('year')) == str(year), movieid))
92
+ if not filtered:
93
+ filtered = movieid
94
+ else:
95
+ filtered = movieid
96
+ movieid = list(filter(lambda k: k.get('kind') in [
97
+ 'movie', 'tv series'], filtered))
98
+
99
+ if not movieid:
100
+ movieid = filtered
101
+ if bulk:
102
+ return movieid
103
+ movieid = movieid[0].movieID
104
+ else:
105
+ movieid = query
106
+ movie = imdb.get_movie(movieid)
107
+ if movie.get("original air date"):
108
+ date = movie["original air date"]
109
+ elif movie.get("year"):
110
+ date = movie.get("year")
111
+ else:
112
+ date = "N/A"
113
+ plot = ""
114
+ if not LONG_IMDB_DESCRIPTION:
115
+ plot = movie.get('plot')
116
+ if plot and len(plot) > 0:
117
+ plot = plot[0]
118
+ else:
119
+ plot = movie.get('plot outline')
120
+ if plot and len(plot) > 800:
121
+ plot = f"{plot[:800]}..."
122
+ return {'title': movie.get('title'), 'votes': movie.get('votes'), "aka": list_to_str(movie.get("akas")), "seasons": movie.get("number of seasons"), "box_office": movie.get('box office'), 'localized_title': movie.get('localized title'), 'kind': movie.get("kind"), "imdb_id": f"tt{movie.get('imdbID')}", "cast": list_to_str(movie.get("cast")), "runtime": list_to_str(movie.get("runtimes")), "countries": list_to_str(movie.get("countries")), "certificates": list_to_str(movie.get("certificates")), "languages": list_to_str(movie.get("languages")), "director": list_to_str(movie.get("director")), "writer": list_to_str(movie.get("writer")), "producer": list_to_str(movie.get("producer")), "composer": list_to_str(movie.get("composer")), "cinematographer": list_to_str(movie.get("cinematographer")), "music_team": list_to_str(movie.get("music department")), "distributors": list_to_str(movie.get("distributors")), 'release_date': date, 'year': movie.get('year'), 'genres': list_to_str(movie.get("genres")), 'poster': movie.get('full-size cover url'), 'plot': plot, 'rating': str(movie.get("rating")), 'url': f'https://www.imdb.com/title/tt{movieid}'}
123
+ # https://github.com/odysseusm
124
+
125
+
126
+ async def broadcast_notification(user_id, message):
127
+ try:
128
+ await message.copy(chat_id=user_id)
129
+ return True, "Succes"
130
+ except FloodWait as e:
131
+ await asyncio.sleep(e.x)
132
+ return await broadcast_notification(user_id, message)
133
+ except InputUserDeactivated:
134
+ await remove_notification(user_id)
135
+ logging.info(
136
+ f"{user_id}-Removed from Database, since deleted account.")
137
+ return False, "Deleted"
138
+ except UserIsBlocked:
139
+ logging.info(f"{user_id} -Blocked the bot.")
140
+ return False, "Blocked"
141
+ except PeerIdInvalid:
142
+ await remove_notification(user_id)
143
+ logging.info(f"{user_id} - PeerIdInvalid")
144
+ return False, "Error"
145
+ except Exception as e:
146
+ return False, "Error"
147
+
148
+
149
+ async def broadcast_messages(user_id, message):
150
+ try:
151
+ await message.copy(chat_id=user_id)
152
+ return True, "Succes"
153
+ except FloodWait as e:
154
+ await asyncio.sleep(e.x)
155
+ return await broadcast_messages(user_id, message)
156
+ except InputUserDeactivated:
157
+ await db.delete_user(int(user_id))
158
+ logging.info(
159
+ f"{user_id}-Removed from Database, since deleted account.")
160
+ return False, "Deleted"
161
+ except UserIsBlocked:
162
+ logging.info(f"{user_id} -Blocked the bot.")
163
+ return False, "Blocked"
164
+ except PeerIdInvalid:
165
+ await db.delete_user(int(user_id))
166
+ logging.info(f"{user_id} - PeerIdInvalid")
167
+ return False, "Error"
168
+ except Exception as e:
169
+ return False, "Error"
170
+
171
+
172
+ async def search_gagala(text):
173
+ usr_agent = {
174
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
175
+ 'Chrome/61.0.3163.100 Safari/537.36'
176
+ }
177
+ text = text.replace(" ", '+')
178
+ url = f'https://www.google.com/search?q={text}'
179
+ response = requests.get(url, headers=usr_agent)
180
+ response.raise_for_status()
181
+ soup = BeautifulSoup(response.text, 'html.parser')
182
+ titles = soup.find_all('h3')
183
+ return [title.getText() for title in titles]
184
+
185
+
186
+ async def get_settings(group_id):
187
+ settings = temp.SETTINGS.get(group_id)
188
+ if not settings:
189
+ settings = await db.get_settings(group_id)
190
+ temp.SETTINGS[group_id] = settings
191
+ return settings
192
+
193
+
194
+ async def save_group_settings(group_id, key, value):
195
+ current = await get_settings(group_id)
196
+ current[key] = value
197
+ temp.SETTINGS[group_id] = current
198
+ await db.update_settings(group_id, current)
199
+
200
+
201
+ async def send_more_files(name):
202
+ name = get_name(name)
203
+ name = name.split(".")[:3]
204
+ name = ' '.join(name)
205
+ name = name.split(" ")[:3]
206
+ name = ' '.join(name)
207
+ files, offset, total_results = await get_search_results(name.lower(), offset=0, filter=True)
208
+ if len(files) > 15:
209
+ files = files[:15]
210
+ if files:
211
+ return files
212
+
213
+
214
+ def get_size(size):
215
+ """Get size in readable format"""
216
+
217
+ units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"]
218
+ size = float(size)
219
+ i = 0
220
+ while size >= 1024.0 and i < len(units):
221
+ i += 1
222
+ size /= 1024.0
223
+ return "%.2f %s" % (size, units[i])
224
+
225
+
226
+ def get_name(name):
227
+ name = name.lower()
228
+ name = name.replace("@cc", '')
229
+ name = name.replace("telegram", '')
230
+ name = name.replace("www", '')
231
+ name = name.replace("join", '')
232
+ name = name.replace("tg", '')
233
+ name = name.replace("link", '')
234
+ name = name.replace("@", '')
235
+ name = name.replace("Team_Tony", '')
236
+ name = name.replace("massmovies0", '')
237
+ name = name.replace("bullmoviee", '')
238
+ name = name.replace("massmovies", '')
239
+ name = name.replace("filmy4cab", '')
240
+ name = name.replace("maassmovies", '')
241
+ name = name.replace("theproffesorr", '')
242
+ name = name.replace("primeroom", '')
243
+ name = name.replace("team_hdt", '')
244
+ name = name.replace("Pulikesi_Meme", '')
245
+ name = name.replace("telugudubbing", '')
246
+ name = name.replace("rickychannel", '')
247
+ name = name.replace("tif", '')
248
+ name = name.replace("cvm", '')
249
+ name = name.replace("playtk", '')
250
+ name = name.replace("tel", '')
251
+ name = name.replace("hw", '')
252
+ name = name.replace("f&t", '')
253
+ name = name.replace("fimy", '')
254
+ name = name.replace("film", '')
255
+ name = name.replace("xyz", '')
256
+ name = name.replace("fbm", '')
257
+ name = name.replace("mwkott", '')
258
+ name = name.replace("team_hdt", '')
259
+ name = name.replace("worldcinematoday", '')
260
+ name = name.replace("cinematic_world", '')
261
+ name = name.replace("cinema", '')
262
+ name = name.replace("hotstar", '')
263
+ name = name.replace("jesseverse", '')
264
+ name = name.replace("apdackup", '')
265
+ name = name.replace("streamersHub", '')
266
+ name = name.replace("tg", '')
267
+ name = name.replace("movies", '')
268
+ name = name.replace("[ava]", '')
269
+ name = name.replace("tamilrockers", '')
270
+ name = name.replace("imax5", '')
271
+ name = name.replace("kerala rock", '')
272
+ name = name.replace("ott", '')
273
+ name = name.replace("rarefilms", '')
274
+ name = name.replace("linkzz", '')
275
+ name = name.replace("movems", '')
276
+ name = name.replace("moviezz", '')
277
+ name = name.replace("clipmate", '')
278
+ name = name.replace("southtamilall", '')
279
+ name = name.replace("apdbackup", '')
280
+ name = name.replace("wmr", '')
281
+ name = name.replace("web", '')
282
+ name = name.replace("rowdystudios", '')
283
+ name = name.replace("alpacinodump", '')
284
+ name = name.replace("fans", '')
285
+ name = name.replace("movie", '')
286
+ name = name.replace("mlf", '')
287
+ name = name.replace("[rmk]", '')
288
+ name = name.replace("[mc]", '')
289
+ name = name.replace("[mfa]", '')
290
+ name = name.replace("[mm]", '')
291
+ name = name.replace("[me]", '')
292
+ name = name.replace("[", '')
293
+ name = name.replace("]", '')
294
+ name = name.replace("mlm", '')
295
+ name = name.replace("RMK", '')
296
+ name = name.replace("1tamilmv", '')
297
+ name = name.replace("linkz", '')
298
+ name = name.replace("tamilMob", '')
299
+ name = name.replace("tg", '')
300
+ name = name.replace("bollyarchives", '')
301
+ name = name.replace("🎞", '')
302
+ name = name.replace("🎬", '')
303
+ name = name.replace("(", '')
304
+ name = name.replace(")", '')
305
+ name = name.replace(" ", '.')
306
+ name = name.replace("_", '.')
307
+ name = name.replace("...", '.')
308
+ name = name.replace("..", '.')
309
+
310
+ if name[0] == '.':
311
+ name = name[1:]
312
+ name = name.capitalize()
313
+ return name
314
+
315
+
316
+ def getseries(name):
317
+ name = name.lower()
318
+ name = name.replace("season", "")
319
+ name = name.replace("series", "")
320
+ name = name.replace("tv", "")
321
+ name = name.replace("episode", "")
322
+ name = name.replace("480p", "")
323
+ name = name.replace("720p", "")
324
+ name = name.replace("1080p", "")
325
+ name = name.replace("hindi", "")
326
+ name = name.replace("tamil", "")
327
+ name = name.replace("english", "")
328
+ name = name.replace("web", "")
329
+ # name = ''.join([i for i in name if not i.isdigit()])
330
+ name = name.replace(" ", "")
331
+ return name
332
+
333
+
334
+ def gen_url(link):
335
+ link = f"https://rocklinks.net/st?api=85b949240ee33cb797db1efc7aa94cb265c6ad35&url={link}"
336
+ try:
337
+ urllink = shortner.tinyurl.short(link)
338
+ except Exception:
339
+ urllink = link
340
+ return urllink
341
+
342
+
343
+ def split_list(l, n):
344
+ for i in range(0, len(l), n):
345
+ yield l[i:i + n]
346
+
347
+
348
+ def get_file_id(msg: Message):
349
+ if msg.media:
350
+ for message_type in ("photo", "animation", "audio", "document", "video", "video_note", "voice", "sticker"):
351
+ obj = getattr(msg, message_type)
352
+ if obj:
353
+ setattr(obj, "message_type", message_type)
354
+ return obj
355
+
356
+
357
+ def extract_user(message: Message) -> Union[int, str]:
358
+ """extracts the user from a message"""
359
+ # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7
360
+ user_id = None
361
+ user_first_name = None
362
+ if message.reply_to_message:
363
+ user_id = message.reply_to_message.from_user.id
364
+ user_first_name = message.reply_to_message.from_user.first_name
365
+
366
+ elif len(message.command) > 1:
367
+ if (
368
+ len(message.entities) > 1 and
369
+ message.entities[1].type == "text_mention"
370
+ ):
371
+
372
+ required_entity = message.entities[1]
373
+ user_id = required_entity.user.id
374
+ user_first_name = required_entity.user.first_name
375
+ else:
376
+ user_id = message.command[1]
377
+ # don't want to make a request -_-
378
+ user_first_name = user_id
379
+ try:
380
+ user_id = int(user_id)
381
+ except ValueError:
382
+ pass
383
+ else:
384
+ user_id = message.from_user.id
385
+ user_first_name = message.from_user.first_name
386
+ return (user_id, user_first_name)
387
+
388
+
389
+ def list_to_str(k):
390
+ if not k:
391
+ return "N/A"
392
+ elif len(k) == 1:
393
+ return str(k[0])
394
+ elif MAX_LIST_ELM:
395
+ k = k[:int(MAX_LIST_ELM)]
396
+ return ' '.join(f'{elem}, ' for elem in k)
397
+ else:
398
+ return ' '.join(f'{elem}, ' for elem in k)
399
+
400
+
401
+ def last_online(from_user):
402
+ time = ""
403
+ if from_user.is_bot:
404
+ time += "🤖 Bot :("
405
+ elif from_user.status == 'recently':
406
+ time += "Recently"
407
+ elif from_user.status == 'within_week':
408
+ time += "Within the last week"
409
+ elif from_user.status == 'within_month':
410
+ time += "Within the last month"
411
+ elif from_user.status == 'long_time_ago':
412
+ time += "A long time ago :("
413
+ elif from_user.status == 'online':
414
+ time += "Currently Online"
415
+ elif from_user.status == 'offline':
416
+ time += datetime.fromtimestamp(
417
+ from_user.last_online_date).strftime("%a, %d %b %Y, %H:%M:%S")
418
+ return time
419
+
420
+
421
+ def split_quotes(text: str) -> List:
422
+ if not any(text.startswith(char) for char in START_CHAR):
423
+ return text.split(None, 1)
424
+ counter = 1 # ignore first char -> is some kind of quote
425
+ while counter < len(text):
426
+ if text[counter] == "\\":
427
+ counter += 1
428
+ elif text[counter] == text[0] or (text[0] == SMART_OPEN and text[counter] == SMART_CLOSE):
429
+ break
430
+ counter += 1
431
+ else:
432
+ return text.split(None, 1)
433
+
434
+ # 1 to avoid starting quote, and counter is exclusive so avoids ending
435
+ key = remove_escapes(text[1:counter].strip())
436
+ # index will be in range, or `else` would have been executed and returned
437
+ rest = text[counter + 1:].strip()
438
+ if not key:
439
+ key = text[0] + text[0]
440
+ return list(filter(None, [key, rest]))
441
+
442
+
443
+ def parser(text, keyword):
444
+ if "buttonalert" in text:
445
+ text = text.replace("\n", "\\n").replace("\t", "\\t")
446
+ buttons = []
447
+ note_data = ""
448
+ prev = 0
449
+ i = 0
450
+ alerts = []
451
+ for match in BTN_URL_REGEX.finditer(text):
452
+ n_escapes = 0
453
+ to_check = match.start(1) - 1
454
+ while to_check > 0 and text[to_check] == "\\":
455
+ n_escapes += 1
456
+ to_check -= 1
457
+ if n_escapes % 2 == 0:
458
+ note_data += text[prev:match.start(1)]
459
+ prev = match.end(1)
460
+ if match.group(3) == "buttonalert":
461
+ if bool(match.group(5)) and buttons:
462
+ buttons[-1].append(InlineKeyboardButton(text=match.group(2),
463
+ callback_data=f"alertmessage:{i}:{keyword}"))
464
+
465
+ else:
466
+ buttons.append([InlineKeyboardButton(text=match.group(
467
+ 2), callback_data=f"alertmessage:{i}:{keyword}")])
468
+
469
+ i += 1
470
+ alerts.append(match.group(4))
471
+ elif bool(match.group(5)) and buttons:
472
+ buttons[-1].append(InlineKeyboardButton(text=match.group(2),
473
+ url=match.group(4).replace(" ", "")))
474
+
475
+ else:
476
+ buttons.append([InlineKeyboardButton(
477
+ text=match.group(2), url=match.group(4).replace(" ", ""))])
478
+
479
+ else:
480
+ note_data += text[prev:to_check]
481
+ prev = match.start(1) - 1
482
+ note_data += text[prev:]
483
+ try:
484
+ return note_data, buttons, alerts
485
+ except Exception:
486
+ return note_data, buttons, None
487
+
488
+
489
+ def remove_escapes(text: str) -> str:
490
+ res = ""
491
+ is_escaped = False
492
+ for counter in range(len(text)):
493
+ if is_escaped:
494
+ res += text[counter]
495
+ is_escaped = False
496
+ elif text[counter] == "\\":
497
+ is_escaped = True
498
+ else:
499
+ res += text[counter]
500
+ return res
501
+
502
+
503
+ def humanbytes(size):
504
+ if not size:
505
+ return ""
506
+ power = 2**10
507
+ n = 0
508
+ Dic_powerN = {0: ' ', 1: 'Ki', 2: 'Mi', 3: 'Gi', 4: 'Ti'}
509
+ while size > power:
510
+ size /= power
511
+ n += 1
512
+ return f"{str(round(size, 2))} {Dic_powerN[n]}B"