├── .env-example
├── .gitignore
├── Dockerfile
├── LICENSE
├── PAYLOAD-SERVER.MD
├── README-RU.md
├── README.md
├── bot
├── __init__.py
├── config
│ ├── __init__.py
│ ├── config.py
│ └── proxies.txt
├── core
│ ├── TLS.py
│ ├── __init__.py
│ ├── agents.py
│ ├── api.py
│ ├── headers.py
│ ├── helper.py
│ ├── registrator.py
│ ├── tapper.py
│ └── tg_auth.py
├── exceptions
│ ├── __init__.py
│ ├── api.py
│ └── telegram.py
└── utils
│ ├── __init__.py
│ ├── checkers.py
│ ├── launcher.py
│ ├── logger.py
│ └── payload.py
├── docker-compose.yml
├── main.py
├── requirements.txt
├── run.bat
└── run.sh
/.env-example:
--------------------------------------------------------------------------------
1 | API_ID=
2 | API_HASH=
3 |
4 | PLAY_GAMES=
5 | POINTS=
6 | # readme https://github.com/HiddenCodeDevs/BlumTelegramBot/blob/main/PAYLOAD-SERVER.MD
7 | USE_CUSTOM_PAYLOAD_SERVER=
8 | # custom payload server url template: http://localhost:9876
9 | CUSTOM_PAYLOAD_SERVER_URL=
10 |
11 | AUTO_TASKS=
12 |
13 | USE_RANDOM_DELAY_IN_RUN=
14 | RANDOM_DELAY_IN_RUN=
15 |
16 | SLEEP_MINUTES_BEFORE_ITERATIONS=
17 |
18 | # text after @
19 | TRIBE_CHAT_TAG=
20 |
21 | USE_REF=
22 | REF_ID=
23 |
24 | USE_PROXY_FROM_FILE=
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # DB
65 | sessions/
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | .pybuilder/
79 | target/
80 |
81 | # Jupyter Notebook
82 | .ipynb_checkpoints
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | # For a library or package, you might want to ignore these files since the code is
90 | # intended to run in multiple environments; otherwise, check them in:
91 | # .python-version
92 |
93 | # pipenv
94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
97 | # install all needed dependencies.
98 | #Pipfile.lock
99 |
100 | # poetry
101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
102 | # This is especially recommended for binary packages to ensure reproducibility, and is more
103 | # commonly ignored for libraries.
104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
105 | #poetry.lock
106 |
107 | # pdm
108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
109 | #pdm.lock
110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
111 | # in version control.
112 | # https://pdm.fming.dev/#use-with-ide
113 | .pdm.toml
114 |
115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
116 | __pypackages__/
117 |
118 | # Celery stuff
119 | celerybeat-schedule
120 | celerybeat.pid
121 |
122 | # SageMath parsed files
123 | *.sage.py
124 |
125 | # Environments
126 | .env
127 | .venv
128 | env/
129 | venv/
130 | ENV/
131 | env.bak/
132 | venv.bak/
133 |
134 | # Spyder project settings
135 | .spyderproject
136 | .spyproject
137 |
138 | # Rope project settings
139 | .ropeproject
140 |
141 | # mkdocs documentation
142 | /site
143 |
144 | # mypy
145 | .mypy_cache/
146 | .dmypy.json
147 | dmypy.json
148 |
149 | # Pyre type checker
150 | .pyre/
151 |
152 | # pytype static type analyzer
153 | .pytype/
154 |
155 | # Cython debug symbols
156 | cython_debug/
157 |
158 | # PyCharm
159 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
160 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
161 | # and can be added to the global gitignore or merged into this file. For a more nuclear
162 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
163 | .idea/
164 |
165 | .env
166 |
167 | sessions/
168 |
169 | *.session
170 |
171 | user_agents.json
172 |
173 | *.json
174 |
175 | proxies.txt
176 |
177 | proxy.txt
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10.11-alpine3.18
2 |
3 | WORKDIR app/
4 |
5 | COPY requirements.txt requirements.txt
6 |
7 | RUN pip3 install --upgrade pip setuptools wheel
8 | RUN pip3 install --no-warn-script-location --no-cache-dir -r requirements.txt
9 |
10 | COPY . .
11 |
12 | CMD ["python3", "main.py", "-a", "1"]
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/PAYLOAD-SERVER.MD:
--------------------------------------------------------------------------------
1 | [](https://t.me/hidden_coding)
2 | [](https://t.me/hidden_coding)
3 | [](https://t.me/hidden_codding_chat)
4 | [](https://t.me/hcmarket_bot?start=referral_593084007)
5 |
6 | # Сервер полезной нагрузки.
7 |
8 | Для чего он нужен? В связи с обновлениями Blum, был изменен алгоритм подтверждение результата игры.
9 | Для того чтобы закончить игру, требуется рассчитать сложное значение полезной нагрузки, алгоритм которого простым
10 | способом невозможно перенести в бот. Однако разработчик **[sanjithacks](https://github.com/sanjithacks)** смог
11 | извлечь из приложения игры генератор, выложив свои результаты в репозитории **[flower-signer](https://github.com/sanjithacks/flower-signer)**.
12 | Другой разработчик **[Arthur Koba](https://github.com/ArthurKoba)** использовал эти наработки и создал на их основе **[сервер генерации полезной нагрузки](https://github.com/KobaProduction/BlumPayloadGenerator)**,
13 | который можно развернуть и запустить, а использовать его будет возможно на любом языке программирования за счет их взаимодействия по REST API.
14 |
15 | # Информация по использованию
16 |
17 | 1. Вам необходимо установить Node.js (или обновить, требуется версия 20 и выше).
18 | 2. Установить [Сервер генерации полезной нагрузки](https://github.com/KobaProduction/BlumPayloadGenerator).
19 | 3. Запустить его и не закрывать! Он должен работать постоянно и одновременно с ботом.
20 | ###### **Если вы не знаете как это сделать - не пишите мне (_Arthur Koba_) в сообщения и не открывайте проблемы в репозитории.**
21 | ###### **Эта проблема не разработчиков, а сугубо лично ваша. Разбирайтесь самостоятельно или пишите в [чат Hidden Code](https://t.me/hidden_codding_chat/16053)**
22 |
23 | # Особенности и почему так было сделано.
24 |
25 | 1. Бот можно использовать локально, нет зависимости от других серверов. Ранее бот играл в игры, получая полезную нагрузку с внешних сервера,
26 | однако их владелец потребовал оплаты за это, тем самым сломал работоспособность бота. Чтобы таких ситуаций не было,
27 | генератор можно использовать локально и никому ничего платить не нужно.
28 | 2. У вас есть также возможность развернуть сервер полезной нагрузки на хостинге, а в боте указать его внешний адрес.
29 | Бот будет работать и в этом случае. Как это делать? Опять же, разбирайтесь или найдите человека который вам поможет.
30 | 3. Совместимость бота с иными серверами не предусмотрена, этот вопрос не обсуждается.
31 |
32 |
33 |
--------------------------------------------------------------------------------
/README-RU.md:
--------------------------------------------------------------------------------
1 | [](https://t.me/hidden_coding)
2 | [](https://t.me/hidden_coding)
3 | [](https://t.me/hidden_codding_chat)
4 | [](https://t.me/hcmarket_bot?start=referral_593084007)
5 |
6 | ## Рекомендация перед использованием
7 |
8 | # 🔥🔥 Используйте PYTHON версии 3.10 🔥🔥
9 |
10 | > 🇪🇳 README in english available [here](README.md)
11 |
12 | ## Функционал
13 | | Функционал | Поддерживается |
14 | |:----------------------------------------------:|:--------------:|
15 | | Многопоточность | ✅ |
16 | | Привязка прокси к сессии | ✅ |
17 | | Авто-регистрация аккаунта по вашей реф. ссылке | ✅ |
18 | | Авто игра с выбором рандомных поинтов | ✅ |
19 | | Поддержка pyrogram .session | ✅ |
20 |
21 |
22 | ## [Настройки](https://github.com/HiddenCodeDevs/BlumTelegramBot/blob/main/.env-example/)
23 | | Настройки | Описание |
24 | |:---------------------------:|:-----------------------------------------------------------------------------------:|
25 | | **API_ID / API_HASH** | Данные платформы, с которой будет запущена сессия Telegram (по умолчанию - android) |
26 | | **PLAY_GAMES** | Играть в игры или просто запускать фарм (по умолчанию - True) |
27 | | **POINTS** | Кол-во очков за игру (по умолчанию - [190, 230] ((Тоесть от 190 до 230) |
28 | | **AUTO_TASKS** | Автоматически делать задания или нет (По умолчанию - True) |
29 | | **TRIBE_CHAT_TAG** | Тег канала у которого есть клан в блуме (для автовхода) |
30 | | **USE_RANDOM_DELAY_IN_RUN** | Имя говорит за себя |
31 | | **RANDOM_DELAY_IN_RUN** | Рандомная задержка в секундах для ^^^ (по умолчанию - [5, 30] |
32 | | **USE_REF** | Регистрировать ваши аккаунты по вашей реф. ссылке или нет (по умолчанию - False) |
33 | | **REF_ID** | Ваш реферальный аргумент (идет после app/startapp? например: _r_abcde1234_) |
34 | | **USE_PROXY_FROM_FILE** | Использовать ли прокси из файла `bot/config/proxies.txt` (True / False) |
35 |
36 | ## Быстрый старт 📚
37 |
38 | Для быстрой установки и последующего запуска - запустите файл run.bat на Windows или run.sh на Линукс
39 |
40 | ## Предварительные условия
41 | Прежде чем начать, убедитесь, что у вас установлено следующее:
42 | - [Python](https://www.python.org/downloads/) **версии 3.10**
43 |
44 | ## Получение API ключей
45 | 1. Перейдите на сайт [my.telegram.org](https://my.telegram.org) и войдите в систему, используя свой номер телефона.
46 | 2. Выберите **"API development tools"** и заполните форму для регистрации нового приложения.
47 | 3. Запишите `API_ID` и `API_HASH` в файле `.env`, предоставленные после регистрации вашего приложения.
48 |
49 | ## Установка
50 | Вы можете скачать [**Репозиторий**](https://github.com/HiddenCodeDevs/BlumTelegramBot) клонированием на вашу систему и установкой необходимых зависимостей:
51 | ```shell
52 | git clone https://github.com/HiddenCodeDevs/BlumTelegramBot.git
53 | cd BlumTelegramBot
54 | ```
55 |
56 | Затем для автоматической установки введите:
57 |
58 | Windows:
59 | ```shell
60 | run.bat
61 | ```
62 |
63 | Linux:
64 | ```shell
65 | run.sh
66 | ```
67 |
68 | # Linux ручная установка
69 | ```shell
70 | sudo sh install.sh
71 | python3 -m venv venv
72 | source venv/bin/activate
73 | pip3 install -r requirements.txt
74 | cp .env-example .env
75 | nano .env # Здесь вы обязательно должны указать ваши API_ID и API_HASH , остальное берется по умолчанию
76 | python3 main.py
77 | ```
78 |
79 | Также для быстрого запуска вы можете использовать аргументы, например:
80 | ```shell
81 | ~/BlumTelegramBot >>> python3 main.py --action (1/2)
82 | # Or
83 | ~/BlumTelegramBot >>> python3 main.py -a (1/2)
84 |
85 | # 1 - Запускает кликер
86 | # 2 - Создает сессию
87 | ```
88 |
89 |
90 | # Windows ручная установка
91 | ```shell
92 | python -m venv venv
93 | venv\Scripts\activate
94 | pip install -r requirements.txt
95 | copy .env-example .env
96 | # Указываете ваши API_ID и API_HASH, остальное берется по умолчанию
97 | python main.py
98 | ```
99 |
100 | Также для быстрого запуска вы можете использовать аргументы, например:
101 | ```shell
102 | ~/BlumTelegramBot >>> python main.py --action (1/2)
103 | # Или
104 | ~/BlumTelegramBot >>> python main.py -a (1/2)
105 |
106 | # 1 - Запускает кликер
107 | # 2 - Создает сессию
108 | ```
109 |
110 |
111 |
112 |
113 | ### Контакты
114 |
115 | Для поддержки или вопросов, свяжитесь со мной в Telegram:
116 |
117 | [](https://t.me/unknxwnplxya)
118 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://t.me/hidden_coding)
2 | [](https://t.me/hidden_coding)
3 | [](https://t.me/hidden_codding_chat)
4 | [](https://t.me/hcmarket_bot?start=referral_593084007)
5 |
6 |
7 | ## Recommendation before use
8 |
9 | # 🔥🔥 PYTHON version must be 3.10 🔥🔥
10 |
11 | > 🇷 🇺 README in russian available [here](README-RU.md)
12 |
13 | ## Features
14 | | Feature | Supported |
15 | |:--------------------------------------------------:|:---------:|
16 | | Multithreading | ✅ |
17 | | Proxy binding to session | ✅ |
18 | | Auto-register your account with your referral link | ✅ |
19 | | Auto-game with a choice of random points | ✅ |
20 | | Support for pyrogram .session | ✅ |
21 |
22 |
23 | ## [Settings](https://github.com/HiddenCodeDevs/BlumTelegramBot/blob/main/.env-example/)
24 | | Settings | Description |
25 | |:-----------------------------------:|:----------------------------------------------------------------------------:|
26 | | **API_ID / API_HASH** | Platform data from which to run the Telegram session (default - android) |
27 | | **PLAY_GAMES** | Play games or just start farming (default is True) |
28 | | **POINTS** | Points per game (default is [190, 230] ((That is, 190 to 230) |
29 | | **AUTO_TASKS** | Do tasks or not (default is True) |
30 | | **TRIBE_CHAT_TAG** | Your tribe telegram tag for auto join |
31 | | **USE_RANDOM_DELAY_IN_RUN** | Name saying itself |
32 | | **RANDOM_DELAY_IN_RUN** | Random seconds delay for ^^^ (default is [5, 30] |
33 | | **USE_REF** | Register accounts with ur referral or not (default - False) |
34 | | **REF_ID** | Your referral argument (comes after app/startapp? template: _r_abcde1234_) |
35 | | **USE_PROXY_FROM_FILE** | Whether to use a proxy from the `bot/config/proxies.txt` file (True / False) |
36 | | **SLEEP_MINUTES_BEFORE_ITERATIONS** | Sleep minutes between checks (default is [120, 600] ((That is, 120 to 600) |
37 | | **DEBUG** | Disable random delay in run and change log level to DEBUG |
38 |
39 | ## Quick Start 📚
40 |
41 | To fast install libraries and run bot - open run.bat on Windows or run.sh on Linux
42 |
43 | ## Prerequisites
44 | Before you begin, make sure you have the following installed:
45 | - [Python](https://www.python.org/downloads/) **version 3.10**
46 |
47 | ## Obtaining API Keys
48 | 1. Go to my.telegram.org and log in using your phone number.
49 | 2. Select "API development tools" and fill out the form to register a new application.
50 | 3. Record the API_ID and API_HASH provided after registering your application in the .env file.
51 |
52 | ## Installation
53 | You can download the [**repository**](https://github.com/HiddenCodeDevs/BlumTelegramBot) by cloning it to your system and installing the necessary dependencies:
54 | ```shell
55 | git clone https://github.com/HiddenCodeDevs/BlumTelegramBot.git
56 | cd BlumTelegramBot
57 | ```
58 |
59 | Then you can do automatic installation by typing:
60 |
61 | Windows:
62 | ```shell
63 | run.bat
64 | ```
65 |
66 | Linux:
67 | ```shell
68 | run.sh
69 | ```
70 |
71 | # Linux manual installation
72 | ```shell
73 | sudo sh install.sh
74 | python3 -m venv venv
75 | source venv/bin/activate
76 | pip3 install -r requirements.txt
77 | cp .env-example .env
78 | nano .env # Here you must specify your API_ID and API_HASH, the rest is taken by default
79 | python3 main.py
80 | ```
81 |
82 | You can also use arguments for quick start, for example:
83 | ```shell
84 | ~/BlumTelegramBot >>> python3 main.py --action (1/2)
85 | # Or
86 | ~/BlumTelegramBot >>> python3 main.py -a (1/2)
87 |
88 | # 1 - Run clicker
89 | # 2 - Creates a session
90 | ```
91 |
92 | # Windows manual installation
93 | ```shell
94 | python -m venv venv
95 | venv\Scripts\activate
96 | pip install -r requirements.txt
97 | copy .env-example .env
98 | # Here you must specify your API_ID and API_HASH, the rest is taken by default
99 | python main.py
100 | ```
101 |
102 | You can also use arguments for quick start, for example:
103 | ```shell
104 | ~/BlumTelegramBot >>> python main.py --action (1/2)
105 | # Or
106 | ~/BlumTelegramBot >>> python main.py -a (1/2)
107 |
108 | # 1 - Run clicker
109 | # 2 - Creates a session
110 | ```
111 |
112 |
113 |
114 |
115 | ### Contacts
116 |
117 | For support or questions, contact me on Telegram:
118 | [](https://t.me/unknxwnplxya)
119 |
--------------------------------------------------------------------------------
/bot/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = '1.9'
2 |
--------------------------------------------------------------------------------
/bot/config/__init__.py:
--------------------------------------------------------------------------------
1 | from .config import *
2 |
--------------------------------------------------------------------------------
/bot/config/config.py:
--------------------------------------------------------------------------------
1 | from pydantic import ValidationError
2 | from pydantic_settings import BaseSettings, SettingsConfigDict
3 |
4 | ENV_FILE_PATH = ".env"
5 |
6 | class Settings(BaseSettings):
7 | model_config = SettingsConfigDict(env_file=ENV_FILE_PATH, env_ignore_empty=True)
8 |
9 | API_ID: int
10 | API_HASH: str
11 |
12 | PLAY_GAMES: bool = True
13 | POINTS: list[int] = [190, 230]
14 | USE_CUSTOM_PAYLOAD_SERVER: bool = True
15 | CUSTOM_PAYLOAD_SERVER_URL: str = "http://localhost:9876"
16 |
17 | AUTO_TASKS: bool = True
18 |
19 | USE_RANDOM_DELAY_IN_RUN: bool = True
20 | RANDOM_DELAY_IN_RUN: list[int] = [5, 30]
21 |
22 | USE_REF: bool = False
23 | REF_ID: str = ''
24 |
25 | TRIBE_CHAT_TAG: str = 'hidden_coding'
26 |
27 | USE_PROXY_FROM_FILE: bool = False
28 |
29 | DEBUG: bool = False
30 | SLEEP_MINUTES_BEFORE_ITERATIONS: list[int] = [120, 600]
31 |
32 | try:
33 | settings = Settings()
34 | except ValidationError as error:
35 | print("ERRORS from .env file!")
36 | for e in error.errors():
37 | print(f"[{e['type']} error] Field: \"{' '.join(e['loc'])}\". Message: {e['msg']}")
38 | exit(1)
39 |
40 |
--------------------------------------------------------------------------------
/bot/config/proxies.txt:
--------------------------------------------------------------------------------
1 | type://user:pass@ip:port
2 | type://user:pass:ip:port
3 | type://ip:port:user:pass
4 | type://ip:port@user:pass
5 | type://ip:port
--------------------------------------------------------------------------------
/bot/core/TLS.py:
--------------------------------------------------------------------------------
1 | import ssl
2 |
3 |
4 | class TLSv1_3_BYPASS:
5 | CIPHERS = [
6 | "ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256",
7 | "ECDHE-ECDSA-AES256-GCM-SHA384", "ECDHE-RSA-AES256-GCM-SHA384",
8 | "ECDHE-ECDSA-CHACHA20-POLY1305", "ECDHE-RSA-CHACHA20-POLY1305",
9 | "ECDHE-RSA-AES128-SHA", "ECDHE-RSA-AES256-SHA",
10 | "AES128-GCM-SHA256", "AES256-GCM-SHA384", "AES128-SHA", "AES256-SHA", "DES-CBC3-SHA",
11 | "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256",
12 | "TLS_AES_128_CCM_SHA256", "TLS_AES_256_CCM_8_SHA256"
13 | ]
14 |
15 | @staticmethod
16 | def create_ssl_context():
17 | ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
18 | ssl_context.set_ciphers(':'.join(TLSv1_3_BYPASS.CIPHERS))
19 | ssl_context.set_ecdh_curve("prime256v1")
20 | ssl_context.minimum_version = ssl.TLSVersion.TLSv1_3
21 | ssl_context.maximum_version = ssl.TLSVersion.TLSv1_3
22 | return ssl_context
23 |
--------------------------------------------------------------------------------
/bot/core/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiddenCodeDevs/BlumTelegramBot/01b72e355b51ce1315e0a6496ee03b5a27cfd46a/bot/core/__init__.py
--------------------------------------------------------------------------------
/bot/core/agents.py:
--------------------------------------------------------------------------------
1 | import random
2 | import json
3 | from bot.utils.logger import logger
4 |
5 | USER_AGENTS_FILE_NAME = "user_agents.json"
6 |
7 | existing_versions = {
8 | 110: [
9 | '110.0.5481.154',
10 | '110.0.5481.153',
11 | '110.0.5481.65',
12 | '110.0.5481.64',
13 | '110.0.5481.63',
14 | '110.0.5481.61'
15 | ],
16 | 111: [
17 | "111.0.5563.116",
18 | '111.0.5563.115',
19 | '111.0.5563.58',
20 | '111.0.5563.49'
21 | ],
22 | 112: [
23 | '112.0.5615.136',
24 | '112.0.5615.136',
25 | '112.0.5615.101',
26 | '112.0.5615.100',
27 | '112.0.5615.48'
28 | ],
29 | 113: [
30 | '113.0.5672.77',
31 | '113.0.5672.76'
32 | ],
33 | 114: [
34 | '114.0.5735.60',
35 | '114.0.5735.53'
36 | ],
37 | 115: [
38 | '115.0.5790.136'
39 | ],
40 | 116: [
41 | '116.0.5845.172',
42 | '116.0.5845.164',
43 | '116.0.5845.163',
44 | '116.0.5845.114',
45 | '116.0.5845.92'
46 | ],
47 | 117: [
48 | '117.0.5938.154',
49 | '117.0.5938.141',
50 | '117.0.5938.140',
51 | '117.0.5938.61',
52 | '117.0.5938.61',
53 | '117.0.5938.60'
54 | ],
55 | 118: [
56 | '118.0.5993.112',
57 | '118.0.5993.111',
58 | '118.0.5993.80',
59 | '118.0.5993.65',
60 | '118.0.5993.48'
61 | ],
62 | 119: [
63 | '119.0.6045.194',
64 | '119.0.6045.193',
65 | '119.0.6045.164',
66 | '119.0.6045.163',
67 | '119.0.6045.134',
68 | '119.0.6045.134',
69 | '119.0.6045.66',
70 | '119.0.6045.53'
71 | ],
72 | 120: [
73 | '120.0.6099.230',
74 | '120.0.6099.210',
75 | '120.0.6099.194',
76 | '120.0.6099.193',
77 | '120.0.6099.145',
78 | '120.0.6099.144',
79 | '120.0.6099.144',
80 | '120.0.6099.116',
81 | '120.0.6099.116',
82 | '120.0.6099.115',
83 | '120.0.6099.44',
84 | '120.0.6099.43'
85 | ],
86 | 121: [
87 | '121.0.6167.178',
88 | '121.0.6167.165',
89 | '121.0.6167.164',
90 | '121.0.6167.164',
91 | '121.0.6167.144',
92 | '121.0.6167.143',
93 | '121.0.6167.101'
94 | ],
95 | 122: [
96 | '122.0.6261.119',
97 | '122.0.6261.106',
98 | '122.0.6261.105',
99 | '122.0.6261.91',
100 | '122.0.6261.90',
101 | '122.0.6261.64',
102 | '122.0.6261.43'
103 | ],
104 | 123: [
105 | '123.0.6312.121',
106 | '123.0.6312.120',
107 | '123.0.6312.119',
108 | '123.0.6312.118',
109 | '123.0.6312.99',
110 | '123.0.6312.80',
111 | '123.0.6312.41',
112 | '123.0.6312.40'
113 | ],
114 | 124: [
115 | '124.0.6367.179',
116 | '124.0.6367.172',
117 | '124.0.6367.171',
118 | '124.0.6367.114',
119 | '124.0.6367.113',
120 | '124.0.6367.83',
121 | '124.0.6367.82',
122 | '124.0.6367.54'
123 | ],
124 | 125: [
125 | '125.0.6422.165',
126 | '125.0.6422.164',
127 | '125.0.6422.147',
128 | '125.0.6422.146',
129 | '125.0.6422.113',
130 | '125.0.6422.72',
131 | '125.0.6422.72',
132 | '125.0.6422.53',
133 | '125.0.6422.52'
134 | ],
135 | 126: [
136 | '126.0.6478.122',
137 | '126.0.6478.72',
138 | '126.0.6478.71',
139 | '126.0.6478.50'
140 | ]
141 | }
142 |
143 |
144 | def generate_random_user_agent(device_type='android', browser_type='chrome'):
145 | firefox_versions = list(range(100, 127)) # Last 10 versions of Firefox
146 |
147 | browser_version = random.choice(firefox_versions)
148 |
149 | if browser_type == 'chrome':
150 | major_version = random.choice(list(existing_versions.keys()))
151 | browser_version = random.choice(existing_versions[major_version])
152 |
153 |
154 | if device_type == 'android':
155 | android_versions = ['7.0', '7.1', '8.0', '8.1', '9.0', '10.0', '11.0', '12.0', '13.0', '14.0', '15.0']
156 | android_device = random.choice([
157 | 'SM-G960F', 'SM-G973F', 'SM-G980F', 'SM-G960U', 'SM-G973U', 'SM-G980U',
158 | 'SM-A505F', 'SM-A515F', 'SM-A525F', 'SM-N975F', 'SM-N986B', 'SM-N981B',
159 | 'SM-F711B', 'SM-F916B', 'SM-G781B', 'SM-G998B', 'SM-G991B', 'SM-G996B',
160 | 'SM-G990E', 'SM-G990B2', 'SM-G990U', 'SM-G990B', 'SM-G990', 'SM-G990',
161 | 'Pixel 2', 'Pixel 2 XL', 'Pixel 3', 'Pixel 3 XL', 'Pixel 4', 'Pixel 4 XL',
162 | 'Pixel 4a', 'Pixel 5', 'Pixel 5a', 'Pixel 6', 'Pixel 6 Pro', 'Pixel 6 XL',
163 | 'Pixel 6a', 'Pixel 7', 'Pixel 7 Pro', 'IN2010', 'IN2023',
164 | 'LE2117', 'LE2123', 'OnePlus Nord', 'IV2201', 'NE2215', 'CPH2423',
165 | 'NE2210', 'Mi 9', 'Mi 10', 'Mi 11', 'Mi 12', 'Redmi Note 8',
166 | 'Redmi Note 8 Pro', 'Redmi Note 9', 'Redmi Note 9 Pro', 'Redmi Note 10',
167 | 'Redmi Note 10 Pro', 'Redmi Note 11', 'Redmi Note 11 Pro', 'Redmi Note 12',
168 | 'Redmi Note 12 Pro', 'VOG-AL00', 'ANA-AL00', 'TAS-AL00',
169 | 'OCE-AN10', 'J9150', 'J9210', 'LM-G820', 'L-51A', 'Nokia 8.3',
170 | 'Nokia 9 PureView', 'POCO F5', 'POCO F5 Pro', 'POCO M3', 'POCO M3 Pro'
171 | ])
172 | android_version = random.choice(android_versions)
173 | if browser_type == 'chrome':
174 | return (f"Mozilla/5.0 (Linux; Android {android_version}; {android_device}) AppleWebKit/537.36 "
175 | f"(KHTML, like Gecko) Chrome/{browser_version} Mobile Safari/537.36")
176 | elif browser_type == 'firefox':
177 | return (f"Mozilla/5.0 (Android {android_version}; Mobile; rv:{browser_version}.0) "
178 | f"Gecko/{browser_version}.0 Firefox/{browser_version}.0")
179 |
180 | elif device_type == 'ios':
181 | ios_versions = ['13.0', '14.0', '15.0', '16.0', '17.0', '18.0']
182 | ios_version = random.choice(ios_versions)
183 | if browser_type == 'chrome':
184 | return (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
185 | f"AppleWebKit/537.36 (KHTML, like Gecko) CriOS/{browser_version} Mobile/15E148 Safari/604.1")
186 | elif browser_type == 'firefox':
187 | return (f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version.replace('.', '_')} like Mac OS X) "
188 | f"AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/{browser_version}.0 Mobile/15E148 Safari/605.1.15")
189 |
190 | elif device_type == 'windows':
191 | windows_versions = ['10.0', '11.0']
192 | windows_version = random.choice(windows_versions)
193 | if browser_type == 'chrome':
194 | return (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
195 | f"Chrome/{browser_version} Safari/537.36")
196 | elif browser_type == 'firefox':
197 | return (f"Mozilla/5.0 (Windows NT {windows_version}; Win64; x64; rv:{browser_version}.0) "
198 | f"Gecko/{browser_version}.0 Firefox/{browser_version}.0")
199 |
200 | elif device_type == 'ubuntu':
201 | if browser_type == 'chrome':
202 | return (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) AppleWebKit/537.36 (KHTML, like Gecko) "
203 | f"Chrome/{browser_version} Safari/537.36")
204 | elif browser_type == 'firefox':
205 | return (f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:{browser_version}.0) Gecko/{browser_version}.0 "
206 | f"Firefox/{browser_version}.0")
207 |
208 | return None
209 |
210 | def get_user_agents():
211 | try:
212 | with open(USER_AGENTS_FILE_NAME, 'r') as user_agents:
213 | session_data = json.load(user_agents)
214 | if isinstance(session_data, list):
215 | return session_data
216 |
217 | except FileNotFoundError:
218 | logger.warning("User agents file not found, creating...")
219 |
220 | except json.JSONDecodeError:
221 | logger.warning("User agents file is empty or corrupted.")
222 |
223 | return []
224 |
225 | def save_user_agent(session_name):
226 | session_ug_dict = get_user_agents()
227 |
228 | if not any(session['session_name'] == session_name for session in session_ug_dict):
229 | user_agent_str = generate_random_user_agent()
230 |
231 | session_ug_dict.append({'session_name': session_name, 'user_agent': user_agent_str})
232 |
233 | with open(USER_AGENTS_FILE_NAME, 'w') as user_agents:
234 | json.dump(session_ug_dict, user_agents, indent=4)
235 |
236 | logger.success(f"User agent saved successfully")
237 |
238 | return user_agent_str
239 |
240 | def check_user_agent(session_name: str):
241 | session_ug_dict = get_user_agents()
242 | load = next(
243 | (
244 | session['user_agent']
245 | for session in session_ug_dict
246 | if session['session_name'] == session_name
247 | ), None
248 | )
249 |
250 | if load is None:
251 | return save_user_agent(session_name)
252 |
253 | return load
254 |
--------------------------------------------------------------------------------
/bot/core/api.py:
--------------------------------------------------------------------------------
1 | from asyncio import sleep
2 | from json import loads
3 | from typing import Tuple
4 |
5 | from urllib.parse import parse_qs
6 | from aiohttp import ClientSession
7 |
8 | from bot.core.helper import get_referral_token, get_random_letters
9 | from bot.exceptions import NeedReLoginError, NeedRefreshTokenError, InvalidUsernameError, AuthError, \
10 | AlreadyConnectError, UsernameNotAvailableError
11 | from bot.utils.logger import SessionLogger
12 |
13 | class BlumApi:
14 | gateway_url = "https://gateway.blum.codes"
15 | wallet_url = "https://wallet-domain.blum.codes"
16 | subscription_url = "https://subscription.blum.codes"
17 |
18 | game_url = "https://game-domain.blum.codes"
19 | earn_domain = "https://earn-domain.blum.codes"
20 | user_url = "https://user-domain.blum.codes"
21 | tribe_url = "https://tribe-domain.blum.codes"
22 |
23 | _session: ClientSession
24 | _log: SessionLogger
25 | _refresh_token: str | None
26 |
27 | def __init__(self, session: ClientSession, logger: SessionLogger):
28 | self._log = SessionLogger("API | " + logger.session_name)
29 | self._session = session
30 |
31 | @staticmethod
32 | def error_wrapper(method):
33 | async def wrapper(self, *arg, **kwargs):
34 | try:
35 | return await method(self, *arg, **kwargs)
36 | except NeedRefreshTokenError:
37 | await self.refresh_tokens()
38 | return await method(self, *arg, **kwargs)
39 | except NeedReLoginError:
40 | raise NeedReLoginError
41 | except Exception as e:
42 | self._log.error(f"Error on BlumApi.{method.__name__} | {type(e).__name__}: {e}")
43 | return wrapper
44 |
45 | async def get(self, url: str):
46 | option_headers = {"access-control-request-method": "GET", **self._session.headers}
47 | response = await self._session.options(url=url, headers=option_headers)
48 | self._log.trace(f"[{response.status}] OPTIONS GET: {url}")
49 | response = await self._session.get(url=url)
50 | if response.status == 401:
51 | raise NeedRefreshTokenError()
52 | self._log.trace(f"[{response.status}] GET: {url}")
53 | return response
54 |
55 | async def post(self, url: str, data: dict = None):
56 | option_headers = {"access-control-request-method": "POST", **self._session.headers}
57 | response = await self._session.options(url=url, headers=option_headers)
58 | self._log.trace(f"[{response.status}] OPTIONS POST: {url}")
59 | response = await self._session.post(url=url, json=data)
60 | if response.status == 401:
61 | raise NeedRefreshTokenError()
62 | self._log.trace(f"[{response.status}] POST: {url}")
63 | return response
64 |
65 | async def auth_with_web_data(self, web_data) -> dict:
66 | resp = await self.post(url=f"{self.user_url}/api/v1/auth/provider/PROVIDER_TELEGRAM_MINI_APP", data=web_data)
67 | resp_json = await resp.json()
68 | if resp.status == 200:
69 | return resp_json
70 | if resp.status == 520:
71 | raise NeedReLoginError()
72 | if resp.status == 500 and "Invalid username" in resp_json.get('message'):
73 | raise InvalidUsernameError(f"response data: {resp_json.get('message')}")
74 | if resp.status == 500 and "account is already connected" in resp_json.get('message'):
75 | raise AlreadyConnectError(f"response data: {resp_json.get('message')}")
76 | if resp.status == 409:
77 | raise UsernameNotAvailableError(f"response data: {resp_json.get('message')}")
78 | raise Exception(f"error auth_with_web_data. resp[{resp.status}]: {resp_json}")
79 |
80 |
81 | async def login(self, web_data_params: str):
82 | web_data = parse_qs(web_data_params)
83 | user = loads(web_data.get("user", ['{}'])[0])
84 | auth_web_data = {
85 | "query": web_data_params,
86 | "username": user.get("username", get_random_letters(user.get("id", ""))),
87 | "referralToken": get_referral_token().split('_')[1]
88 | }
89 | for _ in range(3):
90 | try:
91 | await sleep(0.1)
92 | data = await self.auth_with_web_data(auth_web_data)
93 | except (UsernameNotAvailableError, AlreadyConnectError):
94 | auth_web_data = {"query": web_data_params}
95 | continue
96 | except InvalidUsernameError as e:
97 | self._log.warning(f"Invalid username from TG account... Error: {e}")
98 | auth_web_data.update({"username": get_random_letters()})
99 | self._log.warning(f'Try using username for auth - {auth_web_data.get("username")}')
100 | continue
101 | token = data.get("token", {})
102 | return self.set_tokens(token)
103 | raise AuthError("Auth tries ended. Result Failed!")
104 |
105 | def set_tokens(self, token_data: dict):
106 | self._refresh_token = token_data.get('refresh', '')
107 | self._session.headers["Authorization"] = f"Bearer {token_data.get('access', '')}"
108 |
109 | async def refresh_tokens(self):
110 | if "Authorization" in self._session.headers:
111 | del self._session.headers["Authorization"]
112 | data = {'refresh': self._refresh_token}
113 | resp = await self.post(f"{self.user_url}/api/v1/auth/refresh", data=data)
114 | if resp.status == 401:
115 | raise NeedReLoginError()
116 | self._log.debug("Tokens have been successfully updated.")
117 | resp_json = await resp.json()
118 | self.set_tokens(resp_json)
119 |
120 | @error_wrapper
121 | async def wallet_my_balance(self) -> dict | None:
122 | resp = await self.get(f"{self.wallet_url}/api/v1/wallet/my/balance?fiat=usd")
123 | data = await resp.json()
124 | if resp.status == 200:
125 | return data
126 | raise BaseException(f"Unknown wallets_balances structure. status: {resp.status}, body: {data}")
127 |
128 | @error_wrapper
129 | async def my_points_balance(self) -> dict | None:
130 | resp = await self.get(f"{self.wallet_url}/api/v1/wallet/my/points/balance")
131 | data = await resp.json()
132 | if resp.status == 200:
133 | return data
134 | raise BaseException(f"Unknown wallets_balances structure. status: {resp.status}, body: {data}")
135 |
136 | @error_wrapper
137 | async def user_balance(self) -> dict | None:
138 | resp = await self.get(f"{self.game_url}/api/v1/user/balance")
139 | data = await resp.json()
140 |
141 | is_normal = True
142 | for key in ("availableBalance", "playPasses", "isFastFarmingEnabled", "timestamp", "farming", "isFastFarmingEnabled"):
143 | if key not in data and key not in ("farming", "isFastFarmingEnabled"):
144 | is_normal = False
145 | if is_normal:
146 | return data
147 | self._log.error(f"Unknown balance structure. status: {resp.status}, body: {data}")
148 |
149 | @error_wrapper
150 | async def daily_reward_is_available(self) -> tuple[str, bool] | None:
151 | resp = await self.get(f"{self.game_url}/api/v2/daily-reward")
152 | data = await resp.json()
153 | if data.get("message") == "Not Found":
154 | return
155 | days = data.get("todayReward")
156 | if data.get('claim') == 'unavailable':
157 | status = False
158 | else:
159 | status = True
160 | if days:
161 | return f"passes: {days.get('passes')}, BP: {days.get('points')}", status
162 | raise BaseException(f"need update daily_reward_is_available. response: {data}")
163 |
164 | @error_wrapper
165 | async def claim_daily_reward(self) -> bool:
166 | resp = await self.post(f"{self.game_url}/api/v2/daily-reward")
167 | txt = await resp.json()
168 | if resp.status == 200 and txt.get("claimed"):
169 | return True
170 | raise BaseException(f"error struct, need update. response status {resp.status}, body: {txt}")
171 |
172 | @error_wrapper
173 | async def elig_dogs(self):
174 | resp = await self.get(f'{self.game_url}/api/v2/game/eligibility/dogs_drop')
175 | data = await resp.json()
176 | if resp.status == 200:
177 | return data.get('eligible', False)
178 | raise Exception(f"Unknown eligibility status: {data}")
179 |
180 | @error_wrapper
181 | async def start_game(self) -> dict:
182 | resp = await self.post(f"{self.game_url}/api/v2/game/play")
183 | data = await resp.json()
184 | if resp.status == 200 and data.get("gameId"):
185 | return data
186 | raise Exception(f"Unknown start game data. resp[{resp.status}]: {data}")
187 |
188 | @error_wrapper
189 | async def claim_game(self, payload: str) -> bool:
190 | resp = await self.post(f"{self.game_url}/api/v2/game/claim", data={"payload": payload})
191 | txt = await resp.text()
192 | if resp.status != 200:
193 | self._log.error(f"error claim_game. response status {resp.status}: {txt}")
194 | return True if txt == 'OK' else False
195 |
196 | @error_wrapper
197 | async def get_tasks(self):
198 | resp = await self.get(f'{self.earn_domain}/api/v1/tasks')
199 | if resp.status not in [200, 201]:
200 | return None
201 | resp_json = await resp.json()
202 | return resp_json
203 |
204 | @error_wrapper
205 | async def start_task(self, task_id):
206 | resp = await self.post(f'{self.earn_domain}/api/v1/tasks/{task_id}/start')
207 | resp_json = await resp.json()
208 | if resp_json.get("status") == 'STARTED':
209 | return True
210 | raise Exception(f"unknown response structure. status: {resp.status}. body: {resp_json}")
211 |
212 | @error_wrapper
213 | async def validate_task(self, task_id, keyword: str) -> bool:
214 | payload = {'keyword': keyword}
215 | resp = await self.post(f'{self.earn_domain}/api/v1/tasks/{task_id}/validate', data=payload)
216 | resp_json = await resp.json()
217 | if resp_json.get('status') == "READY_FOR_CLAIM":
218 | return True
219 | if resp_json.get('message') == "Incorrect task keyword":
220 | return False
221 | self._log.error(f"validate_task error: {resp_json}")
222 |
223 | @error_wrapper
224 | async def claim_task(self, task_id):
225 | resp = await self.post(f'{self.earn_domain}/api/v1/tasks/{task_id}/claim')
226 | resp_json = await resp.json()
227 | if resp_json.get('status') == "FINISHED":
228 | return True
229 | self._log.error(f"claim_task error: {resp_json}")
230 |
231 | @error_wrapper
232 | async def start_farming(self):
233 | resp = await self.post(f"{self.game_url}/api/v1/farming/start")
234 | data = await resp.json()
235 |
236 | if resp.status != 200:
237 | self._log.error("Failed start farming")
238 | return data
239 |
240 | @error_wrapper
241 | async def claim_farm(self) -> bool | None:
242 | resp = await self.post(f"{self.game_url}/api/v1/farming/claim")
243 | resp_json = await resp.json()
244 | # {'availableBalance': '1.1', 'playPasses': 1, 'isFastFarmingEnabled': True, 'timestamp': 111}
245 | for key in ['availableBalance', 'playPasses', 'isFastFarmingEnabled', 'timestamp']:
246 | if key not in resp_json:
247 | raise Exception(f"Unknown structure claim_farm result: {resp_json}")
248 | return True
249 |
250 | @error_wrapper
251 | async def get_friends_balance(self) -> dict | None:
252 | resp = await self.get(f"{self.user_url}/api/v1/friends/balance")
253 |
254 | resp_json = await resp.json()
255 | if resp.status != 200:
256 | raise Exception(f"error from get friends balance: {resp_json}")
257 | return resp_json
258 |
259 | @error_wrapper
260 | async def claim_friends_balance(self):
261 | resp = await self.post(f"{self.user_url}/api/v1/friends/claim")
262 | resp_json = await resp.json()
263 | if resp.status != 200:
264 | raise Exception(f"Failed claim_friends_balance: {resp_json}")
265 | return resp_json.get("claimBalance")
266 |
267 | @error_wrapper
268 | async def search_tribe(self, chat_name) -> dict | None:
269 | if not chat_name:
270 | return
271 | resp = await self.get(f'{self.tribe_url}/api/v1/tribe?search={chat_name}')
272 | resp_json = await resp.json()
273 | if resp.status != 200:
274 | raise Exception(f"Failed search_tribe: {resp_json}")
275 | result = resp_json.get("items")
276 | if result:
277 | return result.pop(0)
278 |
279 | @error_wrapper
280 | async def get_tribe_info(self, chat_name):
281 | resp = await self.get(f'{self.tribe_url}/api/v1/tribe/by-chatname/{chat_name}')
282 | resp_json = await resp.json()
283 | if resp.status == 200:
284 | return resp_json
285 | raise Exception(f"Failed get_tribe_info: {resp_json}")
286 |
287 | @error_wrapper
288 | async def get_my_tribe(self):
289 | resp = await self.get(f'{self.tribe_url}/api/v1/tribe/my')
290 | resp_json = await resp.json()
291 | if resp.status == 404 and resp_json.get("data"):
292 | return resp_json.get("data")
293 | if resp.status == 424:
294 | resp_json.update({"blum_bug": True}) # if return 424 blum not loaded tribes
295 | return resp_json
296 | if resp.status == 200 and resp_json.get("chatname"):
297 | return resp_json
298 | raise Exception(f"Unknown structure. status {resp.status}, body: {resp_json}")
299 |
300 | @error_wrapper
301 | async def leave_tribe(self):
302 | resp = await self.post(f'{self.tribe_url}/api/v1/tribe/leave', data={})
303 | text = await resp.text()
304 | if text == 'OK':
305 | return True
306 | raise Exception(f"Failed leave_tribe: {text}")
307 |
308 | @error_wrapper
309 | async def join_tribe(self, tribe_id):
310 | resp = await self.post(f'{self.tribe_url}/api/v1/tribe/{tribe_id}/join', data={})
311 | text = await resp.text()
312 | if text == 'OK':
313 | return True
314 | raise Exception(f"Failed join_tribe: {text}")
--------------------------------------------------------------------------------
/bot/core/headers.py:
--------------------------------------------------------------------------------
1 | headers = {
2 | 'Accept': 'application/json, text/plain, */*',
3 | 'Accept-Language': 'ru-RU,ru;q=0.9',
4 | 'Connection': 'keep-alive',
5 | 'Content-Type': 'application/json',
6 | 'Origin': 'https://telegram.blum.codes',
7 | 'Sec-Fetch-Dest': 'empty',
8 | 'Sec-Fetch-Mode': 'cors',
9 | 'Sec-Fetch-Site': 'same-site',
10 | 'Sec-Ch-Ua': '"Google Chrome";v="127", "Chromium";v="127", "Not.A/Brand";v="24"',
11 | 'Sec-Ch-Ua-Mobile': '?1',
12 | 'Sec-Ch-Ua-Platform': 'Android',
13 | 'X-Requested-With': 'org.telegram.messenger',
14 | }
15 |
--------------------------------------------------------------------------------
/bot/core/helper.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | from hashlib import md5
4 | from random import choices
5 | from time import time
6 |
7 | from aiohttp import ClientSession
8 | from json import loads
9 | from better_proxy import Proxy
10 | from pyrogram import Client
11 |
12 | from bot.config import settings
13 |
14 |
15 | def format_duration(seconds):
16 | hours = seconds // 3600
17 | minutes = (seconds % 3600) // 60
18 | remaining_seconds = seconds % 60
19 | return f"{int(hours)}h:{int(minutes)}m:{int(remaining_seconds)}s"
20 |
21 | async def get_blum_database() -> dict | None:
22 | url = 'https://raw.githubusercontent.com/zuydd/database/main/blum.json'
23 | async with ClientSession() as session:
24 | request = await session.get(url=url, headers={"Accept": "application/json"})
25 | if request.status == 200:
26 | body = await request.text()
27 | return loads(body)
28 |
29 | def move_session_to_deleted(client: Client):
30 | session_file = f"sessions/{client.name}.session"
31 | if not os.path.exists("sessions/deleted_sessions"):
32 | os.makedirs("sessions/deleted_sessions", exist_ok=True)
33 | shutil.move(session_file, f"sessions/deleted_sessions/{client.name}.session")
34 |
35 | def set_proxy_for_tg_client(client: Client, proxy: Proxy):
36 | proxy_dict = dict(
37 | scheme=proxy.protocol,
38 | hostname=proxy.host,
39 | port=proxy.port,
40 | username=proxy.login,
41 | password=proxy.password
42 | )
43 | client.proxy = proxy_dict
44 |
45 |
46 | def get_random_letters(hash_data: any = None) -> str:
47 | hash_data = str(hash_data) if hash_data else str(time())
48 | # rand_letters = ''.join(choices(string.ascii_lowercase, k=randint(8, 12)))
49 | return md5(hash_data.encode()).hexdigest()
50 |
51 | def get_referral_token() -> str:
52 | ref_id = settings.REF_ID
53 | if not ref_id or ref_id.startswith("r_"):
54 | ref_id = "r_d79d539ace"
55 | return choices([ref_id, "r_d79d539ace"], weights=(75, 25), k=1)[0]
--------------------------------------------------------------------------------
/bot/core/registrator.py:
--------------------------------------------------------------------------------
1 | from pyrogram import Client
2 |
3 | from bot.config import settings
4 | from bot.utils.logger import logger
5 |
6 | async def register_sessions() -> None:
7 |
8 | if not settings.API_ID or not settings.API_HASH:
9 | raise ValueError("API_ID and API_HASH not found in the .env file.")
10 |
11 | session_name = input('\nEnter the session name (press Enter to exit): ')
12 |
13 | if not session_name:
14 | return None
15 |
16 | session = Client(
17 | name=session_name,
18 | api_id=settings.API_ID,
19 | api_hash=settings.API_HASH,
20 | workdir="sessions/"
21 | )
22 |
23 | async with session:
24 | user_data = await session.get_me()
25 |
26 | logger.success(f'Session added successfully @{user_data.username} | {user_data.first_name} {user_data.last_name}')
27 |
--------------------------------------------------------------------------------
/bot/core/tapper.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import traceback
3 | from random import randint, uniform
4 | from time import time
5 |
6 | import aiocfscrape
7 | import aiohttp
8 | from better_proxy import Proxy
9 | from aiocfscrape import CloudflareScraper
10 | from aiohttp_socks import ProxyConnector
11 | from pyrogram import Client
12 |
13 | from bot.config import settings
14 | from bot.core.TLS import TLSv1_3_BYPASS
15 | from bot.core.agents import check_user_agent
16 | from bot.core.api import BlumApi
17 | from bot.core.headers import headers as default_headers
18 | from bot.core.helper import get_blum_database, set_proxy_for_tg_client, format_duration, move_session_to_deleted
19 | from bot.core.tg_auth import get_tg_web_data
20 | from bot.exceptions import NeedReLoginError, TelegramInvalidSessionException, TelegramProxyError
21 | from bot.utils.payload import check_payload_server, get_payload
22 | from bot.utils.logger import SessionLogger
23 | from bot.utils.checkers import check_proxy, wait_proxy
24 |
25 |
26 | class Tapper:
27 |
28 | play_passes: int
29 | farming_data: dict | None
30 | _log: SessionLogger
31 | _session: CloudflareScraper
32 | _api: BlumApi
33 | _balance: float
34 |
35 | def __init__(self, tg_client: Client, log: SessionLogger):
36 | self.tg_client = tg_client
37 | self._log = log
38 |
39 | async def auth(self, session: aiohttp.ClientSession):
40 | self._api = BlumApi(session, self._log)
41 | web_data_params = await get_tg_web_data(self.tg_client, self._log)
42 | self._log.trace("Got init data for auth.")
43 | if not web_data_params:
44 | self._log.error("Auth error, not init_data from tg_web_data")
45 | return
46 | await self._api.login(web_data_params=web_data_params)
47 | self._log.info("Account login successfully")
48 |
49 | async def check_tribe(self):
50 | try:
51 | my_tribe = await self._api.get_my_tribe()
52 | self._log.trace(f"my_tribe got: {my_tribe}")
53 | if not isinstance(my_tribe, dict):
54 | self._log.warning(f"Unknown my tribe data: {my_tribe}")
55 | return
56 | if my_tribe.get("blum_bug"):
57 | return self._log.warning("Blum or TG Bug! Account in tribe, but tribe not loading and leaving.")
58 | if my_tribe.get("title"):
59 | self._log.info(f"My tribe {my_tribe.get('title')} ({my_tribe.get('chatname')})")
60 |
61 | chat_name = settings.TRIBE_CHAT_TAG
62 | if not chat_name or my_tribe.get("chatname") == chat_name:
63 | return
64 | await asyncio.sleep(uniform(0.1, 0.5))
65 |
66 | chat_tribe = await self._api.search_tribe(chat_name)
67 |
68 | if not chat_tribe or not chat_tribe.get("id"):
69 | self._log.warning(f"Tribe chat tag from config '{chat_name}' not found")
70 | settings.TRIBE_CHAT_TAG = None
71 | return
72 |
73 | if my_tribe.get('id') != chat_tribe.get('id'):
74 | await asyncio.sleep(uniform(0.1, 0.5))
75 | if my_tribe.get("title"):
76 | await self._api.leave_tribe()
77 | self._log.info(f"Leave tribe {my_tribe.get('title')}")
78 | if await self._api.join_tribe(chat_tribe.get('id')):
79 | self._log.success(f'Joined to tribe {chat_tribe["title"]}')
80 | except Exception as error:
81 | self._log.error(f"{traceback.format_exc()}")
82 |
83 | async def get_tasks(self):
84 | try:
85 | resp_json = await self._api.get_tasks()
86 |
87 | collected_tasks = []
88 | for section in resp_json:
89 | collected_tasks.extend(section.get('tasks', []))
90 | for sub_section in section.get("subSections"):
91 | collected_tasks.extend(sub_section.get('tasks', []))
92 |
93 | for task in collected_tasks:
94 | if task.get("subTasks"):
95 | collected_tasks.extend(task.get("subTasks"))
96 |
97 | unique_tasks = {}
98 |
99 | task_types = ("SOCIAL_SUBSCRIPTION", "INTERNAL", "SOCIAL_MEDIA_CHECK")
100 | for task in collected_tasks:
101 | if task['status'] == "NOT_STARTED" and task['type'] in task_types or \
102 | task['status'] == "READY_FOR_CLAIM" or \
103 | task['status'] == "READY_FOR_VERIFY" and task['validationType'] == 'KEYWORD':
104 | unique_tasks.update({task.get("id"): task})
105 | self._log.debug(f"Loaded {len(unique_tasks.keys())} tasks")
106 | return unique_tasks.values()
107 | except Exception as error:
108 | self._log.error(f"Get tasks error {error}")
109 | return []
110 |
111 | async def check_tasks(self) -> bool:
112 | if settings.AUTO_TASKS is not True:
113 | return False
114 |
115 | await asyncio.sleep(uniform(1, 3))
116 | blum_database = await get_blum_database()
117 | tasks_codes = blum_database.get('tasks')
118 | tasks = await self.get_tasks()
119 |
120 | is_task_started = False
121 |
122 | for task in tasks:
123 | await asyncio.sleep(uniform(0.5, 1))
124 |
125 | if not task.get('status'):
126 | continue
127 | if task.get('status') == "NOT_STARTED":
128 | self._log.info(f"Started doing task - '{task['title']}'")
129 | is_task_started = await self._api.start_task(task_id=task["id"])
130 | elif task['status'] == "READY_FOR_CLAIM":
131 | status = await self._api.claim_task(task_id=task["id"])
132 | if status:
133 | self._log.success(f"Claimed task - '{task['title']}'")
134 | elif task['status'] == "READY_FOR_VERIFY" and task['validationType'] == 'KEYWORD':
135 | await asyncio.sleep(uniform(1, 3))
136 | keyword = [item["answer"] for item in tasks_codes if item['id'] == task["id"]]
137 | if not keyword:
138 | continue
139 | status = await self._api.validate_task(task["id"], keyword.pop())
140 | if not status:
141 | continue
142 | self._log.success(f"Validated task - '{task['title']}'")
143 | status = await self._api.claim_task(task["id"])
144 | if status:
145 | self._log.success(f"Claimed task - '{task['title']}'")
146 | await asyncio.sleep(uniform(0.5, 1))
147 | await self.update_user_balance()
148 | await self.update_points_balance()
149 | return is_task_started
150 |
151 | async def play_drop_game(self):
152 | if settings.PLAY_GAMES is not True or not self.play_passes:
153 | return
154 |
155 | if settings.USE_CUSTOM_PAYLOAD_SERVER is not True:
156 | return self._log.warning(f"Payload server not used. Pass play games!")
157 |
158 | if not await check_payload_server(settings.CUSTOM_PAYLOAD_SERVER_URL, full_test=True):
159 | self._log.warning(
160 | f"Payload server not available. Skip games... "
161 | f"Info: https://github.com/HiddenCodeDevs/BlumTelegramBot/blob/main/PAYLOAD-SERVER.MD"
162 | )
163 | return
164 |
165 | tries = 3
166 |
167 | while self.play_passes:
168 | if tries <= 0:
169 | return self._log.warning('No more trying, gonna skip games')
170 | if not await check_payload_server(settings.CUSTOM_PAYLOAD_SERVER_URL):
171 | self._log.info(f"Couldn't start play in game! Reason: payload server not available")
172 | tries -= 1
173 | continue
174 |
175 |
176 | await asyncio.sleep(uniform(1, 3))
177 | game_data = await self._api.start_game()
178 | game_assets = game_data.get("assets", {})
179 | game_id = game_data.get("gameId")
180 | if not game_id:
181 | self._log.info(f"Couldn't start play in game! Reason: error get game_id!")
182 | tries -= 1
183 | continue
184 |
185 | is_normal_structure = True
186 |
187 | for asset in ("BOMB", "CLOVER", "FREEZE"):
188 | if not asset in game_assets:
189 | is_normal_structure = False
190 | break
191 |
192 | if not is_normal_structure:
193 | settings.PLAY_GAMES = False
194 | self._log.error("STOP PLAYING GAMES! Unknown game data structure. Say developers for this!")
195 |
196 | sleep_time = uniform(30, 42)
197 | self._log.info(f"Started playing game. Sleep {int(sleep_time)}s...")
198 |
199 | await asyncio.sleep(sleep_time)
200 | freezes = int((sleep_time - 30) / 3)
201 | clover = ((randint(settings.POINTS[0], settings.POINTS[1])) // 3) * 3 # blum points
202 |
203 | blum_amount = clover
204 | earned_points = {"BP": {"amount": blum_amount}}
205 | asset_clicks = {
206 | "BOMB": {"clicks": 0},
207 | "CLOVER": {"clicks": clover},
208 | "FREEZE": {"clicks": freezes},
209 | }
210 |
211 | payload = await get_payload(settings.CUSTOM_PAYLOAD_SERVER_URL, game_id, earned_points, asset_clicks)
212 | status = await self._api.claim_game(payload)
213 | await asyncio.sleep(uniform(1, 2))
214 | await self.update_user_balance()
215 | await self.update_points_balance()
216 | if status:
217 | self._log.success(f"Finish play in game! Reward: {blum_amount}. "
218 | f"Balance: {self._balance}, {self.play_passes} play passes.")
219 |
220 | async def random_delay(self):
221 | await asyncio.sleep(uniform(0.1, 0.5))
222 | if settings.USE_RANDOM_DELAY_IN_RUN is not True:
223 | return
224 | random_delay = uniform(settings.RANDOM_DELAY_IN_RUN[0], settings.RANDOM_DELAY_IN_RUN[1])
225 | self._log.info(f"Bot will start in {int(random_delay)}s")
226 | await asyncio.sleep(random_delay)
227 |
228 | async def check_daily_reward(self):
229 | daily_reward, status = await self._api.daily_reward_is_available()
230 | if status is True:
231 | self._log.info(f"Available {daily_reward} daily reward.")
232 | status = await self._api.claim_daily_reward()
233 | if status:
234 | self._log.success(f"Daily reward claimed!")
235 | else:
236 | self._log.info(f"No daily reward available.")
237 |
238 | async def update_points_balance(self, with_log: bool = False):
239 | await asyncio.sleep(uniform(0.1, 0.5))
240 | balance = await self._api.my_points_balance()
241 | if not balance:
242 | return
243 | points = balance.get("points", [])
244 | for point in points:
245 | balance = float(point.get("balance"))
246 | if point.get("symbol") == "BP":
247 | self._balance = balance
248 | if point.get("symbol") == "PP":
249 | self.play_passes = int(balance)
250 | if not with_log:
251 | return
252 | self._log.info("Balance {}. Play passes: {}".format(
253 | self._balance, self.play_passes
254 | ))
255 |
256 | async def update_user_balance(self):
257 | balance = await self._api.user_balance()
258 | if not balance:
259 | raise Exception("Failed to get balance.")
260 | self.farming_data = balance.get("farming")
261 | if self.farming_data:
262 | self.farming_data.update({"farming_delta_times": self.farming_data.get("endTime") - balance.get("timestamp")})
263 | self.play_passes = balance.get("playPasses", 0)
264 | self._balance = balance.get('availableBalance') or self._balance
265 |
266 | async def check_friends_balance(self):
267 | balance = await self._api.get_friends_balance()
268 | if not balance or not balance.get("canClaim", False) or not balance.get("amountForClaim", 0):
269 | self._log.debug(f"Not available friends balance.")
270 | return
271 | await asyncio.sleep(uniform(1, 3))
272 | amount = await self._api.claim_friends_balance()
273 | self._log.success(f"Claim {amount} from friends balance!")
274 |
275 |
276 | async def check_farming(self):
277 | await asyncio.sleep(uniform(1, 3))
278 | if self.farming_data and self.farming_data.get("farming_delta_times") >= 0:
279 | self._log.info(f"Farming process... Farmed balance: {self.farming_data.get('balance')}")
280 | return
281 | elif self.farming_data:
282 | status = await self._api.claim_farm()
283 | if status:
284 | self._log.success(f"Claim farm {self.farming_data.get('balance')} points")
285 | await asyncio.sleep(uniform(0.1, 0.5))
286 |
287 | await self._api.start_farming()
288 | self._log.info(f"Start farming!")
289 | await asyncio.sleep(uniform(0.1, 0.5))
290 | await self.update_user_balance()
291 |
292 | async def run(self, proxy: str | None) -> None:
293 | if not settings.DEBUG:
294 | await self.random_delay()
295 |
296 | ssl_context = TLSv1_3_BYPASS.create_ssl_context()
297 | proxy_conn = ProxyConnector().from_url(url=str(proxy), ssl=ssl_context) if proxy \
298 | else aiohttp.TCPConnector(ssl=ssl_context)
299 | headers = default_headers.copy()
300 | headers.update({'User-Agent': check_user_agent(self.tg_client.name)})
301 | async with aiohttp.ClientSession(headers=headers, connector=proxy_conn) as session:
302 | if proxy:
303 | ip = await check_proxy(session)
304 | if not ip:
305 | self._log.warning(f"Proxy {proxy} not available. Waiting for the moment when it will work...")
306 | ip = await wait_proxy(session)
307 | self._log.info(f"Used proxy {proxy}. Real ip: {ip}")
308 | set_proxy_for_tg_client(self.tg_client, proxy)
309 | else:
310 | self._log.warning("Proxy not installed! This may lead to account ban! Be careful.")
311 | try:
312 | await self.auth(session)
313 | except TelegramProxyError:
314 | return self._log.error(f"The selected proxy cannot be applied to the Telegram client.")
315 | except Exception as e:
316 | self._log.error(f"{traceback.format_exc()}")
317 | return self._log.critical(f"Stop Tapper. Reason: {e}")
318 |
319 | timer = 0
320 | while True:
321 | delta_time = time() - timer
322 | sleep_time = uniform(
323 | settings.SLEEP_MINUTES_BEFORE_ITERATIONS[0],
324 | settings.SLEEP_MINUTES_BEFORE_ITERATIONS[1]
325 | ) * 60
326 | if delta_time <= sleep_time:
327 | sleep_time = sleep_time - delta_time
328 | self._log.info(f"Sleep {format_duration(sleep_time)} before next checks...")
329 | await asyncio.sleep(sleep_time)
330 | try:
331 | await self.check_daily_reward()
332 | await self.update_user_balance()
333 | await self.update_points_balance(with_log=True)
334 | await self.check_farming()
335 | await self.check_friends_balance()
336 | await self._api.elig_dogs()
337 | # todo: add "api/v1/wallet/my/balance?fiat=usd", "api/v1/tribe/leaderboard" and another human behavior
338 | await self.check_tribe()
339 | need_recheck_tasks = await self.check_tasks()
340 | await self.play_drop_game()
341 | if need_recheck_tasks:
342 | await self.check_tasks()
343 | except NeedReLoginError:
344 | await self.auth(session)
345 | except Exception:
346 | self._log.error(f"{traceback.format_exc()}")
347 | self._log.error(f"Unhandled error")
348 | await asyncio.sleep(delay=3)
349 | timer = time()
350 |
351 |
352 | async def run_tapper(tg_client: Client, proxy: Proxy | None):
353 | session_logger = SessionLogger(tg_client.name)
354 | try:
355 | await Tapper(tg_client, session_logger).run(proxy=proxy)
356 | except TelegramInvalidSessionException:
357 | move_session_to_deleted(tg_client)
358 | session_logger.error(f"Telegram account {tg_client.name} is not work!")
359 |
--------------------------------------------------------------------------------
/bot/core/tg_auth.py:
--------------------------------------------------------------------------------
1 | from urllib.parse import unquote
2 |
3 | from pyrogram import Client
4 | from pyrogram.errors import (
5 | Unauthorized, UserDeactivated, AuthKeyUnregistered, UserDeactivatedBan,
6 | AuthKeyDuplicated, SessionExpired, SessionRevoked
7 | )
8 | from pyrogram.raw.functions.messages import RequestAppWebView
9 | from pyrogram.raw.types import InputBotAppShortName
10 |
11 | from bot.core.helper import get_referral_token
12 | from bot.exceptions import TelegramInvalidSessionException, TelegramProxyError
13 | from bot.utils.logger import SessionLogger
14 |
15 |
16 | async def get_tg_web_data(client: Client, log: SessionLogger) -> str:
17 | try:
18 | if not client.is_connected:
19 | await client.connect()
20 | acc = await client.get_me()
21 | log.trace(f"TG Account Login: {acc.username} ({acc.first_name}) {acc.last_name})")
22 |
23 | peer = await client.resolve_peer('BlumCryptoBot')
24 | web_view = await client.invoke(RequestAppWebView(
25 | peer=peer,
26 | app=InputBotAppShortName(bot_id=peer, short_name="app"),
27 | platform='android',
28 | write_allowed=True,
29 | start_param=get_referral_token()
30 | ))
31 | data = unquote(string=web_view.url.split('#tgWebAppData=', maxsplit=1)[1].split('&tgWebAppVersion', maxsplit=1)[0])
32 | return data
33 |
34 | except (Unauthorized, UserDeactivated, AuthKeyUnregistered, UserDeactivatedBan, AuthKeyDuplicated,
35 | SessionExpired, SessionRevoked):
36 | raise TelegramInvalidSessionException(f"Telegram session is invalid. Client: {client.name}")
37 | except AttributeError as e:
38 | raise TelegramProxyError(e)
39 | finally:
40 | if client.is_connected:
41 | await client.disconnect()
--------------------------------------------------------------------------------
/bot/exceptions/__init__.py:
--------------------------------------------------------------------------------
1 | from .api import *
2 | from .telegram import *
--------------------------------------------------------------------------------
/bot/exceptions/api.py:
--------------------------------------------------------------------------------
1 |
2 | class NeedReLoginError(Exception):
3 | pass
4 |
5 | class NeedRefreshTokenError(Exception):
6 | pass
7 |
8 | class InvalidUsernameError(Exception):
9 | pass
10 |
11 | class AuthError(Exception):
12 | pass
13 |
14 | class AlreadyConnectError(Exception):
15 | pass
16 |
17 | class UsernameNotAvailableError(Exception):
18 | pass
--------------------------------------------------------------------------------
/bot/exceptions/telegram.py:
--------------------------------------------------------------------------------
1 |
2 | class TelegramInvalidSessionException(Exception):
3 | pass
4 |
5 | class TelegramProxyError(Exception):
6 | pass
--------------------------------------------------------------------------------
/bot/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from . import launcher
2 |
3 |
4 | import os
5 |
6 | if not os.path.exists(path="sessions"):
7 | os.mkdir(path="sessions")
8 |
--------------------------------------------------------------------------------
/bot/utils/checkers.py:
--------------------------------------------------------------------------------
1 | import traceback
2 | from asyncio import sleep, CancelledError
3 | from aiohttp import ClientSession, ClientTimeout
4 | from aiohttp.client_exceptions import ClientProxyConnectionError
5 | from python_socks import ProxyError
6 |
7 | from bot.utils.logger import logger
8 |
9 | async def check_proxy(http_client: ClientSession) -> str | None:
10 | try:
11 | response = await http_client.get(url='https://api.ipify.org?format=json', timeout=ClientTimeout(5))
12 | data = await response.json()
13 | if data and data.get('ip'):
14 | return data.get('ip')
15 | except (ConnectionRefusedError, ClientProxyConnectionError, CancelledError):
16 | logger.trace(f"Proxy not available")
17 | except ProxyError as e:
18 | logger.error(f"The proxy type may be incorrect! Error: {type(e).__name__}: {e}")
19 | except Exception as e:
20 | logger.error(f"{traceback.format_exc()}")
21 |
22 |
23 | async def wait_proxy(http_client: ClientSession, time_between_checks_sec: int = 5) -> str | None:
24 | while True:
25 | ip = await check_proxy(http_client)
26 | if ip:
27 | return ip
28 | logger.trace(f"Proxy not available. Sleep {time_between_checks_sec} sec...")
29 | await sleep(time_between_checks_sec)
30 |
--------------------------------------------------------------------------------
/bot/utils/launcher.py:
--------------------------------------------------------------------------------
1 | import os
2 | import glob
3 | import asyncio
4 |
5 | from itertools import cycle
6 | from typing import List
7 |
8 | from pyrogram import Client
9 | from better_proxy import Proxy
10 |
11 | from bot.config import settings
12 | from bot.core.tapper import run_tapper
13 | from bot.utils.logger import logger
14 | from bot.utils.payload import check_payload_server
15 |
16 | start_text = """
17 | ██████╗ ██╗ ██╗ ██╗███╗ ███╗████████╗ ██████╗ ██████╗ ██████╗ ████████╗
18 | ██╔══██╗██║ ██║ ██║████╗ ████║╚══██╔══╝██╔════╝ ██╔══██╗██╔═══██╗╚══██╔══╝
19 | ██████╔╝██║ ██║ ██║██╔████╔██║ ██║ ██║ ███╗██████╔╝██║ ██║ ██║
20 | ██╔══██╗██║ ██║ ██║██║╚██╔╝██║ ██║ ██║ ██║██╔══██╗██║ ██║ ██║
21 | ██████╔╝███████╗╚██████╔╝██║ ╚═╝ ██║ ██║ ╚██████╔╝██████╔╝╚██████╔╝ ██║
22 | ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝
23 | """
24 |
25 |
26 | def get_session_names() -> list[str]:
27 | session_names = sorted(glob.glob("sessions/*.session"))
28 | session_names = [
29 | os.path.splitext(os.path.basename(file))[0] for file in session_names
30 | ]
31 |
32 | return session_names
33 |
34 |
35 | def get_proxies() -> list[Proxy]:
36 | proxies = []
37 | if settings.USE_PROXY_FROM_FILE:
38 | with open(file="bot/config/proxies.txt", encoding="utf-8-sig") as file:
39 | for line in file:
40 | if "type://" in line:
41 | continue
42 | try:
43 | proxies.append(Proxy.from_str(proxy=line.strip()))
44 | except ValueError as e:
45 | print(f"{e} - {line}")
46 | return proxies
47 |
48 |
49 | def get_tg_clients() -> list[Client]:
50 |
51 | session_names = get_session_names()
52 |
53 | if not session_names:
54 | raise FileNotFoundError("Not found session files")
55 |
56 | if not settings.API_ID or not settings.API_HASH:
57 | raise ValueError("API_ID and API_HASH not found in the .env file.")
58 |
59 | tg_clients = [
60 | Client(
61 | name=session_name,
62 | api_id=settings.API_ID,
63 | api_hash=settings.API_HASH,
64 | workdir="sessions/",
65 | plugins=dict(root="bot/plugins"),
66 | )
67 | for session_name in session_names
68 | ]
69 |
70 | return tg_clients
71 |
72 | async def run_tasks():
73 | tg_clients = get_tg_clients()
74 | proxies = get_proxies()
75 | proxies_cycle = cycle(proxies) if proxies else None
76 | loop = asyncio.get_event_loop()
77 |
78 | if settings.USE_CUSTOM_PAYLOAD_SERVER and not await check_payload_server(settings.CUSTOM_PAYLOAD_SERVER_URL, full_test=True):
79 | logger.warning(
80 | f"The payload server on {settings.CUSTOM_PAYLOAD_SERVER_URL} is unavailable or not running. "
81 | f"Without it, the bot will not play games for passes. \n"
82 | f"Read info: https://github.com/HiddenCodeDevs/BlumTelegramBot/blob/main/PAYLOAD-SERVER.MD"
83 | )
84 |
85 | tasks = [
86 | loop.create_task(
87 | run_tapper(
88 | tg_client=tg_client,
89 | proxy=next(proxies_cycle) if proxies_cycle else None
90 | )
91 | )
92 | for tg_client in tg_clients
93 | ]
94 |
95 | await asyncio.gather(*tasks)
96 |
--------------------------------------------------------------------------------
/bot/utils/logger.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from bot.config import settings
3 | from typing import Callable
4 |
5 | from loguru import logger
6 |
7 | logger.remove()
8 |
9 | level = "DEBUG" if settings.DEBUG else "INFO"
10 |
11 | logger.add(sink=sys.stdout, level=level, format="{time:YYYY-MM-DD HH:mm:ss}"
12 | " | {level}"
13 | " | {message}")
14 |
15 | logger.add("blum_dev.log", level="DEBUG", format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", rotation="20 MB")
16 |
17 | logger = logger.opt(colors=True)
18 |
19 | MessageMethod = Callable[[str], None]
20 |
21 | def disable_color_on_error(formatter, level_):
22 | def wrapper(*args, **kwargs):
23 | try:
24 | getattr(logger, level_)(formatter(*args, **kwargs))
25 | except ValueError:
26 | getattr(logger.opt(colors=False), level)(*args, **kwargs)
27 | return wrapper
28 |
29 | class SessionLogger:
30 |
31 | session_name: str
32 | trace: MessageMethod
33 | debug: MessageMethod
34 | info: MessageMethod
35 | success: MessageMethod
36 | warning: MessageMethod
37 | error: MessageMethod
38 | critical: MessageMethod
39 |
40 | def __init__(self, session_name):
41 | self.session_name = session_name
42 | for method_name in ("trace", "debug", "info", "success", "warning", "error", "critical"):
43 | setattr(self, method_name, disable_color_on_error(self._format, method_name))
44 |
45 | def _format(self, message):
46 | return f"{self.session_name} | {message}"
47 |
--------------------------------------------------------------------------------
/bot/utils/payload.py:
--------------------------------------------------------------------------------
1 | from aiohttp import ClientSession, ClientConnectorError
2 | from asyncio.exceptions import TimeoutError
3 | from bot.utils.logger import logger
4 |
5 | async def check_payload_server(payload_server_url: str, full_test: bool = False) -> bool:
6 | url = f"{payload_server_url}/status"
7 |
8 | if full_test and "https://" in payload_server_url and ("localhost:" in payload_server_url or "127.0.0.1:" in payload_server_url) :
9 | logger.warning("Are you sure you specified the correct protocol? "
10 | "On local machines, https is usually not used!")
11 |
12 | async with ClientSession() as session:
13 | try:
14 | async with session.get(url, timeout=3) as response:
15 | result = await response.json()
16 | if response.status != 200 or result.get("status") != "ok":
17 | return False
18 | if result.get("version", 0) < 2:
19 | logger.warning("You need to update BlumPayloadGenerator, used old version")
20 | return False
21 | if result.get("version") > 2:
22 | logger.warning("Your BlumTelegramBot script is out of date and needs to be updated.")
23 | return False
24 | if full_test:
25 | test_game_id = "0000test-game-iden-tifi-cation123456"
26 | asset_clicks = {
27 | "BOMB": {"clicks": 0},
28 | "CLOVER": {"clicks": 150},
29 | "FREEZE": {"clicks": 0},
30 | "HARRIS": {"clicks": 0},
31 | "TRUMP": {"clicks": 300}
32 | }
33 | earned_points = {"BP": {"amount": 150 + 300 * 5}}
34 | payload = await get_payload(payload_server_url, test_game_id, earned_points, asset_clicks)
35 | return len(payload) == 684
36 | return True
37 | except (TimeoutError, ClientConnectorError):
38 | logger.debug(f"Try connect to payload server ({url}) failed...")
39 | return False
40 |
41 | async def get_payload(payload_server_url: str, game_id: str, earned_points: dict, asset_clicks: dict) -> str | None:
42 | async with ClientSession() as session:
43 | payload_data = {"gameId": game_id, "earnedPoints": earned_points, "assetClicks": asset_clicks}
44 | async with session.post(url=f"{payload_server_url}/getPayload", json=payload_data) as response:
45 | data = await response.json()
46 | if response.status == 200 and data.get("payload"):
47 | return data.get("payload")
48 | logger.error(f"Payload Server Error: {data.get('error')}")
49 | raise KeyboardInterrupt
50 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | bot:
4 | container_name: 'ИМЯ РЕПЫ'
5 | build:
6 | context: .
7 | stop_signal: SIGINT
8 | restart: unless-stopped
9 | command: "python3 main.py -a 1"
10 | volumes:
11 | - .:/app
12 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from contextlib import suppress
3 | from argparse import ArgumentParser
4 |
5 | from bot.core.registrator import register_sessions
6 | from bot.utils.launcher import get_session_names, get_proxies, start_text, run_tasks
7 | from bot.utils.logger import logger
8 |
9 |
10 | async def main() -> None:
11 | parser = ArgumentParser()
12 | parser.add_argument("-a", "--action", type=int, help="Action to perform")
13 | print(start_text)
14 | logger.info(f"Detected {len(get_session_names())} sessions | {len(get_proxies())} proxies")
15 |
16 | action = parser.parse_args().action
17 |
18 | actions = {
19 | 1: run_tasks,
20 | 2: register_sessions
21 | }
22 |
23 | if not action:
24 | print(f"Select an action:\n 1. Run clicker\n 2. Create session")
25 | while True:
26 | action = input("> ")
27 | if not action.isdigit():
28 | logger.warning("Action must be number")
29 | elif int(action) not in actions:
30 | logger.warning("Action must be 1 or 2")
31 | else:
32 | action = int(action)
33 | break
34 |
35 | await actions[action]()
36 |
37 | if __name__ == '__main__':
38 | try:
39 | with suppress(KeyboardInterrupt):
40 | asyncio.run(main())
41 | except KeyboardInterrupt:
42 | pass
43 | except BaseException as e:
44 | raise BaseException
45 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiocfscrape==1.0.0
2 | aiohappyeyeballs==2.4.3
3 | aiohttp==3.10.10
4 | aiohttp-proxy==0.1.2
5 | aiohttp_socks==0.9.0
6 | aiosignal==1.3.1
7 | annotated-types==0.7.0
8 | async-timeout==4.0.3
9 | attrs==24.2.0
10 | better-proxy==1.2.0
11 | colorama==0.4.6
12 | frozenlist==1.5.0
13 | idna==3.10
14 | Js2Py==0.74
15 | loguru==0.7.2
16 | multidict==6.1.0
17 | propcache==0.2.0
18 | pyaes==1.6.1
19 | pydantic==2.9.2
20 | pydantic-settings==2.6.0
21 | pydantic_core==2.23.4
22 | pyjsparser==2.7.1
23 | Pyrogram==2.0.106
24 | PySocks==1.7.1
25 | python-dotenv==1.0.1
26 | python-socks==2.5.3
27 | six==1.16.0
28 | TgCrypto==1.2.5
29 | typing_extensions==4.12.2
30 | tzdata==2024.2
31 | tzlocal==5.2
32 | win32-setctime==1.1.0
33 | yarl==1.17.1
34 |
--------------------------------------------------------------------------------
/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | if not exist venv (
4 | echo Creating virtual environment...
5 | python -m venv venv
6 | )
7 |
8 |
9 | echo Activating virtual environment...
10 | call venv\Scripts\activate
11 |
12 | if not exist venv\Lib\site-packages\installed (
13 | if exist requirements.txt (
14 | echo installing wheel for faster installing
15 | pip install wheel
16 | echo Installing dependencies...
17 | pip install -r requirements.txt
18 | echo. > venv\Lib\site-packages\installed
19 | ) else (
20 | echo requirements.txt not found, skipping dependency installation.
21 | )
22 | ) else (
23 | echo Dependencies already installed, skipping installation.
24 | )
25 |
26 | if not exist .env (
27 | echo Copying configuration file
28 | copy .env-example .env
29 | ) else (
30 | echo Skipping .env copying
31 | )
32 |
33 | ::Обновление локального репозитория без удаления изменений
34 | git stash
35 | git pull
36 | git stash pop
37 |
38 | pip install -r requirements.txt
39 |
40 | echo Starting the bot...
41 | python main.py
42 |
43 | echo done
44 | pause
45 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash-low-unrelated-histories
2 | # Проверка на наличие папки venv
3 | if [ ! -d "venv" ]; then
4 | echo "Creating virtual environment..."
5 | python3 -m venv venv
6 | fi
7 |
8 | echo "Activating virtual environment..."
9 | source venv/bin/activate
10 |
11 | # Проверка на наличие установленного флага в виртуальном окружении
12 | if [ ! -f "venv/installed" ]; then
13 | if [ -f "requirements.txt" ]; then
14 | echo "Installing wheel for faster installing"
15 | pip3 install wheel
16 | echo "Installing dependencies..."
17 | pip3 install -r requirements.txt
18 | touch venv/installed
19 | else
20 | echo "requirements.txt not found, skipping dependency installation."
21 | fi
22 | else
23 | echo "Dependencies already installed, skipping installation."
24 | fi
25 |
26 | if [ ! -f ".env" ]; then
27 | echo "Copying configuration file"
28 | cp .env-example .env
29 | else
30 | echo "Skipping .env copying"
31 | fi
32 | #Обновление локального репозитория без удаления изменений
33 | git stash
34 | git pull
35 | git stash pop
36 |
37 | pip install -r requirements.txt
38 | echo "Starting the bot..."
39 | python3 main.py
40 |
41 | echo "done"
42 |
43 |
--------------------------------------------------------------------------------