From 199a76d0b5d8fb8c61b49db73cd2b697841d84b6 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 16 Oct 2023 05:10:41 +0300 Subject: [PATCH 01/62] chore: add license --- LICENSE.md | 674 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 675 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/pyproject.toml b/pyproject.toml index 7efba17..99e0dd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,7 @@ name = "ab-global-bot" version = "0.1.0" description = "" +license = "GPL-3.0-or-later" authors = ["Dan Sazonov "] readme = "README.md" From 121f449f7ad34bbf6d62a49a0ef4ffefdd34d175 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 16 Oct 2023 05:30:28 +0300 Subject: [PATCH 02/62] chore: create aiogram template --- bot/main.py | 58 +++- poetry.lock | 728 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 3 files changed, 781 insertions(+), 6 deletions(-) diff --git a/bot/main.py b/bot/main.py index cab47fe..7cb1bab 100644 --- a/bot/main.py +++ b/bot/main.py @@ -1,6 +1,56 @@ -def print_hi(name): - print(f'Hi, {name}') +import asyncio +import logging +import sys +from os import getenv +from aiogram import Bot, Dispatcher, Router, types +from aiogram.enums import ParseMode +from aiogram.filters import CommandStart +from aiogram.types import Message +from aiogram.utils.markdown import hbold -if __name__ == '__main__': - print_hi('PyCharm') +# Bot token can be obtained via https://t.me/BotFather +TOKEN = getenv("BOT_TOKEN") + +# All handlers should be attached to the Router (or Dispatcher) +dp = Dispatcher() + + +@dp.message(CommandStart()) +async def command_start_handler(message: Message) -> None: + """ + This handler receives messages with `/start` command + """ + # Most event objects have aliases for API methods that can be called in events' context + # For example if you want to answer to incoming message you can use `message.answer(...)` alias + # and the target chat will be passed to :ref:`aiogram.methods.send_message.SendMessage` + # method automatically or call API method directly via + # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)` + await message.answer(f"Hello, {hbold(message.from_user.full_name)}!") + + +@dp.message() +async def echo_handler(message: types.Message) -> None: + """ + Handler will forward receive a message back to the sender + + By default, message handler will handle all message types (like a text, photo, sticker etc.) + """ + try: + # Send a copy of the received message + await message.send_copy(chat_id=message.chat.id) + except TypeError: + # But not all the types is supported to be copied so need to handle it + await message.answer("Nice try!") + + +async def main() -> None: + # Initialize Bot instance with a default parse mode which will be passed to all API calls + bot = Bot(TOKEN, parse_mode=ParseMode.HTML) + # And the run events dispatching + await dp.start_polling(bot) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, stream=sys.stdout) + asyncio.run(main()) diff --git a/poetry.lock b/poetry.lock index 24cf95d..b917cd6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,7 +1,731 @@ # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. -package = [] + +[[package]] +name = "aiofiles" +version = "23.1.0" +description = "File support for asyncio." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "aiofiles-23.1.0-py3-none-any.whl", hash = "sha256:9312414ae06472eb6f1d163f555e466a23aed1c8f60c30cccf7121dba2e53eb2"}, + {file = "aiofiles-23.1.0.tar.gz", hash = "sha256:edd247df9a19e0db16534d4baaf536d6609a43e1de5401d7a4c1c148753a1635"}, +] + +[[package]] +name = "aiogram" +version = "3.1.1" +description = "Modern and fully asynchronous framework for Telegram Bot API" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiogram-3.1.1-py3-none-any.whl", hash = "sha256:c381cbf252bca4c8e9095f02283e44d11286c982b923407473fb2457c0dab109"}, + {file = "aiogram-3.1.1.tar.gz", hash = "sha256:4e183d34adf28445d6c6501eb803c7b0b52fd4a09c89bd715b2df9dbd9fa8126"}, +] + +[package.dependencies] +aiofiles = ">=23.1.0,<23.2.0" +aiohttp = ">=3.8.5,<3.9.0" +certifi = ">=2023.7.22" +magic-filter = ">=1.0.11,<1.1.0" +pydantic = ">=2.1.1,<2.4" +typing-extensions = ">=4.7.1,<4.8.0" + +[package.extras] +cli = ["aiogram-cli (>=1.0,<2.0)"] +dev = ["black (>=23.7.0,<23.8.0)", "isort (>=5.11,<6.0)", "mypy (>=1.4.1,<1.5.0)", "packaging (>=23.0,<24.0)", "pre-commit (>=3.3.3,<3.4.0)", "ruff (>=0.0.280,<0.1.0)", "toml (>=0.10.2,<0.11.0)", "towncrier (>=23.6.0,<23.7.0)"] +docs = ["furo (>=2023.7.26,<2023.8.0)", "markdown-include (>=0.8.1,<0.9.0)", "pygments (>=2.15.1,<2.16.0)", "pymdown-extensions (>=10.1,<11.0)", "sphinx (>=7.1.1,<7.2.0)", "sphinx-autobuild (>=2021.3.14,<2021.4.0)", "sphinx-copybutton (>=0.5.2,<0.6.0)", "sphinx-intl (>=2.0.1,<2.1.0)", "sphinx-substitution-extensions (>=2022.2.16,<2022.3.0)", "sphinxcontrib-towncrier (>=0.3.2a0,<0.4.0)", "towncrier (>=23.6.0,<23.7.0)"] +fast = ["uvloop (>=0.17.0)"] +i18n = ["babel (>=2.12.1,<2.13.0)"] +proxy = ["aiohttp-socks (>=0.8.0,<0.9.0)"] +redis = ["redis (>=4.6.0,<4.7.0)"] +test = ["aresponses (>=2.1.6,<2.2.0)", "pycryptodomex (>=3.18,<4.0)", "pytest (>=7.4.0,<7.5.0)", "pytest-aiohttp (>=1.0.4,<1.1.0)", "pytest-asyncio (>=0.21.0,<0.22.0)", "pytest-cov (>=4.0.0,<4.1.0)", "pytest-html (>=3.2.0,<3.3.0)", "pytest-lazy-fixture (>=0.6.3,<0.7.0)", "pytest-mock (>=3.10.0,<3.11.0)", "pytest-mypy (>=0.10.0,<0.11.0)", "pytz (>=2022.7.1,<2022.8.0)"] + +[[package]] +name = "aiohttp" +version = "3.8.6" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:41d55fc043954cddbbd82503d9cc3f4814a40bcef30b3569bc7b5e34130718c1"}, + {file = "aiohttp-3.8.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d84166673694841d8953f0a8d0c90e1087739d24632fe86b1a08819168b4566"}, + {file = "aiohttp-3.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:253bf92b744b3170eb4c4ca2fa58f9c4b87aeb1df42f71d4e78815e6e8b73c9e"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd194939b1f764d6bb05490987bfe104287bbf51b8d862261ccf66f48fb4096"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c5f938d199a6fdbdc10bbb9447496561c3a9a565b43be564648d81e1102ac22"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2817b2f66ca82ee699acd90e05c95e79bbf1dc986abb62b61ec8aaf851e81c93"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fa375b3d34e71ccccf172cab401cd94a72de7a8cc01847a7b3386204093bb47"}, + {file = "aiohttp-3.8.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9de50a199b7710fa2904be5a4a9b51af587ab24c8e540a7243ab737b45844543"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e1d8cb0b56b3587c5c01de3bf2f600f186da7e7b5f7353d1bf26a8ddca57f965"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8e31e9db1bee8b4f407b77fd2507337a0a80665ad7b6c749d08df595d88f1cf5"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7bc88fc494b1f0311d67f29fee6fd636606f4697e8cc793a2d912ac5b19aa38d"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ec00c3305788e04bf6d29d42e504560e159ccaf0be30c09203b468a6c1ccd3b2"}, + {file = "aiohttp-3.8.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad1407db8f2f49329729564f71685557157bfa42b48f4b93e53721a16eb813ed"}, + {file = "aiohttp-3.8.6-cp310-cp310-win32.whl", hash = "sha256:ccc360e87341ad47c777f5723f68adbb52b37ab450c8bc3ca9ca1f3e849e5fe2"}, + {file = "aiohttp-3.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:93c15c8e48e5e7b89d5cb4613479d144fda8344e2d886cf694fd36db4cc86865"}, + {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e2f9cc8e5328f829f6e1fb74a0a3a939b14e67e80832975e01929e320386b34"}, + {file = "aiohttp-3.8.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e6a00ffcc173e765e200ceefb06399ba09c06db97f401f920513a10c803604ca"}, + {file = "aiohttp-3.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:41bdc2ba359032e36c0e9de5a3bd00d6fb7ea558a6ce6b70acedf0da86458321"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14cd52ccf40006c7a6cd34a0f8663734e5363fd981807173faf3a017e202fec9"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d5b785c792802e7b275c420d84f3397668e9d49ab1cb52bd916b3b3ffcf09ad"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1bed815f3dc3d915c5c1e556c397c8667826fbc1b935d95b0ad680787896a358"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96603a562b546632441926cd1293cfcb5b69f0b4159e6077f7c7dbdfb686af4d"}, + {file = "aiohttp-3.8.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d76e8b13161a202d14c9584590c4df4d068c9567c99506497bdd67eaedf36403"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e3f1e3f1a1751bb62b4a1b7f4e435afcdade6c17a4fd9b9d43607cebd242924a"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:76b36b3124f0223903609944a3c8bf28a599b2cc0ce0be60b45211c8e9be97f8"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:a2ece4af1f3c967a4390c284797ab595a9f1bc1130ef8b01828915a05a6ae684"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:16d330b3b9db87c3883e565340d292638a878236418b23cc8b9b11a054aaa887"}, + {file = "aiohttp-3.8.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42c89579f82e49db436b69c938ab3e1559e5a4409eb8639eb4143989bc390f2f"}, + {file = "aiohttp-3.8.6-cp311-cp311-win32.whl", hash = "sha256:efd2fcf7e7b9d7ab16e6b7d54205beded0a9c8566cb30f09c1abe42b4e22bdcb"}, + {file = "aiohttp-3.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:3b2ab182fc28e7a81f6c70bfbd829045d9480063f5ab06f6e601a3eddbbd49a0"}, + {file = "aiohttp-3.8.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fdee8405931b0615220e5ddf8cd7edd8592c606a8e4ca2a00704883c396e4479"}, + {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d25036d161c4fe2225d1abff2bd52c34ed0b1099f02c208cd34d8c05729882f0"}, + {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d791245a894be071d5ab04bbb4850534261a7d4fd363b094a7b9963e8cdbd31"}, + {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0cccd1de239afa866e4ce5c789b3032442f19c261c7d8a01183fd956b1935349"}, + {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f13f60d78224f0dace220d8ab4ef1dbc37115eeeab8c06804fec11bec2bbd07"}, + {file = "aiohttp-3.8.6-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a9b5a0606faca4f6cc0d338359d6fa137104c337f489cd135bb7fbdbccb1e39"}, + {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:13da35c9ceb847732bf5c6c5781dcf4780e14392e5d3b3c689f6d22f8e15ae31"}, + {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:4d4cbe4ffa9d05f46a28252efc5941e0462792930caa370a6efaf491f412bc66"}, + {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:229852e147f44da0241954fc6cb910ba074e597f06789c867cb7fb0621e0ba7a"}, + {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:713103a8bdde61d13490adf47171a1039fd880113981e55401a0f7b42c37d071"}, + {file = "aiohttp-3.8.6-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:45ad816b2c8e3b60b510f30dbd37fe74fd4a772248a52bb021f6fd65dff809b6"}, + {file = "aiohttp-3.8.6-cp36-cp36m-win32.whl", hash = "sha256:2b8d4e166e600dcfbff51919c7a3789ff6ca8b3ecce16e1d9c96d95dd569eb4c"}, + {file = "aiohttp-3.8.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0912ed87fee967940aacc5306d3aa8ba3a459fcd12add0b407081fbefc931e53"}, + {file = "aiohttp-3.8.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e2a988a0c673c2e12084f5e6ba3392d76c75ddb8ebc6c7e9ead68248101cd446"}, + {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebf3fd9f141700b510d4b190094db0ce37ac6361a6806c153c161dc6c041ccda"}, + {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3161ce82ab85acd267c8f4b14aa226047a6bee1e4e6adb74b798bd42c6ae1f80"}, + {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95fc1bf33a9a81469aa760617b5971331cdd74370d1214f0b3109272c0e1e3c"}, + {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c43ecfef7deaf0617cee936836518e7424ee12cb709883f2c9a1adda63cc460"}, + {file = "aiohttp-3.8.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca80e1b90a05a4f476547f904992ae81eda5c2c85c66ee4195bb8f9c5fb47f28"}, + {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:90c72ebb7cb3a08a7f40061079817133f502a160561d0675b0a6adf231382c92"}, + {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bb54c54510e47a8c7c8e63454a6acc817519337b2b78606c4e840871a3e15349"}, + {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:de6a1c9f6803b90e20869e6b99c2c18cef5cc691363954c93cb9adeb26d9f3ae"}, + {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:a3628b6c7b880b181a3ae0a0683698513874df63783fd89de99b7b7539e3e8a8"}, + {file = "aiohttp-3.8.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fc37e9aef10a696a5a4474802930079ccfc14d9f9c10b4662169671ff034b7df"}, + {file = "aiohttp-3.8.6-cp37-cp37m-win32.whl", hash = "sha256:f8ef51e459eb2ad8e7a66c1d6440c808485840ad55ecc3cafefadea47d1b1ba2"}, + {file = "aiohttp-3.8.6-cp37-cp37m-win_amd64.whl", hash = "sha256:b2fe42e523be344124c6c8ef32a011444e869dc5f883c591ed87f84339de5976"}, + {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e2ee0ac5a1f5c7dd3197de309adfb99ac4617ff02b0603fd1e65b07dc772e4b"}, + {file = "aiohttp-3.8.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01770d8c04bd8db568abb636c1fdd4f7140b284b8b3e0b4584f070180c1e5c62"}, + {file = "aiohttp-3.8.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3c68330a59506254b556b99a91857428cab98b2f84061260a67865f7f52899f5"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89341b2c19fb5eac30c341133ae2cc3544d40d9b1892749cdd25892bbc6ac951"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71783b0b6455ac8f34b5ec99d83e686892c50498d5d00b8e56d47f41b38fbe04"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f628dbf3c91e12f4d6c8b3f092069567d8eb17814aebba3d7d60c149391aee3a"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04691bc6601ef47c88f0255043df6f570ada1a9ebef99c34bd0b72866c217ae"}, + {file = "aiohttp-3.8.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee912f7e78287516df155f69da575a0ba33b02dd7c1d6614dbc9463f43066e3"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9c19b26acdd08dd239e0d3669a3dddafd600902e37881f13fbd8a53943079dbc"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:99c5ac4ad492b4a19fc132306cd57075c28446ec2ed970973bbf036bcda1bcc6"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:f0f03211fd14a6a0aed2997d4b1c013d49fb7b50eeb9ffdf5e51f23cfe2c77fa"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:8d399dade330c53b4106160f75f55407e9ae7505263ea86f2ccca6bfcbdb4921"}, + {file = "aiohttp-3.8.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ec4fd86658c6a8964d75426517dc01cbf840bbf32d055ce64a9e63a40fd7b771"}, + {file = "aiohttp-3.8.6-cp38-cp38-win32.whl", hash = "sha256:33164093be11fcef3ce2571a0dccd9041c9a93fa3bde86569d7b03120d276c6f"}, + {file = "aiohttp-3.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:bdf70bfe5a1414ba9afb9d49f0c912dc524cf60141102f3a11143ba3d291870f"}, + {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d52d5dc7c6682b720280f9d9db41d36ebe4791622c842e258c9206232251ab2b"}, + {file = "aiohttp-3.8.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ac39027011414dbd3d87f7edb31680e1f430834c8cef029f11c66dad0670aa5"}, + {file = "aiohttp-3.8.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3f5c7ce535a1d2429a634310e308fb7d718905487257060e5d4598e29dc17f0b"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b30e963f9e0d52c28f284d554a9469af073030030cef8693106d918b2ca92f54"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:918810ef188f84152af6b938254911055a72e0f935b5fbc4c1a4ed0b0584aed1"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:002f23e6ea8d3dd8d149e569fd580c999232b5fbc601c48d55398fbc2e582e8c"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcf3eabd3fd1a5e6092d1242295fa37d0354b2eb2077e6eb670accad78e40e1"}, + {file = "aiohttp-3.8.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:255ba9d6d5ff1a382bb9a578cd563605aa69bec845680e21c44afc2670607a95"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d67f8baed00870aa390ea2590798766256f31dc5ed3ecc737debb6e97e2ede78"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:86f20cee0f0a317c76573b627b954c412ea766d6ada1a9fcf1b805763ae7feeb"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:39a312d0e991690ccc1a61f1e9e42daa519dcc34ad03eb6f826d94c1190190dd"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e827d48cf802de06d9c935088c2924e3c7e7533377d66b6f31ed175c1620e05e"}, + {file = "aiohttp-3.8.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd111d7fc5591ddf377a408ed9067045259ff2770f37e2d94e6478d0f3fc0c17"}, + {file = "aiohttp-3.8.6-cp39-cp39-win32.whl", hash = "sha256:caf486ac1e689dda3502567eb89ffe02876546599bbf915ec94b1fa424eeffd4"}, + {file = "aiohttp-3.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3f0e27e5b733803333bb2371249f41cf42bae8884863e8e8965ec69bebe53132"}, + {file = "aiohttp-3.8.6.tar.gz", hash = "sha256:b0cf2a4501bff9330a8a5248b4ce951851e415bdcce9dc158e76cfd55e15085c"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<4.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "cchardet"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.0.tar.gz", hash = "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-win32.whl", hash = "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d"}, + {file = "charset_normalizer-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-win32.whl", hash = "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786"}, + {file = "charset_normalizer-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-win32.whl", hash = "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df"}, + {file = "charset_normalizer-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-win32.whl", hash = "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c"}, + {file = "charset_normalizer-3.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-win32.whl", hash = "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e"}, + {file = "charset_normalizer-3.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-win32.whl", hash = "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a"}, + {file = "charset_normalizer-3.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884"}, + {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, +] + +[[package]] +name = "frozenlist" +version = "1.4.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:764226ceef3125e53ea2cb275000e309c0aa5464d43bd72abd661e27fffc26ab"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d6484756b12f40003c6128bfcc3fa9f0d49a687e171186c2d85ec82e3758c559"}, + {file = "frozenlist-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9ac08e601308e41eb533f232dbf6b7e4cea762f9f84f6357136eed926c15d12c"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d081f13b095d74b67d550de04df1c756831f3b83dc9881c38985834387487f1b"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71932b597f9895f011f47f17d6428252fc728ba2ae6024e13c3398a087c2cdea"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:981b9ab5a0a3178ff413bca62526bb784249421c24ad7381e39d67981be2c326"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e41f3de4df3e80de75845d3e743b3f1c4c8613c3997a912dbf0229fc61a8b963"}, + {file = "frozenlist-1.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6918d49b1f90821e93069682c06ffde41829c346c66b721e65a5c62b4bab0300"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e5c8764c7829343d919cc2dfc587a8db01c4f70a4ebbc49abde5d4b158b007b"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8d0edd6b1c7fb94922bf569c9b092ee187a83f03fb1a63076e7774b60f9481a8"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e29cda763f752553fa14c68fb2195150bfab22b352572cb36c43c47bedba70eb"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:0c7c1b47859ee2cac3846fde1c1dc0f15da6cec5a0e5c72d101e0f83dcb67ff9"}, + {file = "frozenlist-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:901289d524fdd571be1c7be054f48b1f88ce8dddcbdf1ec698b27d4b8b9e5d62"}, + {file = "frozenlist-1.4.0-cp310-cp310-win32.whl", hash = "sha256:1a0848b52815006ea6596c395f87449f693dc419061cc21e970f139d466dc0a0"}, + {file = "frozenlist-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b206646d176a007466358aa21d85cd8600a415c67c9bd15403336c331a10d956"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:de343e75f40e972bae1ef6090267f8260c1446a1695e77096db6cfa25e759a95"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad2a9eb6d9839ae241701d0918f54c51365a51407fd80f6b8289e2dfca977cc3"}, + {file = "frozenlist-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bd7bd3b3830247580de99c99ea2a01416dfc3c34471ca1298bccabf86d0ff4dc"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdf1847068c362f16b353163391210269e4f0569a3c166bc6a9f74ccbfc7e839"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38461d02d66de17455072c9ba981d35f1d2a73024bee7790ac2f9e361ef1cd0c"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5a32087d720c608f42caed0ef36d2b3ea61a9d09ee59a5142d6070da9041b8f"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd65632acaf0d47608190a71bfe46b209719bf2beb59507db08ccdbe712f969b"}, + {file = "frozenlist-1.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261b9f5d17cac914531331ff1b1d452125bf5daa05faf73b71d935485b0c510b"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b89ac9768b82205936771f8d2eb3ce88503b1556324c9f903e7156669f521472"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:008eb8b31b3ea6896da16c38c1b136cb9fec9e249e77f6211d479db79a4eaf01"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e74b0506fa5aa5598ac6a975a12aa8928cbb58e1f5ac8360792ef15de1aa848f"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:490132667476f6781b4c9458298b0c1cddf237488abd228b0b3650e5ecba7467"}, + {file = "frozenlist-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:76d4711f6f6d08551a7e9ef28c722f4a50dd0fc204c56b4bcd95c6cc05ce6fbb"}, + {file = "frozenlist-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a02eb8ab2b8f200179b5f62b59757685ae9987996ae549ccf30f983f40602431"}, + {file = "frozenlist-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:515e1abc578dd3b275d6a5114030b1330ba044ffba03f94091842852f806f1c1"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f0ed05f5079c708fe74bf9027e95125334b6978bf07fd5ab923e9e55e5fbb9d3"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca265542ca427bf97aed183c1676e2a9c66942e822b14dc6e5f42e038f92a503"}, + {file = "frozenlist-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:491e014f5c43656da08958808588cc6c016847b4360e327a62cb308c791bd2d9"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ae5cd0f333f94f2e03aaf140bb762c64783935cc764ff9c82dff626089bebf"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e78fb68cf9c1a6aa4a9a12e960a5c9dfbdb89b3695197aa7064705662515de2"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5655a942f5f5d2c9ed93d72148226d75369b4f6952680211972a33e59b1dfdc"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11b0746f5d946fecf750428a95f3e9ebe792c1ee3b1e96eeba145dc631a9672"}, + {file = "frozenlist-1.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e66d2a64d44d50d2543405fb183a21f76b3b5fd16f130f5c99187c3fb4e64919"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:88f7bc0fcca81f985f78dd0fa68d2c75abf8272b1f5c323ea4a01a4d7a614efc"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5833593c25ac59ede40ed4de6d67eb42928cca97f26feea219f21d0ed0959b79"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:fec520865f42e5c7f050c2a79038897b1c7d1595e907a9e08e3353293ffc948e"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:b826d97e4276750beca7c8f0f1a4938892697a6bcd8ec8217b3312dad6982781"}, + {file = "frozenlist-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ceb6ec0a10c65540421e20ebd29083c50e6d1143278746a4ef6bcf6153171eb8"}, + {file = "frozenlist-1.4.0-cp38-cp38-win32.whl", hash = "sha256:2b8bcf994563466db019fab287ff390fffbfdb4f905fc77bc1c1d604b1c689cc"}, + {file = "frozenlist-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a6c8097e01886188e5be3e6b14e94ab365f384736aa1fca6a0b9e35bd4a30bc7"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6c38721585f285203e4b4132a352eb3daa19121a035f3182e08e437cface44bf"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a0c6da9aee33ff0b1a451e867da0c1f47408112b3391dd43133838339e410963"}, + {file = "frozenlist-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:93ea75c050c5bb3d98016b4ba2497851eadf0ac154d88a67d7a6816206f6fa7f"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f61e2dc5ad442c52b4887f1fdc112f97caeff4d9e6ebe78879364ac59f1663e1"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa384489fefeb62321b238e64c07ef48398fe80f9e1e6afeff22e140e0850eef"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10ff5faaa22786315ef57097a279b833ecab1a0bfb07d604c9cbb1c4cdc2ed87"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:007df07a6e3eb3e33e9a1fe6a9db7af152bbd8a185f9aaa6ece10a3529e3e1c6"}, + {file = "frozenlist-1.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4f399d28478d1f604c2ff9119907af9726aed73680e5ed1ca634d377abb087"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5374b80521d3d3f2ec5572e05adc94601985cc526fb276d0c8574a6d749f1b3"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ce31ae3e19f3c902de379cf1323d90c649425b86de7bbdf82871b8a2a0615f3d"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7211ef110a9194b6042449431e08c4d80c0481e5891e58d429df5899690511c2"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:556de4430ce324c836789fa4560ca62d1591d2538b8ceb0b4f68fb7b2384a27a"}, + {file = "frozenlist-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7645a8e814a3ee34a89c4a372011dcd817964ce8cb273c8ed6119d706e9613e3"}, + {file = "frozenlist-1.4.0-cp39-cp39-win32.whl", hash = "sha256:19488c57c12d4e8095a922f328df3f179c820c212940a498623ed39160bc3c2f"}, + {file = "frozenlist-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6221d84d463fb110bdd7619b69cb43878a11d51cbb9394ae3105d082d5199167"}, + {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, +] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "magic-filter" +version = "1.0.12" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "magic_filter-1.0.12-py3-none-any.whl", hash = "sha256:e5929e544f310c2b1f154318db8c5cdf544dd658efa998172acd2e4ba0f6c6a6"}, + {file = "magic_filter-1.0.12.tar.gz", hash = "sha256:4751d0b579a5045d1dc250625c4c508c18c3def5ea6afaf3957cb4530d03f7f9"}, +] + +[package.extras] +dev = ["black (>=22.8.0,<22.9.0)", "flake8 (>=5.0.4,<5.1.0)", "isort (>=5.11.5,<5.12.0)", "mypy (>=1.4.1,<1.5.0)", "pre-commit (>=2.20.0,<2.21.0)", "pytest (>=7.1.3,<7.2.0)", "pytest-cov (>=3.0.0,<3.1.0)", "pytest-html (>=3.1.1,<3.2.0)", "types-setuptools (>=65.3.0,<65.4.0)"] + +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "pydantic" +version = "2.3.0" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.3.0-py3-none-any.whl", hash = "sha256:45b5e446c6dfaad9444819a293b921a40e1db1aa61ea08aede0522529ce90e81"}, + {file = "pydantic-2.3.0.tar.gz", hash = "sha256:1607cc106602284cd4a00882986570472f193fde9cb1259bceeaedb26aa79a6d"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.6.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.6.3" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.6.3-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1a0ddaa723c48af27d19f27f1c73bdc615c73686d763388c8683fe34ae777bad"}, + {file = "pydantic_core-2.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5cfde4fab34dd1e3a3f7f3db38182ab6c95e4ea91cf322242ee0be5c2f7e3d2f"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5493a7027bfc6b108e17c3383959485087d5942e87eb62bbac69829eae9bc1f7"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:84e87c16f582f5c753b7f39a71bd6647255512191be2d2dbf49458c4ef024588"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:522a9c4a4d1924facce7270c84b5134c5cabcb01513213662a2e89cf28c1d309"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaafc776e5edc72b3cad1ccedb5fd869cc5c9a591f1213aa9eba31a781be9ac1"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a750a83b2728299ca12e003d73d1264ad0440f60f4fc9cee54acc489249b728"}, + {file = "pydantic_core-2.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e8b374ef41ad5c461efb7a140ce4730661aadf85958b5c6a3e9cf4e040ff4bb"}, + {file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b594b64e8568cf09ee5c9501ede37066b9fc41d83d58f55b9952e32141256acd"}, + {file = "pydantic_core-2.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2a20c533cb80466c1d42a43a4521669ccad7cf2967830ac62c2c2f9cece63e7e"}, + {file = "pydantic_core-2.6.3-cp310-none-win32.whl", hash = "sha256:04fe5c0a43dec39aedba0ec9579001061d4653a9b53a1366b113aca4a3c05ca7"}, + {file = "pydantic_core-2.6.3-cp310-none-win_amd64.whl", hash = "sha256:6bf7d610ac8f0065a286002a23bcce241ea8248c71988bda538edcc90e0c39ad"}, + {file = "pydantic_core-2.6.3-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:6bcc1ad776fffe25ea5c187a028991c031a00ff92d012ca1cc4714087e575973"}, + {file = "pydantic_core-2.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df14f6332834444b4a37685810216cc8fe1fe91f447332cd56294c984ecbff1c"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0b7486d85293f7f0bbc39b34e1d8aa26210b450bbd3d245ec3d732864009819"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a892b5b1871b301ce20d40b037ffbe33d1407a39639c2b05356acfef5536d26a"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:883daa467865e5766931e07eb20f3e8152324f0adf52658f4d302242c12e2c32"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4eb77df2964b64ba190eee00b2312a1fd7a862af8918ec70fc2d6308f76ac64"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce8c84051fa292a5dc54018a40e2a1926fd17980a9422c973e3ebea017aa8da"}, + {file = "pydantic_core-2.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22134a4453bd59b7d1e895c455fe277af9d9d9fbbcb9dc3f4a97b8693e7e2c9b"}, + {file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:02e1c385095efbd997311d85c6021d32369675c09bcbfff3b69d84e59dc103f6"}, + {file = "pydantic_core-2.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d79f1f2f7ebdb9b741296b69049ff44aedd95976bfee38eb4848820628a99b50"}, + {file = "pydantic_core-2.6.3-cp311-none-win32.whl", hash = "sha256:430ddd965ffd068dd70ef4e4d74f2c489c3a313adc28e829dd7262cc0d2dd1e8"}, + {file = "pydantic_core-2.6.3-cp311-none-win_amd64.whl", hash = "sha256:84f8bb34fe76c68c9d96b77c60cef093f5e660ef8e43a6cbfcd991017d375950"}, + {file = "pydantic_core-2.6.3-cp311-none-win_arm64.whl", hash = "sha256:5a2a3c9ef904dcdadb550eedf3291ec3f229431b0084666e2c2aa8ff99a103a2"}, + {file = "pydantic_core-2.6.3-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:8421cf496e746cf8d6b677502ed9a0d1e4e956586cd8b221e1312e0841c002d5"}, + {file = "pydantic_core-2.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bb128c30cf1df0ab78166ded1ecf876620fb9aac84d2413e8ea1594b588c735d"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37a822f630712817b6ecc09ccc378192ef5ff12e2c9bae97eb5968a6cdf3b862"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:240a015102a0c0cc8114f1cba6444499a8a4d0333e178bc504a5c2196defd456"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f90e5e3afb11268628c89f378f7a1ea3f2fe502a28af4192e30a6cdea1e7d5e"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:340e96c08de1069f3d022a85c2a8c63529fd88709468373b418f4cf2c949fb0e"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1480fa4682e8202b560dcdc9eeec1005f62a15742b813c88cdc01d44e85308e5"}, + {file = "pydantic_core-2.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f14546403c2a1d11a130b537dda28f07eb6c1805a43dae4617448074fd49c282"}, + {file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a87c54e72aa2ef30189dc74427421e074ab4561cf2bf314589f6af5b37f45e6d"}, + {file = "pydantic_core-2.6.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f93255b3e4d64785554e544c1c76cd32f4a354fa79e2eeca5d16ac2e7fdd57aa"}, + {file = "pydantic_core-2.6.3-cp312-none-win32.whl", hash = "sha256:f70dc00a91311a1aea124e5f64569ea44c011b58433981313202c46bccbec0e1"}, + {file = "pydantic_core-2.6.3-cp312-none-win_amd64.whl", hash = "sha256:23470a23614c701b37252618e7851e595060a96a23016f9a084f3f92f5ed5881"}, + {file = "pydantic_core-2.6.3-cp312-none-win_arm64.whl", hash = "sha256:1ac1750df1b4339b543531ce793b8fd5c16660a95d13aecaab26b44ce11775e9"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:a53e3195f134bde03620d87a7e2b2f2046e0e5a8195e66d0f244d6d5b2f6d31b"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:f2969e8f72c6236c51f91fbb79c33821d12a811e2a94b7aa59c65f8dbdfad34a"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:672174480a85386dd2e681cadd7d951471ad0bb028ed744c895f11f9d51b9ebe"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:002d0ea50e17ed982c2d65b480bd975fc41086a5a2f9c924ef8fc54419d1dea3"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ccc13afee44b9006a73d2046068d4df96dc5b333bf3509d9a06d1b42db6d8bf"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:439a0de139556745ae53f9cc9668c6c2053444af940d3ef3ecad95b079bc9987"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63b7545d489422d417a0cae6f9898618669608750fc5e62156957e609e728a5"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b44c42edc07a50a081672e25dfe6022554b47f91e793066a7b601ca290f71e42"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1c721bfc575d57305dd922e6a40a8fe3f762905851d694245807a351ad255c58"}, + {file = "pydantic_core-2.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5e4a2cf8c4543f37f5dc881de6c190de08096c53986381daebb56a355be5dfe6"}, + {file = "pydantic_core-2.6.3-cp37-none-win32.whl", hash = "sha256:d9b4916b21931b08096efed090327f8fe78e09ae8f5ad44e07f5c72a7eedb51b"}, + {file = "pydantic_core-2.6.3-cp37-none-win_amd64.whl", hash = "sha256:a8acc9dedd304da161eb071cc7ff1326aa5b66aadec9622b2574ad3ffe225525"}, + {file = "pydantic_core-2.6.3-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:5e9c068f36b9f396399d43bfb6defd4cc99c36215f6ff33ac8b9c14ba15bdf6b"}, + {file = "pydantic_core-2.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e61eae9b31799c32c5f9b7be906be3380e699e74b2db26c227c50a5fc7988698"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85463560c67fc65cd86153a4975d0b720b6d7725cf7ee0b2d291288433fc21b"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9616567800bdc83ce136e5847d41008a1d602213d024207b0ff6cab6753fe645"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e9b65a55bbabda7fccd3500192a79f6e474d8d36e78d1685496aad5f9dbd92c"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f468d520f47807d1eb5d27648393519655eadc578d5dd862d06873cce04c4d1b"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9680dd23055dd874173a3a63a44e7f5a13885a4cfd7e84814be71be24fba83db"}, + {file = "pydantic_core-2.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a718d56c4d55efcfc63f680f207c9f19c8376e5a8a67773535e6f7e80e93170"}, + {file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8ecbac050856eb6c3046dea655b39216597e373aa8e50e134c0e202f9c47efec"}, + {file = "pydantic_core-2.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:788be9844a6e5c4612b74512a76b2153f1877cd845410d756841f6c3420230eb"}, + {file = "pydantic_core-2.6.3-cp38-none-win32.whl", hash = "sha256:07a1aec07333bf5adebd8264047d3dc518563d92aca6f2f5b36f505132399efc"}, + {file = "pydantic_core-2.6.3-cp38-none-win_amd64.whl", hash = "sha256:621afe25cc2b3c4ba05fff53525156d5100eb35c6e5a7cf31d66cc9e1963e378"}, + {file = "pydantic_core-2.6.3-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:813aab5bfb19c98ae370952b6f7190f1e28e565909bfc219a0909db168783465"}, + {file = "pydantic_core-2.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:50555ba3cb58f9861b7a48c493636b996a617db1a72c18da4d7f16d7b1b9952b"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19e20f8baedd7d987bd3f8005c146e6bcbda7cdeefc36fad50c66adb2dd2da48"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b0a5d7edb76c1c57b95df719af703e796fc8e796447a1da939f97bfa8a918d60"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f06e21ad0b504658a3a9edd3d8530e8cea5723f6ea5d280e8db8efc625b47e49"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea053cefa008fda40f92aab937fb9f183cf8752e41dbc7bc68917884454c6362"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:171a4718860790f66d6c2eda1d95dd1edf64f864d2e9f9115840840cf5b5713f"}, + {file = "pydantic_core-2.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ed7ceca6aba5331ece96c0e328cd52f0dcf942b8895a1ed2642de50800b79d3"}, + {file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:acafc4368b289a9f291e204d2c4c75908557d4f36bd3ae937914d4529bf62a76"}, + {file = "pydantic_core-2.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1aa712ba150d5105814e53cb141412217146fedc22621e9acff9236d77d2a5ef"}, + {file = "pydantic_core-2.6.3-cp39-none-win32.whl", hash = "sha256:44b4f937b992394a2e81a5c5ce716f3dcc1237281e81b80c748b2da6dd5cf29a"}, + {file = "pydantic_core-2.6.3-cp39-none-win_amd64.whl", hash = "sha256:9b33bf9658cb29ac1a517c11e865112316d09687d767d7a0e4a63d5c640d1b17"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d7050899026e708fb185e174c63ebc2c4ee7a0c17b0a96ebc50e1f76a231c057"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99faba727727b2e59129c59542284efebbddade4f0ae6a29c8b8d3e1f437beb7"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fa159b902d22b283b680ef52b532b29554ea2a7fc39bf354064751369e9dbd7"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:046af9cfb5384f3684eeb3f58a48698ddab8dd870b4b3f67f825353a14441418"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:930bfe73e665ebce3f0da2c6d64455098aaa67e1a00323c74dc752627879fc67"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:85cc4d105747d2aa3c5cf3e37dac50141bff779545ba59a095f4a96b0a460e70"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b25afe9d5c4f60dcbbe2b277a79be114e2e65a16598db8abee2a2dcde24f162b"}, + {file = "pydantic_core-2.6.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e49ce7dc9f925e1fb010fc3d555250139df61fa6e5a0a95ce356329602c11ea9"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:2dd50d6a1aef0426a1d0199190c6c43ec89812b1f409e7fe44cb0fbf6dfa733c"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6595b0d8c8711e8e1dc389d52648b923b809f68ac1c6f0baa525c6440aa0daa"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ef724a059396751aef71e847178d66ad7fc3fc969a1a40c29f5aac1aa5f8784"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3c8945a105f1589ce8a693753b908815e0748f6279959a4530f6742e1994dcb6"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c8c6660089a25d45333cb9db56bb9e347241a6d7509838dbbd1931d0e19dbc7f"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:692b4ff5c4e828a38716cfa92667661a39886e71136c97b7dac26edef18767f7"}, + {file = "pydantic_core-2.6.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:f1a5d8f18877474c80b7711d870db0eeef9442691fcdb00adabfc97e183ee0b0"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:3796a6152c545339d3b1652183e786df648ecdf7c4f9347e1d30e6750907f5bb"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b962700962f6e7a6bd77e5f37320cabac24b4c0f76afeac05e9f93cf0c620014"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56ea80269077003eaa59723bac1d8bacd2cd15ae30456f2890811efc1e3d4413"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c0ebbebae71ed1e385f7dfd9b74c1cff09fed24a6df43d326dd7f12339ec34"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:252851b38bad3bfda47b104ffd077d4f9604a10cb06fe09d020016a25107bf98"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6656a0ae383d8cd7cc94e91de4e526407b3726049ce8d7939049cbfa426518c8"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9140ded382a5b04a1c030b593ed9bf3088243a0a8b7fa9f071a5736498c5483"}, + {file = "pydantic_core-2.6.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d38bbcef58220f9c81e42c255ef0bf99735d8f11edef69ab0b499da77105158a"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c9d469204abcca28926cbc28ce98f28e50e488767b084fb3fbdf21af11d3de26"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48c1ed8b02ffea4d5c9c220eda27af02b8149fe58526359b3c07eb391cb353a2"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b2b1bfed698fa410ab81982f681f5b1996d3d994ae8073286515ac4d165c2e7"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf9d42a71a4d7a7c1f14f629e5c30eac451a6fc81827d2beefd57d014c006c4a"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4292ca56751aebbe63a84bbfc3b5717abb09b14d4b4442cc43fd7c49a1529efd"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7dc2ce039c7290b4ef64334ec7e6ca6494de6eecc81e21cb4f73b9b39991408c"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:615a31b1629e12445c0e9fc8339b41aaa6cc60bd53bf802d5fe3d2c0cda2ae8d"}, + {file = "pydantic_core-2.6.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1fa1f6312fb84e8c281f32b39affe81984ccd484da6e9d65b3d18c202c666149"}, + {file = "pydantic_core-2.6.3.tar.gz", hash = "sha256:1508f37ba9e3ddc0189e6ff4e2228bd2d3c3a4641cbe8c07177162f76ed696c7"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "yarl" +version = "1.9.2" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, + {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, + {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, + {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, + {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, + {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, + {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, + {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, + {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, + {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, + {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, + {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, + {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "81b2fa642d7f2d1219cf80112ace12d689d053d81be7f7addb98144d56fc0fb2" +content-hash = "3fa7f792e592b90c822b90b41e4438e3d77a1b27d19681df8f8f1ecf5fcc8233" diff --git a/pyproject.toml b/pyproject.toml index 99e0dd5..10702cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" +aiogram = "^3.1.1" [build-system] From 60d8c0dac90e2164d50ed7dec0676cd97c668f41 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 06:35:00 +0300 Subject: [PATCH 03/62] chore: remove comments --- bot/main.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/bot/main.py b/bot/main.py index 7cb1bab..c63c3de 100644 --- a/bot/main.py +++ b/bot/main.py @@ -9,10 +9,8 @@ from aiogram.types import Message from aiogram.utils.markdown import hbold -# Bot token can be obtained via https://t.me/BotFather -TOKEN = getenv("BOT_TOKEN") -# All handlers should be attached to the Router (or Dispatcher) +TOKEN = getenv("BOT_TOKEN") dp = Dispatcher() @@ -21,11 +19,6 @@ async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command """ - # Most event objects have aliases for API methods that can be called in events' context - # For example if you want to answer to incoming message you can use `message.answer(...)` alias - # and the target chat will be passed to :ref:`aiogram.methods.send_message.SendMessage` - # method automatically or call API method directly via - # Bot instance: `bot.send_message(chat_id=message.chat.id, ...)` await message.answer(f"Hello, {hbold(message.from_user.full_name)}!") @@ -37,17 +30,13 @@ async def echo_handler(message: types.Message) -> None: By default, message handler will handle all message types (like a text, photo, sticker etc.) """ try: - # Send a copy of the received message await message.send_copy(chat_id=message.chat.id) except TypeError: - # But not all the types is supported to be copied so need to handle it await message.answer("Nice try!") async def main() -> None: - # Initialize Bot instance with a default parse mode which will be passed to all API calls bot = Bot(TOKEN, parse_mode=ParseMode.HTML) - # And the run events dispatching await dp.start_polling(bot) From 5ea5da7eeb1faf8a84c10bf178ceaf8c698667ca Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 06:44:01 +0300 Subject: [PATCH 04/62] feat: crate config file and add env file --- .env.example | 2 ++ bot/config.py | 7 +++++++ bot/main.py | 5 ++--- poetry.lock | 16 +++++++++++++++- pyproject.toml | 1 + 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 .env.example create mode 100644 bot/config.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..69858fe --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +BOT_TOKEN = ... +ADMIN_ID = ... diff --git a/bot/config.py b/bot/config.py new file mode 100644 index 0000000..a502692 --- /dev/null +++ b/bot/config.py @@ -0,0 +1,7 @@ +import os +from dotenv import load_dotenv + +load_dotenv() + +BOT_TOKEN = os.getenv("BOT_TOKEN") +ADMIN_ID = os.getenv("ADMIN_ID") diff --git a/bot/main.py b/bot/main.py index c63c3de..010f308 100644 --- a/bot/main.py +++ b/bot/main.py @@ -1,7 +1,6 @@ import asyncio import logging import sys -from os import getenv from aiogram import Bot, Dispatcher, Router, types from aiogram.enums import ParseMode @@ -9,8 +8,8 @@ from aiogram.types import Message from aiogram.utils.markdown import hbold +import config -TOKEN = getenv("BOT_TOKEN") dp = Dispatcher() @@ -36,7 +35,7 @@ async def echo_handler(message: types.Message) -> None: async def main() -> None: - bot = Bot(TOKEN, parse_mode=ParseMode.HTML) + bot = Bot(config.BOT_TOKEN, parse_mode=ParseMode.HTML) await dp.start_polling(bot) diff --git a/poetry.lock b/poetry.lock index b917cd6..b127b18 100644 --- a/poetry.lock +++ b/poetry.lock @@ -627,6 +627,20 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "python-dotenv" +version = "1.0.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + [[package]] name = "typing-extensions" version = "4.7.1" @@ -728,4 +742,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "3fa7f792e592b90c822b90b41e4438e3d77a1b27d19681df8f8f1ecf5fcc8233" +content-hash = "28030d85726dcb2844a70e043a4f80ca8278f2fdb87cb5295154b85318868508" diff --git a/pyproject.toml b/pyproject.toml index 10702cd..975a3a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.11" aiogram = "^3.1.1" +python-dotenv = "^1.0.0" [build-system] From 4a57bc08e4f84f7a91614c4323d5f7c346400e14 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 06:49:56 +0300 Subject: [PATCH 05/62] refactor: add dataclass for env vars --- bot/config.py | 18 +++++++++++++++--- bot/main.py | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bot/config.py b/bot/config.py index a502692..5e445cd 100644 --- a/bot/config.py +++ b/bot/config.py @@ -1,7 +1,19 @@ import os +from dataclasses import dataclass + from dotenv import load_dotenv -load_dotenv() -BOT_TOKEN = os.getenv("BOT_TOKEN") -ADMIN_ID = os.getenv("ADMIN_ID") +@dataclass +class Bot: + bot_token: str + admin_id: int + + +def _get_settings(): + load_dotenv() + return Bot(bot_token=os.getenv("BOT_TOKEN"), + admin_id=int(os.getenv("ADMIN_ID"))) + + +settings = _get_settings() diff --git a/bot/main.py b/bot/main.py index 010f308..6285438 100644 --- a/bot/main.py +++ b/bot/main.py @@ -35,7 +35,7 @@ async def echo_handler(message: types.Message) -> None: async def main() -> None: - bot = Bot(config.BOT_TOKEN, parse_mode=ParseMode.HTML) + bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) await dp.start_polling(bot) From 8a0acfc2aa53b726e3bc515af00811df0ccaef7b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 07:01:25 +0300 Subject: [PATCH 06/62] refactor: move the message text to a separate file --- bot/main.py | 17 ++--------------- bot/messages.py | 4 ++++ 2 files changed, 6 insertions(+), 15 deletions(-) create mode 100644 bot/messages.py diff --git a/bot/main.py b/bot/main.py index 6285438..459cad1 100644 --- a/bot/main.py +++ b/bot/main.py @@ -6,9 +6,9 @@ from aiogram.enums import ParseMode from aiogram.filters import CommandStart from aiogram.types import Message -from aiogram.utils.markdown import hbold import config +import messages dp = Dispatcher() @@ -18,20 +18,7 @@ async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command """ - await message.answer(f"Hello, {hbold(message.from_user.full_name)}!") - - -@dp.message() -async def echo_handler(message: types.Message) -> None: - """ - Handler will forward receive a message back to the sender - - By default, message handler will handle all message types (like a text, photo, sticker etc.) - """ - try: - await message.send_copy(chat_id=message.chat.id) - except TypeError: - await message.answer("Nice try!") + await message.answer(messages.START) async def main() -> None: diff --git a/bot/messages.py b/bot/messages.py new file mode 100644 index 0000000..78ecf70 --- /dev/null +++ b/bot/messages.py @@ -0,0 +1,4 @@ +from aiogram.utils.markdown import hbold + +START = f"{hbold('Hello!')}" + From 6053e6dd9211a8705d402acbccb1f0057d82105e Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 07:12:24 +0300 Subject: [PATCH 07/62] feat: help command template --- bot/main.py | 9 +++++++-- bot/messages.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bot/main.py b/bot/main.py index 459cad1..b45b12d 100644 --- a/bot/main.py +++ b/bot/main.py @@ -4,7 +4,7 @@ from aiogram import Bot, Dispatcher, Router, types from aiogram.enums import ParseMode -from aiogram.filters import CommandStart +from aiogram.filters import Command from aiogram.types import Message import config @@ -13,7 +13,7 @@ dp = Dispatcher() -@dp.message(CommandStart()) +@dp.message(Command("start")) async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command @@ -21,6 +21,11 @@ async def command_start_handler(message: Message) -> None: await message.answer(messages.START) +@dp.message(Command("help")) +async def command_help_handler(message: Message) -> None: + await message.answer(messages.HELP) + + async def main() -> None: bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) await dp.start_polling(bot) diff --git a/bot/messages.py b/bot/messages.py index 78ecf70..4dc79f8 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -1,4 +1,4 @@ from aiogram.utils.markdown import hbold START = f"{hbold('Hello!')}" - +HELP = "Sample of /help message" From 7fd13acb9bd9edc44a1114524999201a59ecf0c1 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 07:13:19 +0300 Subject: [PATCH 08/62] feat: stop command template --- bot/main.py | 5 +++++ bot/messages.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/bot/main.py b/bot/main.py index b45b12d..f8bd115 100644 --- a/bot/main.py +++ b/bot/main.py @@ -26,6 +26,11 @@ async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) +@dp.message(Command("stop")) +async def command_stop_handler(message: Message) -> None: + await message.answer(messages.STOP) + + async def main() -> None: bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) await dp.start_polling(bot) diff --git a/bot/messages.py b/bot/messages.py index 4dc79f8..69e841b 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -1,4 +1,7 @@ from aiogram.utils.markdown import hbold START = f"{hbold('Hello!')}" + HELP = "Sample of /help message" + +STOP = "Sample of /stop message" From ea7b4298d8e2645bb3a7cb24c9e5c8ff29d4a78e Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 07:17:18 +0300 Subject: [PATCH 09/62] feat: unexpected message answer template --- bot/main.py | 5 +++++ bot/messages.py | 2 ++ 2 files changed, 7 insertions(+) diff --git a/bot/main.py b/bot/main.py index f8bd115..9dc23ff 100644 --- a/bot/main.py +++ b/bot/main.py @@ -31,6 +31,11 @@ async def command_stop_handler(message: Message) -> None: await message.answer(messages.STOP) +@dp.message() +async def unknown_command_handler(message: Message) -> None: + await message.answer(messages.UNKNOWN) + + async def main() -> None: bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) await dp.start_polling(bot) diff --git a/bot/messages.py b/bot/messages.py index 69e841b..4786d36 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -5,3 +5,5 @@ HELP = "Sample of /help message" STOP = "Sample of /stop message" + +UNKNOWN = "Unknown command, type /help" From 65f614ba91766273be0fb9f88906d9b35c60e324 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Wed, 18 Oct 2023 07:42:08 +0300 Subject: [PATCH 10/62] feat: start/stop functions --- bot/main.py | 14 +++++++++++++- bot/messages.py | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index 9dc23ff..023235e 100644 --- a/bot/main.py +++ b/bot/main.py @@ -11,6 +11,19 @@ import messages dp = Dispatcher() +bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) + + +@dp.startup() +async def on_startup(): + # await config.set_commands(dp) + await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_START) + + +@dp.shutdown() +async def on_shutdown(): + await bot.close() + await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_STOP) @dp.message(Command("start")) @@ -37,7 +50,6 @@ async def unknown_command_handler(message: Message) -> None: async def main() -> None: - bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) await dp.start_polling(bot) diff --git a/bot/messages.py b/bot/messages.py index 4786d36..ea8d5e2 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -7,3 +7,7 @@ STOP = "Sample of /stop message" UNKNOWN = "Unknown command, type /help" + +ON_START = "Бот запущен" + +ON_STOP = "Бот остановлен" From 4d86d5ae8a9762b3df51e9c046990eb73583c9d7 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 12:58:23 +0300 Subject: [PATCH 11/62] chore: install peewee --- poetry.lock | 12 +++++++++++- pyproject.toml | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index b127b18..3b08a4c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -490,6 +490,16 @@ files = [ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] +[[package]] +name = "peewee" +version = "3.17.0" +description = "a little orm" +optional = false +python-versions = "*" +files = [ + {file = "peewee-3.17.0.tar.gz", hash = "sha256:3a56967f28a43ca7a4287f4803752aeeb1a57a08dee2e839b99868181dfb5df8"}, +] + [[package]] name = "pydantic" version = "2.3.0" @@ -742,4 +752,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "28030d85726dcb2844a70e043a4f80ca8278f2fdb87cb5295154b85318868508" +content-hash = "66f7659db9c20f24230b90bf5c9d1ab3f236ca48f9f67c40e109785db2a449fc" diff --git a/pyproject.toml b/pyproject.toml index 975a3a8..afa363c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ readme = "README.md" python = "^3.11" aiogram = "^3.1.1" python-dotenv = "^1.0.0" +peewee = "^3.17.0" [build-system] From 05620e43585e6c3e3b0c48d8a7f2ef765342909b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 13:47:27 +0300 Subject: [PATCH 12/62] chore: add db files to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 2d0653a..7b574d9 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,5 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ +# database source files +bot/*.db \ No newline at end of file From 3703f2e1698f2d643c9a3339bab2a5e05c3d30c1 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 13:47:48 +0300 Subject: [PATCH 13/62] feat: create data models --- bot/db.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 bot/db.py diff --git a/bot/db.py b/bot/db.py new file mode 100644 index 0000000..2c8bb0d --- /dev/null +++ b/bot/db.py @@ -0,0 +1,39 @@ +import peewee as pw + +db = pw.SqliteDatabase('data.db') + + +class BaseModel(pw.Model): + class Meta: + database = db + + +class User(BaseModel): + id = pw.PrimaryKeyField(unique=True) + tg_id = pw.BigIntegerField(unique=True) + date_reg = pw.DateTimeField() + date_act = pw.DateTimeField() + resp_num = pw.IntegerField() + + class Meta: + db_table = 'users' + order_by = 'resp_num' + + +class Word(BaseModel): + id = pw.PrimaryKeyField(unique=True) + word = pw.CharField() + show_num = pw.IntegerField() + vote_num = pw.IntegerField() + + class Meta: + db_table = 'words' + + +def _create_db(): + # only for tests + with db: + db.create_tables([User, Word]) + + +_create_db() From 74483b868b61adde6218eb52ca4b21f81b9f323f Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 13:53:59 +0300 Subject: [PATCH 14/62] refactor: models in another module --- bot/db.py | 33 ++------------------------------- bot/models.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 31 deletions(-) create mode 100644 bot/models.py diff --git a/bot/db.py b/bot/db.py index 2c8bb0d..65bfb65 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,39 +1,10 @@ -import peewee as pw - -db = pw.SqliteDatabase('data.db') - - -class BaseModel(pw.Model): - class Meta: - database = db - - -class User(BaseModel): - id = pw.PrimaryKeyField(unique=True) - tg_id = pw.BigIntegerField(unique=True) - date_reg = pw.DateTimeField() - date_act = pw.DateTimeField() - resp_num = pw.IntegerField() - - class Meta: - db_table = 'users' - order_by = 'resp_num' - - -class Word(BaseModel): - id = pw.PrimaryKeyField(unique=True) - word = pw.CharField() - show_num = pw.IntegerField() - vote_num = pw.IntegerField() - - class Meta: - db_table = 'words' +from models import models_list, db def _create_db(): # only for tests with db: - db.create_tables([User, Word]) + db.create_tables(models_list) _create_db() diff --git a/bot/models.py b/bot/models.py new file mode 100644 index 0000000..10c17e3 --- /dev/null +++ b/bot/models.py @@ -0,0 +1,33 @@ +from peewee import Model, PrimaryKeyField, BigIntegerField, DateTimeField, IntegerField, CharField, SqliteDatabase + +db = SqliteDatabase('data.db') + + +class BaseModel(Model): + class Meta: + database = db + + +class User(BaseModel): + id = PrimaryKeyField(unique=True) + tg_id = BigIntegerField(unique=True) + date_reg = DateTimeField() + date_act = DateTimeField() + resp_num = IntegerField() + + class Meta: + db_table = 'users' + order_by = 'resp_num' + + +class Word(BaseModel): + id = PrimaryKeyField(unique=True) + word = CharField() + show_num = IntegerField() + vote_num = IntegerField() + + class Meta: + db_table = 'words' + + +models_list = [User, Word] From 1bd0e9536be5e18674b0d02e9d5fb85a2d1671bf Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 14:12:39 +0300 Subject: [PATCH 15/62] refactor: db file replace to the root --- .gitignore | 2 +- bot/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7b574d9..2676c28 100644 --- a/.gitignore +++ b/.gitignore @@ -161,4 +161,4 @@ cython_debug/ .idea/ # database source files -bot/*.db \ No newline at end of file +*.db \ No newline at end of file diff --git a/bot/models.py b/bot/models.py index 10c17e3..b124496 100644 --- a/bot/models.py +++ b/bot/models.py @@ -1,6 +1,6 @@ from peewee import Model, PrimaryKeyField, BigIntegerField, DateTimeField, IntegerField, CharField, SqliteDatabase -db = SqliteDatabase('data.db') +db = SqliteDatabase('../data.db') class BaseModel(Model): From 56fc46bcc9de5f20ec2a913a7e6e602f309ab36b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 14:15:29 +0300 Subject: [PATCH 16/62] feat: create file_to_list func --- bot/services.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 bot/services.py diff --git a/bot/services.py b/bot/services.py new file mode 100644 index 0000000..7827978 --- /dev/null +++ b/bot/services.py @@ -0,0 +1,13 @@ +def _file_to_list(path: str) -> list[str]: + """ + Converts a txt file with words on separate lines to a list of strings. + Empty strings would be deleted + + :param path: absolute or relative path to the txt file + :return: list of str + """ + with open(path, 'r', encoding='utf-8') as f: + file_content = f.read().split('\n') + words = list(filter(None, file_content)) # remove empty strings + + return words \ No newline at end of file From a61d577e8b533218c976a65b2ba9fd242bb25734 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 14:31:23 +0300 Subject: [PATCH 17/62] feat: check the existence of data file --- .gitignore | 5 +++-- bot/services.py | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 2676c28..810bd5f 100644 --- a/.gitignore +++ b/.gitignore @@ -160,5 +160,6 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ -# database source files -*.db \ No newline at end of file +# database and data source files +*.db +words.txt diff --git a/bot/services.py b/bot/services.py index 7827978..09367d8 100644 --- a/bot/services.py +++ b/bot/services.py @@ -1,13 +1,24 @@ -def _file_to_list(path: str) -> list[str]: +import os.path + + +def _check_data_file(path: str): + if not os.path.isfile(path): + print("Can't read data file with words") + exit(1) + + +def get_words() -> list[str]: """ Converts a txt file with words on separate lines to a list of strings. Empty strings would be deleted - :param path: absolute or relative path to the txt file :return: list of str """ + path = '../words.txt' + _check_data_file(path) + with open(path, 'r', encoding='utf-8') as f: file_content = f.read().split('\n') words = list(filter(None, file_content)) # remove empty strings - return words \ No newline at end of file + return words From 246763ebb756c2056745fef71912f82a2927bc07 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 18:15:00 +0300 Subject: [PATCH 18/62] feat: create tables func sample --- bot/db.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bot/db.py b/bot/db.py index 65bfb65..b4d8a18 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,10 +1,6 @@ from models import models_list, db -def _create_db(): - # only for tests +def create_tables(): with db: db.create_tables(models_list) - - -_create_db() From 321223f2ce74189a1d83e279420b668e118b0019 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 18:18:13 +0300 Subject: [PATCH 19/62] refactor: db path replace to the config --- bot/config.py | 2 ++ bot/models.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/config.py b/bot/config.py index 5e445cd..c8717aa 100644 --- a/bot/config.py +++ b/bot/config.py @@ -17,3 +17,5 @@ def _get_settings(): settings = _get_settings() + +DB_FILE = '../data.db' diff --git a/bot/models.py b/bot/models.py index b124496..8953afc 100644 --- a/bot/models.py +++ b/bot/models.py @@ -1,6 +1,7 @@ from peewee import Model, PrimaryKeyField, BigIntegerField, DateTimeField, IntegerField, CharField, SqliteDatabase +import config -db = SqliteDatabase('../data.db') +db = SqliteDatabase(config.DB_FILE) class BaseModel(Model): From 6ca318efe79ef8c10ed2f3ab889d16796d589bca Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 18:21:28 +0300 Subject: [PATCH 20/62] feat: sample of functions --- bot/db.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bot/db.py b/bot/db.py index b4d8a18..b547897 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,6 +1,19 @@ from models import models_list, db +def _has_db() -> bool: + pass + + +def _add_words() -> None: + pass + + def create_tables(): + db_prev_state = _has_db() + with db: db.create_tables(models_list) + + if not db_prev_state: + _add_words() From 30310dee15216f95ca56d6c691c1a54666044f33 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 18:24:20 +0300 Subject: [PATCH 21/62] feat: check the data file func --- bot/db.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index b547897..ffa8f60 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,8 +1,11 @@ +import os + from models import models_list, db +import config def _has_db() -> bool: - pass + return os.path.isfile(config.DB_FILE) def _add_words() -> None: From 1cd1ea728f6867e11fcd8ffdb61371d4cc71dd3d Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 18:40:53 +0300 Subject: [PATCH 22/62] chore: add icecream to dev dep --- poetry.lock | 86 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 3 ++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 3b08a4c..88a7430 100644 --- a/poetry.lock +++ b/poetry.lock @@ -173,6 +173,23 @@ files = [ {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, ] +[[package]] +name = "asttokens" +version = "2.4.0" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.0-py2.py3-none-any.whl", hash = "sha256:cf8fc9e61a86461aa9fb161a14a0841a03c405fa829ac6b202670b3495d2ce69"}, + {file = "asttokens-2.4.0.tar.gz", hash = "sha256:2e0171b991b2c959acc6c49318049236844a5da1d65ba2672c4880c1c894834e"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +test = ["astroid", "pytest"] + [[package]] name = "async-timeout" version = "4.0.3" @@ -312,6 +329,31 @@ files = [ {file = "charset_normalizer-3.3.0-py3-none-any.whl", hash = "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2"}, ] +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "executing" +version = "2.0.0" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = "*" +files = [ + {file = "executing-2.0.0-py2.py3-none-any.whl", hash = "sha256:06df6183df67389625f4e763921c6cf978944721abf3e714000200aab95b0657"}, + {file = "executing-2.0.0.tar.gz", hash = "sha256:0ff053696fdeef426cda5bd18eacd94f82c91f49823a2e9090124212ceea9b08"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + [[package]] name = "frozenlist" version = "1.4.0" @@ -382,6 +424,23 @@ files = [ {file = "frozenlist-1.4.0.tar.gz", hash = "sha256:09163bdf0b2907454042edb19f887c6d33806adc71fbd54afc14908bfdc22251"}, ] +[[package]] +name = "icecream" +version = "2.1.3" +description = "Never use print() to debug again; inspect variables, expressions, and program execution with a single, simple function call." +optional = false +python-versions = "*" +files = [ + {file = "icecream-2.1.3-py2.py3-none-any.whl", hash = "sha256:757aec31ad4488b949bc4f499d18e6e5973c40cc4d4fc607229e78cfaec94c34"}, + {file = "icecream-2.1.3.tar.gz", hash = "sha256:0aa4a7c3374ec36153a1d08f81e3080e83d8ac1eefd97d2f4fe9544e8f9b49de"}, +] + +[package.dependencies] +asttokens = ">=2.0.1" +colorama = ">=0.3.9" +executing = ">=0.3.1" +pygments = ">=2.2.0" + [[package]] name = "idna" version = "3.4" @@ -637,6 +696,20 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.16.1" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "python-dotenv" version = "1.0.0" @@ -651,6 +724,17 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "typing-extensions" version = "4.7.1" @@ -752,4 +836,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "66f7659db9c20f24230b90bf5c9d1ab3f236ca48f9f67c40e109785db2a449fc" +content-hash = "e2d5dc24246b9340bc3def77d9f145dbeb9eeaf45d2343854f92dd8202a72b62" diff --git a/pyproject.toml b/pyproject.toml index afa363c..e0e549e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,9 @@ python-dotenv = "^1.0.0" peewee = "^3.17.0" +[tool.poetry.group.dev.dependencies] +icecream = "^2.1.3" + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" From 07e9e62b8a06db61a036df282e5e435049da2f20 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 19:10:26 +0300 Subject: [PATCH 23/62] refactor: replace db state flag --- bot/db.py | 13 +++---------- bot/models.py | 5 ++++- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/bot/db.py b/bot/db.py index ffa8f60..84391e2 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,11 +1,4 @@ -import os - -from models import models_list, db -import config - - -def _has_db() -> bool: - return os.path.isfile(config.DB_FILE) +from models import models_list, db, Word, prev_state def _add_words() -> None: @@ -13,10 +6,10 @@ def _add_words() -> None: def create_tables(): - db_prev_state = _has_db() + has_db = prev_state with db: db.create_tables(models_list) - if not db_prev_state: + if not has_db: _add_words() diff --git a/bot/models.py b/bot/models.py index 8953afc..5d6fb1a 100644 --- a/bot/models.py +++ b/bot/models.py @@ -1,6 +1,9 @@ +import os + from peewee import Model, PrimaryKeyField, BigIntegerField, DateTimeField, IntegerField, CharField, SqliteDatabase import config +prev_state = os.path.isfile(config.DB_FILE) db = SqliteDatabase(config.DB_FILE) @@ -23,7 +26,7 @@ class Meta: class Word(BaseModel): id = PrimaryKeyField(unique=True) - word = CharField() + word = CharField(max_length=100) show_num = IntegerField() vote_num = IntegerField() From 8bd10eeec14aab60f2384310cd2674aefaf7ea7b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 19:12:46 +0300 Subject: [PATCH 24/62] feat: add creator of words objects --- bot/services.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/bot/services.py b/bot/services.py index 09367d8..ea03f2a 100644 --- a/bot/services.py +++ b/bot/services.py @@ -7,7 +7,7 @@ def _check_data_file(path: str): exit(1) -def get_words() -> list[str]: +def _get_words() -> list[str]: """ Converts a txt file with words on separate lines to a list of strings. Empty strings would be deleted @@ -22,3 +22,18 @@ def get_words() -> list[str]: words = list(filter(None, file_content)) # remove empty strings return words + + +def get_words_objects() -> list[dict]: + word_list = _get_words() + words = [] + + for index, _word in enumerate(word_list, start=1): + words.append({ + 'id': index, + 'word': _word, + 'show_num': 0, + 'vote_num': 0 + }) + + return words From f841c3f9ca7dabe84b07f6318f668f3404aed701 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 19:20:37 +0300 Subject: [PATCH 25/62] feat: add words to the db func --- bot/db.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index 84391e2..7954369 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,8 +1,10 @@ from models import models_list, db, Word, prev_state +import services def _add_words() -> None: - pass + with db: + Word.insert_many(services.get_words_objects()).execute() def create_tables(): From 576781276e5c9ce4d3f89339da9756284f0a2d8d Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 19:52:44 +0300 Subject: [PATCH 26/62] feat: add_user func --- bot/db.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index 7954369..670fc16 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,4 +1,6 @@ -from models import models_list, db, Word, prev_state +from datetime import datetime + +from models import models_list, db, Word, User, prev_state import services @@ -15,3 +17,14 @@ def create_tables(): if not has_db: _add_words() + + +def add_user(usr_id: int, usr_date: datetime): + usr_obj = User( + tg_id=usr_id, + date_reg=usr_date, + resp_num=0 + ) + + with db: + usr_obj.save() From 94a02a2208aa4a8edd377183c7d6a1533197584b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 19:53:03 +0300 Subject: [PATCH 27/62] fix: allow null value in User --- bot/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/models.py b/bot/models.py index 5d6fb1a..adca93b 100644 --- a/bot/models.py +++ b/bot/models.py @@ -16,7 +16,7 @@ class User(BaseModel): id = PrimaryKeyField(unique=True) tg_id = BigIntegerField(unique=True) date_reg = DateTimeField() - date_act = DateTimeField() + date_act = DateTimeField(null=True) resp_num = IntegerField() class Meta: From 01432f3a82cfc075d584f17be529c1e53cb25599 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 19:54:58 +0300 Subject: [PATCH 28/62] feat: remove stop command --- bot/main.py | 5 ----- bot/messages.py | 2 -- 2 files changed, 7 deletions(-) diff --git a/bot/main.py b/bot/main.py index 023235e..c89becd 100644 --- a/bot/main.py +++ b/bot/main.py @@ -39,11 +39,6 @@ async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) -@dp.message(Command("stop")) -async def command_stop_handler(message: Message) -> None: - await message.answer(messages.STOP) - - @dp.message() async def unknown_command_handler(message: Message) -> None: await message.answer(messages.UNKNOWN) diff --git a/bot/messages.py b/bot/messages.py index ea8d5e2..ff59cfa 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -4,8 +4,6 @@ HELP = "Sample of /help message" -STOP = "Sample of /stop message" - UNKNOWN = "Unknown command, type /help" ON_START = "Бот запущен" From cc4abbd1916fc47ad2d18ad07b53f2db4f10d696 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 20:13:10 +0300 Subject: [PATCH 29/62] feat(add_user): usr_date could be none --- bot/db.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index 670fc16..572601e 100644 --- a/bot/db.py +++ b/bot/db.py @@ -19,7 +19,9 @@ def create_tables(): _add_words() -def add_user(usr_id: int, usr_date: datetime): +def add_user(usr_id: int, usr_date: datetime = None): + usr_date = usr_date if usr_date else datetime.now() + usr_obj = User( tg_id=usr_id, date_reg=usr_date, From 1beebc77ebe711d61f5dfb7edae11b3a328175b7 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 20:28:11 +0300 Subject: [PATCH 30/62] feat: update user func --- bot/db.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bot/db.py b/bot/db.py index 572601e..573f31d 100644 --- a/bot/db.py +++ b/bot/db.py @@ -30,3 +30,12 @@ def add_user(usr_id: int, usr_date: datetime = None): with db: usr_obj.save() + + +def update_user(usr_id: int, usr_date: datetime = None): + usr = User.get(User.tg_id == usr_id) + usr.date_act = usr_date if usr_date else datetime.now() + usr.resp_num = usr.resp_num + 1 + + with db: + usr.save() From d1d11a3becfae19ad94b6801f9ca1783c21f5eaa Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 20:44:52 +0300 Subject: [PATCH 31/62] feat: update words func --- bot/db.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bot/db.py b/bot/db.py index 573f31d..cfed556 100644 --- a/bot/db.py +++ b/bot/db.py @@ -39,3 +39,16 @@ def update_user(usr_id: int, usr_date: datetime = None): with db: usr.save() + + +def update_words(voted_word_id: int, linked_word_id: int): + voted_word = Word.get(Word.id == voted_word_id) + voted_word.show_num += 1 + voted_word.vote_num += 1 + + linked_word = Word.get(Word.id == linked_word_id) + linked_word.show_num += 1 + + with db: + voted_word.save() + linked_word.save() From fdd6e4f8c058bdb05450067d968514de4822d7d6 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 20:48:54 +0300 Subject: [PATCH 32/62] refactor(update_user): += instead of = --- bot/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index cfed556..75a68de 100644 --- a/bot/db.py +++ b/bot/db.py @@ -35,7 +35,7 @@ def add_user(usr_id: int, usr_date: datetime = None): def update_user(usr_id: int, usr_date: datetime = None): usr = User.get(User.tg_id == usr_id) usr.date_act = usr_date if usr_date else datetime.now() - usr.resp_num = usr.resp_num + 1 + usr.resp_num += 1 with db: usr.save() From f75d2c2f4385eb9005b1465d6cf91a1507aaf458 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 21:06:13 +0300 Subject: [PATCH 33/62] feat: get words func --- bot/db.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bot/db.py b/bot/db.py index 75a68de..0131246 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import Tuple, Any from models import models_list, db, Word, User, prev_state import services @@ -52,3 +53,9 @@ def update_words(voted_word_id: int, linked_word_id: int): with db: voted_word.save() linked_word.save() + + +def get_words(words_ids: tuple[int, int]) -> tuple[str]: + out = tuple(str(Word.get(Word.id == i).word) for i in words_ids) + + return out From 71cb179b8ff5b9ad827a22a34dabf16432b0b13d Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 21:16:42 +0300 Subject: [PATCH 34/62] refactor: change the show count when getting --- bot/db.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/bot/db.py b/bot/db.py index 0131246..66aef46 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,5 +1,4 @@ from datetime import datetime -from typing import Tuple, Any from models import models_list, db, Word, User, prev_state import services @@ -42,20 +41,27 @@ def update_user(usr_id: int, usr_date: datetime = None): usr.save() -def update_words(voted_word_id: int, linked_word_id: int): +def _update_show_num(word_id: int) -> None: + word = Word.get(Word.id == word_id) + word.show_num += 1 + + with db: + word.save() + + +def update_voted_word(voted_word_id: int): voted_word = Word.get(Word.id == voted_word_id) - voted_word.show_num += 1 voted_word.vote_num += 1 - linked_word = Word.get(Word.id == linked_word_id) - linked_word.show_num += 1 - with db: voted_word.save() - linked_word.save() def get_words(words_ids: tuple[int, int]) -> tuple[str]: - out = tuple(str(Word.get(Word.id == i).word) for i in words_ids) + out = [] + + for i in words_ids: + _update_show_num(i) + out.append(str(Word.get(Word.id == i).word)) - return out + return tuple(out) From 7e437c87fb5fbdfde61f5843fadcc89f4eefe510 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Mon, 23 Oct 2023 21:30:28 +0300 Subject: [PATCH 35/62] feat: get max word id func --- bot/db.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bot/db.py b/bot/db.py index 66aef46..ac32a44 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,5 +1,7 @@ from datetime import datetime +from peewee import fn + from models import models_list, db, Word, User, prev_state import services @@ -65,3 +67,8 @@ def get_words(words_ids: tuple[int, int]) -> tuple[str]: out.append(str(Word.get(Word.id == i).word)) return tuple(out) + + +def get_max_word_id() -> int: + max_id = Word.select(fn.MAX(Word.id)).scalar() + return max_id From e619a9c399509c2c20249ccd9ee11712e90f293b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Tue, 24 Oct 2023 10:47:32 +0300 Subject: [PATCH 36/62] feat: implement add user and create tables --- bot/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index c89becd..b5f8fe4 100644 --- a/bot/main.py +++ b/bot/main.py @@ -9,6 +9,7 @@ import config import messages +import db dp = Dispatcher() bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) @@ -16,7 +17,7 @@ @dp.startup() async def on_startup(): - # await config.set_commands(dp) + db.create_tables() await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_START) @@ -31,6 +32,7 @@ async def command_start_handler(message: Message) -> None: """ This handler receives messages with `/start` command """ + db.add_user(message.from_user.id, message.date) await message.answer(messages.START) From 90e2b0db0d631f996d1e50690c76f4350f340573 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sat, 28 Oct 2023 20:43:52 +0300 Subject: [PATCH 37/62] fix: check the existence of the user before adding --- bot/db.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index ac32a44..f116bc1 100644 --- a/bot/db.py +++ b/bot/db.py @@ -30,8 +30,10 @@ def add_user(usr_id: int, usr_date: datetime = None): resp_num=0 ) + query = User.select().where(User.tg_id == usr_id) with db: - usr_obj.save() + if not query.exists(): + usr_obj.save() def update_user(usr_id: int, usr_date: datetime = None): From f2f1665e798a1117c1556da4215acf6c25f89252 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sat, 28 Oct 2023 20:48:02 +0300 Subject: [PATCH 38/62] refactor: replace get_max_word_id --- bot/db.py | 5 ----- bot/services.py | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/bot/db.py b/bot/db.py index f116bc1..fb912b6 100644 --- a/bot/db.py +++ b/bot/db.py @@ -69,8 +69,3 @@ def get_words(words_ids: tuple[int, int]) -> tuple[str]: out.append(str(Word.get(Word.id == i).word)) return tuple(out) - - -def get_max_word_id() -> int: - max_id = Word.select(fn.MAX(Word.id)).scalar() - return max_id diff --git a/bot/services.py b/bot/services.py index ea03f2a..4e00776 100644 --- a/bot/services.py +++ b/bot/services.py @@ -1,4 +1,7 @@ import os.path +from peewee import fn + +from models import Word def _check_data_file(path: str): @@ -37,3 +40,8 @@ def get_words_objects() -> list[dict]: }) return words + + +def get_max_word_id() -> int: + max_id = Word.select(fn.MAX(Word.id)).scalar() + return max_id From 5d4a498449c23a70cfb5065e1b7d0127e9cae1ba Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sat, 28 Oct 2023 21:29:03 +0300 Subject: [PATCH 39/62] feat: create get_words_ids --- bot/services.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bot/services.py b/bot/services.py index 4e00776..5aa7a62 100644 --- a/bot/services.py +++ b/bot/services.py @@ -1,4 +1,6 @@ import os.path +import random + from peewee import fn from models import Word @@ -42,6 +44,12 @@ def get_words_objects() -> list[dict]: return words -def get_max_word_id() -> int: +def get_words_ids() -> tuple[int, int]: max_id = Word.select(fn.MAX(Word.id)).scalar() - return max_id + id_1 = id_2 = random.randint(1, max_id) + + while id_1 == id_2: + id_2 = random.randint(1, max_id) + + return id_1, id_2 + From 51489b2a273f357a5a517244e2d61ee1b8ebdd16 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sat, 28 Oct 2023 23:15:49 +0300 Subject: [PATCH 40/62] feat: add test command --- bot/main.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bot/main.py b/bot/main.py index b5f8fe4..e739218 100644 --- a/bot/main.py +++ b/bot/main.py @@ -10,6 +10,7 @@ import config import messages import db +from bot import services dp = Dispatcher() bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) @@ -41,6 +42,14 @@ async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) +@dp.message(Command("test")) +async def command_help_handler(message: Message) -> None: + ids = services.get_words_ids() + out = db.get_words(ids) + db.update_user(message.from_user.id, message.date) + await message.answer(f'{out[0]}, {out[1]}') + + @dp.message() async def unknown_command_handler(message: Message) -> None: await message.answer(messages.UNKNOWN) From 01b4a9324f31fac55d56da93da733b815a5ef4c7 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sat, 28 Oct 2023 23:28:32 +0300 Subject: [PATCH 41/62] feat: add admin filter to the test command --- bot/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index e739218..ea00c78 100644 --- a/bot/main.py +++ b/bot/main.py @@ -6,6 +6,7 @@ from aiogram.enums import ParseMode from aiogram.filters import Command from aiogram.types import Message +from aiogram import F import config import messages @@ -42,7 +43,7 @@ async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) -@dp.message(Command("test")) +@dp.message((F.from_user.id == config.settings.admin_id) & (F.text == "test")) async def command_help_handler(message: Message) -> None: ids = services.get_words_ids() out = db.get_words(ids) From a121922d980a5aa834b1a1404cd35a792d06356b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 16:19:36 +0300 Subject: [PATCH 42/62] chore: add comment for future refactoring --- bot/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/db.py b/bot/db.py index fb912b6..1add65e 100644 --- a/bot/db.py +++ b/bot/db.py @@ -30,7 +30,7 @@ def add_user(usr_id: int, usr_date: datetime = None): resp_num=0 ) - query = User.select().where(User.tg_id == usr_id) + query = User.select().where(User.tg_id == usr_id) # change to EAFP way with db: if not query.exists(): usr_obj.save() From 87cb4ded8863fce310721ff4ba7d8e0048bbfd8e Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 16:31:58 +0300 Subject: [PATCH 43/62] feat: add on-start keyboard --- bot/keyboards.py | 14 ++++++++++++++ bot/main.py | 3 ++- bot/messages.py | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 bot/keyboards.py diff --git a/bot/keyboards.py b/bot/keyboards.py new file mode 100644 index 0000000..24c0461 --- /dev/null +++ b/bot/keyboards.py @@ -0,0 +1,14 @@ +from aiogram import types +import messages + +kb_on_start = [ + [ + types.KeyboardButton(text=messages.KB_START_TEXT), + ], +] + +keyboard_start = types.ReplyKeyboardMarkup( + keyboard=kb_on_start, + resize_keyboard=True, + input_field_placeholder=messages.KB_START_PH +) diff --git a/bot/main.py b/bot/main.py index ea00c78..0cbb3df 100644 --- a/bot/main.py +++ b/bot/main.py @@ -12,6 +12,7 @@ import messages import db from bot import services +from keyboards import keyboard_start dp = Dispatcher() bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) @@ -35,7 +36,7 @@ async def command_start_handler(message: Message) -> None: This handler receives messages with `/start` command """ db.add_user(message.from_user.id, message.date) - await message.answer(messages.START) + await message.answer(messages.START, reply_markup=keyboard_start) @dp.message(Command("help")) diff --git a/bot/messages.py b/bot/messages.py index ff59cfa..2cf4ffe 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -9,3 +9,6 @@ ON_START = "Бот запущен" ON_STOP = "Бот остановлен" + +KB_START_PH = "Нажмите, если готовы начать" +KB_START_TEXT = "Поехали!" From 0eb949df87b7ad1d7001bfef68448c29daae72d3 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 16:41:37 +0300 Subject: [PATCH 44/62] feat: placeholder for voting handler --- bot/main.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bot/main.py b/bot/main.py index 0cbb3df..c793f5e 100644 --- a/bot/main.py +++ b/bot/main.py @@ -39,6 +39,12 @@ async def command_start_handler(message: Message) -> None: await message.answer(messages.START, reply_markup=keyboard_start) +@dp.message(F.text == messages.KB_START_TEXT) +async def with_puree(message: types.Message): + pass + await message.reply("pass", reply_markup=types.ReplyKeyboardRemove()) + + @dp.message(Command("help")) async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) From cbf1aced1061b5c5d60bc7e3c9fccd2336be68a0 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 16:48:15 +0300 Subject: [PATCH 45/62] feat: add voting keyboard --- bot/keyboards.py | 17 +++++++++++++++-- bot/main.py | 4 ++-- bot/messages.py | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/bot/keyboards.py b/bot/keyboards.py index 24c0461..f61e538 100644 --- a/bot/keyboards.py +++ b/bot/keyboards.py @@ -1,14 +1,27 @@ from aiogram import types import messages -kb_on_start = [ +_kb_on_start = [ [ types.KeyboardButton(text=messages.KB_START_TEXT), ], ] +_kb_on_voting = [ + [ + types.KeyboardButton(text="1"), + types.KeyboardButton(text="2"), + ], +] + keyboard_start = types.ReplyKeyboardMarkup( - keyboard=kb_on_start, + keyboard=_kb_on_start, resize_keyboard=True, input_field_placeholder=messages.KB_START_PH ) + +keyboard_voting = types.ReplyKeyboardMarkup( + keyboard=_kb_on_voting, + resize_keyboard=True, + input_field_placeholder=messages.KB_VOTING_PH +) diff --git a/bot/main.py b/bot/main.py index c793f5e..f4ca465 100644 --- a/bot/main.py +++ b/bot/main.py @@ -12,7 +12,7 @@ import messages import db from bot import services -from keyboards import keyboard_start +from keyboards import keyboard_start, keyboard_voting dp = Dispatcher() bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) @@ -42,7 +42,7 @@ async def command_start_handler(message: Message) -> None: @dp.message(F.text == messages.KB_START_TEXT) async def with_puree(message: types.Message): pass - await message.reply("pass", reply_markup=types.ReplyKeyboardRemove()) + await message.reply("pass", reply_markup=keyboard_voting) @dp.message(Command("help")) diff --git a/bot/messages.py b/bot/messages.py index 2cf4ffe..4d350b3 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -12,3 +12,5 @@ KB_START_PH = "Нажмите, если готовы начать" KB_START_TEXT = "Поехали!" + +KB_VOTING_PH = "Выберите один из двух вариантов" From 5a4a8c08eaaf5d833d431dd0f8aae99a55c41daa Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 16:57:55 +0300 Subject: [PATCH 46/62] feat: generate voting message --- bot/main.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/bot/main.py b/bot/main.py index f4ca465..47aa8af 100644 --- a/bot/main.py +++ b/bot/main.py @@ -7,6 +7,7 @@ from aiogram.filters import Command from aiogram.types import Message from aiogram import F +from aiogram.utils.markdown import hbold import config import messages @@ -18,6 +19,14 @@ bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) +def _new_pair(message) -> str: + ids = services.get_words_ids() + out = db.get_words(ids) + db.update_user(message.from_user.id, message.date) + return f'1. {hbold(out[0])}\n\n' \ + f'2. {hbold(out[1])}' + + @dp.startup() async def on_startup(): db.create_tables() @@ -40,9 +49,9 @@ async def command_start_handler(message: Message) -> None: @dp.message(F.text == messages.KB_START_TEXT) -async def with_puree(message: types.Message): - pass - await message.reply("pass", reply_markup=keyboard_voting) +async def voting_message_handler(message: types.Message): + ans = _new_pair(message) + await message.answer(ans, reply_markup=keyboard_voting) @dp.message(Command("help")) @@ -52,10 +61,7 @@ async def command_help_handler(message: Message) -> None: @dp.message((F.from_user.id == config.settings.admin_id) & (F.text == "test")) async def command_help_handler(message: Message) -> None: - ids = services.get_words_ids() - out = db.get_words(ids) - db.update_user(message.from_user.id, message.date) - await message.answer(f'{out[0]}, {out[1]}') + await message.answer(_new_pair(message)) @dp.message() From 7a5ee235dac38da87d29fdebc191ed94b7b84901 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 17:15:15 +0300 Subject: [PATCH 47/62] chore: comments with todo --- bot/main.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bot/main.py b/bot/main.py index 47aa8af..9a394f5 100644 --- a/bot/main.py +++ b/bot/main.py @@ -50,10 +50,18 @@ async def command_start_handler(message: Message) -> None: @dp.message(F.text == messages.KB_START_TEXT) async def voting_message_handler(message: types.Message): + # todo сохраняем в фсм два айдишника ans = _new_pair(message) await message.answer(ans, reply_markup=keyboard_voting) +# todo хэндлер для цифровой команды +# todo определяем номер цифры +# todo голосуем за айдишник под указанным номером +# todo сбрасываем фсм +# todo показываем новое сообщение +# todo сохраняем в фсм их айдишники + @dp.message(Command("help")) async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) From 21a6190a5a370667a43798654f81e5d8ab05b4af Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 20:16:47 +0300 Subject: [PATCH 48/62] feat: remove a first step in polling --- bot/keyboards.py | 12 ------------ bot/main.py | 8 ++------ bot/messages.py | 3 --- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/bot/keyboards.py b/bot/keyboards.py index f61e538..3521526 100644 --- a/bot/keyboards.py +++ b/bot/keyboards.py @@ -1,12 +1,6 @@ from aiogram import types import messages -_kb_on_start = [ - [ - types.KeyboardButton(text=messages.KB_START_TEXT), - ], -] - _kb_on_voting = [ [ types.KeyboardButton(text="1"), @@ -14,12 +8,6 @@ ], ] -keyboard_start = types.ReplyKeyboardMarkup( - keyboard=_kb_on_start, - resize_keyboard=True, - input_field_placeholder=messages.KB_START_PH -) - keyboard_voting = types.ReplyKeyboardMarkup( keyboard=_kb_on_voting, resize_keyboard=True, diff --git a/bot/main.py b/bot/main.py index 9a394f5..c7ebbda 100644 --- a/bot/main.py +++ b/bot/main.py @@ -13,7 +13,7 @@ import messages import db from bot import services -from keyboards import keyboard_start, keyboard_voting +from keyboards import keyboard_voting dp = Dispatcher() bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) @@ -45,12 +45,8 @@ async def command_start_handler(message: Message) -> None: This handler receives messages with `/start` command """ db.add_user(message.from_user.id, message.date) - await message.answer(messages.START, reply_markup=keyboard_start) + await message.answer(messages.START) - -@dp.message(F.text == messages.KB_START_TEXT) -async def voting_message_handler(message: types.Message): - # todo сохраняем в фсм два айдишника ans = _new_pair(message) await message.answer(ans, reply_markup=keyboard_voting) diff --git a/bot/messages.py b/bot/messages.py index 4d350b3..394ce4d 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -10,7 +10,4 @@ ON_STOP = "Бот остановлен" -KB_START_PH = "Нажмите, если готовы начать" -KB_START_TEXT = "Поехали!" - KB_VOTING_PH = "Выберите один из двух вариантов" From 56e369a4bffed9576be78ccc9ba85c23f99614c2 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 20:23:04 +0300 Subject: [PATCH 49/62] feat: add handler for polling --- bot/main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index c7ebbda..1df48f8 100644 --- a/bot/main.py +++ b/bot/main.py @@ -51,7 +51,12 @@ async def command_start_handler(message: Message) -> None: await message.answer(ans, reply_markup=keyboard_voting) -# todo хэндлер для цифровой команды +@dp.message((F.text == "1") | (F.text == "2")) +async def polling_handler(message: Message) -> None: + ans = _new_pair(message) + await message.answer(ans, reply_markup=keyboard_voting) + + # todo определяем номер цифры # todo голосуем за айдишник под указанным номером # todo сбрасываем фсм From e8524f33256d6a127b8014849661fb7cb0b90e6b Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 20:25:23 +0300 Subject: [PATCH 50/62] refactor: more DRY practice --- bot/main.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/bot/main.py b/bot/main.py index 1df48f8..91b86d1 100644 --- a/bot/main.py +++ b/bot/main.py @@ -39,18 +39,6 @@ async def on_shutdown(): await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_STOP) -@dp.message(Command("start")) -async def command_start_handler(message: Message) -> None: - """ - This handler receives messages with `/start` command - """ - db.add_user(message.from_user.id, message.date) - await message.answer(messages.START) - - ans = _new_pair(message) - await message.answer(ans, reply_markup=keyboard_voting) - - @dp.message((F.text == "1") | (F.text == "2")) async def polling_handler(message: Message) -> None: ans = _new_pair(message) @@ -63,6 +51,17 @@ async def polling_handler(message: Message) -> None: # todo показываем новое сообщение # todo сохраняем в фсм их айдишники +@dp.message(Command("start")) +async def command_start_handler(message: Message) -> None: + """ + This handler receives messages with `/start` command + """ + db.add_user(message.from_user.id, message.date) + await message.answer(messages.START) + + await polling_handler(message) + + @dp.message(Command("help")) async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) From 61a8c4c73bcf0a89c1cf696a736a084c7bbdf3f6 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 20:59:29 +0300 Subject: [PATCH 51/62] feat: endless polling --- bot/main.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/bot/main.py b/bot/main.py index 91b86d1..6cdb02f 100644 --- a/bot/main.py +++ b/bot/main.py @@ -5,6 +5,8 @@ from aiogram import Bot, Dispatcher, Router, types from aiogram.enums import ParseMode from aiogram.filters import Command +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import StatesGroup, State from aiogram.types import Message from aiogram import F from aiogram.utils.markdown import hbold @@ -19,8 +21,11 @@ bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) -def _new_pair(message) -> str: - ids = services.get_words_ids() +class Answer(StatesGroup): + prev_id = State() + + +def _new_pair(message, ids) -> str: out = db.get_words(ids) db.update_user(message.from_user.id, message.date) return f'1. {hbold(out[0])}\n\n' \ @@ -40,16 +45,15 @@ async def on_shutdown(): @dp.message((F.text == "1") | (F.text == "2")) -async def polling_handler(message: Message) -> None: - ans = _new_pair(message) - await message.answer(ans, reply_markup=keyboard_voting) +async def polling_handler(message: Message, state: FSMContext) -> None: + data = await state.get_data() + # todo парсить, определить за кого голос и увеличить + ids = services.get_words_ids() + await state.update_data(prev_id=f'{ids[0]}|{ids[1]}') + ans = _new_pair(message, ids) + await message.answer(f"{ans}\n{ids}\n{data}", reply_markup=keyboard_voting) -# todo определяем номер цифры -# todo голосуем за айдишник под указанным номером -# todo сбрасываем фсм -# todo показываем новое сообщение -# todo сохраняем в фсм их айдишники @dp.message(Command("start")) async def command_start_handler(message: Message) -> None: From 5663b9abbdd986643f378b01cd03bef167138747 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 21:28:49 +0300 Subject: [PATCH 52/62] fix: return the first step of polling --- bot/keyboards.py | 12 ++++++++++++ bot/main.py | 8 +++----- bot/messages.py | 3 +++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/bot/keyboards.py b/bot/keyboards.py index 3521526..3b74dc0 100644 --- a/bot/keyboards.py +++ b/bot/keyboards.py @@ -1,6 +1,18 @@ from aiogram import types import messages +_kb_on_start = [ + [ + types.KeyboardButton(text=messages.KB_START_TEXT), + ], +] + +keyboard_start = types.ReplyKeyboardMarkup( + keyboard=_kb_on_start, + resize_keyboard=True, + input_field_placeholder=messages.KB_START_PH +) + _kb_on_voting = [ [ types.KeyboardButton(text="1"), diff --git a/bot/main.py b/bot/main.py index 6cdb02f..48526f0 100644 --- a/bot/main.py +++ b/bot/main.py @@ -15,7 +15,7 @@ import messages import db from bot import services -from keyboards import keyboard_voting +from keyboards import keyboard_voting, keyboard_start dp = Dispatcher() bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) @@ -44,7 +44,7 @@ async def on_shutdown(): await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_STOP) -@dp.message((F.text == "1") | (F.text == "2")) +@dp.message((F.text == "1") | (F.text == "2") | (F.text == "Поехали!")) async def polling_handler(message: Message, state: FSMContext) -> None: data = await state.get_data() # todo парсить, определить за кого голос и увеличить @@ -61,9 +61,7 @@ async def command_start_handler(message: Message) -> None: This handler receives messages with `/start` command """ db.add_user(message.from_user.id, message.date) - await message.answer(messages.START) - - await polling_handler(message) + await message.answer(messages.START, reply_markup=keyboard_start) @dp.message(Command("help")) diff --git a/bot/messages.py b/bot/messages.py index 394ce4d..7db7014 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -11,3 +11,6 @@ ON_STOP = "Бот остановлен" KB_VOTING_PH = "Выберите один из двух вариантов" + +KB_START_PH = "Нажмите, если готовы начать" +KB_START_TEXT = "Поехали!" From 2784006cd872f33ca4b6424e6824f062f61c4a59 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 22:00:01 +0300 Subject: [PATCH 53/62] feat: add polling --- bot/main.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/bot/main.py b/bot/main.py index 48526f0..35abbc3 100644 --- a/bot/main.py +++ b/bot/main.py @@ -32,6 +32,18 @@ def _new_pair(message, ids) -> str: f'2. {hbold(out[1])}' +def _get_usr_ans(message: Message, data: dict) -> int: + try: + ans = data['prev_id'].split('|') + except KeyError: + ans = [] + + if message.text.isdecimal() and ans: + index = int(message.text) - 1 + return ans[index] + return 0 + + @dp.startup() async def on_startup(): db.create_tables() @@ -47,12 +59,14 @@ async def on_shutdown(): @dp.message((F.text == "1") | (F.text == "2") | (F.text == "Поехали!")) async def polling_handler(message: Message, state: FSMContext) -> None: data = await state.get_data() - # todo парсить, определить за кого голос и увеличить + + voted_id = _get_usr_ans(message, data) + db.update_voted_word(voted_id) ids = services.get_words_ids() await state.update_data(prev_id=f'{ids[0]}|{ids[1]}') ans = _new_pair(message, ids) - await message.answer(f"{ans}\n{ids}\n{data}", reply_markup=keyboard_voting) + await message.answer(ans, reply_markup=keyboard_voting) @dp.message(Command("start")) From a0d7f2643764eb8f39f4462e90a6298c6c1fbe46 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 22:00:47 +0300 Subject: [PATCH 54/62] fix: the word id cannot be zero --- bot/db.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/db.py b/bot/db.py index 1add65e..0773ff3 100644 --- a/bot/db.py +++ b/bot/db.py @@ -54,6 +54,9 @@ def _update_show_num(word_id: int) -> None: def update_voted_word(voted_word_id: int): + if not voted_word_id: + return + voted_word = Word.get(Word.id == voted_word_id) voted_word.vote_num += 1 From e5756b8cd2611ef67155553534dc3ca3f320b1e7 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 22:12:59 +0300 Subject: [PATCH 55/62] refactor: refactor test handler --- bot/main.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bot/main.py b/bot/main.py index 35abbc3..4715b8d 100644 --- a/bot/main.py +++ b/bot/main.py @@ -83,11 +83,6 @@ async def command_help_handler(message: Message) -> None: await message.answer(messages.HELP) -@dp.message((F.from_user.id == config.settings.admin_id) & (F.text == "test")) -async def command_help_handler(message: Message) -> None: - await message.answer(_new_pair(message)) - - @dp.message() async def unknown_command_handler(message: Message) -> None: await message.answer(messages.UNKNOWN) From 0e139e654c168d93be3a2c9081d07ed7188b0913 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 22:18:05 +0300 Subject: [PATCH 56/62] refactor: improve type hints --- bot/config.py | 2 +- bot/db.py | 10 ++++------ bot/main.py | 2 +- bot/services.py | 3 +-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/bot/config.py b/bot/config.py index c8717aa..c413ef4 100644 --- a/bot/config.py +++ b/bot/config.py @@ -10,7 +10,7 @@ class Bot: admin_id: int -def _get_settings(): +def _get_settings() -> Bot: load_dotenv() return Bot(bot_token=os.getenv("BOT_TOKEN"), admin_id=int(os.getenv("ADMIN_ID"))) diff --git a/bot/db.py b/bot/db.py index 0773ff3..633c7ed 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,7 +1,5 @@ from datetime import datetime -from peewee import fn - from models import models_list, db, Word, User, prev_state import services @@ -11,7 +9,7 @@ def _add_words() -> None: Word.insert_many(services.get_words_objects()).execute() -def create_tables(): +def create_tables() -> None: has_db = prev_state with db: @@ -21,7 +19,7 @@ def create_tables(): _add_words() -def add_user(usr_id: int, usr_date: datetime = None): +def add_user(usr_id: int, usr_date: datetime = None) -> None: usr_date = usr_date if usr_date else datetime.now() usr_obj = User( @@ -36,7 +34,7 @@ def add_user(usr_id: int, usr_date: datetime = None): usr_obj.save() -def update_user(usr_id: int, usr_date: datetime = None): +def update_user(usr_id: int, usr_date: datetime = None) -> None: usr = User.get(User.tg_id == usr_id) usr.date_act = usr_date if usr_date else datetime.now() usr.resp_num += 1 @@ -53,7 +51,7 @@ def _update_show_num(word_id: int) -> None: word.save() -def update_voted_word(voted_word_id: int): +def update_voted_word(voted_word_id: int) -> None: if not voted_word_id: return diff --git a/bot/main.py b/bot/main.py index 4715b8d..05da80a 100644 --- a/bot/main.py +++ b/bot/main.py @@ -25,7 +25,7 @@ class Answer(StatesGroup): prev_id = State() -def _new_pair(message, ids) -> str: +def _new_pair(message: Message, ids: tuple[int, int]) -> str: out = db.get_words(ids) db.update_user(message.from_user.id, message.date) return f'1. {hbold(out[0])}\n\n' \ diff --git a/bot/services.py b/bot/services.py index 5aa7a62..f52b3c9 100644 --- a/bot/services.py +++ b/bot/services.py @@ -6,7 +6,7 @@ from models import Word -def _check_data_file(path: str): +def _check_data_file(path: str) -> None: if not os.path.isfile(path): print("Can't read data file with words") exit(1) @@ -52,4 +52,3 @@ def get_words_ids() -> tuple[int, int]: id_2 = random.randint(1, max_id) return id_1, id_2 - From e52112c867ad85a1ba24ba056a7fedfa4c89fc8e Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 22:36:03 +0300 Subject: [PATCH 57/62] fix: update words show num after voting --- bot/db.py | 3 +-- bot/main.py | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/bot/db.py b/bot/db.py index 633c7ed..cbd2c48 100644 --- a/bot/db.py +++ b/bot/db.py @@ -43,7 +43,7 @@ def update_user(usr_id: int, usr_date: datetime = None) -> None: usr.save() -def _update_show_num(word_id: int) -> None: +def update_show_num(word_id: int) -> None: word = Word.get(Word.id == word_id) word.show_num += 1 @@ -66,7 +66,6 @@ def get_words(words_ids: tuple[int, int]) -> tuple[str]: out = [] for i in words_ids: - _update_show_num(i) out.append(str(Word.get(Word.id == i).word)) return tuple(out) diff --git a/bot/main.py b/bot/main.py index 05da80a..8a76af4 100644 --- a/bot/main.py +++ b/bot/main.py @@ -32,18 +32,29 @@ def _new_pair(message: Message, ids: tuple[int, int]) -> str: f'2. {hbold(out[1])}' -def _get_usr_ans(message: Message, data: dict) -> int: +def _parse_state_data(data: dict) -> list[int] | list: try: ans = data['prev_id'].split('|') + return [int(i) for i in ans] except KeyError: - ans = [] + return [] + +def _get_usr_ans(message: Message, ans: list[int]) -> int: if message.text.isdecimal() and ans: index = int(message.text) - 1 return ans[index] return 0 +def _upd_show_nums(ids: list[int, int]) -> None: + if not ids: + return + + for i in ids: + db.update_show_num(i) + + @dp.startup() async def on_startup(): db.create_tables() @@ -58,8 +69,8 @@ async def on_shutdown(): @dp.message((F.text == "1") | (F.text == "2") | (F.text == "Поехали!")) async def polling_handler(message: Message, state: FSMContext) -> None: - data = await state.get_data() - + data = _parse_state_data(await state.get_data()) + _upd_show_nums(data) voted_id = _get_usr_ans(message, data) db.update_voted_word(voted_id) From 219e4d479d86fd4ff893883c7a2a67bc7701bd47 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 22:43:29 +0300 Subject: [PATCH 58/62] fix: update usr resp num after voting --- bot/main.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/main.py b/bot/main.py index 8a76af4..b06aaac 100644 --- a/bot/main.py +++ b/bot/main.py @@ -25,9 +25,8 @@ class Answer(StatesGroup): prev_id = State() -def _new_pair(message: Message, ids: tuple[int, int]) -> str: +def _new_pair(ids: tuple[int, int]) -> str: out = db.get_words(ids) - db.update_user(message.from_user.id, message.date) return f'1. {hbold(out[0])}\n\n' \ f'2. {hbold(out[1])}' @@ -47,12 +46,13 @@ def _get_usr_ans(message: Message, ans: list[int]) -> int: return 0 -def _upd_show_nums(ids: list[int, int]) -> None: +def _update_counters(message: Message, ids: list[int, int]) -> None: if not ids: return for i in ids: db.update_show_num(i) + db.update_user(message.from_user.id, message.date) @dp.startup() @@ -70,13 +70,13 @@ async def on_shutdown(): @dp.message((F.text == "1") | (F.text == "2") | (F.text == "Поехали!")) async def polling_handler(message: Message, state: FSMContext) -> None: data = _parse_state_data(await state.get_data()) - _upd_show_nums(data) + _update_counters(message, data) voted_id = _get_usr_ans(message, data) db.update_voted_word(voted_id) ids = services.get_words_ids() await state.update_data(prev_id=f'{ids[0]}|{ids[1]}') - ans = _new_pair(message, ids) + ans = _new_pair(ids) await message.answer(ans, reply_markup=keyboard_voting) From 7c516235aed61053db2399f066614e72b13c82fc Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 23:06:15 +0300 Subject: [PATCH 59/62] chore: replace messages with templates --- bot/main.py | 2 +- bot/messages.py | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/bot/main.py b/bot/main.py index b06aaac..ac61fce 100644 --- a/bot/main.py +++ b/bot/main.py @@ -67,7 +67,7 @@ async def on_shutdown(): await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_STOP) -@dp.message((F.text == "1") | (F.text == "2") | (F.text == "Поехали!")) +@dp.message((F.text == "1") | (F.text == "2") | (F.text == messages.KB_START_TEXT)) async def polling_handler(message: Message, state: FSMContext) -> None: data = _parse_state_data(await state.get_data()) _update_counters(message, data) diff --git a/bot/messages.py b/bot/messages.py index 7db7014..cef5438 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -1,16 +1,17 @@ from aiogram.utils.markdown import hbold -START = f"{hbold('Hello!')}" +START = f"""{hbold('Привет!')}\n +Это текст команды, которая отобразится на старте""" -HELP = "Sample of /help message" +HELP = "Текст для команды help" -UNKNOWN = "Unknown command, type /help" +UNKNOWN = "Текст, который отобразится, если пользователь отправил неожидаемое сообщение" -ON_START = "Бот запущен" +ON_START = "Бот запущен (будет отправлено админу)" -ON_STOP = "Бот остановлен" +ON_STOP = "Бот остановлен (будет отправлено админу)" -KB_VOTING_PH = "Выберите один из двух вариантов" +KB_VOTING_PH = "Плэйсхолдер для выбора ответа" -KB_START_PH = "Нажмите, если готовы начать" -KB_START_TEXT = "Поехали!" +KB_START_PH = "Плэйсхолдер для старта бота" +KB_START_TEXT = "Сообщение для старта бота" From ed1e145556843436722ab9827596935a1f7cbf14 Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 23:12:31 +0300 Subject: [PATCH 60/62] feat: add voting msg title --- bot/main.py | 3 ++- bot/messages.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index ac61fce..a86f208 100644 --- a/bot/main.py +++ b/bot/main.py @@ -27,7 +27,8 @@ class Answer(StatesGroup): def _new_pair(ids: tuple[int, int]) -> str: out = db.get_words(ids) - return f'1. {hbold(out[0])}\n\n' \ + return f'{messages.VOTING_TITLE}\n' \ + f'1. {hbold(out[0])}\n\n' \ f'2. {hbold(out[1])}' diff --git a/bot/messages.py b/bot/messages.py index cef5438..cfdfff4 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -15,3 +15,5 @@ KB_START_PH = "Плэйсхолдер для старта бота" KB_START_TEXT = "Сообщение для старта бота" + +VOTING_TITLE = "Текст, который будет показан перед двумя вариантами в голосовании" From e12e26e572e58ba3b13b35668dd8ec1d412255ed Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 23:23:32 +0300 Subject: [PATCH 61/62] feat: add tpl for a command list --- bot/main.py | 18 +++++++++++++++++- bot/messages.py | 4 ++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bot/main.py b/bot/main.py index a86f208..9f5b250 100644 --- a/bot/main.py +++ b/bot/main.py @@ -7,7 +7,7 @@ from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import StatesGroup, State -from aiogram.types import Message +from aiogram.types import Message, BotCommand, BotCommandScopeDefault from aiogram import F from aiogram.utils.markdown import hbold @@ -21,6 +21,21 @@ bot = Bot(config.settings.bot_token, parse_mode=ParseMode.HTML) +async def _set_commands(target: Bot): + commands = [ + BotCommand( + command='start', + description=messages.COMMAND_START + ), + BotCommand( + command='help', + description=messages.COMMAND_HELP + ) + ] + + await target.set_my_commands(commands, BotCommandScopeDefault()) + + class Answer(StatesGroup): prev_id = State() @@ -59,6 +74,7 @@ def _update_counters(message: Message, ids: list[int, int]) -> None: @dp.startup() async def on_startup(): db.create_tables() + await _set_commands(bot) await bot.send_message(chat_id=config.settings.admin_id, text=messages.ON_START) diff --git a/bot/messages.py b/bot/messages.py index cfdfff4..bc7e588 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -17,3 +17,7 @@ KB_START_TEXT = "Сообщение для старта бота" VOTING_TITLE = "Текст, который будет показан перед двумя вариантами в голосовании" + +COMMAND_START = "Описание команды /start" +COMMAND_HELP = "Описание команды /help" + From 37d35725c2bc4cb237691d1606e5de376af9a86e Mon Sep 17 00:00:00 2001 From: Dan Sazonov Date: Sun, 29 Oct 2023 23:26:28 +0300 Subject: [PATCH 62/62] chore: reformat code --- bot/db.py | 2 +- bot/keyboards.py | 1 + bot/main.py | 8 ++++---- bot/messages.py | 1 - bot/models.py | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bot/db.py b/bot/db.py index cbd2c48..1a16495 100644 --- a/bot/db.py +++ b/bot/db.py @@ -1,7 +1,7 @@ from datetime import datetime -from models import models_list, db, Word, User, prev_state import services +from models import models_list, db, Word, User, prev_state def _add_words() -> None: diff --git a/bot/keyboards.py b/bot/keyboards.py index 3b74dc0..ead854e 100644 --- a/bot/keyboards.py +++ b/bot/keyboards.py @@ -1,4 +1,5 @@ from aiogram import types + import messages _kb_on_start = [ diff --git a/bot/main.py b/bot/main.py index 9f5b250..7475e28 100644 --- a/bot/main.py +++ b/bot/main.py @@ -2,19 +2,19 @@ import logging import sys -from aiogram import Bot, Dispatcher, Router, types +from aiogram import Bot, Dispatcher +from aiogram import F from aiogram.enums import ParseMode from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import StatesGroup, State from aiogram.types import Message, BotCommand, BotCommandScopeDefault -from aiogram import F from aiogram.utils.markdown import hbold import config -import messages import db -from bot import services +import messages +import services from keyboards import keyboard_voting, keyboard_start dp = Dispatcher() diff --git a/bot/messages.py b/bot/messages.py index bc7e588..77604a2 100644 --- a/bot/messages.py +++ b/bot/messages.py @@ -20,4 +20,3 @@ COMMAND_START = "Описание команды /start" COMMAND_HELP = "Описание команды /help" - diff --git a/bot/models.py b/bot/models.py index adca93b..5791db1 100644 --- a/bot/models.py +++ b/bot/models.py @@ -1,6 +1,7 @@ import os from peewee import Model, PrimaryKeyField, BigIntegerField, DateTimeField, IntegerField, CharField, SqliteDatabase + import config prev_state = os.path.isfile(config.DB_FILE)