Initial commit

This commit is contained in:
Kristofers Solo 2021-09-08 21:48:25 +03:00
parent 16db763d1b
commit aea05cae02
2041 changed files with 191870 additions and 33 deletions

View File

@ -0,0 +1,29 @@
Copyright (c) 2013-2021 by the Babel Team, see AUTHORS for more information.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The name of the author may not be used to endorse or promote
products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,31 @@
Metadata-Version: 2.1
Name: Babel
Version: 2.9.1
Summary: Internationalization utilities
Home-page: http://babel.pocoo.org/
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
License: BSD
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
Requires-Dist: pytz (>=2015.7)
A collection of tools for internationalizing Python applications.

View File

@ -0,0 +1,847 @@
../../Scripts/pybabel.exe,sha256=F7jkcNvcBFObHlskx1hp8AY7Fx3ngaOiqy-46lYXw8Y,106370
Babel-2.9.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
Babel-2.9.1.dist-info/LICENSE,sha256=KMl78z51BuJ3SHvao6abcPFw1q9agnhawKdMhCgELkA,1451
Babel-2.9.1.dist-info/METADATA,sha256=mlkWT3NrQ45RhCJaK7b0eaREy0YsZfn0c4qGbvW8PWw,1223
Babel-2.9.1.dist-info/RECORD,,
Babel-2.9.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110
Babel-2.9.1.dist-info/entry_points.txt,sha256=dyIkorJhQj3IvTvmMylr1wEzW7vfxTw5RTOWa8zoqh0,764
Babel-2.9.1.dist-info/top_level.txt,sha256=mQO3vNkqlcYs_xRaL5EpRIy1IRjMp4N9_vdwmiemPXo,6
babel/__init__.py,sha256=2isYTKcvIoYWhBj1H7DhXFGIVauRTrA5dbeC0I0px8k,714
babel/__pycache__/__init__.cpython-39.pyc,,
babel/__pycache__/_compat.cpython-39.pyc,,
babel/__pycache__/core.cpython-39.pyc,,
babel/__pycache__/dates.cpython-39.pyc,,
babel/__pycache__/languages.cpython-39.pyc,,
babel/__pycache__/lists.cpython-39.pyc,,
babel/__pycache__/localedata.cpython-39.pyc,,
babel/__pycache__/numbers.cpython-39.pyc,,
babel/__pycache__/plural.cpython-39.pyc,,
babel/__pycache__/support.cpython-39.pyc,,
babel/__pycache__/units.cpython-39.pyc,,
babel/__pycache__/util.cpython-39.pyc,,
babel/_compat.py,sha256=DHx6vQR-LazZlNdeBE7wGTOBv1_1HWbRboKOkX76TiY,1685
babel/core.py,sha256=5CmHhtC4GV_Nrd6oq63apsEO2nb3ZPdLsA3MYd9lzBI,36907
babel/dates.py,sha256=cjONUhc89AMhtSpNk8jrtCGrtqwrPuS6rsXOzHZROpM,67709
babel/global.dat,sha256=P2lM1DjyEx9SMpmXlgu4zNeodlF2Qp2hg9yEANLM3ps,254421
babel/languages.py,sha256=UmLTj4Nai3kQrwHX6jptehVLeAw-KAdxcmcp2iDlgvI,2743
babel/lists.py,sha256=yks1P7CNrIoicIptChdpwtDHN9eKVK1sfeDzhLgPVpM,2719
babel/locale-data/af.dat,sha256=jjfQ33ml9BDllSSkfwcIQzzgohE645gzeehkj1KwTMw,171143
babel/locale-data/af_NA.dat,sha256=6A8JdTF_O-lRxJZmjI6Lig4Se2aFbqWhdw34Ak4-67g,1407
babel/locale-data/af_ZA.dat,sha256=ZaMuNgUHOKLle56lUxFOGAAk8QM-_41nuazbqNu5kgg,608
babel/locale-data/agq.dat,sha256=gSE4vg5l5Yze-10YkHjiBaLVqC5fp0xgSr3nMuZB8lw,17339
babel/locale-data/agq_CM.dat,sha256=AxBMSZitoHhaa6djMmFbp6iEPM8OUC04G77ZldkuHR8,609
babel/locale-data/ak.dat,sha256=fk1o-LXIVWd8kObwIG98ltM0H9W68R1_alFWFl8U_2o,15859
babel/locale-data/ak_GH.dat,sha256=tuOpoiGUJqZ_Dlr8xlp9oIqsFL-YQrBQpDUfG5z3YQk,589
babel/locale-data/am.dat,sha256=qe-hJ0p-GstMzzmrdamvejKBIVwLBuJRPyfdEdUQLB0,200581
babel/locale-data/am_ET.dat,sha256=JHNCRHUTq_8VPokwz2uGwCZPGxUR23nJdDIzi_HFIbA,608
babel/locale-data/ar.dat,sha256=aCbEpK5EzE3tG7TDSCPJVF4srTWPDcTF7GIJjr_Z36o,345779
babel/locale-data/ar_001.dat,sha256=Y2HUeGZn8VV7DCPv6nXTWmkqTtzGxZxdvXYYiS5mU-o,1680
babel/locale-data/ar_AE.dat,sha256=UAOwSgM1tC_mFK1Gr8D1t_2HwylT_wv50l7LyDew6BI,1038
babel/locale-data/ar_BH.dat,sha256=f1D15u-5clcZJvrqsnr2XShOLf6hw5hmR587r7hsKEs,651
babel/locale-data/ar_DJ.dat,sha256=NWt7QxMxsj1-zAsJ8aXWGT1SwzKayjlbB8TxvY8P-iA,629
babel/locale-data/ar_DZ.dat,sha256=1KdQGZH5ePC4W3nwrB1F2msNqVVnK0wYwfU-uqYAhXU,1713
babel/locale-data/ar_EG.dat,sha256=EUDxLyjz-znR56wvAE-_IvJX-2ou_ubS3KVU83SHa5Q,688
babel/locale-data/ar_EH.dat,sha256=OseNtW_AOGrIkKmxk2ufJXkpwOXsajJ4uyJAwUidL1g,589
babel/locale-data/ar_ER.dat,sha256=FK8yF8SloHQoOXNpaKlnEMsS9oG0j9dhDhiSwk7euCw,610
babel/locale-data/ar_IL.dat,sha256=TzNjvKnJXxJBRXR55WZ9yVxpHnN-2subVXqjeS7CX4I,1195
babel/locale-data/ar_IQ.dat,sha256=G4JR6ue3O7NNz7Hy2WKKiHg9lqhaHGjZA-UFq8N9TKs,2336
babel/locale-data/ar_JO.dat,sha256=K9WKy9urKZA7k6MIuprjN2Yu-VgySZVzK46jui9NYnY,2335
babel/locale-data/ar_KM.dat,sha256=e0ER6Yw-J2DOL7R8JKuTBpU0_46WK0jLBKpsG56UHgk,1161
babel/locale-data/ar_KW.dat,sha256=hsa41dETEsjs8XPCZkryiCFbPBLrv6aF5ya-ImPAT3g,651
babel/locale-data/ar_LB.dat,sha256=b4DWnwWjHST9MvfKnu6YaGhP2ghxjpZP5TQy7w_OGtU,2336
babel/locale-data/ar_LY.dat,sha256=QyNUUgeaUdsvLQtwRLPQed-PRiDez_ekokUwpxT9jRw,1652
babel/locale-data/ar_MA.dat,sha256=ufYSdBIy96F6zJND6kKZuilh00Lpu-z3txPDPhsgAIk,2007
babel/locale-data/ar_MR.dat,sha256=wjI22LINnrT_HVHqm1ZmJ9Ndb6bFBKvAFkeyvQ8tBi8,2173
babel/locale-data/ar_OM.dat,sha256=gsBDBEK35j4M3ualnSdL6tFD4-u3dSbJX1NtS7znQ_E,651
babel/locale-data/ar_PS.dat,sha256=tTTcOqje-rppgz46-DrkawXtoc9jIQ2vwyA8rRU2uDM,2273
babel/locale-data/ar_QA.dat,sha256=QI6pzsVjeY_nxkk8iKhy_DUzfBJrZFWoL9uYKM3L9CM,651
babel/locale-data/ar_SA.dat,sha256=T6x2_p8wVOfcUx9RZMwa5yM1qxVnDoJCxXPzUcsQXuM,30599
babel/locale-data/ar_SD.dat,sha256=gUbUGVDm-V2a9ViTVska833oAys0b6Au-IJcoMo5leM,651
babel/locale-data/ar_SO.dat,sha256=fSiIj3lwo0FJU0dXbnFUNLPDvV_jejp86mHcLiGtiJ4,608
babel/locale-data/ar_SS.dat,sha256=eAaVq86grUAnWNST_u32IrhbryyjRvUo27UG0js6zS8,631
babel/locale-data/ar_SY.dat,sha256=4ge-CrqdFYfvu3VOdrvd7lRqhGDf2rPfTYkaNBaO4Ks,2335
babel/locale-data/ar_TD.dat,sha256=yVONNY9PuoKVCLjT0w0qp0NLO4kShNT9j9eM_uKtQYY,589
babel/locale-data/ar_TN.dat,sha256=J3SolHTZQ9_7HZGluj2jFu4odczgSZlOYU7spA670XY,1651
babel/locale-data/ar_YE.dat,sha256=tsYZG7eRROrZJyzc_jUbyxbMRT157S3ocGMh7eIiqsI,651
babel/locale-data/as.dat,sha256=sEo9KNUT_mxPE43dnY2qd5x5PS0YptFdgQB33rCj108,234427
babel/locale-data/as_IN.dat,sha256=BfzVFooc90N8ufCwaNAuKzefwYLUen0-o4wUafoLYc0,631
babel/locale-data/asa.dat,sha256=RTNazZw6IqoA6jO4o2v0jUI-x6rKsBHeCEEi9_eWGto,16187
babel/locale-data/asa_TZ.dat,sha256=aSECPcjC7UM3Bb4Cy16Et-RHtNrhlzGM_v4zNMGsreU,590
babel/locale-data/ast.dat,sha256=7s8-hdazQXPAA8cPPc2AymyA1vAI2P6MoHOjtcuDXaM,209617
babel/locale-data/ast_ES.dat,sha256=x4OX34OA3ztvfnETdf49lKg6Gz2q6Lv17Lrf0G4EZ1Y,627
babel/locale-data/az.dat,sha256=2FAR_E0DODOjfyi82cqli4S484eNi8IWcGOJdvaUMXA,194898
babel/locale-data/az_Cyrl.dat,sha256=1AcCgkkW-Oz7hXWJVoUqe1BRwdirpoY32Ub53YIUJDQ,38880
babel/locale-data/az_Cyrl_AZ.dat,sha256=yhmkFuYZLbGzV8Q155t3UrHn-rEqAR9LVmz1sQkKcSI,608
babel/locale-data/az_Latn.dat,sha256=EkZYNfi4vipZ7wH0cvvd1yvqOJxwCNtYADX0SgJMnAE,2225
babel/locale-data/az_Latn_AZ.dat,sha256=yhmkFuYZLbGzV8Q155t3UrHn-rEqAR9LVmz1sQkKcSI,608
babel/locale-data/bas.dat,sha256=SkyEjzAws6O1b_g_mtVKcLFvR_00Z8HK1ubf7X_NRIk,17128
babel/locale-data/bas_CM.dat,sha256=NlquEbS-6vOPdnyIC4r5KRnEbj4Y0oaO56i3IeH2MmI,609
babel/locale-data/be.dat,sha256=R4OLJNVpSSFRz34CGEFpWedQosrBKf227FweNgrhQvU,271438
babel/locale-data/be_BY.dat,sha256=0uCaBRRcbIS46dyiHK85UMi-k3b1b_uspOzBHMos2jM,608
babel/locale-data/bem.dat,sha256=pqGkFsWL-tAPVJfouEiYifYERtShK-WpoABWUzJP0vA,6534
babel/locale-data/bem_ZM.dat,sha256=VbAesm4_2wMn2EmWOW7etCOWlleqpkSAjjMOtKBlEcQ,590
babel/locale-data/bez.dat,sha256=lOnVgx-EVzJ-TL9G6tbvC5qckxtoPwIG9UEQR8RLthY,16977
babel/locale-data/bez_TZ.dat,sha256=PHbB6bmtC5Wn46cFkmE8mjwuSUTr3gCxXkmFzqpiDRQ,590
babel/locale-data/bg.dat,sha256=pUVekrzzgzY-8cecdX7rcvQ9-LNteX4V3yzZJyGu_qM,232833
babel/locale-data/bg_BG.dat,sha256=rHaVmPZT-2n7w37ORQM31-InIsuCBYpv6xWIAc0akkk,626
babel/locale-data/bm.dat,sha256=-E8armVyVSCPOqdRGc55rFLFYJcPPkvMUw5fCzRYAj8,15886
babel/locale-data/bm_ML.dat,sha256=uOp8g5jSZ0dtvQRJ_GmriD3hafnqKpY-D8VhCY0lctk,589
babel/locale-data/bn.dat,sha256=mLx95u-bxnHGpQ3f07-gBC6RYW658gmJzImHukcigK4,263365
babel/locale-data/bn_BD.dat,sha256=J15p67iXhfwwTscvbnmaYH1jxiMf8n7kvXoaYiMDgCI,608
babel/locale-data/bn_IN.dat,sha256=D5aBzoxDMmKe7Bnz25b_MTbqxm3ofuOqA73An-bd0Ic,866
babel/locale-data/bo.dat,sha256=fG5-yWBu0eudG5gbVC5xJQF-C99BVk0zIgiYr-4Q4e4,22525
babel/locale-data/bo_CN.dat,sha256=9gsaOvK_bYpPFDNXZ9lOj3Y-jgDTZlD6JHhlPLJ2Te4,608
babel/locale-data/bo_IN.dat,sha256=EWXLEVA3oaBtJFiwQVxREJbR4fjpyaqQ1P1xVNsMowc,704
babel/locale-data/br.dat,sha256=S6Rh6pNfXOlOKbqW0dpbTc05PfGfvWgfeL3eBZwk4hs,290897
babel/locale-data/br_FR.dat,sha256=ioO-nP2x2c5STVkiH-RvhNxxq6giVfDejh4T-FoSjF8,626
babel/locale-data/brx.dat,sha256=pQJJBfYcX8pxrFlEkfkwwprNf6c6AG3Bkvh-tNpp1YU,124289
babel/locale-data/brx_IN.dat,sha256=9pIIjxmY4jmGi9LDci2mDhdHADN2oD3U53pBIRNNRc4,632
babel/locale-data/bs.dat,sha256=wHmiOLJ8MBJyaM2xpKiuIrIDSn3KHu6kz33rYpzDJjQ,239326
babel/locale-data/bs_Cyrl.dat,sha256=MBDuEafwSU1vWRHHor26JViQTwYsoyR_imtvVlp6N78,213685
babel/locale-data/bs_Cyrl_BA.dat,sha256=49_6kE_iGTwxlkieBZvxsXvVPAUrQ3hlavg2hMkUzFQ,608
babel/locale-data/bs_Latn.dat,sha256=jWzwo3YbhqMRkMWFgGPJ1SoBrHBL5OKB6yqVoJw3cKo,1957
babel/locale-data/bs_Latn_BA.dat,sha256=49_6kE_iGTwxlkieBZvxsXvVPAUrQ3hlavg2hMkUzFQ,608
babel/locale-data/ca.dat,sha256=mWNiuT8Rt5CV1YnXpjqiZP1HlvftCMz-DbaPLLbzEe4,208790
babel/locale-data/ca_AD.dat,sha256=c0uaIsSk6XuXizMkzNYZcMzFcC9ItvvDTh9byxpdYV4,626
babel/locale-data/ca_ES.dat,sha256=xAbDhTv0TIq0M66QrfejjMsbIIthq2CCXMr00ojFzoA,626
babel/locale-data/ca_ES_VALENCIA.dat,sha256=i6YyGQHkRnxTuJLvjemc0tm1Iqqfm0v0dNZnLk_v7R8,3644
babel/locale-data/ca_FR.dat,sha256=bm288L4_PW-Wgb5Ty-9jcQns0i9iVy4zQY8PV3IETHE,645
babel/locale-data/ca_IT.dat,sha256=t21q_Bu_bo2FVP609O53DGAXPbAUGv5yZ-_m71S1bWU,626
babel/locale-data/ccp.dat,sha256=nyA_rk-dZ4U4ATCfcCEQeAj4Df6igjI4XD7HP0j045w,275655
babel/locale-data/ccp_BD.dat,sha256=Lns8llZesFHOnqgmiYntJhMufHDSmzHOL-sYczkVxCs,609
babel/locale-data/ccp_IN.dat,sha256=zRmYBrG8ABv6_4YB7puTs2jsWSBBgqo0yBpvsBVHxZQ,632
babel/locale-data/ce.dat,sha256=XtFwtrhjaJGFgmegHYgoVSqmZs_5o8S85XspljVTzSo,138778
babel/locale-data/ce_RU.dat,sha256=rAfBzB42xX9qOXWNu1kMJ278N99ohijJoWO2XAskSkc,626
babel/locale-data/ceb.dat,sha256=h8hImvVlmbhxs4ufAMKi_C3DExHORd8hrbdd2hQrXx8,103518
babel/locale-data/ceb_PH.dat,sha256=nQM5vO0qo0sARNlhLIXlzxBCevhReUtYPios-RIcw8c,609
babel/locale-data/cgg.dat,sha256=VfxK9EKS6ocduf7D3FKKHIXahll75kOwDuT6jrI6d-o,16228
babel/locale-data/cgg_UG.dat,sha256=87p4kuJQ3R_CLR3yPT-oMPAT9idvdPVElVxpZC5A69s,613
babel/locale-data/chr.dat,sha256=Nk70q12-m-cVNXDVMBrT1YTRtyS9t949i3oDVo6SyKk,200072
babel/locale-data/chr_US.dat,sha256=BS6w-X9I6FuixJ2upUTEdFstYRKU7FCok38SRWahHWc,627
babel/locale-data/ckb.dat,sha256=VfHXHCrZYFQJ0NxHji-px_rJ7gD15bVeVHEQDkjtrIc,41606
babel/locale-data/ckb_IQ.dat,sha256=8qfAOdEjoncef0lQt6zsXiW0Nn9uY4Fb2jQMpgJSxd0,652
babel/locale-data/ckb_IR.dat,sha256=cNWVEpmuwNkAyJm8qdtOho5FEIB-UF2dOHSXoDIdWj0,1204
babel/locale-data/cs.dat,sha256=PzETNpoA6kgHESjeT01YFgtQd31qRnCbrfdWbZ8M7sM,297675
babel/locale-data/cs_CZ.dat,sha256=IG8kxPUf2L53IheXDOJcGUHV5XPaEvrWlhznlScsZAw,626
babel/locale-data/cu.dat,sha256=Ak6g9Gsf7fAe3EsC4ORWtTDTqfGjoHanPHd2VU7uqhY,20265
babel/locale-data/cu_RU.dat,sha256=NHfB25KQf80ELweLH7Qe5OHUIC21BjJZsZvPUZ8wlN8,626
babel/locale-data/cy.dat,sha256=wy7mLRchz59usL93zfp-wZ3iyalLaEO6QHwW7c1L2Pg,315824
babel/locale-data/cy_GB.dat,sha256=ZHRJBfOpeOVn8rfsdEhIF5mY01XFhStFmTVeOmklOAk,626
babel/locale-data/da.dat,sha256=2sCrVwkFfF2RHbLoon79r_JwKXCqZ0oj1dbpe6hBDSY,199867
babel/locale-data/da_DK.dat,sha256=OZkvaI7AQcocAo2rtHtZq3d6-P4mzR0MWWOQ8EJXjSo,626
babel/locale-data/da_GL.dat,sha256=uyqYUJOO4nd3vn92yPsEVm6mYGXWCqSUTG4DtImKc0M,589
babel/locale-data/dav.dat,sha256=nRUgF6cL7_RI2vbdVR407MYHpJ5ALeZtY4lrGcRlVSc,16271
babel/locale-data/dav_KE.dat,sha256=FP78PK3asgspsW7QIhV0jrOHFmDG4iZiGjFBvPQF-6o,609
babel/locale-data/de.dat,sha256=bcoZmi61_dSmFuJN9YA6n_llVQCfbeRtVlkYm8QWpvM,206762
babel/locale-data/de_AT.dat,sha256=-UMZ_o8YNkI3mlluBgFiSXc6ttnqo_adzxsPffafPDw,2563
babel/locale-data/de_BE.dat,sha256=cAHuCiE_b6CoIRItE8FQIpr7fR-gSsF66MtRGhU3xyk,626
babel/locale-data/de_CH.dat,sha256=x8MlguM2lvZtFSB8R-BWNJ2wgy8vjGNRDUPcYO7YGRA,3938
babel/locale-data/de_DE.dat,sha256=uGWbXF2bkaVQcgna0h59yJQJctOj88om0v2NVVCJvPw,626
babel/locale-data/de_IT.dat,sha256=y1GIOM88fIEDhz5M_Seiy5sk47wTF2X5nRspsQJRWXc,1619
babel/locale-data/de_LI.dat,sha256=bggRqr8i9UGeDEA1tqBgRv_lWAGGbb4voVc152sl8js,1321
babel/locale-data/de_LU.dat,sha256=rMtYa8P0yOF9Asg8mMIiBaeFzg1imA6BOFB_1AV0H9g,1065
babel/locale-data/dje.dat,sha256=0lCk_5ABMX3iiY0QcB6N6R1JH1B5db3KFxiu-eS2Umw,16192
babel/locale-data/dje_NE.dat,sha256=YRn5qozp8AlljDhoIVdw1KfDjTwJuQSR1O6zcfh6Z54,590
babel/locale-data/dsb.dat,sha256=GVknAiKWopJw22chqjBGEvoq7GN201Sw64vnh-kqPeA,179644
babel/locale-data/dsb_DE.dat,sha256=UEem7x_iq5-p3Tpzjtja0ONC1qtHvG2MCsQMx8KvTmo,627
babel/locale-data/dua.dat,sha256=GSB_OYiEmeJM7B9QAdgr2IV06ehJUpVdy-ffO5j9SDo,5355
babel/locale-data/dua_CM.dat,sha256=fOhCBrVImDeIe_5GZrgYvvdY70mSsENjcTEzSld5FYs,609
babel/locale-data/dyo.dat,sha256=KJhfyqJEf8mhA9wV3xbD0VOcUm2JT0LQ8xJtHgqMVPM,10541
babel/locale-data/dyo_SN.dat,sha256=coM-rzzSZhBVSz9gchqxki0QnX4zhOD-Lk1nt6sPCxE,590
babel/locale-data/dz.dat,sha256=DG9YNNdDUCJkqwNdcstRrO8BgU9zqe1PTD4Em3dSOak,89908
babel/locale-data/dz_BT.dat,sha256=__K3dx97Ynlcn2gcV40OXMmd-G1YchvffwSv4old74k,608
babel/locale-data/ebu.dat,sha256=j6vvoYG_wPxra74yw8ldqZp_oD25AJFbUvysJkUtClY,16243
babel/locale-data/ebu_KE.dat,sha256=8py7zy73R_NJUahomYGMJF7at0vD_TMjbQy8AT7OgO8,609
babel/locale-data/ee.dat,sha256=EG6FqmRvtVrJdzN3p8QxU35xqBQzvhB2lbaLz-NKN-U,142527
babel/locale-data/ee_GH.dat,sha256=B_aqQhHklFcblsk2BJmIZgwpAX17_pZENzO1Xoo1LpU,589
babel/locale-data/ee_TG.dat,sha256=MG63DGqUVcZLwabu4o_BezzGovDY8g30wKLHkrbEU8o,1141
babel/locale-data/el.dat,sha256=M-uy_xte0SOlEQtwdOMM92GLjpvx09PgWGO3bYsBMI0,244752
babel/locale-data/el_CY.dat,sha256=07pyrGXJzOGhfxhX--19MAVJlDzuBGe1AfsnkOiHBeA,608
babel/locale-data/el_GR.dat,sha256=WqPmX-_0xIHS9A8kTDJbVCJXhqr52NEiW0CyLdGm6yo,626
babel/locale-data/en.dat,sha256=WFZgaqLZ9ZYKZdU8A5K2ZMepmtkc8-ve3vC0VA1dzqM,194682
babel/locale-data/en_001.dat,sha256=Wsq2--QwmhqwjXx3jpyXs4SNShvNFtnQfH74-9RmU5M,27063
babel/locale-data/en_150.dat,sha256=rbNu79tGtS6MsRHloWtHrmdPuZCmwWRThAQ8M7C_5sU,1765
babel/locale-data/en_AE.dat,sha256=EhFOQNZpti_Z7dPcFMbQbuFA5r4LwCwGKigJgowZ5Is,4111
babel/locale-data/en_AG.dat,sha256=AKSzhMbGCUU-P3phlu6HupABdKSEys-M96wBGbNwgBc,627
babel/locale-data/en_AI.dat,sha256=6ihO9cDogLMcSHgmWHgJDTg0OCTfEC4fVftMAbv3-zo,1179
babel/locale-data/en_AS.dat,sha256=XkSzLYRfcXzQ5S8kaps6cLh8lVLcEtpS7v5GVYJ0oxA,608
babel/locale-data/en_AT.dat,sha256=jKCuj9au7zT3QiZd6mRgjRutjPTd3rW7k_aSCopd3lU,1200
babel/locale-data/en_AU.dat,sha256=GgFVbFesUa_9FbZYqtHtgW3fDyUvQCxBMO1KPcH3wl0,23385
babel/locale-data/en_BB.dat,sha256=qYW0ov3TXlP5U49PsDwBoGuNqydFwIDvAzb9g_PU4BQ,608
babel/locale-data/en_BE.dat,sha256=sLyuId-scSREYGWTIRr-jmU6cuaSkgQbNc_jd5ij3Rc,1493
babel/locale-data/en_BI.dat,sha256=FPbgGK-AHgRjz__MFHCzzOUIGhAmhx662sqL-CZWyXk,1162
babel/locale-data/en_BM.dat,sha256=xkpTQIMT4L5hhYXe9ANWZkxgyS2H6wsMdG1qtacDJH8,627
babel/locale-data/en_BS.dat,sha256=zHTFOmXUvagki_mtGdj0lht3V82InuI8-9cTPhvQ8UA,811
babel/locale-data/en_BW.dat,sha256=R88jGKkwpex2QzxjAVOj-tu5HreJy6DlO3ZqqcD7sKw,2771
babel/locale-data/en_BZ.dat,sha256=0gOIW5WU8lL15-_mWj9Xg0uDjtQiQHsWAl34U2sN3lI,2950
babel/locale-data/en_CA.dat,sha256=QiSNMW4tsb8v4mvvKSJ0fjYx9wKvFyOnqZv6G3ih9xQ,25449
babel/locale-data/en_CC.dat,sha256=n1D9R3tb0Kbc3Umv3cS_2IfDv6gXJDkauUmLRQ85stk,1160
babel/locale-data/en_CH.dat,sha256=hU3jzJbghrq6qpPAcRCsN3_UzYw2XNJCvnC0KScoOMk,1100
babel/locale-data/en_CK.dat,sha256=Te6ZAYFDOE6v9domOnOXCxMrCpBSt4y8i6xxX8zplZM,1160
babel/locale-data/en_CM.dat,sha256=vogNfPzFKhhNfFR3TzWWE-P8wKdkeGujj--c757Iy-M,1410
babel/locale-data/en_CX.dat,sha256=CkFYiGUksAivYRZlmH-sfvwQ8HUgJ5Iqx1LD0UXbcCg,1160
babel/locale-data/en_CY.dat,sha256=Vl4Oa2RNsxbDmyq7cJaN9UbtK3KSvSpBQZ3ouieaDXc,608
babel/locale-data/en_DE.dat,sha256=yW9FX1tuZCAANFhM095trr-p-k39XN6VwN5wYMEDS6A,952
babel/locale-data/en_DG.dat,sha256=db0CYrcJAtcIl9MFAGUuzVUfUS4x-I0ppd4nNP_igGs,1141
babel/locale-data/en_DK.dat,sha256=YFMkEHF_fAzY7w7CsIFe7eDhRuw31mfH-oLlniAkqhc,2350
babel/locale-data/en_DM.dat,sha256=d1muNBxiDWlN78TXhEJiANVe_0UxZGjJ96NoIzmPQH0,627
babel/locale-data/en_ER.dat,sha256=ZCZFGjPQWwc95TKwRCihywYh1yj-TaRmnmJqj26-dqE,860
babel/locale-data/en_FI.dat,sha256=D8c6wssToJyrWdn61FXBz0h35HW3BZC_iGA0_MF4npA,2282
babel/locale-data/en_FJ.dat,sha256=4dotX9Otp56WxZz2vqeHMl-FVcBKiT8BlqGktxoWKFM,645
babel/locale-data/en_FK.dat,sha256=VC91FNLl2QBhA5qkxhodFR6TWRlIus-UHJ4dzJNtebk,1183
babel/locale-data/en_FM.dat,sha256=kwQ5xP5wBGKlO-Mb2iqvsStkTC2JfMc46gBnlFTiI3M,589
babel/locale-data/en_GB.dat,sha256=1rovCqWVd8rHtaNZiudq9tj9j1ZSdQpj85HcXMXRCNM,25821
babel/locale-data/en_GD.dat,sha256=MbV-yK2BeGhQSajMlcL9TvEWWmch0zGbfBFcKj5eBzs,608
babel/locale-data/en_GG.dat,sha256=qW--gYp58HZQysSEvEs2e-PzujR6nxJw76OKtml_R9g,1246
babel/locale-data/en_GH.dat,sha256=WfV3h6HkT4PhJVSqNqIcrYOA-eWWywIMUCk6GX2DGkc,862
babel/locale-data/en_GI.dat,sha256=Pcy6njgp-RTvil94c1DoD8gPEJfDqJxSa6Ap3hz4hLQ,1201
babel/locale-data/en_GM.dat,sha256=1-9El_7BaSwYnE2NvFGES1XWvUOA3h31FOz48SyWhGw,858
babel/locale-data/en_GU.dat,sha256=Msmmrz-7nw0Mm50eo0Df8mPsJFRBBbbDtCgjNN8K4Do,688
babel/locale-data/en_GY.dat,sha256=U8FjrK6RICyHeGXrXRziQ-x5MYJv-67aiNk870rRU3U,667
babel/locale-data/en_HK.dat,sha256=-or_oYC7dQt4KP_QTE8085lo6lNr9Sxd15MGoKBpMOg,2008
babel/locale-data/en_IE.dat,sha256=N2NXGWi7XPi2TcOw4lC3Q7YVGESIhF3LoxX2WPor8e4,2043
babel/locale-data/en_IL.dat,sha256=KmYFTJlvN4yPA1h81uPevqnBJbEMySjB6BwcgvtARKY,1397
babel/locale-data/en_IM.dat,sha256=PYHp1IMNWDea7-pfiNMq0lMSwlw6jrd1W8PijFVp_-c,1246
babel/locale-data/en_IN.dat,sha256=coaqyAiTCcprH5Un1jvvgWZCylIgGkVoKVWryR-RtiU,3767
babel/locale-data/en_IO.dat,sha256=KdFDEZ-ATOiBnVWa2p-QJovncI2Voygei_GH8ole4vM,1141
babel/locale-data/en_JE.dat,sha256=7Cmj2eNyJOMB2YSUAkna9CGLn9cHuAMoQedkWzTJtZ8,1246
babel/locale-data/en_JM.dat,sha256=4EK16ZNGL65bz9xtQrucMOxSITM8wVwqhdKEG04s154,1599
babel/locale-data/en_KE.dat,sha256=KFU4dptHt5iSqN9zSPGv5_HkLkN8tcXdMoZZlSaK3OE,1431
babel/locale-data/en_KI.dat,sha256=O13XFTeRaltrxnCjO4PA2NvM_dw-ye0xpJZeEnF0UAI,608
babel/locale-data/en_KN.dat,sha256=G3xPxRBLVKbANLsnKR7b_rFGy6gYCC4YLzRI5gT8i4Y,608
babel/locale-data/en_KY.dat,sha256=TV5QaWQr32vWrQWyBUskv1f3oq2TJVYqQPGEGJyh5lQ,792
babel/locale-data/en_LC.dat,sha256=C_KqmNUBK_9-uE4_kYbUwNIOBeO9VJ9fpLOcaWwWDjM,608
babel/locale-data/en_LR.dat,sha256=768u6chWYyWCDWWpSWkm8CFsSskf4e4-aayUrLDppFI,858
babel/locale-data/en_LS.dat,sha256=K_G56Rgw6R7d6pMU5_KfwOAUvJk_hwdZe9GqU3NNfCI,858
babel/locale-data/en_MG.dat,sha256=HA_OJPZu4eEyZP07J0MtTm8dAz4c5cXns2d5EjLArwc,1411
babel/locale-data/en_MH.dat,sha256=lWjdFRFi5Cw9EBkpRifvGV93XeQke2MudP1tv7MXV6I,1341
babel/locale-data/en_MO.dat,sha256=oal8-XgFkxo3F77Z5wKqP16pocMuo77-Ac9v6doamvY,803
babel/locale-data/en_MP.dat,sha256=4ES9-ArZ1PI5CbAQ3LLDb8sLM6LVHhAnX6KgAz0VSoQ,1322
babel/locale-data/en_MS.dat,sha256=HMWyIEh0-s1zUWHDC6XnKM8inpIDA36BSA_bN2spR0w,1160
babel/locale-data/en_MT.dat,sha256=4LAEeC9KAdPb17kLcMe_p6U1bBuiySoOGQpdVphMNv0,1927
babel/locale-data/en_MU.dat,sha256=Bq5ftR9nbRzJOacnOFQ7qluvifHCFAU81X4SsWWMHVM,1411
babel/locale-data/en_MW.dat,sha256=1-D7UAzwljnuUlgPKs2HNP0ubNQ9HGEKgIUdpkxwc4Y,859
babel/locale-data/en_MY.dat,sha256=koZHcYmaYIjYT6OANOlHdFfPuF-RmF5iyjVbkbtb1pg,689
babel/locale-data/en_NA.dat,sha256=384TeL01HX5bShF-vJgFfy5m65jRjC_SfITw9K852BI,858
babel/locale-data/en_NF.dat,sha256=rEdi2JCWTfieeeS2G0OCnKMblzSSc6NsoiEg0-JO-3c,1160
babel/locale-data/en_NG.dat,sha256=KnyRrrpnzpV97teswZmDpq3eolhm_geKohcIrryBZEA,1412
babel/locale-data/en_NL.dat,sha256=nWoZ94n1gMwXFwzvMAFraJLYNjiXkZMx5vzAM029PRI,1097
babel/locale-data/en_NR.dat,sha256=SVPL_wXvdKEYdWqUYhkRrgwOMc-f0YP1Uaznxqv4NP4,1160
babel/locale-data/en_NU.dat,sha256=0cg8LGLPIboWlBVxtmd4c10rEjqPvUUz2tyyi7kUksY,1160
babel/locale-data/en_NZ.dat,sha256=x-zR1SoiPc9fpAvtblZLyzHZXrW-Np6ydJdqyXQrPnQ,2234
babel/locale-data/en_PG.dat,sha256=Cq0NrSqmEseFhwCIo6noFliCfKnx3wAOenRn3VkED_Y,608
babel/locale-data/en_PH.dat,sha256=W7ezPkuNS7JqciskJ3G25Ic0SbHZTmmmmenv0a39NgI,629
babel/locale-data/en_PK.dat,sha256=0AD-WPif80PqSYV67bzVTvlj_h074ham3WETqh3NoDk,1959
babel/locale-data/en_PN.dat,sha256=zxKpA6olu6dMYMtZpzaq35mSoMKh6AttZc6wSprPtxM,1160
babel/locale-data/en_PR.dat,sha256=GbsBjcumdJ8L-rzRYD1cXU2uzmIUYHQX4chTgkJw02Q,608
babel/locale-data/en_PW.dat,sha256=LH6T7NOgz_1iwCBhMne8ZH2hjPh-JHL2MOY3xktPyho,773
babel/locale-data/en_RW.dat,sha256=RdqSwsBE4s_LG92OJvPPTxK3BoC-qzltS8PFWM2xogQ,1411
babel/locale-data/en_SB.dat,sha256=cW7aw5w5Wos4_O_aRX1Xj4IXuEIq7eQpF50vnCEHKjw,608
babel/locale-data/en_SC.dat,sha256=uVgNnrmBfJL7Jlv_wpfDtbdk5yFjpsTeyCTBKhiSmig,1161
babel/locale-data/en_SD.dat,sha256=5JQaakikEVwEQ0YJm2AdZ2Zgg44mDPfl3ZKEatwChCI,901
babel/locale-data/en_SE.dat,sha256=gpyVY45RU4nB4BKswErRq6UvnyFU6mQqzxzEBR0tDfQ,1427
babel/locale-data/en_SG.dat,sha256=Yky7Bpen7HGGG4IpzKRb3folOUIfQ9nh0YiPqj2THv8,2017
babel/locale-data/en_SH.dat,sha256=slAAeHdppQ8lHrVY8Pat5VFVwP-imbX9RbClTrFJkbE,1183
babel/locale-data/en_SI.dat,sha256=TJey3lYp_l99RHcPcbxFkJ1u4tyP0Yb7TcY-JYAdehw,968
babel/locale-data/en_SL.dat,sha256=daqNUYE7AgFpgo8PjtGYKm1YKqW8WgVQE4ViUnvh-_g,859
babel/locale-data/en_SS.dat,sha256=2e53Ov3bAoJClI2KxnghO_q68wsvBYm5y69cFpvZpGM,881
babel/locale-data/en_SX.dat,sha256=Ldsv42f1G7kgTFRcGdbyL_RnXUj2_whkfivt9xCS9oQ,1163
babel/locale-data/en_SZ.dat,sha256=qidm3zACYSmI6TgdvkJ-URbDk7BdHg1JmENh3jFUsm8,858
babel/locale-data/en_TC.dat,sha256=BqCmasVKStg1Mia681u6ZqtglR5TxC0QgCD2j1XqAwM,589
babel/locale-data/en_TK.dat,sha256=KmgyiXJLdOlswDEemXHOLEuZb5de5ZM0YmdDxNnAHog,1160
babel/locale-data/en_TO.dat,sha256=wOZyazP1wVbQhv9Y_H_NDHb0ldHsMPdZPN8O-O1c5ZE,609
babel/locale-data/en_TT.dat,sha256=UwplYXlbOs4hLPyDovdYDv6yz8KGChSZ6ufJ5htjfQo,627
babel/locale-data/en_TV.dat,sha256=Z_vPwZ0HZB4aDDibrbzesmYFzgKRqk22hS2ZKqhq3_E,1160
babel/locale-data/en_TZ.dat,sha256=syqDYFfemhw375IYXAM-F03S4gxAe7EbaJcYVbjt834,1412
babel/locale-data/en_UG.dat,sha256=yczBoonl1zmDZpeNyAHAKvQ_OArvhP7AWVLOtKv9Jkg,1435
babel/locale-data/en_UM.dat,sha256=QpePixV3RZ9RiqrYuz49bkN6Xeg-UG2y0Po_yaLbSOQ,626
babel/locale-data/en_US.dat,sha256=JU7XRlKyRBNlDNbGDabuIBWP_tfuFahFBUNDL16cE8I,626
babel/locale-data/en_US_POSIX.dat,sha256=22rJAk0xIO2lY6r_nfKPBUtruaYmgtYeTjq9nz4RN0g,1204
babel/locale-data/en_VC.dat,sha256=udrNbZKYSjk5vRAlIMd_k7255C5GUjBmQjOVm_GSshk,608
babel/locale-data/en_VG.dat,sha256=_MFcYRjyNuFMVYGYjGBQMC3C2_IZjcSXGLxNFUt15z4,589
babel/locale-data/en_VI.dat,sha256=ptodXPLBh9jA4d91bhhHarqlw8t0BuiigzyLPxAX3Vw,626
babel/locale-data/en_VU.dat,sha256=OKNpgxA_p9zCpKhmDA-r2qAUHQmeEY-ITSvz6Hqlp8U,609
babel/locale-data/en_WS.dat,sha256=_qLMqdSB0O18FukP062U6fiMk7uFaNUp-u8qjJXB3SU,629
babel/locale-data/en_ZA.dat,sha256=MYqHr53tM6t70LgMH7_wdkltFYoUWLEG-u9T8PK8bbU,3269
babel/locale-data/en_ZM.dat,sha256=Zphtlz3AeWJ4xZaeDtX29_xuSN-aHrmFX8-dg4j9ePs,858
babel/locale-data/en_ZW.dat,sha256=cgE3J3Wk8y0K6uki7a377LFVPQFmDFPCCn5dngZQxMI,3199
babel/locale-data/eo.dat,sha256=8RBQNNdJRH9CcD1IPXEl4pl9Z8U2OBio9vmJg8Z6f2E,40689
babel/locale-data/eo_001.dat,sha256=Mlc_rMI2tpO1RL6ZJcuMDcO5ou3NuFOxZ16TomDvwrs,823
babel/locale-data/es.dat,sha256=Xj1wyewmRlgXf3qDEsDQpFFAovauJMn7tL9VLlOoOFg,199638
babel/locale-data/es_419.dat,sha256=dtalpa188kCJUE70v4IywI7YGXtRKWv61kCBoyEd0gQ,23299
babel/locale-data/es_AR.dat,sha256=fHbtdPaXDRqdHGGB7NNOn_e2aNSX7OZQ1BiysR9PZo0,8828
babel/locale-data/es_BO.dat,sha256=Ks0Vs-LYS9eSPux9FeV6HZnhdWDgSzPB8RWuDLglzkM,1888
babel/locale-data/es_BR.dat,sha256=CQBretr3RreqaOiCUo5-cmR3EIlSUiQVJMd_fhPktIw,628
babel/locale-data/es_BZ.dat,sha256=whnvRybQayDyZH7OFfVQHMR3aHYLZhpDU2gY-j_PbYo,627
babel/locale-data/es_CL.dat,sha256=HPawCg0SjTK7JIBSvE0JJY7gxeirRz5DdkcXUgY9eH8,5128
babel/locale-data/es_CO.dat,sha256=B7TtJd_eH_7KNClQGfsFbFNwwLmL72t305awqTOMIK8,8665
babel/locale-data/es_CR.dat,sha256=Vh36FfFDvJeTDnmjYlOTd2ruW-5I2Wkv34BqALvJHyc,1719
babel/locale-data/es_CU.dat,sha256=hjouuq52dJG7NJR3ypl_nAHpBIOYgV88AYWnR_i0378,629
babel/locale-data/es_DO.dat,sha256=q_tRNydo2SG6AsDS0CJb7FPQvPHuIYi6hjJpBbGmWk4,4020
babel/locale-data/es_EA.dat,sha256=vYi5_0kkB6gIqCXTdtHnD755-gzO2y_Gd-lAy8ahpMU,589
babel/locale-data/es_EC.dat,sha256=VBBKHoCazqVryt0jzkeu2RadhDAityjxxkSXfsBYIq8,3294
babel/locale-data/es_ES.dat,sha256=VCWxAz9w1EHetI06vwya_gkk7vDXGGSXJumViKKb4Ww,626
babel/locale-data/es_GQ.dat,sha256=v1NY_AGhDyOAq1jIFJIQ9FWosDL_RHNvIIufvaEYdWM,872
babel/locale-data/es_GT.dat,sha256=VjbMTOJSdaBbIhZfbeLcF5KTNtCDNN5Q-Y8mYIrICTM,4896
babel/locale-data/es_HN.dat,sha256=_cSRFPkE1DrBlZFmQFO415ymztO6xQo7zZxHaixq9CU,3476
babel/locale-data/es_IC.dat,sha256=ap4rHt9hZKqaWzlIZXgs92V3EWTmkCULMIY6Hf91T3k,589
babel/locale-data/es_MX.dat,sha256=QGf8oLoSurcTEe41nR0B6mFVMQyfqEXttL-tLL0dsPg,24708
babel/locale-data/es_NI.dat,sha256=2C0CW32DPFe5bwQFn0-yGXup7iVdYncLI7ByU-yGSVY,1653
babel/locale-data/es_PA.dat,sha256=TvrAZFMO0Auv5Et41DcMNQKASC3ZrGCalqbxyqVEReA,3884
babel/locale-data/es_PE.dat,sha256=dYZY6-VdFeUIYLzm9wyWQ7fpDc2_prQh_Nm5UsOoOC8,4848
babel/locale-data/es_PH.dat,sha256=az00tcCjyCZo-DL4wkdCwFGwuue6M3KwlrCvT6UJCI4,1205
babel/locale-data/es_PR.dat,sha256=D-Qc8ImING-Wu5LZpSvgfb5tTgKdqtLE6B-IyvJnXck,3798
babel/locale-data/es_PY.dat,sha256=UCgFkmRSNtsFZAZ1QbauP8511OKtbj0OAOdkJJ-0N3M,5319
babel/locale-data/es_SV.dat,sha256=DZ5zGJp04BA99FVtOQIpROt0Mei6Esua2Nw0m48rViI,1402
babel/locale-data/es_US.dat,sha256=8_RWSFk2Vj3v0dHc7iX6Ge20ZWX-M7_wYxH1BtTfv9c,25343
babel/locale-data/es_UY.dat,sha256=-Jw0bOdLf3LWpBykOF6dZ0nIy0I4alpSH5oTo1AXAWM,2540
babel/locale-data/es_VE.dat,sha256=DuMDeGoUvTE_qUGtOUnS7eIpbImIP5x2YeR5KxFRmnA,3752
babel/locale-data/et.dat,sha256=pGdCv2OqghUpVp6vOMfldspch91G98i6kIm6eji2FKE,200192
babel/locale-data/et_EE.dat,sha256=xpoZTzSn447SsQklqYnKUUL27DkZuNsRpQxTn87Tmlc,626
babel/locale-data/eu.dat,sha256=gq-LZDCqk_bt-R29APS4rVYl-K_D3d5sIb5nhjzdZnE,176850
babel/locale-data/eu_ES.dat,sha256=xvTtCQUjEml-wPLAZR4kU_hhXZ-j5oIE5MO577tCdFg,626
babel/locale-data/ewo.dat,sha256=kZQmUNXQ8t64WMf8lNY9_i03WVdbmJA92_wAMK8p1jc,17595
babel/locale-data/ewo_CM.dat,sha256=NirWcwhJ0SSdkIex0afT1IDuwyfJBBcfX7CGnJNIOAQ,609
babel/locale-data/fa.dat,sha256=qX7byceg8QHSgGdOQuJlOPvw1G7B1V7Ge5d_cieSwnI,217724
babel/locale-data/fa_AF.dat,sha256=donKj3WGTXbFV1upg-DOI-7fZgrLoUZjL9mQrj58NDo,11247
babel/locale-data/fa_IR.dat,sha256=ZnDClkeVc1IPiEGa63b7BhvnhklUhgige3sTjeEK6mU,651
babel/locale-data/ff.dat,sha256=fFDGP7W7WqzDGCEe5jMrIWsD9ODm2h_4iZobPVhcRRc,16084
babel/locale-data/ff_Adlm.dat,sha256=ZuHELP4Qnng1f83guWn-4zQ2QsIvvJJfQh_yT-VDgv8,174659
babel/locale-data/ff_Adlm_BF.dat,sha256=7b8PdK1LA0V-odNH3iwuNDDR1vQYQhXvHp-5bB5ZwLc,610
babel/locale-data/ff_Adlm_CM.dat,sha256=32kFf1KDw82I2SKzaVB4P8dBfmkw_mmG6fYAuThS99g,629
babel/locale-data/ff_Adlm_GH.dat,sha256=90UIh5AUwO8eqvY2d7MzCmPwJ2XNFfAMfHqqEr-QZio,1209
babel/locale-data/ff_Adlm_GM.dat,sha256=NqlOMO7KDanw-Z-dnG4jSX1SUESFQrNG1MVCMutQs0w,1205
babel/locale-data/ff_Adlm_GN.dat,sha256=VAK9og8mz1WVXD9RnykNOiKkC69nIF_gGgkwKensMX0,589
babel/locale-data/ff_Adlm_GW.dat,sha256=_BVL7y6irTvBSRhVMmICwv4uNllP5nxIqPGpU5i9sCs,610
babel/locale-data/ff_Adlm_LR.dat,sha256=UYThYdKlKFe4XX52x7WO2xmkiHA9heV9E3K2Mg0RP6o,1205
babel/locale-data/ff_Adlm_MR.dat,sha256=anYa5CmU8BiiYRz2nL12UDCwLJIsUIbZqajTFSYmvd8,1206
babel/locale-data/ff_Adlm_NE.dat,sha256=EmZR_KWVdW7b5TxkRsivHLoYKwHU029v-R0k7zieWQs,610
babel/locale-data/ff_Adlm_NG.dat,sha256=OLPxRiTM2InmMtH2gCRJRhbmwhawtdSR--6001ckT5k,631
babel/locale-data/ff_Adlm_SL.dat,sha256=DE0siwIkfETd-Pd5nvDRWK5F3_55bSYpJFBZfYKHquE,1206
babel/locale-data/ff_Adlm_SN.dat,sha256=9USLkiIrnIVKikQHcPqyF0bwUqc4OiAm9vDisk9boyA,610
babel/locale-data/ff_Latn.dat,sha256=byAYS1KDI0NXZf0r5uEtjiW_dvH3S7nniynJX6jR30w,839
babel/locale-data/ff_Latn_BF.dat,sha256=NNCmS9PhIhnRzZlE6Zn7Sjt560T_zY0oAGvs-wkJQjo,589
babel/locale-data/ff_Latn_CM.dat,sha256=-vhCSM41OmNfJwpW2ArLlVSOBAmbxI4qkdGrOuG7jxw,608
babel/locale-data/ff_Latn_GH.dat,sha256=rAV1pDUEzSqrxYJi7c_bB9eVweCRaIzw1qGZkBw9HB4,1188
babel/locale-data/ff_Latn_GM.dat,sha256=_xfcaqyGrO0UdRy19lGMjo3X_jk_MGCMSvgQjfyL0p4,1184
babel/locale-data/ff_Latn_GN.dat,sha256=cdoXME19UJX1H7WkepaiJEiUql4zOY7h5uO8GKQoZ_4,609
babel/locale-data/ff_Latn_GW.dat,sha256=lIrg2frFHCvM8guhuR5OmGU9Np_yUTIcORKQITZSFYs,589
babel/locale-data/ff_Latn_LR.dat,sha256=Lg_t_ANpKoAXV_TeCGXpGFZOnmMmYIdS9GfylT6Y6h8,1184
babel/locale-data/ff_Latn_MR.dat,sha256=qhCUin06n7Ow841YyfQ_aYJnDB1gCwbh3A-pozV-75s,1185
babel/locale-data/ff_Latn_NE.dat,sha256=vYqMUR9LCykf0H_rTE_oeS9fYK7t-ajKpbK1IpF9-Cs,589
babel/locale-data/ff_Latn_NG.dat,sha256=NAMpFyNWE3dSzIwJTRBwH2SUhoJlu_AzinAtCByfyJA,610
babel/locale-data/ff_Latn_SL.dat,sha256=AMk0G4KKcrT9Yh1902tRDC8JBwXHRDqRgOcw6W8Ne4o,1185
babel/locale-data/ff_Latn_SN.dat,sha256=Idf9JfDjAfWlKouD9gYB6m6_qg9P9xUIRAgMQ5O1-Lg,589
babel/locale-data/fi.dat,sha256=mQkPOSvYmGw1T0FjSrtJ0vgGOZYYTV9Ixz8oTvfFoPQ,227195
babel/locale-data/fi_FI.dat,sha256=CqHzlsNe9s14gZWMaUn9abl4FmLAZknXRX1lP5Pdrc4,626
babel/locale-data/fil.dat,sha256=xZADFjoyLySL2eXDQqLicMAFPoaRfdLo6KX-DGk69hY,179405
babel/locale-data/fil_PH.dat,sha256=U-tLxLn0lDKKpUqEQPLTumOuJOIYi80HvPnUk2SsObY,609
babel/locale-data/fo.dat,sha256=c3gHwJxgpzyEo5QTL_VGVE14Ledk5QJdkedX9a2LIIk,165657
babel/locale-data/fo_DK.dat,sha256=V7Kq03gQkns2EDztSyIiRLr80EtZsGZnmoYPsChW__w,647
babel/locale-data/fo_FO.dat,sha256=WZJB7n6uQpGsPNWVXqP851OGZd5RmfSMbQ-s_C-00tQ,626
babel/locale-data/fr.dat,sha256=PuqjeWNTBK-3hLx2xl3WMzDRjOsHSvq3s9pi9OVRYPk,225342
babel/locale-data/fr_BE.dat,sha256=QW5XQfg_MDjZQazIkAJTp19ZeRRYLEwTP2Q6ix3W3C0,1254
babel/locale-data/fr_BF.dat,sha256=gVdej2f-lrFMVDjQNCma1-odtbYzezlFw8YR16ZuMas,589
babel/locale-data/fr_BI.dat,sha256=hCmnp8GizMpXqkYPSnRFUMb1ltL9xT1aIHUOt8uzR5s,610
babel/locale-data/fr_BJ.dat,sha256=CZgeRt0F7zcnCbuwouXBxTg1Ht6M4UpS1JYNgdnGZOk,589
babel/locale-data/fr_BL.dat,sha256=mN3e240_oM-t97i3jZ33ptBFR3XJFtq2519QXQskeDw,589
babel/locale-data/fr_CA.dat,sha256=hUfSdAwXBvfuhc3dFBmHdmgllVG8dmH62gsD5jYn80U,65858
babel/locale-data/fr_CD.dat,sha256=KqBJ-62QyyMteiPxaihTP7AZAbjuoZothXcV7YBv9cA,1106
babel/locale-data/fr_CF.dat,sha256=zElh_1aCiSapkL83eytl19hKu0R6lrE3xmb_a2lf_cM,589
babel/locale-data/fr_CG.dat,sha256=XqZxi9XodrhYnQqagiCv8zc51Aqv8S_E3AKgZxPO6QA,589
babel/locale-data/fr_CH.dat,sha256=MHWdTsv0DY1KNjVQCSHuEOa1PQ_Mtbm0b3mThVRXkzg,2970
babel/locale-data/fr_CI.dat,sha256=PULA-d30mWwNN2Zsg_58tbUde8ljeA7ej6_bQSvyngM,589
babel/locale-data/fr_CM.dat,sha256=sA21F_q-PMRWez-fKSoxNz8yyaTtxBg78qRRukmbMR0,2083
babel/locale-data/fr_DJ.dat,sha256=retYu_VKqCAemcxMH2ieVzYXLQDnM6-FkxJLfRksBMg,1205
babel/locale-data/fr_DZ.dat,sha256=lT9Bd_4OA78pLByWz-ub9pRT4pfKANUsAG9Gk2NhTtU,1247
babel/locale-data/fr_FR.dat,sha256=oucSQVTi8gnvWNQ07WJPnYh1YQRxbYR6afhy8Wd2YgI,626
babel/locale-data/fr_GA.dat,sha256=hjGGeVpmPCTxP7LEMxE_iUUS-uSfRnY3unJ-800ilGk,589
babel/locale-data/fr_GF.dat,sha256=FwIBhmnYvA-VIAgc_n9JLiENGTZMXFANqyFFpeNjNYc,692
babel/locale-data/fr_GN.dat,sha256=BIJ_Gl1Yp5fVwQNISO_f4o5U3vgOWPKB-4UWMjp_SMw,609
babel/locale-data/fr_GP.dat,sha256=7IjXNU_xYD73C0EaZ2IWMxZ8kzIFRLWgrE07-xHFB8s,626
babel/locale-data/fr_GQ.dat,sha256=zMNFOsgv_5JFDvnqB6AovINlxEdr_QYBGw2Rl6LsdGM,589
babel/locale-data/fr_HT.dat,sha256=FIn4weL4_b_phmnIIQFDdwhqY3UFaLITSGKAZh_sIJw,1873
babel/locale-data/fr_KM.dat,sha256=SaUGzyArQSXa_pwsb9hw0_fs1TjcZq7o2CFW1mAfvQk,609
babel/locale-data/fr_LU.dat,sha256=OZ6lHBdT7fHpiMgMaIEJhNB4ohZVMZRQiJQT98n2gLE,687
babel/locale-data/fr_MA.dat,sha256=7-FeaIFIZGfbunjR-M-lTr0WkTGljmC354Iadk3_S-I,1277
babel/locale-data/fr_MC.dat,sha256=se81gvlIKgey2DgfCYayuXiwV0Wykw-QM4otwduegCQ,626
babel/locale-data/fr_MF.dat,sha256=asOP0aSNk9sx2Jx3R5QigjvOQmgEzRP0atpznWTZEII,589
babel/locale-data/fr_MG.dat,sha256=GL58hdcr_DZePturTSmv-8WScEg60WajuKuijeBs5hQ,609
babel/locale-data/fr_ML.dat,sha256=x_UkTI0saDvoYCiYdNF9CWoyc1VvMAQFBw8APjCEL78,1126
babel/locale-data/fr_MQ.dat,sha256=v3tmYxQ45BkuVen2er9vMsxTceL196E98XYPsGWKXTM,626
babel/locale-data/fr_MR.dat,sha256=0qY-kxib1lGMlxGNcyjEZwj7BV8Da3CZq8DGyiRbcrM,1185
babel/locale-data/fr_MU.dat,sha256=UVc2y4RDe6Jy6_48f043AXBUqWLvktTjnj2osTeAJO0,609
babel/locale-data/fr_NC.dat,sha256=Liy4q5CQx43KEep69njgxfUENHEJRfXaZJlsK_UcIbw,589
babel/locale-data/fr_NE.dat,sha256=beqoAaHiYhcvUeABHOBD_9cJQ01DQzo5nbAZb5JZb88,589
babel/locale-data/fr_PF.dat,sha256=mSlv8dzrvNyo9XfC8yczKIKGaEPGTIpf71Oi1IH_f78,589
babel/locale-data/fr_PM.dat,sha256=yukgtaee7ROFoFHM7dUO9CSYlCmipm1i5ZEIsbvvP0o,589
babel/locale-data/fr_RE.dat,sha256=IN73Uw9cZdifS4rK4SfWiecLcAX0R2F4j1aV_DusCUQ,1142
babel/locale-data/fr_RW.dat,sha256=b6cY_0EAjkJjlLAjdYr7o8vkdzB0sQbIgwgWsFlaO1M,609
babel/locale-data/fr_SC.dat,sha256=ejzZtxh5_XDx1B0oZFQx7oDpuuxAsmNp1mYxGtnRs34,609
babel/locale-data/fr_SN.dat,sha256=AzbXwg-QV0b_-M08HrFFVoi0CvQSW9tK-rNHQ-N-9d0,1277
babel/locale-data/fr_SY.dat,sha256=nc1VVE9d1H23_Kppl-NQLCOyQHhUc3hVpFvIHKFwZ1Q,1247
babel/locale-data/fr_TD.dat,sha256=sUvrTreI6gzMwlLxXI-uLQmmO3bSFSa7zgdrxQqH1_w,1165
babel/locale-data/fr_TG.dat,sha256=GWo6BaOsi8-YZfuWYIEDMyodmtktbkK54R3fNEwvsNY,589
babel/locale-data/fr_TN.dat,sha256=0OMk3LdpZvhd0ZFbYRh2ioViNakNsBe8DQIbGnkCeQo,1185
babel/locale-data/fr_VU.dat,sha256=yly1_BvKQwMXPMXZSc-ZRbAZ04qjatwiNPrglkTge_M,1185
babel/locale-data/fr_WF.dat,sha256=_LOp9Pd-uVPzUqTOLmdKAVmqdhJSc9TQxN-q0AvFZMA,589
babel/locale-data/fr_YT.dat,sha256=M-OawlEGCEqzxMFehDb_3Ofb76HA6nwXEBFBKEi4qMw,589
babel/locale-data/fur.dat,sha256=kBxHiHga0PnTMJY38zYNV29C6Y1hw44T2qazrXRDBQE,35027
babel/locale-data/fur_IT.dat,sha256=-jYgvCMGoV9qmo1lNuPbfFhw2RiwM9-XrMAaisqk3ck,627
babel/locale-data/fy.dat,sha256=cNAuTsUGZ03hww_0oz20axWibvGvrSc_qtjNwX6qT30,110221
babel/locale-data/fy_NL.dat,sha256=6kDdfEWgZuA0BeiB9BM8qhtPVPqUB4zBkpQBmEulOpU,626
babel/locale-data/ga.dat,sha256=BWeLf2Ib-AwjhXVaP8Tj1Np23kyKwpOBuNTOIjueIAg,316865
babel/locale-data/ga_GB.dat,sha256=DVKT5Ub0mvXWADwJua35XUCwxPrRj8olUR-xGv9x07A,626
babel/locale-data/ga_IE.dat,sha256=cCW_n5mlSTNu6JzFj5bZMiJbEXFiOHH8BrCB4MnAi5Y,626
babel/locale-data/gd.dat,sha256=11GEkGyo2nBzMBCd7vfzaif9d_xgR8KIEvJmPrAZ_Lw,302010
babel/locale-data/gd_GB.dat,sha256=6VHHQkNfDnsLrshZ5VM0AvbuOmAkVWFf6KIBK6dXxhk,626
babel/locale-data/gl.dat,sha256=VdJBH1gVlN8SYNhZOZ8-h6svbEAMapF4HVOtgqFXQ44,176367
babel/locale-data/gl_ES.dat,sha256=taQiFoK4jse9XR19xt95xT_BXnzftMPMJgKk_ZIh1xg,626
babel/locale-data/gsw.dat,sha256=BZgIpYmp-FUl1rjUf0EG_pUQERu1aQb9kvqIOG_LD-k,108049
babel/locale-data/gsw_CH.dat,sha256=oNDsu5FZKmaMx0q94MsggWUHYobgGv6lNNwqRbm6mes,627
babel/locale-data/gsw_FR.dat,sha256=4rf2w5Q1j3qezQ5Jf1-0qkY3K2Yk-ArQBBFCciWNfiU,627
babel/locale-data/gsw_LI.dat,sha256=4aFdXjXWs0W3IE-cqe06PKFdB1l1MbQre8NY2sm3lWM,627
babel/locale-data/gu.dat,sha256=Bn86qx8SplDJcC2V5jLIOeAavwh7EWkDZEAKz2nY92Y,246649
babel/locale-data/gu_IN.dat,sha256=4mup-pKABihWun3Ougbz8HiGoXtPDPdAqAKMBma7Gvg,631
babel/locale-data/guz.dat,sha256=bzP0aY17_xjZaiVCMTACE8ThMfl9HcD-ICcMFLjN7B8,16016
babel/locale-data/guz_KE.dat,sha256=S-xrYnojrbkIXv_AMMaLR-0Et8CtW6Ty3FDkikLYqS0,609
babel/locale-data/gv.dat,sha256=lWBEcD66RJTHCg0L0uUEk3twf9iApg0VHCUvCmUGNjw,4146
babel/locale-data/gv_IM.dat,sha256=32eF8Qm1U-NzDs6CsC1a5G40zereETci2vy066Dq9m8,607
babel/locale-data/ha.dat,sha256=YZzU6hOiWG1eQjBfS8xKzG8FLFYroDh0wMIQnp1xnV8,78016
babel/locale-data/ha_GH.dat,sha256=WAVJ2CogGv-7o3KHNfd9YIThisXhPcKkOCStIkCJYOo,1188
babel/locale-data/ha_NE.dat,sha256=4LR2guLb66Q9ptMpxPP1o9RoCbMUvWh0xdbfOtOg92Y,1248
babel/locale-data/ha_NG.dat,sha256=7ArPguvlMmsMd_KuhyAy5K0PTuvdzDgbCrmY5c3hyKk,589
babel/locale-data/haw.dat,sha256=daT0jGNbH-c8-byXW8Ev8k7x6FxWzWzM4kwyGowZrgY,16106
babel/locale-data/haw_US.dat,sha256=0npKxik41EG4w134GeOKBCqQiyn4W_4RU9Xol9An9vI,627
babel/locale-data/he.dat,sha256=B0Bypmykcuj6aTi6m1EOjpPExBZ_wQewVW3AYFRYaiY,271008
babel/locale-data/he_IL.dat,sha256=tv1zu6LbE2qFr5KkxegGM6sl5YjsHeOohznihTWqul4,651
babel/locale-data/hi.dat,sha256=R8gru5lLOJotWFqfLJm7RLkQ9F_k82wCa4KWk0XRnXo,244568
babel/locale-data/hi_IN.dat,sha256=laF8SEGi7j2jIwdbvx9jumoN_ZSlsmM2qct5Qpdzy8g,631
babel/locale-data/hr.dat,sha256=_Bnj62FHBemVoPsm6v2atZTwG_4RgIvKCN-UOxpyMj4,247517
babel/locale-data/hr_BA.dat,sha256=cb0WcMYeSmL0iyadbeYGokENF3IdPgPG8Ir3pt2QWhI,1161
babel/locale-data/hr_HR.dat,sha256=FBTFejY7tzVjCu1TCX5PtUIXfhL2iN0DukagJf7dS6E,608
babel/locale-data/hsb.dat,sha256=7JQcT8q6VIK3yOG2Jx41ksSJj2G_oqcrZH3UXHS0jvo,179178
babel/locale-data/hsb_DE.dat,sha256=mJpofwRoSQoD4uMNdi0xcLP0qyq0IysbE2VkXNYniug,627
babel/locale-data/hu.dat,sha256=0Gvk8Gl8uJtKpPezPTWfR3XX2wkDKIRQsPuTTAYcahg,192963
babel/locale-data/hu_HU.dat,sha256=KndrzgNop55GlUso27Dfpf6rW3RA7YhQibwBFTzufk4,626
babel/locale-data/hy.dat,sha256=cx7Ap0eLvzTGjVGdK30x_8TLZ5QLsFgbCnbqrYu4s8I,214313
babel/locale-data/hy_AM.dat,sha256=4HM865GP-TvuBp3XjB41rgc1QuXLLITSt3skVtB0QHA,608
babel/locale-data/ia.dat,sha256=kIIuryj5kmGxdG2NWtOdPOezBSO3NQGbCVI7dl1Bndk,112822
babel/locale-data/ia_001.dat,sha256=onWUTi-JeTzCyFGYj9VWyvYFnE-0w59rnsDeP3WZfCY,914
babel/locale-data/id.dat,sha256=_jTwyiD3gWQWvaU0Re3tJ6nQI0BEmeQzc6AL2PTBZXg,163265
babel/locale-data/id_ID.dat,sha256=Q10Fz7etpCtqpGmEOzhzIAluNUViuPV6fJ8Ibp4TMDw,608
babel/locale-data/ig.dat,sha256=-aYZuS7od7VW9iec3iOwyWRKdHtdxhISdJIIkeLLgB8,52036
babel/locale-data/ig_NG.dat,sha256=qvl7ZtJLFgRf6Jbys3rPutuwKL0nImrLIyI2KxDJNMY,589
babel/locale-data/ii.dat,sha256=crBpYsdPGu1KoOSjCWHUy67ssD13AfvmK261h-VRMhk,12588
babel/locale-data/ii_CN.dat,sha256=NptEx8Tehw_ZnxhDc7qbpFsDxF_2Dxjx403D2fg2kKA,608
babel/locale-data/is.dat,sha256=ltT4Iwckwve2QSs3Z0MzA1x4KXXK66cFUOcRnlLDG_g,188046
babel/locale-data/is_IS.dat,sha256=vkGTcivdc7UMK2uv1QCKnJkoGh1cFUyK877xmLKNWfQ,626
babel/locale-data/it.dat,sha256=7JMCTHE2jVXjGEkfgmmFdwlQbKCwRo1PfTecsagqfVg,188460
babel/locale-data/it_CH.dat,sha256=aBS-dqAT_jYf8DFLYCQJ4A4MfLi9u_rAekJ5fU8eTLs,2776
babel/locale-data/it_IT.dat,sha256=EPq-ZuC7fD9sVQNf7K7dGReI7pvxix55SFteRJlEheo,626
babel/locale-data/it_SM.dat,sha256=gpwEWv5kVspUSBElJAoge82X76LK1lsgxShdk_BdxwY,626
babel/locale-data/it_VA.dat,sha256=drgEDlw3k2saTMXzEz5-BkkHgCCdnXVQ-aiCHUMYAUk,626
babel/locale-data/ja.dat,sha256=m8MJqRkZ-VMiUTOqi0zTmQe7CMDT5pT_Vj8Ek5nbyG0,200287
babel/locale-data/ja_JP.dat,sha256=fqV-tzCjVKeIhB1TH9R2vsz_kpEwD2KSdYUMOL2zVQY,608
babel/locale-data/jgo.dat,sha256=eEw-8GRRYT4SB36fdEfaLJX76RKeEhJomcYa7wl3WvA,12628
babel/locale-data/jgo_CM.dat,sha256=4EKGSIDydn6xezIwTpMhOZFnheOhhMWfwadqa9rRRpg,609
babel/locale-data/jmc.dat,sha256=q2xbrOlR1h4dKkAb5f-FFeG1yY1zAO6vZ2PqN9tgTLs,16068
babel/locale-data/jmc_TZ.dat,sha256=bpvlP-1bAXEnvIRsPxFHel5X-8eLxF8dUOlkJctN78k,590
babel/locale-data/jv.dat,sha256=LaOeOox55UzDPRCZzpKBtS91S8visMnBQiwFsKvcuYE,129461
babel/locale-data/jv_ID.dat,sha256=H5wi4GL8eID9c2QUxpz6qpFn5ORgdpE2mjYxdkozJiQ,608
babel/locale-data/ka.dat,sha256=isStz_8QWtbWBJidLjlvr1wqrm3DJHYSLoeZkpzJAy4,260705
babel/locale-data/ka_GE.dat,sha256=4G3wWIQOIZM5Z8r1Px0d4DvTOMwbR4Ubvq4expe_gY0,608
babel/locale-data/kab.dat,sha256=-rQbUS5U939yjCgkQd9aA4PFolU4h3cZnP1brEqMLRA,135263
babel/locale-data/kab_DZ.dat,sha256=KbrMqfLO_TlWJlCQVmK5IjqCwVppZUryATx2Rq5YTaE,652
babel/locale-data/kam.dat,sha256=lr-AmdEqLfe0Asb3wwfFYGjzMMqobCr9-ZyDb8Cg-YQ,16175
babel/locale-data/kam_KE.dat,sha256=vfQX-o-otm5WDwv2qrHY1lesX-AQ9cX_2HW-nO820AM,609
babel/locale-data/kde.dat,sha256=iV4F5CHX9rYrt0R_iioeDnTv21snL0t8hZ95QNcGMBE,16475
babel/locale-data/kde_TZ.dat,sha256=RdJ-ovdj55xBfaOc5mE41fqsNdLN_7oSIOcyq7-aApQ,590
babel/locale-data/kea.dat,sha256=Wf1pAdN0uMYvlUpOiQdLgMvvS03rkpkdDwU-P8XzvWA,85757
babel/locale-data/kea_CV.dat,sha256=7lbONkE-y9_doiZJxjyGYM50Yz41JNGwy7dV53vvhEs,590
babel/locale-data/khq.dat,sha256=jmjjC1r6-Cxur_0Uh_WXk92MBgw4fR41PzwlhVnr3qc,15939
babel/locale-data/khq_ML.dat,sha256=CbrIcKwmxw5THhW-nZ-sPFZjsfgpHZUDl-hhWH2toDQ,590
babel/locale-data/ki.dat,sha256=abQFXbMy9mdR96-Nebj3PL4_frRUGAEcsk3_-DQ0TYM,16123
babel/locale-data/ki_KE.dat,sha256=-fcVNnw6zrxr3Bw7mi-vpkzP0v4v9t2hkj5ZEuG_5Zw,608
babel/locale-data/kk.dat,sha256=V4lqjiOozZQv3Zwukg1UlrXhsuDx5u2aA_k--eEsVAM,210242
babel/locale-data/kk_KZ.dat,sha256=DhjfmmZRmr-w8q98Mqr8h_v2hosJAjTtTFo53E3QGQY,608
babel/locale-data/kkj.dat,sha256=466fsXBV_YSBSPGZx0z2FYvYrU8Ql_QIIZjXfNGv4KM,4888
babel/locale-data/kkj_CM.dat,sha256=KY8LZH7ZqifH7BTxFz4ylu4d1LAAxMAD8W-a0gYsjZo,609
babel/locale-data/kl.dat,sha256=gHhmdk-As6QszK0V703UfVG9XcDZo-zA-AKfX3YboWs,58200
babel/locale-data/kl_GL.dat,sha256=RojeiBiofKUHvk9f_ITt44uxy9gGhxz-sVy6sBn4Zcg,589
babel/locale-data/kln.dat,sha256=RvTuZqeDDB_BKquwaUuXRjL89K0d8kcpj5rvD8oaIpc,18003
babel/locale-data/kln_KE.dat,sha256=RydM7LQVL9u4kqeFACUbNwf4M8vQQhP0qkKM_oL2oGM,609
babel/locale-data/km.dat,sha256=wMIgFZSCkKBp1QkkhFPVEgAof5LuMFxz0jEzIWRdMQY,202267
babel/locale-data/km_KH.dat,sha256=xVjkyVxn_hrWpEp6JOzSZKxZFDZ_3UQjRQsVPvBy0CM,608
babel/locale-data/kn.dat,sha256=ZnwTJdH60MjTwgKjjgLR_vEUIHFRreimD4eMv1rAA7A,263580
babel/locale-data/kn_IN.dat,sha256=Kzg5Bayf-ACbA0Nun8rTGYcbi5r2PmghFxlbyQIiKV8,631
babel/locale-data/ko.dat,sha256=5disPatHT9WJa6UOmZXl6iuL0PWplfHyQMjlNzFqki8,175158
babel/locale-data/ko_KP.dat,sha256=2Z1Rbojo6MHJGQdInFOjfZHbpRdwvZfM-FU_08oFGng,789
babel/locale-data/ko_KR.dat,sha256=y-3hO1aBM61NXG2L4o41zAPNlUvfA3RE14q_8SdarcM,608
babel/locale-data/kok.dat,sha256=Ot2FE1ar2aHOhP6L29tqcxHSYoTc46KoQffIDHGkNQM,182871
babel/locale-data/kok_IN.dat,sha256=e5cBMZY7kU_9H69kTorB93cUe7xFASM-2hUfGY3A-ec,632
babel/locale-data/ks.dat,sha256=0DI2B4YMdtXgQZMQKTKL7FrEH8ntI8NMZuK0wMP35x4,102570
babel/locale-data/ks_Arab.dat,sha256=kfXVFhHX_NrcA7tZO6yYXym5wsDvpjma53urJPVeGJg,823
babel/locale-data/ks_Arab_IN.dat,sha256=_fjJMmIU0OJMR66po8yq9ByOzZZ3tomRqVt6RM4BJFw,631
babel/locale-data/ksb.dat,sha256=SxZYlQUncKnA8ljqLb7k84GzFoGzaV1mhC9b8y03mEs,16043
babel/locale-data/ksb_TZ.dat,sha256=2Wzocj-_i0wMu7oi3-8ynqirioha6nG9PPI1-5EMbnY,590
babel/locale-data/ksf.dat,sha256=fE1YgWMw1RpxNYkJyieh2Cwm4NCE9FQbM9P4he1cviw,16515
babel/locale-data/ksf_CM.dat,sha256=1CFxJU8F4NverN5cPa9xvVI-we8x7wbZgP3UfXVnL0o,609
babel/locale-data/ksh.dat,sha256=6ttxGMPLhYNiad_7avsUIF2B7rFuLLxKw3Bv3nB1rsI,88937
babel/locale-data/ksh_DE.dat,sha256=vTdZCAi8MAGFb4wE_zjnNTREloPZHNGc38eXQ0uwtPE,627
babel/locale-data/ku.dat,sha256=s-_gQdcuJLmxKWYuUftIvv4TSjViNOeLQWiQp4Q38zk,28771
babel/locale-data/ku_TR.dat,sha256=EsO9U5XN30PqoR6A-7q72uLJ6An2BMuGbrh6sYrZoFU,608
babel/locale-data/kw.dat,sha256=A-aMyXGee3cHKH3Vrd1rP11oz8Mzvrm05B6DB_c7E9s,7242
babel/locale-data/kw_GB.dat,sha256=nvzq6ku288buMZIPacs8IGm5rrD0LdzYFZQxBe9a_jw,626
babel/locale-data/ky.dat,sha256=5xH7IcsPFiBp80H8j6Se0o2imNAELzqORSyNHqGrm3k,202058
babel/locale-data/ky_KG.dat,sha256=I9WGUgCDXB09jIycutdV0trTbhHFKvbM8Cr4_eTvHmk,608
babel/locale-data/lag.dat,sha256=r2Ms6wyrXqZ_u1eGycN4gIHvoDmq3LHisiMzAVvoLP4,17141
babel/locale-data/lag_TZ.dat,sha256=gB3iS13Egu-2TLYBYwM2xbve9HxMHCQwgoxELuIuxTI,590
babel/locale-data/lb.dat,sha256=4jKUN0pdyWrEMhXiWo8iLEKsx_NQppk41f_21Asp7IA,164597
babel/locale-data/lb_LU.dat,sha256=oksbNG3kkuxhnrv6goNlYcBj0Oejpr9-ptrmWHF6EW4,626
babel/locale-data/lg.dat,sha256=vV3xAg1xWrx9LbkPFxvjeZgQ0w26fXZFPhf6lwBDM00,16434
babel/locale-data/lg_UG.dat,sha256=1HeWA7IllosO0gp2T_nevwD0f2CSAx9lMfQYK-JpafA,612
babel/locale-data/lkt.dat,sha256=NdUx996UtMv1MiuP3iqWSl8T5QDZV8cfVGxpfgzc41s,12766
babel/locale-data/lkt_US.dat,sha256=KoED03rqibBCmXUUHPR12gR0xc9ox489Wxavkf3hJl4,627
babel/locale-data/ln.dat,sha256=hXo1sqtv_f1zrGVd9DAyMJPChHy4oxSAxN2yM8WOeiU,25891
babel/locale-data/ln_AO.dat,sha256=Df8fip-BEQDkkdNenJMZYVEwNEFpJU3e7TBDFk1GCFw,609
babel/locale-data/ln_CD.dat,sha256=cya8q___2YF--XiQKag0Z2j67S_3MXvGMkqjjvao8Js,589
babel/locale-data/ln_CF.dat,sha256=GI1_WE8tFKny1XT5c7Vdr1xpgTYtA20qoi-LbfXcNmA,589
babel/locale-data/ln_CG.dat,sha256=gR1qJakj6apKRWJfeXchgBbbmOYiZJs-sWBiOVC4giI,589
babel/locale-data/lo.dat,sha256=wFheTwOpB9qwdWoEHy0qyUpEPf8zMbKw8tvCc2zpLK8,220343
babel/locale-data/lo_LA.dat,sha256=Le3oiQxOdMmSpTmRje_U-gYUmJIgsRuIrll3v9Gvn9U,608
babel/locale-data/lrc.dat,sha256=FxK5wDraPcV82EnRK7L8Lc6pOpd9DlOz93hqbBlvp4Q,19021
babel/locale-data/lrc_IQ.dat,sha256=IulRzfQLGQNFx-m2GA1E-996l3AmXam6Kb2yxEU7Pzs,1228
babel/locale-data/lrc_IR.dat,sha256=Xwe6srYtOSobQ5_3dgtaFUJPpdCzPfqdMdFw5u3h7iE,652
babel/locale-data/lt.dat,sha256=eQLIpUDmVcOlDL3RWzcvVlUdsaquYCLxuxbJ8MjTWEA,284211
babel/locale-data/lt_LT.dat,sha256=xpcc0-LW9jbhEMcG4_YJ_1Zh8gjMuO_pFWRRl71WVUI,626
babel/locale-data/lu.dat,sha256=39-6RP_EimDpNfh7e9Rfh24evWwSEnaSoXTxU4Xus0g,15888
babel/locale-data/lu_CD.dat,sha256=NLQ9XNdydBzo-3BIWY7FrESS7yLG1BFyU8wsX_QclOw,589
babel/locale-data/luo.dat,sha256=dmxwIb10UL-N2Gx5ymbXU55MakSpMFCGneBfBu5k6f4,15885
babel/locale-data/luo_KE.dat,sha256=NEKNpjQX9ul04z2QZGvlKaYQEpG7qpLnz0fraetUD2w,609
babel/locale-data/luy.dat,sha256=zIdy-rfqrSkpjJVvZEWb5Eh-kvFHltSyVR4Rr_d-jHk,15859
babel/locale-data/luy_KE.dat,sha256=3uCT5nrrTWh8Qcd2-x0vAMbsqdBfLbVNllWdTBPXVk0,609
babel/locale-data/lv.dat,sha256=AxTgcKRkQs4L0G18VPpwyKW2f0pgFl1aB3e0AAIBVDM,214988
babel/locale-data/lv_LV.dat,sha256=DVQGeBkn2bfyW4oBFSk-FG5YDgYoPrcy4P1i2msqbKw,608
babel/locale-data/mai.dat,sha256=TGGQKi7YoVCYMbQcgUkYvWVeHeqPda-wkHv1tdh9ElY,14710
babel/locale-data/mai_IN.dat,sha256=lZ93VuH0KWuLZAwFYQOlGidLcq19FwAh5FcTkbmWHIQ,632
babel/locale-data/mas.dat,sha256=VAYNJuZSIHxhdo-sjuiIa8lLzVywzE6Pt8WnKsmkjmw,17297
babel/locale-data/mas_KE.dat,sha256=H37wvJs04-E8bNfKwZQhqeDajPo6SvpdVwuo3PyJ1AY,609
babel/locale-data/mas_TZ.dat,sha256=9JwDj_IR1VKGVgWxstev2mrAXxIodBYOH4UDM6Q3q1o,611
babel/locale-data/mer.dat,sha256=OLCqftXJD2C30EM8YVyuLO6qPnMb1PSzlwTYmmpc6wM,16088
babel/locale-data/mer_KE.dat,sha256=99Q8eh6kJb1wkSHx_J0OroOC7WZ23Gp5IGAFc-NBQpc,609
babel/locale-data/mfe.dat,sha256=W4iO82UorOHb30ajoWPGnDjhjIWUIIr6HEbcWAlg8yo,15117
babel/locale-data/mfe_MU.dat,sha256=TFnNwSIFyDkJUAVbw4Y2RyGH5uG4nvbKg8uNubPWXpA,590
babel/locale-data/mg.dat,sha256=N388wocbzVzKtwYxGmUZRQ_Xfqdl_8pfhN2EVs191kU,23550
babel/locale-data/mg_MG.dat,sha256=cwl6h3aaMkDtvF9Ei8qvlnO4N1FTSI_VOEVa54g3eHs,589
babel/locale-data/mgh.dat,sha256=rXDO2pNXJITPslfwWEAO_8eTuME2YyKX7EBsBAIVBb8,10479
babel/locale-data/mgh_MZ.dat,sha256=uJyr7jkKxWqYOJ7CmhjAs8AKMOz_cWlojWjFXRj_jPc,609
babel/locale-data/mgo.dat,sha256=C512Q78AJ4yDUul2Wf0liRWjQh-GNv3XDENtmeCq58M,8206
babel/locale-data/mgo_CM.dat,sha256=T5kZuEQ7hzI616QF05Grrv-RZb59B86medbIafdhrtU,609
babel/locale-data/mi.dat,sha256=Nr8tmNRL5KurZuLLC9lojHHjScK_s2V-gCNZINEYmtY,20727
babel/locale-data/mi_NZ.dat,sha256=7o2jTlC9sR5dX2mxLI4qjVIr897Xd5keBTxs7a-_DYU,608
babel/locale-data/mk.dat,sha256=GIgzd7ykCmMFSFI0nEWeXGuQQYf-0CWvfI8cnLSgAQk,234490
babel/locale-data/mk_MK.dat,sha256=DtPgHruh_KrDRllM_vDipwCsbMWzk2bua0lfFsstTus,608
babel/locale-data/ml.dat,sha256=YRd4HGpa2b-k6G8EETwwIc_-klPwXhu5MIc9odvPtqg,285214
babel/locale-data/ml_IN.dat,sha256=_vPZnTZA2VgZoDi31tfu-tR4uRzfj-cFFVMmcB8XZgI,631
babel/locale-data/mn.dat,sha256=Iw1P5tKBu0KAl2JgsbS72Vq99Ye-t9_c5d2oZcoD3Bg,202642
babel/locale-data/mn_MN.dat,sha256=gne5zuFemBThyeemcmnNvI751-rsRwCrCBUQ6uvuK4c,608
babel/locale-data/mni.dat,sha256=GvLzWZHUo046TkH_05BSwDHFtkPTeSJVEaLG_nvWhUE,14622
babel/locale-data/mni_Beng.dat,sha256=NiCHewI8Yl4k7ylwMAZVB7mtk6TZboLcvZl22n9uG9M,666
babel/locale-data/mni_Beng_IN.dat,sha256=Lx0qjRdIKxv05uZIp8e9W-Onti_kqLE-bZiu4yEgCOU,632
babel/locale-data/mr.dat,sha256=CCiBBjuwlhte5k72NPs7U_fpEqRMcI5PPOZDoO6BedM,246797
babel/locale-data/mr_IN.dat,sha256=RkYd5HIWUeP8yK3zFCRWvxvu3FzaK0ta7H1HTQQMUdY,631
babel/locale-data/ms.dat,sha256=KFfk5lBMcmtQcWGOBAyTMqEo4Ghf5S8wg3f1MbZt-W8,152238
babel/locale-data/ms_BN.dat,sha256=CecewZU-8cYw5zaaIteTNbPOE7FqS_IGj6emNKzy3TM,1257
babel/locale-data/ms_ID.dat,sha256=WcRbSIc4fsZMR56iv_9PE24j_rQLUCYPwU7NdEDO7gw,3272
babel/locale-data/ms_MY.dat,sha256=8RsVjifl5WL8sXV_aNTdgjqquxny2SsSBXc4KqJuqlY,608
babel/locale-data/ms_SG.dat,sha256=_nfWkz663QdJKVxb2AQQQUt_Hhl9bMk7hIQcqpVSPbU,627
babel/locale-data/mt.dat,sha256=tnI8T6enmDliuHbzDfAq7Z97GDcdQ1MOfprVbqHVbRQ,78243
babel/locale-data/mt_MT.dat,sha256=2vQihLOtB1H_Ca6e9ZvGCgZ73gVlB_RBsBMFOw22cDs,608
babel/locale-data/mua.dat,sha256=k5P4AS4jugmg9aNeL2jUK6xoTlRW1DvPz2XMtumjFT0,16547
babel/locale-data/mua_CM.dat,sha256=fQm0rv5p23ity5H_pu8jhbHVdaWDpqITuEPRev9q44I,609
babel/locale-data/my.dat,sha256=M0Y81MUWIrt-aQVRHA04KZ73wPMvhd7DihayFm9pVmY,210242
babel/locale-data/my_MM.dat,sha256=9DsxnFuIB4ImQJmOXpJ0Ujt1zMSUin-KV_a7R-irE-w,608
babel/locale-data/mzn.dat,sha256=op9NwjJ4msszz0JO60nljP2t8pWh1jidWAAr9yONCrE,65499
babel/locale-data/mzn_IR.dat,sha256=nw-iEKlN_b_C0VzjCY1SCElyqMgg3jQDZ4whD-lJrpg,652
babel/locale-data/naq.dat,sha256=sfE12PpbssOaqdt_XWnykuk7rTArRVzg2DHBcAdHblY,16617
babel/locale-data/naq_NA.dat,sha256=1Mh98XoWsJgytl-IvQuMXYJlLkYQvvoQ5_rptyu5ZME,590
babel/locale-data/nb.dat,sha256=QaUKfFqj6AfimZOOiGI9DOxReUm-Jv6RysMhTdQ6XV0,210593
babel/locale-data/nb_NO.dat,sha256=bXb8K_1FTSDsqIXeZu2C0oYPCrTUpX0Y5GxKs3vXwBc,626
babel/locale-data/nb_SJ.dat,sha256=kCfUzsB6JilDQnWsVF1CFyg-7Ewq5VOdM-EWC7PJKP4,607
babel/locale-data/nd.dat,sha256=_qfCNG-XGpi-qBzGvXKmKHxVu2WspuqdH_8ECspD1xY,16312
babel/locale-data/nd_ZW.dat,sha256=szM5GcRhUeN0V1SGix3RkcgDkRNJF7D3BWJMYMOkNlY,608
babel/locale-data/nds.dat,sha256=yjSK-CUxMes8T-cNuxynP6Av2MYxnNmn23PsGguUTA0,50632
babel/locale-data/nds_DE.dat,sha256=wQlAFyMOkjMYQd9LVFTqLFt5GuntavA1RWhBf6E3DpM,627
babel/locale-data/nds_NL.dat,sha256=VPodVrFivmTiPf4v5OZ3Foc0_FaQwgRBuK7QiD8xmhU,627
babel/locale-data/ne.dat,sha256=oha7M56GETdAdz6rLbS65qD1oo_w8ab60lDltNGouec,248497
babel/locale-data/ne_IN.dat,sha256=Pc3G-flVbWniVZRu4RzMVYB099rVPhUvxUC1TiTCr8U,1265
babel/locale-data/ne_NP.dat,sha256=lhhB2jPqSBwBrxK6piIkUD0YHwGUNYdlmqlPBOJhb0o,608
babel/locale-data/nl.dat,sha256=Cz1A8HnVqxHnoUgZIy8Ob0Kb_R8a8RbA5jfgO5oLleI,215801
babel/locale-data/nl_AW.dat,sha256=6gmsswLqSrJ0XRfJJguCU0QFSU_dTpUOlPanbq5KGpM,611
babel/locale-data/nl_BE.dat,sha256=IZZy26NCmKbOgbU-ZHHeviTctmUMFxmBeOmloDOCN6c,1835
babel/locale-data/nl_BQ.dat,sha256=E39EYJYegrYGpAdLuSfkizgwgoBtfyRp-1Crb_I5PkI,608
babel/locale-data/nl_CW.dat,sha256=Ho1si5eWdnrkT1_OA7ZWxarnzgfNdUmTlJLUkynzNck,611
babel/locale-data/nl_NL.dat,sha256=kLT_7mliQl_5XhGi5lU_guTBSD6V-DUK92xhdWQxzjk,626
babel/locale-data/nl_SR.dat,sha256=VVLYKCz48vdDn000ZzlokZnD1Qr1T7Tmn47j2wPG9fQ,669
babel/locale-data/nl_SX.dat,sha256=FTjEPrmwtpu8IQVixzrdl0dEyAH524Ml8cWUxd0pvjE,611
babel/locale-data/nmg.dat,sha256=CTnRxMH09auUbe_gky2WT9aYX7OiS_3FEXvxzVDH2-U,16189
babel/locale-data/nmg_CM.dat,sha256=4wv7ftQl9xu_DkfdjxoJ97gcm-pMhM51OCXYX3CQ6gU,609
babel/locale-data/nn.dat,sha256=5rU8EBZu8-b1HBGXSeaeEiPItaYqKWupiG06aTbIBgE,179884
babel/locale-data/nn_NO.dat,sha256=yc4l2fwSD9fD1-sCQirXzrAkfxIqD_garBegapCzWs8,626
babel/locale-data/nnh.dat,sha256=jO3oG64qBHal9MpjumyI90ETg9LoNJ8CuKliElSLnHk,6766
babel/locale-data/nnh_CM.dat,sha256=azweVaEFbSCMHLptoZQ46yKcr_antYfr2pRmxIuZQCk,609
babel/locale-data/nus.dat,sha256=x_agBjiW-ZVuFQenH9AT5Rdn4TbFCJoL8n_N1AbmXBE,9153
babel/locale-data/nus_SS.dat,sha256=XQQtE8pKShDclBfN4yU1Rh_zEqrYFVndB-t2ScdGGUs,590
babel/locale-data/nyn.dat,sha256=PIizZE2pabGtsXhhg6TFiv0OlM352lMKqO2Fb-Wrizo,16275
babel/locale-data/nyn_UG.dat,sha256=i2Qcu0KO73WK35o2BvnFV9yd6dLK_p69_LtbVTMkCJA,613
babel/locale-data/om.dat,sha256=agj_n2fMWPhqLD3cZ3Pkux0y6b9r64pXXBQcl1nB5Bs,16588
babel/locale-data/om_ET.dat,sha256=MhyQf6WK9JWcW9TuiTrQwo2C8tKIELtGKBiJ5Scrt1A,608
babel/locale-data/om_KE.dat,sha256=A-EqNdXkq-j_TZK182yI6rnZYaFGGgAMyM76Q68FdG0,1566
babel/locale-data/or.dat,sha256=-VmSkqqCokDThjp6Jhc0ENRKPAU1Gp3W0NkZfbiOwUA,241213
babel/locale-data/or_IN.dat,sha256=tUmTnuoY49gDDU8WbdUCLyhv_2Yo-JJc_iTZlOJrH2Q,631
babel/locale-data/os.dat,sha256=VRCyHyBI0alMKelyuYpaQw5Z3WEEZxkN0-GC039oqf8,17627
babel/locale-data/os_GE.dat,sha256=bGm3R8Bz7k8wmb2GK_Ol83ud254rlJMbs26c1zN0w4Y,608
babel/locale-data/os_RU.dat,sha256=A2armkX5bdC0hKe6ie0WxB1IB0exTMHAZWk_0PNjFD4,668
babel/locale-data/pa.dat,sha256=oKoR7WJ7Ii8MNYowoXnZ8Lsgbgw25NPBbDYkgVEMPkc,244684
babel/locale-data/pa_Arab.dat,sha256=T7-060vg7GELKQvqGl2tfZSn5_eN4dqGS_gBwymurvE,3984
babel/locale-data/pa_Arab_PK.dat,sha256=-x9ycmOmzJ0ZIzy8J1ryO7nhRuBceY7ravgHBLEgyDY,608
babel/locale-data/pa_Guru.dat,sha256=risWFebHubet9zREjGQ-AIrrtBdOtKXo0yafgX6FhJU,1249
babel/locale-data/pa_Guru_IN.dat,sha256=tyUJVyrhCWckcBP10pfvLg2Xgv9shPpvWBaSiXg-G9c,631
babel/locale-data/pcm.dat,sha256=e7YrXsfssCZkE5qlRy1BRYA4CPZOUiWsrSoYchqqpYE,174311
babel/locale-data/pcm_NG.dat,sha256=E8wPtqkOJFSjTkU6T9V4RCZcer0Vc52PBvZ_LioI3S4,590
babel/locale-data/pl.dat,sha256=OP4mD1-bddsjgwLV2FSJgpTAnlX-2rATEv4qHqdxnQA,236139
babel/locale-data/pl_PL.dat,sha256=V62k9TTJ4FpN6KYLXzlsBBAMjHXnlNFuBnGoY9TBdDc,626
babel/locale-data/prg.dat,sha256=1lqaf6Rcgbak-QLb0gFocafAWd3MXC8rM6Zcgn5ct00,20167
babel/locale-data/prg_001.dat,sha256=m1nhpZ2Lh8TiVNCuKOVyBElL3M0Q9YIRZSC19jqZymE,1567
babel/locale-data/ps.dat,sha256=ln_T0uNZqoPpDfA0obDhJAG6L6YADt_o66ZyFxdx4l0,180248
babel/locale-data/ps_AF.dat,sha256=goJChlJTUKnh7pZiMUkZiRMMth5lshKHVDZFwKq3Iwg,651
babel/locale-data/ps_PK.dat,sha256=_MSs-UxrpD1DJvl72MouRRhRmYbE8F6-MQ6q7ansCVw,7954
babel/locale-data/pt.dat,sha256=xNKXRfbr-VRWZ1xT_p_gx99OW9I16x9Wgf2NyvP7RXk,195079
babel/locale-data/pt_AO.dat,sha256=cTQSDeLXeH4NvRK6Kydc1LB3QyQO9qOSwh4UE7Ngga0,995
babel/locale-data/pt_BR.dat,sha256=PoV5yebMbOhPwgtPQJ4qoKxOhQd3E5NCYcjjgOsvqu4,608
babel/locale-data/pt_CH.dat,sha256=aFs_w4Xa1ZxFfw0GnV7IAj92XFa0xpK9mN4uY4ynDho,626
babel/locale-data/pt_CV.dat,sha256=G5LMZLQqplotaUIvlv1iR3hgaIkwsYhMK0TXzjrmIzI,1015
babel/locale-data/pt_GQ.dat,sha256=mQbJaJxvrVnC7MaTHD8r36VIe7vTfJfudKJc5XjzFg0,589
babel/locale-data/pt_GW.dat,sha256=DTJrtZaU3wXwYHJvKkJK8BAZCcT9fSnsspJ25xuM4Po,975
babel/locale-data/pt_LU.dat,sha256=rgoGwpHgeahu8nLnT-uyVsrkD4CrWWwTCqDct0NBJmw,645
babel/locale-data/pt_MO.dat,sha256=gkvSsQQWHZFLT7SALzaYT0inBlWKH2tTt3R1KDMXc5g,1592
babel/locale-data/pt_MZ.dat,sha256=hmhU3Y7HgDXrsyTUootEEdjCO4dy8wxGRvZRezeWq_Y,1015
babel/locale-data/pt_PT.dat,sha256=C7vXAMw_2sCavBd1R62VD0n3r08CZAA49hFMT_ti9cY,99144
babel/locale-data/pt_ST.dat,sha256=YBm07Nws76viG-9eMRgf3fn-n2B0jCptD5N5s6OWySA,995
babel/locale-data/pt_TL.dat,sha256=qG2kU_auBSaMJNnYn6fYwxspLJ3OK0EpL6Qd9-Qtzi4,975
babel/locale-data/qu.dat,sha256=ghWjIAkE8YxHAfaLXgXiAKedoohrLikSTsUBiM3MnYU,107964
babel/locale-data/qu_BO.dat,sha256=CGWYNs1_AuQG3T-fYwe-q2AwDl9LAEITGRRYzc_MdKQ,836
babel/locale-data/qu_EC.dat,sha256=WewzwnSQA5llc9gb5UYy2ue5Y8_HRb1HnddOVIXcf6Q,810
babel/locale-data/qu_PE.dat,sha256=gT0fXlP3-rFSzIy6SdYVt-6viGPP79ukYHbBynqU4Bk,608
babel/locale-data/rm.dat,sha256=V12vyZyv0wD6Nu84Gl5V33yohhcm-dpGSJ2I9Ot1GwY,67934
babel/locale-data/rm_CH.dat,sha256=atueKnCga8bqm2GrXOwBjQf1ypER1IAjcv4RX6Oz0Sk,626
babel/locale-data/rn.dat,sha256=Dk-PN1zn4fjGlXZXwhEOfUmpJBu_2DlHZaDI_ins0Q0,16781
babel/locale-data/rn_BI.dat,sha256=II-eZWKAf73Hh0aGZifK2NLJvvXWws8a7Uv_2TUZ2VA,589
babel/locale-data/ro.dat,sha256=4B1VWnRbEV3KkNT3hbnkbtq2bAZdNqZIDV4DdD2AEZE,225782
babel/locale-data/ro_MD.dat,sha256=tkVS0RiHpOA2ZjCZbufnznw8aVIFj-XLoWr8whLVN3w,3215
babel/locale-data/ro_RO.dat,sha256=rFeMpPnG0zRek72AxhPuZAuJFKAuA-UL5fAyLAnPiQ8,608
babel/locale-data/rof.dat,sha256=jx81EEsVRwsEnaROsGiG4vQ1jm1wFW_V3yNpPsYZwmM,16170
babel/locale-data/rof_TZ.dat,sha256=6mZ6eFqNAqwuWCZuT7oZClLSv9eWSdGH0efVoQqxj40,590
babel/locale-data/root.dat,sha256=W5zvjlEJQD3MWdvhMHuW72ERorcD51LDhqtyXmmYARk,42432
babel/locale-data/ru.dat,sha256=TM12Pev9K3Ccwr5Z_Wj6hClv_qY_Hqk91f9nD6YSt8k,305212
babel/locale-data/ru_BY.dat,sha256=Pb4BHcT6RF6ONtgLhPcGQXQHVGj9dPrrodoI4ihsTSk,649
babel/locale-data/ru_KG.dat,sha256=iQapNW3xr7lH-HEbM7CIbdQwmUjm2Tgq3iJAMFUC7zc,632
babel/locale-data/ru_KZ.dat,sha256=OnFw_fadGFUzN0KL3WKvL0ekAwCCv5NOIhz2IFbHK0g,629
babel/locale-data/ru_MD.dat,sha256=vZr7Dz0UZlMeWWSisvdMuyOcLyreeihFbILXdQemOXM,627
babel/locale-data/ru_RU.dat,sha256=QhIIdAW2iPQ6LcErVIuxwvaBi1ku8V5-zsy1MZV1YU8,626
babel/locale-data/ru_UA.dat,sha256=ZALhQpV7aWxa78cxM54zedaE6-0YNGkB7NeL5BAeHOs,1747
babel/locale-data/rw.dat,sha256=OJ5hT_uLwSOdlkjZkrP5WmriCJTqE48b2bJTS4cRV6g,16215
babel/locale-data/rw_RW.dat,sha256=G6ta2DtZdiILzTdyZlXTCzoL-oRoF1gekRFCmN_dEyg,589
babel/locale-data/rwk.dat,sha256=uZEibGF11hKUyRFeC3IcSTrxMuWJsK9Z0c2VtykOGu0,16057
babel/locale-data/rwk_TZ.dat,sha256=RtQRCyT2DbqRtc4uzP8Nna10O8KouCqtbtSxCJ-PukI,590
babel/locale-data/sah.dat,sha256=eXy9sdtMoVJcQM3C1-m9w8iGOzgqVguyHD1b_J_Xg78,48180
babel/locale-data/sah_RU.dat,sha256=-Hi7VNsxTYaC-4G8qYQsnSPVMc5jXBYQJBvd5UeC-lo,627
babel/locale-data/saq.dat,sha256=T_ODjjLim-CJKf2XIGBOfuEGtEqeN4I4VbyY6N_Rcl8,16455
babel/locale-data/saq_KE.dat,sha256=uHKDZR4LUK5CGvcVC-dIpdYM3uY1KXVh6vkAOnOrc-w,609
babel/locale-data/sat.dat,sha256=FRyhxW7p0BSz3oG0GLb96hg4e49rufWbNgOX5HifceM,12597
babel/locale-data/sat_Olck.dat,sha256=XNqbalNvgWolPY1M9vZXPpLzFkzYzjSAvKtoP1x5oBs,878
babel/locale-data/sat_Olck_IN.dat,sha256=Rx6KNBVSK2m0PvmKzotwxqBIp30-b5dCQU5-hqSB8tQ,632
babel/locale-data/sbp.dat,sha256=N_zsOwROvCa1Nd1geNViDAkUsWkkPLDrpfXFmjJBwII,16479
babel/locale-data/sbp_TZ.dat,sha256=myr2BmLmSpSCCyRFCjm70nQfdeUAopZ29zxfemg6F8c,590
babel/locale-data/sd.dat,sha256=QoU7MJunjYKrj_TjLuMS6x0BuFxAsE4UcXnJw5XCrXk,194219
babel/locale-data/sd_Arab.dat,sha256=EOWPc5-ACgE6NQEHILMBA_BP6mK35sTdUEEuEFCv748,852
babel/locale-data/sd_Arab_PK.dat,sha256=pNtPPmwu0jQK9V31EOv-lVoFiYwf1iHDxJmB5NNIZzU,608
babel/locale-data/sd_Deva.dat,sha256=0Imih19CK7Tq6YdIazJNgJJMZPwPjKXSo0xFXWnliTA,15216
babel/locale-data/sd_Deva_IN.dat,sha256=Uei2PSaYXixwn6VPwb7xeFMXt8I_jyM_myr-8lADGVs,631
babel/locale-data/se.dat,sha256=cGes_DRLmVtVV0bjj37-QC9s_zALE7zEfrseAwvUhU4,72353
babel/locale-data/se_FI.dat,sha256=klpIv_TDIAH88KnWG6g9AUWvdhmwMSKaSfCC0qPu0tY,46574
babel/locale-data/se_NO.dat,sha256=k-BEm9_tnVXbt-H7pAtUU78dZfuiq7-cTcbsgsqISlg,626
babel/locale-data/se_SE.dat,sha256=BxFV9gNTLfUF3ibsRvgeuRnuDo99396qMA-89tpdEFY,667
babel/locale-data/seh.dat,sha256=0qAVFIAc1GZkYBCp4DP5DM1G2QfcfI0EvPQeP7Uci7c,15910
babel/locale-data/seh_MZ.dat,sha256=feukobIWsGC_o5s_qb0UgFI7gzVCrNSydoRaXs0NUZ0,609
babel/locale-data/ses.dat,sha256=X5NMBmnvudU3XqhKZWH8l36b4RFIf1vXuhLSIqUbgfI,15998
babel/locale-data/ses_ML.dat,sha256=O7stcUKOw8ZkGmgnPqSWBCxPFA3_aDIcHZGAT9yRrtw,590
babel/locale-data/sg.dat,sha256=GQNOEIiWV9wnrISbr5uFtFK9gvWoNNF6G5gUta9V1Io,16635
babel/locale-data/sg_CF.dat,sha256=dDZMdfhJBfy2ShSVhAopU2nIEEBnTssu3Vji2v9SpHg,589
babel/locale-data/shi.dat,sha256=yI6ilP0U3O4u2IGEJcHnEpFxPPqVIuZJRSAOyr-axtA,22036
babel/locale-data/shi_Latn.dat,sha256=rqL_ruNuCpetsrf_JIL02XNbqkoBDDZCC-VjchfA8mY,15618
babel/locale-data/shi_Latn_MA.dat,sha256=blTyj-JXuFz7wgjLjUC19rH4Emj7_-TOtMvBKb7qAus,590
babel/locale-data/shi_Tfng.dat,sha256=PmToPVEqibydgF2nxMw21pujbbqf4odWn7GlEqQL2u0,947
babel/locale-data/shi_Tfng_MA.dat,sha256=blTyj-JXuFz7wgjLjUC19rH4Emj7_-TOtMvBKb7qAus,590
babel/locale-data/si.dat,sha256=zxzkUZZtc4uVe-G1jMp1pAtkW726ZJ872nLo6ZTSaZE,244850
babel/locale-data/si_LK.dat,sha256=2k1GulXssuQkuKMmj4J74iAYHlfh507gp6l75lKDJwg,608
babel/locale-data/sk.dat,sha256=BrxSaUkE6hCEqX1etX6_C6dfuCmSNAJvtH8IFqGzHAE,256860
babel/locale-data/sk_SK.dat,sha256=b8ugTdqk71Ge03FdSEhnOmH0wP5XeDSI40ys2hGovNQ,626
babel/locale-data/sl.dat,sha256=n8vsb9jixRFE_J7WJYW4w1GehxoGJnNHgGjFZFlmpEM,241874
babel/locale-data/sl_SI.dat,sha256=V1oy5BlaUDjrX8b0jv9LK7Mgg4Yx84u-Iry4j3M8LYc,608
babel/locale-data/smn.dat,sha256=iTOwk1p0Y-9jOGDIEuTz3-jwEbC3neN1n4xTKSIfyyY,42674
babel/locale-data/smn_FI.dat,sha256=3FaHTO42uw4LsL2o9CnKHuk86tRNGaorVeYh7SPONWY,627
babel/locale-data/sn.dat,sha256=pfT3kmp47TnfQGNvcsWF43YQaoHOtkXbRFvt3leN80Q,23252
babel/locale-data/sn_ZW.dat,sha256=R48ZM21PI5bjz154uuK-wccs9d-M9YMdiQLtX-rbG5k,608
babel/locale-data/so.dat,sha256=6KnXbRvPY84Ag4eBiPzLfjRvHuuuiu-ZJo5bA7qL7cU,153105
babel/locale-data/so_DJ.dat,sha256=CWxbbQZ8iogPci77q4bpbWHOFBOKISwLOkqixb-TqRA,629
babel/locale-data/so_ET.dat,sha256=JKebALALzWT58gltRAjWVKl3LqFGiy1iD-nbFFsvfZ8,628
babel/locale-data/so_KE.dat,sha256=Ooas5zUI6mtrajAybdc2YQyFlS4RFNUxeXqT0pUQ2fo,1181
babel/locale-data/so_SO.dat,sha256=WtbiqTAukA-EouWNpajrPLIyRqUNmh0A6bcbXQywwqc,589
babel/locale-data/sq.dat,sha256=Gj2LUjCjKeRcNUF86vv8cbX1j_VPUmSBjQxHP8HETHQ,175659
babel/locale-data/sq_AL.dat,sha256=061xsDws549-glqPY5No20svTFqs0pO6Cn9oUtwquVA,608
babel/locale-data/sq_MK.dat,sha256=8D6OAIV9T29UVST-ZD0zjtgYgrScEKaeZDaf8fJBk4E,1181
babel/locale-data/sq_XK.dat,sha256=nFWTBnEjpW6ux-oMbSov3ta96NrvwhjtQ80viYXFfKY,1160
babel/locale-data/sr.dat,sha256=PtgWUhY4n2x_GzfWOjZbYvPrliijqBNBO0KBJmk5v0s,277763
babel/locale-data/sr_Cyrl.dat,sha256=PqtXXFd7yu1Bweljv2UkBHFUYVPlIFY2abO3lfl8t4Y,1957
babel/locale-data/sr_Cyrl_BA.dat,sha256=dShsmp6LRaSkv8ASEvrPf6KeovR0BHLg7DUu-CJp8_8,4710
babel/locale-data/sr_Cyrl_ME.dat,sha256=kT_iSmJ3fWaM4XDKSSCWgfT-zdxMxm8Mg3hISHZC9m8,3873
babel/locale-data/sr_Cyrl_RS.dat,sha256=AAJ6aaa8D73J6na-RIjuqhEfLWKReHLwzYavdFaobhs,608
babel/locale-data/sr_Cyrl_XK.dat,sha256=hXdgzDWE5gDdWBbiNBaEHTl1hjEqqtd9dmnFXPFRkfc,2756
babel/locale-data/sr_Latn.dat,sha256=uGrDTKlSnMMQg7XORMw8Lg5v17HB-mNAFlD0Kczta1I,230252
babel/locale-data/sr_Latn_BA.dat,sha256=ohHi1ZC9VzvqIIRkemStv_FVS9U5CPiLr8I2QUCt0ag,3994
babel/locale-data/sr_Latn_ME.dat,sha256=gPaIdFD34NWU-69gmIOuCw6b8klXWs6RJUeU8ihcSqQ,3074
babel/locale-data/sr_Latn_RS.dat,sha256=AAJ6aaa8D73J6na-RIjuqhEfLWKReHLwzYavdFaobhs,608
babel/locale-data/sr_Latn_XK.dat,sha256=_BATf9SVmbg1Knn5nGG5srTFj0suBkMuTG5gcF46MO8,2194
babel/locale-data/su.dat,sha256=tjQYu8CY2Y_Hz12pRczCaxX7LrRJK4xOvYthqwFgVBg,12452
babel/locale-data/su_Latn.dat,sha256=AoqRqUqiJYE1G-ZRCIIhkYSRQ8s71qDefLwv70XrgZA,718
babel/locale-data/su_Latn_ID.dat,sha256=Hi1QalxGc49vElzHunyzz1Mfc6_1KgzXkGjcj04mq8c,608
babel/locale-data/sv.dat,sha256=DLFxWbJiUU2870aLFBVVDW3_OEWpE2yL8p-Q1h0J9aw,221078
babel/locale-data/sv_AX.dat,sha256=4LqqSZFfMV2iRBS5TyTdWWKfdoN_ahxotRUbyuoaX8g,626
babel/locale-data/sv_FI.dat,sha256=WDEfn4f8iqbtpxYsGA_6mDW_jk4gFolp7gU58aRKt1w,2584
babel/locale-data/sv_SE.dat,sha256=OtNxgFxUFAH5mYJ4yGqp_wLMmnMu9mwVnvJ57BKUOKs,626
babel/locale-data/sw.dat,sha256=LxA1OYCK0j4dbhSCT8KgSwrsF_wA94vHs30XAP-vQ5A,179207
babel/locale-data/sw_CD.dat,sha256=6HlO0ltwzAj1j8ns4jxCI0P36r7MauG7h5EQqpQhFkY,2660
babel/locale-data/sw_KE.dat,sha256=GKdU3qzg_MR2lQE7gRkAZqHA8gD0nBUhgSqNOkS0fbY,35934
babel/locale-data/sw_TZ.dat,sha256=rRGQVQ_Vp0bQ6_KnZTZDn7YDY5HDNiIsUMFLSnKD6nA,589
babel/locale-data/sw_UG.dat,sha256=GN6zqbWL7dor3RJWaJ7EwhMX8BIA6HVUzN7xWswPgbU,633
babel/locale-data/ta.dat,sha256=ytGi4rbhhefQHdKzbgWOCheyBDU6H6JJGOYHWJvuMxE,263466
babel/locale-data/ta_IN.dat,sha256=1D_ISTGx78nHVtYBoccQpH04o6CCL5dIIGRm4RWkMDg,631
babel/locale-data/ta_LK.dat,sha256=sHPH1bp1O7mbhnp0WgIyHfKckEIATHbtNtSGQPdbVSU,1181
babel/locale-data/ta_MY.dat,sha256=SVPpzoaW_lDTyUaLLP05lUjx5bnkv5G0QFrnMC3WRGs,1238
babel/locale-data/ta_SG.dat,sha256=9e057sNK17LS7GQfZ74fzkPmHndampHvierKZNX6vT4,1257
babel/locale-data/te.dat,sha256=QZ7c7f3RbUpaJfZ7fr4tWqrUowkXNnCBOlGGAfH9y4I,262280
babel/locale-data/te_IN.dat,sha256=uRakP3CRkBJKNlCxMpMDOGOi5aeiq5OiLxBbUWn2NZk,631
babel/locale-data/teo.dat,sha256=67b09CdiUgLq-6n63Ox1CvkkxExMIWAJh82HVSoyah8,16671
babel/locale-data/teo_KE.dat,sha256=yZVnSmYqZ77jAPlyuyY_yivRqbj4dwbb99MW52EJNvU,630
babel/locale-data/teo_UG.dat,sha256=o5PkAO5zi67Lxbh5vKaOJF6qerkAFvJu3RrQ0iHlmwA,613
babel/locale-data/tg.dat,sha256=D0Rhojw2aiooD1a0COKgtkD3nvxhPwkvOaBJuqoQgRQ,36303
babel/locale-data/tg_TJ.dat,sha256=ge5GlIElwu5VzdLeWCCEz5A2-F9ihtOH9Ic9k5ii4wY,608
babel/locale-data/th.dat,sha256=qvpR1DSefc-FR-cvQXpDw_sADav-_oRt0RLnBBZd_eU,235280
babel/locale-data/th_TH.dat,sha256=aQd6NJ_y5ObTF2koe4cI_s_mVdG0c7JfaiivpLN8M50,608
babel/locale-data/ti.dat,sha256=wLPieG3yJtUVM8VrrwEjhqnO8El_xTQaTlgsTL2qFP4,73036
babel/locale-data/ti_ER.dat,sha256=c0K7KojJP6dMJAGn1SRfi6lmNy8BvijIUNC4m9gqc2I,958
babel/locale-data/ti_ET.dat,sha256=vT6Tl0BBaMupoRD4fb3kmM_Mufx2EGuqP460HO_Bh7Q,608
babel/locale-data/tk.dat,sha256=CJVPDLCt8C3T8GhrgEsnnWTnwoWm2RcNDfvQ4bjDZSE,167927
babel/locale-data/tk_TM.dat,sha256=06szpphDghkTBac9nMge32AzZKeQdxXeZjsXF0X1-Nk,608
babel/locale-data/to.dat,sha256=ZSHEXy-JD4xKehjprq07mYbMgXKEy0j3kA0P6RKiClw,166458
babel/locale-data/to_TO.dat,sha256=UaceT8b6KsoNoQd68finqhXDLVr_4GtcGFsgTwJaYTc,589
babel/locale-data/tr.dat,sha256=xeuwfXivD-H9zszsBigvaEqV57cp7jkngzv8es_trSQ,209226
babel/locale-data/tr_CY.dat,sha256=sHH4XyRX7XNKfRzQPuDEVNlfBCdyg7jy-bvyP7yPTBw,1184
babel/locale-data/tr_TR.dat,sha256=lUyZY1ya9qqjmhdYhAkP6j4V953QWx_cC16GrVZCaYM,608
babel/locale-data/tt.dat,sha256=szBjiq0ZOV4gwKairWY_axw1_Q2WuV9CO9XotiJtI9A,33588
babel/locale-data/tt_RU.dat,sha256=MqxY1dPvSLsO7huGCYIhaTqwfGw9qHzq-oUt3VwVPyU,626
babel/locale-data/twq.dat,sha256=AW_qiBsx_oBOrFVQ9yvnHEms3jqkxNFRY0hYF8ErVrs,16171
babel/locale-data/twq_NE.dat,sha256=yv89EP--ZBgtvC_Vfy7UN37T7OktzntjGpRKfo89AW4,590
babel/locale-data/tzm.dat,sha256=oVsKo32vfNoupn0mwkP0QmlYCh0irKQvAoIhauxhD1c,16149
babel/locale-data/tzm_MA.dat,sha256=jOAK87vFm-WpZK130vSvCLE8fWxfjeyPs_G1elc5TMk,590
babel/locale-data/ug.dat,sha256=N05TG_7CnVabGWTTHoVWlxPQ6MseNb_y3_YI2_pLiRw,128534
babel/locale-data/ug_CN.dat,sha256=EMQBXnc07gL0zsdKDerIo0Sl6DtZVJaapUCoOL9V22k,608
babel/locale-data/uk.dat,sha256=GfQIacOGkBh-WmYgbPOlmiu6mLYADuzVi919Lk2p9Ig,315834
babel/locale-data/uk_UA.dat,sha256=YE9ivxv_h3qwHTeFyWTV4gt5JaYgYdKQTLHHadAQQT8,608
babel/locale-data/ur.dat,sha256=q4apEHeXrfxzm0HJrKkdHJd6Y54ZUwFC_6lZzzkxvjU,197918
babel/locale-data/ur_IN.dat,sha256=YSaoN2o4C1InByihAFCDOBE_HlCt7xkRl9lyOrfoCTk,12595
babel/locale-data/ur_PK.dat,sha256=VXzQfSYJAIlzcDjPva4UM67jhqIwDUqAVNRGB2YPcfI,608
babel/locale-data/uz.dat,sha256=KvGhlHDdpSZtTcqMENPN-zCjmsIIIwu8o1Oc2MTHjDA,173590
babel/locale-data/uz_Arab.dat,sha256=WrXXAaoKSVPU_PeGQIGU0jmdaVzPtkZQxzhtOlH7VjM,4111
babel/locale-data/uz_Arab_AF.dat,sha256=ONnsHyim0Q-GRD6BAHPTj2Ri4aR41EB5HWhJQrKKXAU,651
babel/locale-data/uz_Cyrl.dat,sha256=_q-bVsz4aZwG1LVqQC5rN_aCn5o-ocAJ1zD8eqVOMj8,98924
babel/locale-data/uz_Cyrl_UZ.dat,sha256=D2g0Iy4gME1-ZrXDPgcs1VlFNW1FWKRD607VKgUsFwA,608
babel/locale-data/uz_Latn.dat,sha256=wkn_uCtrZQx7Ut7_pTVXVU9X956I30Nr4seILvPnZ_o,1265
babel/locale-data/uz_Latn_UZ.dat,sha256=D2g0Iy4gME1-ZrXDPgcs1VlFNW1FWKRD607VKgUsFwA,608
babel/locale-data/vai.dat,sha256=Y2F73JC5rT_LyIvVXYCJEMwnLE0YwmOTiEh-ZsUhx9A,18988
babel/locale-data/vai_Latn.dat,sha256=qys9gT6Krcg_09r52_SMMrAEyI2XzngIuKlPKWpZnhg,14989
babel/locale-data/vai_Latn_LR.dat,sha256=mFG8a5AB_Cnv2lwGAVg5SxhF0lgkrS4vB3UdqB1L8Y4,590
babel/locale-data/vai_Vaii.dat,sha256=rZi5j11eMQeE9MzTRK4Gl5EhqEy_X6o3V06k_E4ioOY,666
babel/locale-data/vai_Vaii_LR.dat,sha256=mFG8a5AB_Cnv2lwGAVg5SxhF0lgkrS4vB3UdqB1L8Y4,590
babel/locale-data/vi.dat,sha256=pfW__H9jiCPiorXwYFxodzP4CjbHhGh6AwCye8RznAQ,162325
babel/locale-data/vi_VN.dat,sha256=hn8-pr09TFtQwAvAau15ETGT4bmPay2o_LGOvLA6Bsk,608
babel/locale-data/vo.dat,sha256=nmu1bYpYLcTQzd5w4nGKygzPddIlbRfHZIJCYPT8bIM,5225
babel/locale-data/vo_001.dat,sha256=sQnDTedm-Ec9H8JaIqP3ZLpsAvJOr8GRr1BADz4lNRc,823
babel/locale-data/vun.dat,sha256=_9UYsm8lOz6Q0DNjRQDTaZU8wR5sv5PS4y-oDn7siqE,16067
babel/locale-data/vun_TZ.dat,sha256=1lrpmdkRCqdDzX4Cel249MWWRGVDmubt9OiMf6Qsrnk,590
babel/locale-data/wae.dat,sha256=hLrXzFfFW-jPsSrA0ZFzjZdhV60X1etEZOKlb6f0Dbw,28660
babel/locale-data/wae_CH.dat,sha256=5fOXgR-rrWoIYbf230jds8iOj1dsbvNW2Qv2XBNaCto,627
babel/locale-data/wo.dat,sha256=btSGt-pyMUGCK1wCUkG_9dccsvl_w9qPmAv1OV9Q5Iw,25698
babel/locale-data/wo_SN.dat,sha256=ryU-hZj708JELq9ldfmRU8I2EDihWGM_6v8QQY4qRuE,589
babel/locale-data/xh.dat,sha256=FILfYkTCJIqiqqcEo29ID_9AxcUGdWePyXJnbOI_kGM,15055
babel/locale-data/xh_ZA.dat,sha256=LPaE6z0iRUaCSVd2CeuJzy_0GfhGCs2KgtYPHqLL18I,608
babel/locale-data/xog.dat,sha256=TU2N1WKiMwbPTe7tF_Hj300hT6Hn-_f5H12h5HU1Xf8,16555
babel/locale-data/xog_UG.dat,sha256=5B_ozUekB9sXcaT_-7brqH1nfv_XEP5CnB2PG84_JlM,613
babel/locale-data/yav.dat,sha256=R_FIP-S4MiI3XULYddxjJtYhFPPwILvVDbHd3S6C1dc,15302
babel/locale-data/yav_CM.dat,sha256=y9SNKPJTMwmSdGc0MO-vn7hUeK4arphzRDp2zBFYigs,609
babel/locale-data/yi.dat,sha256=XNca4NO0IFPfGXT-E7HlW-9BOGi9ZvJSxrx8O13tHUs,30314
babel/locale-data/yi_001.dat,sha256=wdIcCz3ZcZHFJqT28rBWo4iYmRffPPVWpxod_13KIYY,885
babel/locale-data/yo.dat,sha256=a1uZep-5fpoV77z6eDBYeaaWw3c8AMXswNgYdHqpyWs,68429
babel/locale-data/yo_BJ.dat,sha256=oy0uMgotXzVGUeC1paiyc0YEoxsXHf1qQ5eYr9dl5TE,34481
babel/locale-data/yo_NG.dat,sha256=vVCmItRDqtBEzSXYDsXGoiobciBukV84o_LpnAZRiDs,589
babel/locale-data/yue.dat,sha256=kgkc69B6aeh8K1K-iNS1nVb_xi4IWHUcIG9CgIWmjz4,183333
babel/locale-data/yue_Hans.dat,sha256=aXi0gwVQezQ6RZDRAd09wLWhwlKUTfrSyDF-ZjEIJvo,183225
babel/locale-data/yue_Hans_CN.dat,sha256=0SEPKM5hD5K5TXbakL6_Q7mE-Te_ea6eOhSy1uwwJXA,609
babel/locale-data/yue_Hant.dat,sha256=_BWk9N_79PzY7EPWu-O_M8j1ISYhkN29HEbuo-i0AoI,1279
babel/locale-data/yue_Hant_HK.dat,sha256=8iSo-1wkebNgS7h3iCFLd9s-nW8TuQ3-4UFUMUEmbMM,609
babel/locale-data/zgh.dat,sha256=52E-cKoUGWnMZMAeTavTmgj666axYtF6L9u5tHn1JYQ,30498
babel/locale-data/zgh_MA.dat,sha256=sIGElmHSGowAduz_ykRA_-PotBTJaOqmBtRhXJ_swJc,590
babel/locale-data/zh.dat,sha256=ffkBVzcOpHqTELueQekXu2xkPOiFnhAH88ImnVX7ZT8,181044
babel/locale-data/zh_Hans.dat,sha256=-JH1KTn0ibMkBj6gw2V2Q6ixLBL5x0n7B7AkCpU0TRI,1278
babel/locale-data/zh_Hans_CN.dat,sha256=sTrrw5ttuMLr70IDoBM02f7vGVzuB-0gQNQK0IDNyXA,608
babel/locale-data/zh_Hans_HK.dat,sha256=gy8r4jqxvwhNf1BUPjnkRmJGzweemtJ1ylkumHBmUgg,3138
babel/locale-data/zh_Hans_MO.dat,sha256=C1N9WHRhsqPiq-9su32Ar1ZcBpN5P0pTJapGAFRCOrM,3270
babel/locale-data/zh_Hans_SG.dat,sha256=qCiYdP4MAwcVyJyJ-YJ9B_kvsNUxu9nWkeR3XwgNYpQ,3466
babel/locale-data/zh_Hant.dat,sha256=dzsvtbyAGj7fzzCl2xBJ010IQzq9IJsi59vW7Qf2d9Y,185029
babel/locale-data/zh_Hant_HK.dat,sha256=71VE9xyc0m5Gh4gJ1STha37iLm5lvpISG3fZsKFuQ9g,56895
babel/locale-data/zh_Hant_MO.dat,sha256=gTS5IdcTsRoMwAnANseIbj2nyflLj0tBQn2-4AFAaf4,630
babel/locale-data/zh_Hant_TW.dat,sha256=QYchBgi8JmeZgs7rQrUYn93CNNL9aavsOWYzAaTndwM,608
babel/locale-data/zu.dat,sha256=8ONW9eqg4gnEisbcMQeDl71dCVY1ckM4nJOXat9rPuM,167684
babel/locale-data/zu_ZA.dat,sha256=YO0tFWUiAdda0x3XEoL98oPNFGRLuk5OZ8DaR3lUX38,608
babel/localedata.py,sha256=bp9ZCXKgvJ08oOwrOIeknZ3Ks7QVi03HS_IhIDOf_ow,7931
babel/localtime/__init__.py,sha256=43mNcGAGljnTjngbhTMl6xhfeHMR1Sabby2O-Deh4gE,1721
babel/localtime/__pycache__/__init__.cpython-39.pyc,,
babel/localtime/__pycache__/_unix.cpython-39.pyc,,
babel/localtime/__pycache__/_win32.cpython-39.pyc,,
babel/localtime/_unix.py,sha256=P66o3ErKXzhFvj3e3Qk6MBS7AR0qsDqSQclIAMHKp18,4801
babel/localtime/_win32.py,sha256=dGzhQ8AlY5iItSd-i3Fi2O3YWuVJ83PFSWe7EG2BaBg,3086
babel/messages/__init__.py,sha256=mYEtObYlyGT9zKJog4IjXFN-au3uxnc16wg89edsMxo,254
babel/messages/__pycache__/__init__.cpython-39.pyc,,
babel/messages/__pycache__/catalog.cpython-39.pyc,,
babel/messages/__pycache__/checkers.cpython-39.pyc,,
babel/messages/__pycache__/extract.cpython-39.pyc,,
babel/messages/__pycache__/frontend.cpython-39.pyc,,
babel/messages/__pycache__/jslexer.cpython-39.pyc,,
babel/messages/__pycache__/mofile.cpython-39.pyc,,
babel/messages/__pycache__/plurals.cpython-39.pyc,,
babel/messages/__pycache__/pofile.cpython-39.pyc,,
babel/messages/catalog.py,sha256=lQUVsuoNKL8yy--XqrHWX2AEKk6_qj4vpTNwuJJEtOE,32291
babel/messages/checkers.py,sha256=tCqwgZpzwJwhouwSIpKNHW0m48338W1omEllOFDxv9s,6085
babel/messages/extract.py,sha256=V-_eVZzxtANiue1ST3Yp6Kv1N3z5Swy4eu2_GuEt2WU,26474
babel/messages/frontend.py,sha256=skgwlQry-3Sbf2v448eL3KxwUhd4pFRLa_5kkilKgvc,38729
babel/messages/jslexer.py,sha256=NU2h-nZxoNnycllWViSv3X-jB_p1Vpw53uvqVklsMQ4,6334
babel/messages/mofile.py,sha256=2JBYxNfuBqO35U-H_VqB1rgW2gocS0Has1YgGZwYxLI,7204
babel/messages/plurals.py,sha256=GOD89ObneG4--qFbt_PQOvpEY-72o9LdghOsztNgRiw,7206
babel/messages/pofile.py,sha256=fMUcLVi5-vIyKfDjkOde6He1QTSr0n4iYEis560zHSw,22146
babel/numbers.py,sha256=-V_bQC6whhEalzEmYkFtvydZB0_knjLDgzu2C2SzGbM,39872
babel/plural.py,sha256=bo-QkRGinKBBF58KgJoE7aUFKTG9PrFYtpTYvOSfYMw,21314
babel/support.py,sha256=uO02VsCqKTcgBopi1RkQGhco2gRTZi4q3hlrbhgnxsw,22622
babel/units.py,sha256=QPa8jqWKdS_dB4zTp60ffty2PClONBdlbKc885hb2RA,11291
babel/util.py,sha256=o6JpnIY9-dhVdTgli0MAUknu4haiag_swAcLYdi8wVY,7576

View File

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.36.2)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View File

@ -0,0 +1,22 @@
[console_scripts]
pybabel = babel.messages.frontend:main
[distutils.commands]
compile_catalog = babel.messages.frontend:compile_catalog
extract_messages = babel.messages.frontend:extract_messages
init_catalog = babel.messages.frontend:init_catalog
update_catalog = babel.messages.frontend:update_catalog
[distutils.setup_keywords]
message_extractors = babel.messages.frontend:check_message_extractors
[babel.checkers]
num_plurals = babel.messages.checkers:num_plurals
python_format = babel.messages.checkers:python_format
[babel.extractors]
ignore = babel.messages.extract:extract_nothing
python = babel.messages.extract:extract_python
javascript = babel.messages.extract:extract_javascript

View File

@ -0,0 +1,18 @@
Copyright (c) 2017-2018 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,85 @@
Metadata-Version: 2.1
Name: aiogram
Version: 2.14.3
Summary: Is a pretty simple and fully asynchronous framework for Telegram Bot API
Home-page: https://github.com/aiogram/aiogram
Author: Alex Root Junior
Author-email: jroot.junior@gmail.com
License: MIT
Platform: UNKNOWN
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.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Dist: aiohttp (<4.0.0,>=3.7.2)
Requires-Dist: Babel (>=2.8.0)
Requires-Dist: certifi (>=2020.6.20)
Provides-Extra: fast
Requires-Dist: uvloop (<0.15.0,>=0.14.0) ; extra == 'fast'
Requires-Dist: ujson (>=1.35) ; extra == 'fast'
Provides-Extra: proxy
Requires-Dist: aiohttp-socks (<0.6.0,>=0.5.3) ; extra == 'proxy'
AIOGramBot
==========
.. image:: https://img.shields.io/badge/telegram-aiogram-blue.svg?style=flat-square
:target: https://t.me/aiogram_live
:alt: [Telegram] aiogram live
.. 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/status/aiogram.svg?style=flat-square
:target: https://pypi.python.org/pypi/aiogram
:alt: PyPi status
.. image:: https://img.shields.io/pypi/dm/aiogram.svg?style=flat-square
:target: https://pypi.python.org/pypi/aiogram
:alt: PyPi 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/Telegram%20Bot%20API-5.3-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api
:alt: Telegram Bot API
.. image:: https://img.shields.io/readthedocs/aiogram?style=flat-square
:target: http://docs.aiogram.dev/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/github/issues/aiogram/aiogram.svg?style=flat-square
:target: https://github.com/aiogram/aiogram/issues
:alt: Github issues
.. image:: https://img.shields.io/pypi/l/aiogram.svg?style=flat-square
:target: https://opensource.org/licenses/MIT
:alt: MIT License
**aiogram** is a pretty simple and fully asynchronous framework for `Telegram Bot API <https://core.telegram.org/bots/api>`_ written in Python 3.7 with `asyncio <https://docs.python.org/3/library/asyncio.html>`_ and `aiohttp <https://github.com/aio-libs/aiohttp>`_. It helps you to make your bots faster and simpler.
You can `read the docs here <http://docs.aiogram.dev/en/latest/>`_.
Official aiogram resources
--------------------------
- News: `@aiogram_live <https://t.me/aiogram_live>`_
- Community: `@aiogram <https://t.me/aiogram>`_
- Russian community: `@aiogram_ru <https://t.me/aiogram_ru>`_
- Pip: `aiogram <https://pypi.python.org/pypi/aiogram>`_
- Docs: `ReadTheDocs <http://docs.aiogram.dev>`_
- Source: `Github repo <https://github.com/aiogram/aiogram>`_
- Issues/Bug tracker: `Github issues tracker <https://github.com/aiogram/aiogram/issues>`_
- Test bot: `@aiogram_bot <https://t.me/aiogram_bot>`_

View File

@ -0,0 +1,237 @@
aiogram-2.14.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
aiogram-2.14.3.dist-info/LICENSE,sha256=dsHq3STDQqe8E_gn3RwwGQ3JglVUWW2eSjviR0u3kDk,1064
aiogram-2.14.3.dist-info/METADATA,sha256=S1bF0mNu5rfquDHQo83G2lw18-cpXc5lAN7ut_7xrb8,3460
aiogram-2.14.3.dist-info/RECORD,,
aiogram-2.14.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram-2.14.3.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92
aiogram-2.14.3.dist-info/top_level.txt,sha256=glLhE1GuNFQxEVRhRIm2lR2YxenVMqt8Q31x2ewaZQ4,8
aiogram/__init__.py,sha256=tjmseIDVm3WUOtFlgiiRDvoOCwR5nNbeoSFZCCdBFaQ,1011
aiogram/__main__.py,sha256=EiBMkaWdfb_fwJWnHgHAm1JPHvNHcztX1A0rMWg_jxM,1676
aiogram/__pycache__/__init__.cpython-39.pyc,,
aiogram/__pycache__/__main__.cpython-39.pyc,,
aiogram/bot/__init__.py,sha256=OLC2y5Iy7pTHPPAaSrRpk0mnb7I_l-GU0HxW16Gzk3M,117
aiogram/bot/__pycache__/__init__.cpython-39.pyc,,
aiogram/bot/__pycache__/api.cpython-39.pyc,,
aiogram/bot/__pycache__/base.cpython-39.pyc,,
aiogram/bot/__pycache__/bot.cpython-39.pyc,,
aiogram/bot/api.py,sha256=ijelbA0gbxZFBYNnxo4b_W0bicVpGrACveU1NMdyU4o,10319
aiogram/bot/base.py,sha256=qyvL--msJgdRUg7hzDHcGyKaKjyLidxIjMPIxF8AT4Y,10973
aiogram/bot/bot.py,sha256=ETM-EEJnkMYxYz5jsP2mhXuFzhwYD4rcvtRExzlWN1w,171145
aiogram/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/contrib/__pycache__/__init__.cpython-39.pyc,,
aiogram/contrib/fsm_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/contrib/fsm_storage/__pycache__/__init__.cpython-39.pyc,,
aiogram/contrib/fsm_storage/__pycache__/files.cpython-39.pyc,,
aiogram/contrib/fsm_storage/__pycache__/memory.cpython-39.pyc,,
aiogram/contrib/fsm_storage/__pycache__/mongo.cpython-39.pyc,,
aiogram/contrib/fsm_storage/__pycache__/redis.cpython-39.pyc,,
aiogram/contrib/fsm_storage/__pycache__/rethinkdb.cpython-39.pyc,,
aiogram/contrib/fsm_storage/files.py,sha256=x-V7oagYARLep7qdGL6Ob4MJOgrt94mXdYc_RSvoiSc,1422
aiogram/contrib/fsm_storage/memory.py,sha256=Jq30iYqQieTTrAhqTqGUW51xn-tzGd3z2VDj5MOeHO8,4627
aiogram/contrib/fsm_storage/mongo.py,sha256=r-JDWyXQ_WIfm8rJN2sfcSrw_7LLDs4eziXZ7GXk9rw,7941
aiogram/contrib/fsm_storage/redis.py,sha256=AOQHMS1N2lVZ-7Cuvjc-92HNnMqcX-keoq75g2ptbmI,15937
aiogram/contrib/fsm_storage/rethinkdb.py,sha256=2GsWZfiaGxYHpMy50JoSgC5B3L6ZEcg-ezUOtLW3iko,7332
aiogram/contrib/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/contrib/middlewares/__pycache__/__init__.cpython-39.pyc,,
aiogram/contrib/middlewares/__pycache__/environment.cpython-39.pyc,,
aiogram/contrib/middlewares/__pycache__/fsm.cpython-39.pyc,,
aiogram/contrib/middlewares/__pycache__/i18n.cpython-39.pyc,,
aiogram/contrib/middlewares/__pycache__/logging.cpython-39.pyc,,
aiogram/contrib/middlewares/environment.py,sha256=kwyzzhEflW6suV_R-Mw-l4_vtkb-qREZHHkghVWyxg4,746
aiogram/contrib/middlewares/fsm.py,sha256=xKOSSRMxAFOO5y5TRJbg6mYlkJa9Opk48EC3Qb2pcRI,2331
aiogram/contrib/middlewares/i18n.py,sha256=hb8kgKpNkhq1zxhkuD_R7dE5HvR_naRhlK825bnYK9g,4404
aiogram/contrib/middlewares/logging.py,sha256=Nnq4IZngEWhNdb65IK8vvYry8cbcT7Fg4sX-Th4n8n8,21689
aiogram/dispatcher/__init__.py,sha256=Nkrg2GKVGo5HXJVauYIojrR0NTgILnyKbrjOe5rVgeo,336
aiogram/dispatcher/__pycache__/__init__.cpython-39.pyc,,
aiogram/dispatcher/__pycache__/dispatcher.cpython-39.pyc,,
aiogram/dispatcher/__pycache__/handler.cpython-39.pyc,,
aiogram/dispatcher/__pycache__/middlewares.cpython-39.pyc,,
aiogram/dispatcher/__pycache__/storage.cpython-39.pyc,,
aiogram/dispatcher/__pycache__/webhook.cpython-39.pyc,,
aiogram/dispatcher/dispatcher.py,sha256=mxn7wF4pNXsWZASYoi9rps_EDUUz3VGu5Y6T7NTgaQM,54782
aiogram/dispatcher/filters/__init__.py,sha256=O-HF8XQq6NAsQb65q-bDYque0fR2cTq-p7-jN_OSUso,1127
aiogram/dispatcher/filters/__pycache__/__init__.cpython-39.pyc,,
aiogram/dispatcher/filters/__pycache__/builtin.cpython-39.pyc,,
aiogram/dispatcher/filters/__pycache__/factory.cpython-39.pyc,,
aiogram/dispatcher/filters/__pycache__/filters.cpython-39.pyc,,
aiogram/dispatcher/filters/__pycache__/state.cpython-39.pyc,,
aiogram/dispatcher/filters/builtin.py,sha256=xIjPbpqHRnAOZyra0VSfD5HwCYzJXfR7UCX_OBjYo5s,25538
aiogram/dispatcher/filters/factory.py,sha256=S6NJIKwJFzPFyOmTRd-oWCbzAMy3pzcLOb0V2I0I56A,2517
aiogram/dispatcher/filters/filters.py,sha256=A6PbDNVha29Agyx_u13U-dd5sJJubGtvdbgjkZFp0Dw,8383
aiogram/dispatcher/filters/state.py,sha256=TnWEiilNDRhdY1lKLc6H4yGnvqNB2KWF_dZKj4rtt9c,5176
aiogram/dispatcher/handler.py,sha256=nz0pstovw_JguTZR4_v29K-nMmJon4mGUkcJkxoM6SM,4130
aiogram/dispatcher/middlewares.py,sha256=kqWjVbwq0tOUNQp3FeKaoTpuYOpJzeKEALe6UksXGQo,3253
aiogram/dispatcher/storage.py,sha256=vTmnFDl7qJ-yywqx0TTFMvRTPDjZp-sZ11X8NvNe9SU,15580
aiogram/dispatcher/webhook.py,sha256=N8ofR_yeDaLsOya-RZJGocSafDM5XgwTLesnoiq1tQ8,90302
aiogram/types/__init__.py,sha256=w4DZ_oOpBwx744rVw1c4TNbENLPRH3Ny7RqF8QKlFEM,7432
aiogram/types/__pycache__/__init__.cpython-39.pyc,,
aiogram/types/__pycache__/animation.cpython-39.pyc,,
aiogram/types/__pycache__/audio.cpython-39.pyc,,
aiogram/types/__pycache__/auth_widget_data.cpython-39.pyc,,
aiogram/types/__pycache__/base.cpython-39.pyc,,
aiogram/types/__pycache__/bot_command.cpython-39.pyc,,
aiogram/types/__pycache__/bot_command_scope.cpython-39.pyc,,
aiogram/types/__pycache__/callback_game.cpython-39.pyc,,
aiogram/types/__pycache__/callback_query.cpython-39.pyc,,
aiogram/types/__pycache__/chat.cpython-39.pyc,,
aiogram/types/__pycache__/chat_invite_link.cpython-39.pyc,,
aiogram/types/__pycache__/chat_location.cpython-39.pyc,,
aiogram/types/__pycache__/chat_member.cpython-39.pyc,,
aiogram/types/__pycache__/chat_member_updated.cpython-39.pyc,,
aiogram/types/__pycache__/chat_permissions.cpython-39.pyc,,
aiogram/types/__pycache__/chat_photo.cpython-39.pyc,,
aiogram/types/__pycache__/chosen_inline_result.cpython-39.pyc,,
aiogram/types/__pycache__/contact.cpython-39.pyc,,
aiogram/types/__pycache__/dice.cpython-39.pyc,,
aiogram/types/__pycache__/document.cpython-39.pyc,,
aiogram/types/__pycache__/encrypted_credentials.cpython-39.pyc,,
aiogram/types/__pycache__/encrypted_passport_element.cpython-39.pyc,,
aiogram/types/__pycache__/fields.cpython-39.pyc,,
aiogram/types/__pycache__/file.cpython-39.pyc,,
aiogram/types/__pycache__/force_reply.cpython-39.pyc,,
aiogram/types/__pycache__/game.cpython-39.pyc,,
aiogram/types/__pycache__/game_high_score.cpython-39.pyc,,
aiogram/types/__pycache__/inline_keyboard.cpython-39.pyc,,
aiogram/types/__pycache__/inline_query.cpython-39.pyc,,
aiogram/types/__pycache__/inline_query_result.cpython-39.pyc,,
aiogram/types/__pycache__/input_file.cpython-39.pyc,,
aiogram/types/__pycache__/input_media.cpython-39.pyc,,
aiogram/types/__pycache__/input_message_content.cpython-39.pyc,,
aiogram/types/__pycache__/invoice.cpython-39.pyc,,
aiogram/types/__pycache__/labeled_price.cpython-39.pyc,,
aiogram/types/__pycache__/location.cpython-39.pyc,,
aiogram/types/__pycache__/login_url.cpython-39.pyc,,
aiogram/types/__pycache__/mask_position.cpython-39.pyc,,
aiogram/types/__pycache__/message.cpython-39.pyc,,
aiogram/types/__pycache__/message_auto_delete_timer_changed.cpython-39.pyc,,
aiogram/types/__pycache__/message_entity.cpython-39.pyc,,
aiogram/types/__pycache__/message_id.cpython-39.pyc,,
aiogram/types/__pycache__/mixins.cpython-39.pyc,,
aiogram/types/__pycache__/order_info.cpython-39.pyc,,
aiogram/types/__pycache__/passport_data.cpython-39.pyc,,
aiogram/types/__pycache__/passport_element_error.cpython-39.pyc,,
aiogram/types/__pycache__/passport_file.cpython-39.pyc,,
aiogram/types/__pycache__/photo_size.cpython-39.pyc,,
aiogram/types/__pycache__/poll.cpython-39.pyc,,
aiogram/types/__pycache__/pre_checkout_query.cpython-39.pyc,,
aiogram/types/__pycache__/proximity_alert_triggered.cpython-39.pyc,,
aiogram/types/__pycache__/reply_keyboard.cpython-39.pyc,,
aiogram/types/__pycache__/response_parameters.cpython-39.pyc,,
aiogram/types/__pycache__/shipping_address.cpython-39.pyc,,
aiogram/types/__pycache__/shipping_option.cpython-39.pyc,,
aiogram/types/__pycache__/shipping_query.cpython-39.pyc,,
aiogram/types/__pycache__/sticker.cpython-39.pyc,,
aiogram/types/__pycache__/sticker_set.cpython-39.pyc,,
aiogram/types/__pycache__/successful_payment.cpython-39.pyc,,
aiogram/types/__pycache__/update.cpython-39.pyc,,
aiogram/types/__pycache__/user.cpython-39.pyc,,
aiogram/types/__pycache__/user_profile_photos.cpython-39.pyc,,
aiogram/types/__pycache__/venue.cpython-39.pyc,,
aiogram/types/__pycache__/video.cpython-39.pyc,,
aiogram/types/__pycache__/video_note.cpython-39.pyc,,
aiogram/types/__pycache__/voice.cpython-39.pyc,,
aiogram/types/__pycache__/voice_chat_ended.cpython-39.pyc,,
aiogram/types/__pycache__/voice_chat_participants_invited.cpython-39.pyc,,
aiogram/types/__pycache__/voice_chat_scheduled.cpython-39.pyc,,
aiogram/types/__pycache__/voice_chat_started.cpython-39.pyc,,
aiogram/types/__pycache__/webhook_info.cpython-39.pyc,,
aiogram/types/animation.py,sha256=YtZiRt-YYGiefhy2bIWosI4Tch8vHBYZyMgyLwQmEc4,845
aiogram/types/audio.py,sha256=NR5TyMg-lYycE0jbfY65ytTzW_1KtHURQrFGRWKA0Xc,707
aiogram/types/auth_widget_data.py,sha256=NFjrpzNOstqU9hSgRtGK5L41rAhc8Hpz6UnykCTWxD8,1377
aiogram/types/base.py,sha256=OAPxU0pXQKkrK38zlKr_TpmI3soC4Yy5GjWse5MOCNE,8228
aiogram/types/bot_command.py,sha256=0oVwFoVY1VM3IqcYTPQ9ATvCjweoBxvYh64dHeVVpUE,433
aiogram/types/bot_command_scope.py,sha256=FbHqYr5Xzwx1Ao3Qc-EXVJQpz2FnGW_oc2xfTf9DrvQ,4355
aiogram/types/callback_game.py,sha256=rdlqjxiKqnmU89AwmhsOdtsYCNsfK1QcXwqB1cSmF40,226
aiogram/types/callback_query.py,sha256=VfpfdkkH4Df9HYB_hxUz0qy5NGeCxGLuVIxOR_Wm1Ek,3058
aiogram/types/chat.py,sha256=-uwSFnacYVVV_RfhrlOlNxeIg304DgmPFxkptuzV9lY,34415
aiogram/types/chat_invite_link.py,sha256=a5hf1_t2jsftjIdIjBB6-KiU3pMuaosrD0-_OQfdlU4,534
aiogram/types/chat_location.py,sha256=ymG2LCOjfe_uB0g59qDTCOW1GYSRH0HDp3dJz1p2_Fk,449
aiogram/types/chat_member.py,sha256=h2SkBF6gSYzi9MQ-DZmQilLK6QfokL2Y3-iWktLq6Ss,6380
aiogram/types/chat_member_updated.py,sha256=J5k-qePjpxq9xO7f-bkblOcZBYEut8kioK5CNLJU66U,723
aiogram/types/chat_permissions.py,sha256=RynykTfyScq4z51UAqtuONMmzy6b3t6DCRsZ0K0k4Yc,1673
aiogram/types/chat_photo.py,sha256=0ajMgflTGvVlke6oehsCzxViW3HcyY9TiWCatGB8rf0,2905
aiogram/types/chosen_inline_result.py,sha256=VbzJUYnkQG_CgfikEXd2rBpv36GDZU2bh1-xcwzj-MQ,961
aiogram/types/contact.py,sha256=9GllGkYqdVhQBzM8QVY6vNgs5DXz9Yy87ZhlsI5g3fg,640
aiogram/types/dice.py,sha256=c5tHBvXMeD5ooFXgRLutoU7cx7oQj_tD39KX10Qui_k,430
aiogram/types/document.py,sha256=KLPdspuob_KLPg534l_oSn7PSTG0KniX9ETTgFc9PA0,1455
aiogram/types/encrypted_credentials.py,sha256=7rC5u3kvrAyMpOzX2FbPAwvQOP-prQ3XBqdDhwmTfZw,503
aiogram/types/encrypted_passport_element.py,sha256=zu1ke7yQwcrkIHK6BZ5iWtdky7Df3HOHow5S1yJSfnA,769
aiogram/types/fields.py,sha256=_Ty_4aR3o81PUa7h_NuFXyHkfQvkRxGtvF7yQx2346g,5843
aiogram/types/file.py,sha256=lwDtE0yzN-dobkOf9hvQ79T6JBGyCBcOJdbkyf3f4Qo,704
aiogram/types/force_reply.py,sha256=zGJ4SkjBjjpMiFK7ZUJGr99ujpCmhHpmIUMiybiORwo,1092
aiogram/types/game.py,sha256=LT4KjMzHUOns3x5Aaezql-4qDQNA5sdmbPNv1A7Cvuw,725
aiogram/types/game_high_score.py,sha256=WzgG1csKkpiQUVm-JGqpwEiXbJz0-8M5crtg_SwZCuY,485
aiogram/types/inline_keyboard.py,sha256=UYjPEUUIa8FAc8-2krxntEEqrG8DUHnk_xamUA1i9lw,4089
aiogram/types/inline_query.py,sha256=J-6pmPwes84aqjFKyo4ZynuLpilWRk6UKdew_vjdYlQ,3549
aiogram/types/inline_query_result.py,sha256=zuhyE_3oqlWdvsHYCPX71J_zXKNZnAMCWBhLwBj8NK8,37458
aiogram/types/input_file.py,sha256=a_T7k-aFC6-Tihvi7QIWJT2xDHXIMIbIG7Or8In1PIE,5960
aiogram/types/input_media.py,sha256=ESZxZwyZecnmb1UAtaORrTY3TGYOTXxI44e2_eU4TO0,14778
aiogram/types/input_message_content.py,sha256=QF8Pyz9iEblDN20D_i3cbUC1pJ-48fCtAF3rHxSfgrc,8322
aiogram/types/invoice.py,sha256=OJe3ea6zbHiU0v_PfQJFhWljTPGtLSuzBzqGKYxKHqg,430
aiogram/types/labeled_price.py,sha256=oNGpHXL8ArrkeMp1ct8a1MDea_Y5zvEN89Rja74BqHc,445
aiogram/types/location.py,sha256=PegQG2XiaA67D23uNQXGZTj14DpN04XLpU7M3LCgqo0,560
aiogram/types/login_url.py,sha256=N78VBH79VtPmuwlYxmZrYcOmD4mEGU4M3doa2hkqmEc,1107
aiogram/types/mask_position.py,sha256=xwP2mHQCwK8LxYmNv7HJv7FxP-4vnSWVw4QEPYlWG9I,403
aiogram/types/message.py,sha256=MxGWnmsAcADTNrfOptUc8Ev0PhH17i-VmFKMYwsVSxs,134937
aiogram/types/message_auto_delete_timer_changed.py,sha256=lSzTbHsUP_AmCaEo0l7lfSgGBUW-eQOFRF72g9Fwais,337
aiogram/types/message_entity.py,sha256=kvf0bCxumov5I6PbvHcTSHfORPmT9vdic1oJjXJJXrs,4248
aiogram/types/message_id.py,sha256=c9Ws62UlHyiu07YiidYLAUXcPEdqPlK_aBeVDUj9Rx4,234
aiogram/types/mixins.py,sha256=KhQU0UuRoemD8ZL5Ji5UkEdZClRyOO5jo8cG30ZJJOg,1873
aiogram/types/order_info.py,sha256=vCcVqabJvPdD5ZNiqrn7-wn05Wxt-UYnU14iQgFW_kQ,448
aiogram/types/passport_data.py,sha256=mKM9nzkDVMUvVT9IFDBGKs3cB1FOYUI-4hO_L0FS7s8,554
aiogram/types/passport_element_error.py,sha256=O2BHrF31usOKWLBuA5aoo5wCZZSGl9SG5sQRtsmKodc,4341
aiogram/types/passport_file.py,sha256=ktZVp7kY2ECLriCxF83g54Qn1I1lv13ApOhfB_oZc10,497
aiogram/types/photo_size.py,sha256=4ewLUutlDwvRSZFh5ZQMwvWKr51-i558YGRadx7bcAk,485
aiogram/types/poll.py,sha256=Bb16Yxr4myM45PFe6ih0J4z3to9D95gz9uXlw7aGnC8,2356
aiogram/types/pre_checkout_query.py,sha256=VzzWeCBFlf4rJl726vyzpDjP7eHD0yxK5y846yR5-CM,1166
aiogram/types/proximity_alert_triggered.py,sha256=3HVR9Ch-_Wx8YaOp98JPCtzcf8T2vciFzr9fxjpDKHA,458
aiogram/types/reply_keyboard.py,sha256=YBLpeJlLhHIWDUhzi6vppQFoMtXE8FrJivhXt4h6mic,5254
aiogram/types/response_parameters.py,sha256=3WjIHLqlM5aruJCYWoxgsC4-EdGlmI55gJ6s_yycBQI,328
aiogram/types/shipping_address.py,sha256=QyN8hy7iXMmFnuquw8hHl5qiWsQLXtTY2y7k43i2WHc,469
aiogram/types/shipping_option.py,sha256=tUwtLepy0J3_4LH3kaeEVE4UQPKvaQjjG4AgZPviuHE,825
aiogram/types/shipping_query.py,sha256=Q7ruEe4rd0uN0OYUESDZpXui58y50eR-VB75LzpLLFk,707
aiogram/types/sticker.py,sha256=ITYOI8nsIJ6UbgEOIJGdXMJ1Itc9-01QR2ZEcriixLU,1790
aiogram/types/sticker_set.py,sha256=gdnu5Xu5B0VmL0KWP7r94krM_NSsNE2Q6k2nVsFsDMc,564
aiogram/types/successful_payment.py,sha256=OYa5vFeNCgrhOjbsG-JzLIg6b1msjsH8pMz9LcoNLRo,640
aiogram/types/update.py,sha256=JnwBaaSTh1tVY8LunLtwwHzGmGSI1aXJ-Df5DmhoeQg,3065
aiogram/types/user.py,sha256=eakT6DkHgkrIMQPRwtIhxwZBd6TWwdGQEwQcM2-_a20,2650
aiogram/types/user_profile_photos.py,sha256=KYitXWtf9pVVg6Om6cs4DNatYl3yEAk7NUmJ-hp1_ac,396
aiogram/types/venue.py,sha256=0FovO15JG29hTa-Ei6hGs0ypji4CiqXkXVsOnW81rGw,540
aiogram/types/video.py,sha256=j_d7U-1RDANsYattZ-9oQKXKgoIgmLRb9coDfSoNRoY,658
aiogram/types/video_note.py,sha256=xwjW-CMgAbjOflcTd1fIFA8edyQhMIQMLP5PitDorFQ,581
aiogram/types/voice.py,sha256=4A0-WI5vZYszqB7Kc-SQ9z_VvlHuY-9gnUU1ILJyzTY,445
aiogram/types/voice_chat_ended.py,sha256=b_aNbuxmYXFfAt0J_Lb4JIEGbepVSb_FWWBYwfQ3aL0,326
aiogram/types/voice_chat_participants_invited.py,sha256=8XTP5Jc3mpwbaU1qVDKv9ZIhAzeIycSXCaG-sSfwWD0,412
aiogram/types/voice_chat_scheduled.py,sha256=mNfF_fzsS1PjhbGSpNDIXm7AsDUs7-dYrtMMxWxzTyM,356
aiogram/types/voice_chat_started.py,sha256=cGLxDMRw2X_-61CKHpfY4Gj7utDbHx_USn1CATSBN2k,311
aiogram/types/webhook_info.py,sha256=0O6_l_EX5BFy4CVzqpQ1cLBUFr-ypisLUQ4y2QDCP2s,648
aiogram/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
aiogram/utils/__pycache__/__init__.cpython-39.pyc,,
aiogram/utils/__pycache__/auth_widget.cpython-39.pyc,,
aiogram/utils/__pycache__/callback_data.cpython-39.pyc,,
aiogram/utils/__pycache__/deep_linking.cpython-39.pyc,,
aiogram/utils/__pycache__/deprecated.cpython-39.pyc,,
aiogram/utils/__pycache__/emoji.cpython-39.pyc,,
aiogram/utils/__pycache__/exceptions.cpython-39.pyc,,
aiogram/utils/__pycache__/executor.cpython-39.pyc,,
aiogram/utils/__pycache__/helper.cpython-39.pyc,,
aiogram/utils/__pycache__/json.cpython-39.pyc,,
aiogram/utils/__pycache__/markdown.cpython-39.pyc,,
aiogram/utils/__pycache__/mixins.cpython-39.pyc,,
aiogram/utils/__pycache__/parts.cpython-39.pyc,,
aiogram/utils/__pycache__/payload.cpython-39.pyc,,
aiogram/utils/__pycache__/text_decorations.cpython-39.pyc,,
aiogram/utils/auth_widget.py,sha256=d-hDz-zhVA9As6IKufNmI5FsLSKb0bz5saOl0U9zvPA,2126
aiogram/utils/callback_data.py,sha256=DkRXt1Nqr6mh8PYI9jiL4EhsQi4ga-H2YzaAkLJSeX8,4089
aiogram/utils/deep_linking.py,sha256=9OB1JQVQ9ovYotAMjglylWtGrIZSWGEp96xFUu5nz9I,3721
aiogram/utils/deprecated.py,sha256=Oz8GYy-Odwj0eiYAKjm7yQvQ-l-Vmhecr28WkMnkgCY,5539
aiogram/utils/emoji.py,sha256=jedTnV32L_mMZFC6d6RKA8QbaHPtDCJrtuEtW48k72I,220
aiogram/utils/exceptions.py,sha256=QqNu4NAXGWdJ5YfIZ-lPhKwp2BT_OYpBzEjZ6ZtGAAA,14895
aiogram/utils/executor.py,sha256=cBh4JUNxuKtbC2qNwS1Ty3kgJOl4fXuJisCgWiDkMj4,13018
aiogram/utils/helper.py,sha256=6p7yRLe8pZUVjmIYAvGHFhsLjrXdLjxTB2VwVRRpXbo,5779
aiogram/utils/json.py,sha256=Yzaufq_bts3YTzZ1x0v8p1MlRmxjTbinZrtNltJfIz8,843
aiogram/utils/markdown.py,sha256=RKng7KJpGB2WRwBip0UkWNkdJmKPMNROw6WtiOf19EU,5006
aiogram/utils/mixins.py,sha256=t3ZqwMI9Qzws7sw_eB4GS06e1xr8IVwudx9PYtL-xig,1284
aiogram/utils/parts.py,sha256=Oq9AL4kxkBWtYgHdQk0rTimTaA0uLvuQbtsBe16vslc,1556
aiogram/utils/payload.py,sha256=PI789pjTqFSpp5LvKjtw5p0mA5pSYc4cOtXhOLVRLjg,2138
aiogram/utils/text_decorations.py,sha256=DeN3J832_J2RQ4G2s7m-1kamVap1j_qU0l-17RWJWYU,5945

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.33.6)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -0,0 +1,47 @@
import sys
if sys.version_info < (3, 7):
raise ImportError('Your Python version {0} is not supported by aiogram, please install '
'Python 3.7+'.format('.'.join(map(str, sys.version_info[:3]))))
import asyncio
import os
from . import bot
from . import contrib
from . import dispatcher
from . import types
from . import utils
from .bot import Bot
from .dispatcher import Dispatcher
from .dispatcher import filters
from .dispatcher import middlewares
from .utils import exceptions, executor, helper, markdown as md
try:
import uvloop
except ImportError:
uvloop = None
else:
if 'DISABLE_UVLOOP' not in os.environ:
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
__all__ = (
'Bot',
'Dispatcher',
'__api_version__',
'__version__',
'bot',
'contrib',
'dispatcher',
'exceptions',
'executor',
'filters',
'helper',
'md',
'middlewares',
'types',
'utils',
)
__version__ = '2.14.3'
__api_version__ = '5.3'

View File

@ -0,0 +1,83 @@
import platform
import sys
import aiohttp
import aiogram
from aiogram.utils import json
class SysInfo:
@property
def os(self):
return platform.platform()
@property
def python_implementation(self):
return platform.python_implementation()
@property
def python(self):
return sys.version.replace('\n', '')
@property
def aiogram(self):
return aiogram.__version__
@property
def api(self):
return aiogram.__api_version__
@property
def uvloop(self):
try:
import uvloop
except ImportError:
return
return uvloop.__version__
@property
def ujson(self):
try:
import ujson
except ImportError:
return
return ujson.__version__
@property
def rapidjson(self):
try:
import rapidjson
except ImportError:
return
return rapidjson.__version__
@property
def aiohttp(self):
return aiohttp.__version__
def collect(self):
yield f'{self.python_implementation}: {self.python}'
yield f'OS: {self.os}'
yield f'aiogram: {self.aiogram}'
yield f'aiohttp: {self.aiohttp}'
uvloop = self.uvloop
if uvloop:
yield f'uvloop: {uvloop}'
yield f'JSON mode: {json.mode}'
rapidjson = self.rapidjson
if rapidjson:
yield f'rapidjson: {rapidjson}'
ujson = self.ujson
if ujson:
yield f'ujson: {ujson}'
def __str__(self):
return '\n'.join(self.collect())
if __name__ == '__main__':
print(SysInfo())

View File

@ -0,0 +1,9 @@
from . import api
from .base import BaseBot
from .bot import Bot
__all__ = (
'BaseBot',
'Bot',
'api',
)

View File

@ -0,0 +1,290 @@
import logging
import os
from dataclasses import dataclass
from http import HTTPStatus
import aiohttp
from .. import types
from ..utils import exceptions, json
from ..utils.helper import Helper, HelperMode, Item
# Main aiogram logger
log = logging.getLogger('aiogram')
@dataclass(frozen=True)
class TelegramAPIServer:
"""
Base config for API Endpoints
"""
base: str
file: str
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) -> 'TelegramAPIServer':
base = base.rstrip("/")
return cls(
base=f"{base}/bot{{token}}/{{method}}",
file=f"{base}/file/bot{{token}}/{{path}}",
)
TELEGRAM_PRODUCTION = TelegramAPIServer.from_base("https://api.telegram.org")
def check_token(token: str) -> bool:
"""
Validate BOT token
:param token:
:return:
"""
if not isinstance(token, str):
message = (f"Token is invalid! "
f"It must be 'str' type instead of {type(token)} type.")
raise exceptions.ValidationError(message)
if any(x.isspace() for x in token):
message = "Token is invalid! It can't contains spaces."
raise exceptions.ValidationError(message)
left, sep, right = token.partition(':')
if (not sep) or (not left.isdigit()) or (not right):
raise exceptions.ValidationError('Token is invalid!')
return True
def check_result(method_name: str, content_type: str, status_code: int, body: str):
"""
Checks whether `result` is a valid API response.
A result is considered invalid if:
- The server returned an HTTP response code other than 200
- The content of the result is invalid JSON.
- The method call was unsuccessful (The JSON 'ok' field equals False)
:param method_name: The name of the method called
:param status_code: status code
:param content_type: content type of result
:param body: result body
:return: The result parsed to a JSON dictionary
:raises ApiException: if one of the above listed cases is applicable
"""
log.debug('Response for %s: [%d] "%r"', method_name, status_code, body)
if content_type != 'application/json':
raise exceptions.NetworkError(f"Invalid response with content type {content_type}: \"{body}\"")
try:
result_json = json.loads(body)
except ValueError:
result_json = {}
description = result_json.get('description') or body
parameters = types.ResponseParameters(**result_json.get('parameters', {}) or {})
if HTTPStatus.OK <= status_code <= HTTPStatus.IM_USED:
return result_json.get('result')
elif parameters.retry_after:
raise exceptions.RetryAfter(parameters.retry_after)
elif parameters.migrate_to_chat_id:
raise exceptions.MigrateToChat(parameters.migrate_to_chat_id)
elif status_code == HTTPStatus.BAD_REQUEST:
exceptions.BadRequest.detect(description)
elif status_code == HTTPStatus.NOT_FOUND:
exceptions.NotFound.detect(description)
elif status_code == HTTPStatus.CONFLICT:
exceptions.ConflictError.detect(description)
elif status_code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
exceptions.Unauthorized.detect(description)
elif status_code == HTTPStatus.REQUEST_ENTITY_TOO_LARGE:
raise exceptions.NetworkError('File too large for uploading. '
'Check telegram api limits https://core.telegram.org/bots/api#senddocument')
elif status_code >= HTTPStatus.INTERNAL_SERVER_ERROR:
if 'restart' in description:
raise exceptions.RestartingTelegram()
raise exceptions.TelegramAPIError(description)
raise exceptions.TelegramAPIError(f"{description} [{status_code}]")
async def make_request(session, server, token, method, data=None, files=None, **kwargs):
log.debug('Make request: "%s" with data: "%r" and files "%r"', method, data, files)
url = server.api_url(token=token, method=method)
req = compose_data(data, files)
try:
async with session.post(url, data=req, **kwargs) as response:
return check_result(method, response.content_type, response.status, await response.text())
except aiohttp.ClientError as e:
raise exceptions.NetworkError(f"aiohttp client throws an error: {e.__class__.__name__}: {e}")
def guess_filename(obj):
"""
Get file name from object
:param obj:
:return:
"""
name = getattr(obj, 'name', None)
if name and isinstance(name, str) and name[0] != '<' and name[-1] != '>':
return os.path.basename(name)
def compose_data(params=None, files=None):
"""
Prepare request data
:param params:
:param files:
:return:
"""
data = aiohttp.formdata.FormData(quote_fields=False)
if params:
for key, value in params.items():
data.add_field(key, str(value))
if files:
for key, f in files.items():
if isinstance(f, tuple):
if len(f) == 2:
filename, fileobj = f
else:
raise ValueError('Tuple must have exactly 2 elements: filename, fileobj')
elif isinstance(f, types.InputFile):
filename, fileobj = f.filename, f.file
else:
filename, fileobj = guess_filename(f) or key, f
data.add_field(key, fileobj, filename=filename)
return data
class Methods(Helper):
"""
Helper for Telegram API Methods listed on https://core.telegram.org/bots/api
List is updated to Bot API 5.3
"""
mode = HelperMode.lowerCamelCase
# Getting Updates
GET_UPDATES = Item() # getUpdates
SET_WEBHOOK = Item() # setWebhook
DELETE_WEBHOOK = Item() # deleteWebhook
GET_WEBHOOK_INFO = Item() # getWebhookInfo
# Available methods
GET_ME = Item() # getMe
LOG_OUT = Item() # logOut
CLOSE = Item() # close
SEND_MESSAGE = Item() # sendMessage
FORWARD_MESSAGE = Item() # forwardMessage
COPY_MESSAGE = Item() # copyMessage
SEND_PHOTO = Item() # sendPhoto
SEND_AUDIO = Item() # sendAudio
SEND_DOCUMENT = Item() # sendDocument
SEND_VIDEO = Item() # sendVideo
SEND_ANIMATION = Item() # sendAnimation
SEND_VOICE = Item() # sendVoice
SEND_VIDEO_NOTE = Item() # sendVideoNote
SEND_MEDIA_GROUP = Item() # sendMediaGroup
SEND_LOCATION = Item() # sendLocation
EDIT_MESSAGE_LIVE_LOCATION = Item() # editMessageLiveLocation
STOP_MESSAGE_LIVE_LOCATION = Item() # stopMessageLiveLocation
SEND_VENUE = Item() # sendVenue
SEND_CONTACT = Item() # sendContact
SEND_POLL = Item() # sendPoll
SEND_DICE = Item() # sendDice
SEND_CHAT_ACTION = Item() # sendChatAction
GET_USER_PROFILE_PHOTOS = Item() # getUserProfilePhotos
GET_FILE = Item() # getFile
KICK_CHAT_MEMBER = Item() # kickChatMember
BAN_CHAT_MEMBER = Item() # banChatMember
UNBAN_CHAT_MEMBER = Item() # unbanChatMember
RESTRICT_CHAT_MEMBER = Item() # restrictChatMember
PROMOTE_CHAT_MEMBER = Item() # promoteChatMember
SET_CHAT_ADMINISTRATOR_CUSTOM_TITLE = Item() # setChatAdministratorCustomTitle
SET_CHAT_PERMISSIONS = Item() # setChatPermissions
EXPORT_CHAT_INVITE_LINK = Item() # exportChatInviteLink
CREATE_CHAT_INVITE_LINK = Item() # createChatInviteLink
EDIT_CHAT_INVITE_LINK = Item() # editChatInviteLink
REVOKE_CHAT_INVITE_LINK = Item() # revokeChatInviteLink
SET_CHAT_PHOTO = Item() # setChatPhoto
DELETE_CHAT_PHOTO = Item() # deleteChatPhoto
SET_CHAT_TITLE = Item() # setChatTitle
SET_CHAT_DESCRIPTION = Item() # setChatDescription
PIN_CHAT_MESSAGE = Item() # pinChatMessage
UNPIN_CHAT_MESSAGE = Item() # unpinChatMessage
UNPIN_ALL_CHAT_MESSAGES = Item() # unpinAllChatMessages
LEAVE_CHAT = Item() # leaveChat
GET_CHAT = Item() # getChat
GET_CHAT_ADMINISTRATORS = Item() # getChatAdministrators
GET_CHAT_MEMBER_COUNT = Item() # getChatMemberCount
GET_CHAT_MEMBERS_COUNT = Item() # getChatMembersCount (renamed to getChatMemberCount)
GET_CHAT_MEMBER = Item() # getChatMember
SET_CHAT_STICKER_SET = Item() # setChatStickerSet
DELETE_CHAT_STICKER_SET = Item() # deleteChatStickerSet
ANSWER_CALLBACK_QUERY = Item() # answerCallbackQuery
SET_MY_COMMANDS = Item() # setMyCommands
DELETE_MY_COMMANDS = Item() # deleteMyCommands
GET_MY_COMMANDS = Item() # getMyCommands
# Updating messages
EDIT_MESSAGE_TEXT = Item() # editMessageText
EDIT_MESSAGE_CAPTION = Item() # editMessageCaption
EDIT_MESSAGE_MEDIA = Item() # editMessageMedia
EDIT_MESSAGE_REPLY_MARKUP = Item() # editMessageReplyMarkup
STOP_POLL = Item() # stopPoll
DELETE_MESSAGE = Item() # deleteMessage
# Stickers
SEND_STICKER = Item() # sendSticker
GET_STICKER_SET = Item() # getStickerSet
UPLOAD_STICKER_FILE = Item() # uploadStickerFile
CREATE_NEW_STICKER_SET = Item() # createNewStickerSet
ADD_STICKER_TO_SET = Item() # addStickerToSet
SET_STICKER_POSITION_IN_SET = Item() # setStickerPositionInSet
DELETE_STICKER_FROM_SET = Item() # deleteStickerFromSet
SET_STICKER_SET_THUMB = Item() # setStickerSetThumb
# Inline mode
ANSWER_INLINE_QUERY = Item() # answerInlineQuery
# Payments
SEND_INVOICE = Item() # sendInvoice
ANSWER_SHIPPING_QUERY = Item() # answerShippingQuery
ANSWER_PRE_CHECKOUT_QUERY = Item() # answerPreCheckoutQuery
# Telegram Passport
SET_PASSPORT_DATA_ERRORS = Item() # setPassportDataErrors
# Games
SEND_GAME = Item() # sendGame
SET_GAME_SCORE = Item() # setGameScore
GET_GAME_HIGH_SCORES = Item() # getGameHighScores

View File

@ -0,0 +1,298 @@
import asyncio
import contextlib
import io
import ssl
import typing
import warnings
from contextvars import ContextVar
from typing import Dict, List, Optional, Union, Type
import aiohttp
import certifi
from aiohttp.helpers import sentinel
from . import api
from .api import TelegramAPIServer, TELEGRAM_PRODUCTION
from ..types import ParseMode, base
from ..utils import json
from ..utils.auth_widget import check_integrity
from ..utils.deprecated import deprecated
class BaseBot:
"""
Base class for bot. It's raw bot.
"""
_ctx_timeout = ContextVar('TelegramRequestTimeout')
_ctx_token = ContextVar('BotDifferentToken')
def __init__(
self,
token: base.String,
loop: Optional[Union[asyncio.BaseEventLoop, asyncio.AbstractEventLoop]] = None,
connections_limit: Optional[base.Integer] = None,
proxy: Optional[base.String] = None,
proxy_auth: Optional[aiohttp.BasicAuth] = None,
validate_token: Optional[base.Boolean] = True,
parse_mode: typing.Optional[base.String] = None,
timeout: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] = None,
server: TelegramAPIServer = TELEGRAM_PRODUCTION
):
"""
Instructions how to get Bot token is found here: https://core.telegram.org/bots#3-how-do-i-create-a-bot
:param token: token from @BotFather
:type token: :obj:`str`
:param loop: event loop
:type loop: Optional Union :obj:`asyncio.BaseEventLoop`, :obj:`asyncio.AbstractEventLoop`
:param connections_limit: connections limit for aiohttp.ClientSession
:type connections_limit: :obj:`int`
:param proxy: HTTP proxy URL
:type proxy: :obj:`str`
:param proxy_auth: Authentication information
:type proxy_auth: Optional :obj:`aiohttp.BasicAuth`
:param validate_token: Validate token.
:type validate_token: :obj:`bool`
:param parse_mode: You can set default parse mode
:type parse_mode: :obj:`str`
:param timeout: Request timeout
:type timeout: :obj:`typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]]`
:param server: Telegram Bot API Server endpoint.
:type server: :obj:`TelegramAPIServer`
:raise: when token is invalid throw an :obj:`aiogram.utils.exceptions.ValidationError`
"""
self._main_loop = loop
# Authentication
if validate_token:
api.check_token(token)
self._token = None
self.__token = token
self.id = int(token.split(sep=':')[0])
self.server = server
self.proxy = proxy
self.proxy_auth = proxy_auth
# aiohttp main session
ssl_context = ssl.create_default_context(cafile=certifi.where())
self._session: Optional[aiohttp.ClientSession] = None
self._connector_class: Type[aiohttp.TCPConnector] = aiohttp.TCPConnector
self._connector_init = dict(limit=connections_limit, ssl=ssl_context)
if isinstance(proxy, str) and (proxy.startswith('socks5://') or proxy.startswith('socks4://')):
from aiohttp_socks import SocksConnector
from aiohttp_socks.utils import parse_proxy_url
socks_ver, host, port, username, password = parse_proxy_url(proxy)
if proxy_auth:
if not username:
username = proxy_auth.login
if not password:
password = proxy_auth.password
self._connector_class = SocksConnector
self._connector_init.update(
socks_ver=socks_ver, host=host, port=port,
username=username, password=password, rdns=True,
)
self.proxy = None
self.proxy_auth = None
self._timeout = None
self.timeout = timeout
self.parse_mode = parse_mode
def get_new_session(self) -> aiohttp.ClientSession:
return aiohttp.ClientSession(
connector=self._connector_class(**self._connector_init, loop=self._main_loop),
loop=self._main_loop,
json_serialize=json.dumps
)
@property
def loop(self) -> Optional[asyncio.AbstractEventLoop]:
return self._main_loop
@property
def session(self) -> Optional[aiohttp.ClientSession]:
if self._session is None or self._session.closed:
self._session = self.get_new_session()
return self._session
@staticmethod
def _prepare_timeout(
value: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]]
) -> typing.Optional[aiohttp.ClientTimeout]:
if value is None or isinstance(value, aiohttp.ClientTimeout):
return value
return aiohttp.ClientTimeout(total=value)
@property
def timeout(self):
timeout = self._ctx_timeout.get(self._timeout)
if timeout is None:
return sentinel
return timeout
@timeout.setter
def timeout(self, value):
self._timeout = self._prepare_timeout(value)
@timeout.deleter
def timeout(self):
self.timeout = None
@contextlib.contextmanager
def request_timeout(self, timeout: typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]):
"""
Context manager implements opportunity to change request timeout in current context
:param timeout: Request timeout
:type timeout: :obj:`typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]]`
:return:
"""
timeout = self._prepare_timeout(timeout)
token = self._ctx_timeout.set(timeout)
try:
yield
finally:
self._ctx_timeout.reset(token)
@property
def __token(self):
return self._ctx_token.get(self._token)
@__token.setter
def __token(self, value):
self._token = value
@contextlib.contextmanager
def with_token(self, bot_token: base.String, validate_token: Optional[base.Boolean] = True):
if validate_token:
api.check_token(bot_token)
token = self._ctx_token.set(bot_token)
try:
yield
finally:
self._ctx_token.reset(token)
@deprecated("This method's behavior will be changed in aiogram v3.0. "
"More info: https://core.telegram.org/bots/api#close", stacklevel=3)
async def close(self):
"""
Close all client sessions
"""
await self.session.close()
async def request(self, method: base.String,
data: Optional[Dict] = None,
files: Optional[Dict] = None, **kwargs) -> Union[List, Dict, base.Boolean]:
"""
Make an request to Telegram Bot API
https://core.telegram.org/bots/api#making-requests
:param method: API method
:type method: :obj:`str`
:param data: request parameters
:type data: :obj:`dict`
:param files: files
:type files: :obj:`dict`
:return: result
:rtype: Union[List, Dict]
:raise: :obj:`aiogram.exceptions.TelegramApiError`
"""
return await api.make_request(self.session, self.server, self.__token, method, data, files,
proxy=self.proxy, proxy_auth=self.proxy_auth, timeout=self.timeout, **kwargs)
async def download_file(self, file_path: base.String,
destination: Optional[base.InputFile] = None,
timeout: Optional[base.Integer] = sentinel,
chunk_size: Optional[base.Integer] = 65536,
seek: Optional[base.Boolean] = True) -> Union[io.BytesIO, io.FileIO]:
"""
Download file by file_path to destination
if You want to automatically create destination (:class:`io.BytesIO`) use default
value of destination and handle result of this method.
:param file_path: file path on telegram server (You can get it from :obj:`aiogram.types.File`)
:type file_path: :obj:`str`
:param destination: filename or instance of :class:`io.IOBase`. For e. g. :class:`io.BytesIO`
:param timeout: Integer
:param chunk_size: Integer
:param seek: Boolean - go to start of file when downloading is finished.
:return: destination
"""
if destination is None:
destination = io.BytesIO()
url = self.get_file_url(file_path)
dest = destination if isinstance(destination, io.IOBase) else open(destination, 'wb')
async with self.session.get(url, timeout=timeout, proxy=self.proxy, proxy_auth=self.proxy_auth) as response:
while True:
chunk = await response.content.read(chunk_size)
if not chunk:
break
dest.write(chunk)
dest.flush()
if seek:
dest.seek(0)
return dest
def get_file_url(self, file_path):
return self.server.file_url(token=self.__token, path=file_path)
async def send_file(self, file_type, method, file, payload) -> Union[Dict, base.Boolean]:
"""
Send file
https://core.telegram.org/bots/api#inputfile
:param file_type: field name
:param method: API method
:param file: String or io.IOBase
:param payload: request payload
:return: response
"""
if file is None:
files = {}
elif isinstance(file, str):
# You can use file ID or URL in the most of requests
payload[file_type] = file
files = None
else:
files = {file_type: file}
return await self.request(method, payload, files)
@property
def parse_mode(self):
return getattr(self, '_parse_mode', None)
@parse_mode.setter
def parse_mode(self, value):
if value is None:
setattr(self, '_parse_mode', None)
else:
if not isinstance(value, str):
raise TypeError(f"Parse mode must be str, not {type(value)}")
value = value.lower()
if value not in ParseMode.all():
raise ValueError(f"Parse mode must be one of {ParseMode.all()}")
setattr(self, '_parse_mode', value)
if value == 'markdown':
warnings.warn("Parse mode `Markdown` is legacy since Telegram Bot API 4.5, "
"retained for backward compatibility. Use `MarkdownV2` instead.\n"
"https://core.telegram.org/bots/api#markdown-style", stacklevel=3)
@parse_mode.deleter
def parse_mode(self):
self.parse_mode = None
def check_auth_widget(self, data):
return check_integrity(self.__token, data)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
import json
import pathlib
import pickle
import typing
from .memory import MemoryStorage
class _FileStorage(MemoryStorage):
def __init__(self, path: typing.Union[pathlib.Path, str]):
"""
:param path: file path
"""
super(_FileStorage, self).__init__()
path = self.path = pathlib.Path(path)
try:
self.data = self.read(path)
except FileNotFoundError:
pass
async def close(self):
if self.data:
self.write(self.path)
await super(_FileStorage, self).close()
def read(self, path: pathlib.Path):
raise NotImplementedError
def write(self, path: pathlib.Path):
raise NotImplementedError
class JSONStorage(_FileStorage):
"""
JSON File storage based on MemoryStorage
"""
def read(self, path: pathlib.Path):
with path.open('r') as f:
return json.load(f)
def write(self, path: pathlib.Path):
with path.open('w') as f:
return json.dump(self.data, f, indent=4)
class PickleStorage(_FileStorage):
"""
Pickle File storage based on MemoryStorage
"""
def read(self, path: pathlib.Path):
with path.open('rb') as f:
return pickle.load(f)
def write(self, path: pathlib.Path):
with path.open('wb') as f:
return pickle.dump(self.data, f, protocol=pickle.HIGHEST_PROTOCOL)

View File

@ -0,0 +1,112 @@
import copy
import typing
from ...dispatcher.storage import BaseStorage
class MemoryStorage(BaseStorage):
"""
In-memory based states storage.
This type of storage is not recommended for usage in bots, because you will lost all states after restarting.
"""
async def wait_closed(self):
pass
async def close(self):
self.data.clear()
def __init__(self):
self.data = {}
def resolve_address(self, chat, user):
chat_id, user_id = map(str, self.check_address(chat=chat, user=user))
if chat_id not in self.data:
self.data[chat_id] = {}
if user_id not in self.data[chat_id]:
self.data[chat_id][user_id] = {'state': None, 'data': {}, 'bucket': {}}
return chat_id, user_id
async def get_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Optional[str]:
chat, user = self.resolve_address(chat=chat, user=user)
return self.data[chat][user].get("state", self.resolve_state(default))
async def get_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Dict:
chat, user = self.resolve_address(chat=chat, user=user)
return copy.deepcopy(self.data[chat][user]['data'])
async def update_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
data: typing.Dict = None, **kwargs):
if data is None:
data = {}
chat, user = self.resolve_address(chat=chat, user=user)
self.data[chat][user]['data'].update(data, **kwargs)
async def set_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
state: typing.AnyStr = None):
chat, user = self.resolve_address(chat=chat, user=user)
self.data[chat][user]['state'] = self.resolve_state(state)
async def set_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
data: typing.Dict = None):
chat, user = self.resolve_address(chat=chat, user=user)
self.data[chat][user]['data'] = copy.deepcopy(data)
self._cleanup(chat, user)
async def reset_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
with_data: typing.Optional[bool] = True):
await self.set_state(chat=chat, user=user, state=None)
if with_data:
await self.set_data(chat=chat, user=user, data={})
self._cleanup(chat, user)
def has_bucket(self):
return True
async def get_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[dict] = None) -> typing.Dict:
chat, user = self.resolve_address(chat=chat, user=user)
return copy.deepcopy(self.data[chat][user]['bucket'])
async def set_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None):
chat, user = self.resolve_address(chat=chat, user=user)
self.data[chat][user]['bucket'] = copy.deepcopy(bucket)
self._cleanup(chat, user)
async def update_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None, **kwargs):
if bucket is None:
bucket = {}
chat, user = self.resolve_address(chat=chat, user=user)
self.data[chat][user]['bucket'].update(bucket, **kwargs)
def _cleanup(self, chat, user):
chat, user = self.resolve_address(chat=chat, user=user)
if self.data[chat][user] == {'state': None, 'data': {}, 'bucket': {}}:
del self.data[chat][user]
if not self.data[chat]:
del self.data[chat]

View File

@ -0,0 +1,224 @@
"""
This module has mongo storage for finite-state machine
based on `motor <https://github.com/mongodb/motor>`_ driver
"""
from typing import Union, Dict, Optional, List, Tuple, AnyStr
try:
import pymongo
import motor
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
except ModuleNotFoundError as e:
import warnings
warnings.warn("Install motor with `pip install motor`")
raise e
from ...dispatcher.storage import BaseStorage
STATE = 'aiogram_state'
DATA = 'aiogram_data'
BUCKET = 'aiogram_bucket'
COLLECTIONS = (STATE, DATA, BUCKET)
class MongoStorage(BaseStorage):
"""
Mongo-based storage for FSM.
Usage:
.. code-block:: python3
storage = MongoStorage(host='localhost', port=27017, db_name='aiogram_fsm')
dp = Dispatcher(bot, storage=storage)
And need to close Mongo client connections when shutdown
.. code-block:: python3
await dp.storage.close()
await dp.storage.wait_closed()
"""
def __init__(self, host='localhost', port=27017, db_name='aiogram_fsm', uri=None,
username=None, password=None, index=True, **kwargs):
self._host = host
self._port = port
self._db_name: str = db_name
self._uri = uri
self._username = username
self._password = password
self._kwargs = kwargs
self._mongo: Optional[AsyncIOMotorClient] = None
self._db: Optional[AsyncIOMotorDatabase] = None
self._index = index
async def get_client(self) -> AsyncIOMotorClient:
if isinstance(self._mongo, AsyncIOMotorClient):
return self._mongo
if self._uri:
try:
self._mongo = AsyncIOMotorClient(self._uri)
except pymongo.errors.ConfigurationError as e:
if "query() got an unexpected keyword argument 'lifetime'" in e.args[0]:
import logging
logger = logging.getLogger("aiogram")
logger.warning("Run `pip install dnspython==1.16.0` in order to fix ConfigurationError. More information: https://github.com/mongodb/mongo-python-driver/pull/423#issuecomment-528998245")
raise e
return self._mongo
uri = 'mongodb://'
# set username + password
if self._username and self._password:
uri += f'{self._username}:{self._password}@'
# set host and port (optional)
uri += f'{self._host}:{self._port}' if self._host else f'localhost:{self._port}'
# define and return client
self._mongo = AsyncIOMotorClient(uri)
return self._mongo
async def get_db(self) -> AsyncIOMotorDatabase:
"""
Get Mongo db
This property is awaitable.
"""
if isinstance(self._db, AsyncIOMotorDatabase):
return self._db
mongo = await self.get_client()
self._db = mongo.get_database(self._db_name)
if self._index:
await self.apply_index(self._db)
return self._db
@staticmethod
async def apply_index(db):
for collection in COLLECTIONS:
await db[collection].create_index(keys=[('chat', 1), ('user', 1)],
name="chat_user_idx", unique=True, background=True)
async def close(self):
if self._mongo:
self._mongo.close()
async def wait_closed(self):
return True
async def set_state(self, *,
chat: Union[str, int, None] = None,
user: Union[str, int, None] = None,
state: Optional[AnyStr] = None):
chat, user = self.check_address(chat=chat, user=user)
db = await self.get_db()
if state is None:
await db[STATE].delete_one(filter={'chat': chat, 'user': user})
else:
await db[STATE].update_one(
filter={'chat': chat, 'user': user},
update={'$set': {'state': self.resolve_state(state)}},
upsert=True,
)
async def get_state(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
default: Optional[str] = None) -> Optional[str]:
chat, user = self.check_address(chat=chat, user=user)
db = await self.get_db()
result = await db[STATE].find_one(filter={'chat': chat, 'user': user})
return result.get('state') if result else self.resolve_state(default)
async def set_data(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
data: Dict = None):
chat, user = self.check_address(chat=chat, user=user)
db = await self.get_db()
if not data:
await db[DATA].delete_one(filter={'chat': chat, 'user': user})
else:
await db[DATA].update_one(filter={'chat': chat, 'user': user},
update={'$set': {'data': data}}, upsert=True)
async def get_data(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
default: Optional[dict] = None) -> Dict:
chat, user = self.check_address(chat=chat, user=user)
db = await self.get_db()
result = await db[DATA].find_one(filter={'chat': chat, 'user': user})
return result.get('data') if result else default or {}
async def update_data(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
data: Dict = None, **kwargs):
if data is None:
data = {}
temp_data = await self.get_data(chat=chat, user=user, default={})
temp_data.update(data, **kwargs)
await self.set_data(chat=chat, user=user, data=temp_data)
def has_bucket(self):
return True
async def get_bucket(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
default: Optional[dict] = None) -> Dict:
chat, user = self.check_address(chat=chat, user=user)
db = await self.get_db()
result = await db[BUCKET].find_one(filter={'chat': chat, 'user': user})
return result.get('bucket') if result else default or {}
async def set_bucket(self, *, chat: Union[str, int, None] = None, user: Union[str, int, None] = None,
bucket: Dict = None):
chat, user = self.check_address(chat=chat, user=user)
db = await self.get_db()
await db[BUCKET].update_one(filter={'chat': chat, 'user': user},
update={'$set': {'bucket': bucket}}, upsert=True)
async def update_bucket(self, *, chat: Union[str, int, None] = None,
user: Union[str, int, None] = None,
bucket: Dict = None, **kwargs):
if bucket is None:
bucket = {}
temp_bucket = await self.get_bucket(chat=chat, user=user)
temp_bucket.update(bucket, **kwargs)
await self.set_bucket(chat=chat, user=user, bucket=temp_bucket)
async def reset_all(self, full=True):
"""
Reset states in DB
:param full: clean DB or clean only states
:return:
"""
db = await self.get_db()
await db[STATE].drop()
if full:
await db[DATA].drop()
await db[BUCKET].drop()
async def get_states_list(self) -> List[Tuple[int, int]]:
"""
Get list of all stored chat's and user's
:return: list of tuples where first element is chat id and second is user id
"""
db = await self.get_db()
result = []
items = await db[STATE].find().to_list()
for item in items:
result.append(
(int(item['chat']), int(item['user']))
)
return result

View File

@ -0,0 +1,414 @@
"""
This module has redis storage for finite-state machine based on `aioredis <https://github.com/aio-libs/aioredis>`_ driver
"""
import asyncio
import logging
import typing
import aioredis
from ...dispatcher.storage import BaseStorage
from ...utils import json
STATE_KEY = 'state'
STATE_DATA_KEY = 'data'
STATE_BUCKET_KEY = 'bucket'
class RedisStorage(BaseStorage):
"""
Simple Redis-base storage for FSM.
Usage:
.. code-block:: python3
storage = RedisStorage('localhost', 6379, db=5)
dp = Dispatcher(bot, storage=storage)
And need to close Redis connection when shutdown
.. code-block:: python3
await dp.storage.close()
await dp.storage.wait_closed()
"""
def __init__(self, host='localhost', port=6379, db=None, password=None, ssl=None, loop=None, **kwargs):
self._host = host
self._port = port
self._db = db
self._password = password
self._ssl = ssl
self._loop = loop or asyncio.get_event_loop()
self._kwargs = kwargs
self._redis: typing.Optional[aioredis.RedisConnection] = None
self._connection_lock = asyncio.Lock(loop=self._loop)
async def close(self):
async with self._connection_lock:
if self._redis and not self._redis.closed:
self._redis.close()
async def wait_closed(self):
async with self._connection_lock:
if self._redis:
return await self._redis.wait_closed()
return True
async def redis(self) -> aioredis.RedisConnection:
"""
Get Redis connection
"""
# Use thread-safe asyncio Lock because this method without that is not safe
async with self._connection_lock:
if self._redis is None or self._redis.closed:
self._redis = await aioredis.create_connection((self._host, self._port),
db=self._db, password=self._password, ssl=self._ssl,
loop=self._loop,
**self._kwargs)
return self._redis
async def get_record(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None) -> typing.Dict:
"""
Get record from storage
:param chat:
:param user:
:return:
"""
chat, user = self.check_address(chat=chat, user=user)
addr = f"fsm:{chat}:{user}"
conn = await self.redis()
data = await conn.execute('GET', addr)
if data is None:
return {'state': None, 'data': {}}
return json.loads(data)
async def set_record(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
state=None, data=None, bucket=None):
"""
Write record to storage
:param bucket:
:param chat:
:param user:
:param state:
:param data:
:return:
"""
if data is None:
data = {}
if bucket is None:
bucket = {}
chat, user = self.check_address(chat=chat, user=user)
addr = f"fsm:{chat}:{user}"
conn = await self.redis()
if state is None and data == bucket == {}:
await conn.execute('DEL', addr)
else:
record = {'state': state, 'data': data, 'bucket': bucket}
await conn.execute('SET', addr, json.dumps(record))
async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Optional[str]:
record = await self.get_record(chat=chat, user=user)
return record.get('state', self.resolve_state(default))
async def get_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Dict:
record = await self.get_record(chat=chat, user=user)
return record['data']
async def set_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
state: typing.Optional[typing.AnyStr] = None):
record = await self.get_record(chat=chat, user=user)
state = self.resolve_state(state)
await self.set_record(chat=chat, user=user, state=state, data=record['data'])
async def set_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
data: typing.Dict = None):
record = await self.get_record(chat=chat, user=user)
await self.set_record(chat=chat, user=user, state=record['state'], data=data)
async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
data: typing.Dict = None, **kwargs):
if data is None:
data = {}
record = await self.get_record(chat=chat, user=user)
record_data = record.get('data', {})
record_data.update(data, **kwargs)
await self.set_record(chat=chat, user=user, state=record['state'], data=record_data)
async def get_states_list(self) -> typing.List[typing.Tuple[str, str]]:
"""
Get list of all stored chat's and user's
:return: list of tuples where first element is chat id and second is user id
"""
conn = await self.redis()
result = []
keys = await conn.execute('KEYS', 'fsm:*')
for item in keys:
*_, chat, user = item.decode('utf-8').split(':')
result.append((chat, user))
return result
async def reset_all(self, full=True):
"""
Reset states in DB
:param full: clean DB or clean only states
:return:
"""
conn = await self.redis()
if full:
await conn.execute('FLUSHDB')
else:
keys = await conn.execute('KEYS', 'fsm:*')
await conn.execute('DEL', *keys)
def has_bucket(self):
return True
async def get_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Dict:
record = await self.get_record(chat=chat, user=user)
return record.get('bucket', {})
async def set_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None):
record = await self.get_record(chat=chat, user=user)
await self.set_record(chat=chat, user=user, state=record['state'], data=record['data'], bucket=bucket)
async def update_bucket(self, *, chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None, **kwargs):
record = await self.get_record(chat=chat, user=user)
record_bucket = record.get('bucket', {})
if bucket is None:
bucket = {}
record_bucket.update(bucket, **kwargs)
await self.set_record(chat=chat, user=user, state=record['state'], data=record_bucket, bucket=bucket)
class RedisStorage2(BaseStorage):
"""
Busted Redis-base storage for FSM.
Works with Redis connection pool and customizable keys prefix.
Usage:
.. code-block:: python3
storage = RedisStorage2('localhost', 6379, db=5, pool_size=10, prefix='my_fsm_key')
dp = Dispatcher(bot, storage=storage)
And need to close Redis connection when shutdown
.. code-block:: python3
await dp.storage.close()
await dp.storage.wait_closed()
"""
def __init__(self, host: str = 'localhost', port=6379, db=None, password=None,
ssl=None, pool_size=10, loop=None, prefix='fsm',
state_ttl: int = 0,
data_ttl: int = 0,
bucket_ttl: int = 0,
**kwargs):
self._host = host
self._port = port
self._db = db
self._password = password
self._ssl = ssl
self._pool_size = pool_size
self._loop = loop or asyncio.get_event_loop()
self._kwargs = kwargs
self._prefix = (prefix,)
self._state_ttl = state_ttl
self._data_ttl = data_ttl
self._bucket_ttl = bucket_ttl
self._redis: typing.Optional[aioredis.RedisConnection] = None
self._connection_lock = asyncio.Lock(loop=self._loop)
async def redis(self) -> aioredis.Redis:
"""
Get Redis connection
"""
# Use thread-safe asyncio Lock because this method without that is not safe
async with self._connection_lock:
if self._redis is None or self._redis.closed:
self._redis = await aioredis.create_redis_pool((self._host, self._port),
db=self._db, password=self._password, ssl=self._ssl,
minsize=1, maxsize=self._pool_size,
loop=self._loop, **self._kwargs)
return self._redis
def generate_key(self, *parts):
return ':'.join(self._prefix + tuple(map(str, parts)))
async def close(self):
async with self._connection_lock:
if self._redis and not self._redis.closed:
self._redis.close()
async def wait_closed(self):
async with self._connection_lock:
if self._redis:
return await self._redis.wait_closed()
return True
async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Optional[str]:
chat, user = self.check_address(chat=chat, user=user)
key = self.generate_key(chat, user, STATE_KEY)
redis = await self.redis()
return await redis.get(key, encoding='utf8') or self.resolve_state(default)
async def get_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[dict] = None) -> typing.Dict:
chat, user = self.check_address(chat=chat, user=user)
key = self.generate_key(chat, user, STATE_DATA_KEY)
redis = await self.redis()
raw_result = await redis.get(key, encoding='utf8')
if raw_result:
return json.loads(raw_result)
return default or {}
async def set_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
state: typing.Optional[typing.AnyStr] = None):
chat, user = self.check_address(chat=chat, user=user)
key = self.generate_key(chat, user, STATE_KEY)
redis = await self.redis()
if state is None:
await redis.delete(key)
else:
await redis.set(key, self.resolve_state(state), expire=self._state_ttl)
async def set_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
data: typing.Dict = None):
chat, user = self.check_address(chat=chat, user=user)
key = self.generate_key(chat, user, STATE_DATA_KEY)
redis = await self.redis()
if data:
await redis.set(key, json.dumps(data), expire=self._data_ttl)
else:
await redis.delete(key)
async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
data: typing.Dict = None, **kwargs):
if data is None:
data = {}
temp_data = await self.get_data(chat=chat, user=user, default={})
temp_data.update(data, **kwargs)
await self.set_data(chat=chat, user=user, data=temp_data)
def has_bucket(self):
return True
async def get_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[dict] = None) -> typing.Dict:
chat, user = self.check_address(chat=chat, user=user)
key = self.generate_key(chat, user, STATE_BUCKET_KEY)
redis = await self.redis()
raw_result = await redis.get(key, encoding='utf8')
if raw_result:
return json.loads(raw_result)
return default or {}
async def set_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None):
chat, user = self.check_address(chat=chat, user=user)
key = self.generate_key(chat, user, STATE_BUCKET_KEY)
redis = await self.redis()
if bucket:
await redis.set(key, json.dumps(bucket), expire=self._bucket_ttl)
else:
await redis.delete(key)
async def update_bucket(self, *, chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None, **kwargs):
if bucket is None:
bucket = {}
temp_bucket = await self.get_bucket(chat=chat, user=user)
temp_bucket.update(bucket, **kwargs)
await self.set_bucket(chat=chat, user=user, bucket=temp_bucket)
async def reset_all(self, full=True):
"""
Reset states in DB
:param full: clean DB or clean only states
:return:
"""
conn = await self.redis()
if full:
await conn.flushdb()
else:
keys = await conn.keys(self.generate_key('*'))
await conn.delete(*keys)
async def get_states_list(self) -> typing.List[typing.Tuple[str, str]]:
"""
Get list of all stored chat's and user's
:return: list of tuples where first element is chat id and second is user id
"""
conn = await self.redis()
result = []
keys = await conn.keys(self.generate_key('*', '*', STATE_KEY), encoding='utf8')
for item in keys:
*_, chat, user, _ = item.split(':')
result.append((chat, user))
return result
async def import_redis1(self, redis1):
await migrate_redis1_to_redis2(redis1, self)
async def migrate_redis1_to_redis2(storage1: RedisStorage, storage2: RedisStorage2):
"""
Helper for migrating from RedisStorage to RedisStorage2
:param storage1: instance of RedisStorage
:param storage2: instance of RedisStorage2
:return:
"""
if not isinstance(storage1, RedisStorage): # better than assertion
raise TypeError(f"{type(storage1)} is not RedisStorage instance.")
if not isinstance(storage2, RedisStorage):
raise TypeError(f"{type(storage2)} is not RedisStorage instance.")
log = logging.getLogger('aiogram.RedisStorage')
for chat, user in await storage1.get_states_list():
state = await storage1.get_state(chat=chat, user=user)
await storage2.set_state(chat=chat, user=user, state=state)
data = await storage1.get_data(chat=chat, user=user)
await storage2.set_data(chat=chat, user=user, data=data)
bucket = await storage1.get_bucket(chat=chat, user=user)
await storage2.set_bucket(chat=chat, user=user, bucket=bucket)
log.info(f"Migrated user {user} in chat {chat}")

View File

@ -0,0 +1,184 @@
import asyncio
import contextlib
import typing
import rethinkdb
from rethinkdb.asyncio_net.net_asyncio import Connection
from ...dispatcher.storage import BaseStorage
__all__ = ('RethinkDBStorage',)
r = rethinkdb.RethinkDB()
r.set_loop_type('asyncio')
class RethinkDBStorage(BaseStorage):
"""
RethinkDB-based storage for FSM.
Usage:
.. code-block:: python3
storage = RethinkDBStorage(db='aiogram', table='aiogram', user='aiogram', password='aiogram_secret')
dispatcher = Dispatcher(bot, storage=storage)
And need to close connection when shutdown
.. code-block:: python3
await storage.close()
await storage.wait_closed()
"""
def __init__(self,
host: str = 'localhost',
port: int = 28015,
db: str = 'aiogram',
table: str = 'aiogram',
auth_key: typing.Optional[str] = None,
user: typing.Optional[str] = None,
password: typing.Optional[str] = None,
timeout: int = 20,
ssl: typing.Optional[dict] = None,
loop: typing.Optional[asyncio.AbstractEventLoop] = None):
self._host = host
self._port = port
self._db = db
self._table = table
self._auth_key = auth_key
self._user = user
self._password = password
self._timeout = timeout
self._ssl = ssl or {}
self._loop = loop
self._conn: typing.Optional[Connection] = None
async def connect(self) -> Connection:
"""
Get or create a connection.
"""
if self._conn is None:
self._conn = await r.connect(host=self._host,
port=self._port,
db=self._db,
auth_key=self._auth_key,
user=self._user,
password=self._password,
timeout=self._timeout,
ssl=self._ssl,
io_loop=self._loop)
return self._conn
@contextlib.asynccontextmanager
async def connection(self):
conn = await self.connect()
yield conn
async def close(self):
"""
Close a connection.
"""
self._conn.close()
self._conn = None
async def wait_closed(self):
"""
Does nothing
"""
pass
async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Optional[str]:
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
return await r.table(self._table).get(chat)[user]['state'].default(
self.resolve_state(default) or None
).run(conn)
async def get_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Dict:
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
return await r.table(self._table).get(chat)[user]['data'].default(default or {}).run(conn)
async def set_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
state: typing.Optional[typing.AnyStr] = None):
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
await r.table(self._table).insert(
{'id': chat, user: {'state': self.resolve_state(state)}},
conflict="update",
).run(conn)
async def set_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
data: typing.Dict = None):
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
if await r.table(self._table).get(chat).run(conn):
await r.table(self._table).get(chat).update({user: {'data': r.literal(data)}}).run(conn)
else:
await r.table(self._table).insert({'id': chat, user: {'data': data}}).run(conn)
async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
data: typing.Dict = None,
**kwargs):
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
await r.table(self._table).insert({'id': chat, user: {'data': data}}, conflict="update").run(conn)
def has_bucket(self):
return True
async def get_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
default: typing.Optional[dict] = None) -> typing.Dict:
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
return await r.table(self._table).get(chat)[user]['bucket'].default(default or {}).run(conn)
async def set_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None):
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
if await r.table(self._table).get(chat).run(conn):
await r.table(self._table).get(chat).update({user: {'bucket': r.literal(bucket)}}).run(conn)
else:
await r.table(self._table).insert({'id': chat, user: {'bucket': bucket}}).run(conn)
async def update_bucket(self, *, chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None, bucket: typing.Dict = None,
**kwargs):
chat, user = map(str, self.check_address(chat=chat, user=user))
async with self.connection() as conn:
await r.table(self._table).insert({'id': chat, user: {'bucket': bucket}}, conflict="update").run(conn)
async def get_states_list(self) -> typing.List[typing.Tuple[int, int]]:
"""
Get list of all stored chat's and user's
:return: list of tuples where first element is chat id and second is user id
"""
async with self.connection() as conn:
result = []
items = (await r.table(self._table).run(conn)).items
for item in items:
chat = int(item.pop('id'))
for key in item.keys():
user = int(key)
result.append((chat, user))
return result
async def reset_all(self):
"""
Reset states in DB
"""
async with self.connection() as conn:
await r.table(self._table).delete().run(conn)

View File

@ -0,0 +1,27 @@
import asyncio
from aiogram.dispatcher.middlewares import BaseMiddleware
class EnvironmentMiddleware(BaseMiddleware):
def __init__(self, context=None):
super(EnvironmentMiddleware, self).__init__()
if context is None:
context = {}
self.context = context
def update_data(self, data):
dp = self.manager.dispatcher
data.update(
bot=dp.bot,
dispatcher=dp,
loop=dp.loop or asyncio.get_event_loop()
)
if self.context:
data.update(self.context)
async def trigger(self, action, args):
if 'error' not in action and action.startswith('pre_process_'):
self.update_data(args[-1])
return True

View File

@ -0,0 +1,80 @@
import copy
import weakref
from aiogram.dispatcher.middlewares import LifetimeControllerMiddleware
from aiogram.dispatcher.storage import FSMContext
class FSMMiddleware(LifetimeControllerMiddleware):
skip_patterns = ['error', 'update']
def __init__(self):
super(FSMMiddleware, self).__init__()
self._proxies = weakref.WeakKeyDictionary()
async def pre_process(self, obj, data, *args):
proxy = await FSMSStorageProxy.create(self.manager.dispatcher.current_state())
data['state_data'] = proxy
async def post_process(self, obj, data, *args):
proxy = data.get('state_data', None)
if isinstance(proxy, FSMSStorageProxy):
await proxy.save()
class FSMSStorageProxy(dict):
def __init__(self, fsm_context: FSMContext):
super(FSMSStorageProxy, self).__init__()
self.fsm_context = fsm_context
self._copy = {}
self._data = {}
self._state = None
self._is_dirty = False
@classmethod
async def create(cls, fsm_context: FSMContext):
"""
:param fsm_context:
:return:
"""
proxy = cls(fsm_context)
await proxy.load()
return proxy
async def load(self):
self.clear()
self._state = await self.fsm_context.get_state()
self.update(await self.fsm_context.get_data())
self._copy = copy.deepcopy(self)
self._is_dirty = False
@property
def state(self):
return self._state
@state.setter
def state(self, value):
self._state = value
self._is_dirty = True
@state.deleter
def state(self):
self._state = None
self._is_dirty = True
async def save(self, force=False):
if self._copy != self or force:
await self.fsm_context.set_data(data=self)
if self._is_dirty or force:
await self.fsm_context.set_state(self.state)
self._is_dirty = False
self._copy = copy.deepcopy(self)
def __str__(self):
s = super(FSMSStorageProxy, self).__str__()
readable_state = f"'{self.state}'" if self.state else "''"
return f"<{self.__class__.__name__}(state={readable_state}, data={s})>"
def clear(self):
del self.state
return super(FSMSStorageProxy, self).clear()

View File

@ -0,0 +1,154 @@
import gettext
import os
from contextvars import ContextVar
from typing import Any, Dict, Tuple, Optional
from babel import Locale
from babel.support import LazyProxy
from ... import types
from ...dispatcher.middlewares import BaseMiddleware
class I18nMiddleware(BaseMiddleware):
"""
I18n middleware based on gettext util
>>> dp = Dispatcher(bot)
>>> i18n = I18nMiddleware(DOMAIN, LOCALES_DIR)
>>> dp.middleware.setup(i18n)
and then
>>> _ = i18n.gettext
or
>>> _ = i18n = I18nMiddleware(DOMAIN_NAME, LOCALES_DIR)
"""
ctx_locale = ContextVar('ctx_user_locale', default=None)
def __init__(self, domain, path=None, default='en'):
"""
:param domain: domain
:param path: path where located all *.mo files
:param default: default locale name
"""
super(I18nMiddleware, self).__init__()
if path is None:
path = os.path.join(os.getcwd(), 'locales')
self.domain = domain
self.path = path
self.default = default
self.locales = self.find_locales()
def find_locales(self) -> Dict[str, gettext.GNUTranslations]:
"""
Load all compiled locales from path
:return: dict with locales
"""
translations = {}
for name in os.listdir(self.path):
if not os.path.isdir(os.path.join(self.path, name)):
continue
mo_path = os.path.join(self.path, name, 'LC_MESSAGES', self.domain + '.mo')
if os.path.exists(mo_path):
with open(mo_path, 'rb') as fp:
translations[name] = gettext.GNUTranslations(fp)
elif os.path.exists(mo_path[:-2] + 'po'):
raise RuntimeError(f"Found locale '{name}' but this language is not compiled!")
return translations
def reload(self):
"""
Hot reload locales
"""
self.locales = self.find_locales()
@property
def available_locales(self) -> Tuple[str]:
"""
list of loaded locales
:return:
"""
return tuple(self.locales.keys())
def __call__(self, singular, plural=None, n=1, locale=None) -> str:
return self.gettext(singular, plural, n, locale)
def gettext(self, singular, plural=None, n=1, locale=None) -> str:
"""
Get text
:param singular:
:param plural:
:param n:
:param locale:
:return:
"""
if locale is None:
locale = self.ctx_locale.get()
if locale not in self.locales:
if n == 1:
return singular
return plural
translator = self.locales[locale]
if plural is None:
return translator.gettext(singular)
return translator.ngettext(singular, plural, n)
def lazy_gettext(self, singular, plural=None, n=1, locale=None, enable_cache=False) -> LazyProxy:
"""
Lazy get text
:param singular:
:param plural:
:param n:
:param locale:
:param enable_cache:
:return:
"""
return LazyProxy(self.gettext, singular, plural, n, locale, enable_cache=enable_cache)
# noinspection PyMethodMayBeStatic,PyUnusedLocal
async def get_user_locale(self, action: str, args: Tuple[Any]) -> Optional[str]:
"""
User locale getter
You can override the method if you want to use different way of
getting user language.
:param action: event name
:param args: event arguments
:return: locale name or None
"""
user: Optional[types.User] = types.User.get_current()
locale: Optional[Locale] = user.locale if user else None
if locale and locale.language in self.locales:
*_, data = args
language = data['locale'] = locale.language
return language
return self.default
async def trigger(self, action, args):
"""
Event trigger
:param action: event name
:param args: event arguments
:return:
"""
if 'update' not in action \
and 'error' not in action \
and action.startswith('pre_process'):
locale = await self.get_user_locale(action, args)
self.ctx_locale.set(locale)
return True

View File

@ -0,0 +1,461 @@
import time
import logging
from aiogram import types
from aiogram.dispatcher.middlewares import BaseMiddleware
HANDLED_STR = ['Unhandled', 'Handled']
class LoggingMiddleware(BaseMiddleware):
def __init__(self, logger=__name__):
if not isinstance(logger, logging.Logger):
logger = logging.getLogger(logger)
self.logger = logger
super(LoggingMiddleware, self).__init__()
def check_timeout(self, obj):
start = obj.conf.get('_start', None)
if start:
del obj.conf['_start']
return round((time.time() - start) * 1000)
return -1
async def on_pre_process_update(self, update: types.Update, data: dict):
update.conf['_start'] = time.time()
self.logger.debug(f"Received update [ID:{update.update_id}]")
async def on_post_process_update(self, update: types.Update, result, data: dict):
timeout = self.check_timeout(update)
if timeout > 0:
self.logger.info(f"Process update [ID:{update.update_id}]: [success] (in {timeout} ms)")
async def on_pre_process_message(self, message: types.Message, data: dict):
self.logger.info(f"Received message [ID:{message.message_id}] in chat [{message.chat.type}:{message.chat.id}]")
async def on_post_process_message(self, message: types.Message, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"message [ID:{message.message_id}] in chat [{message.chat.type}:{message.chat.id}]")
async def on_pre_process_edited_message(self, edited_message, data: dict):
self.logger.info(f"Received edited message [ID:{edited_message.message_id}] "
f"in chat [{edited_message.chat.type}:{edited_message.chat.id}]")
async def on_post_process_edited_message(self, edited_message, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"edited message [ID:{edited_message.message_id}] "
f"in chat [{edited_message.chat.type}:{edited_message.chat.id}]")
async def on_pre_process_channel_post(self, channel_post: types.Message, data: dict):
self.logger.info(f"Received channel post [ID:{channel_post.message_id}] "
f"in channel [ID:{channel_post.chat.id}]")
async def on_post_process_channel_post(self, channel_post: types.Message, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"channel post [ID:{channel_post.message_id}] "
f"in chat [{channel_post.chat.type}:{channel_post.chat.id}]")
async def on_pre_process_edited_channel_post(self, edited_channel_post: types.Message, data: dict):
self.logger.info(f"Received edited channel post [ID:{edited_channel_post.message_id}] "
f"in channel [ID:{edited_channel_post.chat.id}]")
async def on_post_process_edited_channel_post(self, edited_channel_post: types.Message, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"edited channel post [ID:{edited_channel_post.message_id}] "
f"in channel [ID:{edited_channel_post.chat.id}]")
async def on_pre_process_inline_query(self, inline_query: types.InlineQuery, data: dict):
self.logger.info(f"Received inline query [ID:{inline_query.id}] "
f"from user [ID:{inline_query.from_user.id}]")
async def on_post_process_inline_query(self, inline_query: types.InlineQuery, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"inline query [ID:{inline_query.id}] "
f"from user [ID:{inline_query.from_user.id}]")
async def on_pre_process_chosen_inline_result(self, chosen_inline_result: types.ChosenInlineResult, data: dict):
self.logger.info(f"Received chosen inline result [Inline msg ID:{chosen_inline_result.inline_message_id}] "
f"from user [ID:{chosen_inline_result.from_user.id}] "
f"result [ID:{chosen_inline_result.result_id}]")
async def on_post_process_chosen_inline_result(self, chosen_inline_result, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"chosen inline result [Inline msg ID:{chosen_inline_result.inline_message_id}] "
f"from user [ID:{chosen_inline_result.from_user.id}] "
f"result [ID:{chosen_inline_result.result_id}]")
async def on_pre_process_callback_query(self, callback_query: types.CallbackQuery, data: dict):
if callback_query.message:
text = (f"Received callback query [ID:{callback_query.id}] "
f"from user [ID:{callback_query.from_user.id}] "
f"for message [ID:{callback_query.message.message_id}] "
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]")
if callback_query.message.from_user:
text += f" originally posted by user [ID:{callback_query.message.from_user.id}]"
self.logger.info(text)
else:
self.logger.info(f"Received callback query [ID:{callback_query.id}] "
f"from user [ID:{callback_query.from_user.id}] "
f"for inline message [ID:{callback_query.inline_message_id}] ")
async def on_post_process_callback_query(self, callback_query, results, data: dict):
if callback_query.message:
text = (f"{HANDLED_STR[bool(len(results))]} "
f"callback query [ID:{callback_query.id}] "
f"from user [ID:{callback_query.from_user.id}] "
f"for message [ID:{callback_query.message.message_id}] "
f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]")
if callback_query.message.from_user:
text += f" originally posted by user [ID:{callback_query.message.from_user.id}]"
self.logger.info(text)
else:
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"callback query [ID:{callback_query.id}] "
f"from user [ID:{callback_query.from_user.id}]"
f"from inline message [ID:{callback_query.inline_message_id}]")
async def on_pre_process_shipping_query(self, shipping_query: types.ShippingQuery, data: dict):
self.logger.info(f"Received shipping query [ID:{shipping_query.id}] "
f"from user [ID:{shipping_query.from_user.id}]")
async def on_post_process_shipping_query(self, shipping_query, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"shipping query [ID:{shipping_query.id}] "
f"from user [ID:{shipping_query.from_user.id}]")
async def on_pre_process_pre_checkout_query(self, pre_checkout_query: types.PreCheckoutQuery, data: dict):
self.logger.info(f"Received pre-checkout query [ID:{pre_checkout_query.id}] "
f"from user [ID:{pre_checkout_query.from_user.id}]")
async def on_post_process_pre_checkout_query(self, pre_checkout_query, results, data: dict):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} "
f"pre-checkout query [ID:{pre_checkout_query.id}] "
f"from user [ID:{pre_checkout_query.from_user.id}]")
async def on_pre_process_error(self, update, error, data: dict):
timeout = self.check_timeout(update)
if timeout > 0:
self.logger.info(f"Process update [ID:{update.update_id}]: [failed] (in {timeout} ms)")
async def on_pre_process_poll(self, poll, data):
self.logger.info(f"Received poll [ID:{poll.id}]")
async def on_post_process_poll(self, poll, results, data):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} poll [ID:{poll.id}]")
async def on_pre_process_poll_answer(self, poll_answer, data):
self.logger.info(f"Received poll answer [ID:{poll_answer.poll_id}] "
f"from user [ID:{poll_answer.user.id}]")
async def on_post_process_poll_answer(self, poll_answer, results, data):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} poll answer [ID:{poll_answer.poll_id}] "
f"from user [ID:{poll_answer.user.id}]")
async def on_pre_process_my_chat_member(self, my_chat_member_update, data):
self.logger.info(f"Received chat member update "
f"for user [ID:{my_chat_member_update.from_user.id}]. "
f"Old state: {my_chat_member_update.old_chat_member.to_python()} "
f"New state: {my_chat_member_update.new_chat_member.to_python()} ")
async def on_post_process_my_chat_member(self, my_chat_member_update, results, data):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} my_chat_member "
f"for user [ID:{my_chat_member_update.from_user.id}]")
async def on_pre_process_chat_member(self, chat_member_update, data):
self.logger.info(f"Received chat member update "
f"for user [ID:{chat_member_update.from_user.id}]. "
f"Old state: {chat_member_update.old_chat_member.to_python()} "
f"New state: {chat_member_update.new_chat_member.to_python()} ")
async def on_post_process_chat_member(self, chat_member_update, results, data):
self.logger.debug(f"{HANDLED_STR[bool(len(results))]} chat_member "
f"for user [ID:{chat_member_update.from_user.id}]")
class LoggingFilter(logging.Filter):
"""
Extend LogRecord by data from Telegram Update object.
Can be used in logging config:
.. code-block: python3
'filters': {
'telegram': {
'()': LoggingFilter,
'include_content': True,
}
},
...
'handlers': {
'graypy': {
'()': GELFRabbitHandler,
'url': 'amqp://localhost:5672/',
'routing_key': '#',
'localname': 'testapp',
'filters': ['telegram']
},
},
"""
def __init__(self, name='', prefix='tg', include_content=False):
"""
:param name:
:param prefix: prefix for all records
:param include_content: pass into record all data from Update object
"""
super(LoggingFilter, self).__init__(name=name)
self.prefix = prefix
self.include_content = include_content
def filter(self, record: logging.LogRecord):
"""
Extend LogRecord by data from Telegram Update object.
:param record:
:return:
"""
update = types.Update.get_current(True)
if update:
for key, value in self.make_prefix(self.prefix, self.process_update(update)):
setattr(record, key, value)
return True
def process_update(self, update: types.Update):
"""
Parse Update object
:param update:
:return:
"""
yield 'update_id', update.update_id
if update.message:
yield 'update_type', 'message'
yield from self.process_message(update.message)
if update.edited_message:
yield 'update_type', 'edited_message'
yield from self.process_message(update.edited_message)
if update.channel_post:
yield 'update_type', 'channel_post'
yield from self.process_message(update.channel_post)
if update.edited_channel_post:
yield 'update_type', 'edited_channel_post'
yield from self.process_message(update.edited_channel_post)
if update.inline_query:
yield 'update_type', 'inline_query'
yield from self.process_inline_query(update.inline_query)
if update.chosen_inline_result:
yield 'update_type', 'chosen_inline_result'
yield from self.process_chosen_inline_result(update.chosen_inline_result)
if update.callback_query:
yield 'update_type', 'callback_query'
yield from self.process_callback_query(update.callback_query)
if update.shipping_query:
yield 'update_type', 'shipping_query'
yield from self.process_shipping_query(update.shipping_query)
if update.pre_checkout_query:
yield 'update_type', 'pre_checkout_query'
yield from self.process_pre_checkout_query(update.pre_checkout_query)
def make_prefix(self, prefix, iterable):
"""
Add prefix to the label
:param prefix:
:param iterable:
:return:
"""
if not prefix:
yield from iterable
for key, value in iterable:
yield f"{prefix}_{key}", value
def process_user(self, user: types.User):
"""
Generate user data
:param user:
:return:
"""
if not user:
return
yield 'user_id', user.id
if self.include_content:
yield 'user_full_name', user.full_name
if user.username:
yield 'user_name', f"@{user.username}"
def process_chat(self, chat: types.Chat):
"""
Generate chat data
:param chat:
:return:
"""
if not chat:
return
yield 'chat_id', chat.id
yield 'chat_type', chat.type
if self.include_content:
yield 'chat_title', chat.full_name
if chat.username:
yield 'chat_name', f"@{chat.username}"
def process_message(self, message: types.Message):
yield 'message_content_type', message.content_type
yield from self.process_user(message.from_user)
yield from self.process_chat(message.chat)
if not self.include_content:
return
if message.reply_to_message:
yield from self.make_prefix('reply_to', self.process_message(message.reply_to_message))
if message.forward_from:
yield from self.make_prefix('forward_from', self.process_user(message.forward_from))
if message.forward_from_chat:
yield from self.make_prefix('forward_from_chat', self.process_chat(message.forward_from_chat))
if message.forward_from_message_id:
yield 'message_forward_from_message_id', message.forward_from_message_id
if message.forward_date:
yield 'message_forward_date', message.forward_date
if message.edit_date:
yield 'message_edit_date', message.edit_date
if message.media_group_id:
yield 'message_media_group_id', message.media_group_id
if message.author_signature:
yield 'message_author_signature', message.author_signature
if message.text:
yield 'text', message.text or message.caption
yield 'html_text', message.html_text
elif message.audio:
yield 'audio', message.audio.file_id
elif message.animation:
yield 'animation', message.animation.file_id
elif message.document:
yield 'document', message.document.file_id
elif message.game:
yield 'game', message.game.title
elif message.photo:
yield 'photo', message.photo[-1].file_id
elif message.sticker:
yield 'sticker', message.sticker.file_id
elif message.video:
yield 'video', message.video.file_id
elif message.video_note:
yield 'video_note', message.video_note.file_id
elif message.voice:
yield 'voice', message.voice.file_id
elif message.contact:
yield 'contact_full_name', message.contact.full_name
yield 'contact_phone_number', message.contact.phone_number
elif message.venue:
yield 'venue_address', message.venue.address
yield 'location_latitude', message.venue.location.latitude
yield 'location_longitude', message.venue.location.longitude
elif message.location:
yield 'location_latitude', message.location.latitude
yield 'location_longitude', message.location.longitude
elif message.new_chat_members:
yield 'new_chat_members', [user.id for user in message.new_chat_members]
elif message.left_chat_member:
yield 'left_chat_member', [user.id for user in message.new_chat_members]
elif message.invoice:
yield 'invoice_title', message.invoice.title
yield 'invoice_description', message.invoice.description
yield 'invoice_start_parameter', message.invoice.start_parameter
yield 'invoice_currency', message.invoice.currency
yield 'invoice_total_amount', message.invoice.total_amount
elif message.successful_payment:
yield 'successful_payment_currency', message.successful_payment.currency
yield 'successful_payment_total_amount', message.successful_payment.total_amount
yield 'successful_payment_invoice_payload', message.successful_payment.invoice_payload
yield 'successful_payment_shipping_option_id', message.successful_payment.shipping_option_id
yield 'successful_payment_telegram_payment_charge_id', message.successful_payment.telegram_payment_charge_id
yield 'successful_payment_provider_payment_charge_id', message.successful_payment.provider_payment_charge_id
elif message.connected_website:
yield 'connected_website', message.connected_website
elif message.migrate_from_chat_id:
yield 'migrate_from_chat_id', message.migrate_from_chat_id
elif message.migrate_to_chat_id:
yield 'migrate_to_chat_id', message.migrate_to_chat_id
elif message.pinned_message:
yield from self.make_prefix('pinned_message', message.pinned_message)
elif message.new_chat_title:
yield 'new_chat_title', message.new_chat_title
elif message.new_chat_photo:
yield 'new_chat_photo', message.new_chat_photo[-1].file_id
# elif message.delete_chat_photo:
# yield 'delete_chat_photo', message.delete_chat_photo
# elif message.group_chat_created:
# yield 'group_chat_created', message.group_chat_created
# elif message.passport_data:
# yield 'passport_data', message.passport_data
def process_inline_query(self, inline_query: types.InlineQuery):
yield 'inline_query_id', inline_query.id
yield from self.process_user(inline_query.from_user)
if self.include_content:
yield 'inline_query_text', inline_query.query
if inline_query.location:
yield 'location_latitude', inline_query.location.latitude
yield 'location_longitude', inline_query.location.longitude
if inline_query.offset:
yield 'inline_query_offset', inline_query.offset
def process_chosen_inline_result(self, chosen_inline_result: types.ChosenInlineResult):
yield 'chosen_inline_result_id', chosen_inline_result.result_id
yield from self.process_user(chosen_inline_result.from_user)
if self.include_content:
yield 'inline_query_text', chosen_inline_result.query
if chosen_inline_result.location:
yield 'location_latitude', chosen_inline_result.location.latitude
yield 'location_longitude', chosen_inline_result.location.longitude
def process_callback_query(self, callback_query: types.CallbackQuery):
yield from self.process_user(callback_query.from_user)
yield 'callback_query_data', callback_query.data
if callback_query.message:
yield from self.make_prefix('callback_query_message', self.process_message(callback_query.message))
if callback_query.inline_message_id:
yield 'callback_query_inline_message_id', callback_query.inline_message_id
if callback_query.chat_instance:
yield 'callback_query_chat_instance', callback_query.chat_instance
if callback_query.game_short_name:
yield 'callback_query_game_short_name', callback_query.game_short_name
def process_shipping_query(self, shipping_query: types.ShippingQuery):
yield 'shipping_query_id', shipping_query.id
yield from self.process_user(shipping_query.from_user)
if self.include_content:
yield 'shipping_query_invoice_payload', shipping_query.invoice_payload
def process_pre_checkout_query(self, pre_checkout_query: types.PreCheckoutQuery):
yield 'pre_checkout_query_id', pre_checkout_query.id
yield from self.process_user(pre_checkout_query.from_user)
if self.include_content:
yield 'pre_checkout_query_currency', pre_checkout_query.currency
yield 'pre_checkout_query_total_amount', pre_checkout_query.total_amount
yield 'pre_checkout_query_invoice_payload', pre_checkout_query.invoice_payload
yield 'pre_checkout_query_shipping_option_id', pre_checkout_query.shipping_option_id

View File

@ -0,0 +1,17 @@
from . import filters
from . import handler
from . import middlewares
from . import storage
from . import webhook
from .dispatcher import Dispatcher, FSMContext, DEFAULT_RATE_LIMIT
__all__ = (
'DEFAULT_RATE_LIMIT',
'Dispatcher',
'FSMContext',
'filters',
'handler',
'middlewares',
'storage',
'webhook'
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
from .builtin import Command, CommandHelp, CommandPrivacy, CommandSettings, CommandStart, ContentTypeFilter, \
ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, \
Text, IDFilter, AdminFilter, IsReplyFilter, IsSenderContact, ForwardedMessageFilter, \
ChatTypeFilter, MediaGroupFilter
from .factory import FiltersFactory
from .filters import AbstractFilter, BoundFilter, Filter, FilterNotPassed, FilterRecord, execute_filter, \
check_filters, get_filter_spec, get_filters_spec
__all__ = (
'Command',
'CommandHelp',
'CommandPrivacy',
'CommandSettings',
'CommandStart',
'ContentTypeFilter',
'ExceptionsFilter',
'HashTag',
'Regexp',
'RegexpCommandsFilter',
'StateFilter',
'Text',
'IDFilter',
'AdminFilter',
'IsReplyFilter',
'IsSenderContact',
'ForwardedMessageFilter',
'ChatTypeFilter',
'MediaGroupFilter',
'FiltersFactory',
'AbstractFilter',
'BoundFilter',
'Filter',
'FilterNotPassed',
'FilterRecord',
'execute_filter',
'check_filters',
'get_filter_spec',
'get_filters_spec',
)

View File

@ -0,0 +1,758 @@
import inspect
import re
import typing
import warnings
from contextvars import ContextVar
from dataclasses import dataclass, field
from typing import Any, Dict, Iterable, List, Optional, Union
from babel.support import LazyProxy
from aiogram import types
from aiogram.dispatcher.filters.filters import BoundFilter, Filter
from aiogram.types import CallbackQuery, ChatType, InlineQuery, Message, Poll, ChatMemberUpdated
ChatIDArgumentType = typing.Union[typing.Iterable[typing.Union[int, str]], str, int]
def extract_chat_ids(chat_id: ChatIDArgumentType) -> typing.Set[int]:
# since "str" is also an "Iterable", we have to check for it first
if isinstance(chat_id, str):
return {int(chat_id), }
if isinstance(chat_id, Iterable):
return {int(item) for (item) in chat_id}
# the last possible type is a single "int"
return {chat_id, }
class Command(Filter):
"""
You can handle commands by using this filter.
If filter is successful processed the :obj:`Command.CommandObj` will be passed to the handler arguments.
By default this filter is registered for messages and edited messages handlers.
"""
def __init__(self, commands: Union[Iterable, str],
prefixes: Union[Iterable, str] = '/',
ignore_case: bool = True,
ignore_mention: bool = False,
ignore_caption: bool = True):
"""
Filter can be initialized from filters factory or by simply creating instance of this class.
Examples:
.. code-block:: python
@dp.message_handler(commands=['myCommand'])
@dp.message_handler(Command(['myCommand']))
@dp.message_handler(commands=['myCommand'], commands_prefix='!/')
:param commands: Command or list of commands always without leading slashes (prefix)
:param prefixes: Allowed commands prefix. By default is slash.
If you change the default behavior pass the list of prefixes to this argument.
:param ignore_case: Ignore case of the command
:param ignore_mention: Ignore mention in command
(By default this filter pass only the commands addressed to current bot)
:param ignore_caption: Ignore caption from message (in message types like photo, video, audio, etc)
By default is True. If you want check commands in captions, you also should set required content_types.
Examples:
.. code-block:: python
@dp.message_handler(commands=['myCommand'], commands_ignore_caption=False, content_types=ContentType.ANY)
@dp.message_handler(Command(['myCommand'], ignore_caption=False), content_types=[ContentType.TEXT, ContentType.DOCUMENT])
"""
if isinstance(commands, str):
commands = (commands,)
self.commands = list(map(str.lower, commands)) if ignore_case else commands
self.prefixes = prefixes
self.ignore_case = ignore_case
self.ignore_mention = ignore_mention
self.ignore_caption = ignore_caption
@classmethod
def validate(cls, full_config: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""
Validator for filters factory
From filters factory this filter can be registered with arguments:
- ``command``
- ``commands_prefix`` (will be passed as ``prefixes``)
- ``commands_ignore_mention`` (will be passed as ``ignore_mention``)
- ``commands_ignore_caption`` (will be passed as ``ignore_caption``)
:param full_config:
:return: config or empty dict
"""
config = {}
if 'commands' in full_config:
config['commands'] = full_config.pop('commands')
if config and 'commands_prefix' in full_config:
config['prefixes'] = full_config.pop('commands_prefix')
if config and 'commands_ignore_mention' in full_config:
config['ignore_mention'] = full_config.pop('commands_ignore_mention')
if config and 'commands_ignore_caption' in full_config:
config['ignore_caption'] = full_config.pop('commands_ignore_caption')
return config
async def check(self, message: types.Message):
return await self.check_command(message, self.commands, self.prefixes, self.ignore_case, self.ignore_mention, self.ignore_caption)
@classmethod
async def check_command(cls, message: types.Message, commands, prefixes, ignore_case=True, ignore_mention=False, ignore_caption=True):
text = message.text or (message.caption if not ignore_caption else None)
if not text:
return False
full_command, *args_list = text.split(maxsplit=1)
args = args_list[0] if args_list else None
prefix, (command, _, mention) = full_command[0], full_command[1:].partition('@')
if not ignore_mention and mention and (await message.bot.me).username.lower() != mention.lower():
return False
if prefix not in prefixes:
return False
if (command.lower() if ignore_case else command) not in commands:
return False
return {'command': cls.CommandObj(command=command, prefix=prefix, mention=mention, args=args)}
@dataclass
class CommandObj:
"""
Instance of this object is always has command and it prefix.
Can be passed as keyword argument ``command`` to the handler
"""
"""Command prefix"""
prefix: str = '/'
"""Command without prefix and mention"""
command: str = ''
"""Mention (if available)"""
mention: str = None
"""Command argument"""
args: str = field(repr=False, default=None)
@property
def mentioned(self) -> bool:
"""
This command has mention?
:return:
"""
return bool(self.mention)
@property
def text(self) -> str:
"""
Generate original text from object
:return:
"""
line = self.prefix + self.command
if self.mentioned:
line += '@' + self.mention
if self.args:
line += ' ' + self.args
return line
class CommandStart(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/start`` command.
"""
def __init__(self,
deep_link: typing.Optional[typing.Union[str, typing.Pattern[str]]] = None,
encoded: bool = False):
"""
Also this filter can handle `deep-linking <https://core.telegram.org/bots#deep-linking>`_ arguments.
Example:
.. code-block:: python
@dp.message_handler(CommandStart(re.compile(r'ref-([\\d]+)')))
:param deep_link: string or compiled regular expression (by ``re.compile(...)``).
:param encoded: set True if you're waiting for encoded payload (default - False).
"""
super().__init__(['start'])
self.deep_link = deep_link
self.encoded = encoded
async def check(self, message: types.Message):
"""
If deep-linking is passed to the filter result of the matching will be passed as ``deep_link`` to the handler
:param message:
:return:
"""
from ...utils.deep_linking import decode_payload
check = await super().check(message)
if check and self.deep_link is not None:
payload = decode_payload(message.get_args()) if self.encoded else message.get_args()
if not isinstance(self.deep_link, typing.Pattern):
return False if payload != self.deep_link else {'deep_link': payload}
match = self.deep_link.match(payload)
if match:
return {'deep_link': match}
return False
return check
class CommandHelp(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/help`` command.
"""
def __init__(self):
super().__init__(['help'])
class CommandSettings(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/settings`` command.
"""
def __init__(self):
super().__init__(['settings'])
class CommandPrivacy(Command):
"""
This filter based on :obj:`Command` filter but can handle only ``/privacy`` command.
"""
def __init__(self):
super().__init__(['privacy'])
class Text(Filter):
"""
Simple text filter
"""
_default_params = (
('text', 'equals'),
('text_contains', 'contains'),
('text_startswith', 'startswith'),
('text_endswith', 'endswith'),
)
def __init__(self,
equals: Optional[Union[str, LazyProxy, Iterable[Union[str, LazyProxy]]]] = None,
contains: Optional[Union[str, LazyProxy, Iterable[Union[str, LazyProxy]]]] = None,
startswith: Optional[Union[str, LazyProxy, Iterable[Union[str, LazyProxy]]]] = None,
endswith: Optional[Union[str, LazyProxy, Iterable[Union[str, LazyProxy]]]] = None,
ignore_case=False):
"""
Check text for one of pattern. Only one mode can be used in one filter.
In every pattern, a single string is treated as a list with 1 element.
:param equals: True if object's text in the list
:param contains: True if object's text contains all strings from the list
:param startswith: True if object's text starts with any of strings from the list
:param endswith: True if object's text ends with any of strings from the list
:param ignore_case: case insensitive
"""
# Only one mode can be used. check it.
check = sum(map(lambda s: s is not None, (equals, contains, startswith, endswith)))
if check > 1:
args = "' and '".join([arg[0] for arg in [('equals', equals),
('contains', contains),
('startswith', startswith),
('endswith', endswith)
] if arg[1] is not None])
raise ValueError(f"Arguments '{args}' cannot be used together.")
elif check == 0:
raise ValueError(f"No one mode is specified!")
equals, contains, endswith, startswith = map(lambda e: [e] if isinstance(e, str) or isinstance(e, LazyProxy)
else e,
(equals, contains, endswith, startswith))
self.equals = equals
self.contains = contains
self.endswith = endswith
self.startswith = startswith
self.ignore_case = ignore_case
@classmethod
def validate(cls, full_config: Dict[str, Any]):
for param, key in cls._default_params:
if param in full_config:
return {key: full_config.pop(param)}
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, Poll]):
if isinstance(obj, Message):
text = obj.text or obj.caption or ''
if not text and obj.poll:
text = obj.poll.question
elif isinstance(obj, CallbackQuery):
text = obj.data
elif isinstance(obj, InlineQuery):
text = obj.query
elif isinstance(obj, Poll):
text = obj.question
else:
return False
if self.ignore_case:
text = text.lower()
_pre_process_func = lambda s: str(s).lower()
else:
_pre_process_func = str
# now check
if self.equals is not None:
equals = list(map(_pre_process_func, self.equals))
return text in equals
if self.contains is not None:
contains = list(map(_pre_process_func, self.contains))
return all(map(text.__contains__, contains))
if self.startswith is not None:
startswith = list(map(_pre_process_func, self.startswith))
return any(map(text.startswith, startswith))
if self.endswith is not None:
endswith = list(map(_pre_process_func, self.endswith))
return any(map(text.endswith, endswith))
return False
class HashTag(Filter):
"""
Filter for hashtag's and cashtag's
"""
# TODO: allow to use regexp
def __init__(self, hashtags=None, cashtags=None):
if not hashtags and not cashtags:
raise ValueError('No one hashtag or cashtag is specified!')
if hashtags is None:
hashtags = []
elif isinstance(hashtags, str):
hashtags = [hashtags]
if cashtags is None:
cashtags = []
elif isinstance(cashtags, str):
cashtags = [cashtags.upper()]
else:
cashtags = list(map(str.upper, cashtags))
self.hashtags = hashtags
self.cashtags = cashtags
@classmethod
def validate(cls, full_config: Dict[str, Any]):
config = {}
if 'hashtags' in full_config:
config['hashtags'] = full_config.pop('hashtags')
if 'cashtags' in full_config:
config['cashtags'] = full_config.pop('cashtags')
return config
async def check(self, message: types.Message):
if message.caption:
text = message.caption
entities = message.caption_entities
elif message.text:
text = message.text
entities = message.entities
else:
return False
hashtags, cashtags = self._get_tags(text, entities)
if self.hashtags and set(hashtags) & set(self.hashtags) \
or self.cashtags and set(cashtags) & set(self.cashtags):
return {'hashtags': hashtags, 'cashtags': cashtags}
def _get_tags(self, text, entities):
hashtags = []
cashtags = []
for entity in entities:
if entity.type == types.MessageEntityType.HASHTAG:
value = entity.get_text(text).lstrip('#')
hashtags.append(value)
elif entity.type == types.MessageEntityType.CASHTAG:
value = entity.get_text(text).lstrip('$')
cashtags.append(value)
return hashtags, cashtags
class Regexp(Filter):
"""
Regexp filter for messages and callback query
"""
def __init__(self, regexp):
if not isinstance(regexp, typing.Pattern):
regexp = re.compile(regexp, flags=re.IGNORECASE | re.MULTILINE)
self.regexp = regexp
@classmethod
def validate(cls, full_config: Dict[str, Any]):
if 'regexp' in full_config:
return {'regexp': full_config.pop('regexp')}
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, Poll]):
if isinstance(obj, Message):
content = obj.text or obj.caption or ''
if not content and obj.poll:
content = obj.poll.question
elif isinstance(obj, CallbackQuery) and obj.data:
content = obj.data
elif isinstance(obj, InlineQuery):
content = obj.query
elif isinstance(obj, Poll):
content = obj.question
else:
return False
match = self.regexp.search(content)
if match:
return {'regexp': match}
return False
class RegexpCommandsFilter(BoundFilter):
"""
Check commands by regexp in message
"""
key = 'regexp_commands'
def __init__(self, regexp_commands):
self.regexp_commands = [re.compile(command, flags=re.IGNORECASE | re.MULTILINE) for command in regexp_commands]
async def check(self, message):
if not message.is_command():
return False
command = message.text.split()[0][1:]
command, _, mention = command.partition('@')
if mention and mention != (await message.bot.me).username:
return False
for command in self.regexp_commands:
search = command.search(message.text)
if search:
return {'regexp_command': search}
return False
class ContentTypeFilter(BoundFilter):
"""
Check message content type
"""
key = 'content_types'
required = True
default = types.ContentTypes.TEXT
def __init__(self, content_types):
if isinstance(content_types, str):
content_types = (content_types,)
self.content_types = content_types
async def check(self, message):
return types.ContentType.ANY in self.content_types or \
message.content_type in self.content_types
class IsSenderContact(BoundFilter):
"""
Filter check that the contact matches the sender
`is_sender_contact=True` - contact matches the sender
`is_sender_contact=False` - result will be inverted
"""
key = 'is_sender_contact'
def __init__(self, is_sender_contact: bool):
self.is_sender_contact = is_sender_contact
async def check(self, message: types.Message) -> bool:
if not message.contact:
return False
is_sender_contact = message.contact.user_id == message.from_user.id
if self.is_sender_contact:
return is_sender_contact
else:
return not is_sender_contact
class StateFilter(BoundFilter):
"""
Check user state
"""
key = 'state'
required = True
ctx_state = ContextVar('user_state')
def __init__(self, dispatcher, state):
from aiogram.dispatcher.filters.state import State, StatesGroup
self.dispatcher = dispatcher
states = []
if not isinstance(state, (list, set, tuple, frozenset)) or state is None:
state = [state, ]
for item in state:
if isinstance(item, State):
states.append(item.state)
elif inspect.isclass(item) and issubclass(item, StatesGroup):
states.extend(item.all_states_names)
else:
states.append(item)
self.states = states
def get_target(self, obj):
if isinstance(obj, CallbackQuery):
return getattr(getattr(getattr(obj, 'message', None),'chat', None), 'id', None), getattr(getattr(obj, 'from_user', None), 'id', None)
return getattr(getattr(obj, 'chat', None), 'id', None), getattr(getattr(obj, 'from_user', None), 'id', None)
async def check(self, obj):
if '*' in self.states:
return {'state': self.dispatcher.current_state()}
try:
state = self.ctx_state.get()
except LookupError:
chat, user = self.get_target(obj)
if chat or user:
state = await self.dispatcher.storage.get_state(chat=chat, user=user)
self.ctx_state.set(state)
if state in self.states:
return {'state': self.dispatcher.current_state(), 'raw_state': state}
else:
if state in self.states:
return {'state': self.dispatcher.current_state(), 'raw_state': state}
return False
class ExceptionsFilter(BoundFilter):
"""
Filter for exceptions
"""
key = 'exception'
def __init__(self, exception):
self.exception = exception
async def check(self, update, exception):
try:
raise exception
except self.exception:
return True
except:
return False
class IDFilter(Filter):
def __init__(self,
user_id: Optional[ChatIDArgumentType] = None,
chat_id: Optional[ChatIDArgumentType] = None,
):
"""
:param user_id:
:param chat_id:
"""
if user_id is None and chat_id is None:
raise ValueError("Both user_id and chat_id can't be None")
self.user_id: Optional[typing.Set[int]] = None
self.chat_id: Optional[typing.Set[int]] = None
if user_id:
self.user_id = extract_chat_ids(user_id)
if chat_id:
self.chat_id = extract_chat_ids(chat_id)
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]:
result = {}
if 'user_id' in full_config:
result['user_id'] = full_config.pop('user_id')
if 'chat_id' in full_config:
result['chat_id'] = full_config.pop('chat_id')
return result
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, ChatMemberUpdated]):
if isinstance(obj, Message):
user_id = None
if obj.from_user is not None:
user_id = obj.from_user.id
chat_id = obj.chat.id
elif isinstance(obj, CallbackQuery):
user_id = obj.from_user.id
chat_id = None
if obj.message is not None:
# if the button was sent with message
chat_id = obj.message.chat.id
elif isinstance(obj, InlineQuery):
user_id = obj.from_user.id
chat_id = None
elif isinstance(obj, ChatMemberUpdated):
user_id = obj.from_user.id
chat_id = obj.chat.id
else:
return False
if self.user_id and self.chat_id:
return user_id in self.user_id and chat_id in self.chat_id
if self.user_id:
return user_id in self.user_id
if self.chat_id:
return chat_id in self.chat_id
return False
class AdminFilter(Filter):
"""
Checks if user is admin in a chat.
If is_chat_admin is not set, the filter will check in the current chat (correct only for messages).
is_chat_admin is required for InlineQuery.
"""
def __init__(self, is_chat_admin: Optional[Union[ChatIDArgumentType, bool]] = None):
self._check_current = False
self._chat_ids = None
if is_chat_admin is False:
raise ValueError("is_chat_admin cannot be False")
if not is_chat_admin:
self._check_current = True
return
if isinstance(is_chat_admin, bool):
self._check_current = is_chat_admin
self._chat_ids = extract_chat_ids(is_chat_admin)
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]:
result = {}
if "is_chat_admin" in full_config:
result["is_chat_admin"] = full_config.pop("is_chat_admin")
return result
async def check(self, obj: Union[Message, CallbackQuery, InlineQuery, ChatMemberUpdated]) -> bool:
user_id = obj.from_user.id
if self._check_current:
if isinstance(obj, Message):
chat = obj.chat
elif isinstance(obj, CallbackQuery) and obj.message:
chat = obj.message.chat
elif isinstance(obj, ChatMemberUpdated):
chat = obj.chat
else:
return False
if chat.type == ChatType.PRIVATE: # there is no admin in private chats
return False
chat_ids = [chat.id]
else:
chat_ids = self._chat_ids
admins = [member.user.id for chat_id in chat_ids for member in await obj.bot.get_chat_administrators(chat_id)]
return user_id in admins
class IsReplyFilter(BoundFilter):
"""
Check if message is replied and send reply message to handler
"""
key = 'is_reply'
def __init__(self, is_reply):
self.is_reply = is_reply
async def check(self, msg: Message):
if msg.reply_to_message and self.is_reply:
return {'reply': msg.reply_to_message}
elif not msg.reply_to_message and not self.is_reply:
return True
class ForwardedMessageFilter(BoundFilter):
key = 'is_forwarded'
def __init__(self, is_forwarded: bool):
self.is_forwarded = is_forwarded
async def check(self, message: Message):
return bool(getattr(message, "forward_date")) is self.is_forwarded
class ChatTypeFilter(BoundFilter):
key = 'chat_type'
def __init__(self, chat_type: typing.Container[ChatType]):
if isinstance(chat_type, str):
chat_type = {chat_type}
self.chat_type: typing.Set[str] = set(chat_type)
async def check(self, obj: Union[Message, CallbackQuery, ChatMemberUpdated]):
if isinstance(obj, Message):
obj = obj.chat
elif isinstance(obj, CallbackQuery):
obj = obj.message.chat
elif isinstance(obj, ChatMemberUpdated):
obj = obj.chat
else:
warnings.warn("ChatTypeFilter doesn't support %s as input", type(obj))
return False
return obj.type in self.chat_type
class MediaGroupFilter(BoundFilter):
"""
Check if message is part of a media group.
`is_media_group=True` - the message is part of a media group
`is_media_group=False` - the message is NOT part of a media group
"""
key = "is_media_group"
def __init__(self, is_media_group: bool):
self.is_media_group = is_media_group
async def check(self, message: types.Message) -> bool:
return bool(getattr(message, "media_group_id")) is self.is_media_group

View File

@ -0,0 +1,73 @@
import typing
from .filters import AbstractFilter, FilterRecord
from ..handler import Handler
class FiltersFactory:
"""
Filters factory
"""
def __init__(self, dispatcher):
self._dispatcher = dispatcher
self._registered: typing.List[FilterRecord] = []
def bind(self, callback: typing.Union[typing.Callable, AbstractFilter],
validator: typing.Optional[typing.Callable] = None,
event_handlers: typing.Optional[typing.List[Handler]] = None,
exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None):
"""
Register filter
:param callback: callable or subclass of :obj:`AbstractFilter`
:param validator: custom validator.
:param event_handlers: list of instances of :obj:`Handler`
:param exclude_event_handlers: list of excluded event handlers (:obj:`Handler`)
"""
record = FilterRecord(callback, validator, event_handlers, exclude_event_handlers)
self._registered.append(record)
def unbind(self, callback: typing.Union[typing.Callable, AbstractFilter]):
"""
Unregister filter
:param callback: callable of subclass of :obj:`AbstractFilter`
"""
for record in self._registered:
if record.callback == callback:
self._registered.remove(record)
def resolve(self, event_handler, *custom_filters, **full_config
) -> typing.List[typing.Union[typing.Callable, AbstractFilter]]:
"""
Resolve filters to filters-set
:param event_handler:
:param custom_filters:
:param full_config:
:return:
"""
filters_set = []
filters_set.extend(self._resolve_registered(event_handler,
{k: v for k, v in full_config.items() if v is not None}))
if custom_filters:
filters_set.extend(custom_filters)
return filters_set
def _resolve_registered(self, event_handler, full_config) -> typing.Generator:
"""
Resolve registered filters
:param event_handler:
:param full_config:
:return:
"""
for record in self._registered:
filter_ = record.resolve(self._dispatcher, event_handler, full_config)
if filter_:
yield filter_
if full_config:
raise NameError("Invalid filter name(s): '" + "', ".join(full_config.keys()) + "'")

View File

@ -0,0 +1,289 @@
import abc
import inspect
import typing
from ..handler import Handler, FilterObj
class FilterNotPassed(Exception):
pass
def wrap_async(func):
async def async_wrapper(*args, **kwargs):
return func(*args, **kwargs)
if inspect.isawaitable(func) \
or inspect.iscoroutinefunction(func) \
or isinstance(func, AbstractFilter):
return func
return async_wrapper
def get_filter_spec(dispatcher, filter_: callable):
kwargs = {}
if not callable(filter_):
raise TypeError('Filter must be callable and/or awaitable!')
spec = inspect.getfullargspec(filter_)
if 'dispatcher' in spec:
kwargs['dispatcher'] = dispatcher
if inspect.isawaitable(filter_) \
or inspect.iscoroutinefunction(filter_) \
or isinstance(filter_, AbstractFilter):
return FilterObj(filter=filter_, kwargs=kwargs, is_async=True)
else:
return FilterObj(filter=filter_, kwargs=kwargs, is_async=False)
def get_filters_spec(dispatcher, filters: typing.Iterable[callable]):
data = []
if filters is not None:
for i in filters:
data.append(get_filter_spec(dispatcher, i))
return data
async def execute_filter(filter_: FilterObj, args):
"""
Helper for executing filter
:param filter_:
:param args:
:return:
"""
if filter_.is_async:
return await filter_.filter(*args, **filter_.kwargs)
else:
return filter_.filter(*args, **filter_.kwargs)
async def check_filters(filters: typing.Iterable[FilterObj], args):
"""
Check list of filters
:param filters:
:param args:
:return:
"""
data = {}
if filters is not None:
for filter_ in filters:
f = await execute_filter(filter_, args)
if not f:
raise FilterNotPassed()
elif isinstance(f, dict):
data.update(f)
return data
class FilterRecord:
"""
Filters record for factory
"""
def __init__(self, callback: typing.Union[typing.Callable, 'AbstractFilter'],
validator: typing.Optional[typing.Callable] = None,
event_handlers: typing.Optional[typing.Iterable[Handler]] = None,
exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None):
if event_handlers and exclude_event_handlers:
raise ValueError("'event_handlers' and 'exclude_event_handlers' arguments cannot be used together.")
self.callback = callback
self.event_handlers = event_handlers
self.exclude_event_handlers = exclude_event_handlers
if validator is not None:
if not callable(validator):
raise TypeError(f"validator must be callable, not {type(validator)}")
self.resolver = validator
elif issubclass(callback, AbstractFilter):
self.resolver = callback.validate
else:
raise RuntimeError('validator is required!')
def resolve(self, dispatcher, event_handler, full_config):
if not self._check_event_handler(event_handler):
return
config = self.resolver(full_config)
if config:
if 'dispatcher' not in config:
spec = inspect.getfullargspec(self.callback)
if 'dispatcher' in spec.args:
config['dispatcher'] = dispatcher
for key in config:
if key in full_config:
full_config.pop(key)
return self.callback(**config)
def _check_event_handler(self, event_handler) -> bool:
if self.event_handlers:
return event_handler in self.event_handlers
elif self.exclude_event_handlers:
return event_handler not in self.exclude_event_handlers
return True
class AbstractFilter(abc.ABC):
"""
Abstract class for custom filters.
"""
@classmethod
@abc.abstractmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]:
"""
Validate and parse config.
This method will be called by the filters factory when you bind this filter.
Must be overridden.
:param full_config: dict with arguments passed to handler registrar
:return: Current filter config
"""
pass
@abc.abstractmethod
async def check(self, *args) -> bool:
"""
Will be called when filters checks.
This method must be overridden.
:param args:
:return:
"""
pass
async def __call__(self, *args) -> bool:
return await self.check(*args)
def __invert__(self):
return NotFilter(self)
def __and__(self, other):
if isinstance(self, AndFilter):
self.append(other)
return self
return AndFilter(self, other)
def __or__(self, other):
if isinstance(self, OrFilter):
self.append(other)
return self
return OrFilter(self, other)
class Filter(AbstractFilter):
"""
You can make subclasses of that class for custom filters.
Method ``check`` must be overridden
"""
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]:
"""
Here method ``validate`` is optional.
If you need to use filter from filters factory you need to override this method.
:param full_config: dict with arguments passed to handler registrar
:return: Current filter config
"""
pass
class BoundFilter(Filter):
"""
To easily create your own filters with one parameter, you can inherit from this filter.
You need to implement ``__init__`` method with single argument related with key attribute
and ``check`` method where you need to implement filter logic.
"""
key = None
"""Unique name of the filter argument. You need to override this attribute."""
required = False
"""If :obj:`True` this filter will be added to the all of the registered handlers"""
default = None
"""Default value for configure required filters"""
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]:
"""
If ``cls.key`` is not :obj:`None` and that is in config returns config with that argument.
:param full_config:
:return:
"""
if cls.key is not None:
if cls.key in full_config:
return {cls.key: full_config[cls.key]}
elif cls.required:
return {cls.key: cls.default}
class _LogicFilter(Filter):
@classmethod
def validate(cls, full_config: typing.Dict[str, typing.Any]):
raise ValueError('That filter can\'t be used in filters factory!')
class NotFilter(_LogicFilter):
def __init__(self, target):
self.target = wrap_async(target)
async def check(self, *args):
return not bool(await self.target(*args))
class AndFilter(_LogicFilter):
def __init__(self, *targets):
self.targets = list(wrap_async(target) for target in targets)
async def check(self, *args):
"""
All filters must return a positive result
:param args:
:return:
"""
data = {}
for target in self.targets:
result = await target(*args)
if not result:
return False
if isinstance(result, dict):
data.update(result)
if not data:
return True
return data
def append(self, target):
self.targets.append(wrap_async(target))
class OrFilter(_LogicFilter):
def __init__(self, *targets):
self.targets = list(wrap_async(target) for target in targets)
async def check(self, *args):
"""
One of filters must return a positive result
:param args:
:return:
"""
for target in self.targets:
result = await target(*args)
if result:
if isinstance(result, dict):
return result
return True
return False
def append(self, target):
self.targets.append(wrap_async(target))

View File

@ -0,0 +1,197 @@
import inspect
from typing import Optional
from ..dispatcher import Dispatcher
class State:
"""
State object
"""
def __init__(self, state: Optional[str] = None, group_name: Optional[str] = None):
self._state = state
self._group_name = group_name
self._group = None
@property
def group(self):
if not self._group:
raise RuntimeError('This state is not in any group.')
return self._group
def get_root(self):
return self.group.get_root()
@property
def state(self):
if self._state is None or self._state == '*':
return self._state
if self._group_name is None and self._group:
group = self._group.__full_group_name__
elif self._group_name:
group = self._group_name
else:
group = '@'
return f'{group}:{self._state}'
def set_parent(self, group):
if not issubclass(group, StatesGroup):
raise ValueError('Group must be subclass of StatesGroup')
self._group = group
def __set_name__(self, owner, name):
if self._state is None:
self._state = name
self.set_parent(owner)
def __str__(self):
return f"<State '{self.state or ''}'>"
__repr__ = __str__
async def set(self):
state = Dispatcher.get_current().current_state()
await state.set_state(self.state)
class StatesGroupMeta(type):
def __new__(mcs, name, bases, namespace, **kwargs):
cls = super(StatesGroupMeta, mcs).__new__(mcs, name, bases, namespace)
states = []
childs = []
cls._group_name = name
for name, prop in namespace.items():
if isinstance(prop, State):
states.append(prop)
elif inspect.isclass(prop) and issubclass(prop, StatesGroup):
childs.append(prop)
prop._parent = cls
cls._parent = None
cls._childs = tuple(childs)
cls._states = tuple(states)
cls._state_names = tuple(state.state for state in states)
return cls
@property
def __group_name__(cls) -> str:
return cls._group_name
@property
def __full_group_name__(cls) -> str:
if cls._parent:
return '.'.join((cls._parent.__full_group_name__, cls._group_name))
return cls._group_name
@property
def states(cls) -> tuple:
return cls._states
@property
def childs(cls) -> tuple:
return cls._childs
@property
def all_childs(cls):
result = cls.childs
for child in cls.childs:
result += child.childs
return result
@property
def all_states(cls):
result = cls.states
for group in cls.childs:
result += group.all_states
return result
@property
def all_states_names(cls):
return tuple(state.state for state in cls.all_states)
@property
def states_names(cls) -> tuple:
return tuple(state.state for state in cls.states)
def get_root(cls):
if cls._parent is None:
return cls
return cls._parent.get_root()
def __contains__(cls, item):
if isinstance(item, str):
return item in cls.all_states_names
if isinstance(item, State):
return item in cls.all_states
if isinstance(item, StatesGroup):
return item in cls.all_childs
return False
def __str__(self):
return f"<StatesGroup '{self.__full_group_name__}'>"
class StatesGroup(metaclass=StatesGroupMeta):
@classmethod
async def next(cls) -> str:
state = Dispatcher.get_current().current_state()
state_name = await state.get_state()
try:
next_step = cls.states_names.index(state_name) + 1
except ValueError:
next_step = 0
try:
next_state_name = cls.states[next_step].state
except IndexError:
next_state_name = None
await state.set_state(next_state_name)
return next_state_name
@classmethod
async def previous(cls) -> str:
state = Dispatcher.get_current().current_state()
state_name = await state.get_state()
try:
previous_step = cls.states_names.index(state_name) - 1
except ValueError:
previous_step = 0
if previous_step < 0:
previous_state_name = None
else:
previous_state_name = cls.states[previous_step].state
await state.set_state(previous_state_name)
return previous_state_name
@classmethod
async def first(cls) -> str:
state = Dispatcher.get_current().current_state()
first_step_name = cls.states_names[0]
await state.set_state(first_step_name)
return first_step_name
@classmethod
async def last(cls) -> str:
state = Dispatcher.get_current().current_state()
last_step_name = cls.states_names[-1]
await state.set_state(last_step_name)
return last_step_name
default_state = State()
any_state = State(state='*')

View File

@ -0,0 +1,138 @@
import inspect
from contextvars import ContextVar
from dataclasses import dataclass
from typing import Optional, Iterable, List
ctx_data = ContextVar('ctx_handler_data')
current_handler = ContextVar('current_handler')
@dataclass
class FilterObj:
filter: callable
kwargs: dict
is_async: bool
class SkipHandler(Exception):
pass
class CancelHandler(Exception):
pass
def _get_spec(func: callable):
while hasattr(func, '__wrapped__'): # Try to resolve decorated callbacks
func = func.__wrapped__
return inspect.getfullargspec(func)
def _check_spec(spec: inspect.FullArgSpec, kwargs: dict):
if spec.varkw:
return kwargs
return {k: v for k, v in kwargs.items() if k in set(spec.args + spec.kwonlyargs)}
class Handler:
def __init__(self, dispatcher, once=True, middleware_key=None):
self.dispatcher = dispatcher
self.once = once
self.handlers: List[Handler.HandlerObj] = []
self.middleware_key = middleware_key
def register(self, handler, filters=None, index=None):
"""
Register callback
Filters can be awaitable or not.
:param handler: coroutine
:param filters: list of filters
:param index: you can reorder handlers
"""
from .filters import get_filters_spec
spec = _get_spec(handler)
if filters and not isinstance(filters, (list, tuple, set)):
filters = [filters]
filters = get_filters_spec(self.dispatcher, filters)
record = Handler.HandlerObj(handler=handler, spec=spec, filters=filters)
if index is None:
self.handlers.append(record)
else:
self.handlers.insert(index, record)
def unregister(self, handler):
"""
Remove handler
:param handler: callback
:return:
"""
for handler_obj in self.handlers:
registered = handler_obj.handler
if handler is registered:
self.handlers.remove(handler_obj)
return True
raise ValueError('This handler is not registered!')
async def notify(self, *args):
"""
Notify handlers
:param args:
:return:
"""
from .filters import check_filters, FilterNotPassed
results = []
data = {}
ctx_data.set(data)
if self.middleware_key:
try:
await self.dispatcher.middleware.trigger(f"pre_process_{self.middleware_key}", args + (data,))
except CancelHandler: # Allow to cancel current event
return results
try:
for handler_obj in self.handlers:
try:
data.update(await check_filters(handler_obj.filters, args))
except FilterNotPassed:
continue
else:
ctx_token = current_handler.set(handler_obj.handler)
try:
if self.middleware_key:
await self.dispatcher.middleware.trigger(f"process_{self.middleware_key}", args + (data,))
partial_data = _check_spec(handler_obj.spec, data)
response = await handler_obj.handler(*args, **partial_data)
if response is not None:
results.append(response)
if self.once:
break
except SkipHandler:
continue
except CancelHandler:
break
finally:
current_handler.reset(ctx_token)
finally:
if self.middleware_key:
await self.dispatcher.middleware.trigger(f"post_process_{self.middleware_key}",
args + (results, data,))
return results
@dataclass
class HandlerObj:
handler: callable
spec: inspect.FullArgSpec
filters: Optional[Iterable[FilterObj]] = None

View File

@ -0,0 +1,131 @@
import logging
import typing
log = logging.getLogger('aiogram.Middleware')
class MiddlewareManager:
"""
Middlewares manager. Works only with dispatcher.
"""
def __init__(self, dispatcher):
"""
Init
:param dispatcher: instance of Dispatcher
"""
self.dispatcher = dispatcher
self.bot = dispatcher.bot
self.storage = dispatcher.storage
self.applications = []
@property
def loop(self):
return self.dispatcher.loop
def setup(self, middleware):
"""
Setup middleware
:param middleware:
:return:
"""
if not isinstance(middleware, BaseMiddleware):
raise TypeError(f"`middleware` must be an instance of BaseMiddleware, not {type(middleware)}")
if middleware.is_configured():
raise ValueError('That middleware is already used!')
self.applications.append(middleware)
middleware.setup(self)
log.debug(f"Loaded middleware '{middleware.__class__.__name__}'")
return middleware
async def trigger(self, action: str, args: typing.Iterable):
"""
Call action to middlewares with args lilt.
:param action:
:param args:
:return:
"""
for app in self.applications:
await app.trigger(action, args)
class BaseMiddleware:
"""
Base class for middleware.
All methods on the middle always must be coroutines and name starts with "on_" like "on_process_message".
"""
def __init__(self):
self._configured = False
self._manager = None
@property
def manager(self) -> MiddlewareManager:
"""
Instance of MiddlewareManager
"""
if self._manager is None:
raise RuntimeError('Middleware is not configured!')
return self._manager
def setup(self, manager):
"""
Mark middleware as configured
:param manager:
:return:
"""
self._manager = manager
self._configured = True
def is_configured(self) -> bool:
"""
Check middleware is configured
:return:
"""
return self._configured
async def trigger(self, action, args):
"""
Trigger action.
:param action:
:param args:
:return:
"""
handler_name = f"on_{action}"
handler = getattr(self, handler_name, None)
if not handler:
return None
await handler(*args)
class LifetimeControllerMiddleware(BaseMiddleware):
# TODO: Rename class
skip_patterns = None
async def pre_process(self, obj, data, *args):
pass
async def post_process(self, obj, data, *args):
pass
async def trigger(self, action, args):
if self.skip_patterns is not None and any(item in action for item in self.skip_patterns):
return False
obj, *args, data = args
if action.startswith('pre_process_'):
await self.pre_process(obj, data, *args)
elif action.startswith('post_process_'):
await self.post_process(obj, data, *args)
else:
return False
return True

View File

@ -0,0 +1,506 @@
import copy
import typing
from ..utils.deprecated import warn_deprecated as warn
from ..utils.exceptions import FSMStorageWarning
# Leak bucket
KEY = 'key'
LAST_CALL = 'called_at'
RATE_LIMIT = 'rate_limit'
RESULT = 'result'
EXCEEDED_COUNT = 'exceeded'
DELTA = 'delta'
THROTTLE_MANAGER = '$throttle_manager'
class BaseStorage:
"""
You are able to save current user's state
and data for all steps in states-storage
"""
async def close(self):
"""
You have to override this method and use when application shutdowns.
Perhaps you would like to save data and etc.
:return:
"""
raise NotImplementedError
async def wait_closed(self):
"""
You have to override this method for all asynchronous storages (e.g., Redis).
:return:
"""
raise NotImplementedError
@classmethod
def check_address(cls, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
) -> (typing.Union[str, int], typing.Union[str, int]):
"""
In all storage's methods chat or user is always required.
If one of them is not provided, you have to set missing value based on the provided one.
This method performs the check described above.
:param chat: chat_id
:param user: user_id
:return:
"""
if chat is None and user is None:
raise ValueError('`user` or `chat` parameter is required but no one is provided!')
if user is None:
user = chat
elif chat is None:
chat = user
return chat, user
async def get_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Optional[str]:
"""
Get current state of user in chat. Return `default` if no record is found.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param default:
:return:
"""
raise NotImplementedError
async def get_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[typing.Dict] = None) -> typing.Dict:
"""
Get state-data for user in chat. Return `default` if no data is provided in storage.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param default:
:return:
"""
raise NotImplementedError
async def set_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
state: typing.Optional[typing.AnyStr] = None):
"""
Set new state for user in chat
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param state:
"""
raise NotImplementedError
async def set_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
data: typing.Dict = None):
"""
Set data for user in chat
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param data:
"""
raise NotImplementedError
async def update_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
data: typing.Dict = None,
**kwargs):
"""
Update data for user in chat
You can use data parameter or|and kwargs.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param data:
:param chat:
:param user:
:param kwargs:
:return:
"""
raise NotImplementedError
async def reset_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None):
"""
Reset data for user in chat.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:return:
"""
await self.set_data(chat=chat, user=user, data={})
async def reset_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
with_data: typing.Optional[bool] = True):
"""
Reset state for user in chat.
You may desire to use this method when finishing conversations.
Chat or user is always required. If one of this is not presented,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param with_data:
:return:
"""
chat, user = self.check_address(chat=chat, user=user)
await self.set_state(chat=chat, user=user, state=None)
if with_data:
await self.set_data(chat=chat, user=user, data={})
async def finish(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None):
"""
Finish conversation for user in chat.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:return:
"""
await self.reset_state(chat=chat, user=user, with_data=True)
def has_bucket(self):
return False
async def get_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[dict] = None) -> typing.Dict:
"""
Get bucket for user in chat. Return `default` if no data is provided in storage.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param default:
:return:
"""
raise NotImplementedError
async def set_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None):
"""
Set bucket for user in chat
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:param bucket:
"""
raise NotImplementedError
async def update_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
bucket: typing.Dict = None,
**kwargs):
"""
Update bucket for user in chat
You can use bucket parameter or|and kwargs.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param bucket:
:param chat:
:param user:
:param kwargs:
:return:
"""
raise NotImplementedError
async def reset_bucket(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None):
"""
Reset bucket dor user in chat.
Chat or user is always required. If one of them is not provided,
you have to set missing value based on the provided one.
:param chat:
:param user:
:return:
"""
await self.set_data(chat=chat, user=user, data={})
@staticmethod
def resolve_state(value):
from .filters.state import State
if value is None:
return
if isinstance(value, str):
return value
if isinstance(value, State):
return value.state
return str(value)
class FSMContext:
def __init__(self, storage, chat, user):
self.storage: BaseStorage = storage
self.chat, self.user = self.storage.check_address(chat=chat, user=user)
def proxy(self):
return FSMContextProxy(self)
async def get_state(self, default: typing.Optional[str] = None) -> typing.Optional[str]:
return await self.storage.get_state(chat=self.chat, user=self.user, default=default)
async def get_data(self, default: typing.Optional[str] = None) -> typing.Dict:
return await self.storage.get_data(chat=self.chat, user=self.user, default=default)
async def update_data(self, data: typing.Dict = None, **kwargs):
await self.storage.update_data(chat=self.chat, user=self.user, data=data, **kwargs)
async def set_state(self, state: typing.Optional[typing.AnyStr] = None):
await self.storage.set_state(chat=self.chat, user=self.user, state=state)
async def set_data(self, data: typing.Dict = None):
await self.storage.set_data(chat=self.chat, user=self.user, data=data)
async def reset_state(self, with_data: typing.Optional[bool] = True):
await self.storage.reset_state(chat=self.chat, user=self.user, with_data=with_data)
async def reset_data(self):
await self.storage.reset_data(chat=self.chat, user=self.user)
async def finish(self):
await self.storage.finish(chat=self.chat, user=self.user)
class FSMContextProxy:
def __init__(self, fsm_context: FSMContext):
super(FSMContextProxy, self).__init__()
self.fsm_context = fsm_context
self._copy = {}
self._data = {}
self._state = None
self._is_dirty = False
self._closed = True
async def __aenter__(self):
await self.load()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if exc_type is None:
await self.save()
self._closed = True
def _check_closed(self):
if self._closed:
raise LookupError('Proxy is closed!')
@classmethod
async def create(cls, fsm_context: FSMContext):
"""
:param fsm_context:
:return:
"""
proxy = cls(fsm_context)
await proxy.load()
return proxy
async def load(self):
self._closed = False
self.clear()
self._state = await self.fsm_context.get_state()
self.update(await self.fsm_context.get_data())
self._copy = copy.deepcopy(self._data)
self._is_dirty = False
@property
def state(self):
return self._state
@state.setter
def state(self, value):
self._check_closed()
self._state = value
self._is_dirty = True
@state.deleter
def state(self):
self._check_closed()
self._state = None
self._is_dirty = True
async def save(self, force=False):
self._check_closed()
if self._copy != self._data or force:
await self.fsm_context.set_data(data=self._data)
if self._is_dirty or force:
await self.fsm_context.set_state(self.state)
self._is_dirty = False
self._copy = copy.deepcopy(self._data)
def clear(self):
del self.state
return self._data.clear()
def get(self, value, default=None):
return self._data.get(value, default)
def setdefault(self, key, default):
self._check_closed()
return self._data.setdefault(key, default)
def update(self, data=None, **kwargs):
self._check_closed()
self._data.update(data, **kwargs)
def pop(self, key, default=None):
self._check_closed()
return self._data.pop(key, default)
def keys(self):
return self._data.keys()
def values(self):
return self._data.values()
def items(self):
return self._data.items()
def as_dict(self):
return copy.deepcopy(self._data)
def __len__(self):
return len(self._data)
def __iter__(self):
return self._data.__iter__()
def __getitem__(self, item):
return self._data[item]
def __setitem__(self, key, value):
self._check_closed()
self._data[key] = value
def __delitem__(self, key):
self._check_closed()
del self._data[key]
def __contains__(self, item):
return item in self._data
def __str__(self):
readable_state = f"'{self.state}'" if self.state else "<default>"
result = f"{self.__class__.__name__} state = {readable_state}, data = {self._data}"
if self._closed:
result += ', closed = True'
return result
class DisabledStorage(BaseStorage):
"""
Empty storage. Use it if you don't want to use Finite-State Machine
"""
async def close(self):
pass
async def wait_closed(self):
pass
async def get_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Optional[str]:
return None
async def get_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
default: typing.Optional[str] = None) -> typing.Dict:
self._warn()
return {}
async def update_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
data: typing.Dict = None, **kwargs):
self._warn()
async def set_state(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
state: typing.Optional[typing.AnyStr] = None):
self._warn()
async def set_data(self, *,
chat: typing.Union[str, int, None] = None,
user: typing.Union[str, int, None] = None,
data: typing.Dict = None):
self._warn()
@staticmethod
def _warn():
warn(f"You havent set any storage yet so no states and no data will be saved. \n"
f"You can connect MemoryStorage for debug purposes or non-essential data.",
FSMStorageWarning, 5)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
from . import base
from . import fields
from .animation import Animation
from .audio import Audio
from .auth_widget_data import AuthWidgetData
from .bot_command import BotCommand
from .bot_command_scope import BotCommandScope, BotCommandScopeAllChatAdministrators, \
BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, BotCommandScopeChat, \
BotCommandScopeChatAdministrators, BotCommandScopeChatMember, \
BotCommandScopeDefault, BotCommandScopeType
from .callback_game import CallbackGame
from .callback_query import CallbackQuery
from .chat import Chat, ChatActions, ChatType
from .chat_invite_link import ChatInviteLink
from .chat_location import ChatLocation
from .chat_member import ChatMember, ChatMemberStatus
from .chat_member_updated import ChatMemberUpdated
from .chat_permissions import ChatPermissions
from .chat_photo import ChatPhoto
from .chosen_inline_result import ChosenInlineResult
from .contact import Contact
from .dice import Dice, DiceEmoji
from .document import Document
from .encrypted_credentials import EncryptedCredentials
from .encrypted_passport_element import EncryptedPassportElement
from .file import File
from .force_reply import ForceReply
from .game import Game
from .game_high_score import GameHighScore
from .inline_keyboard import InlineKeyboardButton, InlineKeyboardMarkup
from .inline_query import InlineQuery
from .inline_query_result import InlineQueryResult, InlineQueryResultArticle, InlineQueryResultAudio, \
InlineQueryResultCachedAudio, InlineQueryResultCachedDocument, InlineQueryResultCachedGif, \
InlineQueryResultCachedMpeg4Gif, InlineQueryResultCachedPhoto, InlineQueryResultCachedSticker, \
InlineQueryResultCachedVideo, InlineQueryResultCachedVoice, InlineQueryResultContact, InlineQueryResultDocument, \
InlineQueryResultGame, InlineQueryResultGif, InlineQueryResultLocation, InlineQueryResultMpeg4Gif, \
InlineQueryResultPhoto, InlineQueryResultVenue, InlineQueryResultVideo, InlineQueryResultVoice
from .input_file import InputFile
from .input_media import InputMedia, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, \
InputMediaVideo, MediaGroup
from .input_message_content import InputContactMessageContent, InputLocationMessageContent, InputMessageContent, \
InputTextMessageContent, InputVenueMessageContent, InputInvoiceMessageContent
from .invoice import Invoice
from .labeled_price import LabeledPrice
from .location import Location
from .login_url import LoginUrl
from .mask_position import MaskPosition
from .message import ContentType, ContentTypes, Message, ParseMode
from .message_auto_delete_timer_changed import MessageAutoDeleteTimerChanged
from .message_entity import MessageEntity, MessageEntityType
from .message_id import MessageId
from .order_info import OrderInfo
from .passport_data import PassportData
from .passport_element_error import PassportElementError, PassportElementErrorDataField, PassportElementErrorFile, \
PassportElementErrorFiles, PassportElementErrorFrontSide, PassportElementErrorReverseSide, \
PassportElementErrorSelfie
from .passport_file import PassportFile
from .photo_size import PhotoSize
from .poll import PollOption, Poll, PollAnswer, PollType
from .pre_checkout_query import PreCheckoutQuery
from .proximity_alert_triggered import ProximityAlertTriggered
from .reply_keyboard import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, KeyboardButtonPollType
from .response_parameters import ResponseParameters
from .shipping_address import ShippingAddress
from .shipping_option import ShippingOption
from .shipping_query import ShippingQuery
from .sticker import Sticker
from .sticker_set import StickerSet
from .successful_payment import SuccessfulPayment
from .update import AllowedUpdates, Update
from .user import User
from .user_profile_photos import UserProfilePhotos
from .venue import Venue
from .video import Video
from .video_note import VideoNote
from .voice import Voice
from .voice_chat_ended import VoiceChatEnded
from .voice_chat_participants_invited import VoiceChatParticipantsInvited
from .voice_chat_scheduled import VoiceChatScheduled
from .voice_chat_started import VoiceChatStarted
from .webhook_info import WebhookInfo
__all__ = (
'AllowedUpdates',
'Animation',
'Audio',
'AuthWidgetData',
'BotCommand',
'BotCommandScope',
'BotCommandScopeAllChatAdministrators',
'BotCommandScopeAllGroupChats',
'BotCommandScopeAllPrivateChats',
'BotCommandScopeChat',
'BotCommandScopeChatAdministrators',
'BotCommandScopeChatMember',
'BotCommandScopeDefault',
'BotCommandScopeType',
'CallbackGame',
'CallbackQuery',
'Chat',
'ChatActions',
'ChatInviteLink',
'ChatLocation',
'ChatMember',
'ChatMemberStatus',
'ChatMemberUpdated',
'ChatPermissions',
'ChatPhoto',
'ChatType',
'ChosenInlineResult',
'Contact',
'ContentType',
'ContentTypes',
'Dice',
'DiceEmoji',
'Document',
'EncryptedCredentials',
'EncryptedPassportElement',
'File',
'ForceReply',
'Game',
'GameHighScore',
'InlineKeyboardButton',
'InlineKeyboardMarkup',
'InlineQuery',
'InlineQueryResult',
'InlineQueryResultArticle',
'InlineQueryResultAudio',
'InlineQueryResultCachedAudio',
'InlineQueryResultCachedDocument',
'InlineQueryResultCachedGif',
'InlineQueryResultCachedMpeg4Gif',
'InlineQueryResultCachedPhoto',
'InlineQueryResultCachedSticker',
'InlineQueryResultCachedVideo',
'InlineQueryResultCachedVoice',
'InlineQueryResultContact',
'InlineQueryResultDocument',
'InlineQueryResultGame',
'InlineQueryResultGif',
'InlineQueryResultLocation',
'InlineQueryResultMpeg4Gif',
'InlineQueryResultPhoto',
'InlineQueryResultVenue',
'InlineQueryResultVideo',
'InlineQueryResultVoice',
'InputContactMessageContent',
'InputInvoiceMessageContent',
'InputFile',
'InputLocationMessageContent',
'InputMedia',
'InputMediaAnimation',
'InputMediaAudio',
'InputMediaDocument',
'InputMediaPhoto',
'InputMediaVideo',
'InputMessageContent',
'InputTextMessageContent',
'InputVenueMessageContent',
'Invoice',
'KeyboardButton',
'KeyboardButtonPollType',
'LabeledPrice',
'Location',
'LoginUrl',
'MaskPosition',
'MediaGroup',
'Message',
'MessageAutoDeleteTimerChanged',
'MessageEntity',
'MessageEntityType',
'MessageId',
'OrderInfo',
'ParseMode',
'PassportData',
'PassportElementError',
'PassportElementErrorDataField',
'PassportElementErrorFile',
'PassportElementErrorFiles',
'PassportElementErrorFrontSide',
'PassportElementErrorReverseSide',
'PassportElementErrorSelfie',
'PassportFile',
'PhotoSize',
'Poll',
'PollAnswer',
'PollOption',
'PollType',
'PreCheckoutQuery',
'ProximityAlertTriggered',
'ReplyKeyboardMarkup',
'ReplyKeyboardRemove',
'ResponseParameters',
'ShippingAddress',
'ShippingOption',
'ShippingQuery',
'Sticker',
'StickerSet',
'SuccessfulPayment',
'Update',
'User',
'UserProfilePhotos',
'Venue',
'Video',
'VideoNote',
'Voice',
'VoiceChatEnded',
'VoiceChatParticipantsInvited',
'VoiceChatScheduled',
'VoiceChatStarted',
'WebhookInfo',
'base',
'fields',
)

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