old_01
This commit is contained in:
23
doners/shop-telegram-bot/.dockerignore
Normal file
23
doners/shop-telegram-bot/.dockerignore
Normal file
@@ -0,0 +1,23 @@
|
||||
/images
|
||||
data.db
|
||||
*.pyc
|
||||
*.pyd
|
||||
sticker.tgs
|
||||
config.ini
|
||||
src/test.py
|
||||
aws.sh
|
||||
KP.pem
|
||||
ideas.txt
|
||||
/tmp
|
||||
DOCS/overview.md
|
||||
src/modules/
|
||||
backups/
|
||||
logs/
|
||||
DOCS/
|
||||
.github/
|
||||
.git/
|
||||
tests/
|
||||
LICENCE
|
||||
readme.md
|
||||
start.cmd
|
||||
start.sh
|
||||
2
doners/shop-telegram-bot/.github/FUNDING.yml
vendored
Normal file
2
doners/shop-telegram-bot/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Monero: 43fxouHQFZ5guiyYSUWh6eL1ZQ7pDtVV8D1kKUzp4aYwQBgLjyY8q7KjeEbDvvTYCDPFEdDxz9duBdRrZPnjiSMwVGV4jZj
|
||||
Bitcoin: bc1qnzft2cfty0hqptxjx9ajk4m2q9x3a30gvpylh2
|
||||
27
doners/shop-telegram-bot/.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
27
doners/shop-telegram-bot/.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Помогите нам стать лучше
|
||||
title: "[BUG]"
|
||||
labels: bug
|
||||
assignees: w1png
|
||||
|
||||
---
|
||||
|
||||
**Опишите ошибку**
|
||||
Краткое и понятное описание проблемы.
|
||||
|
||||
**Как воспроизвести ошибку**
|
||||
1. Откройте '...'
|
||||
2. Нажмите на '....'
|
||||
3. Пролистайте до '....'
|
||||
4. Ошибка
|
||||
|
||||
**Ожидаемое поведение**
|
||||
Краткое и понятное описание ожидаемого поведения.
|
||||
|
||||
**Скриншоты**
|
||||
Прикрепите скриншот с проблемой.
|
||||
|
||||
**Информация о системе:**
|
||||
- Операционная система
|
||||
- Версия Python
|
||||
15
doners/shop-telegram-bot/.gitignore
vendored
Normal file
15
doners/shop-telegram-bot/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/images
|
||||
data.db
|
||||
*.pyc
|
||||
*.pyd
|
||||
sticker.tgs
|
||||
config.ini
|
||||
src/test.py
|
||||
aws.sh
|
||||
KP.pem
|
||||
ideas.txt
|
||||
/tmp
|
||||
DOCS/overview.md
|
||||
src/modules/
|
||||
backups/
|
||||
logs/
|
||||
BIN
doners/shop-telegram-bot/DOCS/bot_overview.gif
Normal file
BIN
doners/shop-telegram-bot/DOCS/bot_overview.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 MiB |
7
doners/shop-telegram-bot/Dockerfile
Normal file
7
doners/shop-telegram-bot/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM python:3.10-slim-buster
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
|
||||
RUN python3 -m pip install -r requirements.txt
|
||||
CMD python3 installer.py --nointeract && python3 src/main.py
|
||||
674
doners/shop-telegram-bot/LICENSE
Normal file
674
doners/shop-telegram-bot/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
0
doners/shop-telegram-bot/__init__.py
Normal file
0
doners/shop-telegram-bot/__init__.py
Normal file
174
doners/shop-telegram-bot/installer.py
Normal file
174
doners/shop-telegram-bot/installer.py
Normal file
@@ -0,0 +1,174 @@
|
||||
from os import system, name, remove, mkdir, rmdir, listdir, environ
|
||||
from os.path import exists
|
||||
from sys import argv
|
||||
|
||||
import sqlite3
|
||||
|
||||
|
||||
def clearConsole():
|
||||
system("cls" if name in ("nt", "dos") else "clear")
|
||||
|
||||
def create_config(token, main_admin_id, config_path="config.ini"):
|
||||
DEFAULT_CONFIG_TEXT = f"""[main_settings]
|
||||
token = {token}
|
||||
mainadminid = {main_admin_id}
|
||||
debug = 0
|
||||
|
||||
[shop_settings]
|
||||
name = Название магазина
|
||||
greeting = Добро пожаловать!
|
||||
refundpolicy = Текст для вкладки "Политика возврата"
|
||||
contacts = Текст для вкладки "Контакты"
|
||||
enableimage = 1
|
||||
enablesticker = 0
|
||||
enablephonenumber = 0
|
||||
enabledelivery = 0
|
||||
delivery_price = 0.0
|
||||
enablecaptcha = 1
|
||||
|
||||
[stats_settings]
|
||||
barcolor = 3299ff
|
||||
borderwidth = 1
|
||||
titlefontsize = 20
|
||||
axisfontsize = 12
|
||||
tickfontsize = 8
|
||||
"""
|
||||
with open(config_path, "w") as config:
|
||||
config.write(DEFAULT_CONFIG_TEXT)
|
||||
|
||||
|
||||
CREATE_CATS_TEXT = """
|
||||
CREATE TABLE "cats" (
|
||||
"id" INTEGER,
|
||||
"name" TEXT NOT NULL,
|
||||
PRIMARY KEY("id")
|
||||
)
|
||||
"""
|
||||
CREATE_ITEMS_TEXT = """
|
||||
CREATE TABLE "items" (
|
||||
"id" INTEGER,
|
||||
"name" TEXT NOT NULL,
|
||||
"price" FLOAT NOT NULL,
|
||||
"cat_id" INTEGER NOT NULL,
|
||||
"desc" TEXT,
|
||||
"active" INTEGER,
|
||||
"amount" INTEGER,
|
||||
"image_id" INTEGER,
|
||||
"hide_image" INTEGER,
|
||||
PRIMARY KEY("id")
|
||||
)
|
||||
"""
|
||||
CREATE_ORDERS_TEXT = """
|
||||
CREATE TABLE "orders" (
|
||||
"order_id" INTEGER,
|
||||
"user_id" INTEGER,
|
||||
"item_list" TEXT,
|
||||
"email_adress" TEXT,
|
||||
"phone_number" TEXT,
|
||||
"home_adress" TEXT,
|
||||
"additional_message" TEXT,
|
||||
"date" TEXT,
|
||||
"status" INTEGER
|
||||
)
|
||||
"""
|
||||
CREATE_USERS_TEXT = """
|
||||
CREATE TABLE "users" (
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"is_admin" INTEGER,
|
||||
"is_manager" INTEGER,
|
||||
"notification" INTEGER,
|
||||
"date_created" TEXT,
|
||||
"cart" TEXT,
|
||||
"cart_delivery" INTEGER
|
||||
)
|
||||
"""
|
||||
CREATE_COMMANDS_TEXT = """
|
||||
CREATE TABLE "commands" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"command" TEXT,
|
||||
"response" TEXT,
|
||||
PRIMARY KEY("id")
|
||||
)
|
||||
"""
|
||||
|
||||
def create_db():
|
||||
conn = sqlite3.connect("data.db")
|
||||
c = conn.cursor()
|
||||
c.execute(CREATE_CATS_TEXT)
|
||||
c.execute(CREATE_ITEMS_TEXT)
|
||||
c.execute(CREATE_ORDERS_TEXT)
|
||||
c.execute(CREATE_USERS_TEXT)
|
||||
c.execute(CREATE_COMMANDS_TEXT)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def do_files_exist():
|
||||
return any(list(map(exists, ["config.ini", "images", "data.db"])))
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "--nointeract" in argv:
|
||||
if do_files_exist():
|
||||
exit(0)
|
||||
|
||||
token = environ.get("TELEGRAM_TOKEN")
|
||||
main_admin_id = environ.get("MAIN_ADMIN_ID")
|
||||
if token is None or main_admin_id is None:
|
||||
print("Не указаны переменные окружения TELEGRAM_TOKEN или MAIN_ADMIN_ID")
|
||||
exit(1)
|
||||
create_config(token, main_admin_id)
|
||||
|
||||
create_db()
|
||||
[mkdir(name) for name in ["backups", "images"]]
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
clearConsole()
|
||||
if do_files_exist():
|
||||
while True:
|
||||
confirmation = input("Вы уверены, что хотите повторно запустить процесс установки? Все данные будут утеряны! (y/N) ")
|
||||
if confirmation.lower() in ["y", "yes", "n", "no", ""]:
|
||||
break
|
||||
else:
|
||||
confirmation = "y"
|
||||
|
||||
|
||||
if confirmation.lower() in ["y", "yes"]:
|
||||
print("Вы можете узнать как получить токен бота, перейдя по ссылке: https://youtu.be/fyISLEvzIec")
|
||||
token = input("Введите токен бота: ")
|
||||
print("Вы можете получить ваш ID, написав \"/start\" боту @userinfobot")
|
||||
main_admin_id = input("Введите ID главного администратора: ")
|
||||
if main_admin_id.isalnum():
|
||||
if exists("data.db"):
|
||||
remove("data.db")
|
||||
print("База данных была удалена.")
|
||||
create_db()
|
||||
print("База данных была создана.")
|
||||
if exists("config.ini"):
|
||||
remove("config.ini")
|
||||
print("Файл настроек был удален.")
|
||||
create_config(token, main_admin_id)
|
||||
print("Файл настроек был создан.")
|
||||
if exists("images"):
|
||||
for file in listdir("images"):
|
||||
remove("images/" + file)
|
||||
rmdir("images")
|
||||
print("Папка \"images\" была удалена.")
|
||||
mkdir("images")
|
||||
print("Папка \"images\" была создана.")
|
||||
if exists("backups"):
|
||||
for folder in listdir("backups"):
|
||||
for file in listdir("backups/" + folder):
|
||||
remove(f"backups/{folder}/{file}")
|
||||
rmdir(f"backups/{folder}")
|
||||
rmdir("backups")
|
||||
print("Папка \"backups\" была удалена.")
|
||||
mkdir("backups")
|
||||
print("Папка \"backups\" была создана.")
|
||||
else:
|
||||
print("Неверный ID главного администратора.")
|
||||
else:
|
||||
print("Установка была отменена.")
|
||||
|
||||
|
||||
input("Нажмите ENTER, чтобы продолжить...")
|
||||
83
doners/shop-telegram-bot/readme.md
Normal file
83
doners/shop-telegram-bot/readme.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# ⚠️ Эта версия бота больше не обновляется!
|
||||
Сейчас я переписываю бота, за обновлениями следите на ветке rewrite :)
|
||||
<br>
|
||||
|
||||
## 🪲 В случае возникновения ошибок открывайте тикет во вкладке Issues :)
|
||||
|
||||
# Навигация
|
||||
|
||||
- [Навигация](#навигация)
|
||||
- [Зачем нужен этот бот?](#зачем-нужен-этот-бот)
|
||||
- [Установка](#установка)
|
||||
- [Docker](#docker)
|
||||
- [Python](#python)
|
||||
- [Установка необходимых Python-пакетов](#установка-необходимых-python-пакетов)
|
||||
- [Запуск установщика](#запуск-установщика)
|
||||
- [Запуск бота](#запуск-бота)
|
||||
- [Запуск с помощью скрипта](#запуск-с-помощью-скрипта)
|
||||
- [Linux](#linux)
|
||||
- [Macos](#macos)
|
||||
- [Запуск вручную](#запуск-вручную)
|
||||
- [Режим отладки](#режим-отладки)
|
||||
|
||||
# Зачем нужен этот бот?
|
||||
|
||||
Зачастую люди, желающие открыть маленький интернет-бизнес, делают это с помощью профиля в социальных сетях, что требует вручную обрабатывать каждую заявку. Этот бот позволит каждому быстро открыть автоматизированный магазин на базе телеграм бота, что значительно уменьшит время обработки заказов.
|
||||
|
||||

|
||||
|
||||
# Установка
|
||||
|
||||
## Docker
|
||||
Этот способ установки является основным и рекомендуемым. Он позволяет установить бота в репродуцируемом окружении, что упрощает его обновление и установку на сервер.
|
||||
|
||||
Для работы бота необходимо установить [Docker](https://docs.docker.com/get-docker/) и выполнить следущую команду:
|
||||
|
||||
```
|
||||
docker run -d \
|
||||
-e MAIN_ADMIN_ID='Ваш Telegram ID' \
|
||||
-e TELEGRAM_TOKEN='token' \
|
||||
w1png/shop-telegram-bot
|
||||
```
|
||||
|
||||
## Python
|
||||
|
||||
Для работы бота необходимо установить [Python версии 3.10 и выше](https://www.python.org/downloads/).
|
||||
|
||||
### Установка необходимых Python-пакетов
|
||||
|
||||
python3 -m pip install -r requirements.txt
|
||||
|
||||
### Запуск установщика
|
||||
|
||||
Перед запуском установщика требуется [создать токен](https://youtu.be/fyISLEvzIec) для телеграм бота и [получить ваш ID](https://badcode.ru/kak-v-telegram-uznat-svoi-id/).
|
||||
|
||||
Установщик запускается с помощью команды:
|
||||
|
||||
python3 installer.py
|
||||
|
||||
## Запуск бота
|
||||
|
||||
### Запуск с помощью скрипта
|
||||
|
||||
#### Linux/MacOS
|
||||
|
||||
$ chmod +x start.sh
|
||||
$ ./start.sh
|
||||
|
||||
#### Windows
|
||||
|
||||
$ start.cmd
|
||||
|
||||
### Запуск вручную
|
||||
|
||||
python3 main.py
|
||||
|
||||
# Режим отладки
|
||||
|
||||
Режим отладки можно активировать во вкладке "Основные настройки".
|
||||
После активации в терминале начнут отображаться все сообщения и вызовы в формате:
|
||||
|
||||
DEBUG: <MESSAGE/CALL> [<user_id>] <Сообщение/вызов>
|
||||
|
||||
*Пример: `DEBUG CALL [462741] admin_itemManagement`*
|
||||
6
doners/shop-telegram-bot/requirements.txt
Normal file
6
doners/shop-telegram-bot/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
aiogram==2.19
|
||||
captcha==0.3
|
||||
matplotlib==3.5.1
|
||||
phonenumbers==8.12.40
|
||||
pymorphy2==0.9.1
|
||||
pyparsing==3.0.0
|
||||
0
doners/shop-telegram-bot/src/__init__.py
Normal file
0
doners/shop-telegram-bot/src/__init__.py
Normal file
51
doners/shop-telegram-bot/src/category.py
Normal file
51
doners/shop-telegram-bot/src/category.py
Normal file
@@ -0,0 +1,51 @@
|
||||
import sqlite3
|
||||
import item as itm
|
||||
|
||||
conn = sqlite3.connect("data.db")
|
||||
c = conn.cursor()
|
||||
|
||||
class Category:
|
||||
def __init__(self, cat_id):
|
||||
self.id = cat_id
|
||||
|
||||
def __eq__(self, __o: object) -> bool:
|
||||
return self.get_id() == __o.get_id()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.get_name()
|
||||
|
||||
def get_id(self):
|
||||
return self.id
|
||||
|
||||
def __clist(self):
|
||||
c.execute(f"SELECT * FROM cats WHERE id=?", [self.get_id()])
|
||||
return list(c)[0]
|
||||
|
||||
def get_name(self):
|
||||
return self.__clist()[1]
|
||||
|
||||
def set_name(self, value):
|
||||
c.execute(f"UPDATE cats SET name=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def delete(self):
|
||||
c.execute(f"DELETE FROM cats WHERE id=?", [self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_item_list(self):
|
||||
c.execute(f"SELECT * FROM items WHERE cat_id=?", [self.get_id()])
|
||||
return list(map(itm.Item, [item[0] for item in list(c)]))
|
||||
|
||||
|
||||
def get_cat_list():
|
||||
c.execute(f"SELECT * FROM cats")
|
||||
return list(map(Category, [cat[0] for cat in list(c)]))
|
||||
|
||||
|
||||
def create_cat(cat_name):
|
||||
c.execute(f"INSERT INTO cats(name) VALUES(?)", [cat_name])
|
||||
conn.commit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(get_cat_list())
|
||||
61
doners/shop-telegram-bot/src/commands.py
Normal file
61
doners/shop-telegram-bot/src/commands.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import sqlite3
|
||||
|
||||
conn = sqlite3.connect("data.db")
|
||||
c = conn.cursor()
|
||||
|
||||
|
||||
def get_commands():
|
||||
return list(map(Command, [command[0] for command in list(c.execute("SELECT * FROM commands"))]))
|
||||
|
||||
def does_command_exist(command_id=None, command=None):
|
||||
result = False
|
||||
if command_id:
|
||||
c.execute("SELECT * FROM commands WHERE id=?", [command_id])
|
||||
result = len(list(c)) >= 1
|
||||
elif command:
|
||||
c.execute("SELECT * FROM commands WHERE command=?", [command])
|
||||
result = len(list(c)) >= 1
|
||||
return result
|
||||
|
||||
def get_command_by_command(command):
|
||||
c.execute("SELECT * FROM commands WHERE command=?", [command])
|
||||
return Command(list(c)[0][0])
|
||||
|
||||
|
||||
def create_command(command, response):
|
||||
c.execute("INSERT INTO commands(command, response) VALUES(?,?)", [command, response])
|
||||
conn.commit()
|
||||
|
||||
|
||||
# TODO: rework the permission system and implement command permissions
|
||||
class Command:
|
||||
def __init__(self, command_id=None):
|
||||
self.command_id = command_id
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"[{self.get_id()}] {self.get_command()}"
|
||||
|
||||
def __clist(self):
|
||||
c.execute("SELECT * FROM commands WHERE id=?", [self.command_id])
|
||||
return list(c)[0]
|
||||
|
||||
def get_id(self):
|
||||
return self.command_id
|
||||
|
||||
def get_command(self):
|
||||
return self.__clist()[1]
|
||||
|
||||
def get_response(self):
|
||||
return self.__clist()[2]
|
||||
|
||||
def delete(self):
|
||||
c.execute("DELETE FROM commands WHERE id=?", [self.command_id])
|
||||
conn.commit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# create_command("test", "response")
|
||||
|
||||
for command in get_commands():
|
||||
print(command)
|
||||
111
doners/shop-telegram-bot/src/item.py
Normal file
111
doners/shop-telegram-bot/src/item.py
Normal file
@@ -0,0 +1,111 @@
|
||||
import sqlite3
|
||||
from settings import Settings
|
||||
from os.path import exists
|
||||
from doners.old_2.main import notify_admins
|
||||
|
||||
conn = sqlite3.connect("data.db")
|
||||
c = conn.cursor()
|
||||
|
||||
settings = Settings()
|
||||
|
||||
def does_item_exist(item_id):
|
||||
c.execute(f"SELECT * FROM items WHERE id=?", [item_id])
|
||||
return len(list(c)) == 1
|
||||
|
||||
|
||||
class Item:
|
||||
def __init__(self, item_id):
|
||||
self.__item_id = item_id
|
||||
|
||||
def __eq__(self, __o: object):
|
||||
return self.get_id() == __o.get_id()
|
||||
|
||||
def __repr__(self):
|
||||
return f"[{self.get_id()}] {self.get_name()}"
|
||||
|
||||
def get_id(self):
|
||||
return self.__item_id
|
||||
|
||||
def __clist(self):
|
||||
c.execute(f"SELECT * FROM items WHERE id=?", [self.get_id()])
|
||||
return list(c)[0]
|
||||
|
||||
def get_name(self):
|
||||
return self.__clist()[1]
|
||||
|
||||
def set_name(self, value):
|
||||
c.execute(f"UPDATE items SET name=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_price(self):
|
||||
return self.__clist()[2]
|
||||
|
||||
def set_price(self, value):
|
||||
c.execute(f"UPDATE items SET price=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
# TODO: change to get_cat and change to cat.get_id() everywhere
|
||||
def get_cat_id(self):
|
||||
return self.__clist()[3]
|
||||
|
||||
def set_cat_id(self, value):
|
||||
c.execute(f"UPDATE items SET cat_id=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_desc(self):
|
||||
return self.__clist()[4]
|
||||
|
||||
def set_desc(self, value):
|
||||
c.execute(f"UPDATE items SET desc=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def is_active(self):
|
||||
return self.__clist()[5] == 1
|
||||
|
||||
def set_active(self, value):
|
||||
c.execute(f"UPDATE items SET active=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_amount(self):
|
||||
return int(self.__clist()[6])
|
||||
|
||||
def set_amount(self, value):
|
||||
c.execute(f"UPDATE items SET amount=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_image_id(self):
|
||||
return self.__clist()[7]
|
||||
|
||||
def get_image(self):
|
||||
return open("images/" + self.get_image_id(), "rb")
|
||||
|
||||
def set_image_id(self, value):
|
||||
c.execute(f"UPDATE items SET image_id=? WHERE id=?", [value, self.get_id()])
|
||||
|
||||
async def is_hide_image(self):
|
||||
if exists(f"images/{self.get_image_id()}"):
|
||||
return self.__clist()[8] == 1
|
||||
if self.get_image_id() != "None" and settings.is_debug():
|
||||
await notify_admins(f"Файла \"images/{self.get_image_id()}\" для товара \"{self.get_name()}\" не существует!")
|
||||
return True
|
||||
|
||||
def set_hide_image(self, value):
|
||||
c.execute("UPDATE items SET hide_image=? WHERE id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def delete(self):
|
||||
c.execute(f"DELETE FROM items WHERE id=?", [self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
|
||||
def get_item_list():
|
||||
c.execute("SELECT * FROM items")
|
||||
return list(map(Item, [item[0] for item in list(c)]))
|
||||
|
||||
|
||||
def create_item(name, price, cat_id, desc, image_id="None", active=True):
|
||||
c.execute(f"INSERT INTO items(name, price, cat_id, desc, active, amount, image_id, hide_image) VALUES(?, ?, ?, ?, ?, 0, ?, 0)", [name, price, cat_id, desc, 1 if active else 0, image_id])
|
||||
conn.commit()
|
||||
return get_item_list()[-1]
|
||||
|
||||
|
||||
2522
doners/shop-telegram-bot/src/main.py
Normal file
2522
doners/shop-telegram-bot/src/main.py
Normal file
File diff suppressed because it is too large
Load Diff
477
doners/shop-telegram-bot/src/markups.py
Normal file
477
doners/shop-telegram-bot/src/markups.py
Normal file
@@ -0,0 +1,477 @@
|
||||
from os import listdir
|
||||
from aiogram import types
|
||||
from datetime import datetime
|
||||
|
||||
import text_templates as tt
|
||||
from settings import Settings
|
||||
import commands
|
||||
|
||||
settings = Settings()
|
||||
|
||||
# Back buttons
|
||||
# Misc
|
||||
btnBackAdmin = types.InlineKeyboardButton(text=tt.back, callback_data="admin_adminPanel")
|
||||
|
||||
# Item management
|
||||
btnBackItemManagement = types.InlineKeyboardButton(text=tt.back, callback_data="admin_itemManagement")
|
||||
btnBackEditCatChooseCategory = types.InlineKeyboardButton(text=tt.back, callback_data="admin_editCatChooseCategory")
|
||||
def btnBackEditCat(cat_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_editCat{cat_id}")
|
||||
btnBackEditItemChooseCategory = types.InlineKeyboardButton(text=tt.back, callback_data="admin_editItemChooseCategory")
|
||||
def btnBackEditItemChooseItem(cat_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_editItemChooseItem{cat_id}")
|
||||
def btnBackEditItem(item_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_editItem{item_id}")
|
||||
|
||||
# User management
|
||||
btnBackUserManagement = types.InlineKeyboardButton(text=tt.back, callback_data="admin_userManagement")
|
||||
def btnBackSeeUserProfile(user_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_seeUserProfile{user_id}")
|
||||
def btnBackSeeUserOrders(user_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"admin_seeUserOrders{user_id}")
|
||||
|
||||
# Stats
|
||||
btnBackShopStats = types.InlineKeyboardButton(text=tt.back, callback_data="admin_shopStats")
|
||||
btnBackRegistratonStats = types.InlineKeyboardButton(text=tt.back, callback_data="admin_registrationStatsBack")
|
||||
btnBackOrderStats = types.InlineKeyboardButton(text=tt.back, callback_data="admin_orderStatsBack")
|
||||
|
||||
# Settings
|
||||
btnBackShopSettingsDel = types.InlineKeyboardButton(text=tt.back, callback_data="admin_shopSettingsDel")
|
||||
btnBackShopSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_shopSettings")
|
||||
btnBackStatsSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_statsSettings")
|
||||
btnBackMainSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_mainSettings")
|
||||
btnBackCheckoutSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_checkoutSettings")
|
||||
btnBackAdditionalSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_additionalSettings")
|
||||
btnBackCustomCommands = types.InlineKeyboardButton(text=tt.back, callback_data="admin_customCommands")
|
||||
btnBackSystemSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_systemSettings")
|
||||
btnBackItemSettings = types.InlineKeyboardButton(text=tt.back, callback_data="admin_itemSettings")
|
||||
btnBackBackups = types.InlineKeyboardButton(text=tt.back, callback_data="admin_backups")
|
||||
|
||||
# /start menu
|
||||
btnBackFaq = types.InlineKeyboardButton(text=tt.back, callback_data="faq")
|
||||
btnBackProfile = types.InlineKeyboardButton(text=tt.back, callback_data="profile")
|
||||
btnBackMyOrder = types.InlineKeyboardButton(text=tt.back, callback_data="myOrders")
|
||||
btnBackCatalogue = types.InlineKeyboardButton(text=tt.back, callback_data="catalogue")
|
||||
def btnBackViewCat(cat_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"viewCat{cat_id}")
|
||||
def btnBackViewItem(item_id): return types.InlineKeyboardButton(text=tt.back, callback_data=f"viewItem{item_id}")
|
||||
btnBackCart = types.InlineKeyboardButton(text=tt.back, callback_data="cart")
|
||||
btnBackCartDel = types.InlineKeyboardButton(text=tt.back, callback_data="cartDel")
|
||||
btnBackOrders = types.InlineKeyboardButton(text=tt.back, callback_data="manager_orders")
|
||||
|
||||
# Single buttons
|
||||
btnAdminPanel = types.KeyboardButton(tt.admin_panel)
|
||||
btnOrders = types.KeyboardButton(tt.orders)
|
||||
|
||||
def single_button(btn):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(btn)
|
||||
return markup
|
||||
|
||||
|
||||
# Markups
|
||||
# /start buttons
|
||||
def get_markup_main():
|
||||
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
|
||||
markup.add(types.KeyboardButton(tt.catalogue))
|
||||
markup.add(types.KeyboardButton(tt.cart))
|
||||
markup.add(types.KeyboardButton(tt.profile), types.KeyboardButton(tt.faq))
|
||||
return markup
|
||||
|
||||
def get_markup_admin():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.item_management, callback_data="admin_itemManagement"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.user_management, callback_data="admin_userManagement"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.shop_stats, callback_data="admin_shopStats"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.bot_settings, callback_data="admin_shopSettings"))
|
||||
return markup
|
||||
|
||||
def get_markup_faq():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.contacts, callback_data="contacts"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.refund, callback_data="refund"))
|
||||
return markup
|
||||
|
||||
def get_markup_profile(user):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.my_orders, callback_data="myOrders"))
|
||||
# markup.add(types.InlineKeyboardButton(text=tt.my_support_tickets, callback_data="mySupportTickets"))
|
||||
if user.is_admin():
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_notif if user.notif_on() else tt.enable_notif, callback_data="changeEnableNotif"))
|
||||
return markup
|
||||
|
||||
def get_markup_myOrders(order_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for order in order_list:
|
||||
markup.add(types.InlineKeyboardButton(text=order.get_order_id(), callback_data=f"viewMyOrder{order.get_order_id()}"))
|
||||
markup.add(btnBackProfile)
|
||||
return markup
|
||||
|
||||
def get_markup_viewMyOrder(order):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
match order.get_status():
|
||||
case 0:
|
||||
markup.add(types.InlineKeyboardButton(text=tt.cancel_order, callback_data=f"cancelOrder{order.get_order_id()}"))
|
||||
case -1:
|
||||
markup.add(types.InlineKeyboardButton(text=tt.restore_order, callback_data=f"restoreOrder{order.get_order_id()}"))
|
||||
markup.add(btnBackMyOrder)
|
||||
return markup
|
||||
def get_markup_cart(user):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
delivery_price = '{:.2f}'.format(float(settings.get_delivery_price()))
|
||||
for item_and_amount in user.get_cart_amount():
|
||||
markup.add(types.InlineKeyboardButton(text=f"{item_and_amount[0].get_name()[:30-len(f' - {item_and_amount[1]}шт.')-3]}... - {item_and_amount[1]}шт.", callback_data=f"viewItem{item_and_amount[0].get_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=f"{item_and_amount[0].get_price() * item_and_amount[1]}руб.", callback_data="None"), types.InlineKeyboardButton(text=tt.plus, callback_data=f"addToCartFromCart{item_and_amount[0].get_id()}"), types.InlineKeyboardButton(text=tt.minus, callback_data=f"removeFromCartFromCart{item_and_amount[0].get_id()}"))
|
||||
if settings.is_delivery_enabled():
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delivery_on(delivery_price) if user.is_cart_delivery() else tt.delivery_off(delivery_price), callback_data="changeCartDelivery"))
|
||||
else:
|
||||
markup.add(types.InlineKeyboardButton(text=tt.pickup, callback_data="None"))
|
||||
|
||||
markup.add(types.InlineKeyboardButton(text=tt.clear_cart, callback_data="clearCart"))
|
||||
markup.add(types.InlineKeyboardButton(text=f"Всего: {'{:.2f}'.format(user.get_cart_price())}руб.", callback_data="None"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.cart_checkout, callback_data="checkoutCart"))
|
||||
return markup
|
||||
|
||||
def get_markup_captcha():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text="Новая CAPTCHA", callback_data="refreshCaptcha"))
|
||||
markup.add(btnBackCartDel)
|
||||
return markup
|
||||
|
||||
def get_markup_checkoutCartConfirmation():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.confirm, callback_data=f"checkoutCartConfirm"), types.InlineKeyboardButton(text=tt.deny, callback_data="cart"))
|
||||
return markup
|
||||
|
||||
# Catalogue
|
||||
def get_markup_catalogue(cat_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for cat in cat_list:
|
||||
markup.add(types.InlineKeyboardButton(text=cat.get_name(), callback_data=f"viewCat{cat.get_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.search, callback_data="search"))
|
||||
return markup
|
||||
|
||||
def get_markup_search(query):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for item in query:
|
||||
markup.add(types.InlineKeyboardButton(text=item.get_name(), callback_data=f"viewItem{item.get_id()}"))
|
||||
markup.add(btnBackCatalogue)
|
||||
return markup
|
||||
|
||||
|
||||
def get_markup_viewCat(item_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for item in item_list:
|
||||
if item.is_active():
|
||||
markup.add(types.InlineKeyboardButton(text=f"{item.get_name()} - {item.get_price()} руб.", callback_data=f"viewItem{item.get_id()}"))
|
||||
markup.add(btnBackCatalogue)
|
||||
return markup
|
||||
|
||||
def get_markup_viewItem(item):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.add_to_cart, callback_data=f"addToCart{item.get_id()}"))
|
||||
markup.add(btnBackViewCat(item.get_cat_id()))
|
||||
return markup
|
||||
|
||||
# Admin panel tabs
|
||||
# Item management
|
||||
def get_markup_itemManagement():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.add_cat, callback_data="admin_addCat"), types.InlineKeyboardButton(text=tt.add_item, callback_data="admin_addItem"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.edit_cat, callback_data="admin_editCatChooseCategory"), types.InlineKeyboardButton(text=tt.edit_item, callback_data="admin_editItemChooseCategory"))
|
||||
markup.add(btnBackAdmin)
|
||||
return markup
|
||||
|
||||
def get_markup_editCatChooseCategory(cat_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for cat in cat_list:
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_editCat{cat.get_id()}"))
|
||||
markup.add(btnBackItemManagement)
|
||||
return markup
|
||||
|
||||
def get_markup_editCat(cat_id):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_name, callback_data=f"admin_editCatName{cat_id}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data=f"admin_editCatDelete{cat_id}"))
|
||||
markup.add(btnBackEditCatChooseCategory)
|
||||
return markup
|
||||
|
||||
def get_markup_addItemSetCat(cat_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for cat in cat_list:
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_addItemSetCat{cat.get_id()}"))
|
||||
markup.add(btnBackItemManagement)
|
||||
return markup
|
||||
|
||||
btnSkipAddItemSetImage = types.InlineKeyboardButton(text=tt.skip, callback_data="admin_skipSetAddItemSetImage")
|
||||
|
||||
def get_markup_addItemConfirmation():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.confirm, callback_data="admin_addItemConfirm"), types.InlineKeyboardButton(text=tt.deny, callback_data="admin_itemManagement"))
|
||||
return markup
|
||||
|
||||
def get_markup_editItemChooseCategory(cat_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for cat in cat_list:
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_editItemChooseItem{cat.get_id()}"))
|
||||
markup.add(btnBackItemManagement)
|
||||
return markup
|
||||
|
||||
def get_markup_editItemChooseItem(item_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for item in item_list:
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{item.get_id()}] {item.get_name()}", callback_data=f"admin_editItem{item.get_id()}"))
|
||||
markup.add(btnBackEditItemChooseCategory)
|
||||
return markup
|
||||
|
||||
async def get_markup_editItem(item):
|
||||
itemid = item.get_id()
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_name, callback_data=f"admin_editItemName{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_image, callback_data=f"admin_editItemImage{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.show_image if await item.is_hide_image() else tt.hide_image, callback_data=f"admin_editItemHideImage{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_desc, callback_data=f"admin_editItemDesc{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_price, callback_data=f"admin_editItemPrice{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_item_cat, callback_data=f"admin_editItemCat{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_stock, callback_data=f"admin_editItemStock{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=(tt.hide if item.is_active() else tt.show), callback_data=f"admin_editItemHide{itemid}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data=f"admin_editItemDelete{itemid}"))
|
||||
markup.add(btnBackEditItemChooseItem(item.get_cat_id()))
|
||||
return markup
|
||||
|
||||
def get_markup_editItemCat(item_id, cat_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for cat in cat_list:
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{cat.get_id()}] {cat.get_name()}", callback_data=f"admin_editItemSetCat{cat.get_id()}"))
|
||||
markup.add(btnBackEditItem(item_id))
|
||||
return markup
|
||||
|
||||
# User management
|
||||
def get_markup_userManagement():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.user_profile, callback_data="admin_seeUserProfile"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.notify_everyone, callback_data="admin_notifyEveryone"))
|
||||
markup.add(btnBackAdmin)
|
||||
return markup
|
||||
|
||||
def get_markup_notifyEveryoneConfirmation():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.confirm, callback_data="admin_notifyEveryoneConfirm"), btnBackUserManagement)
|
||||
return markup
|
||||
|
||||
def get_markup_seeUserProfile(user):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.orders, callback_data=f"admin_seeUserOrders{user.get_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.remove_manager_role if user.is_manager() else tt.add_manager_role, callback_data=f"admin_changeUserManager{user.get_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.remove_admin_role if user.is_admin() else tt.add_admin_role, callback_data=f"admin_changeUserAdmin{user.get_id()}"))
|
||||
markup.add(btnBackUserManagement)
|
||||
return markup
|
||||
|
||||
def get_markup_seeUserOrders(user):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for order in user.get_orders():
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{order.get_order_id()}]", callback_data=f"admin_seeUserOrder{order.get_order_id()}"))
|
||||
markup.add(btnBackSeeUserProfile(user.get_id()))
|
||||
return markup
|
||||
|
||||
def get_markup_seeUserOrder(order):
|
||||
markup = markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.processing), callback_data=f"admin_changeOrderStatusProcessing{order.get_order_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.delivery), callback_data=f"admin_changeOrderStatusDelivery{order.get_order_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.done), callback_data=f"admin_changeOrderStatusDone{order.get_order_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.cancelled), callback_data=f"admin_changeOrderStatusCancel{order.get_order_id()}"))
|
||||
markup.add(btnBackSeeUserOrders(order.get_user_id()))
|
||||
return markup
|
||||
|
||||
# Shop stats
|
||||
def get_markup_shopStats():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.registration_stats, callback_data="admin_registrationStats"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.order_stats, callback_data="admin_orderStats"))
|
||||
markup.add(btnBackAdmin)
|
||||
return markup
|
||||
|
||||
def get_markup_registrationStats():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.all_time, callback_data="admin_registrationStatsAllTime"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.monthly, callback_data="admin_registrationStatsMonthly"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.weekly, callback_data="admin_registrationStatsWeekly"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.daily, callback_data="admin_registrationStatsDaily"))
|
||||
markup.add(btnBackShopStats)
|
||||
return markup
|
||||
|
||||
def get_markup_orderStats():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.all_time, callback_data="admin_orderStatsAllTime"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.monthly, callback_data="admin_orderStatsMonthly"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.weekly, callback_data="admin_orderStatsWeekly"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.daily, callback_data="admin_orderStatsDaily"))
|
||||
markup.add(btnBackShopStats)
|
||||
return markup
|
||||
|
||||
# Shop settings
|
||||
def get_markup_shopSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.main_settings, callback_data="admin_mainSettings"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.item_settings, callback_data="admin_itemSettings"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.checkout_settings, callback_data="admin_checkoutSettings"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.stats_settings, callback_data="admin_statsSettings"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.additional_settings, callback_data="admin_additionalSettings"))
|
||||
markup.add(btnBackAdmin)
|
||||
return markup
|
||||
|
||||
def get_markup_mainSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=f"Название: {settings.get_shop_name()}", callback_data="admin_changeShopName"))
|
||||
markup.add(types.InlineKeyboardButton(text=f"Приветствие: {settings.get_shop_greeting()}", callback_data="admin_changeShopGreeting"))
|
||||
markup.add(types.InlineKeyboardButton(text=f"Политика возврата: {settings.get_refund_policy()}", callback_data="admin_changeShopRefundPolicy"))
|
||||
markup.add(types.InlineKeyboardButton(text=f"Контакты: {settings.get_shop_contacts()}", callback_data="admin_changeShopContacts"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_sticker if settings.is_sticker_enabled() else tt.enable_sticker, callback_data="admin_changeEnableSticker"))
|
||||
markup.add(btnBackShopSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_itemSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_item_image if settings.is_item_image_enabled() else tt.enable_item_image, callback_data="admin_changeEnableItemImage"))
|
||||
markup.add(btnBackShopSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_checkoutSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delivery_price('{:.2f}'.format(settings.get_delivery_price())), callback_data="admin_changeDeliveryPrice"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_delivery if settings.is_delivery_enabled() else tt.enable_delivery, callback_data="admin_changeEnableDelivery"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_phone_number if settings.is_phone_number_enabled() else tt.enable_phone_number, callback_data="admin_changeEnablePhoneNumber"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_captcha if settings.is_captcha_enabled() else tt.enable_captcha, callback_data="admin_changeEnableCaptcha"))
|
||||
markup.add(btnBackShopSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_statsSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.graph_color, callback_data="admin_statsSettingsColor"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.border_width, callback_data="admin_statsSettingsBorderWidth"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.title_font_size, callback_data="admin_statsSettingsTitleFontSize"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.axis_font_size, callback_data="admin_statsSettingsAxisFontSize"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.tick_font_size, callback_data="admin_statsSettingsTickFontSize"))
|
||||
markup.add(btnBackShopSettingsDel)
|
||||
return markup
|
||||
|
||||
def get_markup_statsSettingsColor():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text="⬛️", callback_data="admin_statsSettingsColorBlack"), types.InlineKeyboardButton(text="⬜️", callback_data="admin_statsSettingsColorWhite"), types.InlineKeyboardButton(text="🟥", callback_data="admin_statsSettingsColorRed"))
|
||||
markup.add(types.InlineKeyboardButton(text="🟨", callback_data="admin_statsSettingsColorYellow"), types.InlineKeyboardButton(text="🟪", callback_data="admin_statsSettingsColorPurple"), types.InlineKeyboardButton(text="🟦", callback_data="admin_statsSettingsColorBlue"))
|
||||
markup.add(types.InlineKeyboardButton(text="🟧", callback_data="admin_statsSettingsColorOrange"), types.InlineKeyboardButton(text="🟩", callback_data="admin_statsSettingsColorGreen"), types.InlineKeyboardButton(text="🟫", callback_data="admin_statsSettingsColorBrown"))
|
||||
markup.add(btnBackStatsSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_statsSettingsBorderWidth():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_borderwidth() == "0" else tt.minus, callback_data="None" if settings.get_borderwidth() == "0" else "admin_statsSettingsBorderWidthReduce"), types.InlineKeyboardButton(text=settings.get_borderwidth(), callback_data="admin_statsSettingsBorderWidthDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsBorderWidthAdd"))
|
||||
markup.add(btnBackStatsSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_statsSettingsTitleFontSize():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_titlefontsize() == "2" else tt.minus, callback_data="None" if settings.get_titlefontsize() == "2" else "admin_statsSettingsTitleFontSizeReduce"), types.InlineKeyboardButton(text=settings.get_titlefontsize(), callback_data="admin_statsSettingsTitleFontSizeDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsTitleFontSizeAdd"))
|
||||
markup.add(btnBackStatsSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_statsSettingsAxisFontSize():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_axisfontsize() == "2" else tt.minus, callback_data="None" if settings.get_axisfontsize() == "2" else "admin_statsSettingsAxisFontSizeReduce"), types.InlineKeyboardButton(text=settings.get_axisfontsize(), callback_data="admin_statsSettingsAxisFontSizeDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsAxisFontSizeAdd"))
|
||||
markup.add(btnBackStatsSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_statsSettingsTickFontSize():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.unavailable if settings.get_tickfontsize() == "2" else tt.minus, callback_data="None" if settings.get_tickfontsize() == "2" else "admin_statsSettingsTickFontSizeReduce"), types.InlineKeyboardButton(text=settings.get_tickfontsize(), callback_data="admin_statsSettingsTickFontSizeDefault"), types.InlineKeyboardButton(text=tt.plus, callback_data="admin_statsSettingsTickFontSizeAdd"))
|
||||
markup.add(btnBackStatsSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_systemSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.clean_images, callback_data="admin_cleanImagesMenu"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.reset_settings, callback_data="admin_resetSettingsMenu"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.clean_database, callback_data="admin_cleanDatabaseMenu"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.backups, callback_data="admin_backups"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.disable_debug if settings.is_debug() else tt.enable_debug, callback_data="admin_changeEnableDebug"))
|
||||
markup.add(btnBackAdditionalSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_backups():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.update_backup, callback_data="admin_updateBackup"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.load_backup, callback_data="admin_loadBackupMenu"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.clean_backups, callback_data="admin_cleanBackupsMenu"))
|
||||
markup.add(btnBackSystemSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_loadBackupMenu():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
backups = listdir("backups")
|
||||
backups.sort(key=lambda b: datetime.strptime(b, "%d-%m-%Y"))
|
||||
for backup in backups[:90]:
|
||||
markup.add(types.InlineKeyboardButton(text=backup, callback_data=f"admin_loadBackup{backup}"))
|
||||
markup.add(btnBackBackups)
|
||||
return markup
|
||||
|
||||
def get_markup_cleanBackupsMenu():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for days in ["7", "30", "90"]:
|
||||
markup.add(types.InlineKeyboardButton(text=f"{days} дней", callback_data=f"admin_cleanBackups{days}"))
|
||||
markup.add(types.InlineKeyboardButton(text="Удалить все резервные копии", callback_data="admin_cleanBackupsAll"))
|
||||
markup.add(btnBackBackups)
|
||||
return markup
|
||||
|
||||
def get_markup_cleanImagesMenu():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data="admin_cleanImages"))
|
||||
markup.add(btnBackSystemSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_resetSettingsMenu():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.reset, callback_data="admin_resetSettings"))
|
||||
markup.add(btnBackSystemSettings)
|
||||
return markup
|
||||
|
||||
def get_markup_cleanDatabaseMenu():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delete, callback_data="admin_cleanDatabase"))
|
||||
markup.add(btnBackSystemSettings)
|
||||
return markup
|
||||
|
||||
# Manager tab
|
||||
def get_markup_seeOrder(order, user_id=None):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.processing), callback_data=f"manager_changeOrderStatusProcessing{order.get_order_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.delivery), callback_data=f"manager_changeOrderStatusDelivery{order.get_order_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.done), callback_data=f"manager_changeOrderStatusDone{order.get_order_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.change_order_status(tt.cancelled), callback_data=f"manager_changeOrderStatusCancel{order.get_order_id()}"))
|
||||
markup.add(btnBackSeeUserOrders(user_id) if user_id else btnBackOrders)
|
||||
return markup
|
||||
|
||||
def get_markup_orders():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.processing, callback_data="manager_ordersProcessing"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.delivery, callback_data="manager_ordersDelivery"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.done, callback_data="manager_ordersDone"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.cancelled, callback_data="manager_ordersCancelled"))
|
||||
return markup
|
||||
|
||||
def get_markup_ordersByOrderList(order_list):
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for order in order_list:
|
||||
markup.add(types.InlineKeyboardButton(text=f"[{order.get_order_id()}]", callback_data=f"manager_seeOrder{order.get_order_id()}"))
|
||||
markup.add(btnBackOrders)
|
||||
return markup
|
||||
|
||||
def get_markup_additionalSettings():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
markup.add(types.InlineKeyboardButton(text=tt.custom_commands, callback_data="admin_customCommands"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.system_settings, callback_data="admin_systemSettings"))
|
||||
markup.add(btnBackShopSettings)
|
||||
return markup
|
||||
|
||||
# Custom commands
|
||||
def get_markup_customCommands():
|
||||
markup = types.InlineKeyboardMarkup()
|
||||
for command in commands.get_commands():
|
||||
markup.add(types.InlineKeyboardButton(text=command.get_command(), callback_data="None"), types.InlineKeyboardButton(text=tt.delete, callback_data=f"admin_deleteCommand{command.get_id()}"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.line_separator, callback_data="None"))
|
||||
markup.add(types.InlineKeyboardButton(text=tt.add_command, callback_data="admin_addCommand"))
|
||||
markup.add(btnBackAdditionalSettings)
|
||||
return markup
|
||||
108
doners/shop-telegram-bot/src/order.py
Normal file
108
doners/shop-telegram-bot/src/order.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
import item as itm
|
||||
import text_templates as tt
|
||||
from settings import Settings
|
||||
|
||||
settings = Settings()
|
||||
|
||||
conn = sqlite3.connect("data.db")
|
||||
c = conn.cursor()
|
||||
|
||||
class Order:
|
||||
def __init__(self, id):
|
||||
self.id = id
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.get_order_id()}"
|
||||
|
||||
def get_order_id(self):
|
||||
return self.id
|
||||
|
||||
def __clist(self):
|
||||
c.execute(f"SELECT * FROM orders WHERE order_id=?", [self.get_order_id()])
|
||||
return list(c)[0]
|
||||
|
||||
def get_user_id(self):
|
||||
return self.__clist()[1]
|
||||
|
||||
def get_item_list_raw(self):
|
||||
return self.__clist()[2]
|
||||
|
||||
def get_item_list(self):
|
||||
return list(map(itm.Item, [item_id for item_id in self.get_item_list_raw().split(",")]))
|
||||
|
||||
def get_item_list_amount(self):
|
||||
cart = [item.get_id() for item in self.get_item_list()]
|
||||
return [[itm.Item(item_id), cart.count(item_id)] for item_id in set(cart)]
|
||||
|
||||
def get_item_list_price(self):
|
||||
return sum([item_and_price[0].get_price() * item_and_price[1] for item_and_price in self.get_item_list_amount()]) + (float(settings.get_delivery_price()) if self.get_home_adress() != None else 0)
|
||||
|
||||
def set_item_list(self, value):
|
||||
c.execute(f"UPDATE orders SET item_list=? WHERE order_id=?", [value, self.get_order_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_email_adress(self):
|
||||
return self.__clist()[3]
|
||||
|
||||
def set_email_adress(self, value):
|
||||
c.execute(f"UPDATE orders SET email_adress=? WHERE order_id=?", [value, self.get_order_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_phone_number(self):
|
||||
return self.__clist()[4]
|
||||
|
||||
def set_phone_number(self, value):
|
||||
c.execute(f"UPDATE orders SET phone_number=? WHERE order_id=?", [value, self.get_order_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_home_adress(self):
|
||||
return self.__clist()[5]
|
||||
|
||||
def set_home_adress(self, value):
|
||||
c.execute(f"UPDATE orders SET home_adress=? WHERE order_id=?", [value, self.get_order_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_additional_message(self):
|
||||
return self.__clist()[6]
|
||||
|
||||
def get_date(self):
|
||||
return datetime.strptime(self.__clist()[7], "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def get_date_string(self):
|
||||
return self.__clist()[7]
|
||||
|
||||
def get_status(self):
|
||||
return self.__clist()[8]
|
||||
|
||||
def get_status_string(self):
|
||||
return get_status_dict()[self.__clist()[8]]
|
||||
|
||||
def set_status(self, value):
|
||||
c.execute(f"UPDATE orders SET status=? WHERE order_id=?", [value, self.get_order_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_status_dict():
|
||||
return {
|
||||
0: tt.processing,
|
||||
1: tt.delivery,
|
||||
2: tt.done,
|
||||
-1: tt.cancelled,
|
||||
}
|
||||
|
||||
def get_order_list(status=None):
|
||||
if status:
|
||||
c.execute(f"SELECT * FROM orders WHERE status=?", [status])
|
||||
else:
|
||||
c.execute(f"SELECT * FROM orders")
|
||||
return list(map(Order, [order[0] for order in list(c)]))
|
||||
|
||||
def does_order_exist(order_id):
|
||||
c.execute(f"SELECT * FROM orders WHERE order_id=?", [order_id])
|
||||
return len(list(c)) == 1
|
||||
|
||||
def create_order(order_id, user_id, item_list, email_adress, additional_message, phone_number="None", home_adress="None"):
|
||||
c.execute(f"INSERT INTO orders VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)", [order_id, user_id, item_list, email_adress, phone_number, home_adress, additional_message, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 0])
|
||||
conn.commit()
|
||||
return Order(order_id)
|
||||
45
doners/shop-telegram-bot/src/search.py
Normal file
45
doners/shop-telegram-bot/src/search.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import pymorphy2
|
||||
from pyparsing import opAssoc
|
||||
import item as itm
|
||||
|
||||
m = pymorphy2.MorphAnalyzer()
|
||||
|
||||
excluded_words = ["без", "в", "для", "до", "за", "из", "к", "под", "а", "о", "над", "на", "о", "об", "от", "перед", "по", "под", "при", "про", "с", "у"]
|
||||
|
||||
def get_normal_forms(word):
|
||||
return set(p.normal_form for p in m.parse(word))
|
||||
|
||||
|
||||
class Query:
|
||||
def __init__(self, results):
|
||||
self.results = results
|
||||
|
||||
def __get_items(self):
|
||||
return list(map(itm.Item, self.results.keys()))[::-1][:90]
|
||||
|
||||
def match(self):
|
||||
return self.__get_items()
|
||||
|
||||
def price(self):
|
||||
return sorted(list(map(itm.Item, self.results.keys()))[::-1], key=lambda item: item.get_price())
|
||||
|
||||
def popular(self):
|
||||
pass
|
||||
|
||||
# a match in a name is 3 points
|
||||
# a match in a desc is 1 point
|
||||
def search_item(query):
|
||||
points = dict()
|
||||
for item in itm.get_item_list():
|
||||
points[item.get_id()] = 0
|
||||
for word in list(filter(lambda word: word not in excluded_words, query.split())):
|
||||
for p in get_normal_forms(word):
|
||||
for word_title in item.get_name().split():
|
||||
if p in get_normal_forms(word_title):
|
||||
points[item.get_id()] += 3
|
||||
for word_title in item.get_desc().split():
|
||||
if p in get_normal_forms(word_title):
|
||||
points[item.get_id()] += 1
|
||||
if points[item.get_id()] == 0:
|
||||
points.pop(item.get_id())
|
||||
return Query(dict(sorted(points.items(), key=lambda item: item[1])))
|
||||
170
doners/shop-telegram-bot/src/settings.py
Normal file
170
doners/shop-telegram-bot/src/settings.py
Normal file
@@ -0,0 +1,170 @@
|
||||
from configparser import ConfigParser
|
||||
from genericpath import exists
|
||||
from os import remove
|
||||
import sqlite3
|
||||
|
||||
class Settings:
|
||||
def __init__(self, config_path="config.ini"):
|
||||
self.__config_path = config_path
|
||||
|
||||
def __get_config(self):
|
||||
conf = ConfigParser()
|
||||
conf.read(self.__config_path)
|
||||
return conf
|
||||
|
||||
def __set_setting(self, category, subcategory, value):
|
||||
conf = self.__get_config()
|
||||
conf.set(category, subcategory, str(value))
|
||||
with open(self.__config_path, 'w') as config:
|
||||
conf.write(config)
|
||||
|
||||
# main_settings
|
||||
def get_token(self):
|
||||
return self.__get_config()["main_settings"]["token"]
|
||||
|
||||
def set_token(self, value):
|
||||
self.__set_setting("main_settings", "token", value)
|
||||
|
||||
def is_debug(self):
|
||||
return self.__get_config()["main_settings"]["debug"] == "1"
|
||||
|
||||
def set_debug(self, value):
|
||||
self.__set_setting("main_settings", "debug", value)
|
||||
|
||||
def get_main_admin_id(self):
|
||||
return self.__get_config()["main_settings"]["mainadminid"]
|
||||
|
||||
def set_main_admin_id(self, value):
|
||||
self.__set_setting("main_settings", "mainadminid", value)
|
||||
|
||||
# shop_settings
|
||||
def get_shop_name(self):
|
||||
return self.__get_config()["shop_settings"]["name"]
|
||||
|
||||
def set_shop_name(self, value):
|
||||
self.__set_setting("shop_settings", "name", value)
|
||||
|
||||
def get_shop_greeting(self):
|
||||
return self.__get_config()["shop_settings"]["greeting"]
|
||||
|
||||
def set_shop_greeting(self, value):
|
||||
self.__set_setting("shop_settings", "greeting", value)
|
||||
|
||||
def is_sticker_enabled(self):
|
||||
return self.__get_config()["shop_settings"]["enablesticker"] == "1"
|
||||
|
||||
def set_enable_sticker(self, value):
|
||||
self.__set_setting("shop_settings", "enablesticker", value)
|
||||
|
||||
def get_refund_policy(self):
|
||||
return self.__get_config()["shop_settings"]["refundpolicy"]
|
||||
|
||||
def set_refund_policy(self, value):
|
||||
self.__set_setting("shop_settings", "refundpolicy", value)
|
||||
|
||||
def get_shop_contacts(self):
|
||||
return self.__get_config()["shop_settings"]["contacts"]
|
||||
|
||||
def set_shop_contacts(self, value):
|
||||
self.__set_setting("shop_settings", "contacts", value)
|
||||
|
||||
def is_item_image_enabled(self):
|
||||
return self.__get_config()["shop_settings"]["enableimage"] == "1"
|
||||
|
||||
def set_item_image(self, value):
|
||||
self.__set_setting("shop_settings", "enableimage", value)
|
||||
|
||||
def is_phone_number_enabled(self):
|
||||
return self.__get_config()["shop_settings"]["enablephonenumber"] == "1"
|
||||
|
||||
def set_enable_phone_number(self, value):
|
||||
self.__set_setting("shop_settings", "enablephonenumber", value)
|
||||
|
||||
def is_delivery_enabled(self):
|
||||
return self.__get_config()["shop_settings"]["enabledelivery"] == "1"
|
||||
|
||||
def set_delivery(self, value):
|
||||
self.__set_setting("shop_settings", "enabledelivery", value)
|
||||
|
||||
def get_delivery_price(self):
|
||||
return float(self.__get_config()["shop_settings"]["delivery_price"])
|
||||
|
||||
def set_delivery_price(self, value):
|
||||
self.__set_setting("shop_settings", "delivery_price", value)
|
||||
|
||||
def is_captcha_enabled(self):
|
||||
return self.__get_config()["shop_settings"]["enablecaptcha"] == "1"
|
||||
|
||||
def set_enable_captcha(self, value):
|
||||
self.__set_setting("shop_settings", "enablecaptcha", value)
|
||||
|
||||
# stats_settings
|
||||
def get_barcolor(self):
|
||||
return self.__get_config()["stats_settings"]["barcolor"]
|
||||
|
||||
def set_barcolor(self, value):
|
||||
self.__set_setting("stats_settings", "barcolor", value)
|
||||
|
||||
def get_borderwidth(self):
|
||||
return self.__get_config()["stats_settings"]["borderwidth"]
|
||||
|
||||
def set_borderwidth(self, value):
|
||||
self.__set_setting("stats_settings", "borderwidth", value)
|
||||
|
||||
def get_titlefontsize(self):
|
||||
return self.__get_config()["stats_settings"]["titlefontsize"]
|
||||
|
||||
def set_titlefontsize(self, value):
|
||||
self.__set_setting("stats_settings", "titlefontsize", value)
|
||||
|
||||
def get_axisfontsize(self):
|
||||
return self.__get_config()["stats_settings"]["axisfontsize"]
|
||||
|
||||
def set_axisfontsize(self, value):
|
||||
self.__set_setting("stats_settings", "axisfontsize", value)
|
||||
|
||||
def get_tickfontsize(self):
|
||||
return self.__get_config()["stats_settings"]["tickfontsize"]
|
||||
|
||||
def set_tickfontsize(self, value):
|
||||
self.__set_setting("stats_settings", "tickfontsize", value)
|
||||
|
||||
def reset(self):
|
||||
DEFAULT_CONFIG_TEXT = f"""[main_settings]
|
||||
token = {self.get_token()}
|
||||
mainadminid = {self.get_main_admin_id()}
|
||||
debug = 0
|
||||
|
||||
[shop_settings]
|
||||
name = Название магазина
|
||||
greeting = Добро пожаловать!
|
||||
refundpolicy = Текст для вкладки "Политика возврата"
|
||||
contacts = Текст для вкладки "Контакты"
|
||||
enableimage = 1
|
||||
enablesticker = 0
|
||||
enablephonenumber = 0
|
||||
enabledelivery = 0
|
||||
delivery_price = 0.0
|
||||
enablecaptcha = 1
|
||||
|
||||
[stats_settings]
|
||||
barcolor = 3299ff
|
||||
borderwidth = 1
|
||||
titlefontsize = 20
|
||||
axisfontsize = 12
|
||||
tickfontsize = 8
|
||||
"""
|
||||
if exists("config.ini"):
|
||||
remove("config.ini")
|
||||
with open("config.ini", "w") as config:
|
||||
config.write(DEFAULT_CONFIG_TEXT)
|
||||
|
||||
def clean_db(self):
|
||||
conn = sqlite3.connect("data.db")
|
||||
c = conn.cursor()
|
||||
c.execute("DELETE FROM cats")
|
||||
c.execute("DELETE FROM items")
|
||||
c.execute("DELETE FROM orders")
|
||||
c.execute("DELETE FROM commands")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
111
doners/shop-telegram-bot/src/state_handler.py
Normal file
111
doners/shop-telegram-bot/src/state_handler.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from urllib import response
|
||||
from aiogram.dispatcher.filters.state import StatesGroup, State
|
||||
|
||||
# Item management
|
||||
class addCat(StatesGroup):
|
||||
state_message = State()
|
||||
name = State()
|
||||
|
||||
class changeCatName(StatesGroup):
|
||||
state_message = State()
|
||||
cat_id = State()
|
||||
name = State()
|
||||
|
||||
class addItem(StatesGroup):
|
||||
# Required
|
||||
name = State()
|
||||
price = State()
|
||||
cat_id = State()
|
||||
desc = State()
|
||||
confirmation = State()
|
||||
|
||||
# Additional
|
||||
image = State()
|
||||
|
||||
class changeItemPrice(StatesGroup):
|
||||
state_message = State()
|
||||
item_id = State()
|
||||
price = State()
|
||||
|
||||
class changeItemImage(StatesGroup):
|
||||
state_message = State()
|
||||
item_id = State()
|
||||
image = State()
|
||||
|
||||
class changeItemDesc(StatesGroup):
|
||||
state_message = State()
|
||||
item_id = State()
|
||||
desc = State()
|
||||
|
||||
class changeItemName(StatesGroup):
|
||||
state_message = State()
|
||||
item_id = State()
|
||||
name = State()
|
||||
|
||||
class changeItemCat(StatesGroup):
|
||||
item_id = State()
|
||||
cat = State()
|
||||
|
||||
class changeItemStock(StatesGroup):
|
||||
item_id = State()
|
||||
state_message = State()
|
||||
stock = State()
|
||||
|
||||
# User management
|
||||
class notifyEveryone(StatesGroup):
|
||||
state_message = State()
|
||||
message = State()
|
||||
confirmation = State()
|
||||
|
||||
class seeUserProfile(StatesGroup):
|
||||
state_message = State()
|
||||
user_id = State()
|
||||
|
||||
|
||||
# Checkout
|
||||
class checkoutCart(StatesGroup):
|
||||
# Data
|
||||
user_id = State()
|
||||
item_list_comma = State()
|
||||
order_id = State()
|
||||
|
||||
# Required
|
||||
email = State()
|
||||
additional_message = State()
|
||||
confirmation = State()
|
||||
|
||||
# Additional
|
||||
phone_number = State()
|
||||
home_adress = State()
|
||||
captcha = State()
|
||||
|
||||
# Main settings
|
||||
class changeShopName(StatesGroup):
|
||||
state_message = State()
|
||||
name = State()
|
||||
|
||||
class changeShopGreeting(StatesGroup):
|
||||
state_message = State()
|
||||
greeting = State()
|
||||
|
||||
class changeShopRefundPolicy(StatesGroup):
|
||||
state_message = State()
|
||||
refund_policy = State()
|
||||
|
||||
class changeShopContacts(StatesGroup):
|
||||
state_message = State()
|
||||
contacts = State()
|
||||
|
||||
class changeDeliveryPrice(StatesGroup):
|
||||
state_message = State()
|
||||
price = State()
|
||||
|
||||
class addCustomCommand(StatesGroup):
|
||||
command = State()
|
||||
response = State()
|
||||
|
||||
|
||||
# Misc
|
||||
class search(StatesGroup):
|
||||
state_message = State()
|
||||
query = State()
|
||||
63
doners/shop-telegram-bot/src/stats.py
Normal file
63
doners/shop-telegram-bot/src/stats.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import datetime
|
||||
import matplotlib.pyplot as plt
|
||||
import order as ordr
|
||||
import user as usr
|
||||
from random import randint
|
||||
from settings import Settings
|
||||
|
||||
settings = Settings()
|
||||
|
||||
def saveplot(data, title, ylabel):
|
||||
plt.autoscale()
|
||||
plt.figure(figsize=(10, 10))
|
||||
plt.title(title, fontsize=settings.get_titlefontsize())
|
||||
plt.xlabel("Дата", fontsize=settings.get_axisfontsize())
|
||||
plt.ylabel(ylabel, fontsize=settings.get_axisfontsize())
|
||||
plt.tick_params(labelsize=settings.get_tickfontsize())
|
||||
plt.bar(range(len(data)), list(data.values()), color="#" + settings.get_barcolor(), edgecolor="black", linewidth=settings.get_borderwidth())
|
||||
plt.xticks(range(len(data)), list(data.keys()), rotation=90)
|
||||
plt.savefig(f"images/stats.png")
|
||||
plt.close()
|
||||
return open(f"images/stats.png", "rb")
|
||||
|
||||
|
||||
def get_random_data():
|
||||
return {f"{randint(1, 30):02}.{randint(1, 12):02}.{randint(2010, 2030)}": randint(5, 100) for _ in range(randint(2, 30))}
|
||||
|
||||
|
||||
def get_random_graph():
|
||||
return saveplot(get_random_data(), "Название", "Ось Y")
|
||||
|
||||
|
||||
class RegistrationCharts:
|
||||
def __init__(self):
|
||||
self.user_list = usr.get_user_list()
|
||||
|
||||
def saveplot(self, data, title):
|
||||
return saveplot(data, title, "Количество регистраций")
|
||||
|
||||
def all_time(self):
|
||||
return self.saveplot({f"{date.day:02}.{date.month:02}.{date.year}": [user.get_register_date().date() for user in self.user_list].count(date) for date in dict.fromkeys([user.get_register_date().date() for user in self.user_list])}, "Регистрации за все время")
|
||||
|
||||
def last_x_days(self, days):
|
||||
return self.saveplot({f"{(datetime.date.today() - datetime.timedelta(days=i)).day:02}.{(datetime.date.today() - datetime.timedelta(days=i)).month:02}": len(list(filter(lambda user: user.get_register_date().date() == datetime.date.today() - datetime.timedelta(days=i), self.user_list))) for i in range(30, -1, -1)}, f"Регистрации за последние {days} дней")
|
||||
|
||||
def last_x_hours(self, hours):
|
||||
return self.saveplot({f"{(datetime.datetime.today() - datetime.timedelta(hours=i)).hour:02}:00": len(list(filter(lambda user: user.get_register_date().hour == (datetime.datetime.now() - datetime.timedelta(hours=i)).hour and user.get_register_date() > datetime.datetime.now() - datetime.timedelta(hours=hours), self.user_list))) for i in range(hours, -1, -1)}, f"Регистрации за последние {hours} часов.")
|
||||
|
||||
|
||||
class OrderCharts:
|
||||
def __init__(self):
|
||||
self.order_list = ordr.get_order_list()
|
||||
|
||||
def saveplot(self, data, title):
|
||||
return saveplot(data, title, "Количество заказов")
|
||||
|
||||
def all_time(self):
|
||||
return self.saveplot({f"{date.day:02}.{date.month:02}.{date.year}": [order.get_date().date() for order in self.order_list].count(date) for date in dict.fromkeys([order.get_date().date() for order in self.order_list])}, "Заказы за все время")
|
||||
|
||||
def last_x_days(self, days):
|
||||
return self.saveplot({f"{(datetime.date.today() - datetime.timedelta(days=i)).day:02}.{(datetime.date.today() - datetime.timedelta(days=i)).month:02}": len(list(filter(lambda order: order.get_date().date() == datetime.date.today() - datetime.timedelta(days=i), self.order_list))) for i in range(days, -1, -1)}, f"Заказы за последние {days} дней")
|
||||
|
||||
def last_x_hours(self, hours):
|
||||
return self.saveplot({f"{(datetime.datetime.today() - datetime.timedelta(hours=i)).hour:02}:00": len(list(filter(lambda order: order.get_date() > datetime.datetime.now() - datetime.timedelta(hours=hours) and order.get_date().hour == (datetime.datetime.today() - datetime.timedelta(hours=i)).hour, self.order_list))) for i in range(hours, -1, -1)}, "Заказы за сегодня" if hours == (datetime.datetime.now().hour + 1) else f"Заказы за последние {hours} часов.")
|
||||
177
doners/shop-telegram-bot/src/text_templates.py
Normal file
177
doners/shop-telegram-bot/src/text_templates.py
Normal file
@@ -0,0 +1,177 @@
|
||||
from settings import Settings
|
||||
|
||||
settings = Settings()
|
||||
|
||||
line_separator = "➖➖➖➖➖"
|
||||
|
||||
|
||||
# Multiple lines
|
||||
def get_profile_template(user):
|
||||
return f"{line_separator}\n📝 id: {user.get_id()}\n📈 Кол-во заказов: {len(user.get_orders())}\n📅 Дата регистрации: {user.get_register_date_string()}\n{line_separator}"
|
||||
|
||||
def get_faq_template(shop_name):
|
||||
return f"{line_separator}\nℹ️ FAQ магазина {shop_name}\n{line_separator}"
|
||||
|
||||
def get_categories_template():
|
||||
return f"{line_separator}\n🛍️ Категории\n{line_separator}"
|
||||
|
||||
def get_category_was_created_successfuly(cat_name):
|
||||
return f"Категория {cat_name} была успешно создана."
|
||||
|
||||
def get_category_data(cat):
|
||||
return f"{line_separator}\nID: {cat.get_id()}\nНазвание: {cat.get_name()}\n{line_separator}"
|
||||
|
||||
def get_item_card(item=None, name=None, price=None, desc=None, amount=None):
|
||||
if item:
|
||||
name = item.get_name()
|
||||
price = item.get_price()
|
||||
desc = item.get_desc()
|
||||
amount = item.get_amount()
|
||||
|
||||
return f"{line_separator}\n{name} - {'{:.2f}'.format(price)} руб.\nВ наличии: {amount} шт.\n{line_separator}\n{desc}"
|
||||
|
||||
def get_order_confirmation_template(item_amount_dict, cart_price, email_adress, additional_message, phone_number=None, home_adress=None):
|
||||
item_amount_dict_formatted = '\n'.join([f'\t· {item[0].get_name()} - {item[1]} шт.' for item in item_amount_dict])
|
||||
phone_number = f"Номер телефона: {phone_number}\n" if phone_number else ""
|
||||
home_adress = f"Адрес доставки: {home_adress}\n" if home_adress else ""
|
||||
return f"{line_separator}\nТовары:\n{item_amount_dict_formatted}\nСумма: {cart_price}руб.\nEmail: {email_adress}\n{phone_number}{home_adress}Комментарий к заказу: {additional_message}\n{line_separator}\nВы уверены, что хотите оформить заказ?"
|
||||
|
||||
def get_order_template(order):
|
||||
item_list_amount_formatted = '\n'.join([f'\t· {item[0].get_name()} - {item[1]} шт.' for item in order.get_item_list_amount()])
|
||||
phone_number = f"Номер телефона: {order.get_phone_number()}\n" if settings.is_phone_number_enabled() else ""
|
||||
home_adress = f"Адрес доставки: {order.get_home_adress()}\n" if settings.is_delivery_enabled() else f"Самовывоз\n"
|
||||
return f"{line_separator}\nID заказа: {order.get_order_id()}\nID пользователя: {order.get_user_id()}\nТовары:\n{item_list_amount_formatted}\nСумма: {order.get_item_list_price()}руб.\nEmail: {order.get_email_adress()}\n{phone_number}{home_adress}Комментарий к заказу: {order.get_additional_message()}\nСтатус заказа: {order.get_status_string()}\nДата: {order.get_date_string()}\n{line_separator}"
|
||||
|
||||
# Single phrases
|
||||
# /start
|
||||
admin_panel = "🔴 Админ панель"
|
||||
faq = "ℹ️ FAQ"
|
||||
profile = "📁 Профиль"
|
||||
catalogue = "🗄️ Каталог"
|
||||
cart = "🛒 Корзина"
|
||||
support_menu = "☎ Меню тех. поддержки"
|
||||
|
||||
# Admin panel tabs
|
||||
item_management = "📦 Управление товаром"
|
||||
user_management = "🧍 Управление пользователями"
|
||||
shop_stats = "📈 Статистика магазина (BETA)"
|
||||
bot_settings = "⚙ Настройки бота"
|
||||
|
||||
# FAQ
|
||||
contacts = "📞 Контакты"
|
||||
refund = "🎫 Политика возврата"
|
||||
|
||||
# Profile
|
||||
my_orders = "📂 Мои заказы"
|
||||
cancel_order = "❌ Отменить заказ"
|
||||
restore_order = "✅ Восстановить заказ"
|
||||
my_support_tickets = "🙋 Мои тикеты в тех. поддержку"
|
||||
enable_notif = "🔔Включить оповещения о заказах"
|
||||
disable_notif = "🔕Выключить оповещения о заказах"
|
||||
|
||||
# Catalogue / Item / Cart
|
||||
search = "🔍 Найти"
|
||||
add_to_cart = "🛒 Добавить в корзину"
|
||||
cart_is_empty = "Корзина пуста."
|
||||
pickup = "✅Самовывоз"
|
||||
def delivery_on(price): return f"✅ Доставка - {price}руб."
|
||||
def delivery_off(price): return f"❌ Доставка - {price}руб."
|
||||
cart_checkout = "Оформить заказ"
|
||||
clear_cart = "Очистить корзину"
|
||||
processing = "Обрабатывается"
|
||||
delivery = "Ожидает доставки"
|
||||
done = "Готов"
|
||||
cancelled = "Отменён"
|
||||
|
||||
# Item management
|
||||
add_cat = "🛍️ Добавить категорию"
|
||||
add_item = "🗃️ Добавить товар"
|
||||
edit_cat = "✏️ Редактировать категорию"
|
||||
edit_item = "✏️ Редактировать товар"
|
||||
change_name = "📋 Изменить название"
|
||||
change_image = "🖼️ Изменить изображение"
|
||||
hide_image = "🙈 Скрыть изображение"
|
||||
show_image = "🐵 Показать изображение"
|
||||
change_desc = "📝 Изменить описание"
|
||||
change_price = "🏷️ Изменить цену"
|
||||
change_item_cat = "🛍️ Изменить категорию"
|
||||
change_stock = "📦 Изменить кол-во"
|
||||
|
||||
# User management
|
||||
user_profile = "📁Профиль пользователя"
|
||||
notify_everyone = "🔔Оповещение всем пользователям"
|
||||
orders = "📁 Заказы"
|
||||
remove_manager_role = "👨💼 Убрать роль менеджера"
|
||||
add_manager_role = "👨💼 Сделать менеджером"
|
||||
remove_admin_role = "🔴 Убрать роль администратора"
|
||||
add_admin_role = "🔴 Сделать администратором"
|
||||
def change_order_status(status): return f"Изменить статус на \"{status}\""
|
||||
|
||||
# Shop stats
|
||||
registration_stats = "👥Статистика регистраций"
|
||||
order_stats = "📦Статистика заказов"
|
||||
all_time = "За всё время"
|
||||
monthly = "За последние 30 дней"
|
||||
weekly = "За последние 7 дней"
|
||||
daily = "За последние 24 часа"
|
||||
|
||||
# Shop settings
|
||||
main_settings = "🛠️ Основные настройки"
|
||||
item_settings = "🗃️ Настройки товаров"
|
||||
additional_settings = "📖 Дополнительные настройки"
|
||||
custom_commands = "📖 Команды"
|
||||
add_command = "📝 Добавить команду"
|
||||
clean_logs = "📖 Очистить логи"
|
||||
clean_logs_text = "⚠️ Вы уверены, что хотите очистить логи? Они будут удалены без возможности восстановления!\n(Логи за сегодняшний день не будут удалены)"
|
||||
backups = "💾 Резервное копирование"
|
||||
update_backup = "🔄 Обновить резервную копию"
|
||||
load_backup = "💿 Загрузить резервную копию"
|
||||
clean_backups = "🧹 Очистка резервных копий"
|
||||
system_settings = "💻 Система"
|
||||
clean_images = "🗑️ Удалить неиспользуемые изображения"
|
||||
clean_images_text = "⚠️ Вы уверены, что хотите удалить неспользуемые изображения? Они будут удалены без возможности восстановления!"
|
||||
clean_database = "📚 Очистить базу данных"
|
||||
clean_database_text = "⚠️ Вы уверены, что хотите очистить базу данных? Все данные будут удалены без возможности восстановления!"
|
||||
reset_settings = "⚙️ Сбросить настройки"
|
||||
resert_settings_text = "⚠️ Вы уверены, что хотите сбросить настройки? Все данные будут удалены без возможности восстановления!"
|
||||
disable_item_image = "✅ Картинки товаров"
|
||||
enable_item_image = "❌ Картинки товаров"
|
||||
checkout_settings = "💳 Настройки оформления заказа"
|
||||
stats_settings = "📈 Настройки статистики"
|
||||
graph_color = "🌈 Цвет графика"
|
||||
border_width = "🔲 Ширина обводки"
|
||||
title_font_size = "ℹ️ Размер названия графика"
|
||||
axis_font_size = "↔️Размер текста для осей"
|
||||
tick_font_size = "🔢Размер текста для делений"
|
||||
unavailable = "⛔️"
|
||||
minus = "➖"
|
||||
plus = "➕"
|
||||
enable_sticker = "❌ Стикер в приветствии"
|
||||
disable_sticker = "✅ Стикер в приветствии"
|
||||
enable_phone_number = "❌ Номер телефона при заказе"
|
||||
disable_phone_number = "✅ Номер телефона при заказе"
|
||||
enable_delivery = "❌ Доставка"
|
||||
disable_delivery = "✅ Доставка"
|
||||
def delivery_price(price): return f"🚚 Стоимость доставки: {price}руб."
|
||||
enable_captcha = "❌ CAPTCHA при заказе"
|
||||
disable_captcha = "✅ CAPTCHA при заказе"
|
||||
enable_debug = "❌ Режим отладки"
|
||||
disable_debug = "✅ Режим отладки"
|
||||
|
||||
# Manager tab
|
||||
view_order = "📂 Посмотреть заказ"
|
||||
|
||||
# Misc buttons
|
||||
skip = "⏭ Пропустить"
|
||||
back = "🔙 Назад"
|
||||
confirm = "✅ Да"
|
||||
deny = "❌ Нет"
|
||||
error = "Произошла ошибка!"
|
||||
or_press_back = "или нажмите на кнопку \"Назад\"."
|
||||
hide = "🙈 Скрыть"
|
||||
show = "🐵 Показать"
|
||||
delete = "❌ Удалить"
|
||||
reset = "❌ Сбросить"
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(delivery_on)
|
||||
111
doners/shop-telegram-bot/src/user.py
Normal file
111
doners/shop-telegram-bot/src/user.py
Normal file
@@ -0,0 +1,111 @@
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
import item as itm
|
||||
from order import Order
|
||||
from settings import Settings
|
||||
|
||||
conn = sqlite3.connect('data.db')
|
||||
c = conn.cursor()
|
||||
settings = Settings()
|
||||
|
||||
class User:
|
||||
def __init__(self, user_id):
|
||||
self.__user_id = user_id
|
||||
|
||||
if not does_user_exist(self.get_id()):
|
||||
c.execute(f"INSERT INTO users VALUES(?, ?, ?, ?, ?, ?, ?)", [self.get_id(), 1 if str(self.get_id()) == settings.get_main_admin_id() else 0, 0, 0, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "None", 1])
|
||||
conn.commit()
|
||||
|
||||
def get_id(self):
|
||||
return self.__user_id
|
||||
|
||||
def __clist(self):
|
||||
c.execute(f"SELECT * FROM users WHERE user_id=?", [self.get_id()])
|
||||
return list(c)[0]
|
||||
|
||||
def is_admin(self):
|
||||
return self.__clist()[1] == 1
|
||||
|
||||
def set_admin(self, value):
|
||||
c.execute(f"UPDATE users SET is_admin=? WHERE user_id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def is_manager(self):
|
||||
return self.__clist()[2] == 1
|
||||
|
||||
def set_manager(self, value):
|
||||
c.execute(f"UPDATE users SET is_manager=? WHERE user_id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_register_date(self):
|
||||
return datetime.strptime(self.__clist()[4], "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
def get_register_date_string(self):
|
||||
return self.__clist()[4]
|
||||
|
||||
def notif_on(self):
|
||||
return self.__clist()[3] == 1
|
||||
|
||||
def set_notif_enable(self, value):
|
||||
c.execute(f"UPDATE users SET notification=? WHERE user_id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def get_orders(self):
|
||||
c.execute(f"SELECT * FROM orders WHERE user_id=?", [self.get_id()])
|
||||
return list(map(Order, [order[0] for order in list(c)]))[::-1]
|
||||
|
||||
def get_cart_comma(self):
|
||||
return self.__clist()[5]
|
||||
|
||||
def get_cart(self):
|
||||
cart = self.get_cart_comma()
|
||||
return [] if cart == "None" else list(map(itm.Item, cart.split(",")))
|
||||
|
||||
def get_cart_amount(self):
|
||||
cart = [item.get_id() for item in self.get_cart()]
|
||||
return [[itm.Item(item_id), cart.count(item_id)] for item_id in set(cart)]
|
||||
|
||||
def get_cart_price(self):
|
||||
return sum([item_and_price[0].get_price() * item_and_price[1] for item_and_price in self.get_cart_amount()]) + (settings.get_delivery_price() if self.is_cart_delivery() else 0)
|
||||
|
||||
def clear_cart(self):
|
||||
c.execute(f"UPDATE users SET cart=\"None\" WHERE user_id=?", [self.get_id()])
|
||||
self.set_cart_delivery(1)
|
||||
conn.commit()
|
||||
|
||||
def add_to_cart(self, item_id):
|
||||
cart = self.get_cart()
|
||||
c.execute(f"UPDATE users SET cart=? WHERE user_id=?", [",".join([str(item.get_id()) for item in cart + [itm.Item(item_id)]]) if cart else item_id, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def remove_from_cart(self, item_id):
|
||||
cart = [item.get_id() for item in self.get_cart()]
|
||||
cart.remove(str(item_id))
|
||||
c.execute(f"UPDATE users SET cart=? WHERE user_id=?", [",".join(cart) if cart else "None", self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
def is_cart_delivery(self):
|
||||
return self.__clist()[6] == 1
|
||||
|
||||
def set_cart_delivery(self, value):
|
||||
c.execute(f"UPDATE users SET cart_delivery=? WHERE user_id=?", [value, self.get_id()])
|
||||
conn.commit()
|
||||
|
||||
|
||||
def does_user_exist(user_id):
|
||||
c.execute(f"SELECT * FROM users WHERE user_id=?", [user_id])
|
||||
return len(list(c)) != 0
|
||||
|
||||
|
||||
def get_notif_list():
|
||||
c.execute(f"SELECT * FROM users WHERE notification=1")
|
||||
return list(map(User, [user[0] for user in list(c)]))
|
||||
|
||||
|
||||
def get_user_login(message):
|
||||
return message.from_user.username
|
||||
|
||||
|
||||
def get_user_list():
|
||||
c.execute("SELECT * FROM users")
|
||||
return list(map(User, [user[0] for user in list(c)]))
|
||||
3
doners/shop-telegram-bot/start.cmd
Normal file
3
doners/shop-telegram-bot/start.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
cls
|
||||
echo "Starting the bot..."
|
||||
python3 src/main.py
|
||||
4
doners/shop-telegram-bot/start.sh
Normal file
4
doners/shop-telegram-bot/start.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
clear
|
||||
echo "Starting the bot..."
|
||||
python3.10 src/main.py
|
||||
|
||||
0
doners/shop-telegram-bot/tests/__init__.py
Normal file
0
doners/shop-telegram-bot/tests/__init__.py
Normal file
127
doners/shop-telegram-bot/tests/test_item.py
Normal file
127
doners/shop-telegram-bot/tests/test_item.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import unittest
|
||||
from random import randint
|
||||
from .utils import *
|
||||
|
||||
import src.item as itm
|
||||
import src.category as cat
|
||||
|
||||
|
||||
|
||||
class TestItem(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.test_data_random = {
|
||||
"name": get_random_string(10, 40),
|
||||
"price": get_random_float(100, 10000),
|
||||
"cat_id": randint(1, 100),
|
||||
"desc": get_random_string(10, 255),
|
||||
}
|
||||
self.item_random = itm.create_item(self.test_data_random["name"], self.test_data_random["price"], self.test_data_random["cat_id"], self.test_data_random["desc"], active=False)
|
||||
|
||||
self.test_data = {
|
||||
"name": "Test Item Name",
|
||||
"price": 999.99,
|
||||
"cat_id": cat.get_cat_list()[0].get_id(),
|
||||
"desc": "Test Item's description here. Blah-blah-blah..."
|
||||
}
|
||||
self.item = itm.create_item(self.test_data["name"], self.test_data["price"], self.test_data["cat_id"], self.test_data["desc"], active=False)
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
self.item.delete()
|
||||
|
||||
def test_name(self):
|
||||
self.assertEqual(self.item_random.get_name(), self.test_data_random["name"])
|
||||
name_random = get_random_string(2, 20)
|
||||
self.item_random.set_name(name_random)
|
||||
self.test_data_random["name"] = name_random
|
||||
self.assertEqual(self.item_random.get_name(), self.test_data_random["name"])
|
||||
|
||||
self.assertEqual(self.item.get_name(), self.test_data["name"])
|
||||
name = "New test Item name"
|
||||
self.item.set_name(name)
|
||||
self.test_data["name"] = name
|
||||
self.assertEqual(self.item.get_name(), self.test_data["name"])
|
||||
|
||||
def test_price(self):
|
||||
self.assertEqual(self.item_random.get_price(), self.test_data_random["price"])
|
||||
price_random = get_random_float(10, 1000)
|
||||
self.item_random.set_price(price_random)
|
||||
self.test_data_random["price"] = price_random
|
||||
self.assertEqual(self.item_random.get_price(), self.test_data_random["price"])
|
||||
|
||||
self.assertEqual(self.item.get_price(), self.test_data["price"])
|
||||
price = 123.12
|
||||
self.item.set_price(price)
|
||||
self.test_data["price"] = price
|
||||
self.assertEqual(self.item.get_price(), self.test_data["price"])
|
||||
|
||||
def test_cat_id(self):
|
||||
self.assertEqual(self.item_random.get_cat_id(), self.test_data_random["cat_id"])
|
||||
cat_id_random = randint(1, 100)
|
||||
self.item_random.set_cat_id(cat_id_random)
|
||||
self.test_data_random["cat_id"] = cat_id_random
|
||||
self.assertEqual(self.item_random.get_cat_id(), self.test_data_random["cat_id"])
|
||||
|
||||
self.assertEqual(self.item.get_cat_id(), self.test_data["cat_id"])
|
||||
cat_id = 2
|
||||
self.item.set_cat_id(cat_id)
|
||||
self.test_data["cat_id"] = cat_id
|
||||
self.assertEqual(self.item.get_cat_id(), self.test_data["cat_id"])
|
||||
|
||||
def test_desc(self):
|
||||
self.assertEqual(self.item_random.get_desc(), self.test_data_random["desc"])
|
||||
desc_random = get_random_string(10, 255)
|
||||
self.item_random.set_desc(desc_random)
|
||||
self.test_data_random["desc"] = desc_random
|
||||
self.assertEqual(self.item_random.get_desc(), self.test_data_random["desc"])
|
||||
|
||||
self.assertEqual(self.item.get_desc(), self.test_data["desc"])
|
||||
desc = "New desc"
|
||||
self.item.set_desc(desc)
|
||||
self.test_data["desc"] = desc
|
||||
self.assertEqual(self.item.get_desc(), self.test_data["desc"])
|
||||
|
||||
def test_active(self):
|
||||
self.assertFalse(self.item.is_active())
|
||||
|
||||
self.item.set_active(1)
|
||||
self.assertTrue(self.item.is_active())
|
||||
self.item.set_active(0)
|
||||
self.assertFalse(self.item.is_active())
|
||||
|
||||
def test_amount(self):
|
||||
self.assertEqual(self.item_random.get_amount(), 0)
|
||||
amount_random = randint(1, 1000)
|
||||
self.item_random.set_amount(amount_random)
|
||||
self.test_data_random["amount"] = amount_random
|
||||
self.assertEqual(self.item_random.get_amount(), self.test_data_random["amount"])
|
||||
|
||||
self.assertEqual(self.item.get_amount(), 0)
|
||||
amount = 90
|
||||
self.item.set_amount(amount)
|
||||
self.test_data["amount"] = amount
|
||||
self.assertEqual(self.item.get_amount(), self.test_data["amount"])
|
||||
|
||||
# TODO: image test cases
|
||||
@unittest.SkipTest
|
||||
def test_get_image_id(self):
|
||||
pass
|
||||
|
||||
@unittest.SkipTest
|
||||
def test_get_image(self):
|
||||
pass
|
||||
|
||||
@unittest.SkipTest
|
||||
def test_set_image_id(self):
|
||||
pass
|
||||
|
||||
def test_hide_image(self):
|
||||
self.assertFalse(self.item.is_hide_image())
|
||||
self.item.set_hide_image(1)
|
||||
self.assertTrue(self.item.is_hide_image())
|
||||
self.item.set_hide_image(0)
|
||||
self.assertFalse(self.item.is_hide_image())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
8
doners/shop-telegram-bot/tests/utils.py
Normal file
8
doners/shop-telegram-bot/tests/utils.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from random import randint, choice
|
||||
from string import ascii_lowercase, digits
|
||||
|
||||
def get_random_string(a, b):
|
||||
return ''.join([choice(ascii_lowercase+digits) for _ in range(randint(a, b))])
|
||||
|
||||
def get_random_float(a, b):
|
||||
return float(f"{randint(a, b)}.{randint(0, 99)}")
|
||||
Reference in New Issue
Block a user