1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

Initial commit

This commit is contained in:
MoonTestUse1
2024-12-23 19:27:44 +06:00
commit e81df4c87e
4952 changed files with 1705479 additions and 0 deletions

View File

@@ -0,0 +1,164 @@
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
/* Greenlet object interface */
#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
/* This is deprecated and undocumented. It does not change. */
#define GREENLET_VERSION "1.0.0"
#ifndef GREENLET_MODULE
#define implementation_ptr_t void*
#endif
typedef struct _greenlet {
PyObject_HEAD
PyObject* weakreflist;
PyObject* dict;
implementation_ptr_t pimpl;
} PyGreenlet;
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
/* C API functions */
/* Total number of symbols that are exported */
#define PyGreenlet_API_pointers 12
#define PyGreenlet_Type_NUM 0
#define PyExc_GreenletError_NUM 1
#define PyExc_GreenletExit_NUM 2
#define PyGreenlet_New_NUM 3
#define PyGreenlet_GetCurrent_NUM 4
#define PyGreenlet_Throw_NUM 5
#define PyGreenlet_Switch_NUM 6
#define PyGreenlet_SetParent_NUM 7
#define PyGreenlet_MAIN_NUM 8
#define PyGreenlet_STARTED_NUM 9
#define PyGreenlet_ACTIVE_NUM 10
#define PyGreenlet_GET_PARENT_NUM 11
#ifndef GREENLET_MODULE
/* This section is used by modules that uses the greenlet C API */
static void** _PyGreenlet_API = NULL;
# define PyGreenlet_Type \
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
# define PyExc_GreenletError \
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
# define PyExc_GreenletExit \
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
/*
* PyGreenlet_New(PyObject *args)
*
* greenlet.greenlet(run, parent=None)
*/
# define PyGreenlet_New \
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
_PyGreenlet_API[PyGreenlet_New_NUM])
/*
* PyGreenlet_GetCurrent(void)
*
* greenlet.getcurrent()
*/
# define PyGreenlet_GetCurrent \
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
/*
* PyGreenlet_Throw(
* PyGreenlet *greenlet,
* PyObject *typ,
* PyObject *val,
* PyObject *tb)
*
* g.throw(...)
*/
# define PyGreenlet_Throw \
(*(PyObject * (*)(PyGreenlet * self, \
PyObject * typ, \
PyObject * val, \
PyObject * tb)) \
_PyGreenlet_API[PyGreenlet_Throw_NUM])
/*
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
*
* g.switch(*args, **kwargs)
*/
# define PyGreenlet_Switch \
(*(PyObject * \
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
_PyGreenlet_API[PyGreenlet_Switch_NUM])
/*
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
*
* g.parent = new_parent
*/
# define PyGreenlet_SetParent \
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
/*
* PyGreenlet_GetParent(PyObject* greenlet)
*
* return greenlet.parent;
*
* This could return NULL even if there is no exception active.
* If it does not return NULL, you are responsible for decrementing the
* reference count.
*/
# define PyGreenlet_GetParent \
(*(PyGreenlet* (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
/*
* deprecated, undocumented alias.
*/
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
# define PyGreenlet_MAIN \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
# define PyGreenlet_STARTED \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
# define PyGreenlet_ACTIVE \
(*(int (*)(PyGreenlet*)) \
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
/* Macro that imports greenlet and initializes C API */
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
keep the older definition to be sure older code that might have a copy of
the header still works. */
# define PyGreenlet_Import() \
{ \
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
}
#endif /* GREENLET_MODULE */
#ifdef __cplusplus
}
#endif
#endif /* !Py_GREENLETOBJECT_H */

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,19 @@
Copyright 2005-2024 SQLAlchemy authors and contributors <see AUTHORS file>.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,242 @@
Metadata-Version: 2.1
Name: SQLAlchemy
Version: 2.0.27
Summary: Database Abstraction Library
Home-page: https://www.sqlalchemy.org
Author: Mike Bayer
Author-email: mike_mp@zzzcomputing.com
License: MIT
Project-URL: Documentation, https://docs.sqlalchemy.org
Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Database :: Front-Ends
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: typing-extensions >=4.6.0
Requires-Dist: greenlet !=0.4.17 ; platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32")))))
Requires-Dist: importlib-metadata ; python_version < "3.8"
Provides-Extra: aiomysql
Requires-Dist: greenlet !=0.4.17 ; extra == 'aiomysql'
Requires-Dist: aiomysql >=0.2.0 ; extra == 'aiomysql'
Provides-Extra: aioodbc
Requires-Dist: greenlet !=0.4.17 ; extra == 'aioodbc'
Requires-Dist: aioodbc ; extra == 'aioodbc'
Provides-Extra: aiosqlite
Requires-Dist: greenlet !=0.4.17 ; extra == 'aiosqlite'
Requires-Dist: aiosqlite ; extra == 'aiosqlite'
Requires-Dist: typing-extensions !=3.10.0.1 ; extra == 'aiosqlite'
Provides-Extra: asyncio
Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncio'
Provides-Extra: asyncmy
Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncmy'
Requires-Dist: asyncmy !=0.2.4,!=0.2.6,>=0.2.3 ; extra == 'asyncmy'
Provides-Extra: mariadb_connector
Requires-Dist: mariadb !=1.1.2,!=1.1.5,>=1.0.1 ; extra == 'mariadb_connector'
Provides-Extra: mssql
Requires-Dist: pyodbc ; extra == 'mssql'
Provides-Extra: mssql_pymssql
Requires-Dist: pymssql ; extra == 'mssql_pymssql'
Provides-Extra: mssql_pyodbc
Requires-Dist: pyodbc ; extra == 'mssql_pyodbc'
Provides-Extra: mypy
Requires-Dist: mypy >=0.910 ; extra == 'mypy'
Provides-Extra: mysql
Requires-Dist: mysqlclient >=1.4.0 ; extra == 'mysql'
Provides-Extra: mysql_connector
Requires-Dist: mysql-connector-python ; extra == 'mysql_connector'
Provides-Extra: oracle
Requires-Dist: cx-oracle >=8 ; extra == 'oracle'
Provides-Extra: oracle_oracledb
Requires-Dist: oracledb >=1.0.1 ; extra == 'oracle_oracledb'
Provides-Extra: postgresql
Requires-Dist: psycopg2 >=2.7 ; extra == 'postgresql'
Provides-Extra: postgresql_asyncpg
Requires-Dist: greenlet !=0.4.17 ; extra == 'postgresql_asyncpg'
Requires-Dist: asyncpg ; extra == 'postgresql_asyncpg'
Provides-Extra: postgresql_pg8000
Requires-Dist: pg8000 >=1.29.1 ; extra == 'postgresql_pg8000'
Provides-Extra: postgresql_psycopg
Requires-Dist: psycopg >=3.0.7 ; extra == 'postgresql_psycopg'
Provides-Extra: postgresql_psycopg2binary
Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary'
Provides-Extra: postgresql_psycopg2cffi
Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi'
Provides-Extra: postgresql_psycopgbinary
Requires-Dist: psycopg[binary] >=3.0.7 ; extra == 'postgresql_psycopgbinary'
Provides-Extra: pymysql
Requires-Dist: pymysql ; extra == 'pymysql'
Provides-Extra: sqlcipher
Requires-Dist: sqlcipher3-binary ; extra == 'sqlcipher'
SQLAlchemy
==========
|PyPI| |Python| |Downloads|
.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy
:target: https://pypi.org/project/sqlalchemy
:alt: PyPI
.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy
:target: https://pypi.org/project/sqlalchemy
:alt: PyPI - Python Version
.. |Downloads| image:: https://static.pepy.tech/badge/sqlalchemy/month
:target: https://pepy.tech/project/sqlalchemy
:alt: PyPI - Downloads
The Python SQL Toolkit and Object Relational Mapper
Introduction
-------------
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper
that gives application developers the full power and
flexibility of SQL. SQLAlchemy provides a full suite
of well known enterprise-level persistence patterns,
designed for efficient and high-performing database
access, adapted into a simple and Pythonic domain
language.
Major SQLAlchemy features include:
* An industrial strength ORM, built
from the core on the identity map, unit of work,
and data mapper patterns. These patterns
allow transparent persistence of objects
using a declarative configuration system.
Domain models
can be constructed and manipulated naturally,
and changes are synchronized with the
current transaction automatically.
* A relationally-oriented query system, exposing
the full range of SQL's capabilities
explicitly, including joins, subqueries,
correlation, and most everything else,
in terms of the object model.
Writing queries with the ORM uses the same
techniques of relational composition you use
when writing SQL. While you can drop into
literal SQL at any time, it's virtually never
needed.
* A comprehensive and flexible system
of eager loading for related collections and objects.
Collections are cached within a session,
and can be loaded on individual access, all
at once using joins, or by query per collection
across the full result set.
* A Core SQL construction system and DBAPI
interaction layer. The SQLAlchemy Core is
separate from the ORM and is a full database
abstraction layer in its own right, and includes
an extensible Python-based SQL expression
language, schema metadata, connection pooling,
type coercion, and custom types.
* All primary and foreign key constraints are
assumed to be composite and natural. Surrogate
integer primary keys are of course still the
norm, but SQLAlchemy never assumes or hardcodes
to this model.
* Database introspection and generation. Database
schemas can be "reflected" in one step into
Python structures representing database metadata;
those same structures can then generate
CREATE statements right back out - all within
the Core, independent of the ORM.
SQLAlchemy's philosophy:
* SQL databases behave less and less like object
collections the more size and performance start to
matter; object collections behave less and less like
tables and rows the more abstraction starts to matter.
SQLAlchemy aims to accommodate both of these
principles.
* An ORM doesn't need to hide the "R". A relational
database provides rich, set-based functionality
that should be fully exposed. SQLAlchemy's
ORM provides an open-ended set of patterns
that allow a developer to construct a custom
mediation layer between a domain model and
a relational schema, turning the so-called
"object relational impedance" issue into
a distant memory.
* The developer, in all cases, makes all decisions
regarding the design, structure, and naming conventions
of both the object model as well as the relational
schema. SQLAlchemy only provides the means
to automate the execution of these decisions.
* With SQLAlchemy, there's no such thing as
"the ORM generated a bad query" - you
retain full control over the structure of
queries, including how joins are organized,
how subqueries and correlation is used, what
columns are requested. Everything SQLAlchemy
does is ultimately the result of a developer-initiated
decision.
* Don't use an ORM if the problem doesn't need one.
SQLAlchemy consists of a Core and separate ORM
component. The Core offers a full SQL expression
language that allows Pythonic construction
of SQL constructs that render directly to SQL
strings for a target database, returning
result sets that are essentially enhanced DBAPI
cursors.
* Transactions should be the norm. With SQLAlchemy's
ORM, nothing goes to permanent storage until
commit() is called. SQLAlchemy encourages applications
to create a consistent means of delineating
the start and end of a series of operations.
* Never render a literal value in a SQL statement.
Bound parameters are used to the greatest degree
possible, allowing query optimizers to cache
query plans effectively and making SQL injection
attacks a non-issue.
Documentation
-------------
Latest documentation is at:
https://www.sqlalchemy.org/docs/
Installation / Requirements
---------------------------
Full documentation for installation is at
`Installation <https://www.sqlalchemy.org/docs/intro.html#installation>`_.
Getting Help / Development / Bug reporting
------------------------------------------
Please refer to the `SQLAlchemy Community Guide <https://www.sqlalchemy.org/support.html>`_.
Code of Conduct
---------------
Above all, SQLAlchemy places great emphasis on polite, thoughtful, and
constructive communication between users and developers.
Please see our current Code of Conduct at
`Code of Conduct <https://www.sqlalchemy.org/codeofconduct.html>`_.
License
-------
SQLAlchemy is distributed under the `MIT license
<https://www.opensource.org/licenses/mit-license.php>`_.

View File

@@ -0,0 +1,530 @@
SQLAlchemy-2.0.27.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
SQLAlchemy-2.0.27.dist-info/LICENSE,sha256=eYQKk6tEYK_iQW6ePf95YIdsg66dK-JwXoOhBNSXQOs,1119
SQLAlchemy-2.0.27.dist-info/METADATA,sha256=ySjjHZ2y0chER0KC_8dWxR1tJNJ6ZFKSsKn5NpUGQ4c,9844
SQLAlchemy-2.0.27.dist-info/RECORD,,
SQLAlchemy-2.0.27.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
SQLAlchemy-2.0.27.dist-info/WHEEL,sha256=ircjsfhzblqgSzO8ow7-0pXK-RVqDqNRGQ8F650AUNM,102
SQLAlchemy-2.0.27.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
sqlalchemy/__init__.py,sha256=hU_2Jtxg7KtVE1AdYJaSNwubWJozVVoKzMBzda_lewc,13327
sqlalchemy/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/__pycache__/events.cpython-311.pyc,,
sqlalchemy/__pycache__/exc.cpython-311.pyc,,
sqlalchemy/__pycache__/inspection.cpython-311.pyc,,
sqlalchemy/__pycache__/log.cpython-311.pyc,,
sqlalchemy/__pycache__/schema.cpython-311.pyc,,
sqlalchemy/__pycache__/types.cpython-311.pyc,,
sqlalchemy/connectors/__init__.py,sha256=A2AI8p63aT0jT5CsVX33xlTfiGWliOcGahlK0RyTLXg,494
sqlalchemy/connectors/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/connectors/__pycache__/aioodbc.cpython-311.pyc,,
sqlalchemy/connectors/__pycache__/asyncio.cpython-311.pyc,,
sqlalchemy/connectors/__pycache__/pyodbc.cpython-311.pyc,,
sqlalchemy/connectors/aioodbc.py,sha256=fg3xfG-5gLsy-DSyVonNNKYhOf0_lzHmixRFa5edtWI,5462
sqlalchemy/connectors/asyncio.py,sha256=DOy84rX4l0U5Nfn9dYLY8ETFE2tRiuSAZTJVPZpEl0E,6163
sqlalchemy/connectors/pyodbc.py,sha256=IG5lLCyFbnv1wB85HQuMO3S5piWHaB660OBWvBIQhbg,8750
sqlalchemy/cyextension/__init__.py,sha256=Hlfk91RinbOuNF_fybR5R2UtiIcTeUOXS66QOfSSCV0,250
sqlalchemy/cyextension/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/cyextension/collections.cp311-win_amd64.pyd,sha256=sMUnGsAsxmktVQxumYz7FORlt4p0fkcXpNvtdQl0Bfw,175104
sqlalchemy/cyextension/collections.pyx,sha256=GXPkr9cHRLW3Vcu-ik3dVBZMR-zf0Q5_K4J-_8yV-gk,12980
sqlalchemy/cyextension/immutabledict.cp311-win_amd64.pyd,sha256=Md9KeQlBLAJ9k671YhqKNg0oxyY-gGH_izUWkMV0ciE,72704
sqlalchemy/cyextension/immutabledict.pxd,sha256=5iGndSbJCgCkNmRbJ_z14RANs2dSSnAzyiRPUTBk58Y,299
sqlalchemy/cyextension/immutabledict.pyx,sha256=IhB2pR49CrORXQ3LXMFpuCIRc6I08QNvIylE1cPQA5o,3668
sqlalchemy/cyextension/processors.cp311-win_amd64.pyd,sha256=ocBdpnIueLk1-UrvAt1ieovUjd7c94wCy-eh5Bsd384,58368
sqlalchemy/cyextension/processors.pyx,sha256=V9gzqXiNHWsa5DBgYl-3KzclFHY8kXGF_TD1xHFE7eM,1860
sqlalchemy/cyextension/resultproxy.cp311-win_amd64.pyd,sha256=b8Sw6akLIwneSdTRYvga8VuWhc-zz7aX1pa3EACVuwE,60928
sqlalchemy/cyextension/resultproxy.pyx,sha256=h_RrKasbLtKK3LqUh6UiWtkumBlKtcN5eeB_1bZROMA,2827
sqlalchemy/cyextension/util.cp311-win_amd64.pyd,sha256=HZ220KIKcvLl-qn23YNUtRGL6kLMKMPOnKhtyRzR7qA,72192
sqlalchemy/cyextension/util.pyx,sha256=50QYpSAKgLSUfhFEQgSN2e1qHWCMh_b6ZNlErDUS7ec,2621
sqlalchemy/dialects/__init__.py,sha256=SJfQyxMhOL58EB-S6GQv_0jf2oP7MMfmVdlV2UxGWQo,1831
sqlalchemy/dialects/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/dialects/__pycache__/_typing.cpython-311.pyc,,
sqlalchemy/dialects/_typing.py,sha256=mN2r8mU8z-mRh4YS3VeK8Nv_IKJmE0Mb1CrJ-ptILas,913
sqlalchemy/dialects/mssql/__init__.py,sha256=r3oTfX2LLbJAGhM57wdPLWxaZBzunkcmyaTbW0FjLuY,1968
sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/base.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/json.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/provision.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-311.pyc,,
sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-311.pyc,,
sqlalchemy/dialects/mssql/aioodbc.py,sha256=b9bhUKcVj4NzoqJIDfECeE_Rmt51sRy8OOUFz_R3vpg,2086
sqlalchemy/dialects/mssql/base.py,sha256=ERGOPdJHLCF4n9L9-nBeiVQ3X-nl8ryt1FEFLxUwj1o,137336
sqlalchemy/dialects/mssql/information_schema.py,sha256=A1UJAoFb3UtE8YCY3heBgeTMkzWq3j7C2caZ3gcMGZk,8338
sqlalchemy/dialects/mssql/json.py,sha256=nZVVsgmR4Z4dNn9cv5Gucq596gsQ0MvASPuEEtz-Gek,4949
sqlalchemy/dialects/mssql/provision.py,sha256=pa-b74Xr2qsto3BFG1O0I_B25TUT3TOecg6cAKuRcf8,5517
sqlalchemy/dialects/mssql/pymssql.py,sha256=f7xqRif9Dp64de9G8yuC4OyWArwXy_oVq5X0oiwIX4E,4163
sqlalchemy/dialects/mssql/pyodbc.py,sha256=YVI19AnrqxPCBwDqcjrO_rqUUWbV2re7E8iLuV1ilqE,27801
sqlalchemy/dialects/mysql/__init__.py,sha256=PPQDwNqcpxWMt3nFQ66KefX9T9iz7d8lybEwKlfXB1U,2254
sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/base.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/dml.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/expression.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/json.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/provision.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-311.pyc,,
sqlalchemy/dialects/mysql/__pycache__/types.cpython-311.pyc,,
sqlalchemy/dialects/mysql/aiomysql.py,sha256=-YFqFQEx0M2pww3xvsOlaVKflTeoUfIsM8BgfhP1MP0,10296
sqlalchemy/dialects/mysql/asyncmy.py,sha256=h9BBhGsqPG2LWdoh0lPieRLhaQ_BJrhxwsR_G5yPLDQ,10370
sqlalchemy/dialects/mysql/base.py,sha256=LQ-nvj7HIQSntwAycof93th3kpMBatHl402HNQ8z9hc,124297
sqlalchemy/dialects/mysql/cymysql.py,sha256=0mRP3gFe2t7iJYQqJz1Os_TztFwMAF34w2MmXe-4B_w,2384
sqlalchemy/dialects/mysql/dml.py,sha256=n31-m4vfOIL0MdHpUdIfTLgaMzusfQ-yHYoJWO_ndEc,7864
sqlalchemy/dialects/mysql/enumerated.py,sha256=Nz9Sv3ENX-1T18aEoOY8QfZlAcwRf65lIOse7vwjil8,8692
sqlalchemy/dialects/mysql/expression.py,sha256=uxD1fICubfGh8BhAn6WoeS8AF6hAVEvreDShXqRZTqM,4238
sqlalchemy/dialects/mysql/json.py,sha256=i0Lrd_7VKTd3fNm6kQKzrtPERuW0JeSw7XSUWnl1HQI,2350
sqlalchemy/dialects/mysql/mariadb.py,sha256=WoNxkjiPfIbWAkrVEU9MTM7mePeLHZ2uiJsyfvcpv1s,885
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=_XZ60dSn8-iQP-qyn1Utk-lswGciuoDgpW7m7GguSVA,9016
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=1ga6IV7lVLH9BKsMh2M2wSz78l5a82BZnyRqJMaS5Qw,5854
sqlalchemy/dialects/mysql/mysqldb.py,sha256=KdSkQXgCTHfOWzaM9dlQnmb77FR9X8Io6PVWTYRb1XQ,9966
sqlalchemy/dialects/mysql/provision.py,sha256=2ecdVRnZSXy5GF3hpLtQp3T8QW-oFjtTSQgbEePDH1k,3581
sqlalchemy/dialects/mysql/pymysql.py,sha256=Kxi_A34-nbQ5UEFSmy14TXc1v43-1SZ8gE628REGTFo,4220
sqlalchemy/dialects/mysql/pyodbc.py,sha256=CZCEnhyLIgbuiAW32Cw7N1m1aiQv1eBB34pV-txOs70,4435
sqlalchemy/dialects/mysql/reflection.py,sha256=wn8qKHxDb9Dnr8zC_uEgAVjk2lVuObvqPOEiad8568c,23499
sqlalchemy/dialects/mysql/reserved_words.py,sha256=s59cfdGTmlXn3GFCnevugDsc3qiiZn8tL31lief0tvo,9721
sqlalchemy/dialects/mysql/types.py,sha256=2du4p4PnuJgLHvdofFYP5dtkicIDJYeEdrFBhe7uotQ,25040
sqlalchemy/dialects/oracle/__init__.py,sha256=_yFT_k0R6yc7MKQG-Al9QZt8wYZsiCtpkhNlba5xqn8,1560
sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/dialects/oracle/__pycache__/base.cpython-311.pyc,,
sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-311.pyc,,
sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-311.pyc,,
sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-311.pyc,,
sqlalchemy/dialects/oracle/__pycache__/provision.cpython-311.pyc,,
sqlalchemy/dialects/oracle/__pycache__/types.cpython-311.pyc,,
sqlalchemy/dialects/oracle/base.py,sha256=haztCHFbuhnhpcBUr8h1PDLXAavdwtpIjgOg_PSDI_A,121486
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=3Tx3DKvqcCKyXupBuCiCL4B8D5TDO934Q7LYsLJjlkk,57058
sqlalchemy/dialects/oracle/dictionary.py,sha256=tmAZLEACqBAPBE0SEV2jr1R4aPcpNOrbomJl-UmgiR4,20026
sqlalchemy/dialects/oracle/oracledb.py,sha256=kuw08rp-tXKPOtGGutqcs5o2gvRptQXAzNBqPVZoLxg,9798
sqlalchemy/dialects/oracle/provision.py,sha256=KKlXDQnC8n6BjLJWA7AJg3lwXluH1OyStqfP2Uf9rq0,8524
sqlalchemy/dialects/oracle/types.py,sha256=U9EReFRcr0PiwOxT9vg2cA7WOix8LQ2sVp0gRkMHcPo,8518
sqlalchemy/dialects/postgresql/__init__.py,sha256=nfP-oId_1wzvRKo5TM6f2xrXry-IxoMkVd4Dga7LDg8,4062
sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/array.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/base.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/json.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/__pycache__/types.cpython-311.pyc,,
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=fYFqLVxNxAqh3nOvzGOv3Pfpm2BsclHrk71MJZrpJKo,5883
sqlalchemy/dialects/postgresql/array.py,sha256=E69zMO2h7NQFjnWEeFX4fGYDymTbqHAI-laLfy7WrEA,14137
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=CpFNMxOqJUvviWxbEyMiJKnBXTMcvN0IFwaka2gH4R0,41445
sqlalchemy/dialects/postgresql/base.py,sha256=x3xx5650ZKYAq9ShqDGQNCkj5nt3HNQXv0-Xq0ZT1Ro,181406
sqlalchemy/dialects/postgresql/dml.py,sha256=uMiqxEkji-UXqk8gO1ramQEvEfCugYmy8Cv1cnG7DQs,11522
sqlalchemy/dialects/postgresql/ext.py,sha256=ct6NQfMAfBnLYhybpF2wPEq-p8-U0tEpy-aq8NwqJLw,16758
sqlalchemy/dialects/postgresql/hstore.py,sha256=4jAZQMPWl3VE4weDRZrgrbVDRZJTM3X0Xj4twr5znYQ,11938
sqlalchemy/dialects/postgresql/json.py,sha256=TCw4qYTeLhxjDrF1sq3k-yk7UFEhryweKEj8AelBfRc,11537
sqlalchemy/dialects/postgresql/named_types.py,sha256=aaH5fNMZp8ZdmLI1ag9g0UgDGVvX0dpE0PbTu3XVUYc,17595
sqlalchemy/dialects/postgresql/operators.py,sha256=iyZuyx_daRyJjiS5rw-XnZlaWj1bmRiHdy5MXzBrFZw,2937
sqlalchemy/dialects/postgresql/pg8000.py,sha256=TPJXX078vW0FSwZ-DlWNkEOXg7Z4xk8IFwi1droMhPw,19302
sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=rNWjbOOC3SB2jmFAwz5VkbdZub7BCTp60YUmJt3eeRI,9178
sqlalchemy/dialects/postgresql/provision.py,sha256=Uo_6vxVzFryFjLqsrvesRO55VqHrnsAs_pBH_8JtFcA,5937
sqlalchemy/dialects/postgresql/psycopg.py,sha256=zRoaG1ggAKnMBo6fxe6xJa52BxyU2OrOGX-_jDrH2Zk,23113
sqlalchemy/dialects/postgresql/psycopg2.py,sha256=xau5nXatjvPNbzqTF8GF_wrwU4Q5T2zkIMGZkhHvrI0,32483
sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=hFg-9GH08ApPy3foVPUdJKwCEzNSv2zD5l4nH97AqgI,1817
sqlalchemy/dialects/postgresql/ranges.py,sha256=oiTmnZ-hd5WqqGNsXbuOJfoNxpbso_M_49gky8dlCrE,33978
sqlalchemy/dialects/postgresql/types.py,sha256=pd1QmuGwJFLqpY2tK-Ql3FNjtT1Ha-lVvfaR9dimvHc,7603
sqlalchemy/dialects/sqlite/__init__.py,sha256=MmQfjHun1U_4q-Dq_yhs9RzAX0VLixSwWeY5xWiDwag,1239
sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/base.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/json.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-311.pyc,,
sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=TgobCILLu2mjGvDgMTxX3-CPxkj_c5LDYRDJHo5W5qg,12701
sqlalchemy/dialects/sqlite/base.py,sha256=8Ft5tZeT1lHiWliTwoJaNslf8PRITVKKWhjhhRCeVDk,99576
sqlalchemy/dialects/sqlite/dml.py,sha256=8JV6Ise7WtmFniy590X5b19AYZcE51M6N5hef7d9JoA,8683
sqlalchemy/dialects/sqlite/json.py,sha256=-9afZnBt07vInCX20CKzjlTG85wHTO5_cxhcYU4phDc,2869
sqlalchemy/dialects/sqlite/provision.py,sha256=nAXZPEjXFrb6a1LxXZMqKmkQoXgl3MPsSHuMyBQ76NU,5830
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=p0KfzHBwANDMwKTKEJCjR5RxMYqQwS4E8KXjl3Bx6Fw,5511
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=s1eL04d6fqPeUNSTnpxXSjWR8OdCuYcy9ilk7716wzU,28653
sqlalchemy/dialects/type_migration_guidelines.txt,sha256=gyh3JCauAIFi_9XEfqm3vYv_jb2Eqcz2HjpmC9ZEPMM,8384
sqlalchemy/engine/__init__.py,sha256=93FWhb62dLCidc6e4FE65wq_P8GeoWQG1OG6RZMBqhM,2880
sqlalchemy/engine/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/_py_processors.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/_py_row.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/_py_util.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/base.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/characteristics.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/create.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/cursor.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/default.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/events.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/interfaces.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/mock.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/processors.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/reflection.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/result.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/row.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/strategies.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/url.cpython-311.pyc,,
sqlalchemy/engine/__pycache__/util.cpython-311.pyc,,
sqlalchemy/engine/_py_processors.py,sha256=-jlAYPM6etmuKeViiI7BD41kqY0Pr8nzaox22TPqCCQ,3880
sqlalchemy/engine/_py_row.py,sha256=UEGCjAeRsggcUn0QB0PdFC82kuykrOiOZ1KGq_Gf_qQ,3915
sqlalchemy/engine/_py_util.py,sha256=nh1XoVq1b-eGgkdzbqFqzje0RNSmVWotoa6yaB7J5Sw,2558
sqlalchemy/engine/base.py,sha256=0VnqL5IK8rTjs7wanSKgOjuzbKdNjs_8TvzJgUqfNCw,125393
sqlalchemy/engine/characteristics.py,sha256=q_l1-KybkM5Y-5hG74NuQBxHRkidBstqpRBlFLxJPqs,2671
sqlalchemy/engine/create.py,sha256=IC_D9X-t-MN78Ro3shJl4RL0fP9eTvskl4j1bAjagco,33736
sqlalchemy/engine/cursor.py,sha256=nCEzaLslgar67EDE4mqowuJsuiBaCbs5ix7sgD9iWTI,76593
sqlalchemy/engine/default.py,sha256=oB9T6i1k7rMxat1HkDGCf-_i_3eAOymV1yPU4k0tBeU,87011
sqlalchemy/engine/events.py,sha256=e0VHj69fH20sB7gocBhr5Rs2FjR8ioY4iE8VQt70oJg,38332
sqlalchemy/engine/interfaces.py,sha256=G29dfsTCNayoIiRnCQ64y9y9w9wtrGwxnm5t102ApvA,116079
sqlalchemy/engine/mock.py,sha256=wInBRiHwydTc5ELQLivdezDd1ikbSMVXgLVzZrSC0iQ,4310
sqlalchemy/engine/processors.py,sha256=w4MiVMlU6VvfhIW49nygbHcwX8FteGpz7g3IGEqtZb8,2440
sqlalchemy/engine/reflection.py,sha256=TO-tymk7BsfAzc6Fi0GmwtYyOWjfMGkyvytJyMVz_oY,77216
sqlalchemy/engine/result.py,sha256=U245Q3kGUOugqjmv-qSkx8eyDn27fLYV5agIoBHXQCA,79985
sqlalchemy/engine/row.py,sha256=g7ZqmsqX_BtRUzY-zfXoZZ4-5xZ_KJEVbvqKHUIlqRg,12433
sqlalchemy/engine/strategies.py,sha256=fD4DJn0AD371wlUa7s5Sy4j7QtgGyP7gMy_kUyqCLDQ,461
sqlalchemy/engine/url.py,sha256=tOCRmKkqrpsIfNeSDoy6KKTLtQAMtoIn9xa5kmJQebk,31694
sqlalchemy/engine/util.py,sha256=GDoT1xzoCL_CaBvWpBigzXIpUsFAFtjHH8v2e_au7qo,5833
sqlalchemy/event/__init__.py,sha256=09qZzHwt0PkIDsPwuPUVJvNakjtCBjuUJeY0AEJ9j7k,1022
sqlalchemy/event/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/event/__pycache__/api.cpython-311.pyc,,
sqlalchemy/event/__pycache__/attr.cpython-311.pyc,,
sqlalchemy/event/__pycache__/base.cpython-311.pyc,,
sqlalchemy/event/__pycache__/legacy.cpython-311.pyc,,
sqlalchemy/event/__pycache__/registry.cpython-311.pyc,,
sqlalchemy/event/api.py,sha256=74Z-EXtNsv8fvsf8m8bgCkVzJf7QzhMaVXJhCylegLo,8452
sqlalchemy/event/attr.py,sha256=-SHjzXMOs7IICPSgNwpgRS3FIEeLIpB5PyvVlpw8Gp8,21406
sqlalchemy/event/base.py,sha256=haAsH-KuvvY52A6OjwATfvCXy3hdYGgZxkDZiOqbMvI,15416
sqlalchemy/event/legacy.py,sha256=a8VEvS83PvgbomNnaSa3okZmTkxl_buZ7Lfilechjh8,8473
sqlalchemy/event/registry.py,sha256=f31k0FLqIlWpOK9tksiYXnv-yuZPPz9iLQqvKEYV7ko,11221
sqlalchemy/events.py,sha256=OAy8TK21lWzSe8bDUnAbmsP82bsBYy0LL19hR6y3BrM,542
sqlalchemy/exc.py,sha256=k01TD2xp2BM3DrXdo2U5r8yuRfsoqBND4kwvtD1SVN0,24806
sqlalchemy/ext/__init__.py,sha256=YbMQmRS_9HxRyWM-KA_F76WOss1_Em1ZcrnQDIDXoOc,333
sqlalchemy/ext/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/associationproxy.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/automap.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/baked.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/compiler.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/horizontal_shard.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/hybrid.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/indexable.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/instrumentation.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/mutable.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/orderinglist.cpython-311.pyc,,
sqlalchemy/ext/__pycache__/serializer.cpython-311.pyc,,
sqlalchemy/ext/associationproxy.py,sha256=MBtGwISA4wwT9i6op8jfY6u9lCUYn_4JCqtxZpjggok,67776
sqlalchemy/ext/asyncio/__init__.py,sha256=tKYIrERYf8hov9m8DuKWRO_53qhrvj2jRmIYjSGQ2Po,1342
sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/ext/asyncio/__pycache__/base.cpython-311.pyc,,
sqlalchemy/ext/asyncio/__pycache__/engine.cpython-311.pyc,,
sqlalchemy/ext/asyncio/__pycache__/exc.cpython-311.pyc,,
sqlalchemy/ext/asyncio/__pycache__/result.cpython-311.pyc,,
sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-311.pyc,,
sqlalchemy/ext/asyncio/__pycache__/session.cpython-311.pyc,,
sqlalchemy/ext/asyncio/base.py,sha256=slWQTFdgQQlkzrnx3m5a9xT8IRg4iM0gkEbypXr_YXQ,9184
sqlalchemy/ext/asyncio/engine.py,sha256=VKp3nRnmzl7cEIWSxJNJQMK8CbQbigmrvyqpIiVUdgY,49398
sqlalchemy/ext/asyncio/exc.py,sha256=0awLfUB4PhEPVVTKYluyor1tW91GPZZnvdQ-GGSOmJY,660
sqlalchemy/ext/asyncio/result.py,sha256=MtKAqA7hwYIdkpRxlCgHNYYzlB7dvqCtEp-aoDdFjDA,31370
sqlalchemy/ext/asyncio/scoping.py,sha256=CiMQ7ewPNsyEtl9aGOiEZOrUaNYOTP_LrR0_xkdV3r8,54211
sqlalchemy/ext/asyncio/session.py,sha256=gVdecaNFy8fo4YXhfhgwmMpezGaNKsIKL5kSI_j4GoI,64821
sqlalchemy/ext/automap.py,sha256=8mu3_-s4DUnWfmOAZsWFFaADZALSIe1J3NQS2BvUGi8,63041
sqlalchemy/ext/baked.py,sha256=jc6vPocoXXsvdZsOsqgT4kG6guWSZD1TdPjoRBmkbRU,18381
sqlalchemy/ext/compiler.py,sha256=PbvelWqZdzL6y1C6rEc8ledF79t_04MtYV26RUwNhik,20946
sqlalchemy/ext/declarative/__init__.py,sha256=MHSOffOS4MWcqshAuLNQv0vDXpK_Z3lpGXTm1riyLls,1883
sqlalchemy/ext/declarative/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/ext/declarative/__pycache__/extensions.cpython-311.pyc,,
sqlalchemy/ext/declarative/extensions.py,sha256=aPpW0PvTKH3CoSMhsOY5GcUMZOVq-OFsV1hflxmb3Lw,20095
sqlalchemy/ext/horizontal_shard.py,sha256=V8vXEt5ZQb_PM39agZD2IyoQNGSqVI1MhY-6mNV5MRY,17231
sqlalchemy/ext/hybrid.py,sha256=Fc73iUTCJuHcz3McvD3FBbjEvDag1Jw8THY6rL-phA8,53946
sqlalchemy/ext/indexable.py,sha256=aDlVpN4rilRrer9qKg3kO7fqnqB5NX4M5qzYuYM8pvw,11373
sqlalchemy/ext/instrumentation.py,sha256=lFsJECWlN1oc1E0r9TaQDZcxAx4VOz6PSHYrl5fLk9Y,16157
sqlalchemy/ext/mutable.py,sha256=nAz3_lF2xkYSARt7GAWQh-OUMcnpe6s1ocjvQGxCPkc,38428
sqlalchemy/ext/mypy/__init__.py,sha256=aqT8_9sNwzC8PIaEZ4zkCYGBvYPaDD3eCgJtJuk3g6A,247
sqlalchemy/ext/mypy/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/ext/mypy/__pycache__/apply.cpython-311.pyc,,
sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-311.pyc,,
sqlalchemy/ext/mypy/__pycache__/infer.cpython-311.pyc,,
sqlalchemy/ext/mypy/__pycache__/names.cpython-311.pyc,,
sqlalchemy/ext/mypy/__pycache__/plugin.cpython-311.pyc,,
sqlalchemy/ext/mypy/__pycache__/util.cpython-311.pyc,,
sqlalchemy/ext/mypy/apply.py,sha256=1Qb-_FpQ_0LVB2KFA5hVjfPv6DDMIcxXe86Ts1X9GBk,10870
sqlalchemy/ext/mypy/decl_class.py,sha256=f2iWiFVlDFqGb_IoGGotI3IEOUErh25sLT7B_cMfx0g,17899
sqlalchemy/ext/mypy/infer.py,sha256=O-3IjELDSBEAwGGxRM7lr0NWwGD0HMK4vda_iY6iwjs,19959
sqlalchemy/ext/mypy/names.py,sha256=TWsrp5ftD5p0NeyAipgYIir9SUbA4U0BAk0W2FXA3VA,10972
sqlalchemy/ext/mypy/plugin.py,sha256=TDTziLsYFRqyX8UcQMtBBa6TFR4z9N-XNO8wRkHlEOI,10053
sqlalchemy/ext/mypy/util.py,sha256=3iQ1zVpXSUoj2aHa-Kkg4O83JOzqVd8TDEwpZj3SWWs,9786
sqlalchemy/ext/orderinglist.py,sha256=r7La_3nZlGevIgsBL1IB30FvWO_tZHlTKo_FWwid-aY,14800
sqlalchemy/ext/serializer.py,sha256=_7gottqRCI-qkW4Go4o2EnOSnieKDCQ8jQ6muHXw-RM,6363
sqlalchemy/future/__init__.py,sha256=6-qPdjMHX-V-kAPjTQgNuHztmYiwKlJhKhhljuETvoQ,528
sqlalchemy/future/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/future/__pycache__/engine.cpython-311.pyc,,
sqlalchemy/future/engine.py,sha256=N_5W2ab5-ueedWzqNdgLPzTW9audT1IbxF6FCDLRZOc,510
sqlalchemy/inspection.py,sha256=GpmMuSAZ53u4W__iGpvzQKCBMFnTxnHt4Lo7Nq1FSKM,5237
sqlalchemy/log.py,sha256=Sg6PGR_wmseiCCpJfRDEkaMs08XTPPsf0X_iYJLvzS0,8895
sqlalchemy/orm/__init__.py,sha256=I-XesvuyjkAAwnsiF5FnXRLNV6W2nW70EnGAIt2GAjU,8633
sqlalchemy/orm/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/_orm_constructors.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/_typing.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/attributes.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/base.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/bulk_persistence.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/clsregistry.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/collections.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/context.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/decl_api.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/decl_base.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/dependency.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/descriptor_props.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/dynamic.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/evaluator.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/events.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/exc.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/identity.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/instrumentation.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/interfaces.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/loading.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/mapped_collection.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/mapper.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/path_registry.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/persistence.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/properties.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/query.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/relationships.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/scoping.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/session.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/state.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/state_changes.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/strategies.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/strategy_options.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/sync.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/unitofwork.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/util.cpython-311.pyc,,
sqlalchemy/orm/__pycache__/writeonly.cpython-311.pyc,,
sqlalchemy/orm/_orm_constructors.py,sha256=qOQLU_Rly-AYjGDotwZv65PCjjLgdAwqHGPUeZrTYfE,101822
sqlalchemy/orm/_typing.py,sha256=Z9GZT8Vb-wFwvHeOeVE37dvmCWdItLZnqI_pLin4cMc,5152
sqlalchemy/orm/attributes.py,sha256=devlqbjcICNLnG0HEg4wSP2sgm2m8sW9RpoQZuaU0KM,95355
sqlalchemy/orm/base.py,sha256=JWzk2w1k-xT5BW8T7K0DRhMg7lDKFf4T6KrFey_6c4A,28394
sqlalchemy/orm/bulk_persistence.py,sha256=z7yTdor_Nea7R0UMu8kloKvN-t0z2AZ-P-q5FE-oabc,72070
sqlalchemy/orm/clsregistry.py,sha256=ZumBI7I2O-l93LbA4eyMKm0w6al-nNS2QV1VDcJxGko,18528
sqlalchemy/orm/collections.py,sha256=lHjP6uDz0WdwedTqyh_8R2_nzRAK_5ONCIoisHrsb94,53797
sqlalchemy/orm/context.py,sha256=bBieTIPsM10lt5z5Feq4tDjBqpznbsssaiXh7OrTh18,115244
sqlalchemy/orm/decl_api.py,sha256=Xd9s8A7V_jlypP-u-tnpQ5o6Oq-v1A4H1f3hSEj4-Bc,65549
sqlalchemy/orm/decl_base.py,sha256=g6bhTg5M9hsf3JVnMaqn1NS7MTdBJY--L-qf9aQabgs,83753
sqlalchemy/orm/dependency.py,sha256=glstmbB4t-PIRA47u9NgTyyxbENfyQuG9Uzj2iezB_s,48935
sqlalchemy/orm/descriptor_props.py,sha256=PpDt83EX72AhCbBjixQDpYG1P1MqWqGdA-bJpXMSxSw,38254
sqlalchemy/orm/dynamic.py,sha256=m7V2GPS5__4y_hP7BQjD66b6BVEre1pzPwaj2bmRFRM,10084
sqlalchemy/orm/evaluator.py,sha256=gQIDxuoB5Ut2hiFdN768U67JusVkbFt-Bdna_T8npPA,12293
sqlalchemy/orm/events.py,sha256=EeIAGfdSQX0XQLZyURz94KYOlsjwI-ANWjPfyN_-jcQ,130956
sqlalchemy/orm/exc.py,sha256=90xWOIIAmzPguaVH6mxr2xUSGW44aGIPz5WytJSwmR4,7583
sqlalchemy/orm/identity.py,sha256=fOpANTf73r12F_w9DhVoyjkAdh8ldgJcNnwxx0GY8YM,9551
sqlalchemy/orm/instrumentation.py,sha256=a8vi3qEAyO7Z9PYksLkFi_YzxqQhzB-anblegiAtsFw,25075
sqlalchemy/orm/interfaces.py,sha256=IKCWZFHamXIkpBCaEh7YIYy-Jz2hV4-SQt6kGbfOuE8,49842
sqlalchemy/orm/loading.py,sha256=sp7VaIoc9gzrtAOekU_2EWYN3L_9lKuiKkfn6f4VlQQ,59202
sqlalchemy/orm/mapped_collection.py,sha256=AeSzQwj56cLr1tVMC0B-3JsC74IAP7_gbr-EPc4_2uw,20250
sqlalchemy/orm/mapper.py,sha256=13goncHEJueS73Z7EWovVdp1W-xBKSWB5cjqtpyIk5s,175479
sqlalchemy/orm/path_registry.py,sha256=KqS4yYe__beUSpdEjL8qxzL_z7V-FJkolm4AMCBHhGg,26658
sqlalchemy/orm/persistence.py,sha256=MKb7TuSLJUQpyqnHxf6uNmGXSznmZgkkFTD04nHbNUQ,63483
sqlalchemy/orm/properties.py,sha256=xtSDAAeUgDAmZF_OTBd4F18q9h3h7JWnPtQnWU0-aU4,30007
sqlalchemy/orm/query.py,sha256=d2aENAsXkiZuXOxZ5WNKMqsrNHT7_d-_BgXqNa2BtsA,120948
sqlalchemy/orm/relationships.py,sha256=jtIA6Y7jhlSzee-MGu_0YMmtH8Kr4lwanjCBfB_eV_I,131177
sqlalchemy/orm/scoping.py,sha256=aAQMIAAZ-M_m6UGndmkUiDazcphE-klw6wZjFT2Az7E,80842
sqlalchemy/orm/session.py,sha256=QRC0WQSjNFIhyInWnZD-picMoKWS79oIlQAVAcRon_4,198419
sqlalchemy/orm/state.py,sha256=9opH8AR6LnbCRmW1lN2RxEQyxnEi1rcDXlySqrDeUiw,38656
sqlalchemy/orm/state_changes.py,sha256=4i90vDgBGvVGUzhlonlBkZBAZFOWaAXij2X8OEA3-BA,7013
sqlalchemy/orm/strategies.py,sha256=GFppPikNxZJdsi4DW1HcU6pv-EvIeFvTI8K_LDY5nmk,117550
sqlalchemy/orm/strategy_options.py,sha256=cBzotMlredMZ8vM6T04mTnskiS0qSoRkHEQKSLZNARA,86734
sqlalchemy/orm/sync.py,sha256=aMEMhYTj2rtJZJvjqm-cUx2CoQxYl8P6YddCLpLelhM,5943
sqlalchemy/orm/unitofwork.py,sha256=THggzzAaqmYh5PBDob5dHTP_YyHXYdscs3fIxtRV-gE,27829
sqlalchemy/orm/util.py,sha256=xIaltctFxy-u4xdLk1mqdHYsq_bvg4xsaTRQTev7I80,82756
sqlalchemy/orm/writeonly.py,sha256=j5DcpZKOv1tLGQLhKfk-Uw-B0yEG7LezwJWNTq0FtWQ,22983
sqlalchemy/pool/__init__.py,sha256=ZKUPMKdBU57mhu677UsvRs5Aq9s9BwIbMmSNRoTRPoY,1848
sqlalchemy/pool/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/pool/__pycache__/base.cpython-311.pyc,,
sqlalchemy/pool/__pycache__/events.cpython-311.pyc,,
sqlalchemy/pool/__pycache__/impl.cpython-311.pyc,,
sqlalchemy/pool/base.py,sha256=D0sKTRla6wpIFbELyGY2JEHUHR324rveIl93qjjmYr8,53751
sqlalchemy/pool/events.py,sha256=ysyFh0mNDpL4N4rQ-o_BC6tpo_zt0_au_QLBgJqaKY8,13517
sqlalchemy/pool/impl.py,sha256=8VcM4JSUnu4FcSrC5TUzTWT0FYFxfNouKyuXCKnD6KM,18264
sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
sqlalchemy/schema.py,sha256=UFhZjGmYoqN3zkId7M4CbVCd8KaeZUfKUjdlk0sHQ_E,3264
sqlalchemy/sql/__init__.py,sha256=T16ZB3Za0Tq1LQGXeJuuxDkyu2t-XHR2t-8QH1mE1Uw,5965
sqlalchemy/sql/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/_dml_constructors.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/_elements_constructors.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/_orm_types.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/_py_util.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/_typing.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/annotation.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/base.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/cache_key.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/coercions.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/compiler.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/crud.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/ddl.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/default_comparator.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/dml.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/elements.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/events.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/expression.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/functions.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/lambdas.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/naming.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/operators.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/roles.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/schema.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/selectable.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/sqltypes.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/traversals.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/type_api.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/util.cpython-311.pyc,,
sqlalchemy/sql/__pycache__/visitors.cpython-311.pyc,,
sqlalchemy/sql/_dml_constructors.py,sha256=1xMH5Kd6SLhlFwfIs_lOXGC8GTrqW8mQM7Kc3cKyLuw,4007
sqlalchemy/sql/_elements_constructors.py,sha256=DN5B84YTp9K5cfp1qjEpg_2d5WpHujlNBJ-pG7bsKoI,64386
sqlalchemy/sql/_orm_types.py,sha256=_bzlAh3-vTIZoLvAM2ry1SF7rsYRM3-jupfhGWZZn5Y,645
sqlalchemy/sql/_py_util.py,sha256=VzThcXk7fKqT9_mZmXrkxePdwyyl_wIciCftzl2Z_-g,2248
sqlalchemy/sql/_selectable_constructors.py,sha256=mRgtlGyctlb1LMBqFxgn0eGzIXMbyZtQafjUuJWhYjs,19415
sqlalchemy/sql/_typing.py,sha256=i4COgky9Gv0ArXdOpp9xfxpvKqP3lj0I_vJqinvWEck,12940
sqlalchemy/sql/annotation.py,sha256=PslN1KQV9hN8Ji4k8I3-W-cDuRMCCLwMmJcg-n86Yy4,18830
sqlalchemy/sql/base.py,sha256=Twa1DYB1fBGGzU0kIVS0h30-0j67gfduuw9GU6RxGao,75935
sqlalchemy/sql/cache_key.py,sha256=vUWB-pqAtgt8SBRMHEkzo8BQ_1yXjMeGt-aaDB1ieek,34605
sqlalchemy/sql/coercions.py,sha256=O6PA7Gzhr9XQedJs3zIngCivN1vcrNyEhFueN5CqriI,41882
sqlalchemy/sql/compiler.py,sha256=PslsNi8ND2FXVPcFIS3UBMpVthfM47MAu4c_rpBpMHE,278910
sqlalchemy/sql/crud.py,sha256=I5nPPnujtNKHC5C2v1vW4A0mbyomwChT21IYOX3z5fw,58190
sqlalchemy/sql/ddl.py,sha256=NbW8F3UT4BTnda5w5TNPGxXPtv0wHSNB51hhr4gBSJM,46980
sqlalchemy/sql/default_comparator.py,sha256=lXmd8yAUzfyeP5w4vebrQG99oC0bTrmdGc0crBq1GKw,17259
sqlalchemy/sql/dml.py,sha256=lt5FC6BbJNotE65U-fmvEovBxkADfKBnVcnkVYYQxUM,67431
sqlalchemy/sql/elements.py,sha256=tIgio7vC-0gftEddMtUu35cXIWwPqqomat0ufAIFrMM,177377
sqlalchemy/sql/events.py,sha256=pG3jqJbPX18N9Amp47aXiQYMYD_HL_lOXHk-0m8m7Hw,18745
sqlalchemy/sql/expression.py,sha256=T-AgCPp30tgKQYLKeSyqQg_VoJFE69m2yDTz6fn-u1E,7748
sqlalchemy/sql/functions.py,sha256=vxYsWwzQpYhfQ_EwfdA-lGlbh2pkQ30AXGjvHEvVBWo,65741
sqlalchemy/sql/lambdas.py,sha256=i3F6TZEAHSPqTV704LAybfNOMyUJ52x2YE2eCHTlYi4,50738
sqlalchemy/sql/naming.py,sha256=ERVjqo6fBHBw2BwNgpbb5cvsCkq1jjdztczP9BKzVt8,7070
sqlalchemy/sql/operators.py,sha256=6rpSbuFon7iIUCT4SDowYctDyOmFpe2-FdLu2HIX3x8,78508
sqlalchemy/sql/roles.py,sha256=8nO4y1hbP1cA8IzeOn6uPgNZNVILb3E-IMeJWOIScu8,7985
sqlalchemy/sql/schema.py,sha256=iIurzYqmZNRi_wBN-tXFKIM-jt07DIvVuzV_IVsfsTo,234377
sqlalchemy/sql/selectable.py,sha256=fWcddtd9UM3QMcS-3Pg6E98mK9uAZGkhmyOLWlNEigI,239761
sqlalchemy/sql/sqltypes.py,sha256=AJvAe9Nt3Bweic9eC__NVnkVAbIgb_exoajEfTij1R4,130912
sqlalchemy/sql/traversals.py,sha256=p2iXAQc0FvV-l1Q3NNMxIhRYTm8U3Ul630jG3Ys6qCI,34611
sqlalchemy/sql/type_api.py,sha256=zRtzrf5sLjDWnSUvl_vAnG6X8fhY8vuln4jG_Jx4zKY,86164
sqlalchemy/sql/util.py,sha256=ftTiyNGeJK0MIRMqWMV7Xf8iZuiRGocoJRp3MIO3F3Y,49563
sqlalchemy/sql/visitors.py,sha256=oudlabsf9qleuC78GFe_iflRSAD8H-HjaM7T8Frc538,37482
sqlalchemy/testing/__init__.py,sha256=8iT66v5k4J9RmquaH4GLI2DjEA7c_JZSTVig-uuBNw8,3221
sqlalchemy/testing/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/assertions.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/assertsql.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/asyncio.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/config.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/engines.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/entities.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/exclusions.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/pickleable.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/profiling.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/provision.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/requirements.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/schema.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/util.cpython-311.pyc,,
sqlalchemy/testing/__pycache__/warnings.cpython-311.pyc,,
sqlalchemy/testing/assertions.py,sha256=bBn2Ep89FF-WBmzh0VkvnJ9gNMKuqk8OXq7ALpUwar4,32428
sqlalchemy/testing/assertsql.py,sha256=gj4YRBR9cjOtS1WgR3nsyIze1tmqctsNs1uCV8N2Q4w,17333
sqlalchemy/testing/asyncio.py,sha256=GvWrQFrL3xz7rub61oGOS2PXVvw7D9Id3gtkXQjZJLY,3858
sqlalchemy/testing/config.py,sha256=jfFVUiAOm8im6SlqyAdZVSaA51kmADgfBDqrHnngH7c,12517
sqlalchemy/testing/engines.py,sha256=U3FkWECbghiK2_Yv5uKMjco377xoFsi75WZgRZroGWA,13814
sqlalchemy/testing/entities.py,sha256=Um-DFSz81p06DhTK899ZRUOZRw3FtUDeNMVHcIg3eLc,3471
sqlalchemy/testing/exclusions.py,sha256=8kjsaFfjCvPlLsQLD_LIDwuqvVlIVbD5qTWBlKdtNkM,12895
sqlalchemy/testing/fixtures/__init__.py,sha256=B1IFCzEVdCqhEvFrLmgxZ_Fr08jDus5FddSA-lnnAAU,1226
sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/testing/fixtures/__pycache__/base.cpython-311.pyc,,
sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-311.pyc,,
sqlalchemy/testing/fixtures/__pycache__/orm.cpython-311.pyc,,
sqlalchemy/testing/fixtures/__pycache__/sql.cpython-311.pyc,,
sqlalchemy/testing/fixtures/base.py,sha256=S0ODuph0jA2Za4GN3NNhYVIqN9jAa3Q9Vd1N4O4rcTc,12622
sqlalchemy/testing/fixtures/mypy.py,sha256=2H8QxvGvwsb_Z3alRtvCvfXeqGjOb8aemfoYxQiuGMc,12285
sqlalchemy/testing/fixtures/orm.py,sha256=6JvQpIfmgmSTH3Hie4nhmUFfvH0pseujIFA9Lup2Dzw,6322
sqlalchemy/testing/fixtures/sql.py,sha256=Joqh4q1vE3wCaE3eDZUnSobeLNUE-pabIy58ZMURFto,16196
sqlalchemy/testing/pickleable.py,sha256=uYLl557iNep6jSOVl0vK1GwaLHUKidALoPJc-QIrC08,2988
sqlalchemy/testing/plugin/__init__.py,sha256=bbtVIt7LzVnUCcVxHWRH2owOQD067bQwwhyMf_whqHs,253
sqlalchemy/testing/plugin/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-311.pyc,,
sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-311.pyc,,
sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-311.pyc,,
sqlalchemy/testing/plugin/bootstrap.py,sha256=USn6pE-JcE5pSmnEd2wad3goKLx2hdJS3AUUFpXHm-I,1736
sqlalchemy/testing/plugin/plugin_base.py,sha256=CgrNj2wj9KNALu9YfnGSaHX2fXfTtiim_cfx0CPVoy8,22357
sqlalchemy/testing/plugin/pytestplugin.py,sha256=xFbgBkv92U7_nYSyq87MG6OZSg_NR2HOo7CG7IC1cpY,28416
sqlalchemy/testing/profiling.py,sha256=o8_V3TpF_WytudMQQLm1UxlfNDrLCWxUvkH-Kd0unKU,10472
sqlalchemy/testing/provision.py,sha256=ciWoXf3P9ql4hh1yBp0RNEtPr5vyvPbd8RD_DYxNG9U,15115
sqlalchemy/testing/requirements.py,sha256=L_DKVqVxVMbB3JveC_6UhD5oVry2KjBHPxfQd35hrWQ,53600
sqlalchemy/testing/schema.py,sha256=z2Z5rm3iJ1-vgifUxwzxEjt1qu7QOyr3TeDnQdCHlWE,6737
sqlalchemy/testing/suite/__init__.py,sha256=YvTEqUNHaBlgLgWDAWn79mQrUR4VBGUHtprywJlmDT8,741
sqlalchemy/testing/suite/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_cte.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_insert.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_results.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_select.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_types.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-311.pyc,,
sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-311.pyc,,
sqlalchemy/testing/suite/test_cte.py,sha256=C_viXJKClFAm91rtPb42tiAA7gYJwKkqGYVJYap0cLM,6662
sqlalchemy/testing/suite/test_ddl.py,sha256=k6D6RreLkDSSpRUM2hQz-_CA48qV2PYx_2LNyUSoZzE,12420
sqlalchemy/testing/suite/test_deprecations.py,sha256=SKRFZDteBO1rw9-BQjDic5nh7fdyw2ypVOewR2pj7-Q,5490
sqlalchemy/testing/suite/test_dialect.py,sha256=ftOWRXWOotB2_jMJJqwoH9f3X2ucc1HwwOiXp573GwM,23663
sqlalchemy/testing/suite/test_insert.py,sha256=v3zrUZaGlke3cI4vabHg7xaI4gNqcHhtMPgYuf0mOxc,19454
sqlalchemy/testing/suite/test_reflection.py,sha256=C6P9ccG5Eog5uiIHO4s6M7ThnBbEUZKh83CmOMn-KSo,109594
sqlalchemy/testing/suite/test_results.py,sha256=1SlvhdioM1_ZrkQX2IJbJgXNHuleizwAge6-XvHtA0s,16405
sqlalchemy/testing/suite/test_rowcount.py,sha256=DCEGxorDcrT5JCLd3_SNQeZmxT6sKIcuKxX1r6vK4Mg,8158
sqlalchemy/testing/suite/test_select.py,sha256=NwHUSVc4UptVYMGjp3QVLr0OpGxpz2qJG4cNWZW8vTo,60462
sqlalchemy/testing/suite/test_sequence.py,sha256=sIqkfgVqPIgl4lm75EPdag9gK-rTHfUm3pWX-JijPy4,10240
sqlalchemy/testing/suite/test_types.py,sha256=i1fCIXERdtGABdp_T3l1vaPH9AhQ80DJvbjOPbeng1c,67748
sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=juF_KTK1nGrSlsL8z0Ky0rFSNkPGheLB3e0Kq3yRqss,6330
sqlalchemy/testing/suite/test_update_delete.py,sha256=TnJI5U_ZEuu3bni4sH-S6CENxvSZwDgZL-FKSV45bAo,4133
sqlalchemy/testing/util.py,sha256=jX9jlUHSH-7_2OCypZUvitP8JkJbNdr5_ZxU6Aa8DPY,14599
sqlalchemy/testing/warnings.py,sha256=3EhbTlPe4gJnoydj-OKueNOOtGwIRF2kV4XvlFwFYOA,1598
sqlalchemy/types.py,sha256=unCm_O8qKxU3LjLbqeqSNQSsK5k5R5POsyEx2gH6CF4,3244
sqlalchemy/util/__init__.py,sha256=3-O9j9qPk-gTx6hlyLsISc_JOW5MhjV0J_L5nV19qI8,8436
sqlalchemy/util/__pycache__/__init__.cpython-311.pyc,,
sqlalchemy/util/__pycache__/_collections.cpython-311.pyc,,
sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-311.pyc,,
sqlalchemy/util/__pycache__/_has_cy.cpython-311.pyc,,
sqlalchemy/util/__pycache__/_py_collections.cpython-311.pyc,,
sqlalchemy/util/__pycache__/compat.cpython-311.pyc,,
sqlalchemy/util/__pycache__/concurrency.cpython-311.pyc,,
sqlalchemy/util/__pycache__/deprecations.cpython-311.pyc,,
sqlalchemy/util/__pycache__/langhelpers.cpython-311.pyc,,
sqlalchemy/util/__pycache__/preloaded.cpython-311.pyc,,
sqlalchemy/util/__pycache__/queue.cpython-311.pyc,,
sqlalchemy/util/__pycache__/tool_support.cpython-311.pyc,,
sqlalchemy/util/__pycache__/topological.cpython-311.pyc,,
sqlalchemy/util/__pycache__/typing.cpython-311.pyc,,
sqlalchemy/util/_collections.py,sha256=O3iqq0R9TbcXNyFk8nG4QLwkUzdWkCFmqBYcDrajvl8,20778
sqlalchemy/util/_concurrency_py3k.py,sha256=HQ5tLleQd5cR4BOoXKFWVTK7p4fCDW105QxRLW52_ko,8841
sqlalchemy/util/_has_cy.py,sha256=IHGc5hUFbXQuv1a1z2P8yVwz0yGbCYXyQM2qsdcBTyg,1287
sqlalchemy/util/_py_collections.py,sha256=2PUqiKIsF8d-gNDAAqYI8WE6XPyRf1flRLkVsJeXuOo,17255
sqlalchemy/util/compat.py,sha256=ojCAtKHlkqNdYB33PXbAP0zTH1ZXYdTZkJl32cqGnMQ,9014
sqlalchemy/util/concurrency.py,sha256=AOLQUBm9Hm4jDArP8vBYL39FzckyH9S4NsKRvWaYzEE,2500
sqlalchemy/util/deprecations.py,sha256=AnHpDWHi7g2gv_QUTGStQTnr0J94lIF-3aFLOsv9yzg,12372
sqlalchemy/util/langhelpers.py,sha256=1meF9IffDMmz50uxNdUO15FUL0TARzwFcPjwbpOQRX8,67115
sqlalchemy/util/preloaded.py,sha256=78Sl7VjzTOPajbovvARxNeuZb-iYRpEvL5k8m5Bz4vQ,6054
sqlalchemy/util/queue.py,sha256=4SbSbVamUECjCDpMPR035N1ooVHt9W5GjbqkxfZmH5k,10507
sqlalchemy/util/tool_support.py,sha256=DuurikYgDUIIxk3gubUKl6rs-etXt3eeHaZ4ZkIyJXQ,6336
sqlalchemy/util/topological.py,sha256=_NdtAghZjhZ4e2fwWHmn25erP5cvtGgOUMplsCa_VCE,3578
sqlalchemy/util/typing.py,sha256=DG9V94Mh63cqObr_G5X19wH4H3hhWMqZXufVEZ2wtiw,17221

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.42.0)
Root-Is-Purelib: false
Tag: cp311-cp311-win_amd64

View File

@@ -0,0 +1 @@
sqlalchemy

View File

@@ -0,0 +1 @@
version = "24.10.0"

View File

@@ -0,0 +1,222 @@
# don't import any costly modules
import sys
import os
is_pypy = '__pypy__' in sys.builtin_module_names
def warn_distutils_present():
if 'distutils' not in sys.modules:
return
if is_pypy and sys.version_info < (3, 7):
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
return
import warnings
warnings.warn(
"Distutils was imported before Setuptools, but importing Setuptools "
"also replaces the `distutils` module in `sys.modules`. This may lead "
"to undesirable behaviors or errors. To avoid these issues, avoid "
"using distutils directly, ensure that setuptools is installed in the "
"traditional way (e.g. not an editable install), and/or make sure "
"that setuptools is always imported before distutils."
)
def clear_distutils():
if 'distutils' not in sys.modules:
return
import warnings
warnings.warn("Setuptools is replacing distutils.")
mods = [
name
for name in sys.modules
if name == "distutils" or name.startswith("distutils.")
]
for name in mods:
del sys.modules[name]
def enabled():
"""
Allow selection of distutils by environment variable.
"""
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
return which == 'local'
def ensure_local_distutils():
import importlib
clear_distutils()
# With the DistutilsMetaFinder in place,
# perform an import to cause distutils to be
# loaded from setuptools._distutils. Ref #2906.
with shim():
importlib.import_module('distutils')
# check that submodules load as expected
core = importlib.import_module('distutils.core')
assert '_distutils' in core.__file__, core.__file__
assert 'setuptools._distutils.log' not in sys.modules
def do_override():
"""
Ensure that the local copy of distutils is preferred over stdlib.
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
for more motivation.
"""
if enabled():
warn_distutils_present()
ensure_local_distutils()
class _TrivialRe:
def __init__(self, *patterns):
self._patterns = patterns
def match(self, string):
return all(pat in string for pat in self._patterns)
class DistutilsMetaFinder:
def find_spec(self, fullname, path, target=None):
# optimization: only consider top level modules and those
# found in the CPython test suite.
if path is not None and not fullname.startswith('test.'):
return
method_name = 'spec_for_{fullname}'.format(**locals())
method = getattr(self, method_name, lambda: None)
return method()
def spec_for_distutils(self):
if self.is_cpython():
return
import importlib
import importlib.abc
import importlib.util
try:
mod = importlib.import_module('setuptools._distutils')
except Exception:
# There are a couple of cases where setuptools._distutils
# may not be present:
# - An older Setuptools without a local distutils is
# taking precedence. Ref #2957.
# - Path manipulation during sitecustomize removes
# setuptools from the path but only after the hook
# has been loaded. Ref #2980.
# In either case, fall back to stdlib behavior.
return
class DistutilsLoader(importlib.abc.Loader):
def create_module(self, spec):
mod.__name__ = 'distutils'
return mod
def exec_module(self, module):
pass
return importlib.util.spec_from_loader(
'distutils', DistutilsLoader(), origin=mod.__file__
)
@staticmethod
def is_cpython():
"""
Suppress supplying distutils for CPython (build and tests).
Ref #2965 and #3007.
"""
return os.path.isfile('pybuilddir.txt')
def spec_for_pip(self):
"""
Ensure stdlib distutils when running under pip.
See pypa/pip#8761 for rationale.
"""
if self.pip_imported_during_build():
return
clear_distutils()
self.spec_for_distutils = lambda: None
@classmethod
def pip_imported_during_build(cls):
"""
Detect if pip is being imported in a build script. Ref #2355.
"""
import traceback
return any(
cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
)
@staticmethod
def frame_file_is_setup(frame):
"""
Return True if the indicated frame suggests a setup.py file.
"""
# some frames may not have __file__ (#2940)
return frame.f_globals.get('__file__', '').endswith('setup.py')
def spec_for_sensitive_tests(self):
"""
Ensure stdlib distutils when running select tests under CPython.
python/cpython#91169
"""
clear_distutils()
self.spec_for_distutils = lambda: None
sensitive_tests = (
[
'test.test_distutils',
'test.test_peg_generator',
'test.test_importlib',
]
if sys.version_info < (3, 10)
else [
'test.test_distutils',
]
)
for name in DistutilsMetaFinder.sensitive_tests:
setattr(
DistutilsMetaFinder,
f'spec_for_{name}',
DistutilsMetaFinder.spec_for_sensitive_tests,
)
DISTUTILS_FINDER = DistutilsMetaFinder()
def add_shim():
DISTUTILS_FINDER in sys.meta_path or insert_shim()
class shim:
def __enter__(self):
insert_shim()
def __exit__(self, exc, value, tb):
remove_shim()
def insert_shim():
sys.meta_path.insert(0, DISTUTILS_FINDER)
def remove_shim():
try:
sys.meta_path.remove(DISTUTILS_FINDER)
except ValueError:
pass

View File

@@ -0,0 +1 @@
__import__('_distutils_hack').do_override()

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,318 @@
Metadata-Version: 2.3
Name: aiofiles
Version: 24.1.0
Summary: File support for asyncio.
Project-URL: Changelog, https://github.com/Tinche/aiofiles#history
Project-URL: Bug Tracker, https://github.com/Tinche/aiofiles/issues
Project-URL: repository, https://github.com/Tinche/aiofiles
Author-email: Tin Tvrtkovic <tinchester@gmail.com>
License: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.8
Description-Content-Type: text/markdown
# aiofiles: file support for asyncio
[![PyPI](https://img.shields.io/pypi/v/aiofiles.svg)](https://pypi.python.org/pypi/aiofiles)
[![Build](https://github.com/Tinche/aiofiles/workflows/CI/badge.svg)](https://github.com/Tinche/aiofiles/actions)
[![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/Tinche/882f02e3df32136c847ba90d2688f06e/raw/covbadge.json)](https://github.com/Tinche/aiofiles/actions/workflows/main.yml)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/aiofiles.svg)](https://github.com/Tinche/aiofiles)
[![Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
**aiofiles** is an Apache2 licensed library, written in Python, for handling local
disk files in asyncio applications.
Ordinary local file IO is blocking, and cannot easily and portably be made
asynchronous. This means doing file IO may interfere with asyncio applications,
which shouldn't block the executing thread. aiofiles helps with this by
introducing asynchronous versions of files that support delegating operations to
a separate thread pool.
```python
async with aiofiles.open('filename', mode='r') as f:
contents = await f.read()
print(contents)
'My file contents'
```
Asynchronous iteration is also supported.
```python
async with aiofiles.open('filename') as f:
async for line in f:
...
```
Asynchronous interface to tempfile module.
```python
async with aiofiles.tempfile.TemporaryFile('wb') as f:
await f.write(b'Hello, World!')
```
## Features
- a file API very similar to Python's standard, blocking API
- support for buffered and unbuffered binary files, and buffered text files
- support for `async`/`await` ([PEP 492](https://peps.python.org/pep-0492/)) constructs
- async interface to tempfile module
## Installation
To install aiofiles, simply:
```bash
$ pip install aiofiles
```
## Usage
Files are opened using the `aiofiles.open()` coroutine, which in addition to
mirroring the builtin `open` accepts optional `loop` and `executor`
arguments. If `loop` is absent, the default loop will be used, as per the
set asyncio policy. If `executor` is not specified, the default event loop
executor will be used.
In case of success, an asynchronous file object is returned with an
API identical to an ordinary file, except the following methods are coroutines
and delegate to an executor:
- `close`
- `flush`
- `isatty`
- `read`
- `readall`
- `read1`
- `readinto`
- `readline`
- `readlines`
- `seek`
- `seekable`
- `tell`
- `truncate`
- `writable`
- `write`
- `writelines`
In case of failure, one of the usual exceptions will be raised.
`aiofiles.stdin`, `aiofiles.stdout`, `aiofiles.stderr`,
`aiofiles.stdin_bytes`, `aiofiles.stdout_bytes`, and
`aiofiles.stderr_bytes` provide async access to `sys.stdin`,
`sys.stdout`, `sys.stderr`, and their corresponding `.buffer` properties.
The `aiofiles.os` module contains executor-enabled coroutine versions of
several useful `os` functions that deal with files:
- `stat`
- `statvfs`
- `sendfile`
- `rename`
- `renames`
- `replace`
- `remove`
- `unlink`
- `mkdir`
- `makedirs`
- `rmdir`
- `removedirs`
- `link`
- `symlink`
- `readlink`
- `listdir`
- `scandir`
- `access`
- `getcwd`
- `path.abspath`
- `path.exists`
- `path.isfile`
- `path.isdir`
- `path.islink`
- `path.ismount`
- `path.getsize`
- `path.getatime`
- `path.getctime`
- `path.samefile`
- `path.sameopenfile`
### Tempfile
**aiofiles.tempfile** implements the following interfaces:
- TemporaryFile
- NamedTemporaryFile
- SpooledTemporaryFile
- TemporaryDirectory
Results return wrapped with a context manager allowing use with async with and async for.
```python
async with aiofiles.tempfile.NamedTemporaryFile('wb+') as f:
await f.write(b'Line1\n Line2')
await f.seek(0)
async for line in f:
print(line)
async with aiofiles.tempfile.TemporaryDirectory() as d:
filename = os.path.join(d, "file.ext")
```
### Writing tests for aiofiles
Real file IO can be mocked by patching `aiofiles.threadpool.sync_open`
as desired. The return type also needs to be registered with the
`aiofiles.threadpool.wrap` dispatcher:
```python
aiofiles.threadpool.wrap.register(mock.MagicMock)(
lambda *args, **kwargs: aiofiles.threadpool.AsyncBufferedIOBase(*args, **kwargs)
)
async def test_stuff():
write_data = 'data'
read_file_chunks = [
b'file chunks 1',
b'file chunks 2',
b'file chunks 3',
b'',
]
file_chunks_iter = iter(read_file_chunks)
mock_file_stream = mock.MagicMock(
read=lambda *args, **kwargs: next(file_chunks_iter)
)
with mock.patch('aiofiles.threadpool.sync_open', return_value=mock_file_stream) as mock_open:
async with aiofiles.open('filename', 'w') as f:
await f.write(write_data)
assert f.read() == b'file chunks 1'
mock_file_stream.write.assert_called_once_with(write_data)
```
### History
#### 24.1.0 (2024-06-24)
- Import `os.link` conditionally to fix importing on android.
[#175](https://github.com/Tinche/aiofiles/issues/175)
- Remove spurious items from `aiofiles.os.__all__` when running on Windows.
- Switch to more modern async idioms: Remove types.coroutine and make AiofilesContextManager an awaitable instead a coroutine.
- Add `aiofiles.os.path.abspath` and `aiofiles.os.getcwd`.
[#174](https://github.com/Tinche/aiofiles/issues/181)
- _aiofiles_ is now tested on Python 3.13 too.
[#184](https://github.com/Tinche/aiofiles/pull/184)
- Dropped Python 3.7 support. If you require it, use version 23.2.1.
#### 23.2.1 (2023-08-09)
- Import `os.statvfs` conditionally to fix importing on non-UNIX systems.
[#171](https://github.com/Tinche/aiofiles/issues/171) [#172](https://github.com/Tinche/aiofiles/pull/172)
- aiofiles is now also tested on Windows.
#### 23.2.0 (2023-08-09)
- aiofiles is now tested on Python 3.12 too.
[#166](https://github.com/Tinche/aiofiles/issues/166) [#168](https://github.com/Tinche/aiofiles/pull/168)
- On Python 3.12, `aiofiles.tempfile.NamedTemporaryFile` now accepts a `delete_on_close` argument, just like the stdlib version.
- On Python 3.12, `aiofiles.tempfile.NamedTemporaryFile` no longer exposes a `delete` attribute, just like the stdlib version.
- Added `aiofiles.os.statvfs` and `aiofiles.os.path.ismount`.
[#162](https://github.com/Tinche/aiofiles/pull/162)
- Use [PDM](https://pdm.fming.dev/latest/) instead of Poetry.
[#169](https://github.com/Tinche/aiofiles/pull/169)
#### 23.1.0 (2023-02-09)
- Added `aiofiles.os.access`.
[#146](https://github.com/Tinche/aiofiles/pull/146)
- Removed `aiofiles.tempfile.temptypes.AsyncSpooledTemporaryFile.softspace`.
[#151](https://github.com/Tinche/aiofiles/pull/151)
- Added `aiofiles.stdin`, `aiofiles.stdin_bytes`, and other stdio streams.
[#154](https://github.com/Tinche/aiofiles/pull/154)
- Transition to `asyncio.get_running_loop` (vs `asyncio.get_event_loop`) internally.
#### 22.1.0 (2022-09-04)
- Added `aiofiles.os.path.islink`.
[#126](https://github.com/Tinche/aiofiles/pull/126)
- Added `aiofiles.os.readlink`.
[#125](https://github.com/Tinche/aiofiles/pull/125)
- Added `aiofiles.os.symlink`.
[#124](https://github.com/Tinche/aiofiles/pull/124)
- Added `aiofiles.os.unlink`.
[#123](https://github.com/Tinche/aiofiles/pull/123)
- Added `aiofiles.os.link`.
[#121](https://github.com/Tinche/aiofiles/pull/121)
- Added `aiofiles.os.renames`.
[#120](https://github.com/Tinche/aiofiles/pull/120)
- Added `aiofiles.os.{listdir, scandir}`.
[#143](https://github.com/Tinche/aiofiles/pull/143)
- Switched to CalVer.
- Dropped Python 3.6 support. If you require it, use version 0.8.0.
- aiofiles is now tested on Python 3.11.
#### 0.8.0 (2021-11-27)
- aiofiles is now tested on Python 3.10.
- Added `aiofiles.os.replace`.
[#107](https://github.com/Tinche/aiofiles/pull/107)
- Added `aiofiles.os.{makedirs, removedirs}`.
- Added `aiofiles.os.path.{exists, isfile, isdir, getsize, getatime, getctime, samefile, sameopenfile}`.
[#63](https://github.com/Tinche/aiofiles/pull/63)
- Added `suffix`, `prefix`, `dir` args to `aiofiles.tempfile.TemporaryDirectory`.
[#116](https://github.com/Tinche/aiofiles/pull/116)
#### 0.7.0 (2021-05-17)
- Added the `aiofiles.tempfile` module for async temporary files.
[#56](https://github.com/Tinche/aiofiles/pull/56)
- Switched to Poetry and GitHub actions.
- Dropped 3.5 support.
#### 0.6.0 (2020-10-27)
- `aiofiles` is now tested on ppc64le.
- Added `name` and `mode` properties to async file objects.
[#82](https://github.com/Tinche/aiofiles/pull/82)
- Fixed a DeprecationWarning internally.
[#75](https://github.com/Tinche/aiofiles/pull/75)
- Python 3.9 support and tests.
#### 0.5.0 (2020-04-12)
- Python 3.8 support. Code base modernization (using `async/await` instead of `asyncio.coroutine`/`yield from`).
- Added `aiofiles.os.remove`, `aiofiles.os.rename`, `aiofiles.os.mkdir`, `aiofiles.os.rmdir`.
[#62](https://github.com/Tinche/aiofiles/pull/62)
#### 0.4.0 (2018-08-11)
- Python 3.7 support.
- Removed Python 3.3/3.4 support. If you use these versions, stick to aiofiles 0.3.x.
#### 0.3.2 (2017-09-23)
- The LICENSE is now included in the sdist.
[#31](https://github.com/Tinche/aiofiles/pull/31)
#### 0.3.1 (2017-03-10)
- Introduced a changelog.
- `aiofiles.os.sendfile` will now work if the standard `os` module contains a `sendfile` function.
### Contributing
Contributions are very welcome. Tests can be run with `tox`, please ensure
the coverage at least stays the same before you submit a pull request.

View File

@@ -0,0 +1,26 @@
aiofiles-24.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
aiofiles-24.1.0.dist-info/METADATA,sha256=CvUJx21XclgI1Lp5Bt_4AyJesRYg0xCSx4exJZVmaSA,10708
aiofiles-24.1.0.dist-info/RECORD,,
aiofiles-24.1.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
aiofiles-24.1.0.dist-info/licenses/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
aiofiles-24.1.0.dist-info/licenses/NOTICE,sha256=EExY0dRQvWR0wJ2LZLwBgnM6YKw9jCU-M0zegpRSD_E,55
aiofiles/__init__.py,sha256=1iAMJQyJtX3LGIS0AoFTJeO1aJ_RK2jpBSBhg0VoIrE,344
aiofiles/__pycache__/__init__.cpython-311.pyc,,
aiofiles/__pycache__/base.cpython-311.pyc,,
aiofiles/__pycache__/os.cpython-311.pyc,,
aiofiles/__pycache__/ospath.cpython-311.pyc,,
aiofiles/base.py,sha256=zo0FgkCqZ5aosjvxqIvDf2t-RFg1Lc6X8P6rZ56p6fQ,1784
aiofiles/os.py,sha256=0DrsG-eH4h7xRzglv9pIWsQuzqe7ZhVYw5FQS18fIys,1153
aiofiles/ospath.py,sha256=WaYelz_k6ykAFRLStr4bqYIfCVQ-5GGzIqIizykbY2Q,794
aiofiles/tempfile/__init__.py,sha256=hFSNTOjOUv371Ozdfy6FIxeln46Nm3xOVh4ZR3Q94V0,10244
aiofiles/tempfile/__pycache__/__init__.cpython-311.pyc,,
aiofiles/tempfile/__pycache__/temptypes.cpython-311.pyc,,
aiofiles/tempfile/temptypes.py,sha256=ddEvNjMLVlr7WUILCe6ypTqw77yREeIonTk16Uw_NVs,2093
aiofiles/threadpool/__init__.py,sha256=kt0hwwx3bLiYtnA1SORhW8mJ6z4W9Xr7MbY80UIJJrI,3133
aiofiles/threadpool/__pycache__/__init__.cpython-311.pyc,,
aiofiles/threadpool/__pycache__/binary.cpython-311.pyc,,
aiofiles/threadpool/__pycache__/text.cpython-311.pyc,,
aiofiles/threadpool/__pycache__/utils.cpython-311.pyc,,
aiofiles/threadpool/binary.py,sha256=hp-km9VCRu0MLz_wAEUfbCz7OL7xtn9iGAawabpnp5U,2315
aiofiles/threadpool/text.py,sha256=fNmpw2PEkj0BZSldipJXAgZqVGLxALcfOMiuDQ54Eas,1223
aiofiles/threadpool/utils.py,sha256=B59dSZwO_WZs2dFFycKeA91iD2Xq2nNw1EFF8YMBI5k,1868

View File

@@ -0,0 +1,4 @@
Wheel-Version: 1.0
Generator: hatchling 1.25.0
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,2 @@
Asyncio support for files
Copyright 2016 Tin Tvrtkovic

View File

@@ -0,0 +1,22 @@
"""Utilities for asyncio-friendly file handling."""
from .threadpool import (
open,
stdin,
stdout,
stderr,
stdin_bytes,
stdout_bytes,
stderr_bytes,
)
from . import tempfile
__all__ = [
"open",
"tempfile",
"stdin",
"stdout",
"stderr",
"stdin_bytes",
"stdout_bytes",
"stderr_bytes",
]

View File

@@ -0,0 +1,69 @@
"""Various base classes."""
from collections.abc import Awaitable
from contextlib import AbstractAsyncContextManager
from asyncio import get_running_loop
class AsyncBase:
def __init__(self, file, loop, executor):
self._file = file
self._executor = executor
self._ref_loop = loop
@property
def _loop(self):
return self._ref_loop or get_running_loop()
def __aiter__(self):
"""We are our own iterator."""
return self
def __repr__(self):
return super().__repr__() + " wrapping " + repr(self._file)
async def __anext__(self):
"""Simulate normal file iteration."""
line = await self.readline()
if line:
return line
else:
raise StopAsyncIteration
class AsyncIndirectBase(AsyncBase):
def __init__(self, name, loop, executor, indirect):
self._indirect = indirect
self._name = name
super().__init__(None, loop, executor)
@property
def _file(self):
return self._indirect()
@_file.setter
def _file(self, v):
pass # discard writes
class AiofilesContextManager(Awaitable, AbstractAsyncContextManager):
"""An adjusted async context manager for aiofiles."""
__slots__ = ("_coro", "_obj")
def __init__(self, coro):
self._coro = coro
self._obj = None
def __await__(self):
if self._obj is None:
self._obj = yield from self._coro.__await__()
return self._obj
async def __aenter__(self):
return await self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await get_running_loop().run_in_executor(
None, self._obj._file.__exit__, exc_type, exc_val, exc_tb
)
self._obj = None

View File

@@ -0,0 +1,58 @@
"""Async executor versions of file functions from the os module."""
import os
from . import ospath as path
from .ospath import wrap
__all__ = [
"path",
"stat",
"rename",
"renames",
"replace",
"remove",
"unlink",
"mkdir",
"makedirs",
"rmdir",
"removedirs",
"symlink",
"readlink",
"listdir",
"scandir",
"access",
"wrap",
"getcwd",
]
if hasattr(os, "link"):
__all__ += ["link"]
if hasattr(os, "sendfile"):
__all__ += ["sendfile"]
if hasattr(os, "statvfs"):
__all__ += ["statvfs"]
stat = wrap(os.stat)
rename = wrap(os.rename)
renames = wrap(os.renames)
replace = wrap(os.replace)
remove = wrap(os.remove)
unlink = wrap(os.unlink)
mkdir = wrap(os.mkdir)
makedirs = wrap(os.makedirs)
rmdir = wrap(os.rmdir)
removedirs = wrap(os.removedirs)
symlink = wrap(os.symlink)
readlink = wrap(os.readlink)
listdir = wrap(os.listdir)
scandir = wrap(os.scandir)
access = wrap(os.access)
getcwd = wrap(os.getcwd)
if hasattr(os, "link"):
link = wrap(os.link)
if hasattr(os, "sendfile"):
sendfile = wrap(os.sendfile)
if hasattr(os, "statvfs"):
statvfs = wrap(os.statvfs)

View File

@@ -0,0 +1,30 @@
"""Async executor versions of file functions from the os.path module."""
import asyncio
from functools import partial, wraps
from os import path
def wrap(func):
@wraps(func)
async def run(*args, loop=None, executor=None, **kwargs):
if loop is None:
loop = asyncio.get_running_loop()
pfunc = partial(func, *args, **kwargs)
return await loop.run_in_executor(executor, pfunc)
return run
exists = wrap(path.exists)
isfile = wrap(path.isfile)
isdir = wrap(path.isdir)
islink = wrap(path.islink)
ismount = wrap(path.ismount)
getsize = wrap(path.getsize)
getmtime = wrap(path.getmtime)
getatime = wrap(path.getatime)
getctime = wrap(path.getctime)
samefile = wrap(path.samefile)
sameopenfile = wrap(path.sameopenfile)
abspath = wrap(path.abspath)

View File

@@ -0,0 +1,357 @@
import asyncio
from functools import partial, singledispatch
from io import BufferedRandom, BufferedReader, BufferedWriter, FileIO, TextIOBase
from tempfile import NamedTemporaryFile as syncNamedTemporaryFile
from tempfile import SpooledTemporaryFile as syncSpooledTemporaryFile
from tempfile import TemporaryDirectory as syncTemporaryDirectory
from tempfile import TemporaryFile as syncTemporaryFile
from tempfile import _TemporaryFileWrapper as syncTemporaryFileWrapper
from ..base import AiofilesContextManager
from ..threadpool.binary import AsyncBufferedIOBase, AsyncBufferedReader, AsyncFileIO
from ..threadpool.text import AsyncTextIOWrapper
from .temptypes import AsyncSpooledTemporaryFile, AsyncTemporaryDirectory
import sys
__all__ = [
"NamedTemporaryFile",
"TemporaryFile",
"SpooledTemporaryFile",
"TemporaryDirectory",
]
# ================================================================
# Public methods for async open and return of temp file/directory
# objects with async interface
# ================================================================
if sys.version_info >= (3, 12):
def NamedTemporaryFile(
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
delete=True,
delete_on_close=True,
loop=None,
executor=None,
):
"""Async open a named temporary file"""
return AiofilesContextManager(
_temporary_file(
named=True,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
delete=delete,
delete_on_close=delete_on_close,
loop=loop,
executor=executor,
)
)
else:
def NamedTemporaryFile(
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
delete=True,
loop=None,
executor=None,
):
"""Async open a named temporary file"""
return AiofilesContextManager(
_temporary_file(
named=True,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
delete=delete,
loop=loop,
executor=executor,
)
)
def TemporaryFile(
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
loop=None,
executor=None,
):
"""Async open an unnamed temporary file"""
return AiofilesContextManager(
_temporary_file(
named=False,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
loop=loop,
executor=executor,
)
)
def SpooledTemporaryFile(
max_size=0,
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
loop=None,
executor=None,
):
"""Async open a spooled temporary file"""
return AiofilesContextManager(
_spooled_temporary_file(
max_size=max_size,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
loop=loop,
executor=executor,
)
)
def TemporaryDirectory(suffix=None, prefix=None, dir=None, loop=None, executor=None):
"""Async open a temporary directory"""
return AiofilesContextManagerTempDir(
_temporary_directory(
suffix=suffix, prefix=prefix, dir=dir, loop=loop, executor=executor
)
)
# =========================================================
# Internal coroutines to open new temp files/directories
# =========================================================
if sys.version_info >= (3, 12):
async def _temporary_file(
named=True,
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
delete=True,
delete_on_close=True,
loop=None,
executor=None,
max_size=0,
):
"""Async method to open a temporary file with async interface"""
if loop is None:
loop = asyncio.get_running_loop()
if named:
cb = partial(
syncNamedTemporaryFile,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
delete=delete,
delete_on_close=delete_on_close,
)
else:
cb = partial(
syncTemporaryFile,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
)
f = await loop.run_in_executor(executor, cb)
# Wrap based on type of underlying IO object
if type(f) is syncTemporaryFileWrapper:
# _TemporaryFileWrapper was used (named files)
result = wrap(f.file, f, loop=loop, executor=executor)
result._closer = f._closer
return result
else:
# IO object was returned directly without wrapper
return wrap(f, f, loop=loop, executor=executor)
else:
async def _temporary_file(
named=True,
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
delete=True,
loop=None,
executor=None,
max_size=0,
):
"""Async method to open a temporary file with async interface"""
if loop is None:
loop = asyncio.get_running_loop()
if named:
cb = partial(
syncNamedTemporaryFile,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
delete=delete,
)
else:
cb = partial(
syncTemporaryFile,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
)
f = await loop.run_in_executor(executor, cb)
# Wrap based on type of underlying IO object
if type(f) is syncTemporaryFileWrapper:
# _TemporaryFileWrapper was used (named files)
result = wrap(f.file, f, loop=loop, executor=executor)
# add delete property
result.delete = f.delete
return result
else:
# IO object was returned directly without wrapper
return wrap(f, f, loop=loop, executor=executor)
async def _spooled_temporary_file(
max_size=0,
mode="w+b",
buffering=-1,
encoding=None,
newline=None,
suffix=None,
prefix=None,
dir=None,
loop=None,
executor=None,
):
"""Open a spooled temporary file with async interface"""
if loop is None:
loop = asyncio.get_running_loop()
cb = partial(
syncSpooledTemporaryFile,
max_size=max_size,
mode=mode,
buffering=buffering,
encoding=encoding,
newline=newline,
suffix=suffix,
prefix=prefix,
dir=dir,
)
f = await loop.run_in_executor(executor, cb)
# Single interface provided by SpooledTemporaryFile for all modes
return AsyncSpooledTemporaryFile(f, loop=loop, executor=executor)
async def _temporary_directory(
suffix=None, prefix=None, dir=None, loop=None, executor=None
):
"""Async method to open a temporary directory with async interface"""
if loop is None:
loop = asyncio.get_running_loop()
cb = partial(syncTemporaryDirectory, suffix, prefix, dir)
f = await loop.run_in_executor(executor, cb)
return AsyncTemporaryDirectory(f, loop=loop, executor=executor)
class AiofilesContextManagerTempDir(AiofilesContextManager):
"""With returns the directory location, not the object (matching sync lib)"""
async def __aenter__(self):
self._obj = await self._coro
return self._obj.name
@singledispatch
def wrap(base_io_obj, file, *, loop=None, executor=None):
"""Wrap the object with interface based on type of underlying IO"""
raise TypeError("Unsupported IO type: {}".format(base_io_obj))
@wrap.register(TextIOBase)
def _(base_io_obj, file, *, loop=None, executor=None):
return AsyncTextIOWrapper(file, loop=loop, executor=executor)
@wrap.register(BufferedWriter)
def _(base_io_obj, file, *, loop=None, executor=None):
return AsyncBufferedIOBase(file, loop=loop, executor=executor)
@wrap.register(BufferedReader)
@wrap.register(BufferedRandom)
def _(base_io_obj, file, *, loop=None, executor=None):
return AsyncBufferedReader(file, loop=loop, executor=executor)
@wrap.register(FileIO)
def _(base_io_obj, file, *, loop=None, executor=None):
return AsyncFileIO(file, loop=loop, executor=executor)

View File

@@ -0,0 +1,69 @@
"""Async wrappers for spooled temp files and temp directory objects"""
from functools import partial
from ..base import AsyncBase
from ..threadpool.utils import (
cond_delegate_to_executor,
delegate_to_executor,
proxy_property_directly,
)
@delegate_to_executor("fileno", "rollover")
@cond_delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"readline",
"readlines",
"seek",
"tell",
"truncate",
)
@proxy_property_directly("closed", "encoding", "mode", "name", "newlines")
class AsyncSpooledTemporaryFile(AsyncBase):
"""Async wrapper for SpooledTemporaryFile class"""
async def _check(self):
if self._file._rolled:
return
max_size = self._file._max_size
if max_size and self._file.tell() > max_size:
await self.rollover()
async def write(self, s):
"""Implementation to anticipate rollover"""
if self._file._rolled:
cb = partial(self._file.write, s)
return await self._loop.run_in_executor(self._executor, cb)
else:
file = self._file._file # reference underlying base IO object
rv = file.write(s)
await self._check()
return rv
async def writelines(self, iterable):
"""Implementation to anticipate rollover"""
if self._file._rolled:
cb = partial(self._file.writelines, iterable)
return await self._loop.run_in_executor(self._executor, cb)
else:
file = self._file._file # reference underlying base IO object
rv = file.writelines(iterable)
await self._check()
return rv
@delegate_to_executor("cleanup")
@proxy_property_directly("name")
class AsyncTemporaryDirectory:
"""Async wrapper for TemporaryDirectory class"""
def __init__(self, file, loop, executor):
self._file = file
self._loop = loop
self._executor = executor
async def close(self):
await self.cleanup()

View File

@@ -0,0 +1,139 @@
"""Handle files using a thread pool executor."""
import asyncio
import sys
from functools import partial, singledispatch
from io import (
BufferedIOBase,
BufferedRandom,
BufferedReader,
BufferedWriter,
FileIO,
TextIOBase,
)
from ..base import AiofilesContextManager
from .binary import (
AsyncBufferedIOBase,
AsyncBufferedReader,
AsyncFileIO,
AsyncIndirectBufferedIOBase,
)
from .text import AsyncTextIndirectIOWrapper, AsyncTextIOWrapper
sync_open = open
__all__ = (
"open",
"stdin",
"stdout",
"stderr",
"stdin_bytes",
"stdout_bytes",
"stderr_bytes",
)
def open(
file,
mode="r",
buffering=-1,
encoding=None,
errors=None,
newline=None,
closefd=True,
opener=None,
*,
loop=None,
executor=None,
):
return AiofilesContextManager(
_open(
file,
mode=mode,
buffering=buffering,
encoding=encoding,
errors=errors,
newline=newline,
closefd=closefd,
opener=opener,
loop=loop,
executor=executor,
)
)
async def _open(
file,
mode="r",
buffering=-1,
encoding=None,
errors=None,
newline=None,
closefd=True,
opener=None,
*,
loop=None,
executor=None,
):
"""Open an asyncio file."""
if loop is None:
loop = asyncio.get_running_loop()
cb = partial(
sync_open,
file,
mode=mode,
buffering=buffering,
encoding=encoding,
errors=errors,
newline=newline,
closefd=closefd,
opener=opener,
)
f = await loop.run_in_executor(executor, cb)
return wrap(f, loop=loop, executor=executor)
@singledispatch
def wrap(file, *, loop=None, executor=None):
raise TypeError("Unsupported io type: {}.".format(file))
@wrap.register(TextIOBase)
def _(file, *, loop=None, executor=None):
return AsyncTextIOWrapper(file, loop=loop, executor=executor)
@wrap.register(BufferedWriter)
@wrap.register(BufferedIOBase)
def _(file, *, loop=None, executor=None):
return AsyncBufferedIOBase(file, loop=loop, executor=executor)
@wrap.register(BufferedReader)
@wrap.register(BufferedRandom)
def _(file, *, loop=None, executor=None):
return AsyncBufferedReader(file, loop=loop, executor=executor)
@wrap.register(FileIO)
def _(file, *, loop=None, executor=None):
return AsyncFileIO(file, loop=loop, executor=executor)
stdin = AsyncTextIndirectIOWrapper("sys.stdin", None, None, indirect=lambda: sys.stdin)
stdout = AsyncTextIndirectIOWrapper(
"sys.stdout", None, None, indirect=lambda: sys.stdout
)
stderr = AsyncTextIndirectIOWrapper(
"sys.stderr", None, None, indirect=lambda: sys.stderr
)
stdin_bytes = AsyncIndirectBufferedIOBase(
"sys.stdin.buffer", None, None, indirect=lambda: sys.stdin.buffer
)
stdout_bytes = AsyncIndirectBufferedIOBase(
"sys.stdout.buffer", None, None, indirect=lambda: sys.stdout.buffer
)
stderr_bytes = AsyncIndirectBufferedIOBase(
"sys.stderr.buffer", None, None, indirect=lambda: sys.stderr.buffer
)

View File

@@ -0,0 +1,104 @@
from ..base import AsyncBase, AsyncIndirectBase
from .utils import delegate_to_executor, proxy_method_directly, proxy_property_directly
@delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"read1",
"readinto",
"readline",
"readlines",
"seek",
"seekable",
"tell",
"truncate",
"writable",
"write",
"writelines",
)
@proxy_method_directly("detach", "fileno", "readable")
@proxy_property_directly("closed", "raw", "name", "mode")
class AsyncBufferedIOBase(AsyncBase):
"""The asyncio executor version of io.BufferedWriter and BufferedIOBase."""
@delegate_to_executor("peek")
class AsyncBufferedReader(AsyncBufferedIOBase):
"""The asyncio executor version of io.BufferedReader and Random."""
@delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"readall",
"readinto",
"readline",
"readlines",
"seek",
"seekable",
"tell",
"truncate",
"writable",
"write",
"writelines",
)
@proxy_method_directly("fileno", "readable")
@proxy_property_directly("closed", "name", "mode")
class AsyncFileIO(AsyncBase):
"""The asyncio executor version of io.FileIO."""
@delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"read1",
"readinto",
"readline",
"readlines",
"seek",
"seekable",
"tell",
"truncate",
"writable",
"write",
"writelines",
)
@proxy_method_directly("detach", "fileno", "readable")
@proxy_property_directly("closed", "raw", "name", "mode")
class AsyncIndirectBufferedIOBase(AsyncIndirectBase):
"""The indirect asyncio executor version of io.BufferedWriter and BufferedIOBase."""
@delegate_to_executor("peek")
class AsyncIndirectBufferedReader(AsyncIndirectBufferedIOBase):
"""The indirect asyncio executor version of io.BufferedReader and Random."""
@delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"readall",
"readinto",
"readline",
"readlines",
"seek",
"seekable",
"tell",
"truncate",
"writable",
"write",
"writelines",
)
@proxy_method_directly("fileno", "readable")
@proxy_property_directly("closed", "name", "mode")
class AsyncIndirectFileIO(AsyncIndirectBase):
"""The indirect asyncio executor version of io.FileIO."""

View File

@@ -0,0 +1,64 @@
from ..base import AsyncBase, AsyncIndirectBase
from .utils import delegate_to_executor, proxy_method_directly, proxy_property_directly
@delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"readable",
"readline",
"readlines",
"seek",
"seekable",
"tell",
"truncate",
"write",
"writable",
"writelines",
)
@proxy_method_directly("detach", "fileno", "readable")
@proxy_property_directly(
"buffer",
"closed",
"encoding",
"errors",
"line_buffering",
"newlines",
"name",
"mode",
)
class AsyncTextIOWrapper(AsyncBase):
"""The asyncio executor version of io.TextIOWrapper."""
@delegate_to_executor(
"close",
"flush",
"isatty",
"read",
"readable",
"readline",
"readlines",
"seek",
"seekable",
"tell",
"truncate",
"write",
"writable",
"writelines",
)
@proxy_method_directly("detach", "fileno", "readable")
@proxy_property_directly(
"buffer",
"closed",
"encoding",
"errors",
"line_buffering",
"newlines",
"name",
"mode",
)
class AsyncTextIndirectIOWrapper(AsyncIndirectBase):
"""The indirect asyncio executor version of io.TextIOWrapper."""

View File

@@ -0,0 +1,72 @@
import functools
def delegate_to_executor(*attrs):
def cls_builder(cls):
for attr_name in attrs:
setattr(cls, attr_name, _make_delegate_method(attr_name))
return cls
return cls_builder
def proxy_method_directly(*attrs):
def cls_builder(cls):
for attr_name in attrs:
setattr(cls, attr_name, _make_proxy_method(attr_name))
return cls
return cls_builder
def proxy_property_directly(*attrs):
def cls_builder(cls):
for attr_name in attrs:
setattr(cls, attr_name, _make_proxy_property(attr_name))
return cls
return cls_builder
def cond_delegate_to_executor(*attrs):
def cls_builder(cls):
for attr_name in attrs:
setattr(cls, attr_name, _make_cond_delegate_method(attr_name))
return cls
return cls_builder
def _make_delegate_method(attr_name):
async def method(self, *args, **kwargs):
cb = functools.partial(getattr(self._file, attr_name), *args, **kwargs)
return await self._loop.run_in_executor(self._executor, cb)
return method
def _make_proxy_method(attr_name):
def method(self, *args, **kwargs):
return getattr(self._file, attr_name)(*args, **kwargs)
return method
def _make_proxy_property(attr_name):
def proxy_property(self):
return getattr(self._file, attr_name)
return property(proxy_property)
def _make_cond_delegate_method(attr_name):
"""For spooled temp files, delegate only if rolled to file object"""
async def method(self, *args, **kwargs):
if self._file._rolled:
cb = functools.partial(getattr(self._file, attr_name), *args, **kwargs)
return await self._loop.run_in_executor(self._executor, cb)
else:
return getattr(self._file, attr_name)(*args, **kwargs)
return method

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,164 @@
Metadata-Version: 2.4
Name: aiogram
Version: 3.16.0
Summary: Modern and fully asynchronous framework for Telegram Bot API
Project-URL: Homepage, https://aiogram.dev/
Project-URL: Documentation, https://docs.aiogram.dev/
Project-URL: Repository, https://github.com/aiogram/aiogram/
Author-email: Alex Root Junior <jroot.junior@gmail.com>
Maintainer-email: Alex Root Junior <jroot.junior@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,asyncio,bot,framework,telegram,wrapper
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: aiofiles<24.2,>=23.2.1
Requires-Dist: aiohttp<3.12,>=3.9.0
Requires-Dist: certifi>=2023.7.22
Requires-Dist: magic-filter<1.1,>=1.0.12
Requires-Dist: pydantic<2.11,>=2.4.1
Requires-Dist: typing-extensions<=5.0,>=4.7.0
Provides-Extra: cli
Requires-Dist: aiogram-cli<2.0.0,>=1.1.0; extra == 'cli'
Provides-Extra: dev
Requires-Dist: black~=24.4.2; extra == 'dev'
Requires-Dist: isort~=5.13.2; extra == 'dev'
Requires-Dist: motor-types~=1.0.0b4; extra == 'dev'
Requires-Dist: mypy~=1.10.0; extra == 'dev'
Requires-Dist: packaging~=24.1; extra == 'dev'
Requires-Dist: pre-commit~=3.5; extra == 'dev'
Requires-Dist: ruff~=0.5.1; extra == 'dev'
Requires-Dist: toml~=0.10.2; extra == 'dev'
Provides-Extra: docs
Requires-Dist: furo~=2024.8.6; extra == 'docs'
Requires-Dist: markdown-include~=0.8.1; extra == 'docs'
Requires-Dist: pygments~=2.18.0; extra == 'docs'
Requires-Dist: pymdown-extensions~=10.3; extra == 'docs'
Requires-Dist: sphinx-autobuild~=2024.9.3; extra == 'docs'
Requires-Dist: sphinx-copybutton~=0.5.2; extra == 'docs'
Requires-Dist: sphinx-intl~=2.2.0; extra == 'docs'
Requires-Dist: sphinx-substitution-extensions~=2024.8.6; extra == 'docs'
Requires-Dist: sphinxcontrib-towncrier~=0.4.0a0; extra == 'docs'
Requires-Dist: sphinx~=8.0.2; extra == 'docs'
Requires-Dist: towncrier~=24.8.0; extra == 'docs'
Provides-Extra: fast
Requires-Dist: aiodns>=3.0.0; extra == 'fast'
Requires-Dist: uvloop>=0.17.0; ((sys_platform == 'darwin' or sys_platform == 'linux') and platform_python_implementation != 'PyPy' and python_version < '3.13') and extra == 'fast'
Requires-Dist: uvloop>=0.21.0; ((sys_platform == 'darwin' or sys_platform == 'linux') and platform_python_implementation != 'PyPy' and python_version >= '3.13') and extra == 'fast'
Provides-Extra: i18n
Requires-Dist: babel~=2.13.0; extra == 'i18n'
Provides-Extra: mongo
Requires-Dist: motor<3.7.0,>=3.3.2; extra == 'mongo'
Provides-Extra: proxy
Requires-Dist: aiohttp-socks~=0.8.3; extra == 'proxy'
Provides-Extra: redis
Requires-Dist: redis[hiredis]<5.1.0,>=5.0.1; extra == 'redis'
Provides-Extra: test
Requires-Dist: aresponses~=2.1.6; extra == 'test'
Requires-Dist: pycryptodomex~=3.19.0; extra == 'test'
Requires-Dist: pytest-aiohttp~=1.0.5; extra == 'test'
Requires-Dist: pytest-asyncio~=0.21.1; extra == 'test'
Requires-Dist: pytest-cov~=4.1.0; extra == 'test'
Requires-Dist: pytest-html~=4.0.2; extra == 'test'
Requires-Dist: pytest-lazy-fixture~=0.6.3; extra == 'test'
Requires-Dist: pytest-mock~=3.12.0; extra == 'test'
Requires-Dist: pytest-mypy~=0.10.3; extra == 'test'
Requires-Dist: pytest~=7.4.2; extra == 'test'
Requires-Dist: pytz~=2023.3; extra == 'test'
Description-Content-Type: text/x-rst
#######
aiogram
#######
.. image:: https://img.shields.io/pypi/l/aiogram.svg?style=flat-square
:target: https://opensource.org/licenses/MIT
:alt: MIT License
.. image:: https://img.shields.io/pypi/status/aiogram.svg?style=flat-square
:target: https://pypi.python.org/pypi/aiogram
:alt: PyPi status
.. image:: https://img.shields.io/pypi/v/aiogram.svg?style=flat-square
:target: https://pypi.python.org/pypi/aiogram
:alt: PyPi Package Version
.. image:: https://img.shields.io/pypi/dm/aiogram.svg?style=flat-square
:target: https://pypi.python.org/pypi/aiogram
:alt: Downloads
.. image:: https://img.shields.io/pypi/pyversions/aiogram.svg?style=flat-square
:target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions
.. image:: https://img.shields.io/badge/dynamic/json?color=blue&logo=telegram&label=Telegram%20Bot%20API&query=%24.api.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Faiogram%2Faiogram%2Fdev-3.x%2F.butcher%2Fschema%2Fschema.json&style=flat-square
:target: https://core.telegram.org/bots/api
:alt: Telegram Bot API
.. image:: https://img.shields.io/github/actions/workflow/status/aiogram/aiogram/tests.yml?branch=dev-3.x&style=flat-square
:target: https://github.com/aiogram/aiogram/actions
:alt: Tests
.. image:: https://img.shields.io/codecov/c/github/aiogram/aiogram?style=flat-square
:target: https://app.codecov.io/gh/aiogram/aiogram
:alt: Codecov
**aiogram** is a modern and fully asynchronous framework for
`Telegram Bot API <https://core.telegram.org/bots/api>`_ written in Python 3.8+ using
`asyncio <https://docs.python.org/3/library/asyncio.html>`_ and
`aiohttp <https://github.com/aio-libs/aiohttp>`_.
Make your bots faster and more powerful!
Documentation:
- 🇺🇸 `English <https://docs.aiogram.dev/en/dev-3.x/>`_
- 🇺🇦 `Ukrainian <https://docs.aiogram.dev/uk_UA/dev-3.x/>`_
Features
========
- Asynchronous (`asyncio docs <https://docs.python.org/3/library/asyncio.html>`_, :pep:`492`)
- Has type hints (:pep:`484`) and can be used with `mypy <http://mypy-lang.org/>`_
- Supports `PyPy <https://www.pypy.org/>`_
- Supports `Telegram Bot API 8.1 <https://core.telegram.org/bots/api>`_ and gets fast updates to the latest versions of the Bot API
- Telegram Bot API integration code was `autogenerated <https://github.com/aiogram/tg-codegen>`_ and can be easily re-generated when API gets updated
- Updates router (Blueprints)
- Has Finite State Machine
- Uses powerful `magic filters <https://docs.aiogram.dev/en/latest/dispatcher/filters/magic_filters.html#magic-filters>`_
- Middlewares (incoming updates and API calls)
- Provides `Replies into Webhook <https://core.telegram.org/bots/faq#how-can-i-make-requests-in-response-to-updates>`_
- Integrated I18n/L10n support with GNU Gettext (or Fluent)
.. warning::
It is strongly advised that you have prior experience working
with `asyncio <https://docs.python.org/3/library/asyncio.html>`_
before beginning to use **aiogram**.
If you have any questions, you can visit our community chats on Telegram:
- 🇺🇸 `@aiogram <https://t.me/aiogram>`_
- 🇺🇦 `@aiogramua <https://t.me/aiogramua>`_
- 🇺🇿 `@aiogram_uz <https://t.me/aiogram_uz>`_
- 🇰🇿 `@aiogram_kz <https://t.me/aiogram_kz>`_
- 🇷🇺 `@aiogram_ru <https://t.me/aiogram_ru>`_
- 🇮🇷 `@aiogram_fa <https://t.me/aiogram_fa>`_
- 🇮🇹 `@aiogram_it <https://t.me/aiogram_it>`_
- 🇧🇷 `@aiogram_br <https://t.me/aiogram_br>`_

View File

@@ -0,0 +1,987 @@
aiogram-3.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
aiogram-3.16.0.dist-info/METADATA,sha256=9yhjeqn7Ttwig1G-VpDPSAoVDZ44yv117cYiu5Yxxdg,7579
aiogram-3.16.0.dist-info/RECORD,,
aiogram-3.16.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram-3.16.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
aiogram-3.16.0.dist-info/licenses/LICENSE,sha256=kTEqDVZ6fYSuFKAER9NvxX60epaWBKI_h-xGsoiU6Iw,1070
aiogram/__init__.py,sha256=hzEKX_gFXmp5n4GXZEU80SJep1_FAH9owAnFTqd9nK0,948
aiogram/__meta__.py,sha256=IOnLqvH1y-1cyCqiC-TMyabydnglsahpB7IOfxwwMQE,47
aiogram/__pycache__/__init__.cpython-311.pyc,,
aiogram/__pycache__/__meta__.cpython-311.pyc,,
aiogram/__pycache__/exceptions.cpython-311.pyc,,
aiogram/__pycache__/loggers.cpython-311.pyc,,
aiogram/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/client/__pycache__/__init__.cpython-311.pyc,,
aiogram/client/__pycache__/bot.cpython-311.pyc,,
aiogram/client/__pycache__/context_controller.cpython-311.pyc,,
aiogram/client/__pycache__/default.cpython-311.pyc,,
aiogram/client/__pycache__/telegram.cpython-311.pyc,,
aiogram/client/bot.py,sha256=P2mTS6-y1iDYFizmmLkRnxb73iW6H20TiPpd7CuPLPQ,279448
aiogram/client/context_controller.py,sha256=rAminUBsB3K5FDyj-WQopkD1e94c7GrGVXNyaqPbXZ0,761
aiogram/client/default.py,sha256=sqctTqHKsc69UBGh4sJeFbihGH5feDudb685CbMtdDU,2750
aiogram/client/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/client/session/__pycache__/__init__.cpython-311.pyc,,
aiogram/client/session/__pycache__/aiohttp.cpython-311.pyc,,
aiogram/client/session/__pycache__/base.cpython-311.pyc,,
aiogram/client/session/aiohttp.py,sha256=q2OZLVEdz3LghDlEC5KfDEJJLtwYEbfJMXEmsVNnRcQ,7197
aiogram/client/session/base.py,sha256=e3n5NrwL6L-pFhLetTgBg76uLJDxDP6jQqyxPgjtI_0,8335
aiogram/client/session/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/client/session/middlewares/__pycache__/__init__.cpython-311.pyc,,
aiogram/client/session/middlewares/__pycache__/base.cpython-311.pyc,,
aiogram/client/session/middlewares/__pycache__/manager.cpython-311.pyc,,
aiogram/client/session/middlewares/__pycache__/request_logging.cpython-311.pyc,,
aiogram/client/session/middlewares/base.py,sha256=ck8-iOnaYO2FHb3Yjq6E_TZwiPMg8w8THOXqyHDLjro,1394
aiogram/client/session/middlewares/manager.py,sha256=c1CS6S_UvxYVAfCq6MZ4Laet6sxReEmQ915XltTJR5o,1903
aiogram/client/session/middlewares/request_logging.py,sha256=_nUsciMnQeTZ-_nJw3A3NNBlGWzGqKMqYw2oJqJw3s8,1183
aiogram/client/telegram.py,sha256=cu0HUZqwPzvZp-tMI3yewmnNZ78aTpFfEF0ZZVf0qAw,3080
aiogram/dispatcher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/dispatcher/__pycache__/__init__.cpython-311.pyc,,
aiogram/dispatcher/__pycache__/dispatcher.cpython-311.pyc,,
aiogram/dispatcher/__pycache__/flags.cpython-311.pyc,,
aiogram/dispatcher/__pycache__/router.cpython-311.pyc,,
aiogram/dispatcher/dispatcher.py,sha256=j3V5oi-nBUT1CBSBAs8lbCRQNIveZ9sVOJmUHnuJEqE,23205
aiogram/dispatcher/event/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/dispatcher/event/__pycache__/__init__.cpython-311.pyc,,
aiogram/dispatcher/event/__pycache__/bases.cpython-311.pyc,,
aiogram/dispatcher/event/__pycache__/event.cpython-311.pyc,,
aiogram/dispatcher/event/__pycache__/handler.cpython-311.pyc,,
aiogram/dispatcher/event/__pycache__/telegram.cpython-311.pyc,,
aiogram/dispatcher/event/bases.py,sha256=Piod0CEyGvBOwEuE7e70JMQal4XFilFD3NMinR2bEyE,871
aiogram/dispatcher/event/event.py,sha256=YXOroGX5Mj-5w6Jgl9oPf-muIp6ijeWjNJ3hEr2mA68,1352
aiogram/dispatcher/event/handler.py,sha256=rgV8j1x2a8qpmWCue1-uQ3fRzrc8qDdhKNJBkBnFSYo,3631
aiogram/dispatcher/event/telegram.py,sha256=hMY9TulAbD6nhNnoPOk1KhRa3SjzSP1xovNS6zT-bkU,4757
aiogram/dispatcher/flags.py,sha256=vEevCfweZKDCEW2tA5E3UbqMH-TjDBI0ujf7ktzsx9o,3479
aiogram/dispatcher/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/dispatcher/middlewares/__pycache__/__init__.cpython-311.pyc,,
aiogram/dispatcher/middlewares/__pycache__/base.cpython-311.pyc,,
aiogram/dispatcher/middlewares/__pycache__/error.cpython-311.pyc,,
aiogram/dispatcher/middlewares/__pycache__/manager.cpython-311.pyc,,
aiogram/dispatcher/middlewares/__pycache__/user_context.cpython-311.pyc,,
aiogram/dispatcher/middlewares/base.py,sha256=czbD9IDLoHb6pf6HisRGyG8pAQMLbC4R5k-iuTIWSNU,784
aiogram/dispatcher/middlewares/error.py,sha256=xDfQxTo9PT1qXvE35OGesDi7WydeQeB4zgT07eXM5G4,1119
aiogram/dispatcher/middlewares/manager.py,sha256=-ivUdff-Addpd_s_AlDA4RaVsMJmzgWwvt2DIqITnBw,2166
aiogram/dispatcher/middlewares/user_context.py,sha256=QKD5eDq1MkQRU3mNeTwwdgc6KZo9Ik_P4x-tf9u_vvg,7260
aiogram/dispatcher/router.py,sha256=mvFBRbleGea-TfZG496N1Fv33kg01HEG0v4pjrQbcUE,10703
aiogram/enums/__init__.py,sha256=oQH8dIPtHKcgQwX6k6-EIda0PLM_HWyyPy5Oft1BSoY,2014
aiogram/enums/__pycache__/__init__.cpython-311.pyc,,
aiogram/enums/__pycache__/bot_command_scope_type.cpython-311.pyc,,
aiogram/enums/__pycache__/chat_action.cpython-311.pyc,,
aiogram/enums/__pycache__/chat_boost_source_type.cpython-311.pyc,,
aiogram/enums/__pycache__/chat_member_status.cpython-311.pyc,,
aiogram/enums/__pycache__/chat_type.cpython-311.pyc,,
aiogram/enums/__pycache__/content_type.cpython-311.pyc,,
aiogram/enums/__pycache__/currency.cpython-311.pyc,,
aiogram/enums/__pycache__/dice_emoji.cpython-311.pyc,,
aiogram/enums/__pycache__/encrypted_passport_element.cpython-311.pyc,,
aiogram/enums/__pycache__/inline_query_result_type.cpython-311.pyc,,
aiogram/enums/__pycache__/input_media_type.cpython-311.pyc,,
aiogram/enums/__pycache__/input_paid_media_type.cpython-311.pyc,,
aiogram/enums/__pycache__/keyboard_button_poll_type_type.cpython-311.pyc,,
aiogram/enums/__pycache__/mask_position_point.cpython-311.pyc,,
aiogram/enums/__pycache__/menu_button_type.cpython-311.pyc,,
aiogram/enums/__pycache__/message_entity_type.cpython-311.pyc,,
aiogram/enums/__pycache__/message_origin_type.cpython-311.pyc,,
aiogram/enums/__pycache__/paid_media_type.cpython-311.pyc,,
aiogram/enums/__pycache__/parse_mode.cpython-311.pyc,,
aiogram/enums/__pycache__/passport_element_error_type.cpython-311.pyc,,
aiogram/enums/__pycache__/poll_type.cpython-311.pyc,,
aiogram/enums/__pycache__/reaction_type_type.cpython-311.pyc,,
aiogram/enums/__pycache__/revenue_withdrawal_state_type.cpython-311.pyc,,
aiogram/enums/__pycache__/sticker_format.cpython-311.pyc,,
aiogram/enums/__pycache__/sticker_type.cpython-311.pyc,,
aiogram/enums/__pycache__/topic_icon_color.cpython-311.pyc,,
aiogram/enums/__pycache__/transaction_partner_type.cpython-311.pyc,,
aiogram/enums/__pycache__/update_type.cpython-311.pyc,,
aiogram/enums/bot_command_scope_type.py,sha256=aPn5UKE6AN5BpK9kTH1ffJzG3qUOWFu5lyZOifMu5so,477
aiogram/enums/chat_action.py,sha256=KKItRU_RQuNzFMrAtc67Pn0aT4rZuuynpH6-yXfBhRY,972
aiogram/enums/chat_boost_source_type.py,sha256=80MfT0G_9-uv-7MsTs0lZjtJiA7EaUmqYd5b3vH6JtU,277
aiogram/enums/chat_member_status.py,sha256=OLr5ZfkZocicy1t5i30g7ufR8kujBPHENzR5UHnsDpc,334
aiogram/enums/chat_type.py,sha256=1Oi1TYITumT8jZritu1fgHNKDvK9otlAhPm4soyBKQQ,280
aiogram/enums/content_type.py,sha256=sSTHtq4LqeYB4J3zFug1HkhVuORSdcT7g91ZQTjOZrE,2311
aiogram/enums/currency.py,sha256=P5S_e7Y3wYmODiaK_8Fz0MHVwJXkt1P9Y3gd2SGo1iU,1563
aiogram/enums/dice_emoji.py,sha256=9KlOPTQlgt-6d3myDEWWUTB9H8BrE_LRaOZvBM2ceWs,303
aiogram/enums/encrypted_passport_element.py,sha256=m0-tAF9myf6FwPbsuJgj4AYvywzLMzzltUgrqWgGVws,704
aiogram/enums/inline_query_result_type.py,sha256=HGNN5aOg_jZbE4NNfHs21M-xK5cj2rBar4-C77T73YI,465
aiogram/enums/input_media_type.py,sha256=BfCDkzFOjrzJSo2syY2lq3E3JP8AdlGemp7qMnAupw0,291
aiogram/enums/input_paid_media_type.py,sha256=KoqJo7AelAiY8pmS1mRBuQLz97hedWFImEEVFnEoMmY,247
aiogram/enums/keyboard_button_poll_type_type.py,sha256=vPjequaOO5zySi7hiQjyVIfQl-jhtpgCGHYMR7DOYWY,324
aiogram/enums/mask_position_point.py,sha256=lhlx8U-9gtGn_LPiSOHWBe1u8YTLZCERs5x8DRmUubM,290
aiogram/enums/menu_button_type.py,sha256=4FDz7HKaVtfBlvXObCOVAUmyQQvRsyPi0Vu_Kc-HbDs,264
aiogram/enums/message_entity_type.py,sha256=00ioN4bPLNUYvCL_nJzp7rNUPhIqNOqoWY3PjiC4ob8,703
aiogram/enums/message_origin_type.py,sha256=qWHgXPzpuhsTcLx-huqNS1l36JSwqbsCY70_d20nreM,279
aiogram/enums/paid_media_type.py,sha256=FrGQyXq9bimIb8_b0hpuqYG4mWVruoBYkSC6drd7hw4,261
aiogram/enums/parse_mode.py,sha256=t5F3LaNLsUOaNQuYlQseWt36suA-oRgr_uScGEU8TKU,234
aiogram/enums/passport_element_error_type.py,sha256=HCcvvc8DDUNRD-mdFgFqMEFlr_4pa5Us5_U6pO2wXsc,471
aiogram/enums/poll_type.py,sha256=DluT-wQcOGqBgQimYLlO6OlTIIYnnbtHOmY9pjqVToo,200
aiogram/enums/reaction_type_type.py,sha256=hNCHqf9dVAWkgRgNBH50KSFsc7h9OKOwTcuGy7yh42M,251
aiogram/enums/revenue_withdrawal_state_type.py,sha256=ZVw59f9-RYG00TJdSHnZo_LTbdR_J8RmkhDCV4C8AFg,290
aiogram/enums/sticker_format.py,sha256=UmbwulTvkG7TFwS2twsblqiBj89jeEWY-y7P7Ah2qgo,235
aiogram/enums/sticker_type.py,sha256=XonGtnasf3tXwoD2DVRx5E16rHCE5VqgAFSfcFMIAGU,278
aiogram/enums/topic_icon_color.py,sha256=q-DilvJsgVmz5VafMOxj1BKJcMoHzpRrbatTPE4inxk,399
aiogram/enums/transaction_partner_type.py,sha256=zuEwp8BbEK69exgl3RecK5qLLAIWeJDAs-cZbCR5Dvg,383
aiogram/enums/update_type.py,sha256=t0xz16mEz1FiX0mihM0pu6A9AQ252ZM8JxvhiBJsfuY,1136
aiogram/exceptions.py,sha256=Y6wDVG2fyoczPn15U5epm_nnTx8eTC3CgX19SzyiqOE,5153
aiogram/filters/__init__.py,sha256=w7mWuJOIpLDZyUt1eznzMeF-o1UrstHQ-OYbBHHG6ek,1031
aiogram/filters/__pycache__/__init__.cpython-311.pyc,,
aiogram/filters/__pycache__/base.cpython-311.pyc,,
aiogram/filters/__pycache__/callback_data.cpython-311.pyc,,
aiogram/filters/__pycache__/chat_member_updated.cpython-311.pyc,,
aiogram/filters/__pycache__/command.cpython-311.pyc,,
aiogram/filters/__pycache__/exception.cpython-311.pyc,,
aiogram/filters/__pycache__/logic.cpython-311.pyc,,
aiogram/filters/__pycache__/magic_data.cpython-311.pyc,,
aiogram/filters/__pycache__/state.cpython-311.pyc,,
aiogram/filters/base.py,sha256=6mCB2nSjqskDDl9mKCZ3SghhwQ1jryaSTd5PvWii3bM,2005
aiogram/filters/callback_data.py,sha256=WncwuvQ9vEITtPT7YNQj-29bgRJ3o2lKp8tytC_AeNo,6227
aiogram/filters/chat_member_updated.py,sha256=3VsiMYLK6L7AfLS7RQRWwjXThWGN7gj4NAYnR0Wdr8I,7484
aiogram/filters/command.py,sha256=IXb54dwKHLz-Jaq2_HHn2si7r5iAr9pYW3sSxD8szCk,9957
aiogram/filters/exception.py,sha256=HbJIOVrXHh0_LkID6b-yn8Xow-llmLqKYmK0_tqB4YA,1488
aiogram/filters/logic.py,sha256=2M-fYY8m1ybX00wFn_8toRHj5alTfy4T6zkIkYrQZbM,2174
aiogram/filters/magic_data.py,sha256=Y07K9NLDXpXV0EuFR7GR7Yq4SzCYaD3XJsvDH-6JFkQ,718
aiogram/filters/state.py,sha256=A4GuSAo5KPdSZMetCC9crrfbJ-yghLunJUVYkOdBPTI,1448
aiogram/fsm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/fsm/__pycache__/__init__.cpython-311.pyc,,
aiogram/fsm/__pycache__/context.cpython-311.pyc,,
aiogram/fsm/__pycache__/middleware.cpython-311.pyc,,
aiogram/fsm/__pycache__/scene.cpython-311.pyc,,
aiogram/fsm/__pycache__/state.cpython-311.pyc,,
aiogram/fsm/__pycache__/strategy.cpython-311.pyc,,
aiogram/fsm/context.py,sha256=LC6KZoiVQNDmag9jTlCOoBQQ73pobx7E-ovcO_f9tjc,1427
aiogram/fsm/middleware.py,sha256=4MsKWzr93tzLTmv3dph3pKhznuX9EGsrhBhCb6mhzPo,3755
aiogram/fsm/scene.py,sha256=zXLdJDuIi7AMnILVc4dg-pHBUMkblLRSTVQZL2XiuRg,32622
aiogram/fsm/state.py,sha256=T0sPq1_JV5Jq1jVKu67bUQ0-y9Kqsk3hGpj7PgjpacQ,5640
aiogram/fsm/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/fsm/storage/__pycache__/__init__.cpython-311.pyc,,
aiogram/fsm/storage/__pycache__/base.cpython-311.pyc,,
aiogram/fsm/storage/__pycache__/memory.cpython-311.pyc,,
aiogram/fsm/storage/__pycache__/mongo.cpython-311.pyc,,
aiogram/fsm/storage/__pycache__/redis.cpython-311.pyc,,
aiogram/fsm/storage/base.py,sha256=1duD0gW2ikXhFW_Ag3g4Bzr6O0h_lW0hcs1vjYy6hpI,5939
aiogram/fsm/storage/memory.py,sha256=E-cSr5I5gLmE8tGmUd9RrghxOXKerlIPkb0izRjg8U0,2612
aiogram/fsm/storage/mongo.py,sha256=xGvz9rLr6ji8BhumZb1FTvQ5RvmlGquhLAPe1-vi9ms,4880
aiogram/fsm/storage/redis.py,sha256=7ZyaB-AMNEITjdYVn24Bha28_MIFRPX41mn5_0sEtiY,5322
aiogram/fsm/strategy.py,sha256=RUDc6kMae9ZFbyC1bdbAIRz6GoeaIHFzXCXRu8p3Kn0,1147
aiogram/handlers/__init__.py,sha256=XVErtJHhSmg4ytiDTP9J5pQY0RwBN1TsjT5v-Ls7RE4,800
aiogram/handlers/__pycache__/__init__.cpython-311.pyc,,
aiogram/handlers/__pycache__/base.cpython-311.pyc,,
aiogram/handlers/__pycache__/callback_query.cpython-311.pyc,,
aiogram/handlers/__pycache__/chat_member.cpython-311.pyc,,
aiogram/handlers/__pycache__/chosen_inline_result.cpython-311.pyc,,
aiogram/handlers/__pycache__/error.cpython-311.pyc,,
aiogram/handlers/__pycache__/inline_query.cpython-311.pyc,,
aiogram/handlers/__pycache__/message.cpython-311.pyc,,
aiogram/handlers/__pycache__/poll.cpython-311.pyc,,
aiogram/handlers/__pycache__/pre_checkout_query.cpython-311.pyc,,
aiogram/handlers/__pycache__/shipping_query.cpython-311.pyc,,
aiogram/handlers/base.py,sha256=SKLNknDSxAItRHwPMJanOPFztWakJQCun4myH9TBNik,1097
aiogram/handlers/callback_query.py,sha256=Ufz3dpue809iUWaXP_cBmaZLEvlI1l3JdjkqN3UoHOs,1027
aiogram/handlers/chat_member.py,sha256=TCr1m47duHFUpL92W56ok0hF1k0NN3Yk88-1zzEsYmw,322
aiogram/handlers/chosen_inline_result.py,sha256=NjAPc5EgBb8V4p3WVpcPNPd8b1OApymvBN-I75EYMQo,410
aiogram/handlers/error.py,sha256=86pedHPBgYxXYwrW00s0cadf_lRP6TeHEG0goJ6zNJk,352
aiogram/handlers/inline_query.py,sha256=sOkT3uMuvamMnSTxct-cdQFng6GBqaOlSNV4aHUbVPQ,381
aiogram/handlers/message.py,sha256=I6R4TJ8V6bbGVppmxmGSxIkCzeN9JMn1CDojywWrt50,721
aiogram/handlers/poll.py,sha256=Bn075JIux1lNPjKWH3woV_OzMcvPgPvtkgp4W22mUX8,396
aiogram/handlers/pre_checkout_query.py,sha256=ZO0iZFVwRz4CtBWIVLds8JoHhMPqCahz6lwOfR4dEkg,321
aiogram/handlers/shipping_query.py,sha256=ku14YRYvddTbHeKoIwb2oDpfrpsetFC1m2BwIsNwoAs,314
aiogram/loggers.py,sha256=3MCGSmwgXXsWbgtSaTc6bPSwVOIe8FkQZglc0QLVvsY,257
aiogram/methods/__init__.py,sha256=8_DZywxqZ1jw4j2qzHzLtaakG3ziLhg671HeEGrhqN4,9978
aiogram/methods/__pycache__/__init__.cpython-311.pyc,,
aiogram/methods/__pycache__/add_sticker_to_set.cpython-311.pyc,,
aiogram/methods/__pycache__/answer_callback_query.cpython-311.pyc,,
aiogram/methods/__pycache__/answer_inline_query.cpython-311.pyc,,
aiogram/methods/__pycache__/answer_pre_checkout_query.cpython-311.pyc,,
aiogram/methods/__pycache__/answer_shipping_query.cpython-311.pyc,,
aiogram/methods/__pycache__/answer_web_app_query.cpython-311.pyc,,
aiogram/methods/__pycache__/approve_chat_join_request.cpython-311.pyc,,
aiogram/methods/__pycache__/ban_chat_member.cpython-311.pyc,,
aiogram/methods/__pycache__/ban_chat_sender_chat.cpython-311.pyc,,
aiogram/methods/__pycache__/base.cpython-311.pyc,,
aiogram/methods/__pycache__/close.cpython-311.pyc,,
aiogram/methods/__pycache__/close_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/close_general_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/copy_message.cpython-311.pyc,,
aiogram/methods/__pycache__/copy_messages.cpython-311.pyc,,
aiogram/methods/__pycache__/create_chat_invite_link.cpython-311.pyc,,
aiogram/methods/__pycache__/create_chat_subscription_invite_link.cpython-311.pyc,,
aiogram/methods/__pycache__/create_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/create_invoice_link.cpython-311.pyc,,
aiogram/methods/__pycache__/create_new_sticker_set.cpython-311.pyc,,
aiogram/methods/__pycache__/decline_chat_join_request.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_chat_photo.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_chat_sticker_set.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_message.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_messages.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_my_commands.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_sticker_from_set.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_sticker_set.cpython-311.pyc,,
aiogram/methods/__pycache__/delete_webhook.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_chat_invite_link.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_chat_subscription_invite_link.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_general_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_message_caption.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_message_live_location.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_message_media.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_message_reply_markup.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_message_text.cpython-311.pyc,,
aiogram/methods/__pycache__/edit_user_star_subscription.cpython-311.pyc,,
aiogram/methods/__pycache__/export_chat_invite_link.cpython-311.pyc,,
aiogram/methods/__pycache__/forward_message.cpython-311.pyc,,
aiogram/methods/__pycache__/forward_messages.cpython-311.pyc,,
aiogram/methods/__pycache__/get_available_gifts.cpython-311.pyc,,
aiogram/methods/__pycache__/get_business_connection.cpython-311.pyc,,
aiogram/methods/__pycache__/get_chat.cpython-311.pyc,,
aiogram/methods/__pycache__/get_chat_administrators.cpython-311.pyc,,
aiogram/methods/__pycache__/get_chat_member.cpython-311.pyc,,
aiogram/methods/__pycache__/get_chat_member_count.cpython-311.pyc,,
aiogram/methods/__pycache__/get_chat_menu_button.cpython-311.pyc,,
aiogram/methods/__pycache__/get_custom_emoji_stickers.cpython-311.pyc,,
aiogram/methods/__pycache__/get_file.cpython-311.pyc,,
aiogram/methods/__pycache__/get_forum_topic_icon_stickers.cpython-311.pyc,,
aiogram/methods/__pycache__/get_game_high_scores.cpython-311.pyc,,
aiogram/methods/__pycache__/get_me.cpython-311.pyc,,
aiogram/methods/__pycache__/get_my_commands.cpython-311.pyc,,
aiogram/methods/__pycache__/get_my_default_administrator_rights.cpython-311.pyc,,
aiogram/methods/__pycache__/get_my_description.cpython-311.pyc,,
aiogram/methods/__pycache__/get_my_name.cpython-311.pyc,,
aiogram/methods/__pycache__/get_my_short_description.cpython-311.pyc,,
aiogram/methods/__pycache__/get_star_transactions.cpython-311.pyc,,
aiogram/methods/__pycache__/get_sticker_set.cpython-311.pyc,,
aiogram/methods/__pycache__/get_updates.cpython-311.pyc,,
aiogram/methods/__pycache__/get_user_chat_boosts.cpython-311.pyc,,
aiogram/methods/__pycache__/get_user_profile_photos.cpython-311.pyc,,
aiogram/methods/__pycache__/get_webhook_info.cpython-311.pyc,,
aiogram/methods/__pycache__/hide_general_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/leave_chat.cpython-311.pyc,,
aiogram/methods/__pycache__/log_out.cpython-311.pyc,,
aiogram/methods/__pycache__/pin_chat_message.cpython-311.pyc,,
aiogram/methods/__pycache__/promote_chat_member.cpython-311.pyc,,
aiogram/methods/__pycache__/refund_star_payment.cpython-311.pyc,,
aiogram/methods/__pycache__/reopen_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/reopen_general_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/replace_sticker_in_set.cpython-311.pyc,,
aiogram/methods/__pycache__/restrict_chat_member.cpython-311.pyc,,
aiogram/methods/__pycache__/revoke_chat_invite_link.cpython-311.pyc,,
aiogram/methods/__pycache__/save_prepared_inline_message.cpython-311.pyc,,
aiogram/methods/__pycache__/send_animation.cpython-311.pyc,,
aiogram/methods/__pycache__/send_audio.cpython-311.pyc,,
aiogram/methods/__pycache__/send_chat_action.cpython-311.pyc,,
aiogram/methods/__pycache__/send_contact.cpython-311.pyc,,
aiogram/methods/__pycache__/send_dice.cpython-311.pyc,,
aiogram/methods/__pycache__/send_document.cpython-311.pyc,,
aiogram/methods/__pycache__/send_game.cpython-311.pyc,,
aiogram/methods/__pycache__/send_gift.cpython-311.pyc,,
aiogram/methods/__pycache__/send_invoice.cpython-311.pyc,,
aiogram/methods/__pycache__/send_location.cpython-311.pyc,,
aiogram/methods/__pycache__/send_media_group.cpython-311.pyc,,
aiogram/methods/__pycache__/send_message.cpython-311.pyc,,
aiogram/methods/__pycache__/send_paid_media.cpython-311.pyc,,
aiogram/methods/__pycache__/send_photo.cpython-311.pyc,,
aiogram/methods/__pycache__/send_poll.cpython-311.pyc,,
aiogram/methods/__pycache__/send_sticker.cpython-311.pyc,,
aiogram/methods/__pycache__/send_venue.cpython-311.pyc,,
aiogram/methods/__pycache__/send_video.cpython-311.pyc,,
aiogram/methods/__pycache__/send_video_note.cpython-311.pyc,,
aiogram/methods/__pycache__/send_voice.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_administrator_custom_title.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_description.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_menu_button.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_permissions.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_photo.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_sticker_set.cpython-311.pyc,,
aiogram/methods/__pycache__/set_chat_title.cpython-311.pyc,,
aiogram/methods/__pycache__/set_custom_emoji_sticker_set_thumbnail.cpython-311.pyc,,
aiogram/methods/__pycache__/set_game_score.cpython-311.pyc,,
aiogram/methods/__pycache__/set_message_reaction.cpython-311.pyc,,
aiogram/methods/__pycache__/set_my_commands.cpython-311.pyc,,
aiogram/methods/__pycache__/set_my_default_administrator_rights.cpython-311.pyc,,
aiogram/methods/__pycache__/set_my_description.cpython-311.pyc,,
aiogram/methods/__pycache__/set_my_name.cpython-311.pyc,,
aiogram/methods/__pycache__/set_my_short_description.cpython-311.pyc,,
aiogram/methods/__pycache__/set_passport_data_errors.cpython-311.pyc,,
aiogram/methods/__pycache__/set_sticker_emoji_list.cpython-311.pyc,,
aiogram/methods/__pycache__/set_sticker_keywords.cpython-311.pyc,,
aiogram/methods/__pycache__/set_sticker_mask_position.cpython-311.pyc,,
aiogram/methods/__pycache__/set_sticker_position_in_set.cpython-311.pyc,,
aiogram/methods/__pycache__/set_sticker_set_thumbnail.cpython-311.pyc,,
aiogram/methods/__pycache__/set_sticker_set_title.cpython-311.pyc,,
aiogram/methods/__pycache__/set_user_emoji_status.cpython-311.pyc,,
aiogram/methods/__pycache__/set_webhook.cpython-311.pyc,,
aiogram/methods/__pycache__/stop_message_live_location.cpython-311.pyc,,
aiogram/methods/__pycache__/stop_poll.cpython-311.pyc,,
aiogram/methods/__pycache__/unban_chat_member.cpython-311.pyc,,
aiogram/methods/__pycache__/unban_chat_sender_chat.cpython-311.pyc,,
aiogram/methods/__pycache__/unhide_general_forum_topic.cpython-311.pyc,,
aiogram/methods/__pycache__/unpin_all_chat_messages.cpython-311.pyc,,
aiogram/methods/__pycache__/unpin_all_forum_topic_messages.cpython-311.pyc,,
aiogram/methods/__pycache__/unpin_all_general_forum_topic_messages.cpython-311.pyc,,
aiogram/methods/__pycache__/unpin_chat_message.cpython-311.pyc,,
aiogram/methods/__pycache__/upload_sticker_file.cpython-311.pyc,,
aiogram/methods/add_sticker_to_set.py,sha256=PR2B-SDNNYLaH70oevadRNAoiUjFQXLffGSIhQ7uah4,1443
aiogram/methods/answer_callback_query.py,sha256=YVozC3bMDE8YoH6x5TipzWiXCwdWLAU96GWOJTxf1AE,2903
aiogram/methods/answer_inline_query.py,sha256=J6j2jMm_Ag4mPQpygCiYmnwpQDpacJ1QJI98fQ8Ec6I,6011
aiogram/methods/answer_pre_checkout_query.py,sha256=5peOULN2DO0VoK3nEAnsN-E9RWAH5ab64UUc-4apRHk,2172
aiogram/methods/answer_shipping_query.py,sha256=o-yGoUslrd0TCOJTtLQXFMaJysyhknKoMz74eDJD_Cc,2248
aiogram/methods/answer_web_app_query.py,sha256=AXM-hDf_xIarwburmGT27ZoKFoGsbcVbPNM_EHUHKVo,3676
aiogram/methods/approve_chat_join_request.py,sha256=KUetMV7jHSB9_v702JlSI-CVlJMHUCNstvjUC0GU3i4,1281
aiogram/methods/ban_chat_member.py,sha256=hfHkT_Ucg-26oVtvRQwGvijDMn7uaV84agEY7vIEp-E,2481
aiogram/methods/ban_chat_sender_chat.py,sha256=rr77ixJufnD9bWevxTaSHCcJVh3lIf19JGLEctMMm7A,1569
aiogram/methods/base.py,sha256=MJCjm1i9q6mm0gDvt_MErGojvaxkzOfPPyAhc3oWyrQ,2732
aiogram/methods/close.py,sha256=Bnzle__tO6tCMMLHkN1E00ypkrdQ0RjhkBNxhjDOq3k,593
aiogram/methods/close_forum_topic.py,sha256=njPMjFb7CkLYbjEuNWPrSDh2HSpqlmO76B9Xs_Xahco,1475
aiogram/methods/close_general_forum_topic.py,sha256=lLWx-ZgE4eV15D2yLXg1wWVECfcN7_1y2XA5vqr1d84,1223
aiogram/methods/copy_message.py,sha256=stDcUXFLuB2UJUOJv8UkSmHepYxg2d7cEwx4hgJ91Ro,6566
aiogram/methods/copy_messages.py,sha256=RFANcFfHrKypebIvjPqRDFy_QL8s4csFeNvd66GpCEk,3287
aiogram/methods/create_chat_invite_link.py,sha256=Snalw6e_zk5g5FI2wKB4Pr0qbGS1PbjIcq7567RC5YE,2545
aiogram/methods/create_chat_subscription_invite_link.py,sha256=ulylrmNJXJa7hipKx7j_2wOeZSVgufPaW2h5JFO2OIw,2508
aiogram/methods/create_forum_topic.py,sha256=FuJ8R-jkpfk7hPKe6pffiUQE21Hhh22G7cFMCFe5kok,2188
aiogram/methods/create_invoice_link.py,sha256=w9lcW0IqeNNZn42dyEBFTpK8OCgBkeEnabzFuAcB6WE,7572
aiogram/methods/create_new_sticker_set.py,sha256=GqZvQOMXgN1gf44dma_soOIQx1B1eOonyQX1OFaxFxQ,2922
aiogram/methods/decline_chat_join_request.py,sha256=19Sj8OHe6erv3_E4p-Sw_y8XygNYOWrPI5S23CU5P1w,1281
aiogram/methods/delete_chat_photo.py,sha256=0nrFbxWyO7R0pVMAWook7VRXAIU-AeEr_wpiFvyLssQ,1194
aiogram/methods/delete_chat_sticker_set.py,sha256=KaRyvzLt1ystCbkptimtxqUM_4_RzpI7Sl91heSVlKc,1348
aiogram/methods/delete_forum_topic.py,sha256=LrncMipGRW2Be4bYFzSYMjydvRijmPU5Owtmb61e374,1470
aiogram/methods/delete_message.py,sha256=wCMd8oHwYETY1Nk2vP4DYhu-sdrm-LqhQBKPAgQQhSY,1956
aiogram/methods/delete_messages.py,sha256=at3mIij12NykE2s8ABSfCf26CkNGEe_Zs5Am-I-0C84,1395
aiogram/methods/delete_my_commands.py,sha256=LiYVWXeqBxs_hUbq6pQ1h3zSKUyzw7ahdaXb1esAH8Q,2608
aiogram/methods/delete_sticker_from_set.py,sha256=J2PsBRRTBks5TDlp4mlmubem7MSWKh8jzJMmLJQV9CI,944
aiogram/methods/delete_sticker_set.py,sha256=csUT6RkdqmOy0K7vT2QXXR_YJfpQ78S31WYttFqEPY8,908
aiogram/methods/delete_webhook.py,sha256=7kfmOOSlujfXV5JZEozbuQQzNrX5YV0f6nMvUo5aw_k,1155
aiogram/methods/edit_chat_invite_link.py,sha256=N2RqH2DHbRK-zF97jtHlhMV2YykBgHgpqw_iDz8KQRw,2564
aiogram/methods/edit_chat_subscription_invite_link.py,sha256=AvDlMClqa00KJnf4sXZkdOYGBErGtuwLMeHEM0AND5E,1610
aiogram/methods/edit_forum_topic.py,sha256=hELFkBuB_6I3_y2xencOolPXPUjEK3xXCgfzC4lAuCA,2195
aiogram/methods/edit_general_forum_topic.py,sha256=PK37qgJRVISQWhYAWlg3LNGmgneVdQxpJCa1dMiq_uY,1306
aiogram/methods/edit_message_caption.py,sha256=P4F2rg8DVZN5gxUeupY6UtoseQueoqosJqrwoUdsGLk,3971
aiogram/methods/edit_message_live_location.py,sha256=akOy16TytMIR1ONYR8lu8W48HplUl9ugqW-tttrn2Zk,4205
aiogram/methods/edit_message_media.py,sha256=1vAqyZI_oK_65k1hBHRKEMi4C1VMmYGXTec5jeBN7wk,3563
aiogram/methods/edit_message_reply_markup.py,sha256=H5elelToPCzLoOsGDDfOa7nXRQ10AMhxP13quMGRt1k,2658
aiogram/methods/edit_message_text.py,sha256=uoMX4ximNBtcovFBkGAvKIjulDlsdI6QAV7oMvyEtNk,4396
aiogram/methods/edit_user_star_subscription.py,sha256=d1iE8CbFm-gDcG98xFfPbW8oaXDPtuD65FfwofTr20g,1678
aiogram/methods/export_chat_invite_link.py,sha256=oC00_UAsmJ7icNtjmnCbQ9sYyJLoOujNJsLIRMyx9qg,1792
aiogram/methods/forward_message.py,sha256=LS52l47_L5G2TvVZ3WfckJhxVcl_qFhJqR9mpwA-9AU,2608
aiogram/methods/forward_messages.py,sha256=wZhYypMBmtAGA-Ecq6UkSd3wFqFvDsilZGPgE0SxLoE,2768
aiogram/methods/get_available_gifts.py,sha256=7I7-5hS5MoRnlPbkp8kjxo50mdRQDBY3BT6lRRGEAro,446
aiogram/methods/get_business_connection.py,sha256=wJhKMcBoLcOXTxK1_xG8SR2h85e1sW1C4sWD6L6bTTM,1200
aiogram/methods/get_chat.py,sha256=yOPjLV12HVZOk6ch2bUOfyrZBPIhyAXhhHEhiZKEdro,1145
aiogram/methods/get_chat_administrators.py,sha256=7g9dt3fKyJxaFSIaCOTDeE43yQIllAFBmQ9GyohUygI,1809
aiogram/methods/get_chat_member.py,sha256=pltcJQL_aSljObA_XAVa9chzyC5saahC-jCA85U0Z_E,1857
aiogram/methods/get_chat_member_count.py,sha256=phZ05LJUn3EAXBuH_7HqMFOuTg99nyVNjNEVWry1sto,1067
aiogram/methods/get_chat_menu_button.py,sha256=Ko5U5PMvTP08x8SkCUddFASaGr-LbtP_4GhvbM-TP7c,1362
aiogram/methods/get_custom_emoji_stickers.py,sha256=mj0agT_CquoIu48YeAiy0JWb6ZdGXKiWGT5dnu5xwls,1195
aiogram/methods/get_file.py,sha256=FutkpYaOz_xjdU_1FwGY75ZYZ-uqBCZapZBiE5K9sC0,1571
aiogram/methods/get_forum_topic_icon_stickers.py,sha256=KbkTvUq447TgRKJ2ak8fkZrNpiDs9f4DFcGy0Yjs7YM,532
aiogram/methods/get_game_high_scores.py,sha256=seFMcDr_LE0lDUoJ2nPLfzO-5ZjPyF6gyOfxVcDEOhk,2150
aiogram/methods/get_me.py,sha256=gA4vD-v3WYPpZaSTy6mpGZmsV5WXdSHv06EymV9ZQnU,439
aiogram/methods/get_my_commands.py,sha256=uvb8iJUUifDO9o953ilIUVFielX9zPgDhF8mBIRgWFs,2458
aiogram/methods/get_my_default_administrator_rights.py,sha256=dw1MnQACVfVsQeYs5ikZG1jThB15HR8Trv1xHg_oSLY,1359
aiogram/methods/get_my_description.py,sha256=UzgJhr6sl4i4RD9LIYxvTx4z4V3cZMGQavCFucWwv1I,1156
aiogram/methods/get_my_name.py,sha256=GGY2ZnZ_uXL5GqRKHJ2uXs5v_LUBqg6yx0NdrBLZoy8,1057
aiogram/methods/get_my_short_description.py,sha256=yRQ0ertSdcbEy32FC07FAo12q7eyLEN04h0gkxzpmGE,1203
aiogram/methods/get_star_transactions.py,sha256=kCp-vAuYCbogKN6PQ4_ulx4ByBatWbMwuXUTsMib22M,1374
aiogram/methods/get_sticker_set.py,sha256=oh9jEInEgKDfOhAu58ex4rUYRCJCk-BTsOAvNa7GPMg,965
aiogram/methods/get_updates.py,sha256=LvWmTo059jtv0kVKLUw284FqoelqmVbzJ0weMTJrlcs,3018
aiogram/methods/get_user_chat_boosts.py,sha256=2HYUsuZMxrP-YdZO7S4rX0QItzjUN8H01mmwvm2SSkc,1255
aiogram/methods/get_user_profile_photos.py,sha256=URseFqr69kRj4-gqNkSl4TsiEaAK78iAyjYrTv4tCpA,1507
aiogram/methods/get_webhook_info.py,sha256=8h38GW1au2fAZau2qmXuOnmu-H6kIjXQEms3VKLJf5c,576
aiogram/methods/hide_general_forum_topic.py,sha256=dKPkMB4okbduRG9K-Mah5hf6OsKzGg8NrtnTQdcVu78,1270
aiogram/methods/leave_chat.py,sha256=m3F4mKwOmJaxk958rbBZqGIRcM7007JMce1S2zpEvPY,1063
aiogram/methods/log_out.py,sha256=yBkCbabI7CRBkTHwhJ61KmqazMxiUuGRMhxlzl7BWB4,660
aiogram/methods/pin_chat_message.py,sha256=njAbF_yEAZobt9b-0CqxblQpJ2M-jAgZHDpKdw_THAI,2143
aiogram/methods/promote_chat_member.py,sha256=BDT9L1MIVTwWrmph-HUe0Rh7kKMPQt4bkMkICPiDfgE,5568
aiogram/methods/refund_star_payment.py,sha256=s-eELWhpYoE8ubWNvlYc0dmPGCcBiATS0fU50IXXZ5g,1264
aiogram/methods/reopen_forum_topic.py,sha256=TfJtX4zGVNYjjhivHRwBFV9sbWINhr8k_q5lXoLtITw,1480
aiogram/methods/reopen_general_forum_topic.py,sha256=kxJtoOauJNWYxqesoT6qeDvXAJujUZvK-3QO5lnFEw8,1287
aiogram/methods/replace_sticker_in_set.py,sha256=df80oQuwZsYrbmUixIPxtQ75iUqIMZXsiIyoMZHious,1858
aiogram/methods/restrict_chat_member.py,sha256=KFAtkHVDqA4Ny4quy1jCnqH1mxf_5kSo8kDlA_ben4Q,2751
aiogram/methods/revoke_chat_invite_link.py,sha256=RrIN4jcxTHaoThF0Dru3d4z7RGEQk0u0cTkNx13Do-s,1526
aiogram/methods/save_prepared_inline_message.py,sha256=y4LJCL3T5DR9s9IhBmc3dePVMB5zTtMibs5HLse0wwg,5447
aiogram/methods/send_animation.py,sha256=eHPbpRE3KlKpjiP-Yw3Eb1EvL9wT-iX1uejZlj6fPr8,8135
aiogram/methods/send_audio.py,sha256=-eNzLTi4zUWwWGN-zQM8ds7mC6_2b65Bjq7orC7AKoA,7591
aiogram/methods/send_chat_action.py,sha256=sOrGWv2bA9Przot-gPT1ksZ8UEhPpJeEsd7tPd-06gg,3187
aiogram/methods/send_contact.py,sha256=eIOOegKWknSn6JLFLP2YvmGb8QZEBfzGaSkPIH8he_o,5516
aiogram/methods/send_dice.py,sha256=-CpttmcZKVBUWealLfPaN1NPdrgJRmMnPP5rxmLkDK0,5286
aiogram/methods/send_document.py,sha256=qnxEsi7FDUq0inpZzrcIEWljlJYr55oQ7pJee6ZL2U4,7343
aiogram/methods/send_game.py,sha256=JH6trpdVW0Z_9y3QXDuy16TUzjswgQOLx5YWrXoPa9E,4706
aiogram/methods/send_gift.py,sha256=OJoKwSaDZVWUGP6Kia3F3VE6wUWKSwonUPY-zkRfZu8,2221
aiogram/methods/send_invoice.py,sha256=Zmx_jOYodnRXKSF72DhXNdeRRLEd2_3JXT6nQloEc7o,10595
aiogram/methods/send_location.py,sha256=lKr-LIi7STxe17Aj6firxAEuhx0PUn4MVkwj-Hl-Um8,6314
aiogram/methods/send_media_group.py,sha256=8U9jpASibib1zjO1oazUgSJp8PW4KXQr7TJu4q5NKf4,4795
aiogram/methods/send_message.py,sha256=L2-Q2VmLDA7056Usbd15MfsKHpQ-HJnqQ44l1WP7SNE,6569
aiogram/methods/send_paid_media.py,sha256=D0g98FF2EuHEnYkNZpC2d70t3zradPhmSDBHxJHkfYI,5364
aiogram/methods/send_photo.py,sha256=7ROPItBh4kiJQX-P6bxXbl1nkKdegfzh0OGi7go5rcM,6949
aiogram/methods/send_poll.py,sha256=seHWxfLgBrOYybsLnb_q3Owjfth52Kxlue5rmO2QrmA,8844
aiogram/methods/send_sticker.py,sha256=IV5Piwr93mKvOYniUnwR39UkYraW2vkmEQtxoMzo39s,5747
aiogram/methods/send_venue.py,sha256=bpAEscX-0BkoUUXqqQyhbei5q-3The4xqLncEGIUUns,6283
aiogram/methods/send_video.py,sha256=5HpObStieg1HXVK48eakvjS2Njk98PhfZNLt5BhuyuQ,8351
aiogram/methods/send_video_note.py,sha256=ONmBp07CbWQ1K3aAve92bCMg5qSq-3hzjFMCe3zJ15E,6501
aiogram/methods/send_voice.py,sha256=I_ZoBjg8i-n3xXkEZpWrdgsiC5x7tT2E6Ftw4aP_-dM,6739
aiogram/methods/set_chat_administrator_custom_title.py,sha256=lOizk5Z8-tUh6EOiZ2hRyVSUrP2qQzDHlcKIbJCKgSg,1490
aiogram/methods/set_chat_description.py,sha256=AvzTU_XRaTuI9s9iKX_hihCLpsH4F8du3dBQ-wrNZnU,1407
aiogram/methods/set_chat_menu_button.py,sha256=5y8sOvgs6cH4sWBa_NuBnsRH7sE8ch0_s4YygG1PbZY,1622
aiogram/methods/set_chat_permissions.py,sha256=x42Gmf910ycY0Nf5lCnM1Xe6z5bhjNqUMHA673X1J_A,2159
aiogram/methods/set_chat_photo.py,sha256=kHjTl8U-i5B03KaFIEKxQnT8Sgl8CAa_F9kQeFUW_9M,1394
aiogram/methods/set_chat_sticker_set.py,sha256=RFNtIcj8Lq8It6L6qoJLCk247RYVgGROdJM5UoF0HSQ,1571
aiogram/methods/set_chat_title.py,sha256=X21F3pX37t3z13-4o6WJfQHKVfLPr8c_HGO-vNqI3k0,1275
aiogram/methods/set_custom_emoji_sticker_set_thumbnail.py,sha256=E8zURRFNnJnjzxVnYchBta807eCrarjMzpKWp0R5Ypg,1309
aiogram/methods/set_game_score.py,sha256=RDiYH61W1aN6WZoRLKI_vDQJz8-7V1I8tP__UbZLw1s,2626
aiogram/methods/set_message_reaction.py,sha256=QZv0CHqM8UKe7_lKz5g2CQG4QBSQPtRJB2M9fXALCEw,2451
aiogram/methods/set_my_commands.py,sha256=zecvxR_yBdwdLmTL-y8-eYL0X8MGGYA6zIzf7gDcwTs,2797
aiogram/methods/set_my_default_administrator_rights.py,sha256=DKX3KXLXbfv-heE5uJfPbcFXQRvNG6ps3ih1bQNa5RY,1750
aiogram/methods/set_my_description.py,sha256=ww9IAjtC4X6Ti9cFlnE3dleZ_FKE6byjblGUYn2EqwI,1489
aiogram/methods/set_my_name.py,sha256=xqSgEYS3Rgr2ETQlvBuD_VopBsuDX_DZY_MnbsOoJgs,1274
aiogram/methods/set_my_short_description.py,sha256=ONiD6HfZxxnHRjPstEmuBqedjQVjZ-lSJPY14sTRPyc,1639
aiogram/methods/set_passport_data_errors.py,sha256=vw5FhG7o1N0q8bdBGnp7xmt9lmC2M2qqM-kcRlPkolM,2916
aiogram/methods/set_sticker_emoji_list.py,sha256=H4PY-fc-SABtoWZ7Vn4XjNYWEO5702xnt8jRpusU89E,1195
aiogram/methods/set_sticker_keywords.py,sha256=mINPtlVYRMpD6nfDTP78wafUNt5LNhub1js78w6T848,1314
aiogram/methods/set_sticker_mask_position.py,sha256=eoT5D9VmWuRNsUqyzrHlYWd_bVaK583o3hP_KsoevaI,1450
aiogram/methods/set_sticker_position_in_set.py,sha256=QJI3x-tobyjpFoEThjcqGQLjXWdCZTd59ei49Gi2Mcg,1100
aiogram/methods/set_sticker_set_thumbnail.py,sha256=l6hozWYpPQI-Y-QI2YxyXopG8ZXzGme-q2vdl4SUJvk,2891
aiogram/methods/set_sticker_set_title.py,sha256=kf1H5K3capHR5wlu91DXmP6Q1G_TG_AEJeivppuM-58,1011
aiogram/methods/set_user_emoji_status.py,sha256=vaItcEyVzfCwNB7Rr6OwBcUvkNcW1JplZRsyFFH42qA,1921
aiogram/methods/set_webhook.py,sha256=NBp2lbCFqapLawqhVTKdMw-SWUHoKqauGhT12ASkP4w,4470
aiogram/methods/stop_message_live_location.py,sha256=ZvsRZ_qADXt3Oxxk8-6nvOAHT5b-yL2v4isAxNChE7M,2539
aiogram/methods/stop_poll.py,sha256=xs5Bhzdwrny9CueVr1qfe6lOWjPqKoS8te9PYvpTZs4,1933
aiogram/methods/unban_chat_member.py,sha256=kCOiIN03O3RZG4esxneAnt87-iC24TKqW4FeAOcBPCY,1919
aiogram/methods/unban_chat_sender_chat.py,sha256=JwiCTYpl797vXH1iKEmHdvngNgmN3N5MzTM6Bd5QI3o,1376
aiogram/methods/unhide_general_forum_topic.py,sha256=vDxVKNhnBifPmhBxDtIgEGq9DLw5U0E-HUPoIVOEL_I,1223
aiogram/methods/unpin_all_chat_messages.py,sha256=1eYhMh4IsLV4x94XEC7oJkkFeiwe1LSpr_sj2ywyvUY,1303
aiogram/methods/unpin_all_forum_topic_messages.py,sha256=RDUz0D8kSuayzY0DU0DVo6ez2dUH8VdqBmTSthCECCc,1489
aiogram/methods/unpin_all_general_forum_topic_messages.py,sha256=O_PhZuwarHW9I2iFIHggOqgc7QfXtF1GEqZYweOH5PA,1248
aiogram/methods/unpin_chat_message.py,sha256=9iiJcGt8sU1vYKKeMGpk7KgMvTXnLysywExpsjWckgs,1978
aiogram/methods/upload_sticker_file.py,sha256=TgGArcP8fVpAOM7GRmpbk7GO3sKn2PfBEV--WOo0NKo,1951
aiogram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/types/__init__.py,sha256=PlphfdL3sP9HmBO_yeShU1y0UPDnNBWUzruwgjoO4qg,19114
aiogram/types/__pycache__/__init__.cpython-311.pyc,,
aiogram/types/__pycache__/affiliate_info.cpython-311.pyc,,
aiogram/types/__pycache__/animation.cpython-311.pyc,,
aiogram/types/__pycache__/audio.cpython-311.pyc,,
aiogram/types/__pycache__/background_fill.cpython-311.pyc,,
aiogram/types/__pycache__/background_fill_freeform_gradient.cpython-311.pyc,,
aiogram/types/__pycache__/background_fill_gradient.cpython-311.pyc,,
aiogram/types/__pycache__/background_fill_solid.cpython-311.pyc,,
aiogram/types/__pycache__/background_type.cpython-311.pyc,,
aiogram/types/__pycache__/background_type_chat_theme.cpython-311.pyc,,
aiogram/types/__pycache__/background_type_fill.cpython-311.pyc,,
aiogram/types/__pycache__/background_type_pattern.cpython-311.pyc,,
aiogram/types/__pycache__/background_type_wallpaper.cpython-311.pyc,,
aiogram/types/__pycache__/base.cpython-311.pyc,,
aiogram/types/__pycache__/birthdate.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_all_chat_administrators.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_all_group_chats.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_all_private_chats.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_chat.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_chat_administrators.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_chat_member.cpython-311.pyc,,
aiogram/types/__pycache__/bot_command_scope_default.cpython-311.pyc,,
aiogram/types/__pycache__/bot_description.cpython-311.pyc,,
aiogram/types/__pycache__/bot_name.cpython-311.pyc,,
aiogram/types/__pycache__/bot_short_description.cpython-311.pyc,,
aiogram/types/__pycache__/business_connection.cpython-311.pyc,,
aiogram/types/__pycache__/business_intro.cpython-311.pyc,,
aiogram/types/__pycache__/business_location.cpython-311.pyc,,
aiogram/types/__pycache__/business_messages_deleted.cpython-311.pyc,,
aiogram/types/__pycache__/business_opening_hours.cpython-311.pyc,,
aiogram/types/__pycache__/business_opening_hours_interval.cpython-311.pyc,,
aiogram/types/__pycache__/callback_game.cpython-311.pyc,,
aiogram/types/__pycache__/callback_query.cpython-311.pyc,,
aiogram/types/__pycache__/chat.cpython-311.pyc,,
aiogram/types/__pycache__/chat_administrator_rights.cpython-311.pyc,,
aiogram/types/__pycache__/chat_background.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_added.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_removed.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_source.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_source_gift_code.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_source_giveaway.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_source_premium.cpython-311.pyc,,
aiogram/types/__pycache__/chat_boost_updated.cpython-311.pyc,,
aiogram/types/__pycache__/chat_full_info.cpython-311.pyc,,
aiogram/types/__pycache__/chat_invite_link.cpython-311.pyc,,
aiogram/types/__pycache__/chat_join_request.cpython-311.pyc,,
aiogram/types/__pycache__/chat_location.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_administrator.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_banned.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_left.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_member.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_owner.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_restricted.cpython-311.pyc,,
aiogram/types/__pycache__/chat_member_updated.cpython-311.pyc,,
aiogram/types/__pycache__/chat_permissions.cpython-311.pyc,,
aiogram/types/__pycache__/chat_photo.cpython-311.pyc,,
aiogram/types/__pycache__/chat_shared.cpython-311.pyc,,
aiogram/types/__pycache__/chosen_inline_result.cpython-311.pyc,,
aiogram/types/__pycache__/contact.cpython-311.pyc,,
aiogram/types/__pycache__/copy_text_button.cpython-311.pyc,,
aiogram/types/__pycache__/custom.cpython-311.pyc,,
aiogram/types/__pycache__/dice.cpython-311.pyc,,
aiogram/types/__pycache__/document.cpython-311.pyc,,
aiogram/types/__pycache__/downloadable.cpython-311.pyc,,
aiogram/types/__pycache__/encrypted_credentials.cpython-311.pyc,,
aiogram/types/__pycache__/encrypted_passport_element.cpython-311.pyc,,
aiogram/types/__pycache__/error_event.cpython-311.pyc,,
aiogram/types/__pycache__/external_reply_info.cpython-311.pyc,,
aiogram/types/__pycache__/file.cpython-311.pyc,,
aiogram/types/__pycache__/force_reply.cpython-311.pyc,,
aiogram/types/__pycache__/forum_topic.cpython-311.pyc,,
aiogram/types/__pycache__/forum_topic_closed.cpython-311.pyc,,
aiogram/types/__pycache__/forum_topic_created.cpython-311.pyc,,
aiogram/types/__pycache__/forum_topic_edited.cpython-311.pyc,,
aiogram/types/__pycache__/forum_topic_reopened.cpython-311.pyc,,
aiogram/types/__pycache__/game.cpython-311.pyc,,
aiogram/types/__pycache__/game_high_score.cpython-311.pyc,,
aiogram/types/__pycache__/general_forum_topic_hidden.cpython-311.pyc,,
aiogram/types/__pycache__/general_forum_topic_unhidden.cpython-311.pyc,,
aiogram/types/__pycache__/gift.cpython-311.pyc,,
aiogram/types/__pycache__/gifts.cpython-311.pyc,,
aiogram/types/__pycache__/giveaway.cpython-311.pyc,,
aiogram/types/__pycache__/giveaway_completed.cpython-311.pyc,,
aiogram/types/__pycache__/giveaway_created.cpython-311.pyc,,
aiogram/types/__pycache__/giveaway_winners.cpython-311.pyc,,
aiogram/types/__pycache__/inaccessible_message.cpython-311.pyc,,
aiogram/types/__pycache__/inline_keyboard_button.cpython-311.pyc,,
aiogram/types/__pycache__/inline_keyboard_markup.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_article.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_audio.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_audio.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_document.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_gif.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_mpeg4_gif.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_photo.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_sticker.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_video.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_cached_voice.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_contact.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_document.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_game.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_gif.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_location.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_mpeg4_gif.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_photo.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_venue.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_video.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_result_voice.cpython-311.pyc,,
aiogram/types/__pycache__/inline_query_results_button.cpython-311.pyc,,
aiogram/types/__pycache__/input_contact_message_content.cpython-311.pyc,,
aiogram/types/__pycache__/input_file.cpython-311.pyc,,
aiogram/types/__pycache__/input_invoice_message_content.cpython-311.pyc,,
aiogram/types/__pycache__/input_location_message_content.cpython-311.pyc,,
aiogram/types/__pycache__/input_media.cpython-311.pyc,,
aiogram/types/__pycache__/input_media_animation.cpython-311.pyc,,
aiogram/types/__pycache__/input_media_audio.cpython-311.pyc,,
aiogram/types/__pycache__/input_media_document.cpython-311.pyc,,
aiogram/types/__pycache__/input_media_photo.cpython-311.pyc,,
aiogram/types/__pycache__/input_media_video.cpython-311.pyc,,
aiogram/types/__pycache__/input_message_content.cpython-311.pyc,,
aiogram/types/__pycache__/input_paid_media.cpython-311.pyc,,
aiogram/types/__pycache__/input_paid_media_photo.cpython-311.pyc,,
aiogram/types/__pycache__/input_paid_media_video.cpython-311.pyc,,
aiogram/types/__pycache__/input_poll_option.cpython-311.pyc,,
aiogram/types/__pycache__/input_sticker.cpython-311.pyc,,
aiogram/types/__pycache__/input_text_message_content.cpython-311.pyc,,
aiogram/types/__pycache__/input_venue_message_content.cpython-311.pyc,,
aiogram/types/__pycache__/invoice.cpython-311.pyc,,
aiogram/types/__pycache__/keyboard_button.cpython-311.pyc,,
aiogram/types/__pycache__/keyboard_button_poll_type.cpython-311.pyc,,
aiogram/types/__pycache__/keyboard_button_request_chat.cpython-311.pyc,,
aiogram/types/__pycache__/keyboard_button_request_user.cpython-311.pyc,,
aiogram/types/__pycache__/keyboard_button_request_users.cpython-311.pyc,,
aiogram/types/__pycache__/labeled_price.cpython-311.pyc,,
aiogram/types/__pycache__/link_preview_options.cpython-311.pyc,,
aiogram/types/__pycache__/location.cpython-311.pyc,,
aiogram/types/__pycache__/login_url.cpython-311.pyc,,
aiogram/types/__pycache__/mask_position.cpython-311.pyc,,
aiogram/types/__pycache__/maybe_inaccessible_message.cpython-311.pyc,,
aiogram/types/__pycache__/menu_button.cpython-311.pyc,,
aiogram/types/__pycache__/menu_button_commands.cpython-311.pyc,,
aiogram/types/__pycache__/menu_button_default.cpython-311.pyc,,
aiogram/types/__pycache__/menu_button_web_app.cpython-311.pyc,,
aiogram/types/__pycache__/message.cpython-311.pyc,,
aiogram/types/__pycache__/message_auto_delete_timer_changed.cpython-311.pyc,,
aiogram/types/__pycache__/message_entity.cpython-311.pyc,,
aiogram/types/__pycache__/message_id.cpython-311.pyc,,
aiogram/types/__pycache__/message_origin.cpython-311.pyc,,
aiogram/types/__pycache__/message_origin_channel.cpython-311.pyc,,
aiogram/types/__pycache__/message_origin_chat.cpython-311.pyc,,
aiogram/types/__pycache__/message_origin_hidden_user.cpython-311.pyc,,
aiogram/types/__pycache__/message_origin_user.cpython-311.pyc,,
aiogram/types/__pycache__/message_reaction_count_updated.cpython-311.pyc,,
aiogram/types/__pycache__/message_reaction_updated.cpython-311.pyc,,
aiogram/types/__pycache__/order_info.cpython-311.pyc,,
aiogram/types/__pycache__/paid_media.cpython-311.pyc,,
aiogram/types/__pycache__/paid_media_info.cpython-311.pyc,,
aiogram/types/__pycache__/paid_media_photo.cpython-311.pyc,,
aiogram/types/__pycache__/paid_media_preview.cpython-311.pyc,,
aiogram/types/__pycache__/paid_media_purchased.cpython-311.pyc,,
aiogram/types/__pycache__/paid_media_video.cpython-311.pyc,,
aiogram/types/__pycache__/passport_data.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_data_field.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_file.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_files.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_front_side.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_reverse_side.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_selfie.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_translation_file.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_translation_files.cpython-311.pyc,,
aiogram/types/__pycache__/passport_element_error_unspecified.cpython-311.pyc,,
aiogram/types/__pycache__/passport_file.cpython-311.pyc,,
aiogram/types/__pycache__/photo_size.cpython-311.pyc,,
aiogram/types/__pycache__/poll.cpython-311.pyc,,
aiogram/types/__pycache__/poll_answer.cpython-311.pyc,,
aiogram/types/__pycache__/poll_option.cpython-311.pyc,,
aiogram/types/__pycache__/pre_checkout_query.cpython-311.pyc,,
aiogram/types/__pycache__/prepared_inline_message.cpython-311.pyc,,
aiogram/types/__pycache__/proximity_alert_triggered.cpython-311.pyc,,
aiogram/types/__pycache__/reaction_count.cpython-311.pyc,,
aiogram/types/__pycache__/reaction_type.cpython-311.pyc,,
aiogram/types/__pycache__/reaction_type_custom_emoji.cpython-311.pyc,,
aiogram/types/__pycache__/reaction_type_emoji.cpython-311.pyc,,
aiogram/types/__pycache__/reaction_type_paid.cpython-311.pyc,,
aiogram/types/__pycache__/refunded_payment.cpython-311.pyc,,
aiogram/types/__pycache__/reply_keyboard_markup.cpython-311.pyc,,
aiogram/types/__pycache__/reply_keyboard_remove.cpython-311.pyc,,
aiogram/types/__pycache__/reply_parameters.cpython-311.pyc,,
aiogram/types/__pycache__/response_parameters.cpython-311.pyc,,
aiogram/types/__pycache__/revenue_withdrawal_state.cpython-311.pyc,,
aiogram/types/__pycache__/revenue_withdrawal_state_failed.cpython-311.pyc,,
aiogram/types/__pycache__/revenue_withdrawal_state_pending.cpython-311.pyc,,
aiogram/types/__pycache__/revenue_withdrawal_state_succeeded.cpython-311.pyc,,
aiogram/types/__pycache__/sent_web_app_message.cpython-311.pyc,,
aiogram/types/__pycache__/shared_user.cpython-311.pyc,,
aiogram/types/__pycache__/shipping_address.cpython-311.pyc,,
aiogram/types/__pycache__/shipping_option.cpython-311.pyc,,
aiogram/types/__pycache__/shipping_query.cpython-311.pyc,,
aiogram/types/__pycache__/star_transaction.cpython-311.pyc,,
aiogram/types/__pycache__/star_transactions.cpython-311.pyc,,
aiogram/types/__pycache__/sticker.cpython-311.pyc,,
aiogram/types/__pycache__/sticker_set.cpython-311.pyc,,
aiogram/types/__pycache__/story.cpython-311.pyc,,
aiogram/types/__pycache__/successful_payment.cpython-311.pyc,,
aiogram/types/__pycache__/switch_inline_query_chosen_chat.cpython-311.pyc,,
aiogram/types/__pycache__/text_quote.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner_affiliate_program.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner_fragment.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner_other.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner_telegram_ads.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner_telegram_api.cpython-311.pyc,,
aiogram/types/__pycache__/transaction_partner_user.cpython-311.pyc,,
aiogram/types/__pycache__/update.cpython-311.pyc,,
aiogram/types/__pycache__/user.cpython-311.pyc,,
aiogram/types/__pycache__/user_chat_boosts.cpython-311.pyc,,
aiogram/types/__pycache__/user_profile_photos.cpython-311.pyc,,
aiogram/types/__pycache__/user_shared.cpython-311.pyc,,
aiogram/types/__pycache__/users_shared.cpython-311.pyc,,
aiogram/types/__pycache__/venue.cpython-311.pyc,,
aiogram/types/__pycache__/video.cpython-311.pyc,,
aiogram/types/__pycache__/video_chat_ended.cpython-311.pyc,,
aiogram/types/__pycache__/video_chat_participants_invited.cpython-311.pyc,,
aiogram/types/__pycache__/video_chat_scheduled.cpython-311.pyc,,
aiogram/types/__pycache__/video_chat_started.cpython-311.pyc,,
aiogram/types/__pycache__/video_note.cpython-311.pyc,,
aiogram/types/__pycache__/voice.cpython-311.pyc,,
aiogram/types/__pycache__/web_app_data.cpython-311.pyc,,
aiogram/types/__pycache__/web_app_info.cpython-311.pyc,,
aiogram/types/__pycache__/webhook_info.cpython-311.pyc,,
aiogram/types/__pycache__/write_access_allowed.cpython-311.pyc,,
aiogram/types/affiliate_info.py,sha256=3pdlbpp2BMx69FQ9Oe2Fc0ME43H2eTvixrg4B0tSy9Q,2180
aiogram/types/animation.py,sha256=W9LGC1-JeWGolCU3OI1a3dXjtD5pCnaOd_J7ulWhau8,2673
aiogram/types/audio.py,sha256=2WPtZmAQfW6ewCb6ozlcfXXZACQEX7c1MYTEVMAy9Lw,2823
aiogram/types/background_fill.py,sha256=sJj85JGxTXOJVd_dq0jpgH5TbBPlvRKFNjk2CNoM5Bw,513
aiogram/types/background_fill_freeform_gradient.py,sha256=87YdL1Z7TDBNSJRuVFDNKL_5kHPoDxxdhTexN1Pqc5s,1201
aiogram/types/background_fill_gradient.py,sha256=bSkNQPDrdWCrSIoCSBE3iFI3NaOAk2cktmLOFUbX2qA,1434
aiogram/types/background_fill_solid.py,sha256=cL3N_tRhRdpM3rGYjHqh1c05Ys1_w5Sn5RVFX4Kzrhc,1022
aiogram/types/background_type.py,sha256=_UKdw_LQfpR4z7W8wdtEmKV38EUlsSCheh0l4O0d90Y,540
aiogram/types/background_type_chat_theme.py,sha256=1mylq_KCHfLYikVuv2FVNHTK-FivuOc3fIN16ThxGX0,1077
aiogram/types/background_type_fill.py,sha256=P3mjIBc_oZspRCYLZvep1Tjx2F2r3Gz8QnK3ZhBRXr0,1656
aiogram/types/background_type_pattern.py,sha256=7y5N7vxg4EauwCLqAf_7VjfYGcdDLWt5P2wieNZl9i4,2514
aiogram/types/background_type_wallpaper.py,sha256=6uYeiZ4xQBTmJY0jCTeUFAj65Q872NZL7D2-WMeSLac,1868
aiogram/types/base.py,sha256=ZAKfSk-DVyoBoYdyyZUTkoIKzuaj0kJS0ophsohsdI4,1762
aiogram/types/birthdate.py,sha256=ev957uyU2tG4cRqQ838h8LHW0uIPFUu0TdC1YfL0mnY,1056
aiogram/types/bot_command.py,sha256=rJuwZroECtHDZxhyv92K2vyTUlXdieXdCEhewEsIPk0,1017
aiogram/types/bot_command_scope.py,sha256=rs7YoyRACVLm4tMMEQ01LM6ckWbF7o81edYEAnWXoyY,951
aiogram/types/bot_command_scope_all_chat_administrators.py,sha256=3wvcWAh_yZADVbqPQD09kd1hyjmDL_KuoYR8us7A0jI,1311
aiogram/types/bot_command_scope_all_group_chats.py,sha256=BElN12t3RwjYAFgo__K4PSUA5Mf0Vgzx6lzrhO1_kOY,1225
aiogram/types/bot_command_scope_all_private_chats.py,sha256=1Ww3HUSSJINy4qNSqF5lybQJwWHpflhMM2So6EMplyg,1226
aiogram/types/bot_command_scope_chat.py,sha256=chjf7klLRhCM8AB1xwkfkPfUXcw-_vnQekRVkiIB1Rs,1327
aiogram/types/bot_command_scope_chat_administrators.py,sha256=ezkiv48UyMIlKyZXvmlmsLi9cop5pk6cZW6FLgFJr-E,1518
aiogram/types/bot_command_scope_chat_member.py,sha256=IvFP2cemuZtMFdIn7T7hfo4o4-35_1ZBTOHV5pARCQI,1513
aiogram/types/bot_command_scope_default.py,sha256=Aqa5eiFS2_2rd8S19i7qfBPIk8TNPVdOEOMR1oSPMuI,1270
aiogram/types/bot_description.py,sha256=daKHDJ4d2c9j7wCTKwDLgbXy1HNrjbYkZkwC9D7FiPQ,781
aiogram/types/bot_name.py,sha256=ut3sf9j5LYd2xki-_pZ1lnVvN7HOUr6v37llIi75j6w,717
aiogram/types/bot_short_description.py,sha256=UdHLoRvp1hdhEg-W1XYUoDwPeCxvuU2H430JrNDiY4U,849
aiogram/types/business_connection.py,sha256=B3LuYw0bZwli2E2d6L9CGE_Rm1IvnzpD0RyCmZWxljU,2056
aiogram/types/business_intro.py,sha256=TTTNSlPX-bvzbDqKd-YzwXaUjgznpZxnBDGtAKpmSBc,1303
aiogram/types/business_location.py,sha256=OHFm2V3jLoRWJdXS3ubflMjFGd2Wo6sMhMhvsHbJUUY,1103
aiogram/types/business_messages_deleted.py,sha256=LYV9OC1fKsPN3t1TOhkLSJK7lyFSSLkgOuhbGU5O448,1472
aiogram/types/business_opening_hours.py,sha256=D7Cn967ZUdDE-vwJHYLnIeFzxpeKGobQYdlMRl6t-gc,1276
aiogram/types/business_opening_hours_interval.py,sha256=NFi-_QWLEN_0D1fPpqqp2TlrdWBJ71Rz7c4P-lSWj9Y,1329
aiogram/types/callback_game.py,sha256=AF-WqkqFcC6nG_tKoNX1sRjRiKEstbEFJfLy4NPKtRQ,298
aiogram/types/callback_query.py,sha256=JBnbrz0j0C6zK1eMeCgQ7l28ytKVxxkExVbwDsc2u4Y,6017
aiogram/types/chat.py,sha256=TMTsaw9t5L6ohv1qDpj02FoZgud2LSPSgABr_2yhch0,64446
aiogram/types/chat_administrator_rights.py,sha256=xwIgM6y2daN5FSYNJagmhBtgEy88S9GTSQk_OWgrykU,4578
aiogram/types/chat_background.py,sha256=tGylRgxxa5pMDsuQFkG2XrY7mKQCsA9lc-BsaArwXEE,1400
aiogram/types/chat_boost.py,sha256=iAbDstYKPATDpf9C0UTaeeipi1kcFedXhiTC6INxEuQ,1879
aiogram/types/chat_boost_added.py,sha256=4WPPng6cqzhb0qpepGeu4mAoga2ieA2vmqIxWQR5n-A,811
aiogram/types/chat_boost_removed.py,sha256=phu1WXsR6noLlEuiwyF1S0HzlwDmFsk2vwc9TRJ00K0,1768
aiogram/types/chat_boost_source.py,sha256=-s9Um1lFg4ab2MiQpb1SpJlKM2hJHZkpM43TQE6GEMo,476
aiogram/types/chat_boost_source_gift_code.py,sha256=VkPQktCIZdyI7wvhR-c7hgDs8lI2XySv_tCX1IJoFDc,1378
aiogram/types/chat_boost_source_giveaway.py,sha256=4iH0fu4MWRf7HVyS1jUKioG5DVWjFhJ4ueqKzqGZEpQ,2407
aiogram/types/chat_boost_source_premium.py,sha256=nN3o9Kvt5JHXcp1RpYq1WDX8Szzn9so1-FgGR1Olf38,1278
aiogram/types/chat_boost_updated.py,sha256=L0LKL6jutsHYPbCDQoXHaZVjIS5y7uIDXlacKNIFvE0,1002
aiogram/types/chat_full_info.py,sha256=Dhv4b3nh29etQNxdMrzgD7qhoqRSxWwc7FwxDVAUUZI,13707
aiogram/types/chat_invite_link.py,sha256=FfaheNzGBuDsKaV08kR2vtDRowzhTbvFeU6NtKlaLRc,3219
aiogram/types/chat_join_request.py,sha256=-8Ew8ym9tVzArQ9Pg38I_boSNu3sixIC4s_bKMNY2dk,185832
aiogram/types/chat_location.py,sha256=qZUA9NnQBDCHRLvKDP1Oo0wDX5T5tw-j14-NX8tlXSM,1062
aiogram/types/chat_member.py,sha256=sq0RhcwghiW4Gy9XJTskFA3dHxaol5EA5Oify1QJfz4,722
aiogram/types/chat_member_administrator.py,sha256=d7se9Lm1AWDuZRNYGbK3ZLC2NkZVkSzegziJ0Lx66Qc,5453
aiogram/types/chat_member_banned.py,sha256=bvvMSTPn4iNy6N5nGoPYoxR3pjqDZhFFqvGC_Jb1T3M,1500
aiogram/types/chat_member_left.py,sha256=YhW1LjLUG5TDTARmj44DaJ0OyX2Gm8CW9-K6EA7LYgI,1254
aiogram/types/chat_member_member.py,sha256=2AtZsJz3aICbn2eqCmG1p0UsBYSqlNLmN-wnqnC6uks,1482
aiogram/types/chat_member_owner.py,sha256=2gV3f1ubns_eORZjJocWSuwH8IW_cv2j2I0MuHbVa68,1676
aiogram/types/chat_member_restricted.py,sha256=7F40ERHf29ihfrB4tTc9JWL5w5xRBjqVLGoMWMgyWlg,4545
aiogram/types/chat_member_updated.py,sha256=NuDg_rExv6hOZsNi0bjCwRwKlbnGzxOlESoDTrgtIQk,94922
aiogram/types/chat_permissions.py,sha256=A6I6DuCQGRWBqhQuVe8xMnRkd9i0iD7gOZm6Qg7m0LY,4382
aiogram/types/chat_photo.py,sha256=BouKO-6NIn5d17fB9acX9iAbv_gjYzbeKuTqN3BcFD4,1872
aiogram/types/chat_shared.py,sha256=zGBx81dC3SpqbFwYtGIyKKad1QdeejmvdL-N7waJgfs,2254
aiogram/types/chosen_inline_result.py,sha256=VFMo9FklvYjWrPbwrxeIyHrDwLlRvnFObUV5EhaannU,2399
aiogram/types/contact.py,sha256=jWvv9gc-4LyFyBLeAeucyr7A8yjBk7-sJQH10NKoydU,1848
aiogram/types/copy_text_button.py,sha256=wbr68I7ATgVXhbhSfzEpAsKqNiPjUY-AhKVZcnRl1MM,864
aiogram/types/custom.py,sha256=3Xqsx2Llx47Hu1v4hGEFOjbMzkgkdQ9lmKW87Atulp4,819
aiogram/types/dice.py,sha256=QOFOqUx3jqVtqU_nwuWhhSthy5CBR_mNTMuIpwiD2KY,1165
aiogram/types/document.py,sha256=6tKzYSPyON6BoWLn3F3tiXHUxaQNG1YNl6kC6bO-0ZE,2425
aiogram/types/downloadable.py,sha256=FbRdIM3Cpw3tcDzZvwy__u98aSKqMMJn2kKh9mK62B0,77
aiogram/types/encrypted_credentials.py,sha256=1ZC-Q3c84mEVZGEK_cXTkqzX4FzBujn0_necxJ1uSPc,1521
aiogram/types/encrypted_passport_element.py,sha256=MHZyzj2tEw7lOCELnp2Yo5qxgIZbT25_KUl-2G8XSjk,4727
aiogram/types/error_event.py,sha256=m0QrLksRJ0-x0q5xpb6iyI4rqPfiz3XJom_Gh75CJ-8,714
aiogram/types/external_reply_info.py,sha256=N-DmH6A9S_O5aue_9t1kQWxvAyxY1UAHxyq_SUgZXCM,7179
aiogram/types/file.py,sha256=52bE-c7RjZmTdXk8ij8PJ5fsx2-1IfgedM763PmzGnQ,2166
aiogram/types/force_reply.py,sha256=YkTteaPMOUH544hl9h6VDypMxyiLIKFd9KXryouTmVM,3024
aiogram/types/forum_topic.py,sha256=CchPMME1NgB8WT9FWLgQ5mYZ7uXFG2yuTmDMaDZ6kcU,1422
aiogram/types/forum_topic_closed.py,sha256=aw1K6fIadwJtSTZnyTQq3xtEf7920vodFjK5lHeKVgs,309
aiogram/types/forum_topic_created.py,sha256=OPYpne_Zz5tdOpzjYOXGplCxLhF1aeKnHFzpn3qXIRg,1321
aiogram/types/forum_topic_edited.py,sha256=Fq8QDirNBsymOKbAxBYnFqhmXL52XuqKAQrLxePRyA8,1230
aiogram/types/forum_topic_reopened.py,sha256=MloV3nhDpHSLe5VoVgmC99u7ch3IYahIHH2EokGCWgY,315
aiogram/types/game.py,sha256=iY-bnyCh7FH92LUpE4mL2sIP8RYnrEHS3YySkqi_odY,2359
aiogram/types/game_high_score.py,sha256=rwCyO24y4BkcTJSmoLPA7pGEsgfA6-_3i40uvCgxp6k,1203
aiogram/types/general_forum_topic_hidden.py,sha256=QC3u0Qpt3nptdhl3dxqgNXVrptwSAdnEzI-OWP8QMMw,301
aiogram/types/general_forum_topic_unhidden.py,sha256=W8HRGyB81eyooaucgD0ojeAzywD8AjnMnUf2hf7r5Nk,307
aiogram/types/gift.py,sha256=wkMRj3zG0OsykCNwzvSHjdo0-YHO_rr8974ADE9TQWQ,1718
aiogram/types/gifts.py,sha256=xnMajTJ-MoODaiGp0xSF9W9n3vg8oww0aEl1i46umFU,816
aiogram/types/giveaway.py,sha256=PXB4nsACiEbZCkCaXGhzbipX5VJb3Fh9EJEo7dW4b5A,3341
aiogram/types/giveaway_completed.py,sha256=-yBzrJeMF8b5Vhs-PlyZQNl4nAuGmZit7qBnqNwwGyg,1811
aiogram/types/giveaway_created.py,sha256=At22BaRdK_bueF4zp1cSO4QsY6idqSLhpUPkYwhMdWE,1026
aiogram/types/giveaway_winners.py,sha256=wRhpWdD3pTvyOFBdD8qMD3XpvGX-o_tsGCaeX3gH_NQ,3624
aiogram/types/inaccessible_message.py,sha256=3TsiwGGl_6wtQAo9fdCa7wz37-LyrIyTxBa3Lz5hEdk,191444
aiogram/types/inline_keyboard_button.py,sha256=djbmGtYmMlmd7GwwotfEi5vjYmGeNXq7Vc7UoB2VqBY,5055
aiogram/types/inline_keyboard_markup.py,sha256=trf-DSyapyYnNoXeO0qu8h1OcR8TokB2q697jMhN3dA,1267
aiogram/types/inline_query.py,sha256=ji7soi2jhzODC0fgs_d0YFa_pNikYF4TQip3iR6s2BA,7404
aiogram/types/inline_query_result.py,sha256=8Ve8Fe1Yf6O7NHWCYdTVIgnbawI6hIo5B0l2cV5ZN20,2189
aiogram/types/inline_query_result_article.py,sha256=Keq9BuANquPGxWDw9D8Yra7_vNDSvYSRfqK5Ml9e9yE,3759
aiogram/types/inline_query_result_audio.py,sha256=poecL3Exe1oq-r-qUr82pOTJ2jY6z2XKUc2SFi3Mb1k,4361
aiogram/types/inline_query_result_cached_audio.py,sha256=jjdiTkTPVlqKYq-zJJDd6gWN3UgtjQzJW8ZI6cELvtg,4012
aiogram/types/inline_query_result_cached_document.py,sha256=p7Sc5IR1c9RDqARgHtqrE9pQCXgqWHy0F7kThefAloE,4354
aiogram/types/inline_query_result_cached_gif.py,sha256=32aECTB92-l5mEynM-CnGO6KGWUZw_bkYpVYLue94V0,4595
aiogram/types/inline_query_result_cached_mpeg4_gif.py,sha256=GoPtnlrckRsnUKZYdvkEYh3E2mpQkvNRGh6TiSoyH1Q,4693
aiogram/types/inline_query_result_cached_photo.py,sha256=wbRL5JDo0FP-kJ6UjXkQcjsiMVp2LpgJP3Hh-0lt_RU,4763
aiogram/types/inline_query_result_cached_sticker.py,sha256=hqZ8ZvEv0jA0rf7WDVa4SVkaX5khDncpdSPoH5LWEcU,3100
aiogram/types/inline_query_result_cached_video.py,sha256=5Vi8Q7vwnNPN-FAOI0Wh3Qb2iHL1vlMybojeDzOeIac,4733
aiogram/types/inline_query_result_cached_voice.py,sha256=NECjz6Q_sWitYFUAAX6zZOxJcOWiFGxvYeIDcciDhLM,4138
aiogram/types/inline_query_result_contact.py,sha256=vAmYCE49k7pWHNA-1-L0p7pD2TljNdzitFgsE_hrYDI,4111
aiogram/types/inline_query_result_document.py,sha256=9rpiUiPpXiWUYIqIhUNkEehoe1WSPVME0V_Q59EMEDU,5041
aiogram/types/inline_query_result_game.py,sha256=9t2HzFgVgHjPW_pQZ4YBTeGptSbn9KOHcGsbEPpUwzQ,1758
aiogram/types/inline_query_result_gif.py,sha256=QI-T-ZjwLZGOytjkHt0MHL0xhJB1ZfgQBgab9_j6Z1k,5536
aiogram/types/inline_query_result_location.py,sha256=FX_eYlVfcRkcTTxubrTu1m3jGFWbgLEyg0R0TFp3ybY,4961
aiogram/types/inline_query_result_mpeg4_gif.py,sha256=wjaQJdqP9K5HxsKktXGwxe1k1QqtDBeRQ6E6vV6YOkg,5639
aiogram/types/inline_query_result_photo.py,sha256=5pb1VBbyhsK7sr_6K4sQDEywkscDXmZxYylFhojE9NE,5239
aiogram/types/inline_query_result_venue.py,sha256=pln1TVUl5cLvgxTxR1PEYLA7K69L9ZGAGxR_YQ0eQyY,4904
aiogram/types/inline_query_result_video.py,sha256=wdt7J5iSd_dyiWVh53Tmr7z8zHCw9j5DJWz8Dv1rjLE,5836
aiogram/types/inline_query_result_voice.py,sha256=z-gZt8g6V3M9yS1VvSKFAGaEyP9rwfZqHQaiAehus8c,4304
aiogram/types/inline_query_results_button.py,sha256=mcDUu9wvU35tBvH1BhICorSlJ25XUVbpJlh9BWE3HNQ,1878
aiogram/types/input_contact_message_content.py,sha256=GR2RDpizrX7rlxjf05CKzRT3zmLEO1yJSpqbmxCLwQA,1599
aiogram/types/input_file.py,sha256=oCElYrCtJgfP5_ck0MQxvGppVZyXW5BnsHcmSg1Ds1o,4672
aiogram/types/input_invoice_message_content.py,sha256=EJf4oo8a0alA1zACIfPb68VJxE3F0AvoCkuNjfiu1h0,6896
aiogram/types/input_location_message_content.py,sha256=UmmTHtSm-0MbIp2pISbl6FoPo9rjKeBK0pKzKg66GvE,2374
aiogram/types/input_media.py,sha256=2nTD1Wg9VX8x74KTAf2d6OjjGTwPTUXaQL5Ty5TxvYQ,619
aiogram/types/input_media_animation.py,sha256=Lr47yzFVl7Jl0bMZVJc-KbkIV2l8bXlEFL0dXJn-ayM,4367
aiogram/types/input_media_audio.py,sha256=6uuGDZqLjczTey2_PMwPPORlIyflIRoEL_GUowvHv2Q,3700
aiogram/types/input_media_document.py,sha256=ucpg6iUarmGuJELdAz47HZXm3S5qmRewjpWya4D50R0,3622
aiogram/types/input_media_photo.py,sha256=UaTLHv4kOXP6yvSKKcWKKsh0rxLKieRnnkfWaAYAFDI,3121
aiogram/types/input_media_video.py,sha256=6WulgHcE2A6O6zfqhS7rvRmnisLTLR7t77oD7GRAuBc,4503
aiogram/types/input_message_content.py,sha256=wj0C4RyiLpVP4yz0LmmO4qMNNIwRMihjnoMrjBSxjDE,793
aiogram/types/input_paid_media.py,sha256=z-STAz_0G590-eFlOYNTv_HP1LOWbACXM0XMYbEBXrg,380
aiogram/types/input_paid_media_photo.py,sha256=AHnkA6yvhhxdt_ffpCplgOxtqeh0YiDZ0WjVYj0boSA,1498
aiogram/types/input_paid_media_video.py,sha256=nKQwP5BtYARot0LjkAr-EOa7UfbL8DwtFqexwoTNIY4,2977
aiogram/types/input_poll_option.py,sha256=xvXNI_QLkU1UMHaFgHBAF7VKAY7JbZTjMYb0RYkfYVs,1785
aiogram/types/input_sticker.py,sha256=inpy6H4A5UEs2uvrnCOdE4HaFUKncsa_e1KDJcL6foo,2432
aiogram/types/input_text_message_content.py,sha256=i5edPai_FFJ-U7Rqh-PMdEwXI_ez-uFqOl7tzRTch0Y,2676
aiogram/types/input_venue_message_content.py,sha256=CZsCPYQrFdP_BrYBzNJ_Ik39PlJ1VLOsvI5q9vRS0Ns,2411
aiogram/types/invoice.py,sha256=gOiw5G9yXJ8ZkAVupt2ww-mcooYanTix6t7KarSni9Y,1932
aiogram/types/keyboard_button.py,sha256=L0cHsJEsKQziqK3zJN6laQL9drC36AhbhMOG20vhzcE,4352
aiogram/types/keyboard_button_poll_type.py,sha256=tPGlW_jRXaDxZML1_vK080sp6BO86DO5cbBP-TEOf4Q,1153
aiogram/types/keyboard_button_request_chat.py,sha256=N8UBPPlrQ07OSCO-33mfP5lKJ744sugaS86dYWpxMRQ,4448
aiogram/types/keyboard_button_request_user.py,sha256=89efkQPnTYrtpBozhTvXp00YE8uKqgJInVUafJV7nkI,1990
aiogram/types/keyboard_button_request_users.py,sha256=bhj4_scIdCcKxWeuC8f8guU_tcWTvwrbISe_PycG-XE,2747
aiogram/types/labeled_price.py,sha256=7kRZPTy8HgJG4FYrix21xGfhgT_u93fROUBOj1wB4Rk,1330
aiogram/types/link_preview_options.py,sha256=xv8Z-qE_NBfojELQHSlgegmsXso661AL8562z8i0dls,2747
aiogram/types/location.py,sha256=0X9SBlANNn_ODEByFE0oDiJxpKtGEQ6OI6fDXB_1loA,2115
aiogram/types/login_url.py,sha256=VxZTVG1LbmKeon_spDg17HJiPh1l1buOI5eFBNLu1Rk,2771
aiogram/types/mask_position.py,sha256=ZH0pNJ8u4Xlos2MwCtsnSzMNfhgGvacCGdJ42qFp9no,1627
aiogram/types/maybe_inaccessible_message.py,sha256=TbxUHEoW88w4nWer6zhQhWaAjTqr2Xi3H9-ZJRfvA9Q,388
aiogram/types/menu_button.py,sha256=4Q2I_AlbnRe2fQ0f1a-LNl0toGOHI73ehjhMQmLngV4,2103
aiogram/types/menu_button_commands.py,sha256=NMmH1S0jcEivjXmzRfMTLJAO4UBKVhFuo8wS4ONgZwc,1038
aiogram/types/menu_button_default.py,sha256=FnjUz1-aXR3-0oHPv0pNU5yi1M86wnzwCbNnzEMu094,1027
aiogram/types/menu_button_web_app.py,sha256=G-yWLPg91mgaMP672VRj6xmB8_UqDpMeH2iePvY0rw0,1715
aiogram/types/message.py,sha256=YfUVuYllvbgsza_nophh6keE48VDtb1weYtt3s_C_t4,256648
aiogram/types/message_auto_delete_timer_changed.py,sha256=D9GoK5tOtxWTksB_yaIKthfiHWJhOI6SUBWWpW_PRTY,1020
aiogram/types/message_entity.py,sha256=HFSk-rWsATvYBtVxGQFihPhiVNB1n3UusXiOIQNsbfg,3349
aiogram/types/message_id.py,sha256=JGHbRBB4KbmtK0L4VTcWLrlttF3M4eukcUXkmDQF6T0,1072
aiogram/types/message_origin.py,sha256=uSs6n9q5JhIzaV42ldMJJL_8nAx_aCQ3xNqrRVMJsWg,518
aiogram/types/message_origin_channel.py,sha256=tgCfiuS81A0oWD9EY90F0P3B9s-Avsm0FJlw5ra8umA,1795
aiogram/types/message_origin_chat.py,sha256=_EE8lVR8Kp745dHTlT1zHYXEDImbGhq_nuZgteu9lbc,1725
aiogram/types/message_origin_hidden_user.py,sha256=N8WXMNcBlWSuDMRYUxsUiZubnOxsDFOr7GyYQKRrij4,1377
aiogram/types/message_origin_user.py,sha256=FTu0wnkg8AElOQtMjy2Ee-ApL2eX57a5qMrudWMWf90,1349
aiogram/types/message_reaction_count_updated.py,sha256=INsBfsf6UMzCeQraikugzPI9QPRBWH4SeycDKddJhEU,1515
aiogram/types/message_reaction_updated.py,sha256=GCBHczu_2M39wr3PTLod2jAFgFi6IQgT-skLdt5eM4E,2610
aiogram/types/order_info.py,sha256=ECWERhAIl3kkclMOwnNN9NHGQLUtW0B6oElkLubPPr4,1516
aiogram/types/paid_media.py,sha256=gw3vvGNuZugsvlWpqxuZs1Q81ALL9MTZ6RVP5EFMbmk,399
aiogram/types/paid_media_info.py,sha256=82TCrKrl0LUxgBmrR9DSPeOSvzLrxR96vrQfky8TsIU,1321
aiogram/types/paid_media_photo.py,sha256=03xX-iveseL0dkiaHd0TY9q7MLM9qLO20_c8Yu810QY,1124
aiogram/types/paid_media_preview.py,sha256=9ZIG6wR-De8VPU2y5Rn77czZFOdLcW_JxKXQeYsiycw,1522
aiogram/types/paid_media_purchased.py,sha256=J_V6slEyAerISMyRxiiIFKjygHAlusJKCCJY7-LI9Mo,1171
aiogram/types/paid_media_video.py,sha256=afHhTgBEXuyms9V1FaGqJutjveZeHjESmYvav3D3_ck,1095
aiogram/types/passport_data.py,sha256=ByKi34EtxO4ipowiWzyzeZYdrIODBAZnzF8DRJ1hLlw,1312
aiogram/types/passport_element_error.py,sha256=0MooyVK2Yoz-6MTnBDqbyiR9o-cYM5GoM6HC-p6X9jQ,1214
aiogram/types/passport_element_error_data_field.py,sha256=NQXIJOOjXP56vbMTgKl2z9QqTs7FyHlxURO2xSOMQI0,1850
aiogram/types/passport_element_error_file.py,sha256=tlUpJBAJFPUzHnCgd07gIS1c_HvFXFTpQWlB6Kipo_w,1619
aiogram/types/passport_element_error_files.py,sha256=6tX9sDOVBez80HlGwWY2ypo3oKYVyrlDaqYMAqLWBio,1728
aiogram/types/passport_element_error_front_side.py,sha256=669dn7UWwY4GnNnmaUBipHj8U22PahkJFmzLVerAGXk,1721
aiogram/types/passport_element_error_reverse_side.py,sha256=ZVfTzKDmrZqzwcOBNuzsSwfEUip8hHCzeRk2jyiWDak,1704
aiogram/types/passport_element_error_selfie.py,sha256=KR-TG1DguG1w9Kv1rIJRf7qkI_L7euSwr9h5IC8DAJU,1623
aiogram/types/passport_element_error_translation_file.py,sha256=Zm4DzT7yQoHHdANCgq0uW6kmskULr56hwjv0GFPWTGI,1843
aiogram/types/passport_element_error_translation_files.py,sha256=QZqQRIKd0qfVzmiKN9JHDVn32PFdNq6sIkKiZn6GScs,1946
aiogram/types/passport_element_error_unspecified.py,sha256=KqvSPstDlkcEJRDA-JkFX5JK97uO9R4Nqx_Sbp5iW4Q,1651
aiogram/types/passport_file.py,sha256=tN7IbA6M4Zk6WH4CpVeI0exfi_UKoKOUiz1iT1e1epo,1599
aiogram/types/photo_size.py,sha256=J1rOX1QxScCjA0LPm4KgdLZBpsAQfscAaDfuocE4cC0,1664
aiogram/types/poll.py,sha256=WHHT_JkYIBH7D70qk1IsCeR2914X8Uk8Ib78BZZvV74,3731
aiogram/types/poll_answer.py,sha256=ujKTddKv7gCre2Uw4IQ0HT6bvuk2NTZuX1SMgYK4Efc,1608
aiogram/types/poll_option.py,sha256=XfbZtlUS_Eo7yKn1Cy9auho6TY4M9kpqITwrMZYCObo,1439
aiogram/types/pre_checkout_query.py,sha256=CaYsF1zvPb1k5cgka1GC12VMS3eZc3RWVba7VmRr6L4,4410
aiogram/types/prepared_inline_message.py,sha256=u_OAM29FW7DVPIEBhNcoSzprLa-RxllJVBeM2E8o38g,1204
aiogram/types/proximity_alert_triggered.py,sha256=b2zF9r0sjf-cp6Gj-pyTIJq9x5a_JBjdp-DseDGpM_0,1253
aiogram/types/reaction_count.py,sha256=wmMH5iR_tOJ3ieSezjBzRCPQyt7RhH3Lbw8emodaxqE,1332
aiogram/types/reaction_type.py,sha256=EBHvWnPblOe2T6ey0_V9m6vReAX8VcHX4wuzKLVMR0w,442
aiogram/types/reaction_type_custom_emoji.py,sha256=dzZSLhEtBke35GtTgfSxrijMu9jQzob6drdFkYReR7w,1150
aiogram/types/reaction_type_emoji.py,sha256=F5WoRrUm9tOV3YJVNh8quD4D2OfsU53Y-mbqggbnm-w,1682
aiogram/types/reaction_type_paid.py,sha256=dazJsDfQEMF9mgQZ0So4t7S-QFKHGyr6HKyYyBsnPtU,951
aiogram/types/refunded_payment.py,sha256=yaj69LD-j7ffwtTk69ozl0MXz3r2kUlBTgOuWEqvxto,2180
aiogram/types/reply_keyboard_markup.py,sha256=fLfhKqj11SKDkuKrzDdHgGnpEIZhHsYKI7e5pCV9Frg,3367
aiogram/types/reply_keyboard_remove.py,sha256=vt2LTPCyoNO1SzO7JLJAUTLxfcyVh5eVKTkFmjQVvag,2093
aiogram/types/reply_parameters.py,sha256=Sx61q6izo5FGBrrYSCts_I-noEPjB-H-aYglx6lSAUc,3496
aiogram/types/response_parameters.py,sha256=qPu3C079bJZRdUIQwr-r9WIcW6yYW7rdWJlraZQbw-M,1550
aiogram/types/revenue_withdrawal_state.py,sha256=rUzcoQ2Nbmm-2EwkoD-g_xWUp9o3mIsthyE2ZWrg0ZY,585
aiogram/types/revenue_withdrawal_state_failed.py,sha256=uhCBqAV8SpbCkL364ygkvGg7TzarISQ6NniBeHvn3HE,1133
aiogram/types/revenue_withdrawal_state_pending.py,sha256=rVieabsj2ayXpzN7yW8uGN-8p6Qy_lQyrKYFSKH5qmw,1115
aiogram/types/revenue_withdrawal_state_succeeded.py,sha256=d2cBJbxXEPhtSLWb-TzyDy-iumMWkG7G6LWNRBmH7oI,1409
aiogram/types/sent_web_app_message.py,sha256=1p2Xr_3czwPbZd8PpylTSo31ZgeAEBLnlnlRcBY31xc,1168
aiogram/types/shared_user.py,sha256=UPL2qcpYgtKEPL8v5_CTF6NPOZrvR0FCzegIYjJG3vk,2334
aiogram/types/shipping_address.py,sha256=9JTEnZoNaPa-T1p_YolL1ErIGWJol8lAU4F8ycsQ8ZY,1550
aiogram/types/shipping_option.py,sha256=S5Jd4zr-7xbI5uaCLO696jlURKCdgZHgZD9hOrCj-gQ,1092
aiogram/types/shipping_query.py,sha256=ay5_jkVD8JedLvj_Zl6k1JXU7PruHtT7815K3-N7WwE,3452
aiogram/types/star_transaction.py,sha256=kUhevoKZf_PhwJLKOfoSRL1P-05uK3OonNat7S7DjCQ,3947
aiogram/types/star_transactions.py,sha256=QbV2EsfoupTvwfYyD_WFlaiqLBenA6ik6-dwoqbUGjk,948
aiogram/types/sticker.py,sha256=Tcz8iqKzCbfvP7MdV3NVwUEt9u27EmB7_R-N_BxSd6M,5902
aiogram/types/sticker_set.py,sha256=Z6heidYoOQUkfS2PijUSCEg3L_NjgD3hkhChCE6jwpw,2382
aiogram/types/story.py,sha256=2Bm9ZHpfr4_yeBPWFgwnqTs-y4xFgKqW8NLRVWzMdqM,884
aiogram/types/successful_payment.py,sha256=zcXeaVR81zMQm8d3rO0vJ_NV8pOfWRRgCT8t1dXOC6Q,3325
aiogram/types/switch_inline_query_chosen_chat.py,sha256=KH9p_x5aY6XxQn71LRa92cVQMl8glZKdc2m2935i_Yo,2002
aiogram/types/text_quote.py,sha256=u6Carp9C3dKz3-u35EIhnulAxpfGh0_BXsfAG5M4bHs,1857
aiogram/types/transaction_partner.py,sha256=0bzMsto6iUvUO2s1PRdIBrwvh8cRigiBhxcfznb_NDM,821
aiogram/types/transaction_partner_affiliate_program.py,sha256=3EvIelQf7fuzSrQZV8BjK5X5CQYXxehBwr7rTnNS2Xk,1869
aiogram/types/transaction_partner_fragment.py,sha256=sHfiApYXz8E4Jr40pb0szs4b94aAp0UTE6UGQ1xFFdo,1962
aiogram/types/transaction_partner_other.py,sha256=iHViDYZ62dQS3z6k1YkMOqFELyBM4wpAkGzRlif-_xw,1104
aiogram/types/transaction_partner_telegram_ads.py,sha256=QmVjyRZUGNTpxRIFZF4F_pAPj9i4224E1PsnA9GS8YU,1185
aiogram/types/transaction_partner_telegram_api.py,sha256=ntdhxePoaNtTFNKkwVN34VeX60VYkH0PxJnRI4_0z1E,1419
aiogram/types/transaction_partner_user.py,sha256=W_T8f2Fu_4-ZDOQGpt6OrKCkz5p-m3zMhPfvxY7S2fs,2872
aiogram/types/update.py,sha256=vRi2nszPAGjfYLgjVfxvROOrakObWp_hFtf3BS-AAvQ,11923
aiogram/types/user.py,sha256=DW0Xg1Lld2oB5wwOZtBIJggt-WiNLKOY-4HHSpU_t1k,6001
aiogram/types/user_chat_boosts.py,sha256=SLpS9r5MUTva6qyarE6wkGV8e1KTvUqM9ldfXAwHw-o,940
aiogram/types/user_profile_photos.py,sha256=ET0y9iSQw7975QnIjsLzjZ-nWpvhDwMrZRJuxsSdfY8,1118
aiogram/types/user_shared.py,sha256=1yGKAO67fshHSOCtVl3dO_oD1-VOyzPBC4Ak-hflMOQ,1534
aiogram/types/users_shared.py,sha256=W8gUM4q8YZvLEFnvkQsLHV91iMRvC7TcXywXmMVe1Wc,1980
aiogram/types/venue.py,sha256=wEmGfGAiqdhDhTskRoxOKiNVliUaPpPOxBmohfDlL5I,2147
aiogram/types/video.py,sha256=e-uI5iQSKAdhPK-vVz5kiQCIV22kaxmylzN8gzmtwnk,2575
aiogram/types/video_chat_ended.py,sha256=eL3ZSR0WfVcHEEpXrdLm1mvuogXS7RAoxLwX2AwMdeg,839
aiogram/types/video_chat_participants_invited.py,sha256=FQAGFFuJkrkg7GwoYx0IxHebIV4ap1czpALPE7282N8,937
aiogram/types/video_chat_scheduled.py,sha256=G-a24czeK7NVzJ88SKFNutPd_SGnRgTBLy85rHozNLo,990
aiogram/types/video_chat_started.py,sha256=FYHVcH-a5knoXIYMmCQThFt8hjjPwAiZ8QvGLNVWgl4,309
aiogram/types/video_note.py,sha256=Hwa8uTCnyb9yT9eGikFwyVfk6X779md3DNAPbav6VrM,2059
aiogram/types/voice.py,sha256=AMyEJovxyDl2TRVpEfpHLMSmsXNjXIrNDH73HxQUxqg,1932
aiogram/types/web_app_data.py,sha256=CzkuU2VwVYQ7WBkFA6Zh8BL-qUKQeB__mcmhmVZnT2A,1107
aiogram/types/web_app_info.py,sha256=aPH4uLFFLKNLEF3RUm3uYCkg9Hk545kMoUvrkg4TUds,931
aiogram/types/webhook_info.py,sha256=huNLXO-9PAFI8ti5a4TcYfbWCirITn6N-i2Lphc1Yak,2986
aiogram/types/write_access_allowed.py,sha256=3GiPknn_fKHAJ53FROE4AK4MtpwluNlAuZvFuOP6diA,1912
aiogram/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/utils/__pycache__/__init__.cpython-311.pyc,,
aiogram/utils/__pycache__/auth_widget.cpython-311.pyc,,
aiogram/utils/__pycache__/backoff.cpython-311.pyc,,
aiogram/utils/__pycache__/callback_answer.cpython-311.pyc,,
aiogram/utils/__pycache__/chat_action.cpython-311.pyc,,
aiogram/utils/__pycache__/chat_member.cpython-311.pyc,,
aiogram/utils/__pycache__/deep_linking.cpython-311.pyc,,
aiogram/utils/__pycache__/formatting.cpython-311.pyc,,
aiogram/utils/__pycache__/keyboard.cpython-311.pyc,,
aiogram/utils/__pycache__/link.cpython-311.pyc,,
aiogram/utils/__pycache__/magic_filter.cpython-311.pyc,,
aiogram/utils/__pycache__/markdown.cpython-311.pyc,,
aiogram/utils/__pycache__/media_group.cpython-311.pyc,,
aiogram/utils/__pycache__/mixins.cpython-311.pyc,,
aiogram/utils/__pycache__/mypy_hacks.cpython-311.pyc,,
aiogram/utils/__pycache__/payload.cpython-311.pyc,,
aiogram/utils/__pycache__/serialization.cpython-311.pyc,,
aiogram/utils/__pycache__/text_decorations.cpython-311.pyc,,
aiogram/utils/__pycache__/token.cpython-311.pyc,,
aiogram/utils/__pycache__/warnings.cpython-311.pyc,,
aiogram/utils/__pycache__/web_app.cpython-311.pyc,,
aiogram/utils/auth_widget.py,sha256=kcNMXu7hA4kWHw_PeZsGFaeC0m0Sbtbd2-8Q9rWfkHg,981
aiogram/utils/backoff.py,sha256=D_d7z94PyR5USzeoIednnCgW07qQgwQNgVdPGkgrmJM,2123
aiogram/utils/callback_answer.py,sha256=_NHsLMjfZBKOXtyCSHBh4s9V3go-U8Wsk5JqpMh-O4c,6573
aiogram/utils/chat_action.py,sha256=3GW2mDqQQ5fv6LMpeqB0j-J1d1blml2aF4q7vqlnMBQ,12201
aiogram/utils/chat_member.py,sha256=WwMEi__6DYeF55TFuLxPS-OQaDdTa0I2f-yJkt3Dio0,942
aiogram/utils/deep_linking.py,sha256=D89nv29pnL63_N_E_ZfLjiarTQZhg0oq6RnRdWRDSTE,2980
aiogram/utils/formatting.py,sha256=7je7KplskUsRgo4DFVWwnN0g4C8JMBYC4tTVxw-3zr4,16902
aiogram/utils/i18n/__init__.py,sha256=mwTwSL1cuXYi3a3LBICqoH66y4kHVML24YNPbGlZ55M,440
aiogram/utils/i18n/__pycache__/__init__.cpython-311.pyc,,
aiogram/utils/i18n/__pycache__/context.cpython-311.pyc,,
aiogram/utils/i18n/__pycache__/core.cpython-311.pyc,,
aiogram/utils/i18n/__pycache__/lazy_proxy.cpython-311.pyc,,
aiogram/utils/i18n/__pycache__/middleware.cpython-311.pyc,,
aiogram/utils/i18n/context.py,sha256=fhLzDZvxqRMvPu2-lEUrH7n09oHl-H0qo2mLFyKzCJk,549
aiogram/utils/i18n/core.py,sha256=XX_iuwylDTfrfCrafnqiFq28LDr-bvoO4Qnkhy81d_M,3542
aiogram/utils/i18n/lazy_proxy.py,sha256=0yHtyrejG9wG4yzLl1cDEsFomww2nyR7KFSbFyp7_NA,472
aiogram/utils/i18n/middleware.py,sha256=lqeui9-x7ztMQPI0IeQeRawe6orMjpsg2EtvXTzAQj4,5874
aiogram/utils/keyboard.py,sha256=wxFEfjx8n3zPK1v6Sv6a7glKILbZFS-xw_8nfKPrz6U,13343
aiogram/utils/link.py,sha256=z7Zp5uk5z3iNO26-wZrhoCDNLt1N0KjbPYhthOq-50o,2261
aiogram/utils/magic_filter.py,sha256=WGHOC9k2acxrcpkFF-ViQv3LbzRAKtJj5MRv7IEi75c,666
aiogram/utils/markdown.py,sha256=3T1UVSzaAwnYnTNNq6pklq-TTs6Y6zINp-LIWgwrAyU,4481
aiogram/utils/media_group.py,sha256=E28oJhPoTQ4iKXr0QUx5W1nd7VPHAJf0PQz0MeqG46E,14804
aiogram/utils/mixins.py,sha256=ox0Q6BlxcyTfAtzLVMEY9eUfXXhnXtkkLBQlUQ6uL4o,2923
aiogram/utils/mypy_hacks.py,sha256=cm5_tBzfrkG5OOIs2m5U4JfQKohj1KkmCRXNLTJFDy0,433
aiogram/utils/payload.py,sha256=mGJdQryRHL07VbSjdzGbN1NbYzd_mN2MGY1RiY3_KX4,2942
aiogram/utils/serialization.py,sha256=SMiqv2TQed_GOhvhWoaABFM65jjIiXqBJTRRdMQ9nXY,2765
aiogram/utils/text_decorations.py,sha256=eEUykrlq22MQuKrzayVBWpBPaXEZqI6j5PbJH2HyFNE,8256
aiogram/utils/token.py,sha256=iFfmK-pHGyPsF4ekWWcOlfH4OR9xGNAyCIavND8Ykrc,936
aiogram/utils/warnings.py,sha256=xGsQcxrMXtdZfLOGRCqKRIJbw80DA39COcctikBfYAE,89
aiogram/utils/web_app.py,sha256=pYFAohjk8OW3qRQkJebMtKaFJ7vOL4OJivUEh171kkI,7105
aiogram/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/webhook/__pycache__/__init__.cpython-311.pyc,,
aiogram/webhook/__pycache__/aiohttp_server.cpython-311.pyc,,
aiogram/webhook/__pycache__/security.cpython-311.pyc,,
aiogram/webhook/aiohttp_server.py,sha256=3uto1_vwf6h73SXFNSoDxGt49gFHFYDI1wf4uRcxWUg,10454
aiogram/webhook/security.py,sha256=ifd2TrPSOlXfutYyAmRh0LT3hJkB4o79XmEV1lo6fv0,1350

View File

@@ -0,0 +1,4 @@
Wheel-Version: 1.0
Generator: hatchling 1.27.0
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1,18 @@
Copyright (c) 2017 - present Alex Root Junior
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,41 @@
import asyncio as _asyncio
from contextlib import suppress
from aiogram.dispatcher.flags import FlagGenerator
from . import enums, methods, types
from .__meta__ import __api_version__, __version__
from .client import session
from .client.bot import Bot
from .dispatcher.dispatcher import Dispatcher
from .dispatcher.middlewares.base import BaseMiddleware
from .dispatcher.router import Router
from .utils.magic_filter import MagicFilter
from .utils.text_decorations import html_decoration as html
from .utils.text_decorations import markdown_decoration as md
with suppress(ImportError):
import uvloop as _uvloop
_asyncio.set_event_loop_policy(_uvloop.EventLoopPolicy())
F = MagicFilter()
flags = FlagGenerator()
__all__ = (
"__api_version__",
"__version__",
"types",
"methods",
"enums",
"Bot",
"session",
"Dispatcher",
"Router",
"BaseMiddleware",
"F",
"html",
"md",
"flags",
)

View File

@@ -0,0 +1,2 @@
__version__ = "3.16.0"
__api_version__ = "8.1"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
from typing import TYPE_CHECKING, Any, Optional
from pydantic import BaseModel, PrivateAttr
from typing_extensions import Self
if TYPE_CHECKING:
from aiogram.client.bot import Bot
class BotContextController(BaseModel):
_bot: Optional["Bot"] = PrivateAttr()
def model_post_init(self, __context: Any) -> None:
self._bot = __context.get("bot") if __context else None
def as_(self, bot: Optional["Bot"]) -> Self:
"""
Bind object to a bot instance.
:param bot: Bot instance
:return: self
"""
self._bot = bot
return self
@property
def bot(self) -> Optional["Bot"]:
"""
Get bot instance.
:return: Bot instance
"""
return self._bot

View File

@@ -0,0 +1,85 @@
from __future__ import annotations
import sys
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, Optional
if TYPE_CHECKING:
from aiogram.types import LinkPreviewOptions
# @dataclass ??
class Default:
# Is not a dataclass because of JSON serialization.
__slots__ = ("_name",)
def __init__(self, name: str) -> None:
self._name = name
@property
def name(self) -> str:
return self._name
def __str__(self) -> str:
return f"Default({self._name!r})"
def __repr__(self) -> str:
return f"<{self}>"
_dataclass_properties: Dict[str, Any] = {}
if sys.version_info >= (3, 10):
# Speedup attribute access for dataclasses in Python 3.10+
_dataclass_properties.update({"slots": True, "kw_only": True})
@dataclass(**_dataclass_properties)
class DefaultBotProperties:
"""
Default bot properties.
"""
parse_mode: Optional[str] = None
"""Default parse mode for messages."""
disable_notification: Optional[bool] = None
"""Sends the message silently. Users will receive a notification with no sound."""
protect_content: Optional[bool] = None
"""Protects content from copying."""
allow_sending_without_reply: Optional[bool] = None
"""Allows to send messages without reply."""
link_preview: Optional[LinkPreviewOptions] = None
"""Link preview settings."""
link_preview_is_disabled: Optional[bool] = None
"""Disables link preview."""
link_preview_prefer_small_media: Optional[bool] = None
"""Prefer small media in link preview."""
link_preview_prefer_large_media: Optional[bool] = None
"""Prefer large media in link preview."""
link_preview_show_above_text: Optional[bool] = None
"""Show link preview above text."""
show_caption_above_media: Optional[bool] = None
"""Show caption above media."""
def __post_init__(self) -> None:
has_any_link_preview_option = any(
(
self.link_preview_is_disabled,
self.link_preview_prefer_small_media,
self.link_preview_prefer_large_media,
self.link_preview_show_above_text,
)
)
if has_any_link_preview_option and self.link_preview is None:
from ..types import LinkPreviewOptions
self.link_preview = LinkPreviewOptions(
is_disabled=self.link_preview_is_disabled,
prefer_small_media=self.link_preview_prefer_small_media,
prefer_large_media=self.link_preview_prefer_large_media,
show_above_text=self.link_preview_show_above_text,
)
def __getitem__(self, item: str) -> Any:
return getattr(self, item, None)

View File

@@ -0,0 +1,215 @@
from __future__ import annotations
import asyncio
import ssl
from typing import (
TYPE_CHECKING,
Any,
AsyncGenerator,
Dict,
Iterable,
List,
Optional,
Tuple,
Type,
Union,
cast,
)
import certifi
from aiohttp import BasicAuth, ClientError, ClientSession, FormData, TCPConnector
from aiohttp.hdrs import USER_AGENT
from aiohttp.http import SERVER_SOFTWARE
from aiogram.__meta__ import __version__
from aiogram.methods import TelegramMethod
from ...exceptions import TelegramNetworkError
from ...methods.base import TelegramType
from ...types import InputFile
from .base import BaseSession
if TYPE_CHECKING:
from ..bot import Bot
_ProxyBasic = Union[str, Tuple[str, BasicAuth]]
_ProxyChain = Iterable[_ProxyBasic]
_ProxyType = Union[_ProxyChain, _ProxyBasic]
def _retrieve_basic(basic: _ProxyBasic) -> Dict[str, Any]:
from aiohttp_socks.utils import parse_proxy_url # type: ignore
proxy_auth: Optional[BasicAuth] = None
if isinstance(basic, str):
proxy_url = basic
else:
proxy_url, proxy_auth = basic
proxy_type, host, port, username, password = parse_proxy_url(proxy_url)
if isinstance(proxy_auth, BasicAuth):
username = proxy_auth.login
password = proxy_auth.password
return {
"proxy_type": proxy_type,
"host": host,
"port": port,
"username": username,
"password": password,
"rdns": True,
}
def _prepare_connector(chain_or_plain: _ProxyType) -> Tuple[Type["TCPConnector"], Dict[str, Any]]:
from aiohttp_socks import ( # type: ignore
ChainProxyConnector,
ProxyConnector,
ProxyInfo,
)
# since tuple is Iterable(compatible with _ProxyChain) object, we assume that
# user wants chained proxies if tuple is a pair of string(url) and BasicAuth
if isinstance(chain_or_plain, str) or (
isinstance(chain_or_plain, tuple) and len(chain_or_plain) == 2
):
chain_or_plain = cast(_ProxyBasic, chain_or_plain)
return ProxyConnector, _retrieve_basic(chain_or_plain)
chain_or_plain = cast(_ProxyChain, chain_or_plain)
infos: List[ProxyInfo] = []
for basic in chain_or_plain:
infos.append(ProxyInfo(**_retrieve_basic(basic)))
return ChainProxyConnector, {"proxy_infos": infos}
class AiohttpSession(BaseSession):
def __init__(
self, proxy: Optional[_ProxyType] = None, limit: int = 100, **kwargs: Any
) -> None:
"""
Client session based on aiohttp.
:param proxy: The proxy to be used for requests. Default is None.
:param limit: The total number of simultaneous connections. Default is 100.
:param kwargs: Additional keyword arguments.
"""
super().__init__(**kwargs)
self._session: Optional[ClientSession] = None
self._connector_type: Type[TCPConnector] = TCPConnector
self._connector_init: Dict[str, Any] = {
"ssl": ssl.create_default_context(cafile=certifi.where()),
"limit": limit,
"ttl_dns_cache": 3600, # Workaround for https://github.com/aiogram/aiogram/issues/1500
}
self._should_reset_connector = True # flag determines connector state
self._proxy: Optional[_ProxyType] = None
if proxy is not None:
try:
self._setup_proxy_connector(proxy)
except ImportError as exc: # pragma: no cover
raise RuntimeError(
"In order to use aiohttp client for proxy requests, install "
"https://pypi.org/project/aiohttp-socks/"
) from exc
def _setup_proxy_connector(self, proxy: _ProxyType) -> None:
self._connector_type, self._connector_init = _prepare_connector(proxy)
self._proxy = proxy
@property
def proxy(self) -> Optional[_ProxyType]:
return self._proxy
@proxy.setter
def proxy(self, proxy: _ProxyType) -> None:
self._setup_proxy_connector(proxy)
self._should_reset_connector = True
async def create_session(self) -> ClientSession:
if self._should_reset_connector:
await self.close()
if self._session is None or self._session.closed:
self._session = ClientSession(
connector=self._connector_type(**self._connector_init),
headers={
USER_AGENT: f"{SERVER_SOFTWARE} aiogram/{__version__}",
},
)
self._should_reset_connector = False
return self._session
async def close(self) -> None:
if self._session is not None and not self._session.closed:
await self._session.close()
# Wait 250 ms for the underlying SSL connections to close
# https://docs.aiohttp.org/en/stable/client_advanced.html#graceful-shutdown
await asyncio.sleep(0.25)
def build_form_data(self, bot: Bot, method: TelegramMethod[TelegramType]) -> FormData:
form = FormData(quote_fields=False)
files: Dict[str, InputFile] = {}
for key, value in method.model_dump(warnings=False).items():
value = self.prepare_value(value, bot=bot, files=files)
if not value:
continue
form.add_field(key, value)
for key, value in files.items():
form.add_field(
key,
value.read(bot),
filename=value.filename or key,
)
return form
async def make_request(
self, bot: Bot, method: TelegramMethod[TelegramType], timeout: Optional[int] = None
) -> TelegramType:
session = await self.create_session()
url = self.api.api_url(token=bot.token, method=method.__api_method__)
form = self.build_form_data(bot=bot, method=method)
try:
async with session.post(
url, data=form, timeout=self.timeout if timeout is None else timeout
) as resp:
raw_result = await resp.text()
except asyncio.TimeoutError:
raise TelegramNetworkError(method=method, message="Request timeout error")
except ClientError as e:
raise TelegramNetworkError(method=method, message=f"{type(e).__name__}: {e}")
response = self.check_response(
bot=bot, method=method, status_code=resp.status, content=raw_result
)
return cast(TelegramType, response.result)
async def stream_content(
self,
url: str,
headers: Optional[Dict[str, Any]] = None,
timeout: int = 30,
chunk_size: int = 65536,
raise_for_status: bool = True,
) -> AsyncGenerator[bytes, None]:
if headers is None:
headers = {}
session = await self.create_session()
async with session.get(
url, timeout=timeout, headers=headers, raise_for_status=raise_for_status
) as resp:
async for chunk in resp.content.iter_chunked(chunk_size):
yield chunk
async def __aenter__(self) -> AiohttpSession:
await self.create_session()
return self

View File

@@ -0,0 +1,265 @@
from __future__ import annotations
import abc
import datetime
import json
import secrets
from enum import Enum
from http import HTTPStatus
from types import TracebackType
from typing import (
TYPE_CHECKING,
Any,
AsyncGenerator,
Callable,
Dict,
Final,
Optional,
Type,
cast,
)
from pydantic import ValidationError
from aiogram.exceptions import (
ClientDecodeError,
RestartingTelegram,
TelegramAPIError,
TelegramBadRequest,
TelegramConflictError,
TelegramEntityTooLarge,
TelegramForbiddenError,
TelegramMigrateToChat,
TelegramNotFound,
TelegramRetryAfter,
TelegramServerError,
TelegramUnauthorizedError,
)
from ...methods import Response, TelegramMethod
from ...methods.base import TelegramType
from ...types import InputFile, TelegramObject
from ..default import Default
from ..telegram import PRODUCTION, TelegramAPIServer
from .middlewares.manager import RequestMiddlewareManager
if TYPE_CHECKING:
from ..bot import Bot
_JsonLoads = Callable[..., Any]
_JsonDumps = Callable[..., str]
DEFAULT_TIMEOUT: Final[float] = 60.0
class BaseSession(abc.ABC):
"""
This is base class for all HTTP sessions in aiogram.
If you want to create your own session, you must inherit from this class.
"""
def __init__(
self,
api: TelegramAPIServer = PRODUCTION,
json_loads: _JsonLoads = json.loads,
json_dumps: _JsonDumps = json.dumps,
timeout: float = DEFAULT_TIMEOUT,
) -> None:
"""
:param api: Telegram Bot API URL patterns
:param json_loads: JSON loader
:param json_dumps: JSON dumper
:param timeout: Session scope request timeout
"""
self.api = api
self.json_loads = json_loads
self.json_dumps = json_dumps
self.timeout = timeout
self.middleware = RequestMiddlewareManager()
def check_response(
self, bot: Bot, method: TelegramMethod[TelegramType], status_code: int, content: str
) -> Response[TelegramType]:
"""
Check response status
"""
try:
json_data = self.json_loads(content)
except Exception as e:
# Handled error type can't be classified as specific error
# in due to decoder can be customized and raise any exception
raise ClientDecodeError("Failed to decode object", e, content)
try:
response_type = Response[method.__returning__] # type: ignore
response = response_type.model_validate(json_data, context={"bot": bot})
except ValidationError as e:
raise ClientDecodeError("Failed to deserialize object", e, json_data)
if HTTPStatus.OK <= status_code <= HTTPStatus.IM_USED and response.ok:
return response
description = cast(str, response.description)
if parameters := response.parameters:
if parameters.retry_after:
raise TelegramRetryAfter(
method=method, message=description, retry_after=parameters.retry_after
)
if parameters.migrate_to_chat_id:
raise TelegramMigrateToChat(
method=method,
message=description,
migrate_to_chat_id=parameters.migrate_to_chat_id,
)
if status_code == HTTPStatus.BAD_REQUEST:
raise TelegramBadRequest(method=method, message=description)
if status_code == HTTPStatus.NOT_FOUND:
raise TelegramNotFound(method=method, message=description)
if status_code == HTTPStatus.CONFLICT:
raise TelegramConflictError(method=method, message=description)
if status_code == HTTPStatus.UNAUTHORIZED:
raise TelegramUnauthorizedError(method=method, message=description)
if status_code == HTTPStatus.FORBIDDEN:
raise TelegramForbiddenError(method=method, message=description)
if status_code == HTTPStatus.REQUEST_ENTITY_TOO_LARGE:
raise TelegramEntityTooLarge(method=method, message=description)
if status_code >= HTTPStatus.INTERNAL_SERVER_ERROR:
if "restart" in description:
raise RestartingTelegram(method=method, message=description)
raise TelegramServerError(method=method, message=description)
raise TelegramAPIError(
method=method,
message=description,
)
@abc.abstractmethod
async def close(self) -> None: # pragma: no cover
"""
Close client session
"""
pass
@abc.abstractmethod
async def make_request(
self,
bot: Bot,
method: TelegramMethod[TelegramType],
timeout: Optional[int] = None,
) -> TelegramType: # pragma: no cover
"""
Make request to Telegram Bot API
:param bot: Bot instance
:param method: Method instance
:param timeout: Request timeout
:return:
:raise TelegramApiError:
"""
pass
@abc.abstractmethod
async def stream_content(
self,
url: str,
headers: Optional[Dict[str, Any]] = None,
timeout: int = 30,
chunk_size: int = 65536,
raise_for_status: bool = True,
) -> AsyncGenerator[bytes, None]: # pragma: no cover
"""
Stream reader
"""
yield b""
def prepare_value(
self,
value: Any,
bot: Bot,
files: Dict[str, Any],
_dumps_json: bool = True,
) -> Any:
"""
Prepare value before send
"""
if value is None:
return None
if isinstance(value, str):
return value
if isinstance(value, Default):
default_value = bot.default[value.name]
return self.prepare_value(default_value, bot=bot, files=files, _dumps_json=_dumps_json)
if isinstance(value, InputFile):
key = secrets.token_urlsafe(10)
files[key] = value
return f"attach://{key}"
if isinstance(value, dict):
value = {
key: prepared_item
for key, item in value.items()
if (
prepared_item := self.prepare_value(
item, bot=bot, files=files, _dumps_json=False
)
)
is not None
}
if _dumps_json:
return self.json_dumps(value)
return value
if isinstance(value, list):
value = [
prepared_item
for item in value
if (
prepared_item := self.prepare_value(
item, bot=bot, files=files, _dumps_json=False
)
)
is not None
]
if _dumps_json:
return self.json_dumps(value)
return value
if isinstance(value, datetime.timedelta):
now = datetime.datetime.now()
return str(round((now + value).timestamp()))
if isinstance(value, datetime.datetime):
return str(round(value.timestamp()))
if isinstance(value, Enum):
return self.prepare_value(value.value, bot=bot, files=files)
if isinstance(value, TelegramObject):
return self.prepare_value(
value.model_dump(warnings=False),
bot=bot,
files=files,
_dumps_json=_dumps_json,
)
if _dumps_json:
return self.json_dumps(value)
return value
async def __call__(
self,
bot: Bot,
method: TelegramMethod[TelegramType],
timeout: Optional[int] = None,
) -> TelegramType:
middleware = self.middleware.wrap_middlewares(self.make_request, timeout=timeout)
return cast(TelegramType, await middleware(bot, method))
async def __aenter__(self) -> BaseSession:
return self
async def __aexit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
await self.close()

View File

@@ -0,0 +1,53 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Protocol
from aiogram.methods import Response, TelegramMethod
from aiogram.methods.base import TelegramType
if TYPE_CHECKING:
from ...bot import Bot
class NextRequestMiddlewareType(Protocol[TelegramType]): # pragma: no cover
async def __call__(
self,
bot: "Bot",
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
pass
class RequestMiddlewareType(Protocol): # pragma: no cover
async def __call__(
self,
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
pass
class BaseRequestMiddleware(ABC):
"""
Generic middleware class
"""
@abstractmethod
async def __call__(
self,
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
"""
Execute middleware
:param make_request: Wrapped make_request in middlewares chain
:param bot: bot for request making
:param method: Request method (Subclass of :class:`aiogram.methods.base.TelegramMethod`)
:return: :class:`aiogram.methods.Response`
"""
pass

View File

@@ -0,0 +1,62 @@
from __future__ import annotations
from functools import partial
from typing import Any, Callable, List, Optional, Sequence, Union, cast, overload
from aiogram.client.session.middlewares.base import (
NextRequestMiddlewareType,
RequestMiddlewareType,
)
from aiogram.methods.base import TelegramType
class RequestMiddlewareManager(Sequence[RequestMiddlewareType]):
def __init__(self) -> None:
self._middlewares: List[RequestMiddlewareType] = []
def register(
self,
middleware: RequestMiddlewareType,
) -> RequestMiddlewareType:
self._middlewares.append(middleware)
return middleware
def unregister(self, middleware: RequestMiddlewareType) -> None:
self._middlewares.remove(middleware)
def __call__(
self,
middleware: Optional[RequestMiddlewareType] = None,
) -> Union[
Callable[[RequestMiddlewareType], RequestMiddlewareType],
RequestMiddlewareType,
]:
if middleware is None:
return self.register
return self.register(middleware)
@overload
def __getitem__(self, item: int) -> RequestMiddlewareType:
pass
@overload
def __getitem__(self, item: slice) -> Sequence[RequestMiddlewareType]:
pass
def __getitem__(
self, item: Union[int, slice]
) -> Union[RequestMiddlewareType, Sequence[RequestMiddlewareType]]:
return self._middlewares[item]
def __len__(self) -> int:
return len(self._middlewares)
def wrap_middlewares(
self,
callback: NextRequestMiddlewareType[TelegramType],
**kwargs: Any,
) -> NextRequestMiddlewareType[TelegramType]:
middleware = partial(callback, **kwargs)
for m in reversed(self._middlewares):
middleware = partial(m, middleware)
return cast(NextRequestMiddlewareType[TelegramType], middleware)

View File

@@ -0,0 +1,37 @@
import logging
from typing import TYPE_CHECKING, Any, List, Optional, Type
from aiogram import loggers
from aiogram.methods import TelegramMethod
from aiogram.methods.base import Response, TelegramType
from .base import BaseRequestMiddleware, NextRequestMiddlewareType
if TYPE_CHECKING:
from ...bot import Bot
logger = logging.getLogger(__name__)
class RequestLogging(BaseRequestMiddleware):
def __init__(self, ignore_methods: Optional[List[Type[TelegramMethod[Any]]]] = None):
"""
Middleware for logging outgoing requests
:param ignore_methods: methods to ignore in logging middleware
"""
self.ignore_methods = ignore_methods if ignore_methods else []
async def __call__(
self,
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
if type(method) not in self.ignore_methods:
loggers.middlewares.info(
"Make request with method=%r by bot id=%d",
type(method).__name__,
bot.id,
)
return await make_request(bot, method)

View File

@@ -0,0 +1,103 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Union
class FilesPathWrapper(ABC):
@abstractmethod
def to_local(self, path: Union[Path, str]) -> Union[Path, str]:
pass
@abstractmethod
def to_server(self, path: Union[Path, str]) -> Union[Path, str]:
pass
class BareFilesPathWrapper(FilesPathWrapper):
def to_local(self, path: Union[Path, str]) -> Union[Path, str]:
return path
def to_server(self, path: Union[Path, str]) -> Union[Path, str]:
return path
class SimpleFilesPathWrapper(FilesPathWrapper):
def __init__(self, server_path: Path, local_path: Path) -> None:
self.server_path = server_path
self.local_path = local_path
@classmethod
def _resolve(
cls, base1: Union[Path, str], base2: Union[Path, str], value: Union[Path, str]
) -> Path:
relative = Path(value).relative_to(base1)
return base2 / relative
def to_local(self, path: Union[Path, str]) -> Union[Path, str]:
return self._resolve(base1=self.server_path, base2=self.local_path, value=path)
def to_server(self, path: Union[Path, str]) -> Union[Path, str]:
return self._resolve(base1=self.local_path, base2=self.server_path, value=path)
@dataclass(frozen=True)
class TelegramAPIServer:
"""
Base config for API Endpoints
"""
base: str
"""Base URL"""
file: str
"""Files URL"""
is_local: bool = False
"""Mark this server is
in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_."""
wrap_local_file: FilesPathWrapper = BareFilesPathWrapper()
"""Callback to wrap files path in local mode"""
def api_url(self, token: str, method: str) -> str:
"""
Generate URL for API methods
:param token: Bot token
:param method: API method name (case insensitive)
:return: URL
"""
return self.base.format(token=token, method=method)
def file_url(self, token: str, path: str) -> str:
"""
Generate URL for downloading files
:param token: Bot token
:param path: file path
:return: URL
"""
return self.file.format(token=token, path=path)
@classmethod
def from_base(cls, base: str, **kwargs: Any) -> "TelegramAPIServer":
"""
Use this method to auto-generate TelegramAPIServer instance from base URL
:param base: Base URL
:return: instance of :class:`TelegramAPIServer`
"""
base = base.rstrip("/")
return cls(
base=f"{base}/bot{{token}}/{{method}}",
file=f"{base}/file/bot{{token}}/{{path}}",
**kwargs,
)
PRODUCTION = TelegramAPIServer(
base="https://api.telegram.org/bot{token}/{method}",
file="https://api.telegram.org/file/bot{token}/{path}",
)
TEST = TelegramAPIServer(
base="https://api.telegram.org/bot{token}/test/{method}",
file="https://api.telegram.org/file/bot{token}/test/{path}",
)

View File

@@ -0,0 +1,598 @@
from __future__ import annotations
import asyncio
import contextvars
import signal
import warnings
from asyncio import CancelledError, Event, Future, Lock
from contextlib import suppress
from typing import Any, AsyncGenerator, Dict, List, Optional, Set, Union
from .. import loggers
from ..client.bot import Bot
from ..exceptions import TelegramAPIError
from ..fsm.middleware import FSMContextMiddleware
from ..fsm.storage.base import BaseEventIsolation, BaseStorage
from ..fsm.storage.memory import DisabledEventIsolation, MemoryStorage
from ..fsm.strategy import FSMStrategy
from ..methods import GetUpdates, TelegramMethod
from ..methods.base import TelegramType
from ..types import Update, User
from ..types.base import UNSET, UNSET_TYPE
from ..types.update import UpdateTypeLookupError
from ..utils.backoff import Backoff, BackoffConfig
from .event.bases import UNHANDLED, SkipHandler
from .event.telegram import TelegramEventObserver
from .middlewares.error import ErrorsMiddleware
from .middlewares.user_context import UserContextMiddleware
from .router import Router
DEFAULT_BACKOFF_CONFIG = BackoffConfig(min_delay=1.0, max_delay=5.0, factor=1.3, jitter=0.1)
class Dispatcher(Router):
"""
Root router
"""
def __init__(
self,
*, # * - Preventing to pass instance of Bot to the FSM storage
storage: Optional[BaseStorage] = None,
fsm_strategy: FSMStrategy = FSMStrategy.USER_IN_CHAT,
events_isolation: Optional[BaseEventIsolation] = None,
disable_fsm: bool = False,
name: Optional[str] = None,
**kwargs: Any,
) -> None:
"""
Root router
:param storage: Storage for FSM
:param fsm_strategy: FSM strategy
:param events_isolation: Events isolation
:param disable_fsm: Disable FSM, note that if you disable FSM
then you should not use storage and events isolation
:param kwargs: Other arguments, will be passed as keyword arguments to handlers
"""
super(Dispatcher, self).__init__(name=name)
if storage and not isinstance(storage, BaseStorage):
raise TypeError(
f"FSM storage should be instance of 'BaseStorage' not {type(storage).__name__}"
)
# Telegram API provides originally only one event type - Update
# For making easily interactions with events here is registered handler which helps
# to separate Update to different event types like Message, CallbackQuery etc.
self.update = self.observers["update"] = TelegramEventObserver(
router=self, event_name="update"
)
self.update.register(self._listen_update)
# Error handlers should work is out of all other functions
# and should be registered before all others middlewares
self.update.outer_middleware(ErrorsMiddleware(self))
# User context middleware makes small optimization for all other builtin
# middlewares via caching the user and chat instances in the event context
self.update.outer_middleware(UserContextMiddleware())
# FSM middleware should always be registered after User context middleware
# because here is used context from previous step
self.fsm = FSMContextMiddleware(
storage=storage or MemoryStorage(),
strategy=fsm_strategy,
events_isolation=events_isolation or DisabledEventIsolation(),
)
if not disable_fsm:
# Note that when FSM middleware is disabled, the event isolation is also disabled
# Because the isolation mechanism is a part of the FSM
self.update.outer_middleware(self.fsm)
self.shutdown.register(self.fsm.close)
self.workflow_data: Dict[str, Any] = kwargs
self._running_lock = Lock()
self._stop_signal: Optional[Event] = None
self._stopped_signal: Optional[Event] = None
self._handle_update_tasks: Set[asyncio.Task[Any]] = set()
def __getitem__(self, item: str) -> Any:
return self.workflow_data[item]
def __setitem__(self, key: str, value: Any) -> None:
self.workflow_data[key] = value
def __delitem__(self, key: str) -> None:
del self.workflow_data[key]
def get(self, key: str, /, default: Optional[Any] = None) -> Optional[Any]:
return self.workflow_data.get(key, default)
@property
def storage(self) -> BaseStorage:
return self.fsm.storage
@property
def parent_router(self) -> Optional[Router]:
"""
Dispatcher has no parent router and can't be included to any other routers or dispatchers
:return:
"""
return None # noqa: RET501
@parent_router.setter
def parent_router(self, value: Router) -> None:
"""
Dispatcher is root Router then configuring parent router is not allowed
:param value:
:return:
"""
raise RuntimeError("Dispatcher can not be attached to another Router.")
async def feed_update(self, bot: Bot, update: Update, **kwargs: Any) -> Any:
"""
Main entry point for incoming updates
Response of this method can be used as Webhook response
:param bot:
:param update:
"""
loop = asyncio.get_running_loop()
handled = False
start_time = loop.time()
if update.bot != bot:
# Re-mounting update to the current bot instance for making possible to
# use it in shortcuts.
# Here is update is re-created because we need to propagate context to
# all nested objects and attributes of the Update, but it
# is impossible without roundtrip to JSON :(
# The preferred way is that pass already mounted Bot instance to this update
# before call feed_update method
update = Update.model_validate(update.model_dump(), context={"bot": bot})
try:
response = await self.update.wrap_outer_middleware(
self.update.trigger,
update,
{
**self.workflow_data,
**kwargs,
"bot": bot,
},
)
handled = response is not UNHANDLED
return response
finally:
finish_time = loop.time()
duration = (finish_time - start_time) * 1000
loggers.event.info(
"Update id=%s is %s. Duration %d ms by bot id=%d",
update.update_id,
"handled" if handled else "not handled",
duration,
bot.id,
)
async def feed_raw_update(self, bot: Bot, update: Dict[str, Any], **kwargs: Any) -> Any:
"""
Main entry point for incoming updates with automatic Dict->Update serializer
:param bot:
:param update:
:param kwargs:
"""
parsed_update = Update.model_validate(update, context={"bot": bot})
return await self._feed_webhook_update(bot=bot, update=parsed_update, **kwargs)
@classmethod
async def _listen_updates(
cls,
bot: Bot,
polling_timeout: int = 30,
backoff_config: BackoffConfig = DEFAULT_BACKOFF_CONFIG,
allowed_updates: Optional[List[str]] = None,
) -> AsyncGenerator[Update, None]:
"""
Endless updates reader with correctly handling any server-side or connection errors.
So you may not worry that the polling will stop working.
"""
backoff = Backoff(config=backoff_config)
get_updates = GetUpdates(timeout=polling_timeout, allowed_updates=allowed_updates)
kwargs = {}
if bot.session.timeout:
# Request timeout can be lower than session timeout and that's OK.
# To prevent false-positive TimeoutError we should wait longer than polling timeout
kwargs["request_timeout"] = int(bot.session.timeout + polling_timeout)
failed = False
while True:
try:
updates = await bot(get_updates, **kwargs)
except Exception as e:
failed = True
# In cases when Telegram Bot API was inaccessible don't need to stop polling
# process because some developers can't make auto-restarting of the script
loggers.dispatcher.error("Failed to fetch updates - %s: %s", type(e).__name__, e)
# And also backoff timeout is best practice to retry any network activity
loggers.dispatcher.warning(
"Sleep for %f seconds and try again... (tryings = %d, bot id = %d)",
backoff.next_delay,
backoff.counter,
bot.id,
)
await backoff.asleep()
continue
# In case when network connection was fixed let's reset the backoff
# to initial value and then process updates
if failed:
loggers.dispatcher.info(
"Connection established (tryings = %d, bot id = %d)",
backoff.counter,
bot.id,
)
backoff.reset()
failed = False
for update in updates:
yield update
# The getUpdates method returns the earliest 100 unconfirmed updates.
# To confirm an update, use the offset parameter when calling getUpdates
# All updates with update_id less than or equal to offset will be marked
# as confirmed on the server and will no longer be returned.
get_updates.offset = update.update_id + 1
async def _listen_update(self, update: Update, **kwargs: Any) -> Any:
"""
Main updates listener
Workflow:
- Detect content type and propagate to observers in current router
- If no one filter is pass - propagate update to child routers as Update
:param update:
:param kwargs:
:return:
"""
try:
update_type = update.event_type
event = update.event
except UpdateTypeLookupError as e:
warnings.warn(
"Detected unknown update type.\n"
"Seems like Telegram Bot API was updated and you have "
"installed not latest version of aiogram framework"
f"\nUpdate: {update.model_dump_json(exclude_unset=True)}",
RuntimeWarning,
)
raise SkipHandler() from e
kwargs.update(event_update=update)
return await self.propagate_event(update_type=update_type, event=event, **kwargs)
@classmethod
async def silent_call_request(cls, bot: Bot, result: TelegramMethod[Any]) -> None:
"""
Simulate answer into WebHook
:param bot:
:param result:
:return:
"""
try:
await bot(result)
except TelegramAPIError as e:
# In due to WebHook mechanism doesn't allow getting response for
# requests called in answer to WebHook request.
# Need to skip unsuccessful responses.
# For debugging here is added logging.
loggers.event.error("Failed to make answer: %s: %s", e.__class__.__name__, e)
async def _process_update(
self, bot: Bot, update: Update, call_answer: bool = True, **kwargs: Any
) -> bool:
"""
Propagate update to event listeners
:param bot: instance of Bot
:param update: instance of Update
:param call_answer: need to execute response as Telegram method (like answer into webhook)
:param kwargs: contextual data for middlewares, filters and handlers
:return: status
"""
try:
response = await self.feed_update(bot, update, **kwargs)
if call_answer and isinstance(response, TelegramMethod):
await self.silent_call_request(bot=bot, result=response)
return response is not UNHANDLED
except Exception as e:
loggers.event.exception(
"Cause exception while process update id=%d by bot id=%d\n%s: %s",
update.update_id,
bot.id,
e.__class__.__name__,
e,
)
return True # because update was processed but unsuccessful
async def _polling(
self,
bot: Bot,
polling_timeout: int = 30,
handle_as_tasks: bool = True,
backoff_config: BackoffConfig = DEFAULT_BACKOFF_CONFIG,
allowed_updates: Optional[List[str]] = None,
**kwargs: Any,
) -> None:
"""
Internal polling process
:param bot:
:param kwargs:
:return:
"""
user: User = await bot.me()
loggers.dispatcher.info(
"Run polling for bot @%s id=%d - %r", user.username, bot.id, user.full_name
)
try:
async for update in self._listen_updates(
bot,
polling_timeout=polling_timeout,
backoff_config=backoff_config,
allowed_updates=allowed_updates,
):
handle_update = self._process_update(bot=bot, update=update, **kwargs)
if handle_as_tasks:
handle_update_task = asyncio.create_task(handle_update)
self._handle_update_tasks.add(handle_update_task)
handle_update_task.add_done_callback(self._handle_update_tasks.discard)
else:
await handle_update
finally:
loggers.dispatcher.info(
"Polling stopped for bot @%s id=%d - %r", user.username, bot.id, user.full_name
)
async def _feed_webhook_update(self, bot: Bot, update: Update, **kwargs: Any) -> Any:
"""
The same with `Dispatcher.process_update()` but returns real response instead of bool
"""
try:
return await self.feed_update(bot, update, **kwargs)
except Exception as e:
loggers.event.exception(
"Cause exception while process update id=%d by bot id=%d\n%s: %s",
update.update_id,
bot.id,
e.__class__.__name__,
e,
)
raise
async def feed_webhook_update(
self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: float = 55, **kwargs: Any
) -> Optional[TelegramMethod[TelegramType]]:
if not isinstance(update, Update): # Allow to use raw updates
update = Update.model_validate(update, context={"bot": bot})
ctx = contextvars.copy_context()
loop = asyncio.get_running_loop()
waiter = loop.create_future()
def release_waiter(*_: Any) -> None:
if not waiter.done():
waiter.set_result(None)
timeout_handle = loop.call_later(_timeout, release_waiter)
process_updates: Future[Any] = asyncio.ensure_future(
self._feed_webhook_update(bot=bot, update=update, **kwargs)
)
process_updates.add_done_callback(release_waiter, context=ctx)
def process_response(task: Future[Any]) -> None:
warnings.warn(
"Detected slow response into webhook.\n"
"Telegram is waiting for response only first 60 seconds and then re-send update.\n"
"For preventing this situation response into webhook returned immediately "
"and handler is moved to background and still processing update.",
RuntimeWarning,
)
try:
result = task.result()
except Exception as e:
raise e
if isinstance(result, TelegramMethod):
asyncio.ensure_future(self.silent_call_request(bot=bot, result=result))
try:
try:
await waiter
except CancelledError: # pragma: no cover
process_updates.remove_done_callback(release_waiter)
process_updates.cancel()
raise
if process_updates.done():
# TODO: handle exceptions
response: Any = process_updates.result()
if isinstance(response, TelegramMethod):
return response
else:
process_updates.remove_done_callback(release_waiter)
process_updates.add_done_callback(process_response, context=ctx)
finally:
timeout_handle.cancel()
return None
async def stop_polling(self) -> None:
"""
Execute this method if you want to stop polling programmatically
:return:
"""
if not self._running_lock.locked():
raise RuntimeError("Polling is not started")
if not self._stop_signal or not self._stopped_signal:
return
self._stop_signal.set()
await self._stopped_signal.wait()
def _signal_stop_polling(self, sig: signal.Signals) -> None:
if not self._running_lock.locked():
return
loggers.dispatcher.warning("Received %s signal", sig.name)
if not self._stop_signal:
return
self._stop_signal.set()
async def start_polling(
self,
*bots: Bot,
polling_timeout: int = 10,
handle_as_tasks: bool = True,
backoff_config: BackoffConfig = DEFAULT_BACKOFF_CONFIG,
allowed_updates: Optional[Union[List[str], UNSET_TYPE]] = UNSET,
handle_signals: bool = True,
close_bot_session: bool = True,
**kwargs: Any,
) -> None:
"""
Polling runner
:param bots: Bot instances (one or more)
:param polling_timeout: Long-polling wait time
:param handle_as_tasks: Run task for each event and no wait result
:param backoff_config: backoff-retry config
:param allowed_updates: List of the update types you want your bot to receive
By default, all used update types are enabled (resolved from handlers)
:param handle_signals: handle signals (SIGINT/SIGTERM)
:param close_bot_session: close bot sessions on shutdown
:param kwargs: contextual data
:return:
"""
if not bots:
raise ValueError("At least one bot instance is required to start polling")
if "bot" in kwargs:
raise ValueError(
"Keyword argument 'bot' is not acceptable, "
"the bot instance should be passed as positional argument"
)
async with self._running_lock: # Prevent to run this method twice at a once
if self._stop_signal is None:
self._stop_signal = Event()
if self._stopped_signal is None:
self._stopped_signal = Event()
if allowed_updates is UNSET:
allowed_updates = self.resolve_used_update_types()
self._stop_signal.clear()
self._stopped_signal.clear()
if handle_signals:
loop = asyncio.get_running_loop()
with suppress(NotImplementedError): # pragma: no cover
# Signals handling is not supported on Windows
# It also can't be covered on Windows
loop.add_signal_handler(
signal.SIGTERM, self._signal_stop_polling, signal.SIGTERM
)
loop.add_signal_handler(
signal.SIGINT, self._signal_stop_polling, signal.SIGINT
)
workflow_data = {
"dispatcher": self,
"bots": bots,
**self.workflow_data,
**kwargs,
}
if "bot" in workflow_data:
workflow_data.pop("bot")
await self.emit_startup(bot=bots[-1], **workflow_data)
loggers.dispatcher.info("Start polling")
try:
tasks: List[asyncio.Task[Any]] = [
asyncio.create_task(
self._polling(
bot=bot,
handle_as_tasks=handle_as_tasks,
polling_timeout=polling_timeout,
backoff_config=backoff_config,
allowed_updates=allowed_updates,
**workflow_data,
)
)
for bot in bots
]
tasks.append(asyncio.create_task(self._stop_signal.wait()))
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
for task in pending:
# (mostly) Graceful shutdown unfinished tasks
task.cancel()
with suppress(CancelledError):
await task
# Wait finished tasks to propagate unhandled exceptions
await asyncio.gather(*done)
finally:
loggers.dispatcher.info("Polling stopped")
try:
await self.emit_shutdown(bot=bots[-1], **workflow_data)
finally:
if close_bot_session:
await asyncio.gather(*(bot.session.close() for bot in bots))
self._stopped_signal.set()
def run_polling(
self,
*bots: Bot,
polling_timeout: int = 10,
handle_as_tasks: bool = True,
backoff_config: BackoffConfig = DEFAULT_BACKOFF_CONFIG,
allowed_updates: Optional[Union[List[str], UNSET_TYPE]] = UNSET,
handle_signals: bool = True,
close_bot_session: bool = True,
**kwargs: Any,
) -> None:
"""
Run many bots with polling
:param bots: Bot instances (one or more)
:param polling_timeout: Long-polling wait time
:param handle_as_tasks: Run task for each event and no wait result
:param backoff_config: backoff-retry config
:param allowed_updates: List of the update types you want your bot to receive
:param handle_signals: handle signals (SIGINT/SIGTERM)
:param close_bot_session: close bot sessions on shutdown
:param kwargs: contextual data
:return:
"""
with suppress(KeyboardInterrupt):
return asyncio.run(
self.start_polling(
*bots,
**kwargs,
polling_timeout=polling_timeout,
handle_as_tasks=handle_as_tasks,
backoff_config=backoff_config,
allowed_updates=allowed_updates,
handle_signals=handle_signals,
close_bot_session=close_bot_session,
)
)

View File

@@ -0,0 +1,35 @@
from __future__ import annotations
from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, TypeVar, Union
from unittest.mock import sentinel
from ...types import TelegramObject
from ..middlewares.base import BaseMiddleware
MiddlewareEventType = TypeVar("MiddlewareEventType", bound=TelegramObject)
NextMiddlewareType = Callable[[MiddlewareEventType, Dict[str, Any]], Awaitable[Any]]
MiddlewareType = Union[
BaseMiddleware,
Callable[
[NextMiddlewareType[MiddlewareEventType], MiddlewareEventType, Dict[str, Any]],
Awaitable[Any],
],
]
UNHANDLED = sentinel.UNHANDLED
REJECTED = sentinel.REJECTED
class SkipHandler(Exception):
pass
class CancelHandler(Exception):
pass
def skip(message: Optional[str] = None) -> NoReturn:
"""
Raise an SkipHandler
"""
raise SkipHandler(message or "Event skipped")

View File

@@ -0,0 +1,53 @@
from __future__ import annotations
from typing import Any, Callable, List
from .handler import CallbackType, HandlerObject
class EventObserver:
"""
Simple events observer
Is used for managing events is not related with Telegram
(For example startup/shutdown processes)
Handlers can be registered via decorator or method
.. code-block:: python
<observer>.register(my_handler)
.. code-block:: python
@<observer>()
async def my_handler(*args, **kwargs): ...
"""
def __init__(self) -> None:
self.handlers: List[HandlerObject] = []
def register(self, callback: CallbackType) -> None:
"""
Register callback with filters
"""
self.handlers.append(HandlerObject(callback=callback))
async def trigger(self, *args: Any, **kwargs: Any) -> None:
"""
Propagate event to handlers.
Handler will be called when all its filters is pass.
"""
for handler in self.handlers:
await handler.call(*args, **kwargs)
def __call__(self) -> Callable[[CallbackType], CallbackType]:
"""
Decorator for registering event handlers
"""
def wrapper(callback: CallbackType) -> CallbackType:
self.register(callback)
return callback
return wrapper

View File

@@ -0,0 +1,99 @@
import asyncio
import contextvars
import inspect
import warnings
from dataclasses import dataclass, field
from functools import partial
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
from magic_filter.magic import MagicFilter as OriginalMagicFilter
from aiogram.dispatcher.flags import extract_flags_from_object
from aiogram.filters.base import Filter
from aiogram.handlers import BaseHandler
from aiogram.utils.magic_filter import MagicFilter
from aiogram.utils.warnings import Recommendation
CallbackType = Callable[..., Any]
@dataclass
class CallableObject:
callback: CallbackType
awaitable: bool = field(init=False)
params: Set[str] = field(init=False)
varkw: bool = field(init=False)
def __post_init__(self) -> None:
callback = inspect.unwrap(self.callback)
self.awaitable = inspect.isawaitable(callback) or inspect.iscoroutinefunction(callback)
spec = inspect.getfullargspec(callback)
self.params = {*spec.args, *spec.kwonlyargs}
self.varkw = spec.varkw is not None
def _prepare_kwargs(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
if self.varkw:
return kwargs
return {k: kwargs[k] for k in self.params if k in kwargs}
async def call(self, *args: Any, **kwargs: Any) -> Any:
wrapped = partial(self.callback, *args, **self._prepare_kwargs(kwargs))
if self.awaitable:
return await wrapped()
loop = asyncio.get_event_loop()
context = contextvars.copy_context()
wrapped = partial(context.run, wrapped)
return await loop.run_in_executor(None, wrapped)
@dataclass
class FilterObject(CallableObject):
magic: Optional[MagicFilter] = None
def __post_init__(self) -> None:
if isinstance(self.callback, OriginalMagicFilter):
# MagicFilter instance is callable but generates
# only "CallOperation" instead of applying the filter
self.magic = self.callback
self.callback = self.callback.resolve
if not isinstance(self.magic, MagicFilter):
# Issue: https://github.com/aiogram/aiogram/issues/990
warnings.warn(
category=Recommendation,
message="You are using F provided by magic_filter package directly, "
"but it lacks `.as_()` extension."
"\n Please change the import statement: from `from magic_filter import F` "
"to `from aiogram import F` to silence this warning.",
stacklevel=6,
)
super(FilterObject, self).__post_init__()
if isinstance(self.callback, Filter):
self.awaitable = True
@dataclass
class HandlerObject(CallableObject):
filters: Optional[List[FilterObject]] = None
flags: Dict[str, Any] = field(default_factory=dict)
def __post_init__(self) -> None:
super(HandlerObject, self).__post_init__()
callback = inspect.unwrap(self.callback)
if inspect.isclass(callback) and issubclass(callback, BaseHandler):
self.awaitable = True
self.flags.update(extract_flags_from_object(callback))
async def check(self, *args: Any, **kwargs: Any) -> Tuple[bool, Dict[str, Any]]:
if not self.filters:
return True, kwargs
for event_filter in self.filters:
check = await event_filter.call(*args, **kwargs)
if not check:
return False, kwargs
if isinstance(check, dict):
kwargs.update(check)
return True, kwargs

View File

@@ -0,0 +1,141 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
from aiogram.dispatcher.middlewares.manager import MiddlewareManager
from ...exceptions import UnsupportedKeywordArgument
from ...filters.base import Filter
from ...types import TelegramObject
from .bases import UNHANDLED, MiddlewareType, SkipHandler
from .handler import CallbackType, FilterObject, HandlerObject
if TYPE_CHECKING:
from aiogram.dispatcher.router import Router
class TelegramEventObserver:
"""
Event observer for Telegram events
Here you can register handler with filter.
This observer will stop event propagation when first handler is pass.
"""
def __init__(self, router: Router, event_name: str) -> None:
self.router: Router = router
self.event_name: str = event_name
self.handlers: List[HandlerObject] = []
self.middleware = MiddlewareManager()
self.outer_middleware = MiddlewareManager()
# Re-used filters check method from already implemented handler object
# with dummy callback which never will be used
self._handler = HandlerObject(callback=lambda: True, filters=[])
def filter(self, *filters: CallbackType) -> None:
"""
Register filter for all handlers of this event observer
:param filters: positional filters
"""
if self._handler.filters is None:
self._handler.filters = []
self._handler.filters.extend([FilterObject(filter_) for filter_ in filters])
def _resolve_middlewares(self) -> List[MiddlewareType[TelegramObject]]:
middlewares: List[MiddlewareType[TelegramObject]] = []
for router in reversed(tuple(self.router.chain_head)):
observer = router.observers.get(self.event_name)
if observer:
middlewares.extend(observer.middleware)
return middlewares
def register(
self,
callback: CallbackType,
*filters: CallbackType,
flags: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> CallbackType:
"""
Register event handler
"""
if kwargs:
raise UnsupportedKeywordArgument(
"Passing any additional keyword arguments to the registrar method "
"is not supported.\n"
"This error may be caused when you are trying to register filters like in 2.x "
"version of this framework, if it's true just look at correspoding "
"documentation pages.\n"
f"Please remove the {set(kwargs.keys())} arguments from this call.\n"
)
if flags is None:
flags = {}
for item in filters:
if isinstance(item, Filter):
item.update_handler_flags(flags=flags)
self.handlers.append(
HandlerObject(
callback=callback,
filters=[FilterObject(filter_) for filter_ in filters],
flags=flags,
)
)
return callback
def wrap_outer_middleware(
self, callback: Any, event: TelegramObject, data: Dict[str, Any]
) -> Any:
wrapped_outer = self.middleware.wrap_middlewares(
self.outer_middleware,
callback,
)
return wrapped_outer(event, data)
def check_root_filters(self, event: TelegramObject, **kwargs: Any) -> Any:
return self._handler.check(event, **kwargs)
async def trigger(self, event: TelegramObject, **kwargs: Any) -> Any:
"""
Propagate event to handlers and stops propagation on first match.
Handler will be called when all its filters are pass.
"""
for handler in self.handlers:
kwargs["handler"] = handler
result, data = await handler.check(event, **kwargs)
if result:
kwargs.update(data)
try:
wrapped_inner = self.outer_middleware.wrap_middlewares(
self._resolve_middlewares(),
handler.call,
)
return await wrapped_inner(event, kwargs)
except SkipHandler:
continue
return UNHANDLED
def __call__(
self,
*filters: CallbackType,
flags: Optional[Dict[str, Any]] = None,
**kwargs: Any,
) -> Callable[[CallbackType], CallbackType]:
"""
Decorator for registering event handlers
"""
def wrapper(callback: CallbackType) -> CallbackType:
self.register(callback, *filters, flags=flags, **kwargs)
return callback
return wrapper

View File

@@ -0,0 +1,127 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union, cast, overload
from magic_filter import AttrDict, MagicFilter
if TYPE_CHECKING:
from aiogram.dispatcher.event.handler import HandlerObject
@dataclass(frozen=True)
class Flag:
name: str
value: Any
@dataclass(frozen=True)
class FlagDecorator:
flag: Flag
@classmethod
def _with_flag(cls, flag: Flag) -> "FlagDecorator":
return cls(flag)
def _with_value(self, value: Any) -> "FlagDecorator":
new_flag = Flag(self.flag.name, value)
return self._with_flag(new_flag)
@overload
def __call__(self, value: Callable[..., Any], /) -> Callable[..., Any]: # type: ignore
pass
@overload
def __call__(self, value: Any, /) -> "FlagDecorator":
pass
@overload
def __call__(self, **kwargs: Any) -> "FlagDecorator":
pass
def __call__(
self,
value: Optional[Any] = None,
**kwargs: Any,
) -> Union[Callable[..., Any], "FlagDecorator"]:
if value and kwargs:
raise ValueError("The arguments `value` and **kwargs can not be used together")
if value is not None and callable(value):
value.aiogram_flag = {
**extract_flags_from_object(value),
self.flag.name: self.flag.value,
}
return cast(Callable[..., Any], value)
return self._with_value(AttrDict(kwargs) if value is None else value)
if TYPE_CHECKING:
class _ChatActionFlagProtocol(FlagDecorator):
def __call__( # type: ignore[override]
self,
action: str = ...,
interval: float = ...,
initial_sleep: float = ...,
**kwargs: Any,
) -> FlagDecorator:
pass
class FlagGenerator:
def __getattr__(self, name: str) -> FlagDecorator:
if name[0] == "_":
raise AttributeError("Flag name must NOT start with underscore")
return FlagDecorator(Flag(name, True))
if TYPE_CHECKING:
chat_action: _ChatActionFlagProtocol
def extract_flags_from_object(obj: Any) -> Dict[str, Any]:
if not hasattr(obj, "aiogram_flag"):
return {}
return cast(Dict[str, Any], obj.aiogram_flag)
def extract_flags(handler: Union["HandlerObject", Dict[str, Any]]) -> Dict[str, Any]:
"""
Extract flags from handler or middleware context data
:param handler: handler object or data
:return: dictionary with all handler flags
"""
if isinstance(handler, dict) and "handler" in handler:
handler = handler["handler"]
if hasattr(handler, "flags"):
return handler.flags
return {}
def get_flag(
handler: Union["HandlerObject", Dict[str, Any]],
name: str,
*,
default: Optional[Any] = None,
) -> Any:
"""
Get flag by name
:param handler: handler object or data
:param name: name of the flag
:param default: default value (None)
:return: value of the flag or default
"""
flags = extract_flags(handler)
return flags.get(name, default)
def check_flags(handler: Union["HandlerObject", Dict[str, Any]], magic: MagicFilter) -> Any:
"""
Check flags via magic filter
:param handler: handler object or data
:param magic: instance of the magic
:return: the result of magic filter check
"""
flags = extract_flags(handler)
return magic.resolve(AttrDict(flags))

View File

@@ -0,0 +1,29 @@
from abc import ABC, abstractmethod
from typing import Any, Awaitable, Callable, Dict, TypeVar
from aiogram.types import TelegramObject
T = TypeVar("T")
class BaseMiddleware(ABC):
"""
Generic middleware class
"""
@abstractmethod
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any: # pragma: no cover
"""
Execute middleware
:param handler: Wrapped handler in middlewares chain
:param event: Incoming event (Subclass of :class:`aiogram.types.base.TelegramObject`)
:param data: Contextual data. Will be mapped to handler arguments
:return: :class:`Any`
"""
pass

View File

@@ -0,0 +1,36 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, cast
from ...types import TelegramObject, Update
from ...types.error_event import ErrorEvent
from ..event.bases import UNHANDLED, CancelHandler, SkipHandler
from .base import BaseMiddleware
if TYPE_CHECKING:
from ..router import Router
class ErrorsMiddleware(BaseMiddleware):
def __init__(self, router: Router):
self.router = router
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
try:
return await handler(event, data)
except (SkipHandler, CancelHandler): # pragma: no cover
raise
except Exception as e:
response = await self.router.propagate_event(
update_type="error",
event=ErrorEvent(update=cast(Update, event), exception=e),
**data,
)
if response is not UNHANDLED:
return response
raise

View File

@@ -0,0 +1,65 @@
import functools
from typing import Any, Callable, Dict, List, Optional, Sequence, Union, overload
from aiogram.dispatcher.event.bases import (
MiddlewareEventType,
MiddlewareType,
NextMiddlewareType,
)
from aiogram.dispatcher.event.handler import CallbackType
from aiogram.types import TelegramObject
class MiddlewareManager(Sequence[MiddlewareType[TelegramObject]]):
def __init__(self) -> None:
self._middlewares: List[MiddlewareType[TelegramObject]] = []
def register(
self,
middleware: MiddlewareType[TelegramObject],
) -> MiddlewareType[TelegramObject]:
self._middlewares.append(middleware)
return middleware
def unregister(self, middleware: MiddlewareType[TelegramObject]) -> None:
self._middlewares.remove(middleware)
def __call__(
self,
middleware: Optional[MiddlewareType[TelegramObject]] = None,
) -> Union[
Callable[[MiddlewareType[TelegramObject]], MiddlewareType[TelegramObject]],
MiddlewareType[TelegramObject],
]:
if middleware is None:
return self.register
return self.register(middleware)
@overload
def __getitem__(self, item: int) -> MiddlewareType[TelegramObject]:
pass
@overload
def __getitem__(self, item: slice) -> Sequence[MiddlewareType[TelegramObject]]:
pass
def __getitem__(
self, item: Union[int, slice]
) -> Union[MiddlewareType[TelegramObject], Sequence[MiddlewareType[TelegramObject]]]:
return self._middlewares[item]
def __len__(self) -> int:
return len(self._middlewares)
@staticmethod
def wrap_middlewares(
middlewares: Sequence[MiddlewareType[MiddlewareEventType]], handler: CallbackType
) -> NextMiddlewareType[MiddlewareEventType]:
@functools.wraps(handler)
def handler_wrapper(event: TelegramObject, kwargs: Dict[str, Any]) -> Any:
return handler(event, **kwargs)
middleware = handler_wrapper
for m in reversed(middlewares):
middleware = functools.partial(m, middleware) # type: ignore[assignment]
return middleware

View File

@@ -0,0 +1,182 @@
from dataclasses import dataclass
from typing import Any, Awaitable, Callable, Dict, Optional
from aiogram.dispatcher.middlewares.base import BaseMiddleware
from aiogram.types import (
Chat,
ChatBoostSourcePremium,
InaccessibleMessage,
TelegramObject,
Update,
User,
)
EVENT_CONTEXT_KEY = "event_context"
EVENT_FROM_USER_KEY = "event_from_user"
EVENT_CHAT_KEY = "event_chat"
EVENT_THREAD_ID_KEY = "event_thread_id"
@dataclass(frozen=True)
class EventContext:
chat: Optional[Chat] = None
user: Optional[User] = None
thread_id: Optional[int] = None
business_connection_id: Optional[str] = None
@property
def user_id(self) -> Optional[int]:
return self.user.id if self.user else None
@property
def chat_id(self) -> Optional[int]:
return self.chat.id if self.chat else None
class UserContextMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
if not isinstance(event, Update):
raise RuntimeError("UserContextMiddleware got an unexpected event type!")
event_context = data[EVENT_CONTEXT_KEY] = self.resolve_event_context(event=event)
# Backward compatibility
if event_context.user is not None:
data[EVENT_FROM_USER_KEY] = event_context.user
if event_context.chat is not None:
data[EVENT_CHAT_KEY] = event_context.chat
if event_context.thread_id is not None:
data[EVENT_THREAD_ID_KEY] = event_context.thread_id
return await handler(event, data)
@classmethod
def resolve_event_context(cls, event: Update) -> EventContext:
"""
Resolve chat and user instance from Update object
"""
if event.message:
return EventContext(
chat=event.message.chat,
user=event.message.from_user,
thread_id=(
event.message.message_thread_id if event.message.is_topic_message else None
),
)
if event.edited_message:
return EventContext(
chat=event.edited_message.chat,
user=event.edited_message.from_user,
thread_id=(
event.edited_message.message_thread_id
if event.edited_message.is_topic_message
else None
),
)
if event.channel_post:
return EventContext(chat=event.channel_post.chat)
if event.edited_channel_post:
return EventContext(chat=event.edited_channel_post.chat)
if event.inline_query:
return EventContext(user=event.inline_query.from_user)
if event.chosen_inline_result:
return EventContext(user=event.chosen_inline_result.from_user)
if event.callback_query:
callback_query_message = event.callback_query.message
if callback_query_message:
return EventContext(
chat=callback_query_message.chat,
user=event.callback_query.from_user,
thread_id=(
callback_query_message.message_thread_id
if not isinstance(callback_query_message, InaccessibleMessage)
and callback_query_message.is_topic_message
else None
),
business_connection_id=(
callback_query_message.business_connection_id
if not isinstance(callback_query_message, InaccessibleMessage)
else None
),
)
return EventContext(user=event.callback_query.from_user)
if event.shipping_query:
return EventContext(user=event.shipping_query.from_user)
if event.pre_checkout_query:
return EventContext(user=event.pre_checkout_query.from_user)
if event.poll_answer:
return EventContext(
chat=event.poll_answer.voter_chat,
user=event.poll_answer.user,
)
if event.my_chat_member:
return EventContext(
chat=event.my_chat_member.chat, user=event.my_chat_member.from_user
)
if event.chat_member:
return EventContext(chat=event.chat_member.chat, user=event.chat_member.from_user)
if event.chat_join_request:
return EventContext(
chat=event.chat_join_request.chat, user=event.chat_join_request.from_user
)
if event.message_reaction:
return EventContext(
chat=event.message_reaction.chat,
user=event.message_reaction.user,
)
if event.message_reaction_count:
return EventContext(chat=event.message_reaction_count.chat)
if event.chat_boost:
# We only check the premium source, because only it has a sender user,
# other sources have a user, but it is not the sender, but the recipient
if isinstance(event.chat_boost.boost.source, ChatBoostSourcePremium):
return EventContext(
chat=event.chat_boost.chat,
user=event.chat_boost.boost.source.user,
)
return EventContext(chat=event.chat_boost.chat)
if event.removed_chat_boost:
return EventContext(chat=event.removed_chat_boost.chat)
if event.deleted_business_messages:
return EventContext(
chat=event.deleted_business_messages.chat,
business_connection_id=event.deleted_business_messages.business_connection_id,
)
if event.business_connection:
return EventContext(
user=event.business_connection.user,
business_connection_id=event.business_connection.id,
)
if event.business_message:
return EventContext(
chat=event.business_message.chat,
user=event.business_message.from_user,
thread_id=(
event.business_message.message_thread_id
if event.business_message.is_topic_message
else None
),
business_connection_id=event.business_message.business_connection_id,
)
if event.edited_business_message:
return EventContext(
chat=event.edited_business_message.chat,
user=event.edited_business_message.from_user,
thread_id=(
event.edited_business_message.message_thread_id
if event.edited_business_message.is_topic_message
else None
),
business_connection_id=event.edited_business_message.business_connection_id,
)
if event.purchased_paid_media:
return EventContext(
user=event.purchased_paid_media.from_user,
)
return EventContext()

View File

@@ -0,0 +1,275 @@
from __future__ import annotations
from typing import Any, Dict, Final, Generator, List, Optional, Set
from ..types import TelegramObject
from .event.bases import REJECTED, UNHANDLED
from .event.event import EventObserver
from .event.telegram import TelegramEventObserver
INTERNAL_UPDATE_TYPES: Final[frozenset[str]] = frozenset({"update", "error"})
class Router:
"""
Router can route update, and it nested update types like messages, callback query,
polls and all other event types.
Event handlers can be registered in observer by two ways:
- By observer method - :obj:`router.<event_type>.register(handler, <filters, ...>)`
- By decorator - :obj:`@router.<event_type>(<filters, ...>)`
"""
def __init__(self, *, name: Optional[str] = None) -> None:
"""
:param name: Optional router name, can be useful for debugging
"""
self.name = name or hex(id(self))
self._parent_router: Optional[Router] = None
self.sub_routers: List[Router] = []
# Observers
self.message = TelegramEventObserver(router=self, event_name="message")
self.edited_message = TelegramEventObserver(router=self, event_name="edited_message")
self.channel_post = TelegramEventObserver(router=self, event_name="channel_post")
self.edited_channel_post = TelegramEventObserver(
router=self, event_name="edited_channel_post"
)
self.inline_query = TelegramEventObserver(router=self, event_name="inline_query")
self.chosen_inline_result = TelegramEventObserver(
router=self, event_name="chosen_inline_result"
)
self.callback_query = TelegramEventObserver(router=self, event_name="callback_query")
self.shipping_query = TelegramEventObserver(router=self, event_name="shipping_query")
self.pre_checkout_query = TelegramEventObserver(
router=self, event_name="pre_checkout_query"
)
self.poll = TelegramEventObserver(router=self, event_name="poll")
self.poll_answer = TelegramEventObserver(router=self, event_name="poll_answer")
self.my_chat_member = TelegramEventObserver(router=self, event_name="my_chat_member")
self.chat_member = TelegramEventObserver(router=self, event_name="chat_member")
self.chat_join_request = TelegramEventObserver(router=self, event_name="chat_join_request")
self.message_reaction = TelegramEventObserver(router=self, event_name="message_reaction")
self.message_reaction_count = TelegramEventObserver(
router=self, event_name="message_reaction_count"
)
self.chat_boost = TelegramEventObserver(router=self, event_name="chat_boost")
self.removed_chat_boost = TelegramEventObserver(
router=self, event_name="removed_chat_boost"
)
self.deleted_business_messages = TelegramEventObserver(
router=self, event_name="deleted_business_messages"
)
self.business_connection = TelegramEventObserver(
router=self, event_name="business_connection"
)
self.edited_business_message = TelegramEventObserver(
router=self, event_name="edited_business_message"
)
self.business_message = TelegramEventObserver(router=self, event_name="business_message")
self.purchased_paid_media = TelegramEventObserver(
router=self, event_name="purchased_paid_media"
)
self.errors = self.error = TelegramEventObserver(router=self, event_name="error")
self.startup = EventObserver()
self.shutdown = EventObserver()
self.observers: Dict[str, TelegramEventObserver] = {
"message": self.message,
"edited_message": self.edited_message,
"channel_post": self.channel_post,
"edited_channel_post": self.edited_channel_post,
"inline_query": self.inline_query,
"chosen_inline_result": self.chosen_inline_result,
"callback_query": self.callback_query,
"shipping_query": self.shipping_query,
"pre_checkout_query": self.pre_checkout_query,
"poll": self.poll,
"poll_answer": self.poll_answer,
"my_chat_member": self.my_chat_member,
"chat_member": self.chat_member,
"chat_join_request": self.chat_join_request,
"message_reaction": self.message_reaction,
"message_reaction_count": self.message_reaction_count,
"chat_boost": self.chat_boost,
"removed_chat_boost": self.removed_chat_boost,
"deleted_business_messages": self.deleted_business_messages,
"business_connection": self.business_connection,
"edited_business_message": self.edited_business_message,
"business_message": self.business_message,
"purchased_paid_media": self.purchased_paid_media,
"error": self.errors,
}
def __str__(self) -> str:
return f"{type(self).__name__} {self.name!r}"
def __repr__(self) -> str:
return f"<{self}>"
def resolve_used_update_types(self, skip_events: Optional[Set[str]] = None) -> List[str]:
"""
Resolve registered event names
Is useful for getting updates only for registered event types.
:param skip_events: skip specified event names
:return: set of registered names
"""
handlers_in_use: Set[str] = set()
if skip_events is None:
skip_events = set()
skip_events = {*skip_events, *INTERNAL_UPDATE_TYPES}
for router in self.chain_tail:
for update_name, observer in router.observers.items():
if observer.handlers and update_name not in skip_events:
handlers_in_use.add(update_name)
return list(sorted(handlers_in_use)) # NOQA: C413
async def propagate_event(self, update_type: str, event: TelegramObject, **kwargs: Any) -> Any:
kwargs.update(event_router=self)
observer = self.observers.get(update_type)
async def _wrapped(telegram_event: TelegramObject, **data: Any) -> Any:
return await self._propagate_event(
observer=observer, update_type=update_type, event=telegram_event, **data
)
if observer:
return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs)
return await _wrapped(event, **kwargs)
async def _propagate_event(
self,
observer: Optional[TelegramEventObserver],
update_type: str,
event: TelegramObject,
**kwargs: Any,
) -> Any:
response = UNHANDLED
if observer:
# Check globally defined filters before any other handler will be checked.
# This check is placed here instead of `trigger` method to add possibility
# to pass context to handlers from global filters.
result, data = await observer.check_root_filters(event, **kwargs)
if not result:
return UNHANDLED
kwargs.update(data)
response = await observer.trigger(event, **kwargs)
if response is REJECTED: # pragma: no cover
# Possible only if some handler returns REJECTED
return UNHANDLED
if response is not UNHANDLED:
return response
for router in self.sub_routers:
response = await router.propagate_event(update_type=update_type, event=event, **kwargs)
if response is not UNHANDLED:
break
return response
@property
def chain_head(self) -> Generator[Router, None, None]:
router: Optional[Router] = self
while router:
yield router
router = router.parent_router
@property
def chain_tail(self) -> Generator[Router, None, None]:
yield self
for router in self.sub_routers:
yield from router.chain_tail
@property
def parent_router(self) -> Optional[Router]:
return self._parent_router
@parent_router.setter
def parent_router(self, router: Router) -> None:
"""
Internal property setter of parent router fot this router.
Do not use this method in own code.
All routers should be included via `include_router` method.
Self- and circular- referencing are not allowed here
:param router:
"""
if not isinstance(router, Router):
raise ValueError(f"router should be instance of Router not {type(router).__name__!r}")
if self._parent_router:
raise RuntimeError(f"Router is already attached to {self._parent_router!r}")
if self == router:
raise RuntimeError("Self-referencing routers is not allowed")
parent: Optional[Router] = router
while parent is not None:
if parent == self:
raise RuntimeError("Circular referencing of Router is not allowed")
parent = parent.parent_router
self._parent_router = router
router.sub_routers.append(self)
def include_routers(self, *routers: Router) -> None:
"""
Attach multiple routers.
:param routers:
:return:
"""
if not routers:
raise ValueError("At least one router must be provided")
for router in routers:
self.include_router(router)
def include_router(self, router: Router) -> Router:
"""
Attach another router.
:param router:
:return:
"""
if not isinstance(router, Router):
raise ValueError(
f"router should be instance of Router not {type(router).__class__.__name__}"
)
router.parent_router = self
return router
async def emit_startup(self, *args: Any, **kwargs: Any) -> None:
"""
Recursively call startup callbacks
:param args:
:param kwargs:
:return:
"""
kwargs.update(router=self)
await self.startup.trigger(*args, **kwargs)
for router in self.sub_routers:
await router.emit_startup(*args, **kwargs)
async def emit_shutdown(self, *args: Any, **kwargs: Any) -> None:
"""
Recursively call shutdown callbacks to graceful shutdown
:param args:
:param kwargs:
:return:
"""
kwargs.update(router=self)
await self.shutdown.trigger(*args, **kwargs)
for router in self.sub_routers:
await router.emit_shutdown(*args, **kwargs)

View File

@@ -0,0 +1,59 @@
from .bot_command_scope_type import BotCommandScopeType
from .chat_action import ChatAction
from .chat_boost_source_type import ChatBoostSourceType
from .chat_member_status import ChatMemberStatus
from .chat_type import ChatType
from .content_type import ContentType
from .currency import Currency
from .dice_emoji import DiceEmoji
from .encrypted_passport_element import EncryptedPassportElement
from .inline_query_result_type import InlineQueryResultType
from .input_media_type import InputMediaType
from .input_paid_media_type import InputPaidMediaType
from .keyboard_button_poll_type_type import KeyboardButtonPollTypeType
from .mask_position_point import MaskPositionPoint
from .menu_button_type import MenuButtonType
from .message_entity_type import MessageEntityType
from .message_origin_type import MessageOriginType
from .paid_media_type import PaidMediaType
from .parse_mode import ParseMode
from .passport_element_error_type import PassportElementErrorType
from .poll_type import PollType
from .reaction_type_type import ReactionTypeType
from .revenue_withdrawal_state_type import RevenueWithdrawalStateType
from .sticker_format import StickerFormat
from .sticker_type import StickerType
from .topic_icon_color import TopicIconColor
from .transaction_partner_type import TransactionPartnerType
from .update_type import UpdateType
__all__ = (
"BotCommandScopeType",
"ChatAction",
"ChatBoostSourceType",
"ChatMemberStatus",
"ChatType",
"ContentType",
"Currency",
"DiceEmoji",
"EncryptedPassportElement",
"InlineQueryResultType",
"InputMediaType",
"InputPaidMediaType",
"KeyboardButtonPollTypeType",
"MaskPositionPoint",
"MenuButtonType",
"MessageEntityType",
"MessageOriginType",
"PaidMediaType",
"ParseMode",
"PassportElementErrorType",
"PollType",
"ReactionTypeType",
"RevenueWithdrawalStateType",
"StickerFormat",
"StickerType",
"TopicIconColor",
"TransactionPartnerType",
"UpdateType",
)

View File

@@ -0,0 +1,17 @@
from enum import Enum
class BotCommandScopeType(str, Enum):
"""
This object represents the scope to which bot commands are applied.
Source: https://core.telegram.org/bots/api#botcommandscope
"""
DEFAULT = "default"
ALL_PRIVATE_CHATS = "all_private_chats"
ALL_GROUP_CHATS = "all_group_chats"
ALL_CHAT_ADMINISTRATORS = "all_chat_administrators"
CHAT = "chat"
CHAT_ADMINISTRATORS = "chat_administrators"
CHAT_MEMBER = "chat_member"

View File

@@ -0,0 +1,32 @@
from enum import Enum
class ChatAction(str, Enum):
"""
This object represents bot actions.
Choose one, depending on what the user is about to receive:
- typing for text messages,
- upload_photo for photos,
- record_video or upload_video for videos,
- record_voice or upload_voice for voice notes,
- upload_document for general files,
- choose_sticker for stickers,
- find_location for location data,
- record_video_note or upload_video_note for video notes.
Source: https://core.telegram.org/bots/api#sendchataction
"""
TYPING = "typing"
UPLOAD_PHOTO = "upload_photo"
RECORD_VIDEO = "record_video"
UPLOAD_VIDEO = "upload_video"
RECORD_VOICE = "record_voice"
UPLOAD_VOICE = "upload_voice"
UPLOAD_DOCUMENT = "upload_document"
CHOOSE_STICKER = "choose_sticker"
FIND_LOCATION = "find_location"
RECORD_VIDEO_NOTE = "record_video_note"
UPLOAD_VIDEO_NOTE = "upload_video_note"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class ChatBoostSourceType(str, Enum):
"""
This object represents a type of chat boost source.
Source: https://core.telegram.org/bots/api#chatboostsource
"""
PREMIUM = "premium"
GIFT_CODE = "gift_code"
GIVEAWAY = "giveaway"

View File

@@ -0,0 +1,16 @@
from enum import Enum
class ChatMemberStatus(str, Enum):
"""
This object represents chat member status.
Source: https://core.telegram.org/bots/api#chatmember
"""
CREATOR = "creator"
ADMINISTRATOR = "administrator"
MEMBER = "member"
RESTRICTED = "restricted"
LEFT = "left"
KICKED = "kicked"

View File

@@ -0,0 +1,15 @@
from enum import Enum
class ChatType(str, Enum):
"""
This object represents a chat type
Source: https://core.telegram.org/bots/api#chat
"""
SENDER = "sender"
PRIVATE = "private"
GROUP = "group"
SUPERGROUP = "supergroup"
CHANNEL = "channel"

View File

@@ -0,0 +1,66 @@
from enum import Enum
class ContentType(str, Enum):
"""
This object represents a type of content in message
"""
UNKNOWN = "unknown"
ANY = "any"
TEXT = "text"
ANIMATION = "animation"
AUDIO = "audio"
DOCUMENT = "document"
PAID_MEDIA = "paid_media"
PHOTO = "photo"
STICKER = "sticker"
STORY = "story"
VIDEO = "video"
VIDEO_NOTE = "video_note"
VOICE = "voice"
CONTACT = "contact"
DICE = "dice"
GAME = "game"
POLL = "poll"
VENUE = "venue"
LOCATION = "location"
NEW_CHAT_MEMBERS = "new_chat_members"
LEFT_CHAT_MEMBER = "left_chat_member"
NEW_CHAT_TITLE = "new_chat_title"
NEW_CHAT_PHOTO = "new_chat_photo"
DELETE_CHAT_PHOTO = "delete_chat_photo"
GROUP_CHAT_CREATED = "group_chat_created"
SUPERGROUP_CHAT_CREATED = "supergroup_chat_created"
CHANNEL_CHAT_CREATED = "channel_chat_created"
MESSAGE_AUTO_DELETE_TIMER_CHANGED = "message_auto_delete_timer_changed"
MIGRATE_TO_CHAT_ID = "migrate_to_chat_id"
MIGRATE_FROM_CHAT_ID = "migrate_from_chat_id"
PINNED_MESSAGE = "pinned_message"
INVOICE = "invoice"
SUCCESSFUL_PAYMENT = "successful_payment"
REFUNDED_PAYMENT = "refunded_payment"
USERS_SHARED = "users_shared"
CHAT_SHARED = "chat_shared"
CONNECTED_WEBSITE = "connected_website"
WRITE_ACCESS_ALLOWED = "write_access_allowed"
PASSPORT_DATA = "passport_data"
PROXIMITY_ALERT_TRIGGERED = "proximity_alert_triggered"
BOOST_ADDED = "boost_added"
CHAT_BACKGROUND_SET = "chat_background_set"
FORUM_TOPIC_CREATED = "forum_topic_created"
FORUM_TOPIC_EDITED = "forum_topic_edited"
FORUM_TOPIC_CLOSED = "forum_topic_closed"
FORUM_TOPIC_REOPENED = "forum_topic_reopened"
GENERAL_FORUM_TOPIC_HIDDEN = "general_forum_topic_hidden"
GENERAL_FORUM_TOPIC_UNHIDDEN = "general_forum_topic_unhidden"
GIVEAWAY_CREATED = "giveaway_created"
GIVEAWAY = "giveaway"
GIVEAWAY_WINNERS = "giveaway_winners"
GIVEAWAY_COMPLETED = "giveaway_completed"
VIDEO_CHAT_SCHEDULED = "video_chat_scheduled"
VIDEO_CHAT_STARTED = "video_chat_started"
VIDEO_CHAT_ENDED = "video_chat_ended"
VIDEO_CHAT_PARTICIPANTS_INVITED = "video_chat_participants_invited"
WEB_APP_DATA = "web_app_data"
USER_SHARED = "user_shared"

View File

@@ -0,0 +1,96 @@
from enum import Enum
class Currency(str, Enum):
"""
Currencies supported by Telegram Bot API
Source: https://core.telegram.org/bots/payments#supported-currencies
"""
AED = "AED"
AFN = "AFN"
ALL = "ALL"
AMD = "AMD"
ARS = "ARS"
AUD = "AUD"
AZN = "AZN"
BAM = "BAM"
BDT = "BDT"
BGN = "BGN"
BND = "BND"
BOB = "BOB"
BRL = "BRL"
BYN = "BYN"
CAD = "CAD"
CHF = "CHF"
CLP = "CLP"
CNY = "CNY"
COP = "COP"
CRC = "CRC"
CZK = "CZK"
DKK = "DKK"
DOP = "DOP"
DZD = "DZD"
EGP = "EGP"
ETB = "ETB"
EUR = "EUR"
GBP = "GBP"
GEL = "GEL"
GTQ = "GTQ"
HKD = "HKD"
HNL = "HNL"
HRK = "HRK"
HUF = "HUF"
IDR = "IDR"
ILS = "ILS"
INR = "INR"
ISK = "ISK"
JMD = "JMD"
JPY = "JPY"
KES = "KES"
KGS = "KGS"
KRW = "KRW"
KZT = "KZT"
LBP = "LBP"
LKR = "LKR"
MAD = "MAD"
MDL = "MDL"
MNT = "MNT"
MUR = "MUR"
MVR = "MVR"
MXN = "MXN"
MYR = "MYR"
MZN = "MZN"
NGN = "NGN"
NIO = "NIO"
NOK = "NOK"
NPR = "NPR"
NZD = "NZD"
PAB = "PAB"
PEN = "PEN"
PHP = "PHP"
PKR = "PKR"
PLN = "PLN"
PYG = "PYG"
QAR = "QAR"
RON = "RON"
RSD = "RSD"
RUB = "RUB"
SAR = "SAR"
SEK = "SEK"
SGD = "SGD"
THB = "THB"
TJS = "TJS"
TRY = "TRY"
TTD = "TTD"
TWD = "TWD"
TZS = "TZS"
UAH = "UAH"
UGX = "UGX"
USD = "USD"
UYU = "UYU"
UZS = "UZS"
VND = "VND"
YER = "YER"
ZAR = "ZAR"

View File

@@ -0,0 +1,16 @@
from enum import Enum
class DiceEmoji(str, Enum):
"""
Emoji on which the dice throw animation is based
Source: https://core.telegram.org/bots/api#dice
"""
DICE = "🎲"
DART = "🎯"
BASKETBALL = "🏀"
FOOTBALL = ""
SLOT_MACHINE = "🎰"
BOWLING = "🎳"

View File

@@ -0,0 +1,23 @@
from enum import Enum
class EncryptedPassportElement(str, Enum):
"""
This object represents type of encrypted passport element.
Source: https://core.telegram.org/bots/api#encryptedpassportelement
"""
PERSONAL_DETAILS = "personal_details"
PASSPORT = "passport"
DRIVER_LICENSE = "driver_license"
IDENTITY_CARD = "identity_card"
INTERNAL_PASSPORT = "internal_passport"
ADDRESS = "address"
UTILITY_BILL = "utility_bill"
BANK_STATEMENT = "bank_statement"
RENTAL_AGREEMENT = "rental_agreement"
PASSPORT_REGISTRATION = "passport_registration"
TEMPORARY_REGISTRATION = "temporary_registration"
PHONE_NUMBER = "phone_number"
EMAIL = "email"

View File

@@ -0,0 +1,23 @@
from enum import Enum
class InlineQueryResultType(str, Enum):
"""
Type of inline query result
Source: https://core.telegram.org/bots/api#inlinequeryresult
"""
AUDIO = "audio"
DOCUMENT = "document"
GIF = "gif"
MPEG4_GIF = "mpeg4_gif"
PHOTO = "photo"
STICKER = "sticker"
VIDEO = "video"
VOICE = "voice"
ARTICLE = "article"
CONTACT = "contact"
GAME = "game"
LOCATION = "location"
VENUE = "venue"

View File

@@ -0,0 +1,15 @@
from enum import Enum
class InputMediaType(str, Enum):
"""
This object represents input media type
Source: https://core.telegram.org/bots/api#inputmedia
"""
ANIMATION = "animation"
AUDIO = "audio"
DOCUMENT = "document"
PHOTO = "photo"
VIDEO = "video"

View File

@@ -0,0 +1,12 @@
from enum import Enum
class InputPaidMediaType(str, Enum):
"""
This object represents the type of a media in a paid message.
Source: https://core.telegram.org/bots/api#inputpaidmedia
"""
PHOTO = "photo"
VIDEO = "video"

View File

@@ -0,0 +1,12 @@
from enum import Enum
class KeyboardButtonPollTypeType(str, Enum):
"""
This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed.
Source: https://core.telegram.org/bots/api#keyboardbuttonpolltype
"""
QUIZ = "quiz"
REGULAR = "regular"

View File

@@ -0,0 +1,14 @@
from enum import Enum
class MaskPositionPoint(str, Enum):
"""
The part of the face relative to which the mask should be placed.
Source: https://core.telegram.org/bots/api#maskposition
"""
FOREHEAD = "forehead"
EYES = "eyes"
MOUTH = "mouth"
CHIN = "chin"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class MenuButtonType(str, Enum):
"""
This object represents an type of Menu button
Source: https://core.telegram.org/bots/api#menubuttondefault
"""
DEFAULT = "default"
COMMANDS = "commands"
WEB_APP = "web_app"

View File

@@ -0,0 +1,29 @@
from enum import Enum
class MessageEntityType(str, Enum):
"""
This object represents type of message entity
Source: https://core.telegram.org/bots/api#messageentity
"""
MENTION = "mention"
HASHTAG = "hashtag"
CASHTAG = "cashtag"
BOT_COMMAND = "bot_command"
URL = "url"
EMAIL = "email"
PHONE_NUMBER = "phone_number"
BOLD = "bold"
ITALIC = "italic"
UNDERLINE = "underline"
STRIKETHROUGH = "strikethrough"
SPOILER = "spoiler"
BLOCKQUOTE = "blockquote"
EXPANDABLE_BLOCKQUOTE = "expandable_blockquote"
CODE = "code"
PRE = "pre"
TEXT_LINK = "text_link"
TEXT_MENTION = "text_mention"
CUSTOM_EMOJI = "custom_emoji"

View File

@@ -0,0 +1,14 @@
from enum import Enum
class MessageOriginType(str, Enum):
"""
This object represents origin of a message.
Source: https://core.telegram.org/bots/api#messageorigin
"""
USER = "user"
HIDDEN_USER = "hidden_user"
CHAT = "chat"
CHANNEL = "channel"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class PaidMediaType(str, Enum):
"""
This object represents the type of a media in a paid message.
Source: https://core.telegram.org/bots/api#paidmedia
"""
PHOTO = "photo"
PREVIEW = "preview"
VIDEO = "video"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class ParseMode(str, Enum):
"""
Formatting options
Source: https://core.telegram.org/bots/api#formatting-options
"""
MARKDOWN_V2 = "MarkdownV2"
MARKDOWN = "Markdown"
HTML = "HTML"

View File

@@ -0,0 +1,19 @@
from enum import Enum
class PassportElementErrorType(str, Enum):
"""
This object represents a passport element error type.
Source: https://core.telegram.org/bots/api#passportelementerror
"""
DATA = "data"
FRONT_SIDE = "front_side"
REVERSE_SIDE = "reverse_side"
SELFIE = "selfie"
FILE = "file"
FILES = "files"
TRANSLATION_FILE = "translation_file"
TRANSLATION_FILES = "translation_files"
UNSPECIFIED = "unspecified"

View File

@@ -0,0 +1,12 @@
from enum import Enum
class PollType(str, Enum):
"""
This object represents poll type
Source: https://core.telegram.org/bots/api#poll
"""
REGULAR = "regular"
QUIZ = "quiz"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class ReactionTypeType(str, Enum):
"""
This object represents reaction type.
Source: https://core.telegram.org/bots/api#reactiontype
"""
EMOJI = "emoji"
CUSTOM_EMOJI = "custom_emoji"
PAID = "paid"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class RevenueWithdrawalStateType(str, Enum):
"""
This object represents a revenue withdrawal state type
Source: https://core.telegram.org/bots/api#revenuewithdrawalstate
"""
FAILED = "failed"
PENDING = "pending"
SUCCEEDED = "succeeded"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class StickerFormat(str, Enum):
"""
Format of the sticker
Source: https://core.telegram.org/bots/api#createnewstickerset
"""
STATIC = "static"
ANIMATED = "animated"
VIDEO = "video"

View File

@@ -0,0 +1,13 @@
from enum import Enum
class StickerType(str, Enum):
"""
The part of the face relative to which the mask should be placed.
Source: https://core.telegram.org/bots/api#maskposition
"""
REGULAR = "regular"
MASK = "mask"
CUSTOM_EMOJI = "custom_emoji"

View File

@@ -0,0 +1,16 @@
from enum import Enum
class TopicIconColor(int, Enum):
"""
Color of the topic icon in RGB format.
Source: https://github.com/telegramdesktop/tdesktop/blob/991fe491c5ae62705d77aa8fdd44a79caf639c45/Telegram/SourceFiles/data/data_forum_topic.cpp#L51-L56
"""
BLUE = 0x6FB9F0
YELLOW = 0xFFD67E
VIOLET = 0xCB86DB
GREEN = 0x8EEE98
ROSE = 0xFF93B2
RED = 0xFB6F5F

View File

@@ -0,0 +1,16 @@
from enum import Enum
class TransactionPartnerType(str, Enum):
"""
This object represents a type of transaction partner.
Source: https://core.telegram.org/bots/api#transactionpartner
"""
FRAGMENT = "fragment"
OTHER = "other"
USER = "user"
TELEGRAM_ADS = "telegram_ads"
TELEGRAM_API = "telegram_api"
AFFILIATE_PROGRAM = "affiliate_program"

View File

@@ -0,0 +1,33 @@
from enum import Enum
class UpdateType(str, Enum):
"""
This object represents the complete list of allowed update types
Source: https://core.telegram.org/bots/api#update
"""
MESSAGE = "message"
EDITED_MESSAGE = "edited_message"
CHANNEL_POST = "channel_post"
EDITED_CHANNEL_POST = "edited_channel_post"
BUSINESS_CONNECTION = "business_connection"
BUSINESS_MESSAGE = "business_message"
EDITED_BUSINESS_MESSAGE = "edited_business_message"
DELETED_BUSINESS_MESSAGES = "deleted_business_messages"
MESSAGE_REACTION = "message_reaction"
MESSAGE_REACTION_COUNT = "message_reaction_count"
INLINE_QUERY = "inline_query"
CHOSEN_INLINE_RESULT = "chosen_inline_result"
CALLBACK_QUERY = "callback_query"
SHIPPING_QUERY = "shipping_query"
PRE_CHECKOUT_QUERY = "pre_checkout_query"
PURCHASED_PAID_MEDIA = "purchased_paid_media"
POLL = "poll"
POLL_ANSWER = "poll_answer"
MY_CHAT_MEMBER = "my_chat_member"
CHAT_MEMBER = "chat_member"
CHAT_JOIN_REQUEST = "chat_join_request"
CHAT_BOOST = "chat_boost"
REMOVED_CHAT_BOOST = "removed_chat_boost"

View File

@@ -0,0 +1,199 @@
from typing import Any, Optional
from aiogram.methods import TelegramMethod
from aiogram.methods.base import TelegramType
from aiogram.utils.link import docs_url
class AiogramError(Exception):
"""
Base exception for all aiogram errors.
"""
class DetailedAiogramError(AiogramError):
"""
Base exception for all aiogram errors with detailed message.
"""
url: Optional[str] = None
def __init__(self, message: str) -> None:
self.message = message
def __str__(self) -> str:
message = self.message
if self.url:
message += f"\n(background on this error at: {self.url})"
return message
def __repr__(self) -> str:
return f"{type(self).__name__}('{self}')"
class CallbackAnswerException(AiogramError):
"""
Exception for callback answer.
"""
class SceneException(AiogramError):
"""
Exception for scenes.
"""
class UnsupportedKeywordArgument(DetailedAiogramError):
"""
Exception raised when a keyword argument is passed as filter.
"""
url = docs_url("migration_2_to_3.html", fragment_="filtering-events")
class TelegramAPIError(DetailedAiogramError):
"""
Base exception for all Telegram API errors.
"""
label: str = "Telegram server says"
def __init__(
self,
method: TelegramMethod[TelegramType],
message: str,
) -> None:
super().__init__(message=message)
self.method = method
def __str__(self) -> str:
original_message = super().__str__()
return f"{self.label} - {original_message}"
class TelegramNetworkError(TelegramAPIError):
"""
Base exception for all Telegram network errors.
"""
label = "HTTP Client says"
class TelegramRetryAfter(TelegramAPIError):
"""
Exception raised when flood control exceeds.
"""
url = "https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this"
def __init__(
self,
method: TelegramMethod[TelegramType],
message: str,
retry_after: int,
) -> None:
description = f"Flood control exceeded on method {type(method).__name__!r}"
if chat_id := getattr(method, "chat_id", None):
description += f" in chat {chat_id}"
description += f". Retry in {retry_after} seconds."
description += f"\nOriginal description: {message}"
super().__init__(method=method, message=description)
self.retry_after = retry_after
class TelegramMigrateToChat(TelegramAPIError):
"""
Exception raised when chat has been migrated to a supergroup.
"""
url = "https://core.telegram.org/bots/api#responseparameters"
def __init__(
self,
method: TelegramMethod[TelegramType],
message: str,
migrate_to_chat_id: int,
) -> None:
description = f"The group has been migrated to a supergroup with id {migrate_to_chat_id}"
if chat_id := getattr(method, "chat_id", None):
description += f" from {chat_id}"
description += f"\nOriginal description: {message}"
super().__init__(method=method, message=message)
self.migrate_to_chat_id = migrate_to_chat_id
class TelegramBadRequest(TelegramAPIError):
"""
Exception raised when request is malformed.
"""
class TelegramNotFound(TelegramAPIError):
"""
Exception raised when chat, message, user, etc. not found.
"""
class TelegramConflictError(TelegramAPIError):
"""
Exception raised when bot token is already used by another application in polling mode.
"""
class TelegramUnauthorizedError(TelegramAPIError):
"""
Exception raised when bot token is invalid.
"""
class TelegramForbiddenError(TelegramAPIError):
"""
Exception raised when bot is kicked from chat or etc.
"""
class TelegramServerError(TelegramAPIError):
"""
Exception raised when Telegram server returns 5xx error.
"""
class RestartingTelegram(TelegramServerError):
"""
Exception raised when Telegram server is restarting.
It seems like this error is not used by Telegram anymore,
but it's still here for backward compatibility.
Currently, you should expect that Telegram can raise RetryAfter (with timeout 5 seconds)
error instead of this one.
"""
class TelegramEntityTooLarge(TelegramNetworkError):
"""
Exception raised when you are trying to send a file that is too large.
"""
url = "https://core.telegram.org/bots/api#sending-files"
class ClientDecodeError(AiogramError):
"""
Exception raised when client can't decode response. (Malformed response, etc.)
"""
def __init__(self, message: str, original: Exception, data: Any) -> None:
self.message = message
self.original = original
self.data = data
def __str__(self) -> str:
original_type = type(self.original)
return (
f"{self.message}\n"
f"Caused from error: "
f"{original_type.__module__}.{original_type.__name__}: {self.original}\n"
f"Content: {self.data}"
)

View File

@@ -0,0 +1,51 @@
from .base import Filter
from .chat_member_updated import (
ADMINISTRATOR,
CREATOR,
IS_ADMIN,
IS_MEMBER,
IS_NOT_MEMBER,
JOIN_TRANSITION,
KICKED,
LEAVE_TRANSITION,
LEFT,
MEMBER,
PROMOTED_TRANSITION,
RESTRICTED,
ChatMemberUpdatedFilter,
)
from .command import Command, CommandObject, CommandStart
from .exception import ExceptionMessageFilter, ExceptionTypeFilter
from .logic import and_f, invert_f, or_f
from .magic_data import MagicData
from .state import StateFilter
BaseFilter = Filter
__all__ = (
"Filter",
"BaseFilter",
"Command",
"CommandObject",
"CommandStart",
"ExceptionMessageFilter",
"ExceptionTypeFilter",
"StateFilter",
"MagicData",
"ChatMemberUpdatedFilter",
"CREATOR",
"ADMINISTRATOR",
"MEMBER",
"RESTRICTED",
"LEFT",
"KICKED",
"IS_MEMBER",
"IS_ADMIN",
"PROMOTED_TRANSITION",
"IS_NOT_MEMBER",
"JOIN_TRANSITION",
"LEAVE_TRANSITION",
"and_f",
"or_f",
"invert_f",
)

View File

@@ -0,0 +1,55 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Union
if TYPE_CHECKING:
from aiogram.filters.logic import _InvertFilter
class Filter(ABC):
"""
If you want to register own filters like builtin filters you will need to write subclass
of this class with overriding the :code:`__call__`
method and adding filter attributes.
"""
if TYPE_CHECKING:
# This checking type-hint is needed because mypy checks validity of overrides and raises:
# error: Signature of "__call__" incompatible with supertype "BaseFilter" [override]
# https://mypy.readthedocs.io/en/latest/error_code_list.html#check-validity-of-overrides-override
__call__: Callable[..., Awaitable[Union[bool, Dict[str, Any]]]]
else: # pragma: no cover
@abstractmethod
async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
"""
This method should be overridden.
Accepts incoming event and should return boolean or dict.
:return: :class:`bool` or :class:`Dict[str, Any]`
"""
pass
def __invert__(self) -> "_InvertFilter":
from aiogram.filters.logic import invert_f
return invert_f(self)
def update_handler_flags(self, flags: Dict[str, Any]) -> None:
"""
Also if you want to extend handler flags with using this filter
you should implement this method
:param flags: existing flags, can be updated directly
"""
pass
def _signature_to_string(self, *args: Any, **kwargs: Any) -> str:
items = [repr(arg) for arg in args]
items.extend([f"{k}={v!r}" for k, v in kwargs.items() if v is not None])
return f"{type(self).__name__}({', '.join(items)})"
def __await__(self): # type: ignore # pragma: no cover
# Is needed only for inspection and this method is never be called
return self.__call__

View File

@@ -0,0 +1,207 @@
from __future__ import annotations
import sys
import types
import typing
from decimal import Decimal
from enum import Enum
from fractions import Fraction
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
Dict,
Literal,
Optional,
Type,
TypeVar,
Union,
)
from uuid import UUID
from magic_filter import MagicFilter
from pydantic import BaseModel
from pydantic.fields import FieldInfo
from aiogram.filters.base import Filter
from aiogram.types import CallbackQuery
T = TypeVar("T", bound="CallbackData")
MAX_CALLBACK_LENGTH: int = 64
_UNION_TYPES = {typing.Union}
if sys.version_info >= (3, 10): # pragma: no cover
_UNION_TYPES.add(types.UnionType)
class CallbackDataException(Exception):
pass
class CallbackData(BaseModel):
"""
Base class for callback data wrapper
This class should be used as super-class of user-defined callbacks.
The class-keyword :code:`prefix` is required to define prefix
and also the argument :code:`sep` can be passed to define separator (default is :code:`:`).
"""
if TYPE_CHECKING:
__separator__: ClassVar[str]
"""Data separator (default is :code:`:`)"""
__prefix__: ClassVar[str]
"""Callback prefix"""
def __init_subclass__(cls, **kwargs: Any) -> None:
if "prefix" not in kwargs:
raise ValueError(
f"prefix required, usage example: "
f"`class {cls.__name__}(CallbackData, prefix='my_callback'): ...`"
)
cls.__separator__ = kwargs.pop("sep", ":")
cls.__prefix__ = kwargs.pop("prefix")
if cls.__separator__ in cls.__prefix__:
raise ValueError(
f"Separator symbol {cls.__separator__!r} can not be used "
f"inside prefix {cls.__prefix__!r}"
)
super().__init_subclass__(**kwargs)
def _encode_value(self, key: str, value: Any) -> str:
if value is None:
return ""
if isinstance(value, Enum):
return str(value.value)
if isinstance(value, UUID):
return value.hex
if isinstance(value, bool):
return str(int(value))
if isinstance(value, (int, str, float, Decimal, Fraction)):
return str(value)
raise ValueError(
f"Attribute {key}={value!r} of type {type(value).__name__!r}"
f" can not be packed to callback data"
)
def pack(self) -> str:
"""
Generate callback data string
:return: valid callback data for Telegram Bot API
"""
result = [self.__prefix__]
for key, value in self.model_dump(mode="python").items():
encoded = self._encode_value(key, value)
if self.__separator__ in encoded:
raise ValueError(
f"Separator symbol {self.__separator__!r} can not be used "
f"in value {key}={encoded!r}"
)
result.append(encoded)
callback_data = self.__separator__.join(result)
if len(callback_data.encode()) > MAX_CALLBACK_LENGTH:
raise ValueError(
f"Resulted callback data is too long! "
f"len({callback_data!r}.encode()) > {MAX_CALLBACK_LENGTH}"
)
return callback_data
@classmethod
def unpack(cls: Type[T], value: str) -> T:
"""
Parse callback data string
:param value: value from Telegram
:return: instance of CallbackData
"""
prefix, *parts = value.split(cls.__separator__)
names = cls.model_fields.keys()
if len(parts) != len(names):
raise TypeError(
f"Callback data {cls.__name__!r} takes {len(names)} arguments "
f"but {len(parts)} were given"
)
if prefix != cls.__prefix__:
raise ValueError(f"Bad prefix ({prefix!r} != {cls.__prefix__!r})")
payload = {}
for k, v in zip(names, parts): # type: str, Optional[str]
if field := cls.model_fields.get(k):
if v == "" and _check_field_is_nullable(field):
v = None
payload[k] = v
return cls(**payload)
@classmethod
def filter(cls, rule: Optional[MagicFilter] = None) -> CallbackQueryFilter:
"""
Generates a filter for callback query with rule
:param rule: magic rule
:return: instance of filter
"""
return CallbackQueryFilter(callback_data=cls, rule=rule)
class CallbackQueryFilter(Filter):
"""
This filter helps to handle callback query.
Should not be used directly, you should create the instance of this filter
via callback data instance
"""
__slots__ = (
"callback_data",
"rule",
)
def __init__(
self,
*,
callback_data: Type[CallbackData],
rule: Optional[MagicFilter] = None,
):
"""
:param callback_data: Expected type of callback data
:param rule: Magic rule
"""
self.callback_data = callback_data
self.rule = rule
def __str__(self) -> str:
return self._signature_to_string(
callback_data=self.callback_data,
rule=self.rule,
)
async def __call__(self, query: CallbackQuery) -> Union[Literal[False], Dict[str, Any]]:
if not isinstance(query, CallbackQuery) or not query.data:
return False
try:
callback_data = self.callback_data.unpack(query.data)
except (TypeError, ValueError):
return False
if self.rule is None or self.rule.resolve(callback_data):
return {"callback_data": callback_data}
return False
def _check_field_is_nullable(field: FieldInfo) -> bool:
"""
Check if the given field is nullable.
:param field: The FieldInfo object representing the field to check.
:return: True if the field is nullable, False otherwise.
"""
if not field.is_required():
return True
return typing.get_origin(field.annotation) in _UNION_TYPES and type(None) in typing.get_args(
field.annotation
)

View File

@@ -0,0 +1,204 @@
from typing import Any, Dict, Optional, TypeVar, Union
from aiogram.filters.base import Filter
from aiogram.types import ChatMember, ChatMemberUpdated
MarkerT = TypeVar("MarkerT", bound="_MemberStatusMarker")
MarkerGroupT = TypeVar("MarkerGroupT", bound="_MemberStatusGroupMarker")
TransitionT = TypeVar("TransitionT", bound="_MemberStatusTransition")
class _MemberStatusMarker:
__slots__ = (
"name",
"is_member",
)
def __init__(self, name: str, *, is_member: Optional[bool] = None) -> None:
self.name = name
self.is_member = is_member
def __str__(self) -> str:
result = self.name.upper()
if self.is_member is not None:
result = ("+" if self.is_member else "-") + result
return result # noqa: RET504
def __pos__(self: MarkerT) -> MarkerT:
return type(self)(name=self.name, is_member=True)
def __neg__(self: MarkerT) -> MarkerT:
return type(self)(name=self.name, is_member=False)
def __or__(
self, other: Union["_MemberStatusMarker", "_MemberStatusGroupMarker"]
) -> "_MemberStatusGroupMarker":
if isinstance(other, _MemberStatusMarker):
return _MemberStatusGroupMarker(self, other)
if isinstance(other, _MemberStatusGroupMarker):
return other | self
raise TypeError(
f"unsupported operand type(s) for |: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
__ror__ = __or__
def __rshift__(
self, other: Union["_MemberStatusMarker", "_MemberStatusGroupMarker"]
) -> "_MemberStatusTransition":
old = _MemberStatusGroupMarker(self)
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=old, new=_MemberStatusGroupMarker(other))
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=old, new=other)
raise TypeError(
f"unsupported operand type(s) for >>: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __lshift__(
self, other: Union["_MemberStatusMarker", "_MemberStatusGroupMarker"]
) -> "_MemberStatusTransition":
new = _MemberStatusGroupMarker(self)
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=_MemberStatusGroupMarker(other), new=new)
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=other, new=new)
raise TypeError(
f"unsupported operand type(s) for <<: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __hash__(self) -> int:
return hash((self.name, self.is_member))
def check(self, *, member: ChatMember) -> bool:
# Not all member types have `is_member` attribute
is_member = getattr(member, "is_member", None)
status = getattr(member, "status", None)
if self.is_member is not None and is_member != self.is_member:
return False
return self.name == status
class _MemberStatusGroupMarker:
__slots__ = ("statuses",)
def __init__(self, *statuses: _MemberStatusMarker) -> None:
if not statuses:
raise ValueError("Member status group should have at least one status included")
self.statuses = frozenset(statuses)
def __or__(
self: MarkerGroupT, other: Union["_MemberStatusMarker", "_MemberStatusGroupMarker"]
) -> MarkerGroupT:
if isinstance(other, _MemberStatusMarker):
return type(self)(*self.statuses, other)
if isinstance(other, _MemberStatusGroupMarker):
return type(self)(*self.statuses, *other.statuses)
raise TypeError(
f"unsupported operand type(s) for |: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __rshift__(
self, other: Union["_MemberStatusMarker", "_MemberStatusGroupMarker"]
) -> "_MemberStatusTransition":
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=self, new=_MemberStatusGroupMarker(other))
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=self, new=other)
raise TypeError(
f"unsupported operand type(s) for >>: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __lshift__(
self, other: Union["_MemberStatusMarker", "_MemberStatusGroupMarker"]
) -> "_MemberStatusTransition":
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=_MemberStatusGroupMarker(other), new=self)
if isinstance(other, _MemberStatusGroupMarker):
return _MemberStatusTransition(old=other, new=self)
raise TypeError(
f"unsupported operand type(s) for <<: "
f"{type(self).__name__!r} and {type(other).__name__!r}"
)
def __str__(self) -> str:
result = " | ".join(map(str, sorted(self.statuses, key=str)))
if len(self.statuses) != 1:
return f"({result})"
return result
def check(self, *, member: ChatMember) -> bool:
return any(status.check(member=member) for status in self.statuses)
class _MemberStatusTransition:
__slots__ = (
"old",
"new",
)
def __init__(self, *, old: _MemberStatusGroupMarker, new: _MemberStatusGroupMarker) -> None:
self.old = old
self.new = new
def __str__(self) -> str:
return f"{self.old} >> {self.new}"
def __invert__(self: TransitionT) -> TransitionT:
return type(self)(old=self.new, new=self.old)
def check(self, *, old: ChatMember, new: ChatMember) -> bool:
return self.old.check(member=old) and self.new.check(member=new)
CREATOR = _MemberStatusMarker("creator")
ADMINISTRATOR = _MemberStatusMarker("administrator")
MEMBER = _MemberStatusMarker("member")
RESTRICTED = _MemberStatusMarker("restricted")
LEFT = _MemberStatusMarker("left")
KICKED = _MemberStatusMarker("kicked")
IS_MEMBER = CREATOR | ADMINISTRATOR | MEMBER | +RESTRICTED
IS_ADMIN = CREATOR | ADMINISTRATOR
IS_NOT_MEMBER = LEFT | KICKED | -RESTRICTED
JOIN_TRANSITION = IS_NOT_MEMBER >> IS_MEMBER
LEAVE_TRANSITION = ~JOIN_TRANSITION
PROMOTED_TRANSITION = (MEMBER | RESTRICTED | LEFT | KICKED) >> ADMINISTRATOR
class ChatMemberUpdatedFilter(Filter):
__slots__ = ("member_status_changed",)
def __init__(
self,
member_status_changed: Union[
_MemberStatusMarker,
_MemberStatusGroupMarker,
_MemberStatusTransition,
],
):
self.member_status_changed = member_status_changed
def __str__(self) -> str:
return self._signature_to_string(
member_status_changed=self.member_status_changed,
)
async def __call__(self, member_updated: ChatMemberUpdated) -> Union[bool, Dict[str, Any]]:
old = member_updated.old_chat_member
new = member_updated.new_chat_member
rule = self.member_status_changed
if isinstance(rule, (_MemberStatusMarker, _MemberStatusGroupMarker)):
return rule.check(member=new)
if isinstance(rule, _MemberStatusTransition):
return rule.check(old=old, new=new)
# Impossible variant in due to pydantic validation
return False # pragma: no cover

View File

@@ -0,0 +1,298 @@
from __future__ import annotations
import re
from dataclasses import dataclass, field, replace
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
Match,
Optional,
Pattern,
Sequence,
Union,
cast,
)
from magic_filter import MagicFilter
from aiogram.filters.base import Filter
from aiogram.types import BotCommand, Message
from aiogram.utils.deep_linking import decode_payload
if TYPE_CHECKING:
from aiogram import Bot
# TODO: rm type ignore after py3.8 support expiration or mypy bug fix
CommandPatternType = Union[str, re.Pattern, BotCommand] # type: ignore[type-arg]
class CommandException(Exception):
pass
class Command(Filter):
"""
This filter can be helpful for handling commands from the text messages.
Works only with :class:`aiogram.types.message.Message` events which have the :code:`text`.
"""
__slots__ = (
"commands",
"prefix",
"ignore_case",
"ignore_mention",
"magic",
)
def __init__(
self,
*values: CommandPatternType,
commands: Optional[Union[Sequence[CommandPatternType], CommandPatternType]] = None,
prefix: str = "/",
ignore_case: bool = False,
ignore_mention: bool = False,
magic: Optional[MagicFilter] = None,
):
"""
List of commands (string or compiled regexp patterns)
:param prefix: Prefix for command.
Prefix is always a single char but here you can pass all of allowed prefixes,
for example: :code:`"/!"` will work with commands prefixed
by :code:`"/"` or :code:`"!"`.
:param ignore_case: Ignore case (Does not work with regexp, use flags instead)
:param ignore_mention: Ignore bot mention. By default,
bot can not handle commands intended for other bots
:param magic: Validate command object via Magic filter after all checks done
"""
if commands is None:
commands = []
if isinstance(commands, (str, re.Pattern, BotCommand)):
commands = [commands]
if not isinstance(commands, Iterable):
raise ValueError(
"Command filter only supports str, re.Pattern, BotCommand object"
" or their Iterable"
)
items = []
for command in (*values, *commands):
if isinstance(command, BotCommand):
command = command.command
if not isinstance(command, (str, re.Pattern)):
raise ValueError(
"Command filter only supports str, re.Pattern, BotCommand object"
" or their Iterable"
)
if ignore_case and isinstance(command, str):
command = command.casefold()
items.append(command)
if not items:
raise ValueError("At least one command should be specified")
self.commands = tuple(items)
self.prefix = prefix
self.ignore_case = ignore_case
self.ignore_mention = ignore_mention
self.magic = magic
def __str__(self) -> str:
return self._signature_to_string(
*self.commands,
prefix=self.prefix,
ignore_case=self.ignore_case,
ignore_mention=self.ignore_mention,
magic=self.magic,
)
def update_handler_flags(self, flags: Dict[str, Any]) -> None:
commands = flags.setdefault("commands", [])
commands.append(self)
async def __call__(self, message: Message, bot: Bot) -> Union[bool, Dict[str, Any]]:
if not isinstance(message, Message):
return False
text = message.text or message.caption
if not text:
return False
try:
command = await self.parse_command(text=text, bot=bot)
except CommandException:
return False
result = {"command": command}
if command.magic_result and isinstance(command.magic_result, dict):
result.update(command.magic_result)
return result
def extract_command(self, text: str) -> CommandObject:
# First step: separate command with arguments
# "/command@mention arg1 arg2" -> "/command@mention", ["arg1 arg2"]
try:
full_command, *args = text.split(maxsplit=1)
except ValueError:
raise CommandException("not enough values to unpack")
# Separate command into valuable parts
# "/command@mention" -> "/", ("command", "@", "mention")
prefix, (command, _, mention) = full_command[0], full_command[1:].partition("@")
return CommandObject(
prefix=prefix,
command=command,
mention=mention or None,
args=args[0] if args else None,
)
def validate_prefix(self, command: CommandObject) -> None:
if command.prefix not in self.prefix:
raise CommandException("Invalid command prefix")
async def validate_mention(self, bot: Bot, command: CommandObject) -> None:
if command.mention and not self.ignore_mention:
me = await bot.me()
if me.username and command.mention.lower() != me.username.lower():
raise CommandException("Mention did not match")
def validate_command(self, command: CommandObject) -> CommandObject:
for allowed_command in cast(Sequence[CommandPatternType], self.commands):
# Command can be presented as regexp pattern or raw string
# then need to validate that in different ways
if isinstance(allowed_command, Pattern): # Regexp
result = allowed_command.match(command.command)
if result:
return replace(command, regexp_match=result)
command_name = command.command
if self.ignore_case:
command_name = command_name.casefold()
if command_name == allowed_command: # String
return command
raise CommandException("Command did not match pattern")
async def parse_command(self, text: str, bot: Bot) -> CommandObject:
"""
Extract command from the text and validate
:param text:
:param bot:
:return:
"""
command = self.extract_command(text)
self.validate_prefix(command=command)
await self.validate_mention(bot=bot, command=command)
command = self.validate_command(command)
command = self.do_magic(command=command)
return command # noqa: RET504
def do_magic(self, command: CommandObject) -> Any:
if self.magic is None:
return command
result = self.magic.resolve(command)
if not result:
raise CommandException("Rejected via magic filter")
return replace(command, magic_result=result)
@dataclass(frozen=True)
class CommandObject:
"""
Instance of this object is always has command and it prefix.
Can be passed as keyword argument **command** to the handler
"""
prefix: str = "/"
"""Command prefix"""
command: str = ""
"""Command without prefix and mention"""
mention: Optional[str] = None
"""Mention (if available)"""
args: Optional[str] = field(repr=False, default=None)
"""Command argument"""
regexp_match: Optional[Match[str]] = field(repr=False, default=None)
"""Will be presented match result if the command is presented as regexp in filter"""
magic_result: Optional[Any] = field(repr=False, default=None)
@property
def mentioned(self) -> bool:
"""
This command has mention?
"""
return bool(self.mention)
@property
def text(self) -> str:
"""
Generate original text from object
"""
line = self.prefix + self.command
if self.mention:
line += "@" + self.mention
if self.args:
line += " " + self.args
return line
class CommandStart(Command):
def __init__(
self,
deep_link: bool = False,
deep_link_encoded: bool = False,
ignore_case: bool = False,
ignore_mention: bool = False,
magic: Optional[MagicFilter] = None,
):
super().__init__(
"start",
prefix="/",
ignore_case=ignore_case,
ignore_mention=ignore_mention,
magic=magic,
)
self.deep_link = deep_link
self.deep_link_encoded = deep_link_encoded
def __str__(self) -> str:
return self._signature_to_string(
ignore_case=self.ignore_case,
ignore_mention=self.ignore_mention,
magic=self.magic,
deep_link=self.deep_link,
deep_link_encoded=self.deep_link_encoded,
)
async def parse_command(self, text: str, bot: Bot) -> CommandObject:
"""
Extract command from the text and validate
:param text:
:param bot:
:return:
"""
command = self.extract_command(text)
self.validate_prefix(command=command)
await self.validate_mention(bot=bot, command=command)
command = self.validate_command(command)
command = self.validate_deeplink(command=command)
command = self.do_magic(command=command)
return command # noqa: RET504
def validate_deeplink(self, command: CommandObject) -> CommandObject:
if not self.deep_link:
return command
if not command.args:
raise CommandException("Deep-link was missing")
args = command.args
if self.deep_link_encoded:
try:
args = decode_payload(args)
except UnicodeDecodeError as e:
raise CommandException(f"Failed to decode Base64: {e}")
return replace(command, args=args)
return command

View File

@@ -0,0 +1,55 @@
import re
from typing import Any, Dict, Pattern, Type, Union, cast
from aiogram.filters.base import Filter
from aiogram.types import TelegramObject
from aiogram.types.error_event import ErrorEvent
class ExceptionTypeFilter(Filter):
"""
Allows to match exception by type
"""
__slots__ = ("exceptions",)
def __init__(self, *exceptions: Type[Exception]):
"""
:param exceptions: Exception type(s)
"""
if not exceptions:
raise ValueError("At least one exception type is required")
self.exceptions = exceptions
async def __call__(self, obj: TelegramObject) -> Union[bool, Dict[str, Any]]:
return isinstance(cast(ErrorEvent, obj).exception, self.exceptions)
class ExceptionMessageFilter(Filter):
"""
Allow to match exception by message
"""
__slots__ = ("pattern",)
def __init__(self, pattern: Union[str, Pattern[str]]):
"""
:param pattern: Regexp pattern
"""
if isinstance(pattern, str):
pattern = re.compile(pattern)
self.pattern = pattern
def __str__(self) -> str:
return self._signature_to_string(
pattern=self.pattern,
)
async def __call__(
self,
obj: TelegramObject,
) -> Union[bool, Dict[str, Any]]:
result = self.pattern.match(str(cast(ErrorEvent, obj).exception))
if not result:
return False
return {"match_exception": result}

View File

@@ -0,0 +1,77 @@
from abc import ABC
from typing import TYPE_CHECKING, Any, Dict, Union
from aiogram.filters import Filter
if TYPE_CHECKING:
from aiogram.dispatcher.event.handler import CallbackType, FilterObject
class _LogicFilter(Filter, ABC):
pass
class _InvertFilter(_LogicFilter):
__slots__ = ("target",)
def __init__(self, target: "FilterObject") -> None:
self.target = target
async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
return not bool(await self.target.call(*args, **kwargs))
class _AndFilter(_LogicFilter):
__slots__ = ("targets",)
def __init__(self, *targets: "FilterObject") -> None:
self.targets = targets
async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
final_result = {}
for target in self.targets:
result = await target.call(*args, **kwargs)
if not result:
return False
if isinstance(result, dict):
final_result.update(result)
if final_result:
return final_result
return True
class _OrFilter(_LogicFilter):
__slots__ = ("targets",)
def __init__(self, *targets: "FilterObject") -> None:
self.targets = targets
async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
for target in self.targets:
result = await target.call(*args, **kwargs)
if not result:
continue
if isinstance(result, dict):
return result
return bool(result)
return False
def and_f(*targets: "CallbackType") -> _AndFilter:
from aiogram.dispatcher.event.handler import FilterObject
return _AndFilter(*(FilterObject(target) for target in targets))
def or_f(*targets: "CallbackType") -> _OrFilter:
from aiogram.dispatcher.event.handler import FilterObject
return _OrFilter(*(FilterObject(target) for target in targets))
def invert_f(target: "CallbackType") -> _InvertFilter:
from aiogram.dispatcher.event.handler import FilterObject
return _InvertFilter(FilterObject(target))

Some files were not shown because too many files have changed in this diff Show More