From 15ea25451c277365d4e36262e196bd6ce6ff49f7 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 25 May 2023 01:24:33 +0300 Subject: [PATCH 1/3] add certifi, fix py2 issues --- setup.py | 5 +- telegramer/include/BaseHTTPServer/__init__.py | 4 +- telegramer/include/certifi/__init__.py | 4 + telegramer/include/certifi/__main__.py | 12 + telegramer/include/certifi/cacert.pem | 4589 +++++++++++++++++ telegramer/include/certifi/core.py | 108 + telegramer/include/certifi/py.typed | 0 telegramer/include/imghdr/__init__.py | 8 +- 8 files changed, 4722 insertions(+), 8 deletions(-) create mode 100644 telegramer/include/certifi/__init__.py create mode 100644 telegramer/include/certifi/__main__.py create mode 100644 telegramer/include/certifi/cacert.pem create mode 100644 telegramer/include/certifi/core.py create mode 100644 telegramer/include/certifi/py.typed diff --git a/setup.py b/setup.py index 1b379d1..7f6c566 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ __plugin_name__ = "Telegramer" __author__ = "Noam" __author_email__ = "noamgit@gmail.com" -__version__ = "2.1.1.0" +__version__ = "2.1.1.1" __url__ = "https://github.com/noam09" __license__ = "GPLv3" __description__ = "Control Deluge using Telegram" @@ -55,7 +55,8 @@ Send notifications, add and view torrents on Deluge using Telegram messenger """ -__pkg_data__ = {__plugin_name__.lower(): ["data/*"]} +__pkg_data__ = {__plugin_name__.lower(): [ + "data/*", "include/certifi/cacert.pem"]} # 'certifi': ['include/certifi/cacert.pem']} packages = find_packages() setup( diff --git a/telegramer/include/BaseHTTPServer/__init__.py b/telegramer/include/BaseHTTPServer/__init__.py index 1a39485..9f6d900 100644 --- a/telegramer/include/BaseHTTPServer/__init__.py +++ b/telegramer/include/BaseHTTPServer/__init__.py @@ -327,7 +327,7 @@ def handle_one_request(self): method = getattr(self, mname) method() self.wfile.flush() #actually send the response if not already done. - except socket.timeout, e: + except socket.timeout as e: #a read or a write timed out. Discard this connection self.log_error("Request timed out: %r", e) self.close_connection = 1 @@ -595,7 +595,7 @@ def test(HandlerClass = BaseHTTPRequestHandler, httpd = ServerClass(server_address, HandlerClass) sa = httpd.socket.getsockname() - print "Serving HTTP on", sa[0], "port", sa[1], "..." + print("Serving HTTP on", sa[0], "port", sa[1], "...") httpd.serve_forever() diff --git a/telegramer/include/certifi/__init__.py b/telegramer/include/certifi/__init__.py new file mode 100644 index 0000000..705f416 --- /dev/null +++ b/telegramer/include/certifi/__init__.py @@ -0,0 +1,4 @@ +from .core import contents, where + +__all__ = ["contents", "where"] +__version__ = "2023.05.07" diff --git a/telegramer/include/certifi/__main__.py b/telegramer/include/certifi/__main__.py new file mode 100644 index 0000000..8945b5d --- /dev/null +++ b/telegramer/include/certifi/__main__.py @@ -0,0 +1,12 @@ +import argparse + +from certifi import contents, where + +parser = argparse.ArgumentParser() +parser.add_argument("-c", "--contents", action="store_true") +args = parser.parse_args() + +if args.contents: + print(contents()) +else: + print(where()) diff --git a/telegramer/include/certifi/cacert.pem b/telegramer/include/certifi/cacert.pem new file mode 100644 index 0000000..5183934 --- /dev/null +++ b/telegramer/include/certifi/cacert.pem @@ -0,0 +1,4589 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln +biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF +MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT +d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 +76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ +bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c +6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE +emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd +MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt +MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y +MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y +FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi +aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM +gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB +qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 +lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn +8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 +45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO +UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 +O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC +bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv +GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a +77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC +hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 +92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp +Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w +ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt +Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE +BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu +IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow +RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY +U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv +Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br +YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF +nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH +6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt +eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ +c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ +MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH +HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf +jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 +5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB +rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c +wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB +AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp +WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 +xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ +2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ +IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 +aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X +em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR +dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ +OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ +hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy +tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post +# Label: "Hongkong Post Root CA 1" +# Serial: 1000 +# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca +# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 +# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx +FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg +Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG +A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr +b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ +jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn +PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh +ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 +nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h +q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED +MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC +mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 +7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB +oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs +EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO +fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi +AmvZWg== +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi +# Label: "E-Tugra Certification Authority" +# Serial: 7667447206703254355 +# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 +# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 +# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV +BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC +aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV +BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 +Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz +MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ +BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp +em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY +B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH +D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF +Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo +q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D +k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH +fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut +dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM +ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 +zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX +U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 +Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 +XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF +Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR +HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY +GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c +77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 ++GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK +vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 +FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl +yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P +AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD +y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d +NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL +BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ +SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n +a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 +NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT +CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u +Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO +dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI +VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV +9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY +2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY +vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt +bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb +x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ +l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK +TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj +Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw +DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG +7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk +MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr +gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk +GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS +3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm +Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ +l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c +JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP +L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa +LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG +mpv0 +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G4" +# Serial: 289383649854506086828220374796556676440 +# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88 +# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01 +# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88 +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ +2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E +T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j +5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM +C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T +DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX +wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A +2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm +nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl +N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj +c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS +5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS +Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr +hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ +B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI +AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw +H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ +b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk +2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol +IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk +5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY +n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global Certification Authority" +# Serial: 1846098327275375458322922162 +# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e +# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 +# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw +CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x +ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 +c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx +OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI +SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn +swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu +7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 +1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW +80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP +JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l +RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw +hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 +coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc +BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n +twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud +DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W +0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe +uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q +lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB +aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE +sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT +MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe +qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh +VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 +h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 +EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK +yeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P256 Certification Authority" +# Serial: 4151900041497450638097112925 +# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 +# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf +# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN +FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w +DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw +CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh +DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P384 Certification Authority" +# Serial: 2704997926503831671788816187 +# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 +# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 +# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ +j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF +1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G +A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 +AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC +MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu +Sw== +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Label: "GLOBALTRUST 2020" +# Serial: 109160994242082918454945253 +# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 +# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 +# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG +A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw +FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx +MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u +aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b +RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z +YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 +QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw +yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ +BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ +SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH +r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 +4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me +dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw +q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 +nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu +H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC +XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd +6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf ++I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi +kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 +wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB +TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C +MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn +4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I +aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy +qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV +BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk +YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV +BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN +MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF +UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD +VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v +dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj +cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q +yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH +2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX +H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL +zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR +p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz +W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ +SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn +LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 +n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B +u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC +AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L +9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej +rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK +pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 +vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq +OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ +/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 +2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI ++PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 +MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo +tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 +MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu +MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV +BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw +MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg +U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ +n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q +p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq +NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF +8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 +HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa +mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi +7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF +ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P +qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ +v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 +Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD +ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 +WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo +zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR +5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ +GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf +5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq +0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D +P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM +qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP +0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf +E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Label: "TunTrust Root CA" +# Serial: 108534058042236574382096126452369648152337120275 +# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 +# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb +# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS RSA Root CA 2021" +# Serial: 76817823531813593706434026085292783742 +# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 +# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d +# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS ECC Root CA 2021" +# Serial: 137515985548005187474074462014555733966 +# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 +# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 +# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 1977337328857672817 +# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 +# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe +# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus ECC Root CA" +# Serial: 630369271402956006249506845124680065938238527194 +# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 +# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 +# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus Root CA" +# Serial: 387574501246983434957692974888460947164905180485 +# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc +# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 +# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Label: "HiPKI Root CA - G1" +# Serial: 60966262342023497858655262305426234976 +# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 +# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 +# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 159662223612894884239637590694 +# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc +# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 +# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R2 O=Google Trust Services LLC +# Subject: CN=GTS Root R2 O=Google Trust Services LLC +# Label: "GTS Root R2" +# Serial: 159662449406622349769042896298 +# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc +# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94 +# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 159662495401136852707857743206 +# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 +# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 +# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj +# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj +# Label: "Telia Root CA v2" +# Serial: 7288924052977061235122729490515358 +# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 +# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd +# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 1 2020" +# Serial: 165870826978392376648679885835942448534 +# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed +# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 +# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 1 2020" +# Serial: 126288379621884218666039612629459926992 +# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e +# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 +# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS ECC P384 Root G5" +# Serial: 13129116028163249804115411775095713523 +# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed +# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee +# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS RSA4096 Root G5" +# Serial: 11930366277458970227240571539258396554 +# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 +# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 +# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root R1 O=Certainly +# Subject: CN=Certainly Root R1 O=Certainly +# Label: "Certainly Root R1" +# Serial: 188833316161142517227353805653483829216 +# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 +# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af +# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root E1 O=Certainly +# Subject: CN=Certainly Root E1 O=Certainly +# Label: "Certainly Root E1" +# Serial: 8168531406727139161245376702891150584 +# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 +# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b +# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Subject: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Label: "E-Tugra Global Root CA RSA v3" +# Serial: 75951268308633135324246244059508261641472512052 +# MD5 Fingerprint: 22:be:10:f6:c2:f8:03:88:73:5f:33:29:47:28:47:a4 +# SHA1 Fingerprint: e9:a8:5d:22:14:52:1c:5b:aa:0a:b4:be:24:6a:23:8a:c9:ba:e2:a9 +# SHA256 Fingerprint: ef:66:b0:b1:0a:3c:db:9f:2e:36:48:c7:6b:d2:af:18:ea:d2:bf:e6:f1:17:65:5e:28:c4:06:0d:a1:a3:f4:c2 +-----BEGIN CERTIFICATE----- +MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQEL +BQAwgYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUt +VHVncmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYw +JAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIFJTQSB2MzAeFw0yMDAzMTgw +OTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMG +QW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 +Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBD +QSBSU0EgdjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J7 +7gnJY9LTQ91ew6aEOErxjYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscx +uj7X/iWpKo429NEvx7epXTPcMHD4QGxLsqYxYdE0PD0xesevxKenhOGXpOhL9hd8 +7jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF/YP9f4RtNGx/ardLAQO/ +rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8qQedmCeFL +l+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bG +wzrwbMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4 +znKS4iicvObpCdg604nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBO +M/J+JjKsBY04pOZ2PJ8QaQ5tndLBeSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK +5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiMbIedBi3x7+PmBvrFZhNb/FAH +nnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbgh3cXTJ2w2Amo +DVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD +AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSy +tK7mLfcm1ap1LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEL +BQADggIBAImocn+M684uGMQQgC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ +6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN438o2Fi+CiJ+8EUdPdk3ILY7r3y18 +Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/qln0F7psTpURs+APQ +3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3sSdPk +vmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn9 +9t2HVhjYsCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQ +mhty3QUBjYZgv6Rn7rWlDdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YA +VSgU7NbHEqIbZULpkejLPoeJVF3Zr52XnGnnCv8PWniLYypMfUeUP95L6VPQMPHF +9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFHIK+WEj5jlB0E5y67hscM +moi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiXYY60MGo8 +bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ +-----END CERTIFICATE----- + +# Issuer: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Subject: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center +# Label: "E-Tugra Global Root CA ECC v3" +# Serial: 218504919822255052842371958738296604628416471745 +# MD5 Fingerprint: 46:bc:81:bb:f1:b5:1e:f7:4b:96:bc:14:e2:e7:27:64 +# SHA1 Fingerprint: 8a:2f:af:57:53:b1:b0:e6:a1:04:ec:5b:6a:69:71:6d:f6:1c:e2:84 +# SHA256 Fingerprint: 87:3f:46:85:fa:7f:56:36:25:25:2e:6d:36:bc:d7:f1:6f:c2:49:51:f2:64:e4:7e:1b:95:4f:49:08:cd:ca:13 +-----BEGIN CERTIFICATE----- +MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw +gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn +cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD +VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2 +NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r +YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh +IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF +Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ +KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK +fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB +Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C +MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp +ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6 +7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx +vmjkI6TZraE3 +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication RootCA3" +# Serial: 16247922307909811815 +# MD5 Fingerprint: 1c:9a:16:ff:9e:5c:e0:4d:8a:14:01:f4:35:5d:29:26 +# SHA1 Fingerprint: c3:03:c8:22:74:92:e5:61:a2:9c:5f:79:91:2b:1e:44:13:91:30:3a +# SHA256 Fingerprint: 24:a5:5c:2a:b0:51:44:2d:06:17:76:65:41:23:9a:4a:d0:32:d7:c5:51:75:aa:34:ff:de:2f:bc:4f:5c:52:94 +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV +BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw +JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2 +MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg +Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r +CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA +lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG +TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7 +9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7 +8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4 +g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we +GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst ++3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M +0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ +T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw +HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA +FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd +9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI +UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+ +OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke +gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf +iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV +nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD +2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI// +1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad +TdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication ECC RootCA1" +# Serial: 15446673492073852651 +# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 +# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 +# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA1" +# Serial: 113562791157148395269083148143378328608 +# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 +# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a +# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA2" +# Serial: 58605626836079930195615843123109055211 +# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c +# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 +# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- diff --git a/telegramer/include/certifi/core.py b/telegramer/include/certifi/core.py new file mode 100644 index 0000000..de02898 --- /dev/null +++ b/telegramer/include/certifi/core.py @@ -0,0 +1,108 @@ +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem or its contents. +""" +import sys + + +if sys.version_info >= (3, 11): + + from importlib.resources import as_file, files + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the file + # in cases where we're inside of a zipimport situation until someone + # actually calls where(), but we don't want to re-extract the file + # on every call of where(), so we'll do it once then store it in a + # global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you to + # manage the cleanup of this file, so it doesn't actually return a + # path, it returns a context manager that will give you the path + # when you enter it and will do any cleanup when you leave it. In + # the common case of not needing a temporary file, it will just + # return the file system location and the __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + + return _CACERT_PATH + + def contents() -> str: + return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") + +elif sys.version_info >= (3, 7): + + from importlib.resources import path as get_path, read_text + + _CACERT_CTX = None + _CACERT_PATH = None + + def where() -> str: + # This is slightly terrible, but we want to delay extracting the + # file in cases where we're inside of a zipimport situation until + # someone actually calls where(), but we don't want to re-extract + # the file on every call of where(), so we'll do it once then store + # it in a global variable. + global _CACERT_CTX + global _CACERT_PATH + if _CACERT_PATH is None: + # This is slightly janky, the importlib.resources API wants you + # to manage the cleanup of this file, so it doesn't actually + # return a path, it returns a context manager that will give + # you the path when you enter it and will do any cleanup when + # you leave it. In the common case of not needing a temporary + # file, it will just return the file system location and the + # __exit__() is a no-op. + # + # We also have to hold onto the actual context manager, because + # it will do the cleanup whenever it gets garbage collected, so + # we will also store that at the global level as well. + _CACERT_CTX = get_path("certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + + return _CACERT_PATH + + def contents() -> str: + return read_text("certifi", "cacert.pem", encoding="ascii") + +else: + import os + import types + from typing import Union + + Package = Union[types.ModuleType, str] + Resource = Union[str, "os.PathLike"] + + # This fallback will work for Python versions prior to 3.7 that lack the + # importlib.resources module but relies on the existing `where` function + # so won't address issues with environments like PyOxidizer that don't set + # __file__ on modules. + def read_text( + package: Package, + resource: Resource, + encoding: str = 'utf-8', + errors: str = 'strict' + ) -> str: + with open(where(), encoding=encoding) as data: + return data.read() + + # If we don't have importlib.resources, then we will just do the old logic + # of assuming we're on the filesystem and munge the path directly. + def where() -> str: + f = os.path.dirname(__file__) + + return os.path.join(f, "cacert.pem") + + def contents() -> str: + return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/telegramer/include/certifi/py.typed b/telegramer/include/certifi/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/telegramer/include/imghdr/__init__.py b/telegramer/include/imghdr/__init__.py index 1683024..fe0337e 100644 --- a/telegramer/include/imghdr/__init__.py +++ b/telegramer/include/imghdr/__init__.py @@ -144,18 +144,18 @@ def testall(list, recursive, toplevel): import os for filename in list: if os.path.isdir(filename): - print filename + '/:', + print(filename + '/:', end=' '), if recursive or toplevel: print 'recursing down:' import glob names = glob.glob(os.path.join(filename, '*')) testall(names, recursive, 0) else: - print '*** directory (use -r) ***' + print('*** directory (use -r) ***') else: - print filename + ':', + print(filename + ':', end=' ') sys.stdout.flush() try: print what(filename) except IOError: - print '*** not found ***' + print('*** not found ***') From 9f3dd38efdf65595c2c5c1fcf50c36aeaf6e89da Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 25 May 2023 02:21:14 +0300 Subject: [PATCH 2/3] add requirements.txt, fetch packages during build --- Dockerfile-py | 9 +++++++++ requirements.txt | 1 + 2 files changed, 10 insertions(+) create mode 100644 requirements.txt diff --git a/Dockerfile-py b/Dockerfile-py index e2aeb98..1910e9f 100644 --- a/Dockerfile-py +++ b/Dockerfile-py @@ -8,5 +8,14 @@ WORKDIR /usr/src/app COPY telegramer /usr/src/app/telegramer COPY setup.py /usr/src/app/setup.py COPY LICENSE /usr/src/app/LICENSE +COPY requirements.txt /usr/src/app/requirements.txt + +WORKDIR /usr/src/app/telegramer +RUN pip install --no-cache-dir -t ./include -r /usr/src/app/requirements.txt + +WORKDIR /usr/src/app/telegramer/include +RUN rm -rf *.dist-info __pycache__ *.egg-info setuptools + +WORKDIR /usr/src/app RUN python setup.py bdist_egg diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7d6b9c4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +python-telegram-bot==13.11 From fe0517a31366b723751fc4b5d43517ae50578857 Mon Sep 17 00:00:00 2001 From: Noam Date: Thu, 25 May 2023 02:21:50 +0300 Subject: [PATCH 3/3] remove old includes --- telegramer/include/apscheduler/__init__.py | 10 - telegramer/include/apscheduler/events.py | 94 - .../include/apscheduler/executors/__init__.py | 0 .../include/apscheduler/executors/asyncio.py | 59 - .../include/apscheduler/executors/base.py | 146 - .../include/apscheduler/executors/base_py3.py | 43 - .../include/apscheduler/executors/debug.py | 20 - .../include/apscheduler/executors/gevent.py | 30 - .../include/apscheduler/executors/pool.py | 71 - .../include/apscheduler/executors/tornado.py | 54 - .../include/apscheduler/executors/twisted.py | 25 - telegramer/include/apscheduler/job.py | 302 - .../include/apscheduler/jobstores/__init__.py | 0 .../include/apscheduler/jobstores/base.py | 143 - .../include/apscheduler/jobstores/memory.py | 108 - .../include/apscheduler/jobstores/mongodb.py | 141 - .../include/apscheduler/jobstores/redis.py | 150 - .../apscheduler/jobstores/rethinkdb.py | 155 - .../apscheduler/jobstores/sqlalchemy.py | 154 - .../apscheduler/jobstores/zookeeper.py | 178 - .../apscheduler/schedulers/__init__.py | 12 - .../include/apscheduler/schedulers/asyncio.py | 74 - .../apscheduler/schedulers/background.py | 43 - .../include/apscheduler/schedulers/base.py | 1026 --- .../apscheduler/schedulers/blocking.py | 35 - .../include/apscheduler/schedulers/gevent.py | 35 - .../include/apscheduler/schedulers/qt.py | 50 - .../include/apscheduler/schedulers/tornado.py | 63 - .../include/apscheduler/schedulers/twisted.py | 62 - .../include/apscheduler/triggers/__init__.py | 0 .../include/apscheduler/triggers/base.py | 37 - .../include/apscheduler/triggers/combining.py | 95 - .../apscheduler/triggers/cron/__init__.py | 239 - .../apscheduler/triggers/cron/expressions.py | 251 - .../apscheduler/triggers/cron/fields.py | 111 - .../include/apscheduler/triggers/date.py | 51 - .../include/apscheduler/triggers/interval.py | 108 - telegramer/include/apscheduler/util.py | 438 -- telegramer/include/cachetools/__init__.py | 718 -- telegramer/include/cachetools/func.py | 171 - telegramer/include/cachetools/keys.py | 52 - telegramer/include/certifi/__init__.py | 4 - telegramer/include/certifi/__main__.py | 12 - telegramer/include/certifi/cacert.pem | 4589 ------------- telegramer/include/certifi/core.py | 108 - telegramer/include/certifi/py.typed | 0 telegramer/include/pytz/__init__.py | 1559 ----- telegramer/include/pytz/exceptions.py | 59 - telegramer/include/pytz/lazy.py | 172 - telegramer/include/pytz/reference.py | 140 - telegramer/include/pytz/tzfile.py | 133 - telegramer/include/pytz/tzinfo.py | 577 -- .../include/pytz/zoneinfo/Africa/Abidjan | Bin 148 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Accra | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Addis_Ababa | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Algiers | Bin 735 -> 0 bytes .../include/pytz/zoneinfo/Africa/Asmara | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Asmera | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Bamako | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Bangui | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Banjul | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Bissau | Bin 194 -> 0 bytes .../include/pytz/zoneinfo/Africa/Blantyre | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Brazzaville | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Bujumbura | Bin 149 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Cairo | Bin 1955 -> 0 bytes .../include/pytz/zoneinfo/Africa/Casablanca | Bin 2429 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Ceuta | Bin 2036 -> 0 bytes .../include/pytz/zoneinfo/Africa/Conakry | Bin 148 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Dakar | Bin 148 -> 0 bytes .../pytz/zoneinfo/Africa/Dar_es_Salaam | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Djibouti | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Douala | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/El_Aaiun | Bin 2295 -> 0 bytes .../include/pytz/zoneinfo/Africa/Freetown | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Gaborone | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Harare | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Johannesburg | Bin 246 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Juba | Bin 679 -> 0 bytes .../include/pytz/zoneinfo/Africa/Kampala | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Khartoum | Bin 679 -> 0 bytes .../include/pytz/zoneinfo/Africa/Kigali | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Kinshasa | Bin 235 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Lagos | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Libreville | Bin 235 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Lome | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Luanda | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Lubumbashi | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Lusaka | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Malabo | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Maputo | Bin 149 -> 0 bytes .../include/pytz/zoneinfo/Africa/Maseru | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/Africa/Mbabane | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/Africa/Mogadishu | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Monrovia | Bin 208 -> 0 bytes .../include/pytz/zoneinfo/Africa/Nairobi | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Africa/Ndjamena | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Africa/Niamey | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Nouakchott | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Ouagadougou | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Porto-Novo | Bin 235 -> 0 bytes .../include/pytz/zoneinfo/Africa/Sao_Tome | Bin 254 -> 0 bytes .../include/pytz/zoneinfo/Africa/Timbuktu | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Africa/Tripoli | Bin 625 -> 0 bytes telegramer/include/pytz/zoneinfo/Africa/Tunis | Bin 689 -> 0 bytes .../include/pytz/zoneinfo/Africa/Windhoek | Bin 955 -> 0 bytes telegramer/include/pytz/zoneinfo/America/Adak | Bin 2356 -> 0 bytes .../include/pytz/zoneinfo/America/Anchorage | Bin 2371 -> 0 bytes .../include/pytz/zoneinfo/America/Anguilla | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Antigua | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Araguaina | Bin 884 -> 0 bytes .../zoneinfo/America/Argentina/Buenos_Aires | Bin 1076 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Catamarca | Bin 1076 -> 0 bytes .../zoneinfo/America/Argentina/ComodRivadavia | Bin 1076 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Cordoba | Bin 1076 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Jujuy | Bin 1048 -> 0 bytes .../pytz/zoneinfo/America/Argentina/La_Rioja | Bin 1090 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Mendoza | Bin 1076 -> 0 bytes .../zoneinfo/America/Argentina/Rio_Gallegos | Bin 1076 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Salta | Bin 1048 -> 0 bytes .../pytz/zoneinfo/America/Argentina/San_Juan | Bin 1090 -> 0 bytes .../pytz/zoneinfo/America/Argentina/San_Luis | Bin 1102 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Tucuman | Bin 1104 -> 0 bytes .../pytz/zoneinfo/America/Argentina/Ushuaia | Bin 1076 -> 0 bytes .../include/pytz/zoneinfo/America/Aruba | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Asuncion | Bin 2044 -> 0 bytes .../include/pytz/zoneinfo/America/Atikokan | Bin 182 -> 0 bytes telegramer/include/pytz/zoneinfo/America/Atka | Bin 2356 -> 0 bytes .../include/pytz/zoneinfo/America/Bahia | Bin 1024 -> 0 bytes .../pytz/zoneinfo/America/Bahia_Banderas | Bin 1546 -> 0 bytes .../include/pytz/zoneinfo/America/Barbados | Bin 436 -> 0 bytes .../include/pytz/zoneinfo/America/Belem | Bin 576 -> 0 bytes .../include/pytz/zoneinfo/America/Belize | Bin 1614 -> 0 bytes .../pytz/zoneinfo/America/Blanc-Sablon | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Boa_Vista | Bin 632 -> 0 bytes .../include/pytz/zoneinfo/America/Bogota | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Boise | Bin 2394 -> 0 bytes .../pytz/zoneinfo/America/Buenos_Aires | Bin 1076 -> 0 bytes .../pytz/zoneinfo/America/Cambridge_Bay | Bin 2084 -> 0 bytes .../pytz/zoneinfo/America/Campo_Grande | Bin 1444 -> 0 bytes .../include/pytz/zoneinfo/America/Cancun | Bin 782 -> 0 bytes .../include/pytz/zoneinfo/America/Caracas | Bin 264 -> 0 bytes .../include/pytz/zoneinfo/America/Catamarca | Bin 1076 -> 0 bytes .../include/pytz/zoneinfo/America/Cayenne | Bin 198 -> 0 bytes .../include/pytz/zoneinfo/America/Cayman | Bin 182 -> 0 bytes .../include/pytz/zoneinfo/America/Chicago | Bin 3576 -> 0 bytes .../include/pytz/zoneinfo/America/Chihuahua | Bin 1484 -> 0 bytes .../pytz/zoneinfo/America/Coral_Harbour | Bin 182 -> 0 bytes .../include/pytz/zoneinfo/America/Cordoba | Bin 1076 -> 0 bytes .../include/pytz/zoneinfo/America/Costa_Rica | Bin 316 -> 0 bytes .../include/pytz/zoneinfo/America/Creston | Bin 328 -> 0 bytes .../include/pytz/zoneinfo/America/Cuiaba | Bin 1416 -> 0 bytes .../include/pytz/zoneinfo/America/Curacao | Bin 246 -> 0 bytes .../pytz/zoneinfo/America/Danmarkshavn | Bin 698 -> 0 bytes .../include/pytz/zoneinfo/America/Dawson | Bin 1614 -> 0 bytes .../pytz/zoneinfo/America/Dawson_Creek | Bin 1050 -> 0 bytes .../include/pytz/zoneinfo/America/Denver | Bin 2444 -> 0 bytes .../include/pytz/zoneinfo/America/Detroit | Bin 2230 -> 0 bytes .../include/pytz/zoneinfo/America/Dominica | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Edmonton | Bin 2332 -> 0 bytes .../include/pytz/zoneinfo/America/Eirunepe | Bin 656 -> 0 bytes .../include/pytz/zoneinfo/America/El_Salvador | Bin 224 -> 0 bytes .../include/pytz/zoneinfo/America/Ensenada | Bin 2342 -> 0 bytes .../include/pytz/zoneinfo/America/Fort_Nelson | Bin 2240 -> 0 bytes .../include/pytz/zoneinfo/America/Fort_Wayne | Bin 1666 -> 0 bytes .../include/pytz/zoneinfo/America/Fortaleza | Bin 716 -> 0 bytes .../include/pytz/zoneinfo/America/Glace_Bay | Bin 2192 -> 0 bytes .../include/pytz/zoneinfo/America/Godthab | Bin 1878 -> 0 bytes .../include/pytz/zoneinfo/America/Goose_Bay | Bin 3210 -> 0 bytes .../include/pytz/zoneinfo/America/Grand_Turk | Bin 1834 -> 0 bytes .../include/pytz/zoneinfo/America/Grenada | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Guadeloupe | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Guatemala | Bin 280 -> 0 bytes .../include/pytz/zoneinfo/America/Guayaquil | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Guyana | Bin 262 -> 0 bytes .../include/pytz/zoneinfo/America/Halifax | Bin 3424 -> 0 bytes .../include/pytz/zoneinfo/America/Havana | Bin 2416 -> 0 bytes .../include/pytz/zoneinfo/America/Hermosillo | Bin 416 -> 0 bytes .../zoneinfo/America/Indiana/Indianapolis | Bin 1666 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Knox | Bin 2428 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Marengo | Bin 1722 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Petersburg | Bin 1904 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Tell_City | Bin 1684 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Vevay | Bin 1414 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Vincennes | Bin 1694 -> 0 bytes .../pytz/zoneinfo/America/Indiana/Winamac | Bin 1778 -> 0 bytes .../pytz/zoneinfo/America/Indianapolis | Bin 1666 -> 0 bytes .../include/pytz/zoneinfo/America/Inuvik | Bin 1894 -> 0 bytes .../include/pytz/zoneinfo/America/Iqaluit | Bin 2032 -> 0 bytes .../include/pytz/zoneinfo/America/Jamaica | Bin 482 -> 0 bytes .../include/pytz/zoneinfo/America/Jujuy | Bin 1048 -> 0 bytes .../include/pytz/zoneinfo/America/Juneau | Bin 2353 -> 0 bytes .../pytz/zoneinfo/America/Kentucky/Louisville | Bin 2772 -> 0 bytes .../pytz/zoneinfo/America/Kentucky/Monticello | Bin 2352 -> 0 bytes .../include/pytz/zoneinfo/America/Knox_IN | Bin 2428 -> 0 bytes .../include/pytz/zoneinfo/America/Kralendijk | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/La_Paz | Bin 232 -> 0 bytes telegramer/include/pytz/zoneinfo/America/Lima | Bin 406 -> 0 bytes .../include/pytz/zoneinfo/America/Los_Angeles | Bin 2836 -> 0 bytes .../include/pytz/zoneinfo/America/Louisville | Bin 2772 -> 0 bytes .../pytz/zoneinfo/America/Lower_Princes | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Maceio | Bin 744 -> 0 bytes .../include/pytz/zoneinfo/America/Managua | Bin 430 -> 0 bytes .../include/pytz/zoneinfo/America/Manaus | Bin 604 -> 0 bytes .../include/pytz/zoneinfo/America/Marigot | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Martinique | Bin 232 -> 0 bytes .../include/pytz/zoneinfo/America/Matamoros | Bin 1390 -> 0 bytes .../include/pytz/zoneinfo/America/Mazatlan | Bin 1526 -> 0 bytes .../include/pytz/zoneinfo/America/Mendoza | Bin 1076 -> 0 bytes .../include/pytz/zoneinfo/America/Menominee | Bin 2274 -> 0 bytes .../include/pytz/zoneinfo/America/Merida | Bin 1422 -> 0 bytes .../include/pytz/zoneinfo/America/Metlakatla | Bin 1423 -> 0 bytes .../include/pytz/zoneinfo/America/Mexico_City | Bin 1584 -> 0 bytes .../include/pytz/zoneinfo/America/Miquelon | Bin 1666 -> 0 bytes .../include/pytz/zoneinfo/America/Moncton | Bin 3154 -> 0 bytes .../include/pytz/zoneinfo/America/Monterrey | Bin 1390 -> 0 bytes .../include/pytz/zoneinfo/America/Montevideo | Bin 1510 -> 0 bytes .../include/pytz/zoneinfo/America/Montreal | Bin 3494 -> 0 bytes .../include/pytz/zoneinfo/America/Montserrat | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Nassau | Bin 3494 -> 0 bytes .../include/pytz/zoneinfo/America/New_York | Bin 3536 -> 0 bytes .../include/pytz/zoneinfo/America/Nipigon | Bin 2122 -> 0 bytes telegramer/include/pytz/zoneinfo/America/Nome | Bin 2367 -> 0 bytes .../include/pytz/zoneinfo/America/Noronha | Bin 716 -> 0 bytes .../pytz/zoneinfo/America/North_Dakota/Beulah | Bin 2380 -> 0 bytes .../pytz/zoneinfo/America/North_Dakota/Center | Bin 2380 -> 0 bytes .../zoneinfo/America/North_Dakota/New_Salem | Bin 2380 -> 0 bytes telegramer/include/pytz/zoneinfo/America/Nuuk | Bin 1878 -> 0 bytes .../include/pytz/zoneinfo/America/Ojinaga | Bin 1484 -> 0 bytes .../include/pytz/zoneinfo/America/Panama | Bin 182 -> 0 bytes .../include/pytz/zoneinfo/America/Pangnirtung | Bin 2094 -> 0 bytes .../include/pytz/zoneinfo/America/Paramaribo | Bin 262 -> 0 bytes .../include/pytz/zoneinfo/America/Phoenix | Bin 328 -> 0 bytes .../pytz/zoneinfo/America/Port-au-Prince | Bin 1434 -> 0 bytes .../pytz/zoneinfo/America/Port_of_Spain | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Porto_Acre | Bin 628 -> 0 bytes .../include/pytz/zoneinfo/America/Porto_Velho | Bin 576 -> 0 bytes .../include/pytz/zoneinfo/America/Puerto_Rico | Bin 246 -> 0 bytes .../pytz/zoneinfo/America/Punta_Arenas | Bin 1902 -> 0 bytes .../include/pytz/zoneinfo/America/Rainy_River | Bin 2122 -> 0 bytes .../pytz/zoneinfo/America/Rankin_Inlet | Bin 1892 -> 0 bytes .../include/pytz/zoneinfo/America/Recife | Bin 716 -> 0 bytes .../include/pytz/zoneinfo/America/Regina | Bin 980 -> 0 bytes .../include/pytz/zoneinfo/America/Resolute | Bin 1892 -> 0 bytes .../include/pytz/zoneinfo/America/Rio_Branco | Bin 628 -> 0 bytes .../include/pytz/zoneinfo/America/Rosario | Bin 1076 -> 0 bytes .../pytz/zoneinfo/America/Santa_Isabel | Bin 2342 -> 0 bytes .../include/pytz/zoneinfo/America/Santarem | Bin 602 -> 0 bytes .../include/pytz/zoneinfo/America/Santiago | Bin 2529 -> 0 bytes .../pytz/zoneinfo/America/Santo_Domingo | Bin 458 -> 0 bytes .../include/pytz/zoneinfo/America/Sao_Paulo | Bin 1444 -> 0 bytes .../pytz/zoneinfo/America/Scoresbysund | Bin 1916 -> 0 bytes .../include/pytz/zoneinfo/America/Shiprock | Bin 2444 -> 0 bytes .../include/pytz/zoneinfo/America/Sitka | Bin 2329 -> 0 bytes .../pytz/zoneinfo/America/St_Barthelemy | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/St_Johns | Bin 3655 -> 0 bytes .../include/pytz/zoneinfo/America/St_Kitts | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/St_Lucia | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/St_Thomas | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/St_Vincent | Bin 246 -> 0 bytes .../pytz/zoneinfo/America/Swift_Current | Bin 560 -> 0 bytes .../include/pytz/zoneinfo/America/Tegucigalpa | Bin 252 -> 0 bytes .../include/pytz/zoneinfo/America/Thule | Bin 1502 -> 0 bytes .../include/pytz/zoneinfo/America/Thunder_Bay | Bin 2202 -> 0 bytes .../include/pytz/zoneinfo/America/Tijuana | Bin 2342 -> 0 bytes .../include/pytz/zoneinfo/America/Toronto | Bin 3494 -> 0 bytes .../include/pytz/zoneinfo/America/Tortola | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Vancouver | Bin 2892 -> 0 bytes .../include/pytz/zoneinfo/America/Virgin | Bin 246 -> 0 bytes .../include/pytz/zoneinfo/America/Whitehorse | Bin 1614 -> 0 bytes .../include/pytz/zoneinfo/America/Winnipeg | Bin 2868 -> 0 bytes .../include/pytz/zoneinfo/America/Yakutat | Bin 2305 -> 0 bytes .../include/pytz/zoneinfo/America/Yellowknife | Bin 1966 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Casey | Bin 384 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Davis | Bin 297 -> 0 bytes .../pytz/zoneinfo/Antarctica/DumontDUrville | Bin 186 -> 0 bytes .../pytz/zoneinfo/Antarctica/Macquarie | Bin 2260 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Mawson | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/McMurdo | Bin 2437 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Palmer | Bin 1418 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Rothera | Bin 164 -> 0 bytes .../pytz/zoneinfo/Antarctica/South_Pole | Bin 2437 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Syowa | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Troll | Bin 1162 -> 0 bytes .../include/pytz/zoneinfo/Antarctica/Vostok | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Arctic/Longyearbyen | Bin 2228 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Aden | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Almaty | Bin 997 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Amman | Bin 1853 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Anadyr | Bin 1188 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Aqtau | Bin 983 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Aqtobe | Bin 1011 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ashgabat | Bin 619 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ashkhabad | Bin 619 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Atyrau | Bin 991 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Baghdad | Bin 983 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Bahrain | Bin 199 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Baku | Bin 1227 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Bangkok | Bin 199 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Barnaul | Bin 1221 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Beirut | Bin 2154 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Bishkek | Bin 983 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Brunei | Bin 203 -> 0 bytes .../include/pytz/zoneinfo/Asia/Calcutta | Bin 285 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Chita | Bin 1221 -> 0 bytes .../include/pytz/zoneinfo/Asia/Choibalsan | Bin 949 -> 0 bytes .../include/pytz/zoneinfo/Asia/Chongqing | Bin 561 -> 0 bytes .../include/pytz/zoneinfo/Asia/Chungking | Bin 561 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Colombo | Bin 372 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Dacca | Bin 337 -> 0 bytes .../include/pytz/zoneinfo/Asia/Damascus | Bin 2294 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Dhaka | Bin 337 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Dili | Bin 227 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Dubai | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Asia/Dushanbe | Bin 591 -> 0 bytes .../include/pytz/zoneinfo/Asia/Famagusta | Bin 2028 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Gaza | Bin 2422 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Harbin | Bin 561 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Hebron | Bin 2450 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ho_Chi_Minh | Bin 351 -> 0 bytes .../include/pytz/zoneinfo/Asia/Hong_Kong | Bin 1203 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Hovd | Bin 891 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Irkutsk | Bin 1243 -> 0 bytes .../include/pytz/zoneinfo/Asia/Istanbul | Bin 1947 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Jakarta | Bin 355 -> 0 bytes .../include/pytz/zoneinfo/Asia/Jayapura | Bin 221 -> 0 bytes .../include/pytz/zoneinfo/Asia/Jerusalem | Bin 2388 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Kabul | Bin 208 -> 0 bytes .../include/pytz/zoneinfo/Asia/Kamchatka | Bin 1166 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Karachi | Bin 379 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Kashgar | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Asia/Kathmandu | Bin 212 -> 0 bytes .../include/pytz/zoneinfo/Asia/Katmandu | Bin 212 -> 0 bytes .../include/pytz/zoneinfo/Asia/Khandyga | Bin 1271 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Kolkata | Bin 285 -> 0 bytes .../include/pytz/zoneinfo/Asia/Krasnoyarsk | Bin 1207 -> 0 bytes .../include/pytz/zoneinfo/Asia/Kuala_Lumpur | Bin 383 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Kuching | Bin 483 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Kuwait | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Macao | Bin 1227 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Macau | Bin 1227 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Magadan | Bin 1222 -> 0 bytes .../include/pytz/zoneinfo/Asia/Makassar | Bin 254 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Manila | Bin 328 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Muscat | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Nicosia | Bin 2002 -> 0 bytes .../include/pytz/zoneinfo/Asia/Novokuznetsk | Bin 1165 -> 0 bytes .../include/pytz/zoneinfo/Asia/Novosibirsk | Bin 1221 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Omsk | Bin 1207 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Oral | Bin 1005 -> 0 bytes .../include/pytz/zoneinfo/Asia/Phnom_Penh | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Asia/Pontianak | Bin 353 -> 0 bytes .../include/pytz/zoneinfo/Asia/Pyongyang | Bin 237 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Qatar | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Asia/Qostanay | Bin 1011 -> 0 bytes .../include/pytz/zoneinfo/Asia/Qyzylorda | Bin 1025 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Rangoon | Bin 268 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Riyadh | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Saigon | Bin 351 -> 0 bytes .../include/pytz/zoneinfo/Asia/Sakhalin | Bin 1202 -> 0 bytes .../include/pytz/zoneinfo/Asia/Samarkand | Bin 577 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Seoul | Bin 617 -> 0 bytes .../include/pytz/zoneinfo/Asia/Shanghai | Bin 561 -> 0 bytes .../include/pytz/zoneinfo/Asia/Singapore | Bin 383 -> 0 bytes .../include/pytz/zoneinfo/Asia/Srednekolymsk | Bin 1208 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Taipei | Bin 761 -> 0 bytes .../include/pytz/zoneinfo/Asia/Tashkent | Bin 591 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Tbilisi | Bin 1035 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Tehran | Bin 2582 -> 0 bytes .../include/pytz/zoneinfo/Asia/Tel_Aviv | Bin 2388 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Thimbu | Bin 203 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Thimphu | Bin 203 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Tokyo | Bin 309 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Tomsk | Bin 1221 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ujung_Pandang | Bin 254 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ulaanbaatar | Bin 891 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ulan_Bator | Bin 891 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Urumqi | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Asia/Ust-Nera | Bin 1252 -> 0 bytes .../include/pytz/zoneinfo/Asia/Vientiane | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Asia/Vladivostok | Bin 1208 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Yakutsk | Bin 1207 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Yangon | Bin 268 -> 0 bytes .../include/pytz/zoneinfo/Asia/Yekaterinburg | Bin 1243 -> 0 bytes telegramer/include/pytz/zoneinfo/Asia/Yerevan | Bin 1151 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Azores | Bin 3512 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Bermuda | Bin 2396 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Canary | Bin 1897 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Cape_Verde | Bin 270 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Faeroe | Bin 1815 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Faroe | Bin 1815 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Jan_Mayen | Bin 2228 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Madeira | Bin 3503 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Reykjavik | Bin 1162 -> 0 bytes .../pytz/zoneinfo/Atlantic/South_Georgia | Bin 164 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/St_Helena | Bin 148 -> 0 bytes .../include/pytz/zoneinfo/Atlantic/Stanley | Bin 1214 -> 0 bytes .../include/pytz/zoneinfo/Australia/ACT | Bin 2190 -> 0 bytes .../include/pytz/zoneinfo/Australia/Adelaide | Bin 2208 -> 0 bytes .../include/pytz/zoneinfo/Australia/Brisbane | Bin 419 -> 0 bytes .../pytz/zoneinfo/Australia/Broken_Hill | Bin 2229 -> 0 bytes .../include/pytz/zoneinfo/Australia/Canberra | Bin 2190 -> 0 bytes .../include/pytz/zoneinfo/Australia/Currie | Bin 2358 -> 0 bytes .../include/pytz/zoneinfo/Australia/Darwin | Bin 325 -> 0 bytes .../include/pytz/zoneinfo/Australia/Eucla | Bin 470 -> 0 bytes .../include/pytz/zoneinfo/Australia/Hobart | Bin 2358 -> 0 bytes .../include/pytz/zoneinfo/Australia/LHI | Bin 1860 -> 0 bytes .../include/pytz/zoneinfo/Australia/Lindeman | Bin 475 -> 0 bytes .../include/pytz/zoneinfo/Australia/Lord_Howe | Bin 1860 -> 0 bytes .../include/pytz/zoneinfo/Australia/Melbourne | Bin 2190 -> 0 bytes .../include/pytz/zoneinfo/Australia/NSW | Bin 2190 -> 0 bytes .../include/pytz/zoneinfo/Australia/North | Bin 325 -> 0 bytes .../include/pytz/zoneinfo/Australia/Perth | Bin 446 -> 0 bytes .../pytz/zoneinfo/Australia/Queensland | Bin 419 -> 0 bytes .../include/pytz/zoneinfo/Australia/South | Bin 2208 -> 0 bytes .../include/pytz/zoneinfo/Australia/Sydney | Bin 2190 -> 0 bytes .../include/pytz/zoneinfo/Australia/Tasmania | Bin 2358 -> 0 bytes .../include/pytz/zoneinfo/Australia/Victoria | Bin 2190 -> 0 bytes .../include/pytz/zoneinfo/Australia/West | Bin 446 -> 0 bytes .../pytz/zoneinfo/Australia/Yancowinna | Bin 2229 -> 0 bytes telegramer/include/pytz/zoneinfo/Brazil/Acre | Bin 628 -> 0 bytes .../include/pytz/zoneinfo/Brazil/DeNoronha | Bin 716 -> 0 bytes telegramer/include/pytz/zoneinfo/Brazil/East | Bin 1444 -> 0 bytes telegramer/include/pytz/zoneinfo/Brazil/West | Bin 604 -> 0 bytes telegramer/include/pytz/zoneinfo/CET | Bin 2094 -> 0 bytes telegramer/include/pytz/zoneinfo/CST6CDT | Bin 2310 -> 0 bytes .../include/pytz/zoneinfo/Canada/Atlantic | Bin 3424 -> 0 bytes .../include/pytz/zoneinfo/Canada/Central | Bin 2868 -> 0 bytes .../include/pytz/zoneinfo/Canada/Eastern | Bin 3494 -> 0 bytes .../include/pytz/zoneinfo/Canada/Mountain | Bin 2332 -> 0 bytes .../include/pytz/zoneinfo/Canada/Newfoundland | Bin 3655 -> 0 bytes .../include/pytz/zoneinfo/Canada/Pacific | Bin 2892 -> 0 bytes .../include/pytz/zoneinfo/Canada/Saskatchewan | Bin 980 -> 0 bytes telegramer/include/pytz/zoneinfo/Canada/Yukon | Bin 1614 -> 0 bytes .../include/pytz/zoneinfo/Chile/Continental | Bin 2529 -> 0 bytes .../include/pytz/zoneinfo/Chile/EasterIsland | Bin 2233 -> 0 bytes telegramer/include/pytz/zoneinfo/Cuba | Bin 2416 -> 0 bytes telegramer/include/pytz/zoneinfo/EET | Bin 1908 -> 0 bytes telegramer/include/pytz/zoneinfo/EST | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/EST5EDT | Bin 2310 -> 0 bytes telegramer/include/pytz/zoneinfo/Egypt | Bin 1955 -> 0 bytes telegramer/include/pytz/zoneinfo/Eire | Bin 3492 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+0 | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+1 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+10 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+11 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+12 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+2 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+3 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+4 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+5 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+6 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+7 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+8 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT+9 | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-0 | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-1 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-10 | Bin 118 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-11 | Bin 118 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-12 | Bin 118 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-13 | Bin 118 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-14 | Bin 118 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-2 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-3 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-4 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-5 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-6 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-7 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-8 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT-9 | Bin 117 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/GMT0 | Bin 114 -> 0 bytes .../include/pytz/zoneinfo/Etc/Greenwich | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/UCT | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/UTC | Bin 114 -> 0 bytes .../include/pytz/zoneinfo/Etc/Universal | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Etc/Zulu | Bin 114 -> 0 bytes .../include/pytz/zoneinfo/Europe/Amsterdam | Bin 2910 -> 0 bytes .../include/pytz/zoneinfo/Europe/Andorra | Bin 1742 -> 0 bytes .../include/pytz/zoneinfo/Europe/Astrakhan | Bin 1165 -> 0 bytes .../include/pytz/zoneinfo/Europe/Athens | Bin 2262 -> 0 bytes .../include/pytz/zoneinfo/Europe/Belfast | Bin 3648 -> 0 bytes .../include/pytz/zoneinfo/Europe/Belgrade | Bin 1920 -> 0 bytes .../include/pytz/zoneinfo/Europe/Berlin | Bin 2298 -> 0 bytes .../include/pytz/zoneinfo/Europe/Bratislava | Bin 2301 -> 0 bytes .../include/pytz/zoneinfo/Europe/Brussels | Bin 2933 -> 0 bytes .../include/pytz/zoneinfo/Europe/Bucharest | Bin 2184 -> 0 bytes .../include/pytz/zoneinfo/Europe/Budapest | Bin 2368 -> 0 bytes .../include/pytz/zoneinfo/Europe/Busingen | Bin 1909 -> 0 bytes .../include/pytz/zoneinfo/Europe/Chisinau | Bin 2390 -> 0 bytes .../include/pytz/zoneinfo/Europe/Copenhagen | Bin 2137 -> 0 bytes .../include/pytz/zoneinfo/Europe/Dublin | Bin 3492 -> 0 bytes .../include/pytz/zoneinfo/Europe/Gibraltar | Bin 3052 -> 0 bytes .../include/pytz/zoneinfo/Europe/Guernsey | Bin 3648 -> 0 bytes .../include/pytz/zoneinfo/Europe/Helsinki | Bin 1900 -> 0 bytes .../include/pytz/zoneinfo/Europe/Isle_of_Man | Bin 3648 -> 0 bytes .../include/pytz/zoneinfo/Europe/Istanbul | Bin 1947 -> 0 bytes .../include/pytz/zoneinfo/Europe/Jersey | Bin 3648 -> 0 bytes .../include/pytz/zoneinfo/Europe/Kaliningrad | Bin 1493 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Kiev | Bin 2120 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Kirov | Bin 1153 -> 0 bytes .../include/pytz/zoneinfo/Europe/Lisbon | Bin 3497 -> 0 bytes .../include/pytz/zoneinfo/Europe/Ljubljana | Bin 1920 -> 0 bytes .../include/pytz/zoneinfo/Europe/London | Bin 3648 -> 0 bytes .../include/pytz/zoneinfo/Europe/Luxembourg | Bin 2946 -> 0 bytes .../include/pytz/zoneinfo/Europe/Madrid | Bin 2614 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Malta | Bin 2620 -> 0 bytes .../include/pytz/zoneinfo/Europe/Mariehamn | Bin 1900 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Minsk | Bin 1321 -> 0 bytes .../include/pytz/zoneinfo/Europe/Monaco | Bin 2944 -> 0 bytes .../include/pytz/zoneinfo/Europe/Moscow | Bin 1535 -> 0 bytes .../include/pytz/zoneinfo/Europe/Nicosia | Bin 2002 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Oslo | Bin 2228 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Paris | Bin 2962 -> 0 bytes .../include/pytz/zoneinfo/Europe/Podgorica | Bin 1920 -> 0 bytes .../include/pytz/zoneinfo/Europe/Prague | Bin 2301 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Riga | Bin 2198 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Rome | Bin 2641 -> 0 bytes .../include/pytz/zoneinfo/Europe/Samara | Bin 1215 -> 0 bytes .../include/pytz/zoneinfo/Europe/San_Marino | Bin 2641 -> 0 bytes .../include/pytz/zoneinfo/Europe/Sarajevo | Bin 1920 -> 0 bytes .../include/pytz/zoneinfo/Europe/Saratov | Bin 1183 -> 0 bytes .../include/pytz/zoneinfo/Europe/Simferopol | Bin 1469 -> 0 bytes .../include/pytz/zoneinfo/Europe/Skopje | Bin 1920 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Sofia | Bin 2077 -> 0 bytes .../include/pytz/zoneinfo/Europe/Stockholm | Bin 1909 -> 0 bytes .../include/pytz/zoneinfo/Europe/Tallinn | Bin 2148 -> 0 bytes .../include/pytz/zoneinfo/Europe/Tirane | Bin 2084 -> 0 bytes .../include/pytz/zoneinfo/Europe/Tiraspol | Bin 2390 -> 0 bytes .../include/pytz/zoneinfo/Europe/Ulyanovsk | Bin 1267 -> 0 bytes .../include/pytz/zoneinfo/Europe/Uzhgorod | Bin 2066 -> 0 bytes telegramer/include/pytz/zoneinfo/Europe/Vaduz | Bin 1909 -> 0 bytes .../include/pytz/zoneinfo/Europe/Vatican | Bin 2641 -> 0 bytes .../include/pytz/zoneinfo/Europe/Vienna | Bin 2200 -> 0 bytes .../include/pytz/zoneinfo/Europe/Vilnius | Bin 2162 -> 0 bytes .../include/pytz/zoneinfo/Europe/Volgograd | Bin 1165 -> 0 bytes .../include/pytz/zoneinfo/Europe/Warsaw | Bin 2654 -> 0 bytes .../include/pytz/zoneinfo/Europe/Zagreb | Bin 1920 -> 0 bytes .../include/pytz/zoneinfo/Europe/Zaporozhye | Bin 2138 -> 0 bytes .../include/pytz/zoneinfo/Europe/Zurich | Bin 1909 -> 0 bytes telegramer/include/pytz/zoneinfo/Factory | Bin 116 -> 0 bytes telegramer/include/pytz/zoneinfo/GB | Bin 3648 -> 0 bytes telegramer/include/pytz/zoneinfo/GB-Eire | Bin 3648 -> 0 bytes telegramer/include/pytz/zoneinfo/GMT | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/GMT+0 | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/GMT-0 | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/GMT0 | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Greenwich | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/HST | Bin 115 -> 0 bytes telegramer/include/pytz/zoneinfo/Hongkong | Bin 1203 -> 0 bytes telegramer/include/pytz/zoneinfo/Iceland | Bin 1162 -> 0 bytes .../include/pytz/zoneinfo/Indian/Antananarivo | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Indian/Chagos | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Indian/Christmas | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Indian/Cocos | Bin 174 -> 0 bytes .../include/pytz/zoneinfo/Indian/Comoro | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Indian/Kerguelen | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Indian/Mahe | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Indian/Maldives | Bin 199 -> 0 bytes .../include/pytz/zoneinfo/Indian/Mauritius | Bin 241 -> 0 bytes .../include/pytz/zoneinfo/Indian/Mayotte | Bin 265 -> 0 bytes .../include/pytz/zoneinfo/Indian/Reunion | Bin 165 -> 0 bytes telegramer/include/pytz/zoneinfo/Iran | Bin 2582 -> 0 bytes telegramer/include/pytz/zoneinfo/Israel | Bin 2388 -> 0 bytes telegramer/include/pytz/zoneinfo/Jamaica | Bin 482 -> 0 bytes telegramer/include/pytz/zoneinfo/Japan | Bin 309 -> 0 bytes telegramer/include/pytz/zoneinfo/Kwajalein | Bin 316 -> 0 bytes telegramer/include/pytz/zoneinfo/Libya | Bin 625 -> 0 bytes telegramer/include/pytz/zoneinfo/MET | Bin 2094 -> 0 bytes telegramer/include/pytz/zoneinfo/MST | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/MST7MDT | Bin 2310 -> 0 bytes .../include/pytz/zoneinfo/Mexico/BajaNorte | Bin 2342 -> 0 bytes .../include/pytz/zoneinfo/Mexico/BajaSur | Bin 1526 -> 0 bytes .../include/pytz/zoneinfo/Mexico/General | Bin 1584 -> 0 bytes telegramer/include/pytz/zoneinfo/NZ | Bin 2437 -> 0 bytes telegramer/include/pytz/zoneinfo/NZ-CHAT | Bin 2068 -> 0 bytes telegramer/include/pytz/zoneinfo/Navajo | Bin 2444 -> 0 bytes telegramer/include/pytz/zoneinfo/PRC | Bin 561 -> 0 bytes telegramer/include/pytz/zoneinfo/PST8PDT | Bin 2310 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Apia | Bin 612 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Auckland | Bin 2437 -> 0 bytes .../pytz/zoneinfo/Pacific/Bougainville | Bin 268 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Chatham | Bin 2068 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Chuuk | Bin 269 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Easter | Bin 2233 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Efate | Bin 538 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Enderbury | Bin 234 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Fakaofo | Bin 200 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Fiji | Bin 1049 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Funafuti | Bin 166 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Galapagos | Bin 238 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Gambier | Bin 164 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Guadalcanal | Bin 166 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Guam | Bin 494 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Honolulu | Bin 329 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Johnston | Bin 329 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Kanton | Bin 234 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Kiritimati | Bin 238 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Kosrae | Bin 351 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Kwajalein | Bin 316 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Majuro | Bin 310 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Marquesas | Bin 173 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Midway | Bin 175 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Nauru | Bin 252 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Niue | Bin 203 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Norfolk | Bin 880 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Noumea | Bin 304 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Pago_Pago | Bin 175 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Palau | Bin 180 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Pitcairn | Bin 202 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Pohnpei | Bin 303 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Ponape | Bin 303 -> 0 bytes .../pytz/zoneinfo/Pacific/Port_Moresby | Bin 186 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Rarotonga | Bin 603 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Saipan | Bin 494 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Samoa | Bin 175 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Tahiti | Bin 165 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Tarawa | Bin 166 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Tongatapu | Bin 372 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Truk | Bin 269 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Wake | Bin 166 -> 0 bytes .../include/pytz/zoneinfo/Pacific/Wallis | Bin 166 -> 0 bytes telegramer/include/pytz/zoneinfo/Pacific/Yap | Bin 269 -> 0 bytes telegramer/include/pytz/zoneinfo/Poland | Bin 2654 -> 0 bytes telegramer/include/pytz/zoneinfo/Portugal | Bin 3497 -> 0 bytes telegramer/include/pytz/zoneinfo/ROC | Bin 761 -> 0 bytes telegramer/include/pytz/zoneinfo/ROK | Bin 617 -> 0 bytes telegramer/include/pytz/zoneinfo/Singapore | Bin 383 -> 0 bytes telegramer/include/pytz/zoneinfo/Turkey | Bin 1947 -> 0 bytes telegramer/include/pytz/zoneinfo/UCT | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Alaska | Bin 2371 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Aleutian | Bin 2356 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Arizona | Bin 328 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Central | Bin 3576 -> 0 bytes .../include/pytz/zoneinfo/US/East-Indiana | Bin 1666 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Eastern | Bin 3536 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Hawaii | Bin 329 -> 0 bytes .../include/pytz/zoneinfo/US/Indiana-Starke | Bin 2428 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Michigan | Bin 2230 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Mountain | Bin 2444 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Pacific | Bin 2836 -> 0 bytes telegramer/include/pytz/zoneinfo/US/Samoa | Bin 175 -> 0 bytes telegramer/include/pytz/zoneinfo/UTC | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/Universal | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/W-SU | Bin 1535 -> 0 bytes telegramer/include/pytz/zoneinfo/WET | Bin 1905 -> 0 bytes telegramer/include/pytz/zoneinfo/Zulu | Bin 114 -> 0 bytes telegramer/include/pytz/zoneinfo/iso3166.tab | 274 - telegramer/include/pytz/zoneinfo/leapseconds | 82 - telegramer/include/pytz/zoneinfo/tzdata.zi | 4437 ------------- telegramer/include/pytz/zoneinfo/zone.tab | 454 -- telegramer/include/pytz/zoneinfo/zone1970.tab | 374 -- telegramer/include/telegram/__init__.py | 328 - telegramer/include/telegram/__main__.py | 54 - telegramer/include/telegram/base.py | 152 - telegramer/include/telegram/bot.py | 5861 ----------------- telegramer/include/telegram/botcommand.py | 50 - .../include/telegram/botcommandscope.py | 263 - telegramer/include/telegram/callbackquery.py | 660 -- telegramer/include/telegram/chat.py | 1815 ----- telegramer/include/telegram/chataction.py | 74 - telegramer/include/telegram/chatinvitelink.py | 146 - .../include/telegram/chatjoinrequest.py | 159 - telegramer/include/telegram/chatlocation.py | 73 - telegramer/include/telegram/chatmember.py | 715 -- .../include/telegram/chatmemberupdated.py | 173 - .../include/telegram/chatpermissions.py | 124 - .../include/telegram/choseninlineresult.py | 98 - telegramer/include/telegram/constants.py | 398 -- telegramer/include/telegram/dice.py | 92 - telegramer/include/telegram/error.py | 151 - telegramer/include/telegram/ext/__init__.py | 106 - .../include/telegram/ext/basepersistence.py | 566 -- .../include/telegram/ext/callbackcontext.py | 361 - .../include/telegram/ext/callbackdatacache.py | 408 -- .../telegram/ext/callbackqueryhandler.py | 236 - .../telegram/ext/chatjoinrequesthandler.py | 100 - .../include/telegram/ext/chatmemberhandler.py | 145 - .../telegram/ext/choseninlineresulthandler.py | 160 - .../include/telegram/ext/commandhandler.py | 456 -- .../include/telegram/ext/contexttypes.py | 202 - .../telegram/ext/conversationhandler.py | 725 -- telegramer/include/telegram/ext/defaults.py | 267 - .../include/telegram/ext/dictpersistence.py | 404 -- telegramer/include/telegram/ext/dispatcher.py | 820 --- telegramer/include/telegram/ext/extbot.py | 341 - telegramer/include/telegram/ext/filters.py | 2342 ------- telegramer/include/telegram/ext/handler.py | 260 - .../telegram/ext/inlinequeryhandler.py | 221 - telegramer/include/telegram/ext/jobqueue.py | 659 -- .../include/telegram/ext/messagehandler.py | 208 - .../include/telegram/ext/messagequeue.py | 334 - .../include/telegram/ext/picklepersistence.py | 463 -- .../include/telegram/ext/pollanswerhandler.py | 98 - .../include/telegram/ext/pollhandler.py | 98 - .../telegram/ext/precheckoutqueryhandler.py | 98 - .../include/telegram/ext/regexhandler.py | 166 - .../telegram/ext/shippingqueryhandler.py | 97 - .../telegram/ext/stringcommandhandler.py | 149 - .../telegram/ext/stringregexhandler.py | 166 - .../include/telegram/ext/typehandler.py | 108 - telegramer/include/telegram/ext/updater.py | 890 --- .../include/telegram/ext/utils/__init__.py | 17 - .../include/telegram/ext/utils/promise.py | 158 - .../include/telegram/ext/utils/types.py | 62 - .../telegram/ext/utils/webhookhandler.py | 177 - telegramer/include/telegram/files/__init__.py | 0 .../include/telegram/files/animation.py | 137 - telegramer/include/telegram/files/audio.py | 141 - .../include/telegram/files/chatphoto.py | 132 - telegramer/include/telegram/files/contact.py | 68 - telegramer/include/telegram/files/document.py | 125 - telegramer/include/telegram/files/file.py | 213 - .../include/telegram/files/inputfile.py | 119 - .../include/telegram/files/inputmedia.py | 525 -- telegramer/include/telegram/files/location.py | 91 - .../include/telegram/files/photosize.py | 98 - telegramer/include/telegram/files/sticker.py | 316 - telegramer/include/telegram/files/venue.py | 107 - telegramer/include/telegram/files/video.py | 138 - .../include/telegram/files/videonote.py | 124 - telegramer/include/telegram/files/voice.py | 106 - telegramer/include/telegram/forcereply.py | 78 - telegramer/include/telegram/games/__init__.py | 0 .../include/telegram/games/callbackgame.py | 27 - telegramer/include/telegram/games/game.py | 186 - .../include/telegram/games/gamehighscore.py | 67 - .../include/telegram/inline/__init__.py | 0 .../telegram/inline/inlinekeyboardbutton.py | 177 - .../telegram/inline/inlinekeyboardmarkup.py | 138 - .../include/telegram/inline/inlinequery.py | 168 - .../telegram/inline/inlinequeryresult.py | 71 - .../inline/inlinequeryresultarticle.py | 105 - .../telegram/inline/inlinequeryresultaudio.py | 116 - .../inline/inlinequeryresultcachedaudio.py | 100 - .../inline/inlinequeryresultcacheddocument.py | 113 - .../inline/inlinequeryresultcachedgif.py | 108 - .../inline/inlinequeryresultcachedmpeg4gif.py | 108 - .../inline/inlinequeryresultcachedphoto.py | 114 - .../inline/inlinequeryresultcachedsticker.py | 71 - .../inline/inlinequeryresultcachedvideo.py | 113 - .../inline/inlinequeryresultcachedvoice.py | 105 - .../inline/inlinequeryresultcontact.py | 107 - .../inline/inlinequeryresultdocument.py | 135 - .../telegram/inline/inlinequeryresultgame.py | 62 - .../telegram/inline/inlinequeryresultgif.py | 137 - .../inline/inlinequeryresultlocation.py | 131 - .../inline/inlinequeryresultmpeg4gif.py | 136 - .../telegram/inline/inlinequeryresultphoto.py | 129 - .../telegram/inline/inlinequeryresultvenue.py | 133 - .../telegram/inline/inlinequeryresultvideo.py | 146 - .../telegram/inline/inlinequeryresultvoice.py | 112 - .../inline/inputcontactmessagecontent.py | 66 - .../inline/inputinvoicemessagecontent.py | 242 - .../inline/inputlocationmessagecontent.py | 88 - .../telegram/inline/inputmessagecontent.py | 34 - .../inline/inputtextmessagecontent.py | 88 - .../inline/inputvenuemessagecontent.py | 102 - telegramer/include/telegram/keyboardbutton.py | 83 - .../telegram/keyboardbuttonpolltype.py | 45 - telegramer/include/telegram/loginurl.py | 89 - telegramer/include/telegram/message.py | 3010 --------- .../telegram/messageautodeletetimerchanged.py | 56 - telegramer/include/telegram/messageentity.py | 135 - telegramer/include/telegram/messageid.py | 40 - telegramer/include/telegram/parsemode.py | 45 - .../include/telegram/passport/__init__.py | 0 .../include/telegram/passport/credentials.py | 497 -- telegramer/include/telegram/passport/data.py | 153 - .../passport/encryptedpassportelement.py | 266 - .../include/telegram/passport/passportdata.py | 121 - .../passport/passportelementerrors.py | 382 -- .../include/telegram/passport/passportfile.py | 160 - .../include/telegram/payment/__init__.py | 0 .../include/telegram/payment/invoice.py | 86 - .../include/telegram/payment/labeledprice.py | 54 - .../include/telegram/payment/orderinfo.py | 79 - .../telegram/payment/precheckoutquery.py | 140 - .../telegram/payment/shippingaddress.py | 86 - .../telegram/payment/shippingoption.py | 70 - .../include/telegram/payment/shippingquery.py | 113 - .../telegram/payment/successfulpayment.py | 107 - telegramer/include/telegram/poll.py | 295 - .../telegram/proximityalerttriggered.py | 69 - telegramer/include/telegram/py.typed | 0 .../include/telegram/replykeyboardmarkup.py | 291 - .../include/telegram/replykeyboardremove.py | 64 - telegramer/include/telegram/replymarkup.py | 33 - telegramer/include/telegram/update.py | 416 -- telegramer/include/telegram/user.py | 1243 ---- .../include/telegram/userprofilephotos.py | 79 - telegramer/include/telegram/utils/__init__.py | 0 .../include/telegram/utils/deprecate.py | 47 - telegramer/include/telegram/utils/helpers.py | 596 -- telegramer/include/telegram/utils/promise.py | 38 - telegramer/include/telegram/utils/request.py | 400 -- telegramer/include/telegram/utils/types.py | 57 - .../include/telegram/utils/webhookhandler.py | 35 - .../include/telegram/vendor/__init__.py | 0 .../telegram/vendor/ptb_urllib3/__init__.py | 0 .../vendor/ptb_urllib3/urllib3/__init__.py | 96 - .../ptb_urllib3/urllib3/_collections.py | 327 - .../vendor/ptb_urllib3/urllib3/connection.py | 369 -- .../ptb_urllib3/urllib3/connectionpool.py | 912 --- .../ptb_urllib3/urllib3/contrib/__init__.py | 0 .../ptb_urllib3/urllib3/contrib/appengine.py | 296 - .../ptb_urllib3/urllib3/contrib/ntlmpool.py | 112 - .../ptb_urllib3/urllib3/contrib/pyopenssl.py | 450 -- .../ptb_urllib3/urllib3/contrib/socks.py | 192 - .../vendor/ptb_urllib3/urllib3/exceptions.py | 246 - .../vendor/ptb_urllib3/urllib3/fields.py | 178 - .../vendor/ptb_urllib3/urllib3/filepost.py | 94 - .../ptb_urllib3/urllib3/packages/__init__.py | 5 - .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 53 - .../urllib3/packages/ordered_dict.py | 259 - .../ptb_urllib3/urllib3/packages/six.py | 868 --- .../packages/ssl_match_hostname/__init__.p | 19 - .../packages/ssl_match_hostname/_implement | 157 - .../vendor/ptb_urllib3/urllib3/poolmanager.py | 363 - .../vendor/ptb_urllib3/urllib3/request.py | 148 - .../vendor/ptb_urllib3/urllib3/response.py | 618 -- .../ptb_urllib3/urllib3/util/__init__.py | 52 - .../ptb_urllib3/urllib3/util/connection.py | 130 - .../ptb_urllib3/urllib3/util/request.py | 118 - .../ptb_urllib3/urllib3/util/response.py | 81 - .../vendor/ptb_urllib3/urllib3/util/retry.py | 389 -- .../ptb_urllib3/urllib3/util/selectors.py | 529 -- .../vendor/ptb_urllib3/urllib3/util/ssl_.py | 336 - .../ptb_urllib3/urllib3/util/timeout.py | 242 - .../vendor/ptb_urllib3/urllib3/util/url.py | 226 - .../vendor/ptb_urllib3/urllib3/util/wait.py | 40 - telegramer/include/telegram/version.py | 24 - telegramer/include/telegram/voicechat.py | 169 - telegramer/include/telegram/webhookinfo.py | 110 - telegramer/include/tornado/__init__.py | 26 - telegramer/include/tornado/_locale_data.py | 80 - telegramer/include/tornado/auth.py | 1187 ---- telegramer/include/tornado/autoreload.py | 363 - telegramer/include/tornado/concurrent.py | 263 - telegramer/include/tornado/curl_httpclient.py | 583 -- telegramer/include/tornado/escape.py | 402 -- telegramer/include/tornado/gen.py | 872 --- telegramer/include/tornado/http1connection.py | 842 --- telegramer/include/tornado/httpclient.py | 790 --- telegramer/include/tornado/httpserver.py | 398 -- telegramer/include/tornado/httputil.py | 1133 ---- telegramer/include/tornado/ioloop.py | 944 --- telegramer/include/tornado/iostream.py | 1660 ----- telegramer/include/tornado/locale.py | 581 -- telegramer/include/tornado/locks.py | 571 -- telegramer/include/tornado/log.py | 339 - telegramer/include/tornado/netutil.py | 617 -- telegramer/include/tornado/options.py | 735 --- .../include/tornado/platform/__init__.py | 0 .../include/tornado/platform/asyncio.py | 611 -- .../include/tornado/platform/caresresolver.py | 89 - .../include/tornado/platform/twisted.py | 146 - telegramer/include/tornado/process.py | 373 -- telegramer/include/tornado/py.typed | 0 telegramer/include/tornado/queues.py | 414 -- telegramer/include/tornado/routing.py | 717 -- .../include/tornado/simple_httpclient.py | 699 -- telegramer/include/tornado/speedups.c | 70 - telegramer/include/tornado/tcpclient.py | 328 - telegramer/include/tornado/tcpserver.py | 334 - telegramer/include/tornado/template.py | 1048 --- telegramer/include/tornado/testing.py | 818 --- telegramer/include/tornado/util.py | 474 -- telegramer/include/tornado/web.py | 3588 ---------- telegramer/include/tornado/websocket.py | 1666 ----- telegramer/include/tornado/wsgi.py | 199 - telegramer/include/tzlocal/__init__.py | 13 - telegramer/include/tzlocal/unix.py | 215 - telegramer/include/tzlocal/utils.py | 125 - telegramer/include/tzlocal/win32.py | 137 - telegramer/include/tzlocal/windows_tz.py | 699 -- 877 files changed, 91072 deletions(-) delete mode 100644 telegramer/include/apscheduler/__init__.py delete mode 100644 telegramer/include/apscheduler/events.py delete mode 100644 telegramer/include/apscheduler/executors/__init__.py delete mode 100644 telegramer/include/apscheduler/executors/asyncio.py delete mode 100644 telegramer/include/apscheduler/executors/base.py delete mode 100644 telegramer/include/apscheduler/executors/base_py3.py delete mode 100644 telegramer/include/apscheduler/executors/debug.py delete mode 100644 telegramer/include/apscheduler/executors/gevent.py delete mode 100644 telegramer/include/apscheduler/executors/pool.py delete mode 100644 telegramer/include/apscheduler/executors/tornado.py delete mode 100644 telegramer/include/apscheduler/executors/twisted.py delete mode 100644 telegramer/include/apscheduler/job.py delete mode 100644 telegramer/include/apscheduler/jobstores/__init__.py delete mode 100644 telegramer/include/apscheduler/jobstores/base.py delete mode 100644 telegramer/include/apscheduler/jobstores/memory.py delete mode 100644 telegramer/include/apscheduler/jobstores/mongodb.py delete mode 100644 telegramer/include/apscheduler/jobstores/redis.py delete mode 100644 telegramer/include/apscheduler/jobstores/rethinkdb.py delete mode 100644 telegramer/include/apscheduler/jobstores/sqlalchemy.py delete mode 100644 telegramer/include/apscheduler/jobstores/zookeeper.py delete mode 100644 telegramer/include/apscheduler/schedulers/__init__.py delete mode 100644 telegramer/include/apscheduler/schedulers/asyncio.py delete mode 100644 telegramer/include/apscheduler/schedulers/background.py delete mode 100644 telegramer/include/apscheduler/schedulers/base.py delete mode 100644 telegramer/include/apscheduler/schedulers/blocking.py delete mode 100644 telegramer/include/apscheduler/schedulers/gevent.py delete mode 100644 telegramer/include/apscheduler/schedulers/qt.py delete mode 100644 telegramer/include/apscheduler/schedulers/tornado.py delete mode 100644 telegramer/include/apscheduler/schedulers/twisted.py delete mode 100644 telegramer/include/apscheduler/triggers/__init__.py delete mode 100644 telegramer/include/apscheduler/triggers/base.py delete mode 100644 telegramer/include/apscheduler/triggers/combining.py delete mode 100644 telegramer/include/apscheduler/triggers/cron/__init__.py delete mode 100644 telegramer/include/apscheduler/triggers/cron/expressions.py delete mode 100644 telegramer/include/apscheduler/triggers/cron/fields.py delete mode 100644 telegramer/include/apscheduler/triggers/date.py delete mode 100644 telegramer/include/apscheduler/triggers/interval.py delete mode 100644 telegramer/include/apscheduler/util.py delete mode 100644 telegramer/include/cachetools/__init__.py delete mode 100644 telegramer/include/cachetools/func.py delete mode 100644 telegramer/include/cachetools/keys.py delete mode 100644 telegramer/include/certifi/__init__.py delete mode 100644 telegramer/include/certifi/__main__.py delete mode 100644 telegramer/include/certifi/cacert.pem delete mode 100644 telegramer/include/certifi/core.py delete mode 100644 telegramer/include/certifi/py.typed delete mode 100644 telegramer/include/pytz/__init__.py delete mode 100644 telegramer/include/pytz/exceptions.py delete mode 100644 telegramer/include/pytz/lazy.py delete mode 100644 telegramer/include/pytz/reference.py delete mode 100644 telegramer/include/pytz/tzfile.py delete mode 100644 telegramer/include/pytz/tzinfo.py delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Abidjan delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Accra delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Addis_Ababa delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Algiers delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Asmara delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Asmera delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Bamako delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Bangui delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Banjul delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Bissau delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Blantyre delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Brazzaville delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Bujumbura delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Cairo delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Casablanca delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Ceuta delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Conakry delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Dakar delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Dar_es_Salaam delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Djibouti delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Douala delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/El_Aaiun delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Freetown delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Gaborone delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Harare delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Johannesburg delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Juba delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Kampala delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Khartoum delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Kigali delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Kinshasa delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Lagos delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Libreville delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Lome delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Luanda delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Lubumbashi delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Lusaka delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Malabo delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Maputo delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Maseru delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Mbabane delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Mogadishu delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Monrovia delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Nairobi delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Ndjamena delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Niamey delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Nouakchott delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Ouagadougou delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Porto-Novo delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Sao_Tome delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Timbuktu delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Tripoli delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Tunis delete mode 100644 telegramer/include/pytz/zoneinfo/Africa/Windhoek delete mode 100644 telegramer/include/pytz/zoneinfo/America/Adak delete mode 100644 telegramer/include/pytz/zoneinfo/America/Anchorage delete mode 100644 telegramer/include/pytz/zoneinfo/America/Anguilla delete mode 100644 telegramer/include/pytz/zoneinfo/America/Antigua delete mode 100644 telegramer/include/pytz/zoneinfo/America/Araguaina delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Buenos_Aires delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Catamarca delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/ComodRivadavia delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Cordoba delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Jujuy delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/La_Rioja delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Mendoza delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Rio_Gallegos delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Salta delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/San_Juan delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/San_Luis delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Tucuman delete mode 100644 telegramer/include/pytz/zoneinfo/America/Argentina/Ushuaia delete mode 100644 telegramer/include/pytz/zoneinfo/America/Aruba delete mode 100644 telegramer/include/pytz/zoneinfo/America/Asuncion delete mode 100644 telegramer/include/pytz/zoneinfo/America/Atikokan delete mode 100644 telegramer/include/pytz/zoneinfo/America/Atka delete mode 100644 telegramer/include/pytz/zoneinfo/America/Bahia delete mode 100644 telegramer/include/pytz/zoneinfo/America/Bahia_Banderas delete mode 100644 telegramer/include/pytz/zoneinfo/America/Barbados delete mode 100644 telegramer/include/pytz/zoneinfo/America/Belem delete mode 100644 telegramer/include/pytz/zoneinfo/America/Belize delete mode 100644 telegramer/include/pytz/zoneinfo/America/Blanc-Sablon delete mode 100644 telegramer/include/pytz/zoneinfo/America/Boa_Vista delete mode 100644 telegramer/include/pytz/zoneinfo/America/Bogota delete mode 100644 telegramer/include/pytz/zoneinfo/America/Boise delete mode 100644 telegramer/include/pytz/zoneinfo/America/Buenos_Aires delete mode 100644 telegramer/include/pytz/zoneinfo/America/Cambridge_Bay delete mode 100644 telegramer/include/pytz/zoneinfo/America/Campo_Grande delete mode 100644 telegramer/include/pytz/zoneinfo/America/Cancun delete mode 100644 telegramer/include/pytz/zoneinfo/America/Caracas delete mode 100644 telegramer/include/pytz/zoneinfo/America/Catamarca delete mode 100644 telegramer/include/pytz/zoneinfo/America/Cayenne delete mode 100644 telegramer/include/pytz/zoneinfo/America/Cayman delete mode 100644 telegramer/include/pytz/zoneinfo/America/Chicago delete mode 100644 telegramer/include/pytz/zoneinfo/America/Chihuahua delete mode 100644 telegramer/include/pytz/zoneinfo/America/Coral_Harbour delete mode 100644 telegramer/include/pytz/zoneinfo/America/Cordoba delete mode 100644 telegramer/include/pytz/zoneinfo/America/Costa_Rica delete mode 100644 telegramer/include/pytz/zoneinfo/America/Creston delete mode 100644 telegramer/include/pytz/zoneinfo/America/Cuiaba delete mode 100644 telegramer/include/pytz/zoneinfo/America/Curacao delete mode 100644 telegramer/include/pytz/zoneinfo/America/Danmarkshavn delete mode 100644 telegramer/include/pytz/zoneinfo/America/Dawson delete mode 100644 telegramer/include/pytz/zoneinfo/America/Dawson_Creek delete mode 100644 telegramer/include/pytz/zoneinfo/America/Denver delete mode 100644 telegramer/include/pytz/zoneinfo/America/Detroit delete mode 100644 telegramer/include/pytz/zoneinfo/America/Dominica delete mode 100644 telegramer/include/pytz/zoneinfo/America/Edmonton delete mode 100644 telegramer/include/pytz/zoneinfo/America/Eirunepe delete mode 100644 telegramer/include/pytz/zoneinfo/America/El_Salvador delete mode 100644 telegramer/include/pytz/zoneinfo/America/Ensenada delete mode 100644 telegramer/include/pytz/zoneinfo/America/Fort_Nelson delete mode 100644 telegramer/include/pytz/zoneinfo/America/Fort_Wayne delete mode 100644 telegramer/include/pytz/zoneinfo/America/Fortaleza delete mode 100644 telegramer/include/pytz/zoneinfo/America/Glace_Bay delete mode 100644 telegramer/include/pytz/zoneinfo/America/Godthab delete mode 100644 telegramer/include/pytz/zoneinfo/America/Goose_Bay delete mode 100644 telegramer/include/pytz/zoneinfo/America/Grand_Turk delete mode 100644 telegramer/include/pytz/zoneinfo/America/Grenada delete mode 100644 telegramer/include/pytz/zoneinfo/America/Guadeloupe delete mode 100644 telegramer/include/pytz/zoneinfo/America/Guatemala delete mode 100644 telegramer/include/pytz/zoneinfo/America/Guayaquil delete mode 100644 telegramer/include/pytz/zoneinfo/America/Guyana delete mode 100644 telegramer/include/pytz/zoneinfo/America/Halifax delete mode 100644 telegramer/include/pytz/zoneinfo/America/Havana delete mode 100644 telegramer/include/pytz/zoneinfo/America/Hermosillo delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Indianapolis delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Knox delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Marengo delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Petersburg delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Tell_City delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Vevay delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Vincennes delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indiana/Winamac delete mode 100644 telegramer/include/pytz/zoneinfo/America/Indianapolis delete mode 100644 telegramer/include/pytz/zoneinfo/America/Inuvik delete mode 100644 telegramer/include/pytz/zoneinfo/America/Iqaluit delete mode 100644 telegramer/include/pytz/zoneinfo/America/Jamaica delete mode 100644 telegramer/include/pytz/zoneinfo/America/Jujuy delete mode 100644 telegramer/include/pytz/zoneinfo/America/Juneau delete mode 100644 telegramer/include/pytz/zoneinfo/America/Kentucky/Louisville delete mode 100644 telegramer/include/pytz/zoneinfo/America/Kentucky/Monticello delete mode 100644 telegramer/include/pytz/zoneinfo/America/Knox_IN delete mode 100644 telegramer/include/pytz/zoneinfo/America/Kralendijk delete mode 100644 telegramer/include/pytz/zoneinfo/America/La_Paz delete mode 100644 telegramer/include/pytz/zoneinfo/America/Lima delete mode 100644 telegramer/include/pytz/zoneinfo/America/Los_Angeles delete mode 100644 telegramer/include/pytz/zoneinfo/America/Louisville delete mode 100644 telegramer/include/pytz/zoneinfo/America/Lower_Princes delete mode 100644 telegramer/include/pytz/zoneinfo/America/Maceio delete mode 100644 telegramer/include/pytz/zoneinfo/America/Managua delete mode 100644 telegramer/include/pytz/zoneinfo/America/Manaus delete mode 100644 telegramer/include/pytz/zoneinfo/America/Marigot delete mode 100644 telegramer/include/pytz/zoneinfo/America/Martinique delete mode 100644 telegramer/include/pytz/zoneinfo/America/Matamoros delete mode 100644 telegramer/include/pytz/zoneinfo/America/Mazatlan delete mode 100644 telegramer/include/pytz/zoneinfo/America/Mendoza delete mode 100644 telegramer/include/pytz/zoneinfo/America/Menominee delete mode 100644 telegramer/include/pytz/zoneinfo/America/Merida delete mode 100644 telegramer/include/pytz/zoneinfo/America/Metlakatla delete mode 100644 telegramer/include/pytz/zoneinfo/America/Mexico_City delete mode 100644 telegramer/include/pytz/zoneinfo/America/Miquelon delete mode 100644 telegramer/include/pytz/zoneinfo/America/Moncton delete mode 100644 telegramer/include/pytz/zoneinfo/America/Monterrey delete mode 100644 telegramer/include/pytz/zoneinfo/America/Montevideo delete mode 100644 telegramer/include/pytz/zoneinfo/America/Montreal delete mode 100644 telegramer/include/pytz/zoneinfo/America/Montserrat delete mode 100644 telegramer/include/pytz/zoneinfo/America/Nassau delete mode 100644 telegramer/include/pytz/zoneinfo/America/New_York delete mode 100644 telegramer/include/pytz/zoneinfo/America/Nipigon delete mode 100644 telegramer/include/pytz/zoneinfo/America/Nome delete mode 100644 telegramer/include/pytz/zoneinfo/America/Noronha delete mode 100644 telegramer/include/pytz/zoneinfo/America/North_Dakota/Beulah delete mode 100644 telegramer/include/pytz/zoneinfo/America/North_Dakota/Center delete mode 100644 telegramer/include/pytz/zoneinfo/America/North_Dakota/New_Salem delete mode 100644 telegramer/include/pytz/zoneinfo/America/Nuuk delete mode 100644 telegramer/include/pytz/zoneinfo/America/Ojinaga delete mode 100644 telegramer/include/pytz/zoneinfo/America/Panama delete mode 100644 telegramer/include/pytz/zoneinfo/America/Pangnirtung delete mode 100644 telegramer/include/pytz/zoneinfo/America/Paramaribo delete mode 100644 telegramer/include/pytz/zoneinfo/America/Phoenix delete mode 100644 telegramer/include/pytz/zoneinfo/America/Port-au-Prince delete mode 100644 telegramer/include/pytz/zoneinfo/America/Port_of_Spain delete mode 100644 telegramer/include/pytz/zoneinfo/America/Porto_Acre delete mode 100644 telegramer/include/pytz/zoneinfo/America/Porto_Velho delete mode 100644 telegramer/include/pytz/zoneinfo/America/Puerto_Rico delete mode 100644 telegramer/include/pytz/zoneinfo/America/Punta_Arenas delete mode 100644 telegramer/include/pytz/zoneinfo/America/Rainy_River delete mode 100644 telegramer/include/pytz/zoneinfo/America/Rankin_Inlet delete mode 100644 telegramer/include/pytz/zoneinfo/America/Recife delete mode 100644 telegramer/include/pytz/zoneinfo/America/Regina delete mode 100644 telegramer/include/pytz/zoneinfo/America/Resolute delete mode 100644 telegramer/include/pytz/zoneinfo/America/Rio_Branco delete mode 100644 telegramer/include/pytz/zoneinfo/America/Rosario delete mode 100644 telegramer/include/pytz/zoneinfo/America/Santa_Isabel delete mode 100644 telegramer/include/pytz/zoneinfo/America/Santarem delete mode 100644 telegramer/include/pytz/zoneinfo/America/Santiago delete mode 100644 telegramer/include/pytz/zoneinfo/America/Santo_Domingo delete mode 100644 telegramer/include/pytz/zoneinfo/America/Sao_Paulo delete mode 100644 telegramer/include/pytz/zoneinfo/America/Scoresbysund delete mode 100644 telegramer/include/pytz/zoneinfo/America/Shiprock delete mode 100644 telegramer/include/pytz/zoneinfo/America/Sitka delete mode 100644 telegramer/include/pytz/zoneinfo/America/St_Barthelemy delete mode 100644 telegramer/include/pytz/zoneinfo/America/St_Johns delete mode 100644 telegramer/include/pytz/zoneinfo/America/St_Kitts delete mode 100644 telegramer/include/pytz/zoneinfo/America/St_Lucia delete mode 100644 telegramer/include/pytz/zoneinfo/America/St_Thomas delete mode 100644 telegramer/include/pytz/zoneinfo/America/St_Vincent delete mode 100644 telegramer/include/pytz/zoneinfo/America/Swift_Current delete mode 100644 telegramer/include/pytz/zoneinfo/America/Tegucigalpa delete mode 100644 telegramer/include/pytz/zoneinfo/America/Thule delete mode 100644 telegramer/include/pytz/zoneinfo/America/Thunder_Bay delete mode 100644 telegramer/include/pytz/zoneinfo/America/Tijuana delete mode 100644 telegramer/include/pytz/zoneinfo/America/Toronto delete mode 100644 telegramer/include/pytz/zoneinfo/America/Tortola delete mode 100644 telegramer/include/pytz/zoneinfo/America/Vancouver delete mode 100644 telegramer/include/pytz/zoneinfo/America/Virgin delete mode 100644 telegramer/include/pytz/zoneinfo/America/Whitehorse delete mode 100644 telegramer/include/pytz/zoneinfo/America/Winnipeg delete mode 100644 telegramer/include/pytz/zoneinfo/America/Yakutat delete mode 100644 telegramer/include/pytz/zoneinfo/America/Yellowknife delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Casey delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Davis delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/DumontDUrville delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Macquarie delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Mawson delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/McMurdo delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Palmer delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Rothera delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/South_Pole delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Syowa delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Troll delete mode 100644 telegramer/include/pytz/zoneinfo/Antarctica/Vostok delete mode 100644 telegramer/include/pytz/zoneinfo/Arctic/Longyearbyen delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Aden delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Almaty delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Amman delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Anadyr delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Aqtau delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Aqtobe delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ashgabat delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ashkhabad delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Atyrau delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Baghdad delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Bahrain delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Baku delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Bangkok delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Barnaul delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Beirut delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Bishkek delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Brunei delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Calcutta delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Chita delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Choibalsan delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Chongqing delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Chungking delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Colombo delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Dacca delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Damascus delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Dhaka delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Dili delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Dubai delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Dushanbe delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Famagusta delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Gaza delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Harbin delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Hebron delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ho_Chi_Minh delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Hong_Kong delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Hovd delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Irkutsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Istanbul delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Jakarta delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Jayapura delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Jerusalem delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kabul delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kamchatka delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Karachi delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kashgar delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kathmandu delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Katmandu delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Khandyga delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kolkata delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Krasnoyarsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kuala_Lumpur delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kuching delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Kuwait delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Macao delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Macau delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Magadan delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Makassar delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Manila delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Muscat delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Nicosia delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Novokuznetsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Novosibirsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Omsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Oral delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Phnom_Penh delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Pontianak delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Pyongyang delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Qatar delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Qostanay delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Qyzylorda delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Rangoon delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Riyadh delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Saigon delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Sakhalin delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Samarkand delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Seoul delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Shanghai delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Singapore delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Srednekolymsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Taipei delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Tashkent delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Tbilisi delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Tehran delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Tel_Aviv delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Thimbu delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Thimphu delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Tokyo delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Tomsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ujung_Pandang delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ulaanbaatar delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ulan_Bator delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Urumqi delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Ust-Nera delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Vientiane delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Vladivostok delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Yakutsk delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Yangon delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Yekaterinburg delete mode 100644 telegramer/include/pytz/zoneinfo/Asia/Yerevan delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Azores delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Bermuda delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Canary delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Cape_Verde delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Faeroe delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Faroe delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Jan_Mayen delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Madeira delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Reykjavik delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/South_Georgia delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/St_Helena delete mode 100644 telegramer/include/pytz/zoneinfo/Atlantic/Stanley delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/ACT delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Adelaide delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Brisbane delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Broken_Hill delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Canberra delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Currie delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Darwin delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Eucla delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Hobart delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/LHI delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Lindeman delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Lord_Howe delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Melbourne delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/NSW delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/North delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Perth delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Queensland delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/South delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Sydney delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Tasmania delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Victoria delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/West delete mode 100644 telegramer/include/pytz/zoneinfo/Australia/Yancowinna delete mode 100644 telegramer/include/pytz/zoneinfo/Brazil/Acre delete mode 100644 telegramer/include/pytz/zoneinfo/Brazil/DeNoronha delete mode 100644 telegramer/include/pytz/zoneinfo/Brazil/East delete mode 100644 telegramer/include/pytz/zoneinfo/Brazil/West delete mode 100644 telegramer/include/pytz/zoneinfo/CET delete mode 100644 telegramer/include/pytz/zoneinfo/CST6CDT delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Atlantic delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Central delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Eastern delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Mountain delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Newfoundland delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Pacific delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Saskatchewan delete mode 100644 telegramer/include/pytz/zoneinfo/Canada/Yukon delete mode 100644 telegramer/include/pytz/zoneinfo/Chile/Continental delete mode 100644 telegramer/include/pytz/zoneinfo/Chile/EasterIsland delete mode 100644 telegramer/include/pytz/zoneinfo/Cuba delete mode 100644 telegramer/include/pytz/zoneinfo/EET delete mode 100644 telegramer/include/pytz/zoneinfo/EST delete mode 100644 telegramer/include/pytz/zoneinfo/EST5EDT delete mode 100644 telegramer/include/pytz/zoneinfo/Egypt delete mode 100644 telegramer/include/pytz/zoneinfo/Eire delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+0 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+1 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+10 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+11 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+12 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+2 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+3 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+4 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+5 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+6 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+7 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+8 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT+9 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-0 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-1 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-10 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-11 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-12 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-13 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-14 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-2 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-3 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-4 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-5 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-6 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-7 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-8 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT-9 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/GMT0 delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/Greenwich delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/UCT delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/UTC delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/Universal delete mode 100644 telegramer/include/pytz/zoneinfo/Etc/Zulu delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Amsterdam delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Andorra delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Astrakhan delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Athens delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Belfast delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Belgrade delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Berlin delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Bratislava delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Brussels delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Bucharest delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Budapest delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Busingen delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Chisinau delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Copenhagen delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Dublin delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Gibraltar delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Guernsey delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Helsinki delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Isle_of_Man delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Istanbul delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Jersey delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Kaliningrad delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Kiev delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Kirov delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Lisbon delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Ljubljana delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/London delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Luxembourg delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Madrid delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Malta delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Mariehamn delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Minsk delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Monaco delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Moscow delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Nicosia delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Oslo delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Paris delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Podgorica delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Prague delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Riga delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Rome delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Samara delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/San_Marino delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Sarajevo delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Saratov delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Simferopol delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Skopje delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Sofia delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Stockholm delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Tallinn delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Tirane delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Tiraspol delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Ulyanovsk delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Uzhgorod delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Vaduz delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Vatican delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Vienna delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Vilnius delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Volgograd delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Warsaw delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Zagreb delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Zaporozhye delete mode 100644 telegramer/include/pytz/zoneinfo/Europe/Zurich delete mode 100644 telegramer/include/pytz/zoneinfo/Factory delete mode 100644 telegramer/include/pytz/zoneinfo/GB delete mode 100644 telegramer/include/pytz/zoneinfo/GB-Eire delete mode 100644 telegramer/include/pytz/zoneinfo/GMT delete mode 100644 telegramer/include/pytz/zoneinfo/GMT+0 delete mode 100644 telegramer/include/pytz/zoneinfo/GMT-0 delete mode 100644 telegramer/include/pytz/zoneinfo/GMT0 delete mode 100644 telegramer/include/pytz/zoneinfo/Greenwich delete mode 100644 telegramer/include/pytz/zoneinfo/HST delete mode 100644 telegramer/include/pytz/zoneinfo/Hongkong delete mode 100644 telegramer/include/pytz/zoneinfo/Iceland delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Antananarivo delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Chagos delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Christmas delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Cocos delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Comoro delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Kerguelen delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Mahe delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Maldives delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Mauritius delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Mayotte delete mode 100644 telegramer/include/pytz/zoneinfo/Indian/Reunion delete mode 100644 telegramer/include/pytz/zoneinfo/Iran delete mode 100644 telegramer/include/pytz/zoneinfo/Israel delete mode 100644 telegramer/include/pytz/zoneinfo/Jamaica delete mode 100644 telegramer/include/pytz/zoneinfo/Japan delete mode 100644 telegramer/include/pytz/zoneinfo/Kwajalein delete mode 100644 telegramer/include/pytz/zoneinfo/Libya delete mode 100644 telegramer/include/pytz/zoneinfo/MET delete mode 100644 telegramer/include/pytz/zoneinfo/MST delete mode 100644 telegramer/include/pytz/zoneinfo/MST7MDT delete mode 100644 telegramer/include/pytz/zoneinfo/Mexico/BajaNorte delete mode 100644 telegramer/include/pytz/zoneinfo/Mexico/BajaSur delete mode 100644 telegramer/include/pytz/zoneinfo/Mexico/General delete mode 100644 telegramer/include/pytz/zoneinfo/NZ delete mode 100644 telegramer/include/pytz/zoneinfo/NZ-CHAT delete mode 100644 telegramer/include/pytz/zoneinfo/Navajo delete mode 100644 telegramer/include/pytz/zoneinfo/PRC delete mode 100644 telegramer/include/pytz/zoneinfo/PST8PDT delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Apia delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Auckland delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Bougainville delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Chatham delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Chuuk delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Easter delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Efate delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Enderbury delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Fakaofo delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Fiji delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Funafuti delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Galapagos delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Gambier delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Guadalcanal delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Guam delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Honolulu delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Johnston delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Kanton delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Kiritimati delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Kosrae delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Kwajalein delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Majuro delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Marquesas delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Midway delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Nauru delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Niue delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Norfolk delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Noumea delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Pago_Pago delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Palau delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Pitcairn delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Pohnpei delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Ponape delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Port_Moresby delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Rarotonga delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Saipan delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Samoa delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Tahiti delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Tarawa delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Tongatapu delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Truk delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Wake delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Wallis delete mode 100644 telegramer/include/pytz/zoneinfo/Pacific/Yap delete mode 100644 telegramer/include/pytz/zoneinfo/Poland delete mode 100644 telegramer/include/pytz/zoneinfo/Portugal delete mode 100644 telegramer/include/pytz/zoneinfo/ROC delete mode 100644 telegramer/include/pytz/zoneinfo/ROK delete mode 100644 telegramer/include/pytz/zoneinfo/Singapore delete mode 100644 telegramer/include/pytz/zoneinfo/Turkey delete mode 100644 telegramer/include/pytz/zoneinfo/UCT delete mode 100644 telegramer/include/pytz/zoneinfo/US/Alaska delete mode 100644 telegramer/include/pytz/zoneinfo/US/Aleutian delete mode 100644 telegramer/include/pytz/zoneinfo/US/Arizona delete mode 100644 telegramer/include/pytz/zoneinfo/US/Central delete mode 100644 telegramer/include/pytz/zoneinfo/US/East-Indiana delete mode 100644 telegramer/include/pytz/zoneinfo/US/Eastern delete mode 100644 telegramer/include/pytz/zoneinfo/US/Hawaii delete mode 100644 telegramer/include/pytz/zoneinfo/US/Indiana-Starke delete mode 100644 telegramer/include/pytz/zoneinfo/US/Michigan delete mode 100644 telegramer/include/pytz/zoneinfo/US/Mountain delete mode 100644 telegramer/include/pytz/zoneinfo/US/Pacific delete mode 100644 telegramer/include/pytz/zoneinfo/US/Samoa delete mode 100644 telegramer/include/pytz/zoneinfo/UTC delete mode 100644 telegramer/include/pytz/zoneinfo/Universal delete mode 100644 telegramer/include/pytz/zoneinfo/W-SU delete mode 100644 telegramer/include/pytz/zoneinfo/WET delete mode 100644 telegramer/include/pytz/zoneinfo/Zulu delete mode 100644 telegramer/include/pytz/zoneinfo/iso3166.tab delete mode 100644 telegramer/include/pytz/zoneinfo/leapseconds delete mode 100644 telegramer/include/pytz/zoneinfo/tzdata.zi delete mode 100644 telegramer/include/pytz/zoneinfo/zone.tab delete mode 100644 telegramer/include/pytz/zoneinfo/zone1970.tab delete mode 100644 telegramer/include/telegram/__init__.py delete mode 100644 telegramer/include/telegram/__main__.py delete mode 100644 telegramer/include/telegram/base.py delete mode 100644 telegramer/include/telegram/bot.py delete mode 100644 telegramer/include/telegram/botcommand.py delete mode 100644 telegramer/include/telegram/botcommandscope.py delete mode 100644 telegramer/include/telegram/callbackquery.py delete mode 100644 telegramer/include/telegram/chat.py delete mode 100644 telegramer/include/telegram/chataction.py delete mode 100644 telegramer/include/telegram/chatinvitelink.py delete mode 100644 telegramer/include/telegram/chatjoinrequest.py delete mode 100644 telegramer/include/telegram/chatlocation.py delete mode 100644 telegramer/include/telegram/chatmember.py delete mode 100644 telegramer/include/telegram/chatmemberupdated.py delete mode 100644 telegramer/include/telegram/chatpermissions.py delete mode 100644 telegramer/include/telegram/choseninlineresult.py delete mode 100644 telegramer/include/telegram/constants.py delete mode 100644 telegramer/include/telegram/dice.py delete mode 100644 telegramer/include/telegram/error.py delete mode 100644 telegramer/include/telegram/ext/__init__.py delete mode 100644 telegramer/include/telegram/ext/basepersistence.py delete mode 100644 telegramer/include/telegram/ext/callbackcontext.py delete mode 100644 telegramer/include/telegram/ext/callbackdatacache.py delete mode 100644 telegramer/include/telegram/ext/callbackqueryhandler.py delete mode 100644 telegramer/include/telegram/ext/chatjoinrequesthandler.py delete mode 100644 telegramer/include/telegram/ext/chatmemberhandler.py delete mode 100644 telegramer/include/telegram/ext/choseninlineresulthandler.py delete mode 100644 telegramer/include/telegram/ext/commandhandler.py delete mode 100644 telegramer/include/telegram/ext/contexttypes.py delete mode 100644 telegramer/include/telegram/ext/conversationhandler.py delete mode 100644 telegramer/include/telegram/ext/defaults.py delete mode 100644 telegramer/include/telegram/ext/dictpersistence.py delete mode 100644 telegramer/include/telegram/ext/dispatcher.py delete mode 100644 telegramer/include/telegram/ext/extbot.py delete mode 100644 telegramer/include/telegram/ext/filters.py delete mode 100644 telegramer/include/telegram/ext/handler.py delete mode 100644 telegramer/include/telegram/ext/inlinequeryhandler.py delete mode 100644 telegramer/include/telegram/ext/jobqueue.py delete mode 100644 telegramer/include/telegram/ext/messagehandler.py delete mode 100644 telegramer/include/telegram/ext/messagequeue.py delete mode 100644 telegramer/include/telegram/ext/picklepersistence.py delete mode 100644 telegramer/include/telegram/ext/pollanswerhandler.py delete mode 100644 telegramer/include/telegram/ext/pollhandler.py delete mode 100644 telegramer/include/telegram/ext/precheckoutqueryhandler.py delete mode 100644 telegramer/include/telegram/ext/regexhandler.py delete mode 100644 telegramer/include/telegram/ext/shippingqueryhandler.py delete mode 100644 telegramer/include/telegram/ext/stringcommandhandler.py delete mode 100644 telegramer/include/telegram/ext/stringregexhandler.py delete mode 100644 telegramer/include/telegram/ext/typehandler.py delete mode 100644 telegramer/include/telegram/ext/updater.py delete mode 100644 telegramer/include/telegram/ext/utils/__init__.py delete mode 100644 telegramer/include/telegram/ext/utils/promise.py delete mode 100644 telegramer/include/telegram/ext/utils/types.py delete mode 100644 telegramer/include/telegram/ext/utils/webhookhandler.py delete mode 100644 telegramer/include/telegram/files/__init__.py delete mode 100644 telegramer/include/telegram/files/animation.py delete mode 100644 telegramer/include/telegram/files/audio.py delete mode 100644 telegramer/include/telegram/files/chatphoto.py delete mode 100644 telegramer/include/telegram/files/contact.py delete mode 100644 telegramer/include/telegram/files/document.py delete mode 100644 telegramer/include/telegram/files/file.py delete mode 100644 telegramer/include/telegram/files/inputfile.py delete mode 100644 telegramer/include/telegram/files/inputmedia.py delete mode 100644 telegramer/include/telegram/files/location.py delete mode 100644 telegramer/include/telegram/files/photosize.py delete mode 100644 telegramer/include/telegram/files/sticker.py delete mode 100644 telegramer/include/telegram/files/venue.py delete mode 100644 telegramer/include/telegram/files/video.py delete mode 100644 telegramer/include/telegram/files/videonote.py delete mode 100644 telegramer/include/telegram/files/voice.py delete mode 100644 telegramer/include/telegram/forcereply.py delete mode 100644 telegramer/include/telegram/games/__init__.py delete mode 100644 telegramer/include/telegram/games/callbackgame.py delete mode 100644 telegramer/include/telegram/games/game.py delete mode 100644 telegramer/include/telegram/games/gamehighscore.py delete mode 100644 telegramer/include/telegram/inline/__init__.py delete mode 100644 telegramer/include/telegram/inline/inlinekeyboardbutton.py delete mode 100644 telegramer/include/telegram/inline/inlinekeyboardmarkup.py delete mode 100644 telegramer/include/telegram/inline/inlinequery.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresult.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultarticle.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultaudio.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedaudio.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcacheddocument.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedgif.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedmpeg4gif.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedphoto.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedsticker.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedvideo.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcachedvoice.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultcontact.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultdocument.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultgame.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultgif.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultlocation.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultmpeg4gif.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultphoto.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultvenue.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultvideo.py delete mode 100644 telegramer/include/telegram/inline/inlinequeryresultvoice.py delete mode 100644 telegramer/include/telegram/inline/inputcontactmessagecontent.py delete mode 100644 telegramer/include/telegram/inline/inputinvoicemessagecontent.py delete mode 100644 telegramer/include/telegram/inline/inputlocationmessagecontent.py delete mode 100644 telegramer/include/telegram/inline/inputmessagecontent.py delete mode 100644 telegramer/include/telegram/inline/inputtextmessagecontent.py delete mode 100644 telegramer/include/telegram/inline/inputvenuemessagecontent.py delete mode 100644 telegramer/include/telegram/keyboardbutton.py delete mode 100644 telegramer/include/telegram/keyboardbuttonpolltype.py delete mode 100644 telegramer/include/telegram/loginurl.py delete mode 100644 telegramer/include/telegram/message.py delete mode 100644 telegramer/include/telegram/messageautodeletetimerchanged.py delete mode 100644 telegramer/include/telegram/messageentity.py delete mode 100644 telegramer/include/telegram/messageid.py delete mode 100644 telegramer/include/telegram/parsemode.py delete mode 100644 telegramer/include/telegram/passport/__init__.py delete mode 100644 telegramer/include/telegram/passport/credentials.py delete mode 100644 telegramer/include/telegram/passport/data.py delete mode 100644 telegramer/include/telegram/passport/encryptedpassportelement.py delete mode 100644 telegramer/include/telegram/passport/passportdata.py delete mode 100644 telegramer/include/telegram/passport/passportelementerrors.py delete mode 100644 telegramer/include/telegram/passport/passportfile.py delete mode 100644 telegramer/include/telegram/payment/__init__.py delete mode 100644 telegramer/include/telegram/payment/invoice.py delete mode 100644 telegramer/include/telegram/payment/labeledprice.py delete mode 100644 telegramer/include/telegram/payment/orderinfo.py delete mode 100644 telegramer/include/telegram/payment/precheckoutquery.py delete mode 100644 telegramer/include/telegram/payment/shippingaddress.py delete mode 100644 telegramer/include/telegram/payment/shippingoption.py delete mode 100644 telegramer/include/telegram/payment/shippingquery.py delete mode 100644 telegramer/include/telegram/payment/successfulpayment.py delete mode 100644 telegramer/include/telegram/poll.py delete mode 100644 telegramer/include/telegram/proximityalerttriggered.py delete mode 100644 telegramer/include/telegram/py.typed delete mode 100644 telegramer/include/telegram/replykeyboardmarkup.py delete mode 100644 telegramer/include/telegram/replykeyboardremove.py delete mode 100644 telegramer/include/telegram/replymarkup.py delete mode 100644 telegramer/include/telegram/update.py delete mode 100644 telegramer/include/telegram/user.py delete mode 100644 telegramer/include/telegram/userprofilephotos.py delete mode 100644 telegramer/include/telegram/utils/__init__.py delete mode 100644 telegramer/include/telegram/utils/deprecate.py delete mode 100644 telegramer/include/telegram/utils/helpers.py delete mode 100644 telegramer/include/telegram/utils/promise.py delete mode 100644 telegramer/include/telegram/utils/request.py delete mode 100644 telegramer/include/telegram/utils/types.py delete mode 100644 telegramer/include/telegram/utils/webhookhandler.py delete mode 100644 telegramer/include/telegram/vendor/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/_collections.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connection.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connectionpool.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/appengine.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/ntlmpool.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/pyopenssl.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/socks.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/exceptions.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/fields.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/filepost.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/makefile.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ordered_dict.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/six.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/__init__.p delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/_implement delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/poolmanager.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/request.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/response.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/__init__.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/connection.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/request.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/response.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/retry.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/selectors.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/ssl_.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/timeout.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/url.py delete mode 100644 telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/wait.py delete mode 100644 telegramer/include/telegram/version.py delete mode 100644 telegramer/include/telegram/voicechat.py delete mode 100644 telegramer/include/telegram/webhookinfo.py delete mode 100644 telegramer/include/tornado/__init__.py delete mode 100644 telegramer/include/tornado/_locale_data.py delete mode 100644 telegramer/include/tornado/auth.py delete mode 100644 telegramer/include/tornado/autoreload.py delete mode 100644 telegramer/include/tornado/concurrent.py delete mode 100644 telegramer/include/tornado/curl_httpclient.py delete mode 100644 telegramer/include/tornado/escape.py delete mode 100644 telegramer/include/tornado/gen.py delete mode 100644 telegramer/include/tornado/http1connection.py delete mode 100644 telegramer/include/tornado/httpclient.py delete mode 100644 telegramer/include/tornado/httpserver.py delete mode 100644 telegramer/include/tornado/httputil.py delete mode 100644 telegramer/include/tornado/ioloop.py delete mode 100644 telegramer/include/tornado/iostream.py delete mode 100644 telegramer/include/tornado/locale.py delete mode 100644 telegramer/include/tornado/locks.py delete mode 100644 telegramer/include/tornado/log.py delete mode 100644 telegramer/include/tornado/netutil.py delete mode 100644 telegramer/include/tornado/options.py delete mode 100644 telegramer/include/tornado/platform/__init__.py delete mode 100644 telegramer/include/tornado/platform/asyncio.py delete mode 100644 telegramer/include/tornado/platform/caresresolver.py delete mode 100644 telegramer/include/tornado/platform/twisted.py delete mode 100644 telegramer/include/tornado/process.py delete mode 100644 telegramer/include/tornado/py.typed delete mode 100644 telegramer/include/tornado/queues.py delete mode 100644 telegramer/include/tornado/routing.py delete mode 100644 telegramer/include/tornado/simple_httpclient.py delete mode 100644 telegramer/include/tornado/speedups.c delete mode 100644 telegramer/include/tornado/tcpclient.py delete mode 100644 telegramer/include/tornado/tcpserver.py delete mode 100644 telegramer/include/tornado/template.py delete mode 100644 telegramer/include/tornado/testing.py delete mode 100644 telegramer/include/tornado/util.py delete mode 100644 telegramer/include/tornado/web.py delete mode 100644 telegramer/include/tornado/websocket.py delete mode 100644 telegramer/include/tornado/wsgi.py delete mode 100644 telegramer/include/tzlocal/__init__.py delete mode 100644 telegramer/include/tzlocal/unix.py delete mode 100644 telegramer/include/tzlocal/utils.py delete mode 100644 telegramer/include/tzlocal/win32.py delete mode 100644 telegramer/include/tzlocal/windows_tz.py diff --git a/telegramer/include/apscheduler/__init__.py b/telegramer/include/apscheduler/__init__.py deleted file mode 100644 index 968169a..0000000 --- a/telegramer/include/apscheduler/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from pkg_resources import get_distribution, DistributionNotFound - -try: - release = get_distribution('APScheduler').version.split('-')[0] -except DistributionNotFound: - release = '3.5.0' - -version_info = tuple(int(x) if x.isdigit() else x for x in release.split('.')) -version = __version__ = '.'.join(str(x) for x in version_info[:3]) -del get_distribution, DistributionNotFound diff --git a/telegramer/include/apscheduler/events.py b/telegramer/include/apscheduler/events.py deleted file mode 100644 index 016da03..0000000 --- a/telegramer/include/apscheduler/events.py +++ /dev/null @@ -1,94 +0,0 @@ -__all__ = ('EVENT_SCHEDULER_STARTED', 'EVENT_SCHEDULER_SHUTDOWN', 'EVENT_SCHEDULER_PAUSED', - 'EVENT_SCHEDULER_RESUMED', 'EVENT_EXECUTOR_ADDED', 'EVENT_EXECUTOR_REMOVED', - 'EVENT_JOBSTORE_ADDED', 'EVENT_JOBSTORE_REMOVED', 'EVENT_ALL_JOBS_REMOVED', - 'EVENT_JOB_ADDED', 'EVENT_JOB_REMOVED', 'EVENT_JOB_MODIFIED', 'EVENT_JOB_EXECUTED', - 'EVENT_JOB_ERROR', 'EVENT_JOB_MISSED', 'EVENT_JOB_SUBMITTED', 'EVENT_JOB_MAX_INSTANCES', - 'SchedulerEvent', 'JobEvent', 'JobExecutionEvent', 'JobSubmissionEvent') - - -EVENT_SCHEDULER_STARTED = EVENT_SCHEDULER_START = 2 ** 0 -EVENT_SCHEDULER_SHUTDOWN = 2 ** 1 -EVENT_SCHEDULER_PAUSED = 2 ** 2 -EVENT_SCHEDULER_RESUMED = 2 ** 3 -EVENT_EXECUTOR_ADDED = 2 ** 4 -EVENT_EXECUTOR_REMOVED = 2 ** 5 -EVENT_JOBSTORE_ADDED = 2 ** 6 -EVENT_JOBSTORE_REMOVED = 2 ** 7 -EVENT_ALL_JOBS_REMOVED = 2 ** 8 -EVENT_JOB_ADDED = 2 ** 9 -EVENT_JOB_REMOVED = 2 ** 10 -EVENT_JOB_MODIFIED = 2 ** 11 -EVENT_JOB_EXECUTED = 2 ** 12 -EVENT_JOB_ERROR = 2 ** 13 -EVENT_JOB_MISSED = 2 ** 14 -EVENT_JOB_SUBMITTED = 2 ** 15 -EVENT_JOB_MAX_INSTANCES = 2 ** 16 -EVENT_ALL = (EVENT_SCHEDULER_STARTED | EVENT_SCHEDULER_SHUTDOWN | EVENT_SCHEDULER_PAUSED | - EVENT_SCHEDULER_RESUMED | EVENT_EXECUTOR_ADDED | EVENT_EXECUTOR_REMOVED | - EVENT_JOBSTORE_ADDED | EVENT_JOBSTORE_REMOVED | EVENT_ALL_JOBS_REMOVED | - EVENT_JOB_ADDED | EVENT_JOB_REMOVED | EVENT_JOB_MODIFIED | EVENT_JOB_EXECUTED | - EVENT_JOB_ERROR | EVENT_JOB_MISSED | EVENT_JOB_SUBMITTED | EVENT_JOB_MAX_INSTANCES) - - -class SchedulerEvent(object): - """ - An event that concerns the scheduler itself. - - :ivar code: the type code of this event - :ivar alias: alias of the job store or executor that was added or removed (if applicable) - """ - - def __init__(self, code, alias=None): - super(SchedulerEvent, self).__init__() - self.code = code - self.alias = alias - - def __repr__(self): - return '<%s (code=%d)>' % (self.__class__.__name__, self.code) - - -class JobEvent(SchedulerEvent): - """ - An event that concerns a job. - - :ivar code: the type code of this event - :ivar job_id: identifier of the job in question - :ivar jobstore: alias of the job store containing the job in question - """ - - def __init__(self, code, job_id, jobstore): - super(JobEvent, self).__init__(code) - self.code = code - self.job_id = job_id - self.jobstore = jobstore - - -class JobSubmissionEvent(JobEvent): - """ - An event that concerns the submission of a job to its executor. - - :ivar scheduled_run_times: a list of datetimes when the job was intended to run - """ - - def __init__(self, code, job_id, jobstore, scheduled_run_times): - super(JobSubmissionEvent, self).__init__(code, job_id, jobstore) - self.scheduled_run_times = scheduled_run_times - - -class JobExecutionEvent(JobEvent): - """ - An event that concerns the running of a job within its executor. - - :ivar scheduled_run_time: the time when the job was scheduled to be run - :ivar retval: the return value of the successfully executed job - :ivar exception: the exception raised by the job - :ivar traceback: a formatted traceback for the exception - """ - - def __init__(self, code, job_id, jobstore, scheduled_run_time, retval=None, exception=None, - traceback=None): - super(JobExecutionEvent, self).__init__(code, job_id, jobstore) - self.scheduled_run_time = scheduled_run_time - self.retval = retval - self.exception = exception - self.traceback = traceback diff --git a/telegramer/include/apscheduler/executors/__init__.py b/telegramer/include/apscheduler/executors/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/apscheduler/executors/asyncio.py b/telegramer/include/apscheduler/executors/asyncio.py deleted file mode 100644 index 06fc7f9..0000000 --- a/telegramer/include/apscheduler/executors/asyncio.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import absolute_import - -import sys - -from apscheduler.executors.base import BaseExecutor, run_job -from apscheduler.util import iscoroutinefunction_partial - -try: - from apscheduler.executors.base_py3 import run_coroutine_job -except ImportError: - run_coroutine_job = None - - -class AsyncIOExecutor(BaseExecutor): - """ - Runs jobs in the default executor of the event loop. - - If the job function is a native coroutine function, it is scheduled to be run directly in the - event loop as soon as possible. All other functions are run in the event loop's default - executor which is usually a thread pool. - - Plugin alias: ``asyncio`` - """ - - def start(self, scheduler, alias): - super(AsyncIOExecutor, self).start(scheduler, alias) - self._eventloop = scheduler._eventloop - self._pending_futures = set() - - def shutdown(self, wait=True): - # There is no way to honor wait=True without converting this method into a coroutine method - for f in self._pending_futures: - if not f.done(): - f.cancel() - - self._pending_futures.clear() - - def _do_submit_job(self, job, run_times): - def callback(f): - self._pending_futures.discard(f) - try: - events = f.result() - except BaseException: - self._run_job_error(job.id, *sys.exc_info()[1:]) - else: - self._run_job_success(job.id, events) - - if iscoroutinefunction_partial(job.func): - if run_coroutine_job is not None: - coro = run_coroutine_job(job, job._jobstore_alias, run_times, self._logger.name) - f = self._eventloop.create_task(coro) - else: - raise Exception('Executing coroutine based jobs is not supported with Trollius') - else: - f = self._eventloop.run_in_executor(None, run_job, job, job._jobstore_alias, run_times, - self._logger.name) - - f.add_done_callback(callback) - self._pending_futures.add(f) diff --git a/telegramer/include/apscheduler/executors/base.py b/telegramer/include/apscheduler/executors/base.py deleted file mode 100644 index 4c09fc1..0000000 --- a/telegramer/include/apscheduler/executors/base.py +++ /dev/null @@ -1,146 +0,0 @@ -from abc import ABCMeta, abstractmethod -from collections import defaultdict -from datetime import datetime, timedelta -from traceback import format_tb -import logging -import sys - -from pytz import utc -import six - -from apscheduler.events import ( - JobExecutionEvent, EVENT_JOB_MISSED, EVENT_JOB_ERROR, EVENT_JOB_EXECUTED) - - -class MaxInstancesReachedError(Exception): - def __init__(self, job): - super(MaxInstancesReachedError, self).__init__( - 'Job "%s" has already reached its maximum number of instances (%d)' % - (job.id, job.max_instances)) - - -class BaseExecutor(six.with_metaclass(ABCMeta, object)): - """Abstract base class that defines the interface that every executor must implement.""" - - _scheduler = None - _lock = None - _logger = logging.getLogger('apscheduler.executors') - - def __init__(self): - super(BaseExecutor, self).__init__() - self._instances = defaultdict(lambda: 0) - - def start(self, scheduler, alias): - """ - Called by the scheduler when the scheduler is being started or when the executor is being - added to an already running scheduler. - - :param apscheduler.schedulers.base.BaseScheduler scheduler: the scheduler that is starting - this executor - :param str|unicode alias: alias of this executor as it was assigned to the scheduler - - """ - self._scheduler = scheduler - self._lock = scheduler._create_lock() - self._logger = logging.getLogger('apscheduler.executors.%s' % alias) - - def shutdown(self, wait=True): - """ - Shuts down this executor. - - :param bool wait: ``True`` to wait until all submitted jobs - have been executed - """ - - def submit_job(self, job, run_times): - """ - Submits job for execution. - - :param Job job: job to execute - :param list[datetime] run_times: list of datetimes specifying - when the job should have been run - :raises MaxInstancesReachedError: if the maximum number of - allowed instances for this job has been reached - - """ - assert self._lock is not None, 'This executor has not been started yet' - with self._lock: - if self._instances[job.id] >= job.max_instances: - raise MaxInstancesReachedError(job) - - self._do_submit_job(job, run_times) - self._instances[job.id] += 1 - - @abstractmethod - def _do_submit_job(self, job, run_times): - """Performs the actual task of scheduling `run_job` to be called.""" - - def _run_job_success(self, job_id, events): - """ - Called by the executor with the list of generated events when :func:`run_job` has been - successfully called. - - """ - with self._lock: - self._instances[job_id] -= 1 - if self._instances[job_id] == 0: - del self._instances[job_id] - - for event in events: - self._scheduler._dispatch_event(event) - - def _run_job_error(self, job_id, exc, traceback=None): - """Called by the executor with the exception if there is an error calling `run_job`.""" - with self._lock: - self._instances[job_id] -= 1 - if self._instances[job_id] == 0: - del self._instances[job_id] - - exc_info = (exc.__class__, exc, traceback) - self._logger.error('Error running job %s', job_id, exc_info=exc_info) - - -def run_job(job, jobstore_alias, run_times, logger_name): - """ - Called by executors to run the job. Returns a list of scheduler events to be dispatched by the - scheduler. - - """ - events = [] - logger = logging.getLogger(logger_name) - for run_time in run_times: - # See if the job missed its run time window, and handle - # possible misfires accordingly - if job.misfire_grace_time is not None: - difference = datetime.now(utc) - run_time - grace_time = timedelta(seconds=job.misfire_grace_time) - if difference > grace_time: - events.append(JobExecutionEvent(EVENT_JOB_MISSED, job.id, jobstore_alias, - run_time)) - logger.warning('Run time of job "%s" was missed by %s', job, difference) - continue - - logger.info('Running job "%s" (scheduled at %s)', job, run_time) - try: - retval = job.func(*job.args, **job.kwargs) - except BaseException: - exc, tb = sys.exc_info()[1:] - formatted_tb = ''.join(format_tb(tb)) - events.append(JobExecutionEvent(EVENT_JOB_ERROR, job.id, jobstore_alias, run_time, - exception=exc, traceback=formatted_tb)) - logger.exception('Job "%s" raised an exception', job) - - # This is to prevent cyclic references that would lead to memory leaks - if six.PY2: - sys.exc_clear() - del tb - else: - import traceback - traceback.clear_frames(tb) - del tb - else: - events.append(JobExecutionEvent(EVENT_JOB_EXECUTED, job.id, jobstore_alias, run_time, - retval=retval)) - logger.info('Job "%s" executed successfully', job) - - return events diff --git a/telegramer/include/apscheduler/executors/base_py3.py b/telegramer/include/apscheduler/executors/base_py3.py deleted file mode 100644 index 7111d2a..0000000 --- a/telegramer/include/apscheduler/executors/base_py3.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging -import sys -import traceback -from datetime import datetime, timedelta -from traceback import format_tb - -from pytz import utc - -from apscheduler.events import ( - JobExecutionEvent, EVENT_JOB_MISSED, EVENT_JOB_ERROR, EVENT_JOB_EXECUTED) - - -async def run_coroutine_job(job, jobstore_alias, run_times, logger_name): - """Coroutine version of run_job().""" - events = [] - logger = logging.getLogger(logger_name) - for run_time in run_times: - # See if the job missed its run time window, and handle possible misfires accordingly - if job.misfire_grace_time is not None: - difference = datetime.now(utc) - run_time - grace_time = timedelta(seconds=job.misfire_grace_time) - if difference > grace_time: - events.append(JobExecutionEvent(EVENT_JOB_MISSED, job.id, jobstore_alias, - run_time)) - logger.warning('Run time of job "%s" was missed by %s', job, difference) - continue - - logger.info('Running job "%s" (scheduled at %s)', job, run_time) - try: - retval = await job.func(*job.args, **job.kwargs) - except BaseException: - exc, tb = sys.exc_info()[1:] - formatted_tb = ''.join(format_tb(tb)) - events.append(JobExecutionEvent(EVENT_JOB_ERROR, job.id, jobstore_alias, run_time, - exception=exc, traceback=formatted_tb)) - logger.exception('Job "%s" raised an exception', job) - traceback.clear_frames(tb) - else: - events.append(JobExecutionEvent(EVENT_JOB_EXECUTED, job.id, jobstore_alias, run_time, - retval=retval)) - logger.info('Job "%s" executed successfully', job) - - return events diff --git a/telegramer/include/apscheduler/executors/debug.py b/telegramer/include/apscheduler/executors/debug.py deleted file mode 100644 index ac739ae..0000000 --- a/telegramer/include/apscheduler/executors/debug.py +++ /dev/null @@ -1,20 +0,0 @@ -import sys - -from apscheduler.executors.base import BaseExecutor, run_job - - -class DebugExecutor(BaseExecutor): - """ - A special executor that executes the target callable directly instead of deferring it to a - thread or process. - - Plugin alias: ``debug`` - """ - - def _do_submit_job(self, job, run_times): - try: - events = run_job(job, job._jobstore_alias, run_times, self._logger.name) - except BaseException: - self._run_job_error(job.id, *sys.exc_info()[1:]) - else: - self._run_job_success(job.id, events) diff --git a/telegramer/include/apscheduler/executors/gevent.py b/telegramer/include/apscheduler/executors/gevent.py deleted file mode 100644 index 1235bb6..0000000 --- a/telegramer/include/apscheduler/executors/gevent.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import absolute_import -import sys - -from apscheduler.executors.base import BaseExecutor, run_job - - -try: - import gevent -except ImportError: # pragma: nocover - raise ImportError('GeventExecutor requires gevent installed') - - -class GeventExecutor(BaseExecutor): - """ - Runs jobs as greenlets. - - Plugin alias: ``gevent`` - """ - - def _do_submit_job(self, job, run_times): - def callback(greenlet): - try: - events = greenlet.get() - except BaseException: - self._run_job_error(job.id, *sys.exc_info()[1:]) - else: - self._run_job_success(job.id, events) - - gevent.spawn(run_job, job, job._jobstore_alias, run_times, self._logger.name).\ - link(callback) diff --git a/telegramer/include/apscheduler/executors/pool.py b/telegramer/include/apscheduler/executors/pool.py deleted file mode 100644 index c85896e..0000000 --- a/telegramer/include/apscheduler/executors/pool.py +++ /dev/null @@ -1,71 +0,0 @@ -from abc import abstractmethod -import concurrent.futures - -from apscheduler.executors.base import BaseExecutor, run_job - -try: - from concurrent.futures.process import BrokenProcessPool -except ImportError: - BrokenProcessPool = None - - -class BasePoolExecutor(BaseExecutor): - @abstractmethod - def __init__(self, pool): - super(BasePoolExecutor, self).__init__() - self._pool = pool - - def _do_submit_job(self, job, run_times): - def callback(f): - exc, tb = (f.exception_info() if hasattr(f, 'exception_info') else - (f.exception(), getattr(f.exception(), '__traceback__', None))) - if exc: - self._run_job_error(job.id, exc, tb) - else: - self._run_job_success(job.id, f.result()) - - try: - f = self._pool.submit(run_job, job, job._jobstore_alias, run_times, self._logger.name) - except BrokenProcessPool: - self._logger.warning('Process pool is broken; replacing pool with a fresh instance') - self._pool = self._pool.__class__(self._pool._max_workers) - f = self._pool.submit(run_job, job, job._jobstore_alias, run_times, self._logger.name) - - f.add_done_callback(callback) - - def shutdown(self, wait=True): - self._pool.shutdown(wait) - - -class ThreadPoolExecutor(BasePoolExecutor): - """ - An executor that runs jobs in a concurrent.futures thread pool. - - Plugin alias: ``threadpool`` - - :param max_workers: the maximum number of spawned threads. - :param pool_kwargs: dict of keyword arguments to pass to the underlying - ThreadPoolExecutor constructor - """ - - def __init__(self, max_workers=10, pool_kwargs=None): - pool_kwargs = pool_kwargs or {} - pool = concurrent.futures.ThreadPoolExecutor(int(max_workers), **pool_kwargs) - super(ThreadPoolExecutor, self).__init__(pool) - - -class ProcessPoolExecutor(BasePoolExecutor): - """ - An executor that runs jobs in a concurrent.futures process pool. - - Plugin alias: ``processpool`` - - :param max_workers: the maximum number of spawned processes. - :param pool_kwargs: dict of keyword arguments to pass to the underlying - ProcessPoolExecutor constructor - """ - - def __init__(self, max_workers=10, pool_kwargs=None): - pool_kwargs = pool_kwargs or {} - pool = concurrent.futures.ProcessPoolExecutor(int(max_workers), **pool_kwargs) - super(ProcessPoolExecutor, self).__init__(pool) diff --git a/telegramer/include/apscheduler/executors/tornado.py b/telegramer/include/apscheduler/executors/tornado.py deleted file mode 100644 index 3b97eec..0000000 --- a/telegramer/include/apscheduler/executors/tornado.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import absolute_import - -import sys -from concurrent.futures import ThreadPoolExecutor - -from tornado.gen import convert_yielded - -from apscheduler.executors.base import BaseExecutor, run_job - -try: - from apscheduler.executors.base_py3 import run_coroutine_job - from apscheduler.util import iscoroutinefunction_partial -except ImportError: - def iscoroutinefunction_partial(func): - return False - - -class TornadoExecutor(BaseExecutor): - """ - Runs jobs either in a thread pool or directly on the I/O loop. - - If the job function is a native coroutine function, it is scheduled to be run directly in the - I/O loop as soon as possible. All other functions are run in a thread pool. - - Plugin alias: ``tornado`` - - :param int max_workers: maximum number of worker threads in the thread pool - """ - - def __init__(self, max_workers=10): - super(TornadoExecutor, self).__init__() - self.executor = ThreadPoolExecutor(max_workers) - - def start(self, scheduler, alias): - super(TornadoExecutor, self).start(scheduler, alias) - self._ioloop = scheduler._ioloop - - def _do_submit_job(self, job, run_times): - def callback(f): - try: - events = f.result() - except BaseException: - self._run_job_error(job.id, *sys.exc_info()[1:]) - else: - self._run_job_success(job.id, events) - - if iscoroutinefunction_partial(job.func): - f = run_coroutine_job(job, job._jobstore_alias, run_times, self._logger.name) - else: - f = self.executor.submit(run_job, job, job._jobstore_alias, run_times, - self._logger.name) - - f = convert_yielded(f) - f.add_done_callback(callback) diff --git a/telegramer/include/apscheduler/executors/twisted.py b/telegramer/include/apscheduler/executors/twisted.py deleted file mode 100644 index c7bcf64..0000000 --- a/telegramer/include/apscheduler/executors/twisted.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import absolute_import - -from apscheduler.executors.base import BaseExecutor, run_job - - -class TwistedExecutor(BaseExecutor): - """ - Runs jobs in the reactor's thread pool. - - Plugin alias: ``twisted`` - """ - - def start(self, scheduler, alias): - super(TwistedExecutor, self).start(scheduler, alias) - self._reactor = scheduler._reactor - - def _do_submit_job(self, job, run_times): - def callback(success, result): - if success: - self._run_job_success(job.id, result) - else: - self._run_job_error(job.id, result.value, result.tb) - - self._reactor.getThreadPool().callInThreadWithCallback( - callback, run_job, job, job._jobstore_alias, run_times, self._logger.name) diff --git a/telegramer/include/apscheduler/job.py b/telegramer/include/apscheduler/job.py deleted file mode 100644 index 445d9a8..0000000 --- a/telegramer/include/apscheduler/job.py +++ /dev/null @@ -1,302 +0,0 @@ -from inspect import ismethod, isclass -from uuid import uuid4 - -import six - -from apscheduler.triggers.base import BaseTrigger -from apscheduler.util import ( - ref_to_obj, obj_to_ref, datetime_repr, repr_escape, get_callable_name, check_callable_args, - convert_to_datetime) - -try: - from collections.abc import Iterable, Mapping -except ImportError: - from collections import Iterable, Mapping - - -class Job(object): - """ - Contains the options given when scheduling callables and its current schedule and other state. - This class should never be instantiated by the user. - - :var str id: the unique identifier of this job - :var str name: the description of this job - :var func: the callable to execute - :var tuple|list args: positional arguments to the callable - :var dict kwargs: keyword arguments to the callable - :var bool coalesce: whether to only run the job once when several run times are due - :var trigger: the trigger object that controls the schedule of this job - :var str executor: the name of the executor that will run this job - :var int misfire_grace_time: the time (in seconds) how much this job's execution is allowed to - be late (``None`` means "allow the job to run no matter how late it is") - :var int max_instances: the maximum number of concurrently executing instances allowed for this - job - :var datetime.datetime next_run_time: the next scheduled run time of this job - - .. note:: - The ``misfire_grace_time`` has some non-obvious effects on job execution. See the - :ref:`missed-job-executions` section in the documentation for an in-depth explanation. - """ - - __slots__ = ('_scheduler', '_jobstore_alias', 'id', 'trigger', 'executor', 'func', 'func_ref', - 'args', 'kwargs', 'name', 'misfire_grace_time', 'coalesce', 'max_instances', - 'next_run_time', '__weakref__') - - def __init__(self, scheduler, id=None, **kwargs): - super(Job, self).__init__() - self._scheduler = scheduler - self._jobstore_alias = None - self._modify(id=id or uuid4().hex, **kwargs) - - def modify(self, **changes): - """ - Makes the given changes to this job and saves it in the associated job store. - - Accepted keyword arguments are the same as the variables on this class. - - .. seealso:: :meth:`~apscheduler.schedulers.base.BaseScheduler.modify_job` - - :return Job: this job instance - - """ - self._scheduler.modify_job(self.id, self._jobstore_alias, **changes) - return self - - def reschedule(self, trigger, **trigger_args): - """ - Shortcut for switching the trigger on this job. - - .. seealso:: :meth:`~apscheduler.schedulers.base.BaseScheduler.reschedule_job` - - :return Job: this job instance - - """ - self._scheduler.reschedule_job(self.id, self._jobstore_alias, trigger, **trigger_args) - return self - - def pause(self): - """ - Temporarily suspend the execution of this job. - - .. seealso:: :meth:`~apscheduler.schedulers.base.BaseScheduler.pause_job` - - :return Job: this job instance - - """ - self._scheduler.pause_job(self.id, self._jobstore_alias) - return self - - def resume(self): - """ - Resume the schedule of this job if previously paused. - - .. seealso:: :meth:`~apscheduler.schedulers.base.BaseScheduler.resume_job` - - :return Job: this job instance - - """ - self._scheduler.resume_job(self.id, self._jobstore_alias) - return self - - def remove(self): - """ - Unschedules this job and removes it from its associated job store. - - .. seealso:: :meth:`~apscheduler.schedulers.base.BaseScheduler.remove_job` - - """ - self._scheduler.remove_job(self.id, self._jobstore_alias) - - @property - def pending(self): - """ - Returns ``True`` if the referenced job is still waiting to be added to its designated job - store. - - """ - return self._jobstore_alias is None - - # - # Private API - # - - def _get_run_times(self, now): - """ - Computes the scheduled run times between ``next_run_time`` and ``now`` (inclusive). - - :type now: datetime.datetime - :rtype: list[datetime.datetime] - - """ - run_times = [] - next_run_time = self.next_run_time - while next_run_time and next_run_time <= now: - run_times.append(next_run_time) - next_run_time = self.trigger.get_next_fire_time(next_run_time, now) - - return run_times - - def _modify(self, **changes): - """ - Validates the changes to the Job and makes the modifications if and only if all of them - validate. - - """ - approved = {} - - if 'id' in changes: - value = changes.pop('id') - if not isinstance(value, six.string_types): - raise TypeError("id must be a nonempty string") - if hasattr(self, 'id'): - raise ValueError('The job ID may not be changed') - approved['id'] = value - - if 'func' in changes or 'args' in changes or 'kwargs' in changes: - func = changes.pop('func') if 'func' in changes else self.func - args = changes.pop('args') if 'args' in changes else self.args - kwargs = changes.pop('kwargs') if 'kwargs' in changes else self.kwargs - - if isinstance(func, six.string_types): - func_ref = func - func = ref_to_obj(func) - elif callable(func): - try: - func_ref = obj_to_ref(func) - except ValueError: - # If this happens, this Job won't be serializable - func_ref = None - else: - raise TypeError('func must be a callable or a textual reference to one') - - if not hasattr(self, 'name') and changes.get('name', None) is None: - changes['name'] = get_callable_name(func) - - if isinstance(args, six.string_types) or not isinstance(args, Iterable): - raise TypeError('args must be a non-string iterable') - if isinstance(kwargs, six.string_types) or not isinstance(kwargs, Mapping): - raise TypeError('kwargs must be a dict-like object') - - check_callable_args(func, args, kwargs) - - approved['func'] = func - approved['func_ref'] = func_ref - approved['args'] = args - approved['kwargs'] = kwargs - - if 'name' in changes: - value = changes.pop('name') - if not value or not isinstance(value, six.string_types): - raise TypeError("name must be a nonempty string") - approved['name'] = value - - if 'misfire_grace_time' in changes: - value = changes.pop('misfire_grace_time') - if value is not None and (not isinstance(value, six.integer_types) or value <= 0): - raise TypeError('misfire_grace_time must be either None or a positive integer') - approved['misfire_grace_time'] = value - - if 'coalesce' in changes: - value = bool(changes.pop('coalesce')) - approved['coalesce'] = value - - if 'max_instances' in changes: - value = changes.pop('max_instances') - if not isinstance(value, six.integer_types) or value <= 0: - raise TypeError('max_instances must be a positive integer') - approved['max_instances'] = value - - if 'trigger' in changes: - trigger = changes.pop('trigger') - if not isinstance(trigger, BaseTrigger): - raise TypeError('Expected a trigger instance, got %s instead' % - trigger.__class__.__name__) - - approved['trigger'] = trigger - - if 'executor' in changes: - value = changes.pop('executor') - if not isinstance(value, six.string_types): - raise TypeError('executor must be a string') - approved['executor'] = value - - if 'next_run_time' in changes: - value = changes.pop('next_run_time') - approved['next_run_time'] = convert_to_datetime(value, self._scheduler.timezone, - 'next_run_time') - - if changes: - raise AttributeError('The following are not modifiable attributes of Job: %s' % - ', '.join(changes)) - - for key, value in six.iteritems(approved): - setattr(self, key, value) - - def __getstate__(self): - # Don't allow this Job to be serialized if the function reference could not be determined - if not self.func_ref: - raise ValueError( - 'This Job cannot be serialized since the reference to its callable (%r) could not ' - 'be determined. Consider giving a textual reference (module:function name) ' - 'instead.' % (self.func,)) - - # Instance methods cannot survive serialization as-is, so store the "self" argument - # explicitly - func = self.func - if ismethod(func) and not isclass(func.__self__) and obj_to_ref(func) == self.func_ref: - args = (func.__self__,) + tuple(self.args) - else: - args = self.args - - return { - 'version': 1, - 'id': self.id, - 'func': self.func_ref, - 'trigger': self.trigger, - 'executor': self.executor, - 'args': args, - 'kwargs': self.kwargs, - 'name': self.name, - 'misfire_grace_time': self.misfire_grace_time, - 'coalesce': self.coalesce, - 'max_instances': self.max_instances, - 'next_run_time': self.next_run_time - } - - def __setstate__(self, state): - if state.get('version', 1) > 1: - raise ValueError('Job has version %s, but only version 1 can be handled' % - state['version']) - - self.id = state['id'] - self.func_ref = state['func'] - self.func = ref_to_obj(self.func_ref) - self.trigger = state['trigger'] - self.executor = state['executor'] - self.args = state['args'] - self.kwargs = state['kwargs'] - self.name = state['name'] - self.misfire_grace_time = state['misfire_grace_time'] - self.coalesce = state['coalesce'] - self.max_instances = state['max_instances'] - self.next_run_time = state['next_run_time'] - - def __eq__(self, other): - if isinstance(other, Job): - return self.id == other.id - return NotImplemented - - def __repr__(self): - return '' % (repr_escape(self.id), repr_escape(self.name)) - - def __str__(self): - return repr_escape(self.__unicode__()) - - def __unicode__(self): - if hasattr(self, 'next_run_time'): - status = ('next run at: ' + datetime_repr(self.next_run_time) if - self.next_run_time else 'paused') - else: - status = 'pending' - - return u'%s (trigger: %s, %s)' % (self.name, self.trigger, status) diff --git a/telegramer/include/apscheduler/jobstores/__init__.py b/telegramer/include/apscheduler/jobstores/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/apscheduler/jobstores/base.py b/telegramer/include/apscheduler/jobstores/base.py deleted file mode 100644 index 9cff66c..0000000 --- a/telegramer/include/apscheduler/jobstores/base.py +++ /dev/null @@ -1,143 +0,0 @@ -from abc import ABCMeta, abstractmethod -import logging - -import six - - -class JobLookupError(KeyError): - """Raised when the job store cannot find a job for update or removal.""" - - def __init__(self, job_id): - super(JobLookupError, self).__init__(u'No job by the id of %s was found' % job_id) - - -class ConflictingIdError(KeyError): - """Raised when the uniqueness of job IDs is being violated.""" - - def __init__(self, job_id): - super(ConflictingIdError, self).__init__( - u'Job identifier (%s) conflicts with an existing job' % job_id) - - -class TransientJobError(ValueError): - """ - Raised when an attempt to add transient (with no func_ref) job to a persistent job store is - detected. - """ - - def __init__(self, job_id): - super(TransientJobError, self).__init__( - u'Job (%s) cannot be added to this job store because a reference to the callable ' - u'could not be determined.' % job_id) - - -class BaseJobStore(six.with_metaclass(ABCMeta)): - """Abstract base class that defines the interface that every job store must implement.""" - - _scheduler = None - _alias = None - _logger = logging.getLogger('apscheduler.jobstores') - - def start(self, scheduler, alias): - """ - Called by the scheduler when the scheduler is being started or when the job store is being - added to an already running scheduler. - - :param apscheduler.schedulers.base.BaseScheduler scheduler: the scheduler that is starting - this job store - :param str|unicode alias: alias of this job store as it was assigned to the scheduler - """ - - self._scheduler = scheduler - self._alias = alias - self._logger = logging.getLogger('apscheduler.jobstores.%s' % alias) - - def shutdown(self): - """Frees any resources still bound to this job store.""" - - def _fix_paused_jobs_sorting(self, jobs): - for i, job in enumerate(jobs): - if job.next_run_time is not None: - if i > 0: - paused_jobs = jobs[:i] - del jobs[:i] - jobs.extend(paused_jobs) - break - - @abstractmethod - def lookup_job(self, job_id): - """ - Returns a specific job, or ``None`` if it isn't found.. - - The job store is responsible for setting the ``scheduler`` and ``jobstore`` attributes of - the returned job to point to the scheduler and itself, respectively. - - :param str|unicode job_id: identifier of the job - :rtype: Job - """ - - @abstractmethod - def get_due_jobs(self, now): - """ - Returns the list of jobs that have ``next_run_time`` earlier or equal to ``now``. - The returned jobs must be sorted by next run time (ascending). - - :param datetime.datetime now: the current (timezone aware) datetime - :rtype: list[Job] - """ - - @abstractmethod - def get_next_run_time(self): - """ - Returns the earliest run time of all the jobs stored in this job store, or ``None`` if - there are no active jobs. - - :rtype: datetime.datetime - """ - - @abstractmethod - def get_all_jobs(self): - """ - Returns a list of all jobs in this job store. - The returned jobs should be sorted by next run time (ascending). - Paused jobs (next_run_time == None) should be sorted last. - - The job store is responsible for setting the ``scheduler`` and ``jobstore`` attributes of - the returned jobs to point to the scheduler and itself, respectively. - - :rtype: list[Job] - """ - - @abstractmethod - def add_job(self, job): - """ - Adds the given job to this store. - - :param Job job: the job to add - :raises ConflictingIdError: if there is another job in this store with the same ID - """ - - @abstractmethod - def update_job(self, job): - """ - Replaces the job in the store with the given newer version. - - :param Job job: the job to update - :raises JobLookupError: if the job does not exist - """ - - @abstractmethod - def remove_job(self, job_id): - """ - Removes the given job from this store. - - :param str|unicode job_id: identifier of the job - :raises JobLookupError: if the job does not exist - """ - - @abstractmethod - def remove_all_jobs(self): - """Removes all jobs from this store.""" - - def __repr__(self): - return '<%s>' % self.__class__.__name__ diff --git a/telegramer/include/apscheduler/jobstores/memory.py b/telegramer/include/apscheduler/jobstores/memory.py deleted file mode 100644 index abfe7c6..0000000 --- a/telegramer/include/apscheduler/jobstores/memory.py +++ /dev/null @@ -1,108 +0,0 @@ -from __future__ import absolute_import - -from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError -from apscheduler.util import datetime_to_utc_timestamp - - -class MemoryJobStore(BaseJobStore): - """ - Stores jobs in an array in RAM. Provides no persistence support. - - Plugin alias: ``memory`` - """ - - def __init__(self): - super(MemoryJobStore, self).__init__() - # list of (job, timestamp), sorted by next_run_time and job id (ascending) - self._jobs = [] - self._jobs_index = {} # id -> (job, timestamp) lookup table - - def lookup_job(self, job_id): - return self._jobs_index.get(job_id, (None, None))[0] - - def get_due_jobs(self, now): - now_timestamp = datetime_to_utc_timestamp(now) - pending = [] - for job, timestamp in self._jobs: - if timestamp is None or timestamp > now_timestamp: - break - pending.append(job) - - return pending - - def get_next_run_time(self): - return self._jobs[0][0].next_run_time if self._jobs else None - - def get_all_jobs(self): - return [j[0] for j in self._jobs] - - def add_job(self, job): - if job.id in self._jobs_index: - raise ConflictingIdError(job.id) - - timestamp = datetime_to_utc_timestamp(job.next_run_time) - index = self._get_job_index(timestamp, job.id) - self._jobs.insert(index, (job, timestamp)) - self._jobs_index[job.id] = (job, timestamp) - - def update_job(self, job): - old_job, old_timestamp = self._jobs_index.get(job.id, (None, None)) - if old_job is None: - raise JobLookupError(job.id) - - # If the next run time has not changed, simply replace the job in its present index. - # Otherwise, reinsert the job to the list to preserve the ordering. - old_index = self._get_job_index(old_timestamp, old_job.id) - new_timestamp = datetime_to_utc_timestamp(job.next_run_time) - if old_timestamp == new_timestamp: - self._jobs[old_index] = (job, new_timestamp) - else: - del self._jobs[old_index] - new_index = self._get_job_index(new_timestamp, job.id) - self._jobs.insert(new_index, (job, new_timestamp)) - - self._jobs_index[old_job.id] = (job, new_timestamp) - - def remove_job(self, job_id): - job, timestamp = self._jobs_index.get(job_id, (None, None)) - if job is None: - raise JobLookupError(job_id) - - index = self._get_job_index(timestamp, job_id) - del self._jobs[index] - del self._jobs_index[job.id] - - def remove_all_jobs(self): - self._jobs = [] - self._jobs_index = {} - - def shutdown(self): - self.remove_all_jobs() - - def _get_job_index(self, timestamp, job_id): - """ - Returns the index of the given job, or if it's not found, the index where the job should be - inserted based on the given timestamp. - - :type timestamp: int - :type job_id: str - - """ - lo, hi = 0, len(self._jobs) - timestamp = float('inf') if timestamp is None else timestamp - while lo < hi: - mid = (lo + hi) // 2 - mid_job, mid_timestamp = self._jobs[mid] - mid_timestamp = float('inf') if mid_timestamp is None else mid_timestamp - if mid_timestamp > timestamp: - hi = mid - elif mid_timestamp < timestamp: - lo = mid + 1 - elif mid_job.id > job_id: - hi = mid - elif mid_job.id < job_id: - lo = mid + 1 - else: - return mid - - return lo diff --git a/telegramer/include/apscheduler/jobstores/mongodb.py b/telegramer/include/apscheduler/jobstores/mongodb.py deleted file mode 100644 index 5a00f94..0000000 --- a/telegramer/include/apscheduler/jobstores/mongodb.py +++ /dev/null @@ -1,141 +0,0 @@ -from __future__ import absolute_import -import warnings - -from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError -from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime -from apscheduler.job import Job - -try: - import cPickle as pickle -except ImportError: # pragma: nocover - import pickle - -try: - from bson.binary import Binary - from pymongo.errors import DuplicateKeyError - from pymongo import MongoClient, ASCENDING -except ImportError: # pragma: nocover - raise ImportError('MongoDBJobStore requires PyMongo installed') - - -class MongoDBJobStore(BaseJobStore): - """ - Stores jobs in a MongoDB database. Any leftover keyword arguments are directly passed to - pymongo's `MongoClient - `_. - - Plugin alias: ``mongodb`` - - :param str database: database to store jobs in - :param str collection: collection to store jobs in - :param client: a :class:`~pymongo.mongo_client.MongoClient` instance to use instead of - providing connection arguments - :param int pickle_protocol: pickle protocol level to use (for serialization), defaults to the - highest available - """ - - def __init__(self, database='apscheduler', collection='jobs', client=None, - pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args): - super(MongoDBJobStore, self).__init__() - self.pickle_protocol = pickle_protocol - - if not database: - raise ValueError('The "database" parameter must not be empty') - if not collection: - raise ValueError('The "collection" parameter must not be empty') - - if client: - self.client = maybe_ref(client) - else: - connect_args.setdefault('w', 1) - self.client = MongoClient(**connect_args) - - self.collection = self.client[database][collection] - - def start(self, scheduler, alias): - super(MongoDBJobStore, self).start(scheduler, alias) - self.collection.create_index('next_run_time', sparse=True) - - @property - def connection(self): - warnings.warn('The "connection" member is deprecated -- use "client" instead', - DeprecationWarning) - return self.client - - def lookup_job(self, job_id): - document = self.collection.find_one(job_id, ['job_state']) - return self._reconstitute_job(document['job_state']) if document else None - - def get_due_jobs(self, now): - timestamp = datetime_to_utc_timestamp(now) - return self._get_jobs({'next_run_time': {'$lte': timestamp}}) - - def get_next_run_time(self): - document = self.collection.find_one({'next_run_time': {'$ne': None}}, - projection=['next_run_time'], - sort=[('next_run_time', ASCENDING)]) - return utc_timestamp_to_datetime(document['next_run_time']) if document else None - - def get_all_jobs(self): - jobs = self._get_jobs({}) - self._fix_paused_jobs_sorting(jobs) - return jobs - - def add_job(self, job): - try: - self.collection.insert_one({ - '_id': job.id, - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': Binary(pickle.dumps(job.__getstate__(), self.pickle_protocol)) - }) - except DuplicateKeyError: - raise ConflictingIdError(job.id) - - def update_job(self, job): - changes = { - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': Binary(pickle.dumps(job.__getstate__(), self.pickle_protocol)) - } - result = self.collection.update_one({'_id': job.id}, {'$set': changes}) - if result and result.matched_count == 0: - raise JobLookupError(job.id) - - def remove_job(self, job_id): - result = self.collection.delete_one({'_id': job_id}) - if result and result.deleted_count == 0: - raise JobLookupError(job_id) - - def remove_all_jobs(self): - self.collection.delete_many({}) - - def shutdown(self): - self.client.close() - - def _reconstitute_job(self, job_state): - job_state = pickle.loads(job_state) - job = Job.__new__(Job) - job.__setstate__(job_state) - job._scheduler = self._scheduler - job._jobstore_alias = self._alias - return job - - def _get_jobs(self, conditions): - jobs = [] - failed_job_ids = [] - for document in self.collection.find(conditions, ['_id', 'job_state'], - sort=[('next_run_time', ASCENDING)]): - try: - jobs.append(self._reconstitute_job(document['job_state'])) - except BaseException: - self._logger.exception('Unable to restore job "%s" -- removing it', - document['_id']) - failed_job_ids.append(document['_id']) - - # Remove all the jobs we failed to restore - if failed_job_ids: - self.collection.delete_many({'_id': {'$in': failed_job_ids}}) - - return jobs - - def __repr__(self): - return '<%s (client=%s)>' % (self.__class__.__name__, self.client) diff --git a/telegramer/include/apscheduler/jobstores/redis.py b/telegramer/include/apscheduler/jobstores/redis.py deleted file mode 100644 index 5bb69d6..0000000 --- a/telegramer/include/apscheduler/jobstores/redis.py +++ /dev/null @@ -1,150 +0,0 @@ -from __future__ import absolute_import -from datetime import datetime - -from pytz import utc -import six - -from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError -from apscheduler.util import datetime_to_utc_timestamp, utc_timestamp_to_datetime -from apscheduler.job import Job - -try: - import cPickle as pickle -except ImportError: # pragma: nocover - import pickle - -try: - from redis import Redis -except ImportError: # pragma: nocover - raise ImportError('RedisJobStore requires redis installed') - - -class RedisJobStore(BaseJobStore): - """ - Stores jobs in a Redis database. Any leftover keyword arguments are directly passed to redis's - :class:`~redis.StrictRedis`. - - Plugin alias: ``redis`` - - :param int db: the database number to store jobs in - :param str jobs_key: key to store jobs in - :param str run_times_key: key to store the jobs' run times in - :param int pickle_protocol: pickle protocol level to use (for serialization), defaults to the - highest available - """ - - def __init__(self, db=0, jobs_key='apscheduler.jobs', run_times_key='apscheduler.run_times', - pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args): - super(RedisJobStore, self).__init__() - - if db is None: - raise ValueError('The "db" parameter must not be empty') - if not jobs_key: - raise ValueError('The "jobs_key" parameter must not be empty') - if not run_times_key: - raise ValueError('The "run_times_key" parameter must not be empty') - - self.pickle_protocol = pickle_protocol - self.jobs_key = jobs_key - self.run_times_key = run_times_key - self.redis = Redis(db=int(db), **connect_args) - - def lookup_job(self, job_id): - job_state = self.redis.hget(self.jobs_key, job_id) - return self._reconstitute_job(job_state) if job_state else None - - def get_due_jobs(self, now): - timestamp = datetime_to_utc_timestamp(now) - job_ids = self.redis.zrangebyscore(self.run_times_key, 0, timestamp) - if job_ids: - job_states = self.redis.hmget(self.jobs_key, *job_ids) - return self._reconstitute_jobs(six.moves.zip(job_ids, job_states)) - return [] - - def get_next_run_time(self): - next_run_time = self.redis.zrange(self.run_times_key, 0, 0, withscores=True) - if next_run_time: - return utc_timestamp_to_datetime(next_run_time[0][1]) - - def get_all_jobs(self): - job_states = self.redis.hgetall(self.jobs_key) - jobs = self._reconstitute_jobs(six.iteritems(job_states)) - paused_sort_key = datetime(9999, 12, 31, tzinfo=utc) - return sorted(jobs, key=lambda job: job.next_run_time or paused_sort_key) - - def add_job(self, job): - if self.redis.hexists(self.jobs_key, job.id): - raise ConflictingIdError(job.id) - - with self.redis.pipeline() as pipe: - pipe.multi() - pipe.hset(self.jobs_key, job.id, pickle.dumps(job.__getstate__(), - self.pickle_protocol)) - if job.next_run_time: - pipe.zadd(self.run_times_key, - {job.id: datetime_to_utc_timestamp(job.next_run_time)}) - - pipe.execute() - - def update_job(self, job): - if not self.redis.hexists(self.jobs_key, job.id): - raise JobLookupError(job.id) - - with self.redis.pipeline() as pipe: - pipe.hset(self.jobs_key, job.id, pickle.dumps(job.__getstate__(), - self.pickle_protocol)) - if job.next_run_time: - pipe.zadd(self.run_times_key, - {job.id: datetime_to_utc_timestamp(job.next_run_time)}) - else: - pipe.zrem(self.run_times_key, job.id) - - pipe.execute() - - def remove_job(self, job_id): - if not self.redis.hexists(self.jobs_key, job_id): - raise JobLookupError(job_id) - - with self.redis.pipeline() as pipe: - pipe.hdel(self.jobs_key, job_id) - pipe.zrem(self.run_times_key, job_id) - pipe.execute() - - def remove_all_jobs(self): - with self.redis.pipeline() as pipe: - pipe.delete(self.jobs_key) - pipe.delete(self.run_times_key) - pipe.execute() - - def shutdown(self): - self.redis.connection_pool.disconnect() - - def _reconstitute_job(self, job_state): - job_state = pickle.loads(job_state) - job = Job.__new__(Job) - job.__setstate__(job_state) - job._scheduler = self._scheduler - job._jobstore_alias = self._alias - return job - - def _reconstitute_jobs(self, job_states): - jobs = [] - failed_job_ids = [] - for job_id, job_state in job_states: - try: - jobs.append(self._reconstitute_job(job_state)) - except BaseException: - self._logger.exception('Unable to restore job "%s" -- removing it', job_id) - failed_job_ids.append(job_id) - - # Remove all the jobs we failed to restore - if failed_job_ids: - with self.redis.pipeline() as pipe: - pipe.hdel(self.jobs_key, *failed_job_ids) - pipe.zrem(self.run_times_key, *failed_job_ids) - pipe.execute() - - return jobs - - def __repr__(self): - return '<%s>' % self.__class__.__name__ diff --git a/telegramer/include/apscheduler/jobstores/rethinkdb.py b/telegramer/include/apscheduler/jobstores/rethinkdb.py deleted file mode 100644 index d8a78cd..0000000 --- a/telegramer/include/apscheduler/jobstores/rethinkdb.py +++ /dev/null @@ -1,155 +0,0 @@ -from __future__ import absolute_import - -from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError -from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime -from apscheduler.job import Job - -try: - import cPickle as pickle -except ImportError: # pragma: nocover - import pickle - -try: - from rethinkdb import RethinkDB -except ImportError: # pragma: nocover - raise ImportError('RethinkDBJobStore requires rethinkdb installed') - - -class RethinkDBJobStore(BaseJobStore): - """ - Stores jobs in a RethinkDB database. Any leftover keyword arguments are directly passed to - rethinkdb's `RethinkdbClient `_. - - Plugin alias: ``rethinkdb`` - - :param str database: database to store jobs in - :param str collection: collection to store jobs in - :param client: a :class:`rethinkdb.net.Connection` instance to use instead of providing - connection arguments - :param int pickle_protocol: pickle protocol level to use (for serialization), defaults to the - highest available - """ - - def __init__(self, database='apscheduler', table='jobs', client=None, - pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args): - super(RethinkDBJobStore, self).__init__() - - if not database: - raise ValueError('The "database" parameter must not be empty') - if not table: - raise ValueError('The "table" parameter must not be empty') - - self.database = database - self.table_name = table - self.table = None - self.client = client - self.pickle_protocol = pickle_protocol - self.connect_args = connect_args - self.r = RethinkDB() - self.conn = None - - def start(self, scheduler, alias): - super(RethinkDBJobStore, self).start(scheduler, alias) - - if self.client: - self.conn = maybe_ref(self.client) - else: - self.conn = self.r.connect(db=self.database, **self.connect_args) - - if self.database not in self.r.db_list().run(self.conn): - self.r.db_create(self.database).run(self.conn) - - if self.table_name not in self.r.table_list().run(self.conn): - self.r.table_create(self.table_name).run(self.conn) - - if 'next_run_time' not in self.r.table(self.table_name).index_list().run(self.conn): - self.r.table(self.table_name).index_create('next_run_time').run(self.conn) - - self.table = self.r.db(self.database).table(self.table_name) - - def lookup_job(self, job_id): - results = list(self.table.get_all(job_id).pluck('job_state').run(self.conn)) - return self._reconstitute_job(results[0]['job_state']) if results else None - - def get_due_jobs(self, now): - return self._get_jobs(self.r.row['next_run_time'] <= datetime_to_utc_timestamp(now)) - - def get_next_run_time(self): - results = list( - self.table - .filter(self.r.row['next_run_time'] != None) # noqa - .order_by(self.r.asc('next_run_time')) - .map(lambda x: x['next_run_time']) - .limit(1) - .run(self.conn) - ) - return utc_timestamp_to_datetime(results[0]) if results else None - - def get_all_jobs(self): - jobs = self._get_jobs() - self._fix_paused_jobs_sorting(jobs) - return jobs - - def add_job(self, job): - job_dict = { - 'id': job.id, - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': self.r.binary(pickle.dumps(job.__getstate__(), self.pickle_protocol)) - } - results = self.table.insert(job_dict).run(self.conn) - if results['errors'] > 0: - raise ConflictingIdError(job.id) - - def update_job(self, job): - changes = { - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': self.r.binary(pickle.dumps(job.__getstate__(), self.pickle_protocol)) - } - results = self.table.get_all(job.id).update(changes).run(self.conn) - skipped = False in map(lambda x: results[x] == 0, results.keys()) - if results['skipped'] > 0 or results['errors'] > 0 or not skipped: - raise JobLookupError(job.id) - - def remove_job(self, job_id): - results = self.table.get_all(job_id).delete().run(self.conn) - if results['deleted'] + results['skipped'] != 1: - raise JobLookupError(job_id) - - def remove_all_jobs(self): - self.table.delete().run(self.conn) - - def shutdown(self): - self.conn.close() - - def _reconstitute_job(self, job_state): - job_state = pickle.loads(job_state) - job = Job.__new__(Job) - job.__setstate__(job_state) - job._scheduler = self._scheduler - job._jobstore_alias = self._alias - return job - - def _get_jobs(self, predicate=None): - jobs = [] - failed_job_ids = [] - query = (self.table.filter(self.r.row['next_run_time'] != None).filter(predicate) # noqa - if predicate else self.table) - query = query.order_by('next_run_time', 'id').pluck('id', 'job_state') - - for document in query.run(self.conn): - try: - jobs.append(self._reconstitute_job(document['job_state'])) - except Exception: - self._logger.exception('Unable to restore job "%s" -- removing it', document['id']) - failed_job_ids.append(document['id']) - - # Remove all the jobs we failed to restore - if failed_job_ids: - self.r.expr(failed_job_ids).for_each( - lambda job_id: self.table.get_all(job_id).delete()).run(self.conn) - - return jobs - - def __repr__(self): - connection = self.conn - return '<%s (connection=%s)>' % (self.__class__.__name__, connection) diff --git a/telegramer/include/apscheduler/jobstores/sqlalchemy.py b/telegramer/include/apscheduler/jobstores/sqlalchemy.py deleted file mode 100644 index dcfd3e5..0000000 --- a/telegramer/include/apscheduler/jobstores/sqlalchemy.py +++ /dev/null @@ -1,154 +0,0 @@ -from __future__ import absolute_import - -from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError -from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime -from apscheduler.job import Job - -try: - import cPickle as pickle -except ImportError: # pragma: nocover - import pickle - -try: - from sqlalchemy import ( - create_engine, Table, Column, MetaData, Unicode, Float, LargeBinary, select, and_) - from sqlalchemy.exc import IntegrityError - from sqlalchemy.sql.expression import null -except ImportError: # pragma: nocover - raise ImportError('SQLAlchemyJobStore requires SQLAlchemy installed') - - -class SQLAlchemyJobStore(BaseJobStore): - """ - Stores jobs in a database table using SQLAlchemy. - The table will be created if it doesn't exist in the database. - - Plugin alias: ``sqlalchemy`` - - :param str url: connection string (see - :ref:`SQLAlchemy documentation ` on this) - :param engine: an SQLAlchemy :class:`~sqlalchemy.engine.Engine` to use instead of creating a - new one based on ``url`` - :param str tablename: name of the table to store jobs in - :param metadata: a :class:`~sqlalchemy.schema.MetaData` instance to use instead of creating a - new one - :param int pickle_protocol: pickle protocol level to use (for serialization), defaults to the - highest available - :param str tableschema: name of the (existing) schema in the target database where the table - should be - :param dict engine_options: keyword arguments to :func:`~sqlalchemy.create_engine` - (ignored if ``engine`` is given) - """ - - def __init__(self, url=None, engine=None, tablename='apscheduler_jobs', metadata=None, - pickle_protocol=pickle.HIGHEST_PROTOCOL, tableschema=None, engine_options=None): - super(SQLAlchemyJobStore, self).__init__() - self.pickle_protocol = pickle_protocol - metadata = maybe_ref(metadata) or MetaData() - - if engine: - self.engine = maybe_ref(engine) - elif url: - self.engine = create_engine(url, **(engine_options or {})) - else: - raise ValueError('Need either "engine" or "url" defined') - - # 191 = max key length in MySQL for InnoDB/utf8mb4 tables, - # 25 = precision that translates to an 8-byte float - self.jobs_t = Table( - tablename, metadata, - Column('id', Unicode(191, _warn_on_bytestring=False), primary_key=True), - Column('next_run_time', Float(25), index=True), - Column('job_state', LargeBinary, nullable=False), - schema=tableschema - ) - - def start(self, scheduler, alias): - super(SQLAlchemyJobStore, self).start(scheduler, alias) - self.jobs_t.create(self.engine, True) - - def lookup_job(self, job_id): - selectable = select([self.jobs_t.c.job_state]).where(self.jobs_t.c.id == job_id) - job_state = self.engine.execute(selectable).scalar() - return self._reconstitute_job(job_state) if job_state else None - - def get_due_jobs(self, now): - timestamp = datetime_to_utc_timestamp(now) - return self._get_jobs(self.jobs_t.c.next_run_time <= timestamp) - - def get_next_run_time(self): - selectable = select([self.jobs_t.c.next_run_time]).\ - where(self.jobs_t.c.next_run_time != null()).\ - order_by(self.jobs_t.c.next_run_time).limit(1) - next_run_time = self.engine.execute(selectable).scalar() - return utc_timestamp_to_datetime(next_run_time) - - def get_all_jobs(self): - jobs = self._get_jobs() - self._fix_paused_jobs_sorting(jobs) - return jobs - - def add_job(self, job): - insert = self.jobs_t.insert().values(**{ - 'id': job.id, - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol) - }) - try: - self.engine.execute(insert) - except IntegrityError: - raise ConflictingIdError(job.id) - - def update_job(self, job): - update = self.jobs_t.update().values(**{ - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': pickle.dumps(job.__getstate__(), self.pickle_protocol) - }).where(self.jobs_t.c.id == job.id) - result = self.engine.execute(update) - if result.rowcount == 0: - raise JobLookupError(job.id) - - def remove_job(self, job_id): - delete = self.jobs_t.delete().where(self.jobs_t.c.id == job_id) - result = self.engine.execute(delete) - if result.rowcount == 0: - raise JobLookupError(job_id) - - def remove_all_jobs(self): - delete = self.jobs_t.delete() - self.engine.execute(delete) - - def shutdown(self): - self.engine.dispose() - - def _reconstitute_job(self, job_state): - job_state = pickle.loads(job_state) - job_state['jobstore'] = self - job = Job.__new__(Job) - job.__setstate__(job_state) - job._scheduler = self._scheduler - job._jobstore_alias = self._alias - return job - - def _get_jobs(self, *conditions): - jobs = [] - selectable = select([self.jobs_t.c.id, self.jobs_t.c.job_state]).\ - order_by(self.jobs_t.c.next_run_time) - selectable = selectable.where(and_(*conditions)) if conditions else selectable - failed_job_ids = set() - for row in self.engine.execute(selectable): - try: - jobs.append(self._reconstitute_job(row.job_state)) - except BaseException: - self._logger.exception('Unable to restore job "%s" -- removing it', row.id) - failed_job_ids.add(row.id) - - # Remove all the jobs we failed to restore - if failed_job_ids: - delete = self.jobs_t.delete().where(self.jobs_t.c.id.in_(failed_job_ids)) - self.engine.execute(delete) - - return jobs - - def __repr__(self): - return '<%s (url=%s)>' % (self.__class__.__name__, self.engine.url) diff --git a/telegramer/include/apscheduler/jobstores/zookeeper.py b/telegramer/include/apscheduler/jobstores/zookeeper.py deleted file mode 100644 index 5253069..0000000 --- a/telegramer/include/apscheduler/jobstores/zookeeper.py +++ /dev/null @@ -1,178 +0,0 @@ -from __future__ import absolute_import - -from datetime import datetime - -from pytz import utc -from kazoo.exceptions import NoNodeError, NodeExistsError - -from apscheduler.jobstores.base import BaseJobStore, JobLookupError, ConflictingIdError -from apscheduler.util import maybe_ref, datetime_to_utc_timestamp, utc_timestamp_to_datetime -from apscheduler.job import Job - -try: - import cPickle as pickle -except ImportError: # pragma: nocover - import pickle - -try: - from kazoo.client import KazooClient -except ImportError: # pragma: nocover - raise ImportError('ZooKeeperJobStore requires Kazoo installed') - - -class ZooKeeperJobStore(BaseJobStore): - """ - Stores jobs in a ZooKeeper tree. Any leftover keyword arguments are directly passed to - kazoo's `KazooClient - `_. - - Plugin alias: ``zookeeper`` - - :param str path: path to store jobs in - :param client: a :class:`~kazoo.client.KazooClient` instance to use instead of - providing connection arguments - :param int pickle_protocol: pickle protocol level to use (for serialization), defaults to the - highest available - """ - - def __init__(self, path='/apscheduler', client=None, close_connection_on_exit=False, - pickle_protocol=pickle.HIGHEST_PROTOCOL, **connect_args): - super(ZooKeeperJobStore, self).__init__() - self.pickle_protocol = pickle_protocol - self.close_connection_on_exit = close_connection_on_exit - - if not path: - raise ValueError('The "path" parameter must not be empty') - - self.path = path - - if client: - self.client = maybe_ref(client) - else: - self.client = KazooClient(**connect_args) - self._ensured_path = False - - def _ensure_paths(self): - if not self._ensured_path: - self.client.ensure_path(self.path) - self._ensured_path = True - - def start(self, scheduler, alias): - super(ZooKeeperJobStore, self).start(scheduler, alias) - if not self.client.connected: - self.client.start() - - def lookup_job(self, job_id): - self._ensure_paths() - node_path = self.path + "/" + str(job_id) - try: - content, _ = self.client.get(node_path) - doc = pickle.loads(content) - job = self._reconstitute_job(doc['job_state']) - return job - except BaseException: - return None - - def get_due_jobs(self, now): - timestamp = datetime_to_utc_timestamp(now) - jobs = [job_def['job'] for job_def in self._get_jobs() - if job_def['next_run_time'] is not None and job_def['next_run_time'] <= timestamp] - return jobs - - def get_next_run_time(self): - next_runs = [job_def['next_run_time'] for job_def in self._get_jobs() - if job_def['next_run_time'] is not None] - return utc_timestamp_to_datetime(min(next_runs)) if len(next_runs) > 0 else None - - def get_all_jobs(self): - jobs = [job_def['job'] for job_def in self._get_jobs()] - self._fix_paused_jobs_sorting(jobs) - return jobs - - def add_job(self, job): - self._ensure_paths() - node_path = self.path + "/" + str(job.id) - value = { - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': job.__getstate__() - } - data = pickle.dumps(value, self.pickle_protocol) - try: - self.client.create(node_path, value=data) - except NodeExistsError: - raise ConflictingIdError(job.id) - - def update_job(self, job): - self._ensure_paths() - node_path = self.path + "/" + str(job.id) - changes = { - 'next_run_time': datetime_to_utc_timestamp(job.next_run_time), - 'job_state': job.__getstate__() - } - data = pickle.dumps(changes, self.pickle_protocol) - try: - self.client.set(node_path, value=data) - except NoNodeError: - raise JobLookupError(job.id) - - def remove_job(self, job_id): - self._ensure_paths() - node_path = self.path + "/" + str(job_id) - try: - self.client.delete(node_path) - except NoNodeError: - raise JobLookupError(job_id) - - def remove_all_jobs(self): - try: - self.client.delete(self.path, recursive=True) - except NoNodeError: - pass - self._ensured_path = False - - def shutdown(self): - if self.close_connection_on_exit: - self.client.stop() - self.client.close() - - def _reconstitute_job(self, job_state): - job_state = job_state - job = Job.__new__(Job) - job.__setstate__(job_state) - job._scheduler = self._scheduler - job._jobstore_alias = self._alias - return job - - def _get_jobs(self): - self._ensure_paths() - jobs = [] - failed_job_ids = [] - all_ids = self.client.get_children(self.path) - for node_name in all_ids: - try: - node_path = self.path + "/" + node_name - content, _ = self.client.get(node_path) - doc = pickle.loads(content) - job_def = { - 'job_id': node_name, - 'next_run_time': doc['next_run_time'] if doc['next_run_time'] else None, - 'job_state': doc['job_state'], - 'job': self._reconstitute_job(doc['job_state']), - 'creation_time': _.ctime - } - jobs.append(job_def) - except BaseException: - self._logger.exception('Unable to restore job "%s" -- removing it' % node_name) - failed_job_ids.append(node_name) - - # Remove all the jobs we failed to restore - if failed_job_ids: - for failed_id in failed_job_ids: - self.remove_job(failed_id) - paused_sort_key = datetime(9999, 12, 31, tzinfo=utc) - return sorted(jobs, key=lambda job_def: (job_def['job'].next_run_time or paused_sort_key, - job_def['creation_time'])) - - def __repr__(self): - self._logger.exception('<%s (client=%s)>' % (self.__class__.__name__, self.client)) - return '<%s (client=%s)>' % (self.__class__.__name__, self.client) diff --git a/telegramer/include/apscheduler/schedulers/__init__.py b/telegramer/include/apscheduler/schedulers/__init__.py deleted file mode 100644 index bd8a790..0000000 --- a/telegramer/include/apscheduler/schedulers/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -class SchedulerAlreadyRunningError(Exception): - """Raised when attempting to start or configure the scheduler when it's already running.""" - - def __str__(self): - return 'Scheduler is already running' - - -class SchedulerNotRunningError(Exception): - """Raised when attempting to shutdown the scheduler when it's not running.""" - - def __str__(self): - return 'Scheduler is not running' diff --git a/telegramer/include/apscheduler/schedulers/asyncio.py b/telegramer/include/apscheduler/schedulers/asyncio.py deleted file mode 100644 index 70ebede..0000000 --- a/telegramer/include/apscheduler/schedulers/asyncio.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import absolute_import -from functools import wraps, partial - -from apscheduler.schedulers.base import BaseScheduler -from apscheduler.util import maybe_ref - -try: - import asyncio -except ImportError: # pragma: nocover - try: - import trollius as asyncio - except ImportError: - raise ImportError( - 'AsyncIOScheduler requires either Python 3.4 or the asyncio package installed') - - -def run_in_event_loop(func): - @wraps(func) - def wrapper(self, *args, **kwargs): - wrapped = partial(func, self, *args, **kwargs) - self._eventloop.call_soon_threadsafe(wrapped) - return wrapper - - -class AsyncIOScheduler(BaseScheduler): - """ - A scheduler that runs on an asyncio (:pep:`3156`) event loop. - - The default executor can run jobs based on native coroutines (``async def``). - - Extra options: - - ============== ============================================================= - ``event_loop`` AsyncIO event loop to use (defaults to the global event loop) - ============== ============================================================= - """ - - _eventloop = None - _timeout = None - - def start(self, paused=False): - if not self._eventloop: - self._eventloop = asyncio.get_event_loop() - - super(AsyncIOScheduler, self).start(paused) - - @run_in_event_loop - def shutdown(self, wait=True): - super(AsyncIOScheduler, self).shutdown(wait) - self._stop_timer() - - def _configure(self, config): - self._eventloop = maybe_ref(config.pop('event_loop', None)) - super(AsyncIOScheduler, self)._configure(config) - - def _start_timer(self, wait_seconds): - self._stop_timer() - if wait_seconds is not None: - self._timeout = self._eventloop.call_later(wait_seconds, self.wakeup) - - def _stop_timer(self): - if self._timeout: - self._timeout.cancel() - del self._timeout - - @run_in_event_loop - def wakeup(self): - self._stop_timer() - wait_seconds = self._process_jobs() - self._start_timer(wait_seconds) - - def _create_default_executor(self): - from apscheduler.executors.asyncio import AsyncIOExecutor - return AsyncIOExecutor() diff --git a/telegramer/include/apscheduler/schedulers/background.py b/telegramer/include/apscheduler/schedulers/background.py deleted file mode 100644 index bb8f77d..0000000 --- a/telegramer/include/apscheduler/schedulers/background.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import absolute_import - -from threading import Thread, Event - -from apscheduler.schedulers.base import BaseScheduler -from apscheduler.schedulers.blocking import BlockingScheduler -from apscheduler.util import asbool - - -class BackgroundScheduler(BlockingScheduler): - """ - A scheduler that runs in the background using a separate thread - (:meth:`~apscheduler.schedulers.base.BaseScheduler.start` will return immediately). - - Extra options: - - ========== ============================================================================= - ``daemon`` Set the ``daemon`` option in the background thread (defaults to ``True``, see - `the documentation - `_ - for further details) - ========== ============================================================================= - """ - - _thread = None - - def _configure(self, config): - self._daemon = asbool(config.pop('daemon', True)) - super(BackgroundScheduler, self)._configure(config) - - def start(self, *args, **kwargs): - if self._event is None or self._event.is_set(): - self._event = Event() - - BaseScheduler.start(self, *args, **kwargs) - self._thread = Thread(target=self._main_loop, name='APScheduler') - self._thread.daemon = self._daemon - self._thread.start() - - def shutdown(self, *args, **kwargs): - super(BackgroundScheduler, self).shutdown(*args, **kwargs) - self._thread.join() - del self._thread diff --git a/telegramer/include/apscheduler/schedulers/base.py b/telegramer/include/apscheduler/schedulers/base.py deleted file mode 100644 index 444de8e..0000000 --- a/telegramer/include/apscheduler/schedulers/base.py +++ /dev/null @@ -1,1026 +0,0 @@ -from __future__ import print_function - -from abc import ABCMeta, abstractmethod -from threading import RLock -from datetime import datetime, timedelta -from logging import getLogger -import warnings -import sys - -from pkg_resources import iter_entry_points -from tzlocal import get_localzone -import six - -from apscheduler.schedulers import SchedulerAlreadyRunningError, SchedulerNotRunningError -from apscheduler.executors.base import MaxInstancesReachedError, BaseExecutor -from apscheduler.executors.pool import ThreadPoolExecutor -from apscheduler.jobstores.base import ConflictingIdError, JobLookupError, BaseJobStore -from apscheduler.jobstores.memory import MemoryJobStore -from apscheduler.job import Job -from apscheduler.triggers.base import BaseTrigger -from apscheduler.util import ( - asbool, asint, astimezone, maybe_ref, timedelta_seconds, undefined, TIMEOUT_MAX) -from apscheduler.events import ( - SchedulerEvent, JobEvent, JobSubmissionEvent, EVENT_SCHEDULER_START, EVENT_SCHEDULER_SHUTDOWN, - EVENT_JOBSTORE_ADDED, EVENT_JOBSTORE_REMOVED, EVENT_ALL, EVENT_JOB_MODIFIED, EVENT_JOB_REMOVED, - EVENT_JOB_ADDED, EVENT_EXECUTOR_ADDED, EVENT_EXECUTOR_REMOVED, EVENT_ALL_JOBS_REMOVED, - EVENT_JOB_SUBMITTED, EVENT_JOB_MAX_INSTANCES, EVENT_SCHEDULER_RESUMED, EVENT_SCHEDULER_PAUSED) - -try: - from collections.abc import MutableMapping -except ImportError: - from collections import MutableMapping - -#: constant indicating a scheduler's stopped state -STATE_STOPPED = 0 -#: constant indicating a scheduler's running state (started and processing jobs) -STATE_RUNNING = 1 -#: constant indicating a scheduler's paused state (started but not processing jobs) -STATE_PAUSED = 2 - - -class BaseScheduler(six.with_metaclass(ABCMeta)): - """ - Abstract base class for all schedulers. - - Takes the following keyword arguments: - - :param str|logging.Logger logger: logger to use for the scheduler's logging (defaults to - apscheduler.scheduler) - :param str|datetime.tzinfo timezone: the default time zone (defaults to the local timezone) - :param int|float jobstore_retry_interval: the minimum number of seconds to wait between - retries in the scheduler's main loop if the job store raises an exception when getting - the list of due jobs - :param dict job_defaults: default values for newly added jobs - :param dict jobstores: a dictionary of job store alias -> job store instance or configuration - dict - :param dict executors: a dictionary of executor alias -> executor instance or configuration - dict - - :ivar int state: current running state of the scheduler (one of the following constants from - ``apscheduler.schedulers.base``: ``STATE_STOPPED``, ``STATE_RUNNING``, ``STATE_PAUSED``) - - .. seealso:: :ref:`scheduler-config` - """ - - _trigger_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.triggers')) - _trigger_classes = {} - _executor_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.executors')) - _executor_classes = {} - _jobstore_plugins = dict((ep.name, ep) for ep in iter_entry_points('apscheduler.jobstores')) - _jobstore_classes = {} - - # - # Public API - # - - def __init__(self, gconfig={}, **options): - super(BaseScheduler, self).__init__() - self._executors = {} - self._executors_lock = self._create_lock() - self._jobstores = {} - self._jobstores_lock = self._create_lock() - self._listeners = [] - self._listeners_lock = self._create_lock() - self._pending_jobs = [] - self.state = STATE_STOPPED - self.configure(gconfig, **options) - - def __getstate__(self): - raise TypeError("Schedulers cannot be serialized. Ensure that you are not passing a " - "scheduler instance as an argument to a job, or scheduling an instance " - "method where the instance contains a scheduler as an attribute.") - - def configure(self, gconfig={}, prefix='apscheduler.', **options): - """ - Reconfigures the scheduler with the given options. - - Can only be done when the scheduler isn't running. - - :param dict gconfig: a "global" configuration dictionary whose values can be overridden by - keyword arguments to this method - :param str|unicode prefix: pick only those keys from ``gconfig`` that are prefixed with - this string (pass an empty string or ``None`` to use all keys) - :raises SchedulerAlreadyRunningError: if the scheduler is already running - - """ - if self.state != STATE_STOPPED: - raise SchedulerAlreadyRunningError - - # If a non-empty prefix was given, strip it from the keys in the - # global configuration dict - if prefix: - prefixlen = len(prefix) - gconfig = dict((key[prefixlen:], value) for key, value in six.iteritems(gconfig) - if key.startswith(prefix)) - - # Create a structure from the dotted options - # (e.g. "a.b.c = d" -> {'a': {'b': {'c': 'd'}}}) - config = {} - for key, value in six.iteritems(gconfig): - parts = key.split('.') - parent = config - key = parts.pop(0) - while parts: - parent = parent.setdefault(key, {}) - key = parts.pop(0) - parent[key] = value - - # Override any options with explicit keyword arguments - config.update(options) - self._configure(config) - - def start(self, paused=False): - """ - Start the configured executors and job stores and begin processing scheduled jobs. - - :param bool paused: if ``True``, don't start job processing until :meth:`resume` is called - :raises SchedulerAlreadyRunningError: if the scheduler is already running - :raises RuntimeError: if running under uWSGI with threads disabled - - """ - if self.state != STATE_STOPPED: - raise SchedulerAlreadyRunningError - - self._check_uwsgi() - - with self._executors_lock: - # Create a default executor if nothing else is configured - if 'default' not in self._executors: - self.add_executor(self._create_default_executor(), 'default') - - # Start all the executors - for alias, executor in six.iteritems(self._executors): - executor.start(self, alias) - - with self._jobstores_lock: - # Create a default job store if nothing else is configured - if 'default' not in self._jobstores: - self.add_jobstore(self._create_default_jobstore(), 'default') - - # Start all the job stores - for alias, store in six.iteritems(self._jobstores): - store.start(self, alias) - - # Schedule all pending jobs - for job, jobstore_alias, replace_existing in self._pending_jobs: - self._real_add_job(job, jobstore_alias, replace_existing) - del self._pending_jobs[:] - - self.state = STATE_PAUSED if paused else STATE_RUNNING - self._logger.info('Scheduler started') - self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_START)) - - if not paused: - self.wakeup() - - @abstractmethod - def shutdown(self, wait=True): - """ - Shuts down the scheduler, along with its executors and job stores. - - Does not interrupt any currently running jobs. - - :param bool wait: ``True`` to wait until all currently executing jobs have finished - :raises SchedulerNotRunningError: if the scheduler has not been started yet - - """ - if self.state == STATE_STOPPED: - raise SchedulerNotRunningError - - self.state = STATE_STOPPED - - # Shut down all executors - with self._executors_lock, self._jobstores_lock: - for executor in six.itervalues(self._executors): - executor.shutdown(wait) - - # Shut down all job stores - for jobstore in six.itervalues(self._jobstores): - jobstore.shutdown() - - self._logger.info('Scheduler has been shut down') - self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_SHUTDOWN)) - - def pause(self): - """ - Pause job processing in the scheduler. - - This will prevent the scheduler from waking up to do job processing until :meth:`resume` - is called. It will not however stop any already running job processing. - - """ - if self.state == STATE_STOPPED: - raise SchedulerNotRunningError - elif self.state == STATE_RUNNING: - self.state = STATE_PAUSED - self._logger.info('Paused scheduler job processing') - self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_PAUSED)) - - def resume(self): - """Resume job processing in the scheduler.""" - if self.state == STATE_STOPPED: - raise SchedulerNotRunningError - elif self.state == STATE_PAUSED: - self.state = STATE_RUNNING - self._logger.info('Resumed scheduler job processing') - self._dispatch_event(SchedulerEvent(EVENT_SCHEDULER_RESUMED)) - self.wakeup() - - @property - def running(self): - """ - Return ``True`` if the scheduler has been started. - - This is a shortcut for ``scheduler.state != STATE_STOPPED``. - - """ - return self.state != STATE_STOPPED - - def add_executor(self, executor, alias='default', **executor_opts): - """ - Adds an executor to this scheduler. - - Any extra keyword arguments will be passed to the executor plugin's constructor, assuming - that the first argument is the name of an executor plugin. - - :param str|unicode|apscheduler.executors.base.BaseExecutor executor: either an executor - instance or the name of an executor plugin - :param str|unicode alias: alias for the scheduler - :raises ValueError: if there is already an executor by the given alias - - """ - with self._executors_lock: - if alias in self._executors: - raise ValueError('This scheduler already has an executor by the alias of "%s"' % - alias) - - if isinstance(executor, BaseExecutor): - self._executors[alias] = executor - elif isinstance(executor, six.string_types): - self._executors[alias] = executor = self._create_plugin_instance( - 'executor', executor, executor_opts) - else: - raise TypeError('Expected an executor instance or a string, got %s instead' % - executor.__class__.__name__) - - # Start the executor right away if the scheduler is running - if self.state != STATE_STOPPED: - executor.start(self, alias) - - self._dispatch_event(SchedulerEvent(EVENT_EXECUTOR_ADDED, alias)) - - def remove_executor(self, alias, shutdown=True): - """ - Removes the executor by the given alias from this scheduler. - - :param str|unicode alias: alias of the executor - :param bool shutdown: ``True`` to shut down the executor after - removing it - - """ - with self._executors_lock: - executor = self._lookup_executor(alias) - del self._executors[alias] - - if shutdown: - executor.shutdown() - - self._dispatch_event(SchedulerEvent(EVENT_EXECUTOR_REMOVED, alias)) - - def add_jobstore(self, jobstore, alias='default', **jobstore_opts): - """ - Adds a job store to this scheduler. - - Any extra keyword arguments will be passed to the job store plugin's constructor, assuming - that the first argument is the name of a job store plugin. - - :param str|unicode|apscheduler.jobstores.base.BaseJobStore jobstore: job store to be added - :param str|unicode alias: alias for the job store - :raises ValueError: if there is already a job store by the given alias - - """ - with self._jobstores_lock: - if alias in self._jobstores: - raise ValueError('This scheduler already has a job store by the alias of "%s"' % - alias) - - if isinstance(jobstore, BaseJobStore): - self._jobstores[alias] = jobstore - elif isinstance(jobstore, six.string_types): - self._jobstores[alias] = jobstore = self._create_plugin_instance( - 'jobstore', jobstore, jobstore_opts) - else: - raise TypeError('Expected a job store instance or a string, got %s instead' % - jobstore.__class__.__name__) - - # Start the job store right away if the scheduler isn't stopped - if self.state != STATE_STOPPED: - jobstore.start(self, alias) - - # Notify listeners that a new job store has been added - self._dispatch_event(SchedulerEvent(EVENT_JOBSTORE_ADDED, alias)) - - # Notify the scheduler so it can scan the new job store for jobs - if self.state != STATE_STOPPED: - self.wakeup() - - def remove_jobstore(self, alias, shutdown=True): - """ - Removes the job store by the given alias from this scheduler. - - :param str|unicode alias: alias of the job store - :param bool shutdown: ``True`` to shut down the job store after removing it - - """ - with self._jobstores_lock: - jobstore = self._lookup_jobstore(alias) - del self._jobstores[alias] - - if shutdown: - jobstore.shutdown() - - self._dispatch_event(SchedulerEvent(EVENT_JOBSTORE_REMOVED, alias)) - - def add_listener(self, callback, mask=EVENT_ALL): - """ - add_listener(callback, mask=EVENT_ALL) - - Adds a listener for scheduler events. - - When a matching event occurs, ``callback`` is executed with the event object as its - sole argument. If the ``mask`` parameter is not provided, the callback will receive events - of all types. - - :param callback: any callable that takes one argument - :param int mask: bitmask that indicates which events should be - listened to - - .. seealso:: :mod:`apscheduler.events` - .. seealso:: :ref:`scheduler-events` - - """ - with self._listeners_lock: - self._listeners.append((callback, mask)) - - def remove_listener(self, callback): - """Removes a previously added event listener.""" - - with self._listeners_lock: - for i, (cb, _) in enumerate(self._listeners): - if callback == cb: - del self._listeners[i] - - def add_job(self, func, trigger=None, args=None, kwargs=None, id=None, name=None, - misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined, - next_run_time=undefined, jobstore='default', executor='default', - replace_existing=False, **trigger_args): - """ - add_job(func, trigger=None, args=None, kwargs=None, id=None, \ - name=None, misfire_grace_time=undefined, coalesce=undefined, \ - max_instances=undefined, next_run_time=undefined, \ - jobstore='default', executor='default', \ - replace_existing=False, **trigger_args) - - Adds the given job to the job list and wakes up the scheduler if it's already running. - - Any option that defaults to ``undefined`` will be replaced with the corresponding default - value when the job is scheduled (which happens when the scheduler is started, or - immediately if the scheduler is already running). - - The ``func`` argument can be given either as a callable object or a textual reference in - the ``package.module:some.object`` format, where the first half (separated by ``:``) is an - importable module and the second half is a reference to the callable object, relative to - the module. - - The ``trigger`` argument can either be: - #. the alias name of the trigger (e.g. ``date``, ``interval`` or ``cron``), in which case - any extra keyword arguments to this method are passed on to the trigger's constructor - #. an instance of a trigger class - - :param func: callable (or a textual reference to one) to run at the given time - :param str|apscheduler.triggers.base.BaseTrigger trigger: trigger that determines when - ``func`` is called - :param list|tuple args: list of positional arguments to call func with - :param dict kwargs: dict of keyword arguments to call func with - :param str|unicode id: explicit identifier for the job (for modifying it later) - :param str|unicode name: textual description of the job - :param int misfire_grace_time: seconds after the designated runtime that the job is still - allowed to be run (or ``None`` to allow the job to run no matter how late it is) - :param bool coalesce: run once instead of many times if the scheduler determines that the - job should be run more than once in succession - :param int max_instances: maximum number of concurrently running instances allowed for this - job - :param datetime next_run_time: when to first run the job, regardless of the trigger (pass - ``None`` to add the job as paused) - :param str|unicode jobstore: alias of the job store to store the job in - :param str|unicode executor: alias of the executor to run the job with - :param bool replace_existing: ``True`` to replace an existing job with the same ``id`` - (but retain the number of runs from the existing one) - :rtype: Job - - """ - job_kwargs = { - 'trigger': self._create_trigger(trigger, trigger_args), - 'executor': executor, - 'func': func, - 'args': tuple(args) if args is not None else (), - 'kwargs': dict(kwargs) if kwargs is not None else {}, - 'id': id, - 'name': name, - 'misfire_grace_time': misfire_grace_time, - 'coalesce': coalesce, - 'max_instances': max_instances, - 'next_run_time': next_run_time - } - job_kwargs = dict((key, value) for key, value in six.iteritems(job_kwargs) if - value is not undefined) - job = Job(self, **job_kwargs) - - # Don't really add jobs to job stores before the scheduler is up and running - with self._jobstores_lock: - if self.state == STATE_STOPPED: - self._pending_jobs.append((job, jobstore, replace_existing)) - self._logger.info('Adding job tentatively -- it will be properly scheduled when ' - 'the scheduler starts') - else: - self._real_add_job(job, jobstore, replace_existing) - - return job - - def scheduled_job(self, trigger, args=None, kwargs=None, id=None, name=None, - misfire_grace_time=undefined, coalesce=undefined, max_instances=undefined, - next_run_time=undefined, jobstore='default', executor='default', - **trigger_args): - """ - scheduled_job(trigger, args=None, kwargs=None, id=None, \ - name=None, misfire_grace_time=undefined, \ - coalesce=undefined, max_instances=undefined, \ - next_run_time=undefined, jobstore='default', \ - executor='default',**trigger_args) - - A decorator version of :meth:`add_job`, except that ``replace_existing`` is always - ``True``. - - .. important:: The ``id`` argument must be given if scheduling a job in a persistent job - store. The scheduler cannot, however, enforce this requirement. - - """ - def inner(func): - self.add_job(func, trigger, args, kwargs, id, name, misfire_grace_time, coalesce, - max_instances, next_run_time, jobstore, executor, True, **trigger_args) - return func - return inner - - def modify_job(self, job_id, jobstore=None, **changes): - """ - Modifies the properties of a single job. - - Modifications are passed to this method as extra keyword arguments. - - :param str|unicode job_id: the identifier of the job - :param str|unicode jobstore: alias of the job store that contains the job - :return Job: the relevant job instance - - """ - with self._jobstores_lock: - job, jobstore = self._lookup_job(job_id, jobstore) - job._modify(**changes) - if jobstore: - self._lookup_jobstore(jobstore).update_job(job) - - self._dispatch_event(JobEvent(EVENT_JOB_MODIFIED, job_id, jobstore)) - - # Wake up the scheduler since the job's next run time may have been changed - if self.state == STATE_RUNNING: - self.wakeup() - - return job - - def reschedule_job(self, job_id, jobstore=None, trigger=None, **trigger_args): - """ - Constructs a new trigger for a job and updates its next run time. - - Extra keyword arguments are passed directly to the trigger's constructor. - - :param str|unicode job_id: the identifier of the job - :param str|unicode jobstore: alias of the job store that contains the job - :param trigger: alias of the trigger type or a trigger instance - :return Job: the relevant job instance - - """ - trigger = self._create_trigger(trigger, trigger_args) - now = datetime.now(self.timezone) - next_run_time = trigger.get_next_fire_time(None, now) - return self.modify_job(job_id, jobstore, trigger=trigger, next_run_time=next_run_time) - - def pause_job(self, job_id, jobstore=None): - """ - Causes the given job not to be executed until it is explicitly resumed. - - :param str|unicode job_id: the identifier of the job - :param str|unicode jobstore: alias of the job store that contains the job - :return Job: the relevant job instance - - """ - return self.modify_job(job_id, jobstore, next_run_time=None) - - def resume_job(self, job_id, jobstore=None): - """ - Resumes the schedule of the given job, or removes the job if its schedule is finished. - - :param str|unicode job_id: the identifier of the job - :param str|unicode jobstore: alias of the job store that contains the job - :return Job|None: the relevant job instance if the job was rescheduled, or ``None`` if no - next run time could be calculated and the job was removed - - """ - with self._jobstores_lock: - job, jobstore = self._lookup_job(job_id, jobstore) - now = datetime.now(self.timezone) - next_run_time = job.trigger.get_next_fire_time(None, now) - if next_run_time: - return self.modify_job(job_id, jobstore, next_run_time=next_run_time) - else: - self.remove_job(job.id, jobstore) - - def get_jobs(self, jobstore=None, pending=None): - """ - Returns a list of pending jobs (if the scheduler hasn't been started yet) and scheduled - jobs, either from a specific job store or from all of them. - - If the scheduler has not been started yet, only pending jobs can be returned because the - job stores haven't been started yet either. - - :param str|unicode jobstore: alias of the job store - :param bool pending: **DEPRECATED** - :rtype: list[Job] - - """ - if pending is not None: - warnings.warn('The "pending" option is deprecated -- get_jobs() always returns ' - 'scheduled jobs if the scheduler has been started and pending jobs ' - 'otherwise', DeprecationWarning) - - with self._jobstores_lock: - jobs = [] - if self.state == STATE_STOPPED: - for job, alias, replace_existing in self._pending_jobs: - if jobstore is None or alias == jobstore: - jobs.append(job) - else: - for alias, store in six.iteritems(self._jobstores): - if jobstore is None or alias == jobstore: - jobs.extend(store.get_all_jobs()) - - return jobs - - def get_job(self, job_id, jobstore=None): - """ - Returns the Job that matches the given ``job_id``. - - :param str|unicode job_id: the identifier of the job - :param str|unicode jobstore: alias of the job store that most likely contains the job - :return: the Job by the given ID, or ``None`` if it wasn't found - :rtype: Job - - """ - with self._jobstores_lock: - try: - return self._lookup_job(job_id, jobstore)[0] - except JobLookupError: - return - - def remove_job(self, job_id, jobstore=None): - """ - Removes a job, preventing it from being run any more. - - :param str|unicode job_id: the identifier of the job - :param str|unicode jobstore: alias of the job store that contains the job - :raises JobLookupError: if the job was not found - - """ - jobstore_alias = None - with self._jobstores_lock: - # Check if the job is among the pending jobs - if self.state == STATE_STOPPED: - for i, (job, alias, replace_existing) in enumerate(self._pending_jobs): - if job.id == job_id and jobstore in (None, alias): - del self._pending_jobs[i] - jobstore_alias = alias - break - else: - # Otherwise, try to remove it from each store until it succeeds or we run out of - # stores to check - for alias, store in six.iteritems(self._jobstores): - if jobstore in (None, alias): - try: - store.remove_job(job_id) - jobstore_alias = alias - break - except JobLookupError: - continue - - if jobstore_alias is None: - raise JobLookupError(job_id) - - # Notify listeners that a job has been removed - event = JobEvent(EVENT_JOB_REMOVED, job_id, jobstore_alias) - self._dispatch_event(event) - - self._logger.info('Removed job %s', job_id) - - def remove_all_jobs(self, jobstore=None): - """ - Removes all jobs from the specified job store, or all job stores if none is given. - - :param str|unicode jobstore: alias of the job store - - """ - with self._jobstores_lock: - if self.state == STATE_STOPPED: - if jobstore: - self._pending_jobs = [pending for pending in self._pending_jobs if - pending[1] != jobstore] - else: - self._pending_jobs = [] - else: - for alias, store in six.iteritems(self._jobstores): - if jobstore in (None, alias): - store.remove_all_jobs() - - self._dispatch_event(SchedulerEvent(EVENT_ALL_JOBS_REMOVED, jobstore)) - - def print_jobs(self, jobstore=None, out=None): - """ - print_jobs(jobstore=None, out=sys.stdout) - - Prints out a textual listing of all jobs currently scheduled on either all job stores or - just a specific one. - - :param str|unicode jobstore: alias of the job store, ``None`` to list jobs from all stores - :param file out: a file-like object to print to (defaults to **sys.stdout** if nothing is - given) - - """ - out = out or sys.stdout - with self._jobstores_lock: - if self.state == STATE_STOPPED: - print(u'Pending jobs:', file=out) - if self._pending_jobs: - for job, jobstore_alias, replace_existing in self._pending_jobs: - if jobstore in (None, jobstore_alias): - print(u' %s' % job, file=out) - else: - print(u' No pending jobs', file=out) - else: - for alias, store in sorted(six.iteritems(self._jobstores)): - if jobstore in (None, alias): - print(u'Jobstore %s:' % alias, file=out) - jobs = store.get_all_jobs() - if jobs: - for job in jobs: - print(u' %s' % job, file=out) - else: - print(u' No scheduled jobs', file=out) - - @abstractmethod - def wakeup(self): - """ - Notifies the scheduler that there may be jobs due for execution. - Triggers :meth:`_process_jobs` to be run in an implementation specific manner. - """ - - # - # Private API - # - - def _configure(self, config): - # Set general options - self._logger = maybe_ref(config.pop('logger', None)) or getLogger('apscheduler.scheduler') - self.timezone = astimezone(config.pop('timezone', None)) or get_localzone() - self.jobstore_retry_interval = float(config.pop('jobstore_retry_interval', 10)) - - # Set the job defaults - job_defaults = config.get('job_defaults', {}) - self._job_defaults = { - 'misfire_grace_time': asint(job_defaults.get('misfire_grace_time', 1)), - 'coalesce': asbool(job_defaults.get('coalesce', True)), - 'max_instances': asint(job_defaults.get('max_instances', 1)) - } - - # Configure executors - self._executors.clear() - for alias, value in six.iteritems(config.get('executors', {})): - if isinstance(value, BaseExecutor): - self.add_executor(value, alias) - elif isinstance(value, MutableMapping): - executor_class = value.pop('class', None) - plugin = value.pop('type', None) - if plugin: - executor = self._create_plugin_instance('executor', plugin, value) - elif executor_class: - cls = maybe_ref(executor_class) - executor = cls(**value) - else: - raise ValueError( - 'Cannot create executor "%s" -- either "type" or "class" must be defined' % - alias) - - self.add_executor(executor, alias) - else: - raise TypeError( - "Expected executor instance or dict for executors['%s'], got %s instead" % - (alias, value.__class__.__name__)) - - # Configure job stores - self._jobstores.clear() - for alias, value in six.iteritems(config.get('jobstores', {})): - if isinstance(value, BaseJobStore): - self.add_jobstore(value, alias) - elif isinstance(value, MutableMapping): - jobstore_class = value.pop('class', None) - plugin = value.pop('type', None) - if plugin: - jobstore = self._create_plugin_instance('jobstore', plugin, value) - elif jobstore_class: - cls = maybe_ref(jobstore_class) - jobstore = cls(**value) - else: - raise ValueError( - 'Cannot create job store "%s" -- either "type" or "class" must be ' - 'defined' % alias) - - self.add_jobstore(jobstore, alias) - else: - raise TypeError( - "Expected job store instance or dict for jobstores['%s'], got %s instead" % - (alias, value.__class__.__name__)) - - def _create_default_executor(self): - """Creates a default executor store, specific to the particular scheduler type.""" - return ThreadPoolExecutor() - - def _create_default_jobstore(self): - """Creates a default job store, specific to the particular scheduler type.""" - return MemoryJobStore() - - def _lookup_executor(self, alias): - """ - Returns the executor instance by the given name from the list of executors that were added - to this scheduler. - - :type alias: str - :raises KeyError: if no executor by the given alias is not found - - """ - try: - return self._executors[alias] - except KeyError: - raise KeyError('No such executor: %s' % alias) - - def _lookup_jobstore(self, alias): - """ - Returns the job store instance by the given name from the list of job stores that were - added to this scheduler. - - :type alias: str - :raises KeyError: if no job store by the given alias is not found - - """ - try: - return self._jobstores[alias] - except KeyError: - raise KeyError('No such job store: %s' % alias) - - def _lookup_job(self, job_id, jobstore_alias): - """ - Finds a job by its ID. - - :type job_id: str - :param str jobstore_alias: alias of a job store to look in - :return tuple[Job, str]: a tuple of job, jobstore alias (jobstore alias is None in case of - a pending job) - :raises JobLookupError: if no job by the given ID is found. - - """ - if self.state == STATE_STOPPED: - # Check if the job is among the pending jobs - for job, alias, replace_existing in self._pending_jobs: - if job.id == job_id: - return job, None - else: - # Look in all job stores - for alias, store in six.iteritems(self._jobstores): - if jobstore_alias in (None, alias): - job = store.lookup_job(job_id) - if job is not None: - return job, alias - - raise JobLookupError(job_id) - - def _dispatch_event(self, event): - """ - Dispatches the given event to interested listeners. - - :param SchedulerEvent event: the event to send - - """ - with self._listeners_lock: - listeners = tuple(self._listeners) - - for cb, mask in listeners: - if event.code & mask: - try: - cb(event) - except BaseException: - self._logger.exception('Error notifying listener') - - def _check_uwsgi(self): - """Check if we're running under uWSGI with threads disabled.""" - uwsgi_module = sys.modules.get('uwsgi') - if not getattr(uwsgi_module, 'has_threads', True): - raise RuntimeError('The scheduler seems to be running under uWSGI, but threads have ' - 'been disabled. You must run uWSGI with the --enable-threads ' - 'option for the scheduler to work.') - - def _real_add_job(self, job, jobstore_alias, replace_existing): - """ - :param Job job: the job to add - :param bool replace_existing: ``True`` to use update_job() in case the job already exists - in the store - - """ - # Fill in undefined values with defaults - replacements = {} - for key, value in six.iteritems(self._job_defaults): - if not hasattr(job, key): - replacements[key] = value - - # Calculate the next run time if there is none defined - if not hasattr(job, 'next_run_time'): - now = datetime.now(self.timezone) - replacements['next_run_time'] = job.trigger.get_next_fire_time(None, now) - - # Apply any replacements - job._modify(**replacements) - - # Add the job to the given job store - store = self._lookup_jobstore(jobstore_alias) - try: - store.add_job(job) - except ConflictingIdError: - if replace_existing: - store.update_job(job) - else: - raise - - # Mark the job as no longer pending - job._jobstore_alias = jobstore_alias - - # Notify listeners that a new job has been added - event = JobEvent(EVENT_JOB_ADDED, job.id, jobstore_alias) - self._dispatch_event(event) - - self._logger.info('Added job "%s" to job store "%s"', job.name, jobstore_alias) - - # Notify the scheduler about the new job - if self.state == STATE_RUNNING: - self.wakeup() - - def _create_plugin_instance(self, type_, alias, constructor_kwargs): - """Creates an instance of the given plugin type, loading the plugin first if necessary.""" - plugin_container, class_container, base_class = { - 'trigger': (self._trigger_plugins, self._trigger_classes, BaseTrigger), - 'jobstore': (self._jobstore_plugins, self._jobstore_classes, BaseJobStore), - 'executor': (self._executor_plugins, self._executor_classes, BaseExecutor) - }[type_] - - try: - plugin_cls = class_container[alias] - except KeyError: - if alias in plugin_container: - plugin_cls = class_container[alias] = plugin_container[alias].load() - if not issubclass(plugin_cls, base_class): - raise TypeError('The {0} entry point does not point to a {0} class'. - format(type_)) - else: - raise LookupError('No {0} by the name "{1}" was found'.format(type_, alias)) - - return plugin_cls(**constructor_kwargs) - - def _create_trigger(self, trigger, trigger_args): - if isinstance(trigger, BaseTrigger): - return trigger - elif trigger is None: - trigger = 'date' - elif not isinstance(trigger, six.string_types): - raise TypeError('Expected a trigger instance or string, got %s instead' % - trigger.__class__.__name__) - - # Use the scheduler's time zone if nothing else is specified - trigger_args.setdefault('timezone', self.timezone) - - # Instantiate the trigger class - return self._create_plugin_instance('trigger', trigger, trigger_args) - - def _create_lock(self): - """Creates a reentrant lock object.""" - return RLock() - - def _process_jobs(self): - """ - Iterates through jobs in every jobstore, starts jobs that are due and figures out how long - to wait for the next round. - - If the ``get_due_jobs()`` call raises an exception, a new wakeup is scheduled in at least - ``jobstore_retry_interval`` seconds. - - """ - if self.state == STATE_PAUSED: - self._logger.debug('Scheduler is paused -- not processing jobs') - return None - - self._logger.debug('Looking for jobs to run') - now = datetime.now(self.timezone) - next_wakeup_time = None - events = [] - - with self._jobstores_lock: - for jobstore_alias, jobstore in six.iteritems(self._jobstores): - try: - due_jobs = jobstore.get_due_jobs(now) - except Exception as e: - # Schedule a wakeup at least in jobstore_retry_interval seconds - self._logger.warning('Error getting due jobs from job store %r: %s', - jobstore_alias, e) - retry_wakeup_time = now + timedelta(seconds=self.jobstore_retry_interval) - if not next_wakeup_time or next_wakeup_time > retry_wakeup_time: - next_wakeup_time = retry_wakeup_time - - continue - - for job in due_jobs: - # Look up the job's executor - try: - executor = self._lookup_executor(job.executor) - except BaseException: - self._logger.error( - 'Executor lookup ("%s") failed for job "%s" -- removing it from the ' - 'job store', job.executor, job) - self.remove_job(job.id, jobstore_alias) - continue - - run_times = job._get_run_times(now) - run_times = run_times[-1:] if run_times and job.coalesce else run_times - if run_times: - try: - executor.submit_job(job, run_times) - except MaxInstancesReachedError: - self._logger.warning( - 'Execution of job "%s" skipped: maximum number of running ' - 'instances reached (%d)', job, job.max_instances) - event = JobSubmissionEvent(EVENT_JOB_MAX_INSTANCES, job.id, - jobstore_alias, run_times) - events.append(event) - except BaseException: - self._logger.exception('Error submitting job "%s" to executor "%s"', - job, job.executor) - else: - event = JobSubmissionEvent(EVENT_JOB_SUBMITTED, job.id, jobstore_alias, - run_times) - events.append(event) - - # Update the job if it has a next execution time. - # Otherwise remove it from the job store. - job_next_run = job.trigger.get_next_fire_time(run_times[-1], now) - if job_next_run: - job._modify(next_run_time=job_next_run) - jobstore.update_job(job) - else: - self.remove_job(job.id, jobstore_alias) - - # Set a new next wakeup time if there isn't one yet or - # the jobstore has an even earlier one - jobstore_next_run_time = jobstore.get_next_run_time() - if jobstore_next_run_time and (next_wakeup_time is None or - jobstore_next_run_time < next_wakeup_time): - next_wakeup_time = jobstore_next_run_time.astimezone(self.timezone) - - # Dispatch collected events - for event in events: - self._dispatch_event(event) - - # Determine the delay until this method should be called again - if self.state == STATE_PAUSED: - wait_seconds = None - self._logger.debug('Scheduler is paused; waiting until resume() is called') - elif next_wakeup_time is None: - wait_seconds = None - self._logger.debug('No jobs; waiting until a job is added') - else: - wait_seconds = min(max(timedelta_seconds(next_wakeup_time - now), 0), TIMEOUT_MAX) - self._logger.debug('Next wakeup is due at %s (in %f seconds)', next_wakeup_time, - wait_seconds) - - return wait_seconds diff --git a/telegramer/include/apscheduler/schedulers/blocking.py b/telegramer/include/apscheduler/schedulers/blocking.py deleted file mode 100644 index 4ecc9f6..0000000 --- a/telegramer/include/apscheduler/schedulers/blocking.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import absolute_import - -from threading import Event - -from apscheduler.schedulers.base import BaseScheduler, STATE_STOPPED -from apscheduler.util import TIMEOUT_MAX - - -class BlockingScheduler(BaseScheduler): - """ - A scheduler that runs in the foreground - (:meth:`~apscheduler.schedulers.base.BaseScheduler.start` will block). - """ - _event = None - - def start(self, *args, **kwargs): - if self._event is None or self._event.is_set(): - self._event = Event() - - super(BlockingScheduler, self).start(*args, **kwargs) - self._main_loop() - - def shutdown(self, wait=True): - super(BlockingScheduler, self).shutdown(wait) - self._event.set() - - def _main_loop(self): - wait_seconds = TIMEOUT_MAX - while self.state != STATE_STOPPED: - self._event.wait(wait_seconds) - self._event.clear() - wait_seconds = self._process_jobs() - - def wakeup(self): - self._event.set() diff --git a/telegramer/include/apscheduler/schedulers/gevent.py b/telegramer/include/apscheduler/schedulers/gevent.py deleted file mode 100644 index d48ed74..0000000 --- a/telegramer/include/apscheduler/schedulers/gevent.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import absolute_import - -from apscheduler.schedulers.blocking import BlockingScheduler -from apscheduler.schedulers.base import BaseScheduler - -try: - from gevent.event import Event - from gevent.lock import RLock - import gevent -except ImportError: # pragma: nocover - raise ImportError('GeventScheduler requires gevent installed') - - -class GeventScheduler(BlockingScheduler): - """A scheduler that runs as a Gevent greenlet.""" - - _greenlet = None - - def start(self, *args, **kwargs): - self._event = Event() - BaseScheduler.start(self, *args, **kwargs) - self._greenlet = gevent.spawn(self._main_loop) - return self._greenlet - - def shutdown(self, *args, **kwargs): - super(GeventScheduler, self).shutdown(*args, **kwargs) - self._greenlet.join() - del self._greenlet - - def _create_lock(self): - return RLock() - - def _create_default_executor(self): - from apscheduler.executors.gevent import GeventExecutor - return GeventExecutor() diff --git a/telegramer/include/apscheduler/schedulers/qt.py b/telegramer/include/apscheduler/schedulers/qt.py deleted file mode 100644 index 600f6e6..0000000 --- a/telegramer/include/apscheduler/schedulers/qt.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import absolute_import - -from apscheduler.schedulers.base import BaseScheduler - -try: - from PyQt5.QtCore import QObject, QTimer -except (ImportError, RuntimeError): # pragma: nocover - try: - from PyQt4.QtCore import QObject, QTimer - except ImportError: - try: - from PySide6.QtCore import QObject, QTimer # noqa - except ImportError: - try: - from PySide2.QtCore import QObject, QTimer # noqa - except ImportError: - try: - from PySide.QtCore import QObject, QTimer # noqa - except ImportError: - raise ImportError('QtScheduler requires either PyQt5, PyQt4, PySide6, PySide2 ' - 'or PySide installed') - - -class QtScheduler(BaseScheduler): - """A scheduler that runs in a Qt event loop.""" - - _timer = None - - def shutdown(self, *args, **kwargs): - super(QtScheduler, self).shutdown(*args, **kwargs) - self._stop_timer() - - def _start_timer(self, wait_seconds): - self._stop_timer() - if wait_seconds is not None: - wait_time = min(wait_seconds * 1000, 2147483647) - self._timer = QTimer.singleShot(wait_time, self._process_jobs) - - def _stop_timer(self): - if self._timer: - if self._timer.isActive(): - self._timer.stop() - del self._timer - - def wakeup(self): - self._start_timer(0) - - def _process_jobs(self): - wait_seconds = super(QtScheduler, self)._process_jobs() - self._start_timer(wait_seconds) diff --git a/telegramer/include/apscheduler/schedulers/tornado.py b/telegramer/include/apscheduler/schedulers/tornado.py deleted file mode 100644 index 0a9171f..0000000 --- a/telegramer/include/apscheduler/schedulers/tornado.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import absolute_import - -from datetime import timedelta -from functools import wraps - -from apscheduler.schedulers.base import BaseScheduler -from apscheduler.util import maybe_ref - -try: - from tornado.ioloop import IOLoop -except ImportError: # pragma: nocover - raise ImportError('TornadoScheduler requires tornado installed') - - -def run_in_ioloop(func): - @wraps(func) - def wrapper(self, *args, **kwargs): - self._ioloop.add_callback(func, self, *args, **kwargs) - return wrapper - - -class TornadoScheduler(BaseScheduler): - """ - A scheduler that runs on a Tornado IOLoop. - - The default executor can run jobs based on native coroutines (``async def``). - - =========== =============================================================== - ``io_loop`` Tornado IOLoop instance to use (defaults to the global IO loop) - =========== =============================================================== - """ - - _ioloop = None - _timeout = None - - @run_in_ioloop - def shutdown(self, wait=True): - super(TornadoScheduler, self).shutdown(wait) - self._stop_timer() - - def _configure(self, config): - self._ioloop = maybe_ref(config.pop('io_loop', None)) or IOLoop.current() - super(TornadoScheduler, self)._configure(config) - - def _start_timer(self, wait_seconds): - self._stop_timer() - if wait_seconds is not None: - self._timeout = self._ioloop.add_timeout(timedelta(seconds=wait_seconds), self.wakeup) - - def _stop_timer(self): - if self._timeout: - self._ioloop.remove_timeout(self._timeout) - del self._timeout - - def _create_default_executor(self): - from apscheduler.executors.tornado import TornadoExecutor - return TornadoExecutor() - - @run_in_ioloop - def wakeup(self): - self._stop_timer() - wait_seconds = self._process_jobs() - self._start_timer(wait_seconds) diff --git a/telegramer/include/apscheduler/schedulers/twisted.py b/telegramer/include/apscheduler/schedulers/twisted.py deleted file mode 100644 index 6b43a84..0000000 --- a/telegramer/include/apscheduler/schedulers/twisted.py +++ /dev/null @@ -1,62 +0,0 @@ -from __future__ import absolute_import - -from functools import wraps - -from apscheduler.schedulers.base import BaseScheduler -from apscheduler.util import maybe_ref - -try: - from twisted.internet import reactor as default_reactor -except ImportError: # pragma: nocover - raise ImportError('TwistedScheduler requires Twisted installed') - - -def run_in_reactor(func): - @wraps(func) - def wrapper(self, *args, **kwargs): - self._reactor.callFromThread(func, self, *args, **kwargs) - return wrapper - - -class TwistedScheduler(BaseScheduler): - """ - A scheduler that runs on a Twisted reactor. - - Extra options: - - =========== ======================================================== - ``reactor`` Reactor instance to use (defaults to the global reactor) - =========== ======================================================== - """ - - _reactor = None - _delayedcall = None - - def _configure(self, config): - self._reactor = maybe_ref(config.pop('reactor', default_reactor)) - super(TwistedScheduler, self)._configure(config) - - @run_in_reactor - def shutdown(self, wait=True): - super(TwistedScheduler, self).shutdown(wait) - self._stop_timer() - - def _start_timer(self, wait_seconds): - self._stop_timer() - if wait_seconds is not None: - self._delayedcall = self._reactor.callLater(wait_seconds, self.wakeup) - - def _stop_timer(self): - if self._delayedcall and self._delayedcall.active(): - self._delayedcall.cancel() - del self._delayedcall - - @run_in_reactor - def wakeup(self): - self._stop_timer() - wait_seconds = self._process_jobs() - self._start_timer(wait_seconds) - - def _create_default_executor(self): - from apscheduler.executors.twisted import TwistedExecutor - return TwistedExecutor() diff --git a/telegramer/include/apscheduler/triggers/__init__.py b/telegramer/include/apscheduler/triggers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/apscheduler/triggers/base.py b/telegramer/include/apscheduler/triggers/base.py deleted file mode 100644 index 55d010d..0000000 --- a/telegramer/include/apscheduler/triggers/base.py +++ /dev/null @@ -1,37 +0,0 @@ -from abc import ABCMeta, abstractmethod -from datetime import timedelta -import random - -import six - - -class BaseTrigger(six.with_metaclass(ABCMeta)): - """Abstract base class that defines the interface that every trigger must implement.""" - - __slots__ = () - - @abstractmethod - def get_next_fire_time(self, previous_fire_time, now): - """ - Returns the next datetime to fire on, If no such datetime can be calculated, returns - ``None``. - - :param datetime.datetime previous_fire_time: the previous time the trigger was fired - :param datetime.datetime now: current datetime - """ - - def _apply_jitter(self, next_fire_time, jitter, now): - """ - Randomize ``next_fire_time`` by adding a random value (the jitter). - - :param datetime.datetime|None next_fire_time: next fire time without jitter applied. If - ``None``, returns ``None``. - :param int|None jitter: maximum number of seconds to add to ``next_fire_time`` - (if ``None`` or ``0``, returns ``next_fire_time``) - :param datetime.datetime now: current datetime - :return datetime.datetime|None: next fire time with a jitter. - """ - if next_fire_time is None or not jitter: - return next_fire_time - - return next_fire_time + timedelta(seconds=random.uniform(0, jitter)) diff --git a/telegramer/include/apscheduler/triggers/combining.py b/telegramer/include/apscheduler/triggers/combining.py deleted file mode 100644 index bb90006..0000000 --- a/telegramer/include/apscheduler/triggers/combining.py +++ /dev/null @@ -1,95 +0,0 @@ -from apscheduler.triggers.base import BaseTrigger -from apscheduler.util import obj_to_ref, ref_to_obj - - -class BaseCombiningTrigger(BaseTrigger): - __slots__ = ('triggers', 'jitter') - - def __init__(self, triggers, jitter=None): - self.triggers = triggers - self.jitter = jitter - - def __getstate__(self): - return { - 'version': 1, - 'triggers': [(obj_to_ref(trigger.__class__), trigger.__getstate__()) - for trigger in self.triggers], - 'jitter': self.jitter - } - - def __setstate__(self, state): - if state.get('version', 1) > 1: - raise ValueError( - 'Got serialized data for version %s of %s, but only versions up to 1 can be ' - 'handled' % (state['version'], self.__class__.__name__)) - - self.jitter = state['jitter'] - self.triggers = [] - for clsref, state in state['triggers']: - cls = ref_to_obj(clsref) - trigger = cls.__new__(cls) - trigger.__setstate__(state) - self.triggers.append(trigger) - - def __repr__(self): - return '<{}({}{})>'.format(self.__class__.__name__, self.triggers, - ', jitter={}'.format(self.jitter) if self.jitter else '') - - -class AndTrigger(BaseCombiningTrigger): - """ - Always returns the earliest next fire time that all the given triggers can agree on. - The trigger is considered to be finished when any of the given triggers has finished its - schedule. - - Trigger alias: ``and`` - - :param list triggers: triggers to combine - :param int|None jitter: delay the job execution by ``jitter`` seconds at most - """ - - __slots__ = () - - def get_next_fire_time(self, previous_fire_time, now): - while True: - fire_times = [trigger.get_next_fire_time(previous_fire_time, now) - for trigger in self.triggers] - if None in fire_times: - return None - elif min(fire_times) == max(fire_times): - return self._apply_jitter(fire_times[0], self.jitter, now) - else: - now = max(fire_times) - - def __str__(self): - return 'and[{}]'.format(', '.join(str(trigger) for trigger in self.triggers)) - - -class OrTrigger(BaseCombiningTrigger): - """ - Always returns the earliest next fire time produced by any of the given triggers. - The trigger is considered finished when all the given triggers have finished their schedules. - - Trigger alias: ``or`` - - :param list triggers: triggers to combine - :param int|None jitter: delay the job execution by ``jitter`` seconds at most - - .. note:: Triggers that depends on the previous fire time, such as the interval trigger, may - seem to behave strangely since they are always passed the previous fire time produced by - any of the given triggers. - """ - - __slots__ = () - - def get_next_fire_time(self, previous_fire_time, now): - fire_times = [trigger.get_next_fire_time(previous_fire_time, now) - for trigger in self.triggers] - fire_times = [fire_time for fire_time in fire_times if fire_time is not None] - if fire_times: - return self._apply_jitter(min(fire_times), self.jitter, now) - else: - return None - - def __str__(self): - return 'or[{}]'.format(', '.join(str(trigger) for trigger in self.triggers)) diff --git a/telegramer/include/apscheduler/triggers/cron/__init__.py b/telegramer/include/apscheduler/triggers/cron/__init__.py deleted file mode 100644 index b5389dd..0000000 --- a/telegramer/include/apscheduler/triggers/cron/__init__.py +++ /dev/null @@ -1,239 +0,0 @@ -from datetime import datetime, timedelta - -from tzlocal import get_localzone -import six - -from apscheduler.triggers.base import BaseTrigger -from apscheduler.triggers.cron.fields import ( - BaseField, MonthField, WeekField, DayOfMonthField, DayOfWeekField, DEFAULT_VALUES) -from apscheduler.util import ( - datetime_ceil, convert_to_datetime, datetime_repr, astimezone, localize, normalize) - - -class CronTrigger(BaseTrigger): - """ - Triggers when current time matches all specified time constraints, - similarly to how the UNIX cron scheduler works. - - :param int|str year: 4-digit year - :param int|str month: month (1-12) - :param int|str day: day of month (1-31) - :param int|str week: ISO week (1-53) - :param int|str day_of_week: number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun) - :param int|str hour: hour (0-23) - :param int|str minute: minute (0-59) - :param int|str second: second (0-59) - :param datetime|str start_date: earliest possible date/time to trigger on (inclusive) - :param datetime|str end_date: latest possible date/time to trigger on (inclusive) - :param datetime.tzinfo|str timezone: time zone to use for the date/time calculations (defaults - to scheduler timezone) - :param int|None jitter: delay the job execution by ``jitter`` seconds at most - - .. note:: The first weekday is always **monday**. - """ - - FIELD_NAMES = ('year', 'month', 'day', 'week', 'day_of_week', 'hour', 'minute', 'second') - FIELDS_MAP = { - 'year': BaseField, - 'month': MonthField, - 'week': WeekField, - 'day': DayOfMonthField, - 'day_of_week': DayOfWeekField, - 'hour': BaseField, - 'minute': BaseField, - 'second': BaseField - } - - __slots__ = 'timezone', 'start_date', 'end_date', 'fields', 'jitter' - - def __init__(self, year=None, month=None, day=None, week=None, day_of_week=None, hour=None, - minute=None, second=None, start_date=None, end_date=None, timezone=None, - jitter=None): - if timezone: - self.timezone = astimezone(timezone) - elif isinstance(start_date, datetime) and start_date.tzinfo: - self.timezone = start_date.tzinfo - elif isinstance(end_date, datetime) and end_date.tzinfo: - self.timezone = end_date.tzinfo - else: - self.timezone = get_localzone() - - self.start_date = convert_to_datetime(start_date, self.timezone, 'start_date') - self.end_date = convert_to_datetime(end_date, self.timezone, 'end_date') - - self.jitter = jitter - - values = dict((key, value) for (key, value) in six.iteritems(locals()) - if key in self.FIELD_NAMES and value is not None) - self.fields = [] - assign_defaults = False - for field_name in self.FIELD_NAMES: - if field_name in values: - exprs = values.pop(field_name) - is_default = False - assign_defaults = not values - elif assign_defaults: - exprs = DEFAULT_VALUES[field_name] - is_default = True - else: - exprs = '*' - is_default = True - - field_class = self.FIELDS_MAP[field_name] - field = field_class(field_name, exprs, is_default) - self.fields.append(field) - - @classmethod - def from_crontab(cls, expr, timezone=None): - """ - Create a :class:`~CronTrigger` from a standard crontab expression. - - See https://en.wikipedia.org/wiki/Cron for more information on the format accepted here. - - :param expr: minute, hour, day of month, month, day of week - :param datetime.tzinfo|str timezone: time zone to use for the date/time calculations ( - defaults to scheduler timezone) - :return: a :class:`~CronTrigger` instance - - """ - values = expr.split() - if len(values) != 5: - raise ValueError('Wrong number of fields; got {}, expected 5'.format(len(values))) - - return cls(minute=values[0], hour=values[1], day=values[2], month=values[3], - day_of_week=values[4], timezone=timezone) - - def _increment_field_value(self, dateval, fieldnum): - """ - Increments the designated field and resets all less significant fields to their minimum - values. - - :type dateval: datetime - :type fieldnum: int - :return: a tuple containing the new date, and the number of the field that was actually - incremented - :rtype: tuple - """ - - values = {} - i = 0 - while i < len(self.fields): - field = self.fields[i] - if not field.REAL: - if i == fieldnum: - fieldnum -= 1 - i -= 1 - else: - i += 1 - continue - - if i < fieldnum: - values[field.name] = field.get_value(dateval) - i += 1 - elif i > fieldnum: - values[field.name] = field.get_min(dateval) - i += 1 - else: - value = field.get_value(dateval) - maxval = field.get_max(dateval) - if value == maxval: - fieldnum -= 1 - i -= 1 - else: - values[field.name] = value + 1 - i += 1 - - difference = datetime(**values) - dateval.replace(tzinfo=None) - return normalize(dateval + difference), fieldnum - - def _set_field_value(self, dateval, fieldnum, new_value): - values = {} - for i, field in enumerate(self.fields): - if field.REAL: - if i < fieldnum: - values[field.name] = field.get_value(dateval) - elif i > fieldnum: - values[field.name] = field.get_min(dateval) - else: - values[field.name] = new_value - - return localize(datetime(**values), self.timezone) - - def get_next_fire_time(self, previous_fire_time, now): - if previous_fire_time: - start_date = min(now, previous_fire_time + timedelta(microseconds=1)) - if start_date == previous_fire_time: - start_date += timedelta(microseconds=1) - else: - start_date = max(now, self.start_date) if self.start_date else now - - fieldnum = 0 - next_date = datetime_ceil(start_date).astimezone(self.timezone) - while 0 <= fieldnum < len(self.fields): - field = self.fields[fieldnum] - curr_value = field.get_value(next_date) - next_value = field.get_next_value(next_date) - - if next_value is None: - # No valid value was found - next_date, fieldnum = self._increment_field_value(next_date, fieldnum - 1) - elif next_value > curr_value: - # A valid, but higher than the starting value, was found - if field.REAL: - next_date = self._set_field_value(next_date, fieldnum, next_value) - fieldnum += 1 - else: - next_date, fieldnum = self._increment_field_value(next_date, fieldnum) - else: - # A valid value was found, no changes necessary - fieldnum += 1 - - # Return if the date has rolled past the end date - if self.end_date and next_date > self.end_date: - return None - - if fieldnum >= 0: - next_date = self._apply_jitter(next_date, self.jitter, now) - return min(next_date, self.end_date) if self.end_date else next_date - - def __getstate__(self): - return { - 'version': 2, - 'timezone': self.timezone, - 'start_date': self.start_date, - 'end_date': self.end_date, - 'fields': self.fields, - 'jitter': self.jitter, - } - - def __setstate__(self, state): - # This is for compatibility with APScheduler 3.0.x - if isinstance(state, tuple): - state = state[1] - - if state.get('version', 1) > 2: - raise ValueError( - 'Got serialized data for version %s of %s, but only versions up to 2 can be ' - 'handled' % (state['version'], self.__class__.__name__)) - - self.timezone = state['timezone'] - self.start_date = state['start_date'] - self.end_date = state['end_date'] - self.fields = state['fields'] - self.jitter = state.get('jitter') - - def __str__(self): - options = ["%s='%s'" % (f.name, f) for f in self.fields if not f.is_default] - return 'cron[%s]' % (', '.join(options)) - - def __repr__(self): - options = ["%s='%s'" % (f.name, f) for f in self.fields if not f.is_default] - if self.start_date: - options.append("start_date=%r" % datetime_repr(self.start_date)) - if self.end_date: - options.append("end_date=%r" % datetime_repr(self.end_date)) - if self.jitter: - options.append('jitter=%s' % self.jitter) - - return "<%s (%s, timezone='%s')>" % ( - self.__class__.__name__, ', '.join(options), self.timezone) diff --git a/telegramer/include/apscheduler/triggers/cron/expressions.py b/telegramer/include/apscheduler/triggers/cron/expressions.py deleted file mode 100644 index 55a3716..0000000 --- a/telegramer/include/apscheduler/triggers/cron/expressions.py +++ /dev/null @@ -1,251 +0,0 @@ -"""This module contains the expressions applicable for CronTrigger's fields.""" - -from calendar import monthrange -import re - -from apscheduler.util import asint - -__all__ = ('AllExpression', 'RangeExpression', 'WeekdayRangeExpression', - 'WeekdayPositionExpression', 'LastDayOfMonthExpression') - - -WEEKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] -MONTHS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] - - -class AllExpression(object): - value_re = re.compile(r'\*(?:/(?P\d+))?$') - - def __init__(self, step=None): - self.step = asint(step) - if self.step == 0: - raise ValueError('Increment must be higher than 0') - - def validate_range(self, field_name): - from apscheduler.triggers.cron.fields import MIN_VALUES, MAX_VALUES - - value_range = MAX_VALUES[field_name] - MIN_VALUES[field_name] - if self.step and self.step > value_range: - raise ValueError('the step value ({}) is higher than the total range of the ' - 'expression ({})'.format(self.step, value_range)) - - def get_next_value(self, date, field): - start = field.get_value(date) - minval = field.get_min(date) - maxval = field.get_max(date) - start = max(start, minval) - - if not self.step: - next = start - else: - distance_to_next = (self.step - (start - minval)) % self.step - next = start + distance_to_next - - if next <= maxval: - return next - - def __eq__(self, other): - return isinstance(other, self.__class__) and self.step == other.step - - def __str__(self): - if self.step: - return '*/%d' % self.step - return '*' - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, self.step) - - -class RangeExpression(AllExpression): - value_re = re.compile( - r'(?P\d+)(?:-(?P\d+))?(?:/(?P\d+))?$') - - def __init__(self, first, last=None, step=None): - super(RangeExpression, self).__init__(step) - first = asint(first) - last = asint(last) - if last is None and step is None: - last = first - if last is not None and first > last: - raise ValueError('The minimum value in a range must not be higher than the maximum') - self.first = first - self.last = last - - def validate_range(self, field_name): - from apscheduler.triggers.cron.fields import MIN_VALUES, MAX_VALUES - - super(RangeExpression, self).validate_range(field_name) - if self.first < MIN_VALUES[field_name]: - raise ValueError('the first value ({}) is lower than the minimum value ({})' - .format(self.first, MIN_VALUES[field_name])) - if self.last is not None and self.last > MAX_VALUES[field_name]: - raise ValueError('the last value ({}) is higher than the maximum value ({})' - .format(self.last, MAX_VALUES[field_name])) - value_range = (self.last or MAX_VALUES[field_name]) - self.first - if self.step and self.step > value_range: - raise ValueError('the step value ({}) is higher than the total range of the ' - 'expression ({})'.format(self.step, value_range)) - - def get_next_value(self, date, field): - startval = field.get_value(date) - minval = field.get_min(date) - maxval = field.get_max(date) - - # Apply range limits - minval = max(minval, self.first) - maxval = min(maxval, self.last) if self.last is not None else maxval - nextval = max(minval, startval) - - # Apply the step if defined - if self.step: - distance_to_next = (self.step - (nextval - minval)) % self.step - nextval += distance_to_next - - return nextval if nextval <= maxval else None - - def __eq__(self, other): - return (isinstance(other, self.__class__) and self.first == other.first and - self.last == other.last) - - def __str__(self): - if self.last != self.first and self.last is not None: - range = '%d-%d' % (self.first, self.last) - else: - range = str(self.first) - - if self.step: - return '%s/%d' % (range, self.step) - return range - - def __repr__(self): - args = [str(self.first)] - if self.last != self.first and self.last is not None or self.step: - args.append(str(self.last)) - if self.step: - args.append(str(self.step)) - return "%s(%s)" % (self.__class__.__name__, ', '.join(args)) - - -class MonthRangeExpression(RangeExpression): - value_re = re.compile(r'(?P[a-z]+)(?:-(?P[a-z]+))?', re.IGNORECASE) - - def __init__(self, first, last=None): - try: - first_num = MONTHS.index(first.lower()) + 1 - except ValueError: - raise ValueError('Invalid month name "%s"' % first) - - if last: - try: - last_num = MONTHS.index(last.lower()) + 1 - except ValueError: - raise ValueError('Invalid month name "%s"' % last) - else: - last_num = None - - super(MonthRangeExpression, self).__init__(first_num, last_num) - - def __str__(self): - if self.last != self.first and self.last is not None: - return '%s-%s' % (MONTHS[self.first - 1], MONTHS[self.last - 1]) - return MONTHS[self.first - 1] - - def __repr__(self): - args = ["'%s'" % MONTHS[self.first]] - if self.last != self.first and self.last is not None: - args.append("'%s'" % MONTHS[self.last - 1]) - return "%s(%s)" % (self.__class__.__name__, ', '.join(args)) - - -class WeekdayRangeExpression(RangeExpression): - value_re = re.compile(r'(?P[a-z]+)(?:-(?P[a-z]+))?', re.IGNORECASE) - - def __init__(self, first, last=None): - try: - first_num = WEEKDAYS.index(first.lower()) - except ValueError: - raise ValueError('Invalid weekday name "%s"' % first) - - if last: - try: - last_num = WEEKDAYS.index(last.lower()) - except ValueError: - raise ValueError('Invalid weekday name "%s"' % last) - else: - last_num = None - - super(WeekdayRangeExpression, self).__init__(first_num, last_num) - - def __str__(self): - if self.last != self.first and self.last is not None: - return '%s-%s' % (WEEKDAYS[self.first], WEEKDAYS[self.last]) - return WEEKDAYS[self.first] - - def __repr__(self): - args = ["'%s'" % WEEKDAYS[self.first]] - if self.last != self.first and self.last is not None: - args.append("'%s'" % WEEKDAYS[self.last]) - return "%s(%s)" % (self.__class__.__name__, ', '.join(args)) - - -class WeekdayPositionExpression(AllExpression): - options = ['1st', '2nd', '3rd', '4th', '5th', 'last'] - value_re = re.compile(r'(?P%s) +(?P(?:\d+|\w+))' % - '|'.join(options), re.IGNORECASE) - - def __init__(self, option_name, weekday_name): - super(WeekdayPositionExpression, self).__init__(None) - try: - self.option_num = self.options.index(option_name.lower()) - except ValueError: - raise ValueError('Invalid weekday position "%s"' % option_name) - - try: - self.weekday = WEEKDAYS.index(weekday_name.lower()) - except ValueError: - raise ValueError('Invalid weekday name "%s"' % weekday_name) - - def get_next_value(self, date, field): - # Figure out the weekday of the month's first day and the number of days in that month - first_day_wday, last_day = monthrange(date.year, date.month) - - # Calculate which day of the month is the first of the target weekdays - first_hit_day = self.weekday - first_day_wday + 1 - if first_hit_day <= 0: - first_hit_day += 7 - - # Calculate what day of the month the target weekday would be - if self.option_num < 5: - target_day = first_hit_day + self.option_num * 7 - else: - target_day = first_hit_day + ((last_day - first_hit_day) // 7) * 7 - - if target_day <= last_day and target_day >= date.day: - return target_day - - def __eq__(self, other): - return (super(WeekdayPositionExpression, self).__eq__(other) and - self.option_num == other.option_num and self.weekday == other.weekday) - - def __str__(self): - return '%s %s' % (self.options[self.option_num], WEEKDAYS[self.weekday]) - - def __repr__(self): - return "%s('%s', '%s')" % (self.__class__.__name__, self.options[self.option_num], - WEEKDAYS[self.weekday]) - - -class LastDayOfMonthExpression(AllExpression): - value_re = re.compile(r'last', re.IGNORECASE) - - def __init__(self): - super(LastDayOfMonthExpression, self).__init__(None) - - def get_next_value(self, date, field): - return monthrange(date.year, date.month)[1] - - def __str__(self): - return 'last' - - def __repr__(self): - return "%s()" % self.__class__.__name__ diff --git a/telegramer/include/apscheduler/triggers/cron/fields.py b/telegramer/include/apscheduler/triggers/cron/fields.py deleted file mode 100644 index 86d620c..0000000 --- a/telegramer/include/apscheduler/triggers/cron/fields.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Fields represent CronTrigger options which map to :class:`~datetime.datetime` fields.""" - -from calendar import monthrange -import re - -import six - -from apscheduler.triggers.cron.expressions import ( - AllExpression, RangeExpression, WeekdayPositionExpression, LastDayOfMonthExpression, - WeekdayRangeExpression, MonthRangeExpression) - - -__all__ = ('MIN_VALUES', 'MAX_VALUES', 'DEFAULT_VALUES', 'BaseField', 'WeekField', - 'DayOfMonthField', 'DayOfWeekField') - - -MIN_VALUES = {'year': 1970, 'month': 1, 'day': 1, 'week': 1, 'day_of_week': 0, 'hour': 0, - 'minute': 0, 'second': 0} -MAX_VALUES = {'year': 9999, 'month': 12, 'day': 31, 'week': 53, 'day_of_week': 6, 'hour': 23, - 'minute': 59, 'second': 59} -DEFAULT_VALUES = {'year': '*', 'month': 1, 'day': 1, 'week': '*', 'day_of_week': '*', 'hour': 0, - 'minute': 0, 'second': 0} -SEPARATOR = re.compile(' *, *') - - -class BaseField(object): - REAL = True - COMPILERS = [AllExpression, RangeExpression] - - def __init__(self, name, exprs, is_default=False): - self.name = name - self.is_default = is_default - self.compile_expressions(exprs) - - def get_min(self, dateval): - return MIN_VALUES[self.name] - - def get_max(self, dateval): - return MAX_VALUES[self.name] - - def get_value(self, dateval): - return getattr(dateval, self.name) - - def get_next_value(self, dateval): - smallest = None - for expr in self.expressions: - value = expr.get_next_value(dateval, self) - if smallest is None or (value is not None and value < smallest): - smallest = value - - return smallest - - def compile_expressions(self, exprs): - self.expressions = [] - - # Split a comma-separated expression list, if any - for expr in SEPARATOR.split(str(exprs).strip()): - self.compile_expression(expr) - - def compile_expression(self, expr): - for compiler in self.COMPILERS: - match = compiler.value_re.match(expr) - if match: - compiled_expr = compiler(**match.groupdict()) - - try: - compiled_expr.validate_range(self.name) - except ValueError as e: - exc = ValueError('Error validating expression {!r}: {}'.format(expr, e)) - six.raise_from(exc, None) - - self.expressions.append(compiled_expr) - return - - raise ValueError('Unrecognized expression "%s" for field "%s"' % (expr, self.name)) - - def __eq__(self, other): - return isinstance(self, self.__class__) and self.expressions == other.expressions - - def __str__(self): - expr_strings = (str(e) for e in self.expressions) - return ','.join(expr_strings) - - def __repr__(self): - return "%s('%s', '%s')" % (self.__class__.__name__, self.name, self) - - -class WeekField(BaseField): - REAL = False - - def get_value(self, dateval): - return dateval.isocalendar()[1] - - -class DayOfMonthField(BaseField): - COMPILERS = BaseField.COMPILERS + [WeekdayPositionExpression, LastDayOfMonthExpression] - - def get_max(self, dateval): - return monthrange(dateval.year, dateval.month)[1] - - -class DayOfWeekField(BaseField): - REAL = False - COMPILERS = BaseField.COMPILERS + [WeekdayRangeExpression] - - def get_value(self, dateval): - return dateval.weekday() - - -class MonthField(BaseField): - COMPILERS = BaseField.COMPILERS + [MonthRangeExpression] diff --git a/telegramer/include/apscheduler/triggers/date.py b/telegramer/include/apscheduler/triggers/date.py deleted file mode 100644 index 0768100..0000000 --- a/telegramer/include/apscheduler/triggers/date.py +++ /dev/null @@ -1,51 +0,0 @@ -from datetime import datetime - -from tzlocal import get_localzone - -from apscheduler.triggers.base import BaseTrigger -from apscheduler.util import convert_to_datetime, datetime_repr, astimezone - - -class DateTrigger(BaseTrigger): - """ - Triggers once on the given datetime. If ``run_date`` is left empty, current time is used. - - :param datetime|str run_date: the date/time to run the job at - :param datetime.tzinfo|str timezone: time zone for ``run_date`` if it doesn't have one already - """ - - __slots__ = 'run_date' - - def __init__(self, run_date=None, timezone=None): - timezone = astimezone(timezone) or get_localzone() - if run_date is not None: - self.run_date = convert_to_datetime(run_date, timezone, 'run_date') - else: - self.run_date = datetime.now(timezone) - - def get_next_fire_time(self, previous_fire_time, now): - return self.run_date if previous_fire_time is None else None - - def __getstate__(self): - return { - 'version': 1, - 'run_date': self.run_date - } - - def __setstate__(self, state): - # This is for compatibility with APScheduler 3.0.x - if isinstance(state, tuple): - state = state[1] - - if state.get('version', 1) > 1: - raise ValueError( - 'Got serialized data for version %s of %s, but only version 1 can be handled' % - (state['version'], self.__class__.__name__)) - - self.run_date = state['run_date'] - - def __str__(self): - return 'date[%s]' % datetime_repr(self.run_date) - - def __repr__(self): - return "<%s (run_date='%s')>" % (self.__class__.__name__, datetime_repr(self.run_date)) diff --git a/telegramer/include/apscheduler/triggers/interval.py b/telegramer/include/apscheduler/triggers/interval.py deleted file mode 100644 index b0e2dbd..0000000 --- a/telegramer/include/apscheduler/triggers/interval.py +++ /dev/null @@ -1,108 +0,0 @@ -from datetime import timedelta, datetime -from math import ceil - -from tzlocal import get_localzone - -from apscheduler.triggers.base import BaseTrigger -from apscheduler.util import ( - convert_to_datetime, normalize, timedelta_seconds, datetime_repr, - astimezone) - - -class IntervalTrigger(BaseTrigger): - """ - Triggers on specified intervals, starting on ``start_date`` if specified, ``datetime.now()`` + - interval otherwise. - - :param int weeks: number of weeks to wait - :param int days: number of days to wait - :param int hours: number of hours to wait - :param int minutes: number of minutes to wait - :param int seconds: number of seconds to wait - :param datetime|str start_date: starting point for the interval calculation - :param datetime|str end_date: latest possible date/time to trigger on - :param datetime.tzinfo|str timezone: time zone to use for the date/time calculations - :param int|None jitter: delay the job execution by ``jitter`` seconds at most - """ - - __slots__ = 'timezone', 'start_date', 'end_date', 'interval', 'interval_length', 'jitter' - - def __init__(self, weeks=0, days=0, hours=0, minutes=0, seconds=0, start_date=None, - end_date=None, timezone=None, jitter=None): - self.interval = timedelta(weeks=weeks, days=days, hours=hours, minutes=minutes, - seconds=seconds) - self.interval_length = timedelta_seconds(self.interval) - if self.interval_length == 0: - self.interval = timedelta(seconds=1) - self.interval_length = 1 - - if timezone: - self.timezone = astimezone(timezone) - elif isinstance(start_date, datetime) and start_date.tzinfo: - self.timezone = start_date.tzinfo - elif isinstance(end_date, datetime) and end_date.tzinfo: - self.timezone = end_date.tzinfo - else: - self.timezone = get_localzone() - - start_date = start_date or (datetime.now(self.timezone) + self.interval) - self.start_date = convert_to_datetime(start_date, self.timezone, 'start_date') - self.end_date = convert_to_datetime(end_date, self.timezone, 'end_date') - - self.jitter = jitter - - def get_next_fire_time(self, previous_fire_time, now): - if previous_fire_time: - next_fire_time = previous_fire_time + self.interval - elif self.start_date > now: - next_fire_time = self.start_date - else: - timediff_seconds = timedelta_seconds(now - self.start_date) - next_interval_num = int(ceil(timediff_seconds / self.interval_length)) - next_fire_time = self.start_date + self.interval * next_interval_num - - if self.jitter is not None: - next_fire_time = self._apply_jitter(next_fire_time, self.jitter, now) - - if not self.end_date or next_fire_time <= self.end_date: - return normalize(next_fire_time) - - def __getstate__(self): - return { - 'version': 2, - 'timezone': self.timezone, - 'start_date': self.start_date, - 'end_date': self.end_date, - 'interval': self.interval, - 'jitter': self.jitter, - } - - def __setstate__(self, state): - # This is for compatibility with APScheduler 3.0.x - if isinstance(state, tuple): - state = state[1] - - if state.get('version', 1) > 2: - raise ValueError( - 'Got serialized data for version %s of %s, but only versions up to 2 can be ' - 'handled' % (state['version'], self.__class__.__name__)) - - self.timezone = state['timezone'] - self.start_date = state['start_date'] - self.end_date = state['end_date'] - self.interval = state['interval'] - self.interval_length = timedelta_seconds(self.interval) - self.jitter = state.get('jitter') - - def __str__(self): - return 'interval[%s]' % str(self.interval) - - def __repr__(self): - options = ['interval=%r' % self.interval, 'start_date=%r' % datetime_repr(self.start_date)] - if self.end_date: - options.append("end_date=%r" % datetime_repr(self.end_date)) - if self.jitter: - options.append('jitter=%s' % self.jitter) - - return "<%s (%s, timezone='%s')>" % ( - self.__class__.__name__, ', '.join(options), self.timezone) diff --git a/telegramer/include/apscheduler/util.py b/telegramer/include/apscheduler/util.py deleted file mode 100644 index d929a48..0000000 --- a/telegramer/include/apscheduler/util.py +++ /dev/null @@ -1,438 +0,0 @@ -"""This module contains several handy functions primarily meant for internal use.""" - -from __future__ import division - -from datetime import date, datetime, time, timedelta, tzinfo -from calendar import timegm -from functools import partial -from inspect import isclass, ismethod -import re -import sys - -from pytz import timezone, utc, FixedOffset -import six - -try: - from inspect import signature -except ImportError: # pragma: nocover - from funcsigs import signature - -try: - from threading import TIMEOUT_MAX -except ImportError: - TIMEOUT_MAX = 4294967 # Maximum value accepted by Event.wait() on Windows - -try: - from asyncio import iscoroutinefunction -except ImportError: - try: - from trollius import iscoroutinefunction - except ImportError: - def iscoroutinefunction(func): - return False - -__all__ = ('asint', 'asbool', 'astimezone', 'convert_to_datetime', 'datetime_to_utc_timestamp', - 'utc_timestamp_to_datetime', 'timedelta_seconds', 'datetime_ceil', 'get_callable_name', - 'obj_to_ref', 'ref_to_obj', 'maybe_ref', 'repr_escape', 'check_callable_args', - 'normalize', 'localize', 'TIMEOUT_MAX') - - -class _Undefined(object): - def __nonzero__(self): - return False - - def __bool__(self): - return False - - def __repr__(self): - return '' - - -undefined = _Undefined() #: a unique object that only signifies that no value is defined - - -def asint(text): - """ - Safely converts a string to an integer, returning ``None`` if the string is ``None``. - - :type text: str - :rtype: int - - """ - if text is not None: - return int(text) - - -def asbool(obj): - """ - Interprets an object as a boolean value. - - :rtype: bool - - """ - if isinstance(obj, str): - obj = obj.strip().lower() - if obj in ('true', 'yes', 'on', 'y', 't', '1'): - return True - if obj in ('false', 'no', 'off', 'n', 'f', '0'): - return False - raise ValueError('Unable to interpret value "%s" as boolean' % obj) - return bool(obj) - - -def astimezone(obj): - """ - Interprets an object as a timezone. - - :rtype: tzinfo - - """ - if isinstance(obj, six.string_types): - return timezone(obj) - if isinstance(obj, tzinfo): - if obj.tzname(None) == 'local': - raise ValueError( - 'Unable to determine the name of the local timezone -- you must explicitly ' - 'specify the name of the local timezone. Please refrain from using timezones like ' - 'EST to prevent problems with daylight saving time. Instead, use a locale based ' - 'timezone name (such as Europe/Helsinki).') - return obj - if obj is not None: - raise TypeError('Expected tzinfo, got %s instead' % obj.__class__.__name__) - - -_DATE_REGEX = re.compile( - r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})' - r'(?:[ T](?P\d{1,2}):(?P\d{1,2}):(?P\d{1,2})' - r'(?:\.(?P\d{1,6}))?' - r'(?PZ|[+-]\d\d:\d\d)?)?$') - - -def convert_to_datetime(input, tz, arg_name): - """ - Converts the given object to a timezone aware datetime object. - - If a timezone aware datetime object is passed, it is returned unmodified. - If a native datetime object is passed, it is given the specified timezone. - If the input is a string, it is parsed as a datetime with the given timezone. - - Date strings are accepted in three different forms: date only (Y-m-d), date with time - (Y-m-d H:M:S) or with date+time with microseconds (Y-m-d H:M:S.micro). Additionally you can - override the time zone by giving a specific offset in the format specified by ISO 8601: - Z (UTC), +HH:MM or -HH:MM. - - :param str|datetime input: the datetime or string to convert to a timezone aware datetime - :param datetime.tzinfo tz: timezone to interpret ``input`` in - :param str arg_name: the name of the argument (used in an error message) - :rtype: datetime - - """ - if input is None: - return - elif isinstance(input, datetime): - datetime_ = input - elif isinstance(input, date): - datetime_ = datetime.combine(input, time()) - elif isinstance(input, six.string_types): - m = _DATE_REGEX.match(input) - if not m: - raise ValueError('Invalid date string') - - values = m.groupdict() - tzname = values.pop('timezone') - if tzname == 'Z': - tz = utc - elif tzname: - hours, minutes = (int(x) for x in tzname[1:].split(':')) - sign = 1 if tzname[0] == '+' else -1 - tz = FixedOffset(sign * (hours * 60 + minutes)) - - values = {k: int(v or 0) for k, v in values.items()} - datetime_ = datetime(**values) - else: - raise TypeError('Unsupported type for %s: %s' % (arg_name, input.__class__.__name__)) - - if datetime_.tzinfo is not None: - return datetime_ - if tz is None: - raise ValueError( - 'The "tz" argument must be specified if %s has no timezone information' % arg_name) - if isinstance(tz, six.string_types): - tz = timezone(tz) - - return localize(datetime_, tz) - - -def datetime_to_utc_timestamp(timeval): - """ - Converts a datetime instance to a timestamp. - - :type timeval: datetime - :rtype: float - - """ - if timeval is not None: - return timegm(timeval.utctimetuple()) + timeval.microsecond / 1000000 - - -def utc_timestamp_to_datetime(timestamp): - """ - Converts the given timestamp to a datetime instance. - - :type timestamp: float - :rtype: datetime - - """ - if timestamp is not None: - return datetime.fromtimestamp(timestamp, utc) - - -def timedelta_seconds(delta): - """ - Converts the given timedelta to seconds. - - :type delta: timedelta - :rtype: float - - """ - return delta.days * 24 * 60 * 60 + delta.seconds + \ - delta.microseconds / 1000000.0 - - -def datetime_ceil(dateval): - """ - Rounds the given datetime object upwards. - - :type dateval: datetime - - """ - if dateval.microsecond > 0: - return dateval + timedelta(seconds=1, microseconds=-dateval.microsecond) - return dateval - - -def datetime_repr(dateval): - return dateval.strftime('%Y-%m-%d %H:%M:%S %Z') if dateval else 'None' - - -def get_callable_name(func): - """ - Returns the best available display name for the given function/callable. - - :rtype: str - - """ - # the easy case (on Python 3.3+) - if hasattr(func, '__qualname__'): - return func.__qualname__ - - # class methods, bound and unbound methods - f_self = getattr(func, '__self__', None) or getattr(func, 'im_self', None) - if f_self and hasattr(func, '__name__'): - f_class = f_self if isclass(f_self) else f_self.__class__ - else: - f_class = getattr(func, 'im_class', None) - - if f_class and hasattr(func, '__name__'): - return '%s.%s' % (f_class.__name__, func.__name__) - - # class or class instance - if hasattr(func, '__call__'): - # class - if hasattr(func, '__name__'): - return func.__name__ - - # instance of a class with a __call__ method - return func.__class__.__name__ - - raise TypeError('Unable to determine a name for %r -- maybe it is not a callable?' % func) - - -def obj_to_ref(obj): - """ - Returns the path to the given callable. - - :rtype: str - :raises TypeError: if the given object is not callable - :raises ValueError: if the given object is a :class:`~functools.partial`, lambda or a nested - function - - """ - if isinstance(obj, partial): - raise ValueError('Cannot create a reference to a partial()') - - name = get_callable_name(obj) - if '' in name: - raise ValueError('Cannot create a reference to a lambda') - if '' in name: - raise ValueError('Cannot create a reference to a nested function') - - if ismethod(obj): - if hasattr(obj, 'im_self') and obj.im_self: - # bound method - module = obj.im_self.__module__ - elif hasattr(obj, 'im_class') and obj.im_class: - # unbound method - module = obj.im_class.__module__ - else: - module = obj.__module__ - else: - module = obj.__module__ - return '%s:%s' % (module, name) - - -def ref_to_obj(ref): - """ - Returns the object pointed to by ``ref``. - - :type ref: str - - """ - if not isinstance(ref, six.string_types): - raise TypeError('References must be strings') - if ':' not in ref: - raise ValueError('Invalid reference') - - modulename, rest = ref.split(':', 1) - try: - obj = __import__(modulename, fromlist=[rest]) - except ImportError: - raise LookupError('Error resolving reference %s: could not import module' % ref) - - try: - for name in rest.split('.'): - obj = getattr(obj, name) - return obj - except Exception: - raise LookupError('Error resolving reference %s: error looking up object' % ref) - - -def maybe_ref(ref): - """ - Returns the object that the given reference points to, if it is indeed a reference. - If it is not a reference, the object is returned as-is. - - """ - if not isinstance(ref, str): - return ref - return ref_to_obj(ref) - - -if six.PY2: - def repr_escape(string): - if isinstance(string, six.text_type): - return string.encode('ascii', 'backslashreplace') - return string -else: - def repr_escape(string): - return string - - -def check_callable_args(func, args, kwargs): - """ - Ensures that the given callable can be called with the given arguments. - - :type args: tuple - :type kwargs: dict - - """ - pos_kwargs_conflicts = [] # parameters that have a match in both args and kwargs - positional_only_kwargs = [] # positional-only parameters that have a match in kwargs - unsatisfied_args = [] # parameters in signature that don't have a match in args or kwargs - unsatisfied_kwargs = [] # keyword-only arguments that don't have a match in kwargs - unmatched_args = list(args) # args that didn't match any of the parameters in the signature - # kwargs that didn't match any of the parameters in the signature - unmatched_kwargs = list(kwargs) - # indicates if the signature defines *args and **kwargs respectively - has_varargs = has_var_kwargs = False - - try: - if sys.version_info >= (3, 5): - sig = signature(func, follow_wrapped=False) - else: - sig = signature(func) - except ValueError: - # signature() doesn't work against every kind of callable - return - - for param in six.itervalues(sig.parameters): - if param.kind == param.POSITIONAL_OR_KEYWORD: - if param.name in unmatched_kwargs and unmatched_args: - pos_kwargs_conflicts.append(param.name) - elif unmatched_args: - del unmatched_args[0] - elif param.name in unmatched_kwargs: - unmatched_kwargs.remove(param.name) - elif param.default is param.empty: - unsatisfied_args.append(param.name) - elif param.kind == param.POSITIONAL_ONLY: - if unmatched_args: - del unmatched_args[0] - elif param.name in unmatched_kwargs: - unmatched_kwargs.remove(param.name) - positional_only_kwargs.append(param.name) - elif param.default is param.empty: - unsatisfied_args.append(param.name) - elif param.kind == param.KEYWORD_ONLY: - if param.name in unmatched_kwargs: - unmatched_kwargs.remove(param.name) - elif param.default is param.empty: - unsatisfied_kwargs.append(param.name) - elif param.kind == param.VAR_POSITIONAL: - has_varargs = True - elif param.kind == param.VAR_KEYWORD: - has_var_kwargs = True - - # Make sure there are no conflicts between args and kwargs - if pos_kwargs_conflicts: - raise ValueError('The following arguments are supplied in both args and kwargs: %s' % - ', '.join(pos_kwargs_conflicts)) - - # Check if keyword arguments are being fed to positional-only parameters - if positional_only_kwargs: - raise ValueError('The following arguments cannot be given as keyword arguments: %s' % - ', '.join(positional_only_kwargs)) - - # Check that the number of positional arguments minus the number of matched kwargs matches the - # argspec - if unsatisfied_args: - raise ValueError('The following arguments have not been supplied: %s' % - ', '.join(unsatisfied_args)) - - # Check that all keyword-only arguments have been supplied - if unsatisfied_kwargs: - raise ValueError( - 'The following keyword-only arguments have not been supplied in kwargs: %s' % - ', '.join(unsatisfied_kwargs)) - - # Check that the callable can accept the given number of positional arguments - if not has_varargs and unmatched_args: - raise ValueError( - 'The list of positional arguments is longer than the target callable can handle ' - '(allowed: %d, given in args: %d)' % (len(args) - len(unmatched_args), len(args))) - - # Check that the callable can accept the given keyword arguments - if not has_var_kwargs and unmatched_kwargs: - raise ValueError( - 'The target callable does not accept the following keyword arguments: %s' % - ', '.join(unmatched_kwargs)) - - -def iscoroutinefunction_partial(f): - while isinstance(f, partial): - f = f.func - - # The asyncio version of iscoroutinefunction includes testing for @coroutine - # decorations vs. the inspect version which does not. - return iscoroutinefunction(f) - - -def normalize(dt): - return datetime.fromtimestamp(dt.timestamp(), dt.tzinfo) - - -def localize(dt, tzinfo): - if hasattr(tzinfo, 'localize'): - return tzinfo.localize(dt) - - return normalize(dt.replace(tzinfo=tzinfo)) diff --git a/telegramer/include/cachetools/__init__.py b/telegramer/include/cachetools/__init__.py deleted file mode 100644 index 03b6f25..0000000 --- a/telegramer/include/cachetools/__init__.py +++ /dev/null @@ -1,718 +0,0 @@ -"""Extensible memoizing collections and decorators.""" - -__all__ = ( - "Cache", - "FIFOCache", - "LFUCache", - "LRUCache", - "MRUCache", - "RRCache", - "TLRUCache", - "TTLCache", - "cached", - "cachedmethod", -) - -__version__ = "5.0.0" - -import collections -import collections.abc -import functools -import heapq -import random -import time - -from .keys import hashkey as _defaultkey - - -def _methodkey(_, *args, **kwargs): - return _defaultkey(*args, **kwargs) - - -class _DefaultSize: - - __slots__ = () - - def __getitem__(self, _): - return 1 - - def __setitem__(self, _, value): - assert value == 1 - - def pop(self, _): - return 1 - - -class Cache(collections.abc.MutableMapping): - """Mutable mapping to serve as a simple cache or cache base class.""" - - __marker = object() - - __size = _DefaultSize() - - def __init__(self, maxsize, getsizeof=None): - if getsizeof: - self.getsizeof = getsizeof - if self.getsizeof is not Cache.getsizeof: - self.__size = dict() - self.__data = dict() - self.__currsize = 0 - self.__maxsize = maxsize - - def __repr__(self): - return "%s(%s, maxsize=%r, currsize=%r)" % ( - self.__class__.__name__, - repr(self.__data), - self.__maxsize, - self.__currsize, - ) - - def __getitem__(self, key): - try: - return self.__data[key] - except KeyError: - return self.__missing__(key) - - def __setitem__(self, key, value): - maxsize = self.__maxsize - size = self.getsizeof(value) - if size > maxsize: - raise ValueError("value too large") - if key not in self.__data or self.__size[key] < size: - while self.__currsize + size > maxsize: - self.popitem() - if key in self.__data: - diffsize = size - self.__size[key] - else: - diffsize = size - self.__data[key] = value - self.__size[key] = size - self.__currsize += diffsize - - def __delitem__(self, key): - size = self.__size.pop(key) - del self.__data[key] - self.__currsize -= size - - def __contains__(self, key): - return key in self.__data - - def __missing__(self, key): - raise KeyError(key) - - def __iter__(self): - return iter(self.__data) - - def __len__(self): - return len(self.__data) - - def get(self, key, default=None): - if key in self: - return self[key] - else: - return default - - def pop(self, key, default=__marker): - if key in self: - value = self[key] - del self[key] - elif default is self.__marker: - raise KeyError(key) - else: - value = default - return value - - def setdefault(self, key, default=None): - if key in self: - value = self[key] - else: - self[key] = value = default - return value - - @property - def maxsize(self): - """The maximum size of the cache.""" - return self.__maxsize - - @property - def currsize(self): - """The current size of the cache.""" - return self.__currsize - - @staticmethod - def getsizeof(value): - """Return the size of a cache element's value.""" - return 1 - - -class FIFOCache(Cache): - """First In First Out (FIFO) cache implementation.""" - - def __init__(self, maxsize, getsizeof=None): - Cache.__init__(self, maxsize, getsizeof) - self.__order = collections.OrderedDict() - - def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): - cache_setitem(self, key, value) - try: - self.__order.move_to_end(key) - except KeyError: - self.__order[key] = None - - def __delitem__(self, key, cache_delitem=Cache.__delitem__): - cache_delitem(self, key) - del self.__order[key] - - def popitem(self): - """Remove and return the `(key, value)` pair first inserted.""" - try: - key = next(iter(self.__order)) - except StopIteration: - raise KeyError("%s is empty" % type(self).__name__) from None - else: - return (key, self.pop(key)) - - -class LFUCache(Cache): - """Least Frequently Used (LFU) cache implementation.""" - - def __init__(self, maxsize, getsizeof=None): - Cache.__init__(self, maxsize, getsizeof) - self.__counter = collections.Counter() - - def __getitem__(self, key, cache_getitem=Cache.__getitem__): - value = cache_getitem(self, key) - if key in self: # __missing__ may not store item - self.__counter[key] -= 1 - return value - - def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): - cache_setitem(self, key, value) - self.__counter[key] -= 1 - - def __delitem__(self, key, cache_delitem=Cache.__delitem__): - cache_delitem(self, key) - del self.__counter[key] - - def popitem(self): - """Remove and return the `(key, value)` pair least frequently used.""" - try: - ((key, _),) = self.__counter.most_common(1) - except ValueError: - raise KeyError("%s is empty" % type(self).__name__) from None - else: - return (key, self.pop(key)) - - -class LRUCache(Cache): - """Least Recently Used (LRU) cache implementation.""" - - def __init__(self, maxsize, getsizeof=None): - Cache.__init__(self, maxsize, getsizeof) - self.__order = collections.OrderedDict() - - def __getitem__(self, key, cache_getitem=Cache.__getitem__): - value = cache_getitem(self, key) - if key in self: # __missing__ may not store item - self.__update(key) - return value - - def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): - cache_setitem(self, key, value) - self.__update(key) - - def __delitem__(self, key, cache_delitem=Cache.__delitem__): - cache_delitem(self, key) - del self.__order[key] - - def popitem(self): - """Remove and return the `(key, value)` pair least recently used.""" - try: - key = next(iter(self.__order)) - except StopIteration: - raise KeyError("%s is empty" % type(self).__name__) from None - else: - return (key, self.pop(key)) - - def __update(self, key): - try: - self.__order.move_to_end(key) - except KeyError: - self.__order[key] = None - - -class MRUCache(Cache): - """Most Recently Used (MRU) cache implementation.""" - - def __init__(self, maxsize, getsizeof=None): - Cache.__init__(self, maxsize, getsizeof) - self.__order = collections.OrderedDict() - - def __getitem__(self, key, cache_getitem=Cache.__getitem__): - value = cache_getitem(self, key) - if key in self: # __missing__ may not store item - self.__update(key) - return value - - def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): - cache_setitem(self, key, value) - self.__update(key) - - def __delitem__(self, key, cache_delitem=Cache.__delitem__): - cache_delitem(self, key) - del self.__order[key] - - def popitem(self): - """Remove and return the `(key, value)` pair most recently used.""" - try: - key = next(iter(self.__order)) - except StopIteration: - raise KeyError("%s is empty" % type(self).__name__) from None - else: - return (key, self.pop(key)) - - def __update(self, key): - try: - self.__order.move_to_end(key, last=False) - except KeyError: - self.__order[key] = None - - -class RRCache(Cache): - """Random Replacement (RR) cache implementation.""" - - def __init__(self, maxsize, choice=random.choice, getsizeof=None): - Cache.__init__(self, maxsize, getsizeof) - self.__choice = choice - - @property - def choice(self): - """The `choice` function used by the cache.""" - return self.__choice - - def popitem(self): - """Remove and return a random `(key, value)` pair.""" - try: - key = self.__choice(list(self)) - except IndexError: - raise KeyError("%s is empty" % type(self).__name__) from None - else: - return (key, self.pop(key)) - - -class _TimedCache(Cache): - """Base class for time aware cache implementations.""" - - class _Timer: - def __init__(self, timer): - self.__timer = timer - self.__nesting = 0 - - def __call__(self): - if self.__nesting == 0: - return self.__timer() - else: - return self.__time - - def __enter__(self): - if self.__nesting == 0: - self.__time = time = self.__timer() - else: - time = self.__time - self.__nesting += 1 - return time - - def __exit__(self, *exc): - self.__nesting -= 1 - - def __reduce__(self): - return _TimedCache._Timer, (self.__timer,) - - def __getattr__(self, name): - return getattr(self.__timer, name) - - def __init__(self, maxsize, timer=time.monotonic, getsizeof=None): - Cache.__init__(self, maxsize, getsizeof) - self.__timer = _TimedCache._Timer(timer) - - def __repr__(self, cache_repr=Cache.__repr__): - with self.__timer as time: - self.expire(time) - return cache_repr(self) - - def __len__(self, cache_len=Cache.__len__): - with self.__timer as time: - self.expire(time) - return cache_len(self) - - @property - def currsize(self): - with self.__timer as time: - self.expire(time) - return super().currsize - - @property - def timer(self): - """The timer function used by the cache.""" - return self.__timer - - def clear(self): - with self.__timer as time: - self.expire(time) - Cache.clear(self) - - def get(self, *args, **kwargs): - with self.__timer: - return Cache.get(self, *args, **kwargs) - - def pop(self, *args, **kwargs): - with self.__timer: - return Cache.pop(self, *args, **kwargs) - - def setdefault(self, *args, **kwargs): - with self.__timer: - return Cache.setdefault(self, *args, **kwargs) - - -class TTLCache(_TimedCache): - """LRU Cache implementation with per-item time-to-live (TTL) value.""" - - class _Link: - - __slots__ = ("key", "expires", "next", "prev") - - def __init__(self, key=None, expires=None): - self.key = key - self.expires = expires - - def __reduce__(self): - return TTLCache._Link, (self.key, self.expires) - - def unlink(self): - next = self.next - prev = self.prev - prev.next = next - next.prev = prev - - def __init__(self, maxsize, ttl, timer=time.monotonic, getsizeof=None): - _TimedCache.__init__(self, maxsize, timer, getsizeof) - self.__root = root = TTLCache._Link() - root.prev = root.next = root - self.__links = collections.OrderedDict() - self.__ttl = ttl - - def __contains__(self, key): - try: - link = self.__links[key] # no reordering - except KeyError: - return False - else: - return self.timer() < link.expires - - def __getitem__(self, key, cache_getitem=Cache.__getitem__): - try: - link = self.__getlink(key) - except KeyError: - expired = False - else: - expired = not (self.timer() < link.expires) - if expired: - return self.__missing__(key) - else: - return cache_getitem(self, key) - - def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): - with self.timer as time: - self.expire(time) - cache_setitem(self, key, value) - try: - link = self.__getlink(key) - except KeyError: - self.__links[key] = link = TTLCache._Link(key) - else: - link.unlink() - link.expires = time + self.__ttl - link.next = root = self.__root - link.prev = prev = root.prev - prev.next = root.prev = link - - def __delitem__(self, key, cache_delitem=Cache.__delitem__): - cache_delitem(self, key) - link = self.__links.pop(key) - link.unlink() - if not (self.timer() < link.expires): - raise KeyError(key) - - def __iter__(self): - root = self.__root - curr = root.next - while curr is not root: - # "freeze" time for iterator access - with self.timer as time: - if time < curr.expires: - yield curr.key - curr = curr.next - - def __setstate__(self, state): - self.__dict__.update(state) - root = self.__root - root.prev = root.next = root - for link in sorted(self.__links.values(), key=lambda obj: obj.expires): - link.next = root - link.prev = prev = root.prev - prev.next = root.prev = link - self.expire(self.timer()) - - @property - def ttl(self): - """The time-to-live value of the cache's items.""" - return self.__ttl - - def expire(self, time=None): - """Remove expired items from the cache.""" - if time is None: - time = self.timer() - root = self.__root - curr = root.next - links = self.__links - cache_delitem = Cache.__delitem__ - while curr is not root and not (time < curr.expires): - cache_delitem(self, curr.key) - del links[curr.key] - next = curr.next - curr.unlink() - curr = next - - def popitem(self): - """Remove and return the `(key, value)` pair least recently used that - has not already expired. - - """ - with self.timer as time: - self.expire(time) - try: - key = next(iter(self.__links)) - except StopIteration: - raise KeyError("%s is empty" % type(self).__name__) from None - else: - return (key, self.pop(key)) - - def __getlink(self, key): - value = self.__links[key] - self.__links.move_to_end(key) - return value - - -class TLRUCache(_TimedCache): - """Time aware Least Recently Used (TLRU) cache implementation.""" - - @functools.total_ordering - class _Item: - - __slots__ = ("key", "expires", "removed") - - def __init__(self, key=None, expires=None): - self.key = key - self.expires = expires - self.removed = False - - def __lt__(self, other): - return self.expires < other.expires - - def __init__(self, maxsize, ttu, timer=time.monotonic, getsizeof=None): - _TimedCache.__init__(self, maxsize, timer, getsizeof) - self.__items = collections.OrderedDict() - self.__order = [] - self.__ttu = ttu - - def __contains__(self, key): - try: - item = self.__items[key] # no reordering - except KeyError: - return False - else: - return self.timer() < item.expires - - def __getitem__(self, key, cache_getitem=Cache.__getitem__): - try: - item = self.__getitem(key) - except KeyError: - expired = False - else: - expired = not (self.timer() < item.expires) - if expired: - return self.__missing__(key) - else: - return cache_getitem(self, key) - - def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): - with self.timer as time: - expires = self.__ttu(key, value, time) - if not (time < expires): - return # skip expired items - self.expire(time) - cache_setitem(self, key, value) - # removing an existing item would break the heap structure, so - # only mark it as removed for now - try: - self.__getitem(key).removed = True - except KeyError: - pass - self.__items[key] = item = TLRUCache._Item(key, expires) - heapq.heappush(self.__order, item) - - def __delitem__(self, key, cache_delitem=Cache.__delitem__): - with self.timer as time: - # no self.expire() for performance reasons, e.g. self.clear() [#67] - cache_delitem(self, key) - item = self.__items.pop(key) - item.removed = True - if not (time < item.expires): - raise KeyError(key) - - def __iter__(self): - for curr in self.__order: - # "freeze" time for iterator access - with self.timer as time: - if time < curr.expires and not curr.removed: - yield curr.key - - @property - def ttu(self): - """The local time-to-use function used by the cache.""" - return self.__ttu - - def expire(self, time=None): - """Remove expired items from the cache.""" - if time is None: - time = self.timer() - items = self.__items - order = self.__order - # clean up the heap if too many items are marked as removed - if len(order) > len(items) * 2: - self.__order = order = [item for item in order if not item.removed] - heapq.heapify(order) - cache_delitem = Cache.__delitem__ - while order and (order[0].removed or not (time < order[0].expires)): - item = heapq.heappop(order) - if not item.removed: - cache_delitem(self, item.key) - del items[item.key] - - def popitem(self): - """Remove and return the `(key, value)` pair least recently used that - has not already expired. - - """ - with self.timer as time: - self.expire(time) - try: - key = next(iter(self.__items)) - except StopIteration: - raise KeyError("%s is empty" % self.__class__.__name__) from None - else: - return (key, self.pop(key)) - - def __getitem(self, key): - value = self.__items[key] - self.__items.move_to_end(key) - return value - - -def cached(cache, key=_defaultkey, lock=None): - """Decorator to wrap a function with a memoizing callable that saves - results in a cache. - - """ - - def decorator(func): - if cache is None: - - def wrapper(*args, **kwargs): - return func(*args, **kwargs) - - elif lock is None: - - def wrapper(*args, **kwargs): - k = key(*args, **kwargs) - try: - return cache[k] - except KeyError: - pass # key not found - v = func(*args, **kwargs) - try: - cache[k] = v - except ValueError: - pass # value too large - return v - - else: - - def wrapper(*args, **kwargs): - k = key(*args, **kwargs) - try: - with lock: - return cache[k] - except KeyError: - pass # key not found - v = func(*args, **kwargs) - # in case of a race, prefer the item already in the cache - try: - with lock: - return cache.setdefault(k, v) - except ValueError: - return v # value too large - - return functools.update_wrapper(wrapper, func) - - return decorator - - -def cachedmethod(cache, key=_methodkey, lock=None): - """Decorator to wrap a class or instance method with a memoizing - callable that saves results in a cache. - - """ - - def decorator(method): - if lock is None: - - def wrapper(self, *args, **kwargs): - c = cache(self) - if c is None: - return method(self, *args, **kwargs) - k = key(self, *args, **kwargs) - try: - return c[k] - except KeyError: - pass # key not found - v = method(self, *args, **kwargs) - try: - c[k] = v - except ValueError: - pass # value too large - return v - - else: - - def wrapper(self, *args, **kwargs): - c = cache(self) - if c is None: - return method(self, *args, **kwargs) - k = key(self, *args, **kwargs) - try: - with lock(self): - return c[k] - except KeyError: - pass # key not found - v = method(self, *args, **kwargs) - # in case of a race, prefer the item already in the cache - try: - with lock(self): - return c.setdefault(k, v) - except ValueError: - return v # value too large - - return functools.update_wrapper(wrapper, method) - - return decorator diff --git a/telegramer/include/cachetools/func.py b/telegramer/include/cachetools/func.py deleted file mode 100644 index 01702c2..0000000 --- a/telegramer/include/cachetools/func.py +++ /dev/null @@ -1,171 +0,0 @@ -"""`functools.lru_cache` compatible memoizing function decorators.""" - -__all__ = ("fifo_cache", "lfu_cache", "lru_cache", "mru_cache", "rr_cache", "ttl_cache") - -import collections -import functools -import math -import random -import time - -try: - from threading import RLock -except ImportError: # pragma: no cover - from dummy_threading import RLock - -from . import FIFOCache, LFUCache, LRUCache, MRUCache, RRCache, TTLCache -from . import keys - - -_CacheInfo = collections.namedtuple( - "CacheInfo", ["hits", "misses", "maxsize", "currsize"] -) - - -class _UnboundCache(dict): - @property - def maxsize(self): - return None - - @property - def currsize(self): - return len(self) - - -class _UnboundTTLCache(TTLCache): - def __init__(self, ttl, timer): - TTLCache.__init__(self, math.inf, ttl, timer) - - @property - def maxsize(self): - return None - - -def _cache(cache, typed): - maxsize = cache.maxsize - - def decorator(func): - key = keys.typedkey if typed else keys.hashkey - lock = RLock() - stats = [0, 0] - - def wrapper(*args, **kwargs): - k = key(*args, **kwargs) - with lock: - try: - v = cache[k] - stats[0] += 1 - return v - except KeyError: - stats[1] += 1 - v = func(*args, **kwargs) - # in case of a race, prefer the item already in the cache - try: - with lock: - return cache.setdefault(k, v) - except ValueError: - return v # value too large - - def cache_info(): - with lock: - hits, misses = stats - maxsize = cache.maxsize - currsize = cache.currsize - return _CacheInfo(hits, misses, maxsize, currsize) - - def cache_clear(): - with lock: - try: - cache.clear() - finally: - stats[:] = [0, 0] - - wrapper.cache_info = cache_info - wrapper.cache_clear = cache_clear - wrapper.cache_parameters = lambda: {"maxsize": maxsize, "typed": typed} - functools.update_wrapper(wrapper, func) - return wrapper - - return decorator - - -def fifo_cache(maxsize=128, typed=False): - """Decorator to wrap a function with a memoizing callable that saves - up to `maxsize` results based on a First In First Out (FIFO) - algorithm. - - """ - if maxsize is None: - return _cache(_UnboundCache(), typed) - elif callable(maxsize): - return _cache(FIFOCache(128), typed)(maxsize) - else: - return _cache(FIFOCache(maxsize), typed) - - -def lfu_cache(maxsize=128, typed=False): - """Decorator to wrap a function with a memoizing callable that saves - up to `maxsize` results based on a Least Frequently Used (LFU) - algorithm. - - """ - if maxsize is None: - return _cache(_UnboundCache(), typed) - elif callable(maxsize): - return _cache(LFUCache(128), typed)(maxsize) - else: - return _cache(LFUCache(maxsize), typed) - - -def lru_cache(maxsize=128, typed=False): - """Decorator to wrap a function with a memoizing callable that saves - up to `maxsize` results based on a Least Recently Used (LRU) - algorithm. - - """ - if maxsize is None: - return _cache(_UnboundCache(), typed) - elif callable(maxsize): - return _cache(LRUCache(128), typed)(maxsize) - else: - return _cache(LRUCache(maxsize), typed) - - -def mru_cache(maxsize=128, typed=False): - """Decorator to wrap a function with a memoizing callable that saves - up to `maxsize` results based on a Most Recently Used (MRU) - algorithm. - """ - if maxsize is None: - return _cache(_UnboundCache(), typed) - elif callable(maxsize): - return _cache(MRUCache(128), typed)(maxsize) - else: - return _cache(MRUCache(maxsize), typed) - - -def rr_cache(maxsize=128, choice=random.choice, typed=False): - """Decorator to wrap a function with a memoizing callable that saves - up to `maxsize` results based on a Random Replacement (RR) - algorithm. - - """ - if maxsize is None: - return _cache(_UnboundCache(), typed) - elif callable(maxsize): - return _cache(RRCache(128, choice), typed)(maxsize) - else: - return _cache(RRCache(maxsize, choice), typed) - - -def ttl_cache(maxsize=128, ttl=600, timer=time.monotonic, typed=False): - """Decorator to wrap a function with a memoizing callable that saves - up to `maxsize` results based on a Least Recently Used (LRU) - algorithm with a per-item time-to-live (TTL) value. - """ - if maxsize is None: - return _cache(_UnboundTTLCache(ttl, timer), typed) - elif callable(maxsize): - return _cache(TTLCache(128, ttl, timer), typed)(maxsize) - else: - return _cache(TTLCache(maxsize, ttl, timer), typed) diff --git a/telegramer/include/cachetools/keys.py b/telegramer/include/cachetools/keys.py deleted file mode 100644 index 13630a4..0000000 --- a/telegramer/include/cachetools/keys.py +++ /dev/null @@ -1,52 +0,0 @@ -"""Key functions for memoizing decorators.""" - -__all__ = ("hashkey", "typedkey") - - -class _HashedTuple(tuple): - """A tuple that ensures that hash() will be called no more than once - per element, since cache decorators will hash the key multiple - times on a cache miss. See also _HashedSeq in the standard - library functools implementation. - - """ - - __hashvalue = None - - def __hash__(self, hash=tuple.__hash__): - hashvalue = self.__hashvalue - if hashvalue is None: - self.__hashvalue = hashvalue = hash(self) - return hashvalue - - def __add__(self, other, add=tuple.__add__): - return _HashedTuple(add(self, other)) - - def __radd__(self, other, add=tuple.__add__): - return _HashedTuple(add(other, self)) - - def __getstate__(self): - return {} - - -# used for separating keyword arguments; we do not use an object -# instance here so identity is preserved when pickling/unpickling -_kwmark = (_HashedTuple,) - - -def hashkey(*args, **kwargs): - """Return a cache key for the specified hashable arguments.""" - - if kwargs: - return _HashedTuple(args + sum(sorted(kwargs.items()), _kwmark)) - else: - return _HashedTuple(args) - - -def typedkey(*args, **kwargs): - """Return a typed cache key for the specified hashable arguments.""" - - key = hashkey(*args, **kwargs) - key += tuple(type(v) for v in args) - key += tuple(type(v) for _, v in sorted(kwargs.items())) - return key diff --git a/telegramer/include/certifi/__init__.py b/telegramer/include/certifi/__init__.py deleted file mode 100644 index 705f416..0000000 --- a/telegramer/include/certifi/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import contents, where - -__all__ = ["contents", "where"] -__version__ = "2023.05.07" diff --git a/telegramer/include/certifi/__main__.py b/telegramer/include/certifi/__main__.py deleted file mode 100644 index 8945b5d..0000000 --- a/telegramer/include/certifi/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import argparse - -from certifi import contents, where - -parser = argparse.ArgumentParser() -parser.add_argument("-c", "--contents", action="store_true") -args = parser.parse_args() - -if args.contents: - print(contents()) -else: - print(where()) diff --git a/telegramer/include/certifi/cacert.pem b/telegramer/include/certifi/cacert.pem deleted file mode 100644 index 5183934..0000000 --- a/telegramer/include/certifi/cacert.pem +++ /dev/null @@ -1,4589 +0,0 @@ - -# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA -# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA -# Label: "GlobalSign Root CA" -# Serial: 4835703278459707669005204 -# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a -# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c -# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- - -# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited -# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited -# Label: "Entrust.net Premium 2048 Secure Server CA" -# Serial: 946069240 -# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 -# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 -# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML -RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp -bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 -IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 -MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 -LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp -YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG -A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq -K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe -sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX -MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT -XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ -HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH -4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub -j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo -U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf -zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b -u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ -bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er -fF6adulZkMV8gzURZVE= ------END CERTIFICATE----- - -# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust -# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust -# Label: "Baltimore CyberTrust Root" -# Serial: 33554617 -# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 -# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 -# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- - -# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. -# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. -# Label: "Entrust Root Certification Authority" -# Serial: 1164660820 -# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 -# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 -# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c ------BEGIN CERTIFICATE----- -MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 -Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW -KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl -cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw -NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw -NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy -ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV -BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo -Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 -4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 -KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI -rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi -94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB -sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi -gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo -kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE -vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA -A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t -O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua -AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP -9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ -eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m -0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE----- - -# Issuer: CN=AAA Certificate Services O=Comodo CA Limited -# Subject: CN=AAA Certificate Services O=Comodo CA Limited -# Label: "Comodo AAA Services root" -# Serial: 1 -# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 -# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 -# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 ------BEGIN CERTIFICATE----- -MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj -YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM -GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua -BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe -3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 -YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR -rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm -ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU -oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF -MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v -QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t -b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF -AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q -GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz -Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 -G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi -l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 -smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited -# Label: "QuoVadis Root CA 2" -# Serial: 1289 -# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b -# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 -# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 ------BEGIN CERTIFICATE----- -MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x -GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv -b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV -BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W -YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa -GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg -Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J -WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB -rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp -+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 -ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i -Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz -PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og -/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH -oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI -yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud -EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 -A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL -MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT -ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f -BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn -g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl -fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K -WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha -B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc -hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR -TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD -mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z -ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y -4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza -8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 3" -# Serial: 1478 -# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf -# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 -# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 ------BEGIN CERTIFICATE----- -MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x -GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv -b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV -BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W -YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM -V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB -4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr -H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd -8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv -vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT -mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe -btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc -T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt -WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ -c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A -4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD -VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG -CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 -aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 -aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu -dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw -czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G -A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC -TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg -Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 -7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem -d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd -+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B -4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN -t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x -DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 -k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s -zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j -Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT -mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK -4SVhM7JZG+Ju1zdXtg2pEto= ------END CERTIFICATE----- - -# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 -# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 -# Label: "Security Communication Root CA" -# Serial: 0 -# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a -# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 -# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY -MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t -dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 -WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD -VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 -9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ -DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 -Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N -QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ -xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G -A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG -kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr -Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 -Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU -JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot -RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== ------END CERTIFICATE----- - -# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com -# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com -# Label: "XRamp Global CA Root" -# Serial: 107108908803651509692980124233745014957 -# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 -# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 -# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB -gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk -MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY -UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx -NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 -dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy -dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 -38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP -KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q -DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 -qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa -JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi -PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P -BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs -jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 -eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD -ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR -vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt -qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa -IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy -i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ -O+7ETPTsJ3xCwnR8gooJybQDJbw= ------END CERTIFICATE----- - -# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority -# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority -# Label: "Go Daddy Class 2 CA" -# Serial: 0 -# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 -# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 -# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh -MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE -YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 -MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo -ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg -MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN -ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA -PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w -wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi -EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY -avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ -YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE -sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h -/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 -IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD -ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy -OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P -TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ -HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER -dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf -ReYNnyicsbkqWletNw+vHX/bvZ8= ------END CERTIFICATE----- - -# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority -# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority -# Label: "Starfield Class 2 CA" -# Serial: 0 -# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 -# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a -# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl -MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp -U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw -NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE -ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp -ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 -DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf -8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN -+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 -X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa -K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA -1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G -A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR -zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 -YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD -bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 -L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D -eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl -xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp -VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY -WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root CA" -# Serial: 17154717934120587862167794914071425081 -# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 -# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 -# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c ------BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c -JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP -mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ -wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 -VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ -AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB -AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun -pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC -dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf -fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm -NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx -H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe -+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root CA" -# Serial: 10944719598952040374951832963794454346 -# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e -# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 -# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- - -# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert High Assurance EV Root CA" -# Serial: 3553400076410547919724730734378100087 -# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a -# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 -# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- - -# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG -# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG -# Label: "SwissSign Gold CA - G2" -# Serial: 13492815561806991280 -# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 -# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 -# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 ------BEGIN CERTIFICATE----- -MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln -biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF -MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT -d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 -76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ -bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c -6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE -emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd -MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt -MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y -MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y -FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi -aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM -gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB -qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 -lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn -8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov -L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 -45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO -UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 -O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC -bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv -GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a -77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC -hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 -92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp -Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w -ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt -Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ ------END CERTIFICATE----- - -# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG -# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG -# Label: "SwissSign Silver CA - G2" -# Serial: 5700383053117599563 -# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 -# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb -# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 ------BEGIN CERTIFICATE----- -MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu -IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow -RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY -U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv -Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br -YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF -nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH -6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt -eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ -c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ -MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH -HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf -jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 -5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB -rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU -F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c -wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 -cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB -AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp -WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 -xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ -2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ -IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 -aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X -em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR -dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ -OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ -hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy -tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u ------END CERTIFICATE----- - -# Issuer: CN=SecureTrust CA O=SecureTrust Corporation -# Subject: CN=SecureTrust CA O=SecureTrust Corporation -# Label: "SecureTrust CA" -# Serial: 17199774589125277788362757014266862032 -# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 -# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 -# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 ------BEGIN CERTIFICATE----- -MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x -FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz -MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv -cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz -Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO -0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao -wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj -7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS -8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT -BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg -JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC -NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 -6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ -3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm -D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS -CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR -3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= ------END CERTIFICATE----- - -# Issuer: CN=Secure Global CA O=SecureTrust Corporation -# Subject: CN=Secure Global CA O=SecureTrust Corporation -# Label: "Secure Global CA" -# Serial: 9751836167731051554232119481456978597 -# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de -# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b -# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 ------BEGIN CERTIFICATE----- -MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x -GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx -MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg -Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ -iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa -/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ -jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI -HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 -sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w -gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw -KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG -AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L -URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO -H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm -I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY -iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc -f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW ------END CERTIFICATE----- - -# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO Certification Authority O=COMODO CA Limited -# Label: "COMODO Certification Authority" -# Serial: 104350513648249232941998508985834464573 -# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 -# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b -# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 ------BEGIN CERTIFICATE----- -MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB -gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV -BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw -MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl -YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P -RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 -UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI -2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 -Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp -+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ -DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O -nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW -/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g -PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u -QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY -SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv -IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ -RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 -zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd -BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB -ZQ== ------END CERTIFICATE----- - -# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited -# Label: "COMODO ECC Certification Authority" -# Serial: 41578283867086692638256921589707938090 -# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 -# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 -# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 ------BEGIN CERTIFICATE----- -MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT -IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw -MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy -ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N -T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR -FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J -cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW -BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm -fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv -GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE----- - -# Issuer: CN=Certigna O=Dhimyotis -# Subject: CN=Certigna O=Dhimyotis -# Label: "Certigna" -# Serial: 18364802974209362175 -# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff -# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 -# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d ------BEGIN CERTIFICATE----- -MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV -BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X -DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ -BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 -QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny -gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw -zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q -130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 -JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw -DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw -ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT -AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj -AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG -9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h -bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc -fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu -HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w -t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw -WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== ------END CERTIFICATE----- - -# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority -# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority -# Label: "ePKI Root Certification Authority" -# Serial: 28956088682735189655030529057352760477 -# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 -# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 -# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 ------BEGIN CERTIFICATE----- -MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe -MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 -ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe -Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw -IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL -SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH -SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh -ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X -DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 -TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ -fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA -sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU -WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS -nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH -dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip -NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC -AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF -MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH -ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB -uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl -PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP -JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ -gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 -j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 -5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB -o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS -/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z -Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE -W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D -hNQ+IIX3Sj0rnP0qCglN6oH4EZw= ------END CERTIFICATE----- - -# Issuer: O=certSIGN OU=certSIGN ROOT CA -# Subject: O=certSIGN OU=certSIGN ROOT CA -# Label: "certSIGN ROOT CA" -# Serial: 35210227249154 -# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 -# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b -# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb ------BEGIN CERTIFICATE----- -MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT -AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD -QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP -MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do -0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ -UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d -RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ -OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv -JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C -AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O -BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ -LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY -MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ -44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I -Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw -i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN -9u6wWk5JRFRYX0KD ------END CERTIFICATE----- - -# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) -# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) -# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" -# Serial: 80544274841616 -# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 -# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 -# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG -EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 -MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl -cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR -dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB -pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM -b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm -aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz -IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT -lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz -AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 -VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG -ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 -BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG -AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M -U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh -bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C -+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC -bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F -uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 -XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= ------END CERTIFICATE----- - -# Issuer: CN=Hongkong Post Root CA 1 O=Hongkong Post -# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post -# Label: "Hongkong Post Root CA 1" -# Serial: 1000 -# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca -# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 -# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 ------BEGIN CERTIFICATE----- -MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx -FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg -Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG -A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr -b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ -jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn -PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh -ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 -nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h -q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED -MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC -mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 -7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB -oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs -EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO -fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi -AmvZWg== ------END CERTIFICATE----- - -# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. -# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. -# Label: "SecureSign RootCA11" -# Serial: 1 -# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 -# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 -# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 ------BEGIN CERTIFICATE----- -MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr -MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG -A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 -MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp -Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD -QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz -i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 -h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV -MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 -UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni -8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC -h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD -VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB -AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm -KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ -X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr -QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 -pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN -QSdJQO7e5iNEOdyhIta6A/I= ------END CERTIFICATE----- - -# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. -# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. -# Label: "Microsec e-Szigno Root CA 2009" -# Serial: 14014712776195784473 -# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 -# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e -# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 ------BEGIN CERTIFICATE----- -MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD -VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 -ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G -CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y -OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx -FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp -Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o -dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP -kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc -cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U -fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 -N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC -xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 -+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G -A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM -Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG -SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h -mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk -ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 -tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c -2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t -HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 -# Label: "GlobalSign Root CA - R3" -# Serial: 4835703278459759426209954 -# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 -# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad -# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 -MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 -RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT -gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm -KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd -QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ -XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o -LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU -RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp -jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK -6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX -mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs -Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH -WD9f ------END CERTIFICATE----- - -# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" -# Serial: 6047274297262753887 -# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 -# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa -# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE -BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h -cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy -MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg -Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 -thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM -cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG -L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i -NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h -X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b -m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy -Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja -EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T -KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF -6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh -OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD -VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD -VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp -cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv -ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl -AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF -661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 -am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 -ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 -PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS -3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k -SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF -3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM -ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g -StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz -Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB -jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V ------END CERTIFICATE----- - -# Issuer: CN=Izenpe.com O=IZENPE S.A. -# Subject: CN=Izenpe.com O=IZENPE S.A. -# Label: "Izenpe.com" -# Serial: 917563065490389241595536686991402621 -# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 -# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 -# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f ------BEGIN CERTIFICATE----- -MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 -MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 -ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD -VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j -b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq -scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO -xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H -LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX -uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD -yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ -JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q -rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN -BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L -hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB -QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ -HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu -Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg -QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB -BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx -MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA -A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb -laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 -awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo -JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw -LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT -VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk -LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb -UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ -QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ -naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls -QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== ------END CERTIFICATE----- - -# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. -# Label: "Go Daddy Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 -# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b -# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz -NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE -AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD -E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH -/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy -DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh -GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR -tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX -WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu -9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr -gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo -2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI -4uJEvlz36hz1 ------END CERTIFICATE----- - -# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Label: "Starfield Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 -# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e -# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw -MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp -Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg -nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 -HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N -Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN -dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 -HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G -CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU -sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 -4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg -8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 -mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- - -# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. -# Label: "Starfield Services Root Certificate Authority - G2" -# Serial: 0 -# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 -# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f -# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs -ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 -MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD -VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy -ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy -dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p -OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 -8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K -Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe -hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk -6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q -AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI -bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB -ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z -qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd -iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn -0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN -sSi6 ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Commercial O=AffirmTrust -# Subject: CN=AffirmTrust Commercial O=AffirmTrust -# Label: "AffirmTrust Commercial" -# Serial: 8608355977964138876 -# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 -# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 -# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP -Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr -ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL -MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 -yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr -VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ -nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG -XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj -vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt -Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g -N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC -nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Networking O=AffirmTrust -# Subject: CN=AffirmTrust Networking O=AffirmTrust -# Label: "AffirmTrust Networking" -# Serial: 8957382827206547757 -# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f -# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f -# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y -YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua -kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL -QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp -6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG -yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i -QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO -tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu -QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ -Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u -olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 -x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Premium O=AffirmTrust -# Subject: CN=AffirmTrust Premium O=AffirmTrust -# Label: "AffirmTrust Premium" -# Serial: 7893706540734352110 -# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 -# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 -# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz -dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG -A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U -cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf -qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ -JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ -+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS -s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 -HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 -70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG -V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S -qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S -5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia -C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX -OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE -FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 -KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B -8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ -MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc -0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ -u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF -u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH -YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 -GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO -RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e -KeC2uAloGRwYQw== ------END CERTIFICATE----- - -# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust -# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust -# Label: "AffirmTrust Premium ECC" -# Serial: 8401224907861490260 -# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d -# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb -# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC -VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ -cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ -BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt -VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D -0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 -ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G -A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs -aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I -flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE----- - -# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Network CA" -# Serial: 279744 -# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 -# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e -# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM -MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D -ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU -cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 -WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg -Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw -IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH -UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM -TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU -BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM -kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x -AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV -HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y -sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL -I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 -J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY -VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE----- - -# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA -# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA -# Label: "TWCA Root Certification Authority" -# Serial: 1 -# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 -# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 -# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES -MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU -V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz -WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO -LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE -AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH -K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX -RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z -rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx -3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq -hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC -MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls -XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D -lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn -aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ -YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== ------END CERTIFICATE----- - -# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 -# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 -# Label: "Security Communication RootCA2" -# Serial: 0 -# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 -# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 -# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl -MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe -U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX -DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy -dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj -YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV -OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr -zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM -VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ -hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO -ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw -awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs -OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 -DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF -coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc -okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 -t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy -1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ -SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 ------END CERTIFICATE----- - -# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 -# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 -# Label: "Actalis Authentication Root CA" -# Serial: 6271844772424770508 -# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 -# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac -# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 ------BEGIN CERTIFICATE----- -MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE -BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w -MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 -IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC -SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 -ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv -UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX -4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 -KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ -gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb -rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ -51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F -be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe -KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F -v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn -fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 -jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz -ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt -ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL -e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 -jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz -WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V -SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j -pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX -X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok -fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R -K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU -ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU -LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT -LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== ------END CERTIFICATE----- - -# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 -# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 -# Label: "Buypass Class 2 Root CA" -# Serial: 2 -# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 -# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 -# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg -Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow -TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw -HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr -6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV -L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 -1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx -MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ -QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB -arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr -Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi -FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS -P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN -9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP -AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz -uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h -9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s -A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t -OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo -+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 -KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 -DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us -H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ -I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 -5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h -3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz -Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= ------END CERTIFICATE----- - -# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 -# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 -# Label: "Buypass Class 3 Root CA" -# Serial: 2 -# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec -# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 -# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg -Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow -TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw -HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y -ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E -N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 -tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX -0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c -/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X -KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY -zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS -O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D -34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP -K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 -AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv -Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj -QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV -cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS -IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 -HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa -O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv -033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u -dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE -kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 -3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD -u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq -4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= ------END CERTIFICATE----- - -# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Label: "T-TeleSec GlobalRoot Class 3" -# Serial: 1 -# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef -# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 -# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx -KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd -BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl -YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 -OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy -aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 -ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN -8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ -RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 -hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 -ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM -EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 -A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy -WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ -1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 -6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT -91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml -e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p -TpPDpFQUWw== ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH -# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH -# Label: "D-TRUST Root Class 3 CA 2 2009" -# Serial: 623603 -# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f -# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 -# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 ------BEGIN CERTIFICATE----- -MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD -bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha -ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM -HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 -UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 -tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R -ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM -lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp -/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G -A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G -A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj -dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy -MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl -cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js -L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL -BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni -acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 -o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K -zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 -PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y -Johw1+qRzT65ysCQblrGXnRl11z+o+I= ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH -# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH -# Label: "D-TRUST Root Class 3 CA 2 EV 2009" -# Serial: 623604 -# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 -# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 -# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD -bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw -NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV -BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn -ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 -3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z -qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR -p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 -HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw -ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea -HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw -Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh -c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E -RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt -dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku -Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp -3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 -nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF -CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na -xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX -KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 ------END CERTIFICATE----- - -# Issuer: CN=CA Disig Root R2 O=Disig a.s. -# Subject: CN=CA Disig Root R2 O=Disig a.s. -# Label: "CA Disig Root R2" -# Serial: 10572350602393338211 -# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 -# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 -# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV -BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu -MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy -MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx -EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw -ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe -NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH -PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I -x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe -QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR -yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO -QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 -H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ -QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD -i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs -nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 -rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud -DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI -hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM -tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf -GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb -lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka -+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal -TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i -nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 -gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr -G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os -zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x -L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL ------END CERTIFICATE----- - -# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV -# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV -# Label: "ACCVRAIZ1" -# Serial: 6828503384748696800 -# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 -# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 -# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 ------BEGIN CERTIFICATE----- -MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE -AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw -CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ -BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND -VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb -qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY -HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo -G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA -lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr -IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ -0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH -k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 -4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO -m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa -cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl -uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI -KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls -ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG -AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 -VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT -VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG -CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA -cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA -QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA -7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA -cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA -QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA -czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu -aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt -aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud -DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF -BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp -D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU -JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m -AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD -vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms -tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH -7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h -I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA -h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF -d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H -pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 ------END CERTIFICATE----- - -# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA -# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA -# Label: "TWCA Global Root CA" -# Serial: 3262 -# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 -# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 -# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx -EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT -VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 -NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT -B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF -10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz -0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh -MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH -zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc -46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 -yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi -laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP -oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA -BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE -qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm -4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL -1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn -LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF -H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo -RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ -nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh -15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW -6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW -nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j -wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz -aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy -KwbQBM0= ------END CERTIFICATE----- - -# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera -# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera -# Label: "TeliaSonera Root CA v1" -# Serial: 199041966741090107964904287217786801558 -# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c -# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 -# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 ------BEGIN CERTIFICATE----- -MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw -NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv -b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD -VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F -VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 -7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X -Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ -/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs -81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm -dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe -Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu -sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 -pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs -slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ -arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD -VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG -9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl -dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx -0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj -TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed -Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 -Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI -OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 -vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW -t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn -HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx -SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= ------END CERTIFICATE----- - -# Issuer: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi -# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi -# Label: "E-Tugra Certification Authority" -# Serial: 7667447206703254355 -# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 -# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 -# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV -BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC -aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV -BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 -Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz -MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ -BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp -em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN -ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY -B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH -D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF -Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo -q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D -k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH -fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut -dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM -ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 -zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn -rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX -U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 -Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 -XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF -Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR -HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY -GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c -77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 -+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK -vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 -FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl -yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P -AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD -y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d -NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== ------END CERTIFICATE----- - -# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center -# Label: "T-TeleSec GlobalRoot Class 2" -# Serial: 1 -# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a -# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 -# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx -KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd -BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl -YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 -OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy -aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 -ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd -AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC -FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi -1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq -jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ -wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ -WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy -NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC -uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw -IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 -g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN -9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP -BSeOE6Fuwg== ------END CERTIFICATE----- - -# Issuer: CN=Atos TrustedRoot 2011 O=Atos -# Subject: CN=Atos TrustedRoot 2011 O=Atos -# Label: "Atos TrustedRoot 2011" -# Serial: 6643877497813316402 -# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 -# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 -# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE -AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG -EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM -FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC -REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp -Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM -VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ -SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ -4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L -cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi -eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV -HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG -A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 -DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j -vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP -DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc -maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D -lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv -KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 1 G3" -# Serial: 687049649626669250736271037606554624078720034195 -# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab -# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 -# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 -MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV -wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe -rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 -68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh -4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp -UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o -abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc -3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G -KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt -hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO -Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt -zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD -ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC -MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 -cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN -qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 -YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv -b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 -8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k -NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj -ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp -q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt -nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 2 G3" -# Serial: 390156079458959257446133169266079962026824725800 -# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 -# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 -# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 -MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf -qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW -n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym -c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ -O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 -o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j -IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq -IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz -8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh -vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l -7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG -cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD -ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 -AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC -roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga -W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n -lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE -+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV -csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd -dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg -KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM -HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 -WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M ------END CERTIFICATE----- - -# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited -# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited -# Label: "QuoVadis Root CA 3 G3" -# Serial: 268090761170461462463995952157327242137089239581 -# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 -# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d -# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 -MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR -/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu -FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR -U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c -ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR -FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k -A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw -eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl -sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp -VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q -A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ -ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD -ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px -KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI -FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv -oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg -u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP -0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf -3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl -8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ -DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN -PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ -ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root G2" -# Serial: 15385348160840213938643033620894905419 -# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d -# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f -# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 ------BEGIN CERTIFICATE----- -MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA -n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc -biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp -EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA -bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu -YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB -AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW -BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI -QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I -0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni -lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 -B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv -ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo -IhNzbM8m9Yop5w== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Assured ID Root G3" -# Serial: 15459312981008553731928384953135426796 -# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb -# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 -# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 ------BEGIN CERTIFICATE----- -MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg -RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf -Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q -RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD -AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY -JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv -6pZjamVFkpUBtA== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G2" -# Serial: 4293743540046975378534879503202253541 -# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 -# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 -# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f ------BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH -MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI -2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx -1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ -q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz -tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ -vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV -5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY -1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 -NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG -Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 -8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe -pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Global Root G3" -# Serial: 7089244469030293291760083333884364146 -# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca -# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e -# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 ------BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe -Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw -EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG -fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO -Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd -BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx -AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ -oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 -sycX ------END CERTIFICATE----- - -# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com -# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com -# Label: "DigiCert Trusted Root G4" -# Serial: 7451500558977370777930084869016614236 -# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 -# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 -# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 ------BEGIN CERTIFICATE----- -MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg -RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y -ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If -xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV -ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO -DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ -jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ -CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi -EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM -fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY -uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK -chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t -9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD -ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 -SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd -+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc -fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa -sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N -cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N -0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie -4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI -r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 -/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm -gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ ------END CERTIFICATE----- - -# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited -# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited -# Label: "COMODO RSA Certification Authority" -# Serial: 101909084537582093308941363524873193117 -# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 -# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 -# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 ------BEGIN CERTIFICATE----- -MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB -hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV -BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT -EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR -6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X -pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC -9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV -/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf -Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z -+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w -qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah -SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC -u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf -Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq -crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E -FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB -/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl -wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM -4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV -2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna -FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ -CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK -boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke -jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL -S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb -QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl -0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB -NVOFBkpdn627G190 ------END CERTIFICATE----- - -# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network -# Label: "USERTrust RSA Certification Authority" -# Serial: 2645093764781058787591871645665788717 -# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 -# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e -# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 ------BEGIN CERTIFICATE----- -MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw -MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B -3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY -tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ -Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 -VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT -79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 -c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT -Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l -c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee -UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE -Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd -BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF -Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO -VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 -ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs -8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR -iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze -Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ -XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ -qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB -VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB -L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG -jjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE----- - -# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network -# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network -# Label: "USERTrust ECC Certification Authority" -# Serial: 123013823720199481456569720443997572134 -# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 -# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 -# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a ------BEGIN CERTIFICATE----- -MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL -MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl -eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT -JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT -Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg -VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo -I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng -o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G -A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB -zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW -RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 -# Label: "GlobalSign ECC Root CA - R5" -# Serial: 32785792099990507226680698011560947931244 -# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 -# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa -# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 ------BEGIN CERTIFICATE----- -MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc -8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke -hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI -KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg -515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO -xwy8p2Fp8fc74SrL+SvzZpA3 ------END CERTIFICATE----- - -# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust -# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust -# Label: "IdenTrust Commercial Root CA 1" -# Serial: 13298821034946342390520003877796839426 -# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 -# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 -# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu -VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw -MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw -JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT -3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU -+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp -S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 -bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi -T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL -vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK -Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK -dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT -c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv -l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N -iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD -ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH -6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt -LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 -nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 -+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK -W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT -AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq -l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG -4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ -mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A -7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H ------END CERTIFICATE----- - -# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust -# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust -# Label: "IdenTrust Public Sector Root CA 1" -# Serial: 13298821034946342390521976156843933698 -# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba -# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd -# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f ------BEGIN CERTIFICATE----- -MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu -VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN -MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 -MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 -ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy -RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS -bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF -/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R -3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw -EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy -9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V -GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ -2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV -WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD -W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN -AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj -t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV -DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 -TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G -lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW -mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df -WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 -+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ -tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA -GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv -8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c ------END CERTIFICATE----- - -# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - G2" -# Serial: 1246989352 -# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 -# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 -# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 ------BEGIN CERTIFICATE----- -MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 -cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs -IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz -dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy -NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu -dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt -dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 -aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T -RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN -cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW -wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 -U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 -jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN -BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ -jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ -Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v -1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R -nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH -VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== ------END CERTIFICATE----- - -# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - EC1" -# Serial: 51543124481930649114116133369 -# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc -# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 -# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 ------BEGIN CERTIFICATE----- -MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG -A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 -d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu -dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq -RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy -MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD -VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 -L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g -Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi -A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt -ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH -Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC -R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX -hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G ------END CERTIFICATE----- - -# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority -# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority -# Label: "CFCA EV ROOT" -# Serial: 407555286 -# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 -# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 -# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd ------BEGIN CERTIFICATE----- -MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y -aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx -MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j -aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP -T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 -sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL -TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 -/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp -7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz -EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt -hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP -a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot -aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg -TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV -PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv -cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL -tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd -BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB -ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT -ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL -jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS -ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy -P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 -xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d -Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN -5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe -/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z -AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ -5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su ------END CERTIFICATE----- - -# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed -# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed -# Label: "OISTE WISeKey Global Root GB CA" -# Serial: 157768595616588414422159278966750757568 -# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d -# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed -# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 ------BEGIN CERTIFICATE----- -MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt -MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg -Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i -YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x -CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG -b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh -bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 -HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx -WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX -1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk -u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P -99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r -M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB -BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh -cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 -gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO -ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf -aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic -Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= ------END CERTIFICATE----- - -# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. -# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. -# Label: "SZAFIR ROOT CA2" -# Serial: 357043034767186914217277344587386743377558296292 -# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 -# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de -# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe ------BEGIN CERTIFICATE----- -MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL -BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 -ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw -NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L -cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg -Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN -QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT -3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw -3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 -3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 -BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN -XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF -AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw -8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG -nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP -oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy -d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg -LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== ------END CERTIFICATE----- - -# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Network CA 2" -# Serial: 44979900017204383099463764357512596969 -# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 -# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 -# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 ------BEGIN CERTIFICATE----- -MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB -gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu -QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG -A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz -OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ -VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 -b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA -DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn -0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB -OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE -fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E -Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m -o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i -sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW -OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez -Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS -adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n -3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ -F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf -CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 -XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm -djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ -WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb -AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq -P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko -b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj -XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P -5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi -DrW5viSP ------END CERTIFICATE----- - -# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Label: "Hellenic Academic and Research Institutions RootCA 2015" -# Serial: 0 -# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce -# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 -# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 ------BEGIN CERTIFICATE----- -MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix -DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k -IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT -N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v -dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG -A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh -ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx -QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 -dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA -4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 -AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 -4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C -ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV -9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD -gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 -Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq -NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko -LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc -Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd -ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I -XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI -M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot -9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V -Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea -j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh -X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ -l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf -bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 -pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK -e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 -vm9qp/UsQu0yrbYhnr68 ------END CERTIFICATE----- - -# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority -# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" -# Serial: 0 -# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef -# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 -# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 ------BEGIN CERTIFICATE----- -MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN -BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl -c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl -bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv -b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ -BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj -YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 -MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 -dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg -QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa -jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi -C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep -lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof -TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR ------END CERTIFICATE----- - -# Issuer: CN=ISRG Root X1 O=Internet Security Research Group -# Subject: CN=ISRG Root X1 O=Internet Security Research Group -# Label: "ISRG Root X1" -# Serial: 172886928669790476064670243504169061120 -# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e -# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 -# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- - -# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM -# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM -# Label: "AC RAIZ FNMT-RCM" -# Serial: 485876308206448804701554682760554759 -# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d -# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 -# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx -CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ -WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ -BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG -Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ -yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf -BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz -WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF -tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z -374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC -IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL -mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 -wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS -MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 -ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet -UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H -YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 -LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD -nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 -RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM -LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf -77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N -JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm -fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp -6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp -1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B -9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok -RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv -uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 1 O=Amazon -# Subject: CN=Amazon Root CA 1 O=Amazon -# Label: "Amazon Root CA 1" -# Serial: 143266978916655856878034712317230054538369994 -# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 -# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 -# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 2 O=Amazon -# Subject: CN=Amazon Root CA 2 O=Amazon -# Label: "Amazon Root CA 2" -# Serial: 143266982885963551818349160658925006970653239 -# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 -# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a -# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK -gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ -W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg -1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K -8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r -2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me -z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR -8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj -mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz -7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 -+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI -0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB -Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm -UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 -LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY -+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS -k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl -7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm -btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl -urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ -fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 -n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE -76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H -9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT -4PsJYGw= ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 3 O=Amazon -# Subject: CN=Amazon Root CA 3 O=Amazon -# Label: "Amazon Root CA 3" -# Serial: 143266986699090766294700635381230934788665930 -# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 -# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e -# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl -ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr -ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr -BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM -YyRIHN8wfdVoOw== ------END CERTIFICATE----- - -# Issuer: CN=Amazon Root CA 4 O=Amazon -# Subject: CN=Amazon Root CA 4 O=Amazon -# Label: "Amazon Root CA 4" -# Serial: 143266989758080763974105200630763877849284878 -# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd -# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be -# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 ------BEGIN CERTIFICATE----- -MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi -9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk -M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB -MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw -CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW -1KyLa2tJElMzrdfkviT8tQp21KW8EA== ------END CERTIFICATE----- - -# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM -# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM -# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" -# Serial: 1 -# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 -# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca -# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 ------BEGIN CERTIFICATE----- -MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx -GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp -bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w -KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 -BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy -dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG -EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll -IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU -QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT -TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg -LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 -a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr -LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr -N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X -YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ -iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f -AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH -V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh -AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf -IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 -lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c -8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf -lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= ------END CERTIFICATE----- - -# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. -# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. -# Label: "GDCA TrustAUTH R5 ROOT" -# Serial: 9009899650740120186 -# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 -# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 -# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 ------BEGIN CERTIFICATE----- -MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE -BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ -IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 -MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV -BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w -HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj -Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj -TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u -KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj -qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm -MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 -ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP -zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk -L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC -jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA -HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC -AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg -p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm -DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 -COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry -L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf -JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg -IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io -2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV -09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ -XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq -T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe -MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== ------END CERTIFICATE----- - -# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation -# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation -# Label: "SSL.com Root Certification Authority RSA" -# Serial: 8875640296558310041 -# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 -# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb -# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 ------BEGIN CERTIFICATE----- -MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE -BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK -DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz -OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv -dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv -bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R -xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX -qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC -C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 -6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh -/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF -YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E -JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc -US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 -ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm -+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi -M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV -HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G -A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV -cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc -Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs -PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ -q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 -cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr -a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I -H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y -K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu -nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf -oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY -Ic2wBlX7Jz9TkHCpBB5XJ7k= ------END CERTIFICATE----- - -# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation -# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation -# Label: "SSL.com Root Certification Authority ECC" -# Serial: 8495723813297216424 -# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e -# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a -# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 ------BEGIN CERTIFICATE----- -MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC -VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T -U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz -WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 -b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS -b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB -BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI -7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg -CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud -EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD -VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T -kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ -gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl ------END CERTIFICATE----- - -# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation -# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation -# Label: "SSL.com EV Root Certification Authority RSA R2" -# Serial: 6248227494352943350 -# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 -# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a -# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c ------BEGIN CERTIFICATE----- -MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV -BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE -CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy -MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G -A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD -DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq -M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf -OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa -4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 -HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR -aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA -b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ -Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV -PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO -pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu -UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY -MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV -HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 -9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW -s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 -Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg -cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM -79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz -/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt -ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm -Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK -QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ -w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi -S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 -mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== ------END CERTIFICATE----- - -# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation -# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation -# Label: "SSL.com EV Root Certification Authority ECC" -# Serial: 3182246526754555285 -# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 -# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d -# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 ------BEGIN CERTIFICATE----- -MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC -VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T -U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx -NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv -dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv -bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 -AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA -VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku -WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP -MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX -5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ -ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg -h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 -# Label: "GlobalSign Root CA - R6" -# Serial: 1417766617973444989252670301619537 -# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae -# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 -# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg -MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh -bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx -MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET -MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI -xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k -ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD -aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw -LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw -1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX -k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 -SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h -bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n -WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY -rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce -MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu -bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN -nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt -Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 -55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj -vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf -cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz -oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp -nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs -pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v -JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R -8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 -5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= ------END CERTIFICATE----- - -# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed -# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed -# Label: "OISTE WISeKey Global Root GC CA" -# Serial: 44084345621038548146064804565436152554 -# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 -# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 -# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d ------BEGIN CERTIFICATE----- -MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw -CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 -bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg -Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ -BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu -ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS -b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni -eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W -p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T -rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV -57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg -Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 ------END CERTIFICATE----- - -# Issuer: CN=UCA Global G2 Root O=UniTrust -# Subject: CN=UCA Global G2 Root O=UniTrust -# Label: "UCA Global G2 Root" -# Serial: 124779693093741543919145257850076631279 -# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 -# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a -# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 -MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH -bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x -CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds -b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr -b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 -kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm -VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R -VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc -C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj -tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY -D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv -j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl -NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 -iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP -O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV -ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj -L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 -1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl -1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU -b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV -PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj -y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb -EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg -DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI -+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy -YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX -UB+K+wb1whnw0A== ------END CERTIFICATE----- - -# Issuer: CN=UCA Extended Validation Root O=UniTrust -# Subject: CN=UCA Extended Validation Root O=UniTrust -# Label: "UCA Extended Validation Root" -# Serial: 106100277556486529736699587978573607008 -# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 -# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a -# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH -MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF -eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx -MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV -BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog -D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS -sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop -O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk -sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi -c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj -VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz -KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ -TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G -sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs -1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD -fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN -l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR -ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ -VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 -c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp -4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s -t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj -2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO -vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C -xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx -cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM -fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax ------END CERTIFICATE----- - -# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 -# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 -# Label: "Certigna Root CA" -# Serial: 269714418870597844693661054334862075617 -# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 -# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 -# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 ------BEGIN CERTIFICATE----- -MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw -WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw -MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x -MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD -VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX -BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO -ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M -CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu -I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm -TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh -C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf -ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz -IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT -Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k -JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 -hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB -GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of -1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov -L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo -dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr -aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq -hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L -6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG -HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 -0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB -lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi -o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 -gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v -faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 -Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh -jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw -3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= ------END CERTIFICATE----- - -# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI -# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI -# Label: "emSign Root CA - G1" -# Serial: 235931866688319308814040 -# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac -# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c -# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 ------BEGIN CERTIFICATE----- -MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD -VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU -ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH -MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO -MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv -Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz -f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO -8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq -d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM -tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt -Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB -o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD -AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x -PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM -wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d -GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH -6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby -RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx -iN66zB+Afko= ------END CERTIFICATE----- - -# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI -# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI -# Label: "emSign ECC Root CA - G3" -# Serial: 287880440101571086945156 -# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 -# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 -# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b ------BEGIN CERTIFICATE----- -MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG -EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo -bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g -RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ -TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s -b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw -djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 -WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS -fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB -zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq -hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB -CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD -+JbNR6iC8hZVdyR+EhCVBCyj ------END CERTIFICATE----- - -# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI -# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI -# Label: "emSign Root CA - C1" -# Serial: 825510296613316004955058 -# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 -# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 -# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f ------BEGIN CERTIFICATE----- -MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG -A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg -SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw -MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln -biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v -dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ -BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ -HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH -3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH -GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c -xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 -aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq -TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 -/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 -kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG -YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT -+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo -WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= ------END CERTIFICATE----- - -# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI -# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI -# Label: "emSign ECC Root CA - C3" -# Serial: 582948710642506000014504 -# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 -# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 -# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 ------BEGIN CERTIFICATE----- -MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG -EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx -IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw -MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln -biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND -IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci -MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti -sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O -BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB -Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c -3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J -0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== ------END CERTIFICATE----- - -# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post -# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post -# Label: "Hongkong Post Root CA 3" -# Serial: 46170865288971385588281144162979347873371282084 -# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 -# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 -# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 ------BEGIN CERTIFICATE----- -MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQEL -BQAwbzELMAkGA1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJ -SG9uZyBLb25nMRYwFAYDVQQKEw1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25n -a29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2MDMwMjI5NDZaFw00MjA2MDMwMjI5 -NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtvbmcxEjAQBgNVBAcT -CUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMXSG9u -Z2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCziNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFO -dem1p+/l6TWZ5Mwc50tfjTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mI -VoBc+L0sPOFMV4i707mV78vH9toxdCim5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV -9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOesL4jpNrcyCse2m5FHomY -2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj0mRiikKY -vLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+Tt -bNe/JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZb -x39ri1UbSsUgYT2uy1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+ -l2oBlKN8W4UdKjk60FSh0Tlxnf0h+bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YK -TE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsGxVd7GYYKecsAyVKvQv83j+Gj -Hno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwIDAQABo2MwYTAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e -i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEw -DQYJKoZIhvcNAQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG -7BJ8dNVI0lkUmcDrudHr9EgwW62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCk -MpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWldy8joRTnU+kLBEUx3XZL7av9YROXr -gZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov+BS5gLNdTaqX4fnk -GMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDceqFS -3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJm -Ozj/2ZQw9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+ -l6mc1X5VTMbeRRAc6uk7nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6c -JfTzPV4e0hz5sy229zdcxsshTrD3mUcYhcErulWuBurQB7Lcq9CClnXO0lD+mefP -L5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB60PZ2Pierc+xYw5F9KBa -LJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fqdBb9HxEG -mpv0 ------END CERTIFICATE----- - -# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only -# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only -# Label: "Entrust Root Certification Authority - G4" -# Serial: 289383649854506086828220374796556676440 -# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88 -# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01 -# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88 ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw -gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL -Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg -MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw -BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 -MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 -c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ -bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg -Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ -2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E -T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j -5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM -C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T -DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX -wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A -2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm -nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 -dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl -N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj -c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS -5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS -Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr -hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ -B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI -AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw -H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ -b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk -2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol -IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk -5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY -n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== ------END CERTIFICATE----- - -# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation -# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation -# Label: "Microsoft ECC Root Certificate Authority 2017" -# Serial: 136839042543790627607696632466672567020 -# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 -# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 -# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 ------BEGIN CERTIFICATE----- -MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw -CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD -VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw -MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV -UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy -b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR -ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb -hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 -FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV -L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB -iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= ------END CERTIFICATE----- - -# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation -# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation -# Label: "Microsoft RSA Root Certificate Authority 2017" -# Serial: 40975477897264996090493496164228220339 -# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 -# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 -# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 ------BEGIN CERTIFICATE----- -MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl -MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw -NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 -IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG -EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N -aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ -Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 -ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 -HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm -gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ -jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc -aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG -YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 -W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K -UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH -+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q -W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC -NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC -LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC -gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 -tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh -SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 -TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 -pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR -xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp -GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 -dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN -AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB -RA+GsCyRxj3qrg+E ------END CERTIFICATE----- - -# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. -# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. -# Label: "e-Szigno Root CA 2017" -# Serial: 411379200276854331539784714 -# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 -# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 -# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 ------BEGIN CERTIFICATE----- -MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV -BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk -LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv -b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ -BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg -THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v -IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv -xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H -Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G -A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB -eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo -jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ -+efcMQ== ------END CERTIFICATE----- - -# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 -# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 -# Label: "certSIGN Root CA G2" -# Serial: 313609486401300475190 -# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 -# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 -# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 ------BEGIN CERTIFICATE----- -MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV -BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g -Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ -BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ -R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF -dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw -vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ -uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp -n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs -cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW -xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P -rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF -DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx -DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy -LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C -eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ -d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq -kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC -b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl -qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 -OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c -NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk -ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO -pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj -03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk -PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE -1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX -QRBdJ3NghVdJIgc= ------END CERTIFICATE----- - -# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. -# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. -# Label: "Trustwave Global Certification Authority" -# Serial: 1846098327275375458322922162 -# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e -# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 -# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 ------BEGIN CERTIFICATE----- -MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x -ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 -c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx -OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI -SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI -b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn -swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu -7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 -1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW -80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP -JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l -RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw -hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 -coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc -BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n -twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud -DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W -0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe -uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q -lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB -aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE -sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT -MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe -qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh -VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 -h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 -EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK -yeC2nOnOcXHebD8WpHk= ------END CERTIFICATE----- - -# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. -# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. -# Label: "Trustwave Global ECC P256 Certification Authority" -# Serial: 4151900041497450638097112925 -# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 -# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf -# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 ------BEGIN CERTIFICATE----- -MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD -VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf -BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 -YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x -NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G -A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 -d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF -Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN -FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w -DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw -CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh -DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 ------END CERTIFICATE----- - -# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. -# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. -# Label: "Trustwave Global ECC P384 Certification Authority" -# Serial: 2704997926503831671788816187 -# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 -# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 -# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 ------BEGIN CERTIFICATE----- -MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD -VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf -BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 -YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x -NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G -A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 -d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF -Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB -BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ -j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF -1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G -A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 -AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC -MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu -Sw== ------END CERTIFICATE----- - -# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. -# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. -# Label: "NAVER Global Root Certification Authority" -# Serial: 9013692873798656336226253319739695165984492813 -# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b -# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 -# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 ------BEGIN CERTIFICATE----- -MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM -BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG -T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx -CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD -b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA -iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH -38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE -HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz -kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP -szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq -vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf -nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG -YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo -0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a -CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K -AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I -36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB -Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN -qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj -cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm -+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL -hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe -lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 -p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 -piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR -LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX -5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO -dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul -9XXeifdy ------END CERTIFICATE----- - -# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres -# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres -# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" -# Serial: 131542671362353147877283741781055151509 -# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb -# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a -# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb ------BEGIN CERTIFICATE----- -MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw -CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw -FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S -Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 -MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL -DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS -QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB -BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH -sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK -Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu -SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC -MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy -v+c= ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa -# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa -# Label: "GlobalSign Root R46" -# Serial: 1552617688466950547958867513931858518042577 -# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef -# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 -# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA -MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD -VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy -MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt -c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ -OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG -vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud -316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo -0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE -y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF -zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE -+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN -I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs -x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa -ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC -4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 -7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg -JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti -2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk -pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF -FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt -rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk -ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 -u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP -4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 -N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 -vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa -# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa -# Label: "GlobalSign Root E46" -# Serial: 1552617690338932563915843282459653771421763 -# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f -# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 -# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 ------BEGIN CERTIFICATE----- -MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx -CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD -ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw -MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex -HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq -R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd -yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ -7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 -+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= ------END CERTIFICATE----- - -# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH -# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH -# Label: "GLOBALTRUST 2020" -# Serial: 109160994242082918454945253 -# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 -# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 -# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a ------BEGIN CERTIFICATE----- -MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG -A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw -FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx -MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u -aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b -RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z -YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 -QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw -yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ -BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ -SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH -r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 -4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me -dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw -q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 -nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu -H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA -VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC -XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd -6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf -+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi -kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 -wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB -TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C -MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn -4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I -aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy -qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== ------END CERTIFICATE----- - -# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz -# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz -# Label: "ANF Secure Server Root CA" -# Serial: 996390341000653745 -# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 -# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 -# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 ------BEGIN CERTIFICATE----- -MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNV -BAUTCUc2MzI4NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlk -YWQgZGUgQ2VydGlmaWNhY2lvbjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNV -BAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3QgQ0EwHhcNMTkwOTA0MTAwMDM4WhcN -MzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEwMQswCQYDVQQGEwJF -UzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQwEgYD -VQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9v -dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCj -cqQZAZ2cC4Ffc0m6p6zzBE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9q -yGFOtibBTI3/TO80sh9l2Ll49a2pcbnvT1gdpd50IJeh7WhM3pIXS7yr/2WanvtH -2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcvB2VSAKduyK9o7PQUlrZX -H1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXsezx76W0OL -zc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyR -p1RMVwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQz -W7i1o0TJrH93PB0j7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/ -SiOL9V8BY9KHcyi1Swr1+KuCLH5zJTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJn -LNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe8TZBAQIvfXOn3kLMTOmJDVb3 -n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVOHj1tyRRM4y5B -u8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj -o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC -AgEATh65isagmD9uw2nAalxJUqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L -9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzxj6ptBZNscsdW699QIyjlRRA96Gej -rw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDtdD+4E5UGUcjohybK -pFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM5gf0 -vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjq -OknkJjCb5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ -/zo1PqVUSlJZS2Db7v54EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ9 -2zg/LFis6ELhDtjTO0wugumDLmsx2d1Hhk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI -+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGyg77FGr8H6lnco4g175x2 -MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3r5+qPeoo -tt7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= ------END CERTIFICATE----- - -# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Label: "Certum EC-384 CA" -# Serial: 160250656287871593594747141429395092468 -# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 -# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed -# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 ------BEGIN CERTIFICATE----- -MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw -CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw -JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT -EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 -WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT -LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX -BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE -KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm -Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 -EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J -UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn -nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= ------END CERTIFICATE----- - -# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority -# Label: "Certum Trusted Root CA" -# Serial: 40870380103424195783807378461123655149 -# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 -# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 -# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd ------BEGIN CERTIFICATE----- -MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6 -MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu -MScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNV -BAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwHhcNMTgwMzE2MTIxMDEzWhcNNDMw -MzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEg -U3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZ -n0EGze2jusDbCSzBfN8pfktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/q -p1x4EaTByIVcJdPTsuclzxFUl6s1wB52HO8AU5853BSlLCIls3Jy/I2z5T4IHhQq -NwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2fJmItdUDmj0VDT06qKhF -8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGtg/BKEiJ3 -HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGa -mqi4NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi -7VdNIuJGmj8PkTQkfVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSF -ytKAQd8FqKPVhJBPC/PgP5sZ0jeJP/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0P -qafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSYnjYJdmZm/Bo/6khUHL4wvYBQ -v3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHKHRzQ+8S1h9E6 -Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 -vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQAD -ggIBAEii1QALLtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4 -WxmB82M+w85bj/UvXgF2Ez8sALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvo -zMrnadyHncI013nR03e4qllY/p0m+jiGPp2Kh2RX5Rc64vmNueMzeMGQ2Ljdt4NR -5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8CYyqOhNf6DR5UMEQ -GfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA4kZf -5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq -0Uc9NneoWWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7D -P78v3DSk+yshzWePS/Tj6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTM -qJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmTOPQD8rv7gmsHINFSH5pkAnuYZttcTVoP -0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZckbxJF0WddCajJFdr60qZf -E2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb ------END CERTIFICATE----- - -# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique -# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique -# Label: "TunTrust Root CA" -# Serial: 108534058042236574382096126452369648152337120275 -# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 -# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb -# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 ------BEGIN CERTIFICATE----- -MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL -BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg -Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv -b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG -EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u -IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ -n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd -2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF -VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ -GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF -li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU -r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 -eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb -MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg -jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB -7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW -5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE -ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 -90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z -xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu -QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 -FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH -22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP -xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn -dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 -Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b -nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ -CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH -u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj -d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= ------END CERTIFICATE----- - -# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Label: "HARICA TLS RSA Root CA 2021" -# Serial: 76817823531813593706434026085292783742 -# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 -# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d -# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs -MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl -c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg -Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL -MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl -YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv -b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l -mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE -4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv -a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M -pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw -Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b -LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY -AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB -AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq -E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr -W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ -CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE -AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU -X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 -f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja -H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP -JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P -zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt -jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 -/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT -BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 -aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW -xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU -63ZTGI0RmLo= ------END CERTIFICATE----- - -# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA -# Label: "HARICA TLS ECC Root CA 2021" -# Serial: 137515985548005187474074462014555733966 -# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 -# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 -# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 ------BEGIN CERTIFICATE----- -MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw -CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh -cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v -dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG -A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj -aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg -Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 -KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y -STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD -AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw -SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN -nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps ------END CERTIFICATE----- - -# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 -# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" -# Serial: 1977337328857672817 -# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 -# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe -# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE -BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h -cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 -MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg -Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 -thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM -cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG -L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i -NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h -X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b -m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy -Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja -EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T -KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF -6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh -OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc -tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd -IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j -b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC -AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw -ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m -iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF -Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ -hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P -Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE -EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV -1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t -CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR -5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw -f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 -ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK -GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV ------END CERTIFICATE----- - -# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. -# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. -# Label: "vTrus ECC Root CA" -# Serial: 630369271402956006249506845124680065938238527194 -# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 -# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 -# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 ------BEGIN CERTIFICATE----- -MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw -RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY -BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz -MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u -LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 -v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd -e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw -V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA -AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG -GJTO ------END CERTIFICATE----- - -# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. -# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. -# Label: "vTrus Root CA" -# Serial: 387574501246983434957692974888460947164905180485 -# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc -# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 -# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 ------BEGIN CERTIFICATE----- -MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL -BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x -FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx -MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s -THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc -IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU -AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ -GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 -8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH -flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt -J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim -0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN -pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ -UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW -OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB -AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet -8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd -nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j -bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM -Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv -TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS -S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr -I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 -b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB -UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P -Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven -sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= ------END CERTIFICATE----- - -# Issuer: CN=ISRG Root X2 O=Internet Security Research Group -# Subject: CN=ISRG Root X2 O=Internet Security Research Group -# Label: "ISRG Root X2" -# Serial: 87493402998870891108772069816698636114 -# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 -# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af -# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 ------BEGIN CERTIFICATE----- -MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw -CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg -R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 -MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT -ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw -EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW -+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 -ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI -zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW -tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 -/q4AaOeMSQ+2b1tbFfLn ------END CERTIFICATE----- - -# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. -# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. -# Label: "HiPKI Root CA - G1" -# Serial: 60966262342023497858655262305426234976 -# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 -# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 -# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc ------BEGIN CERTIFICATE----- -MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP -MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 -ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa -Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 -YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw -qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv -Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 -lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz -Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ -KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK -FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj -HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr -y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ -/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM -a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 -fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG -SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi -7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc -SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza -ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc -XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg -iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho -L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF -Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr -kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ -vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU -YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== ------END CERTIFICATE----- - -# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 -# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 -# Label: "GlobalSign ECC Root CA - R4" -# Serial: 159662223612894884239637590694 -# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc -# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 -# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 ------BEGIN CERTIFICATE----- -MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD -VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh -bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw -MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g -UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT -BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx -uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV -HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ -+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 -bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R1 O=Google Trust Services LLC -# Subject: CN=GTS Root R1 O=Google Trust Services LLC -# Label: "GTS Root R1" -# Serial: 159662320309726417404178440727 -# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 -# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a -# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf ------BEGIN CERTIFICATE----- -MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo -27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w -Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw -TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl -qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH -szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 -Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk -MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 -wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p -aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN -VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID -AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb -C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe -QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy -h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 -7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J -ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef -MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ -Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT -6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ -0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm -2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb -bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R2 O=Google Trust Services LLC -# Subject: CN=GTS Root R2 O=Google Trust Services LLC -# Label: "GTS Root R2" -# Serial: 159662449406622349769042896298 -# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc -# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94 -# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8 ------BEGIN CERTIFICATE----- -MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt -nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY -6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu -MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k -RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg -f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV -+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo -dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW -Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa -G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq -gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID -AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H -vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 -0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC -B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u -NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg -yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev -HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 -xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR -TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg -JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV -7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl -6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R3 O=Google Trust Services LLC -# Subject: CN=GTS Root R3 O=Google Trust Services LLC -# Label: "GTS Root R3" -# Serial: 159662495401136852707857743206 -# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 -# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 -# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 ------BEGIN CERTIFICATE----- -MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD -VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG -A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw -WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz -IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G -jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 -4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 -VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm -ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X ------END CERTIFICATE----- - -# Issuer: CN=GTS Root R4 O=Google Trust Services LLC -# Subject: CN=GTS Root R4 O=Google Trust Services LLC -# Label: "GTS Root R4" -# Serial: 159662532700760215368942768210 -# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 -# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 -# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d ------BEGIN CERTIFICATE----- -MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD -VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG -A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw -WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz -IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi -QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR -HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D -9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 -p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD ------END CERTIFICATE----- - -# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj -# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj -# Label: "Telia Root CA v2" -# Serial: 7288924052977061235122729490515358 -# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 -# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd -# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx -CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE -AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 -NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ -MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq -AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 -vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 -lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD -n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT -7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o -6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC -TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 -WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R -DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI -pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj -YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy -rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw -AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ -8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi -0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM -A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS -SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K -TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF -6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er -3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt -Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT -VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW -ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA -rBPuUBQemMc= ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH -# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH -# Label: "D-TRUST BR Root CA 1 2020" -# Serial: 165870826978392376648679885835942448534 -# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed -# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 -# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 ------BEGIN CERTIFICATE----- -MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw -CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS -VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 -NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG -A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB -BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS -zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 -QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ -VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g -PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf -Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l -dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 -c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO -PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW -wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV -dWNbFJWcHwHP2NVypw87 ------END CERTIFICATE----- - -# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH -# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH -# Label: "D-TRUST EV Root CA 1 2020" -# Serial: 126288379621884218666039612629459926992 -# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e -# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 -# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db ------BEGIN CERTIFICATE----- -MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw -CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS -VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 -NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG -A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB -BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC -/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD -wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 -OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g -PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf -Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l -dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 -c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO -PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA -y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb -gfM0agPnIjhQW+0ZT0MW ------END CERTIFICATE----- - -# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. -# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. -# Label: "DigiCert TLS ECC P384 Root G5" -# Serial: 13129116028163249804115411775095713523 -# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed -# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee -# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 ------BEGIN CERTIFICATE----- -MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp -Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 -MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ -bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG -ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS -7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp -0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS -B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 -BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ -LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 -DXZDjC5Ty3zfDBeWUA== ------END CERTIFICATE----- - -# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. -# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. -# Label: "DigiCert TLS RSA4096 Root G5" -# Serial: 11930366277458970227240571539258396554 -# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 -# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 -# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 ------BEGIN CERTIFICATE----- -MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN -MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT -HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN -NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs -IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ -ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 -2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp -wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM -pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD -nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po -sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx -Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd -Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX -KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe -XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL -tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv -TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN -AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw -GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H -PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF -O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ -REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik -AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv -/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ -p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw -MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF -qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK -ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ ------END CERTIFICATE----- - -# Issuer: CN=Certainly Root R1 O=Certainly -# Subject: CN=Certainly Root R1 O=Certainly -# Label: "Certainly Root R1" -# Serial: 188833316161142517227353805653483829216 -# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 -# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af -# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 ------BEGIN CERTIFICATE----- -MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw -PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy -dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 -YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 -1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT -vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed -aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 -1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 -r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 -cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ -wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ -6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA -2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH -Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR -eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB -/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u -d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr -PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d -8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi -1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd -rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di -taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 -lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj -yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn -Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy -yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n -wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 -OV+KmalBWQewLK8= ------END CERTIFICATE----- - -# Issuer: CN=Certainly Root E1 O=Certainly -# Subject: CN=Certainly Root E1 O=Certainly -# Label: "Certainly Root E1" -# Serial: 8168531406727139161245376702891150584 -# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 -# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b -# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 ------BEGIN CERTIFICATE----- -MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw -CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu -bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ -BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s -eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK -+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 -QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 -hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm -ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG -BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR ------END CERTIFICATE----- - -# Issuer: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Subject: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Label: "E-Tugra Global Root CA RSA v3" -# Serial: 75951268308633135324246244059508261641472512052 -# MD5 Fingerprint: 22:be:10:f6:c2:f8:03:88:73:5f:33:29:47:28:47:a4 -# SHA1 Fingerprint: e9:a8:5d:22:14:52:1c:5b:aa:0a:b4:be:24:6a:23:8a:c9:ba:e2:a9 -# SHA256 Fingerprint: ef:66:b0:b1:0a:3c:db:9f:2e:36:48:c7:6b:d2:af:18:ea:d2:bf:e6:f1:17:65:5e:28:c4:06:0d:a1:a3:f4:c2 ------BEGIN CERTIFICATE----- -MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQEL -BQAwgYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUt -VHVncmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYw -JAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIFJTQSB2MzAeFw0yMDAzMTgw -OTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMG -QW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 -Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBD -QSBSU0EgdjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J7 -7gnJY9LTQ91ew6aEOErxjYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscx -uj7X/iWpKo429NEvx7epXTPcMHD4QGxLsqYxYdE0PD0xesevxKenhOGXpOhL9hd8 -7jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF/YP9f4RtNGx/ardLAQO/ -rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8qQedmCeFL -l+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bG -wzrwbMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4 -znKS4iicvObpCdg604nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBO -M/J+JjKsBY04pOZ2PJ8QaQ5tndLBeSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK -5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiMbIedBi3x7+PmBvrFZhNb/FAH -nnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbgh3cXTJ2w2Amo -DVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD -AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSy -tK7mLfcm1ap1LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEL -BQADggIBAImocn+M684uGMQQgC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ -6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN438o2Fi+CiJ+8EUdPdk3ILY7r3y18 -Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/qln0F7psTpURs+APQ -3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3sSdPk -vmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn9 -9t2HVhjYsCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQ -mhty3QUBjYZgv6Rn7rWlDdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YA -VSgU7NbHEqIbZULpkejLPoeJVF3Zr52XnGnnCv8PWniLYypMfUeUP95L6VPQMPHF -9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFHIK+WEj5jlB0E5y67hscM -moi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiXYY60MGo8 -bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ ------END CERTIFICATE----- - -# Issuer: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Subject: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Label: "E-Tugra Global Root CA ECC v3" -# Serial: 218504919822255052842371958738296604628416471745 -# MD5 Fingerprint: 46:bc:81:bb:f1:b5:1e:f7:4b:96:bc:14:e2:e7:27:64 -# SHA1 Fingerprint: 8a:2f:af:57:53:b1:b0:e6:a1:04:ec:5b:6a:69:71:6d:f6:1c:e2:84 -# SHA256 Fingerprint: 87:3f:46:85:fa:7f:56:36:25:25:2e:6d:36:bc:d7:f1:6f:c2:49:51:f2:64:e4:7e:1b:95:4f:49:08:cd:ca:13 ------BEGIN CERTIFICATE----- -MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw -gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn -cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD -VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2 -NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r -YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh -IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF -Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ -KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK -fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB -Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C -MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp -ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6 -7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx -vmjkI6TZraE3 ------END CERTIFICATE----- - -# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. -# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. -# Label: "Security Communication RootCA3" -# Serial: 16247922307909811815 -# MD5 Fingerprint: 1c:9a:16:ff:9e:5c:e0:4d:8a:14:01:f4:35:5d:29:26 -# SHA1 Fingerprint: c3:03:c8:22:74:92:e5:61:a2:9c:5f:79:91:2b:1e:44:13:91:30:3a -# SHA256 Fingerprint: 24:a5:5c:2a:b0:51:44:2d:06:17:76:65:41:23:9a:4a:d0:32:d7:c5:51:75:aa:34:ff:de:2f:bc:4f:5c:52:94 ------BEGIN CERTIFICATE----- -MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV -BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw -JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2 -MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg -Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r -CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA -lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG -TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7 -9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7 -8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4 -g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we -GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst -+3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M -0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ -T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw -HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS -YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA -FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd -9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI -UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+ -OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke -gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf -iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV -nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD -2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI// -1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad -TdJ0MN1kURXbg4NR16/9M51NZg== ------END CERTIFICATE----- - -# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. -# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. -# Label: "Security Communication ECC RootCA1" -# Serial: 15446673492073852651 -# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 -# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 -# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 ------BEGIN CERTIFICATE----- -MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT -AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD -VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx -NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT -HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 -IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi -AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl -dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK -ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu -9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O -be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= ------END CERTIFICATE----- - -# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY -# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY -# Label: "BJCA Global Root CA1" -# Serial: 113562791157148395269083148143378328608 -# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 -# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a -# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU -MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI -T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz -MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF -SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh -bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z -xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ -spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 -58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR -at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll -5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq -nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK -V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ -pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO -z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn -jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ -WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF -7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 -YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli -awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u -+2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 -X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN -SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo -P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI -+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz -znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 -eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 -YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy -r/6zcCwupvI= ------END CERTIFICATE----- - -# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY -# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY -# Label: "BJCA Global Root CA2" -# Serial: 58605626836079930195615843123109055211 -# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c -# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 -# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 ------BEGIN CERTIFICATE----- -MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw -CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ -VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy -MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ -TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS -b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B -IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ -+kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK -sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA -94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B -43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== ------END CERTIFICATE----- diff --git a/telegramer/include/certifi/core.py b/telegramer/include/certifi/core.py deleted file mode 100644 index de02898..0000000 --- a/telegramer/include/certifi/core.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -certifi.py -~~~~~~~~~~ - -This module returns the installation location of cacert.pem or its contents. -""" -import sys - - -if sys.version_info >= (3, 11): - - from importlib.resources import as_file, files - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the file - # in cases where we're inside of a zipimport situation until someone - # actually calls where(), but we don't want to re-extract the file - # on every call of where(), so we'll do it once then store it in a - # global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you to - # manage the cleanup of this file, so it doesn't actually return a - # path, it returns a context manager that will give you the path - # when you enter it and will do any cleanup when you leave it. In - # the common case of not needing a temporary file, it will just - # return the file system location and the __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - - return _CACERT_PATH - - def contents() -> str: - return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") - -elif sys.version_info >= (3, 7): - - from importlib.resources import path as get_path, read_text - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the - # file in cases where we're inside of a zipimport situation until - # someone actually calls where(), but we don't want to re-extract - # the file on every call of where(), so we'll do it once then store - # it in a global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you - # to manage the cleanup of this file, so it doesn't actually - # return a path, it returns a context manager that will give - # you the path when you enter it and will do any cleanup when - # you leave it. In the common case of not needing a temporary - # file, it will just return the file system location and the - # __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = get_path("certifi", "cacert.pem") - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - - return _CACERT_PATH - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") - -else: - import os - import types - from typing import Union - - Package = Union[types.ModuleType, str] - Resource = Union[str, "os.PathLike"] - - # This fallback will work for Python versions prior to 3.7 that lack the - # importlib.resources module but relies on the existing `where` function - # so won't address issues with environments like PyOxidizer that don't set - # __file__ on modules. - def read_text( - package: Package, - resource: Resource, - encoding: str = 'utf-8', - errors: str = 'strict' - ) -> str: - with open(where(), encoding=encoding) as data: - return data.read() - - # If we don't have importlib.resources, then we will just do the old logic - # of assuming we're on the filesystem and munge the path directly. - def where() -> str: - f = os.path.dirname(__file__) - - return os.path.join(f, "cacert.pem") - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/telegramer/include/certifi/py.typed b/telegramer/include/certifi/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/pytz/__init__.py b/telegramer/include/pytz/__init__.py deleted file mode 100644 index 900e8ca..0000000 --- a/telegramer/include/pytz/__init__.py +++ /dev/null @@ -1,1559 +0,0 @@ -''' -datetime.tzinfo timezone definitions generated from the -Olson timezone database: - - ftp://elsie.nci.nih.gov/pub/tz*.tar.gz - -See the datetime section of the Python Library Reference for information -on how to use these modules. -''' - -import sys -import datetime -import os.path - -from pytz.exceptions import AmbiguousTimeError -from pytz.exceptions import InvalidTimeError -from pytz.exceptions import NonExistentTimeError -from pytz.exceptions import UnknownTimeZoneError -from pytz.lazy import LazyDict, LazyList, LazySet # noqa -from pytz.tzinfo import unpickler, BaseTzInfo -from pytz.tzfile import build_tzinfo - - -# The IANA (nee Olson) database is updated several times a year. -OLSON_VERSION = '2022a' -VERSION = '2022.1' # pip compatible version number. -__version__ = VERSION - -OLSEN_VERSION = OLSON_VERSION # Old releases had this misspelling - -__all__ = [ - 'timezone', 'utc', 'country_timezones', 'country_names', - 'AmbiguousTimeError', 'InvalidTimeError', - 'NonExistentTimeError', 'UnknownTimeZoneError', - 'all_timezones', 'all_timezones_set', - 'common_timezones', 'common_timezones_set', - 'BaseTzInfo', 'FixedOffset', -] - - -if sys.version_info[0] > 2: # Python 3.x - - # Python 3.x doesn't have unicode(), making writing code - # for Python 2.3 and Python 3.x a pain. - unicode = str - - def ascii(s): - r""" - >>> ascii('Hello') - 'Hello' - >>> ascii('\N{TRADE MARK SIGN}') #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - UnicodeEncodeError: ... - """ - if type(s) == bytes: - s = s.decode('ASCII') - else: - s.encode('ASCII') # Raise an exception if not ASCII - return s # But the string - not a byte string. - -else: # Python 2.x - - def ascii(s): - r""" - >>> ascii('Hello') - 'Hello' - >>> ascii(u'Hello') - 'Hello' - >>> ascii(u'\N{TRADE MARK SIGN}') #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - UnicodeEncodeError: ... - """ - return s.encode('ASCII') - - -def open_resource(name): - """Open a resource from the zoneinfo subdir for reading. - - Uses the pkg_resources module if available and no standard file - found at the calculated location. - - It is possible to specify different location for zoneinfo - subdir by using the PYTZ_TZDATADIR environment variable. - """ - name_parts = name.lstrip('/').split('/') - for part in name_parts: - if part == os.path.pardir or os.path.sep in part: - raise ValueError('Bad path segment: %r' % part) - zoneinfo_dir = os.environ.get('PYTZ_TZDATADIR', None) - if zoneinfo_dir is not None: - filename = os.path.join(zoneinfo_dir, *name_parts) - else: - filename = os.path.join(os.path.dirname(__file__), - 'zoneinfo', *name_parts) - if not os.path.exists(filename): - # http://bugs.launchpad.net/bugs/383171 - we avoid using this - # unless absolutely necessary to help when a broken version of - # pkg_resources is installed. - try: - from pkg_resources import resource_stream - except ImportError: - resource_stream = None - - if resource_stream is not None: - return resource_stream(__name__, 'zoneinfo/' + name) - return open(filename, 'rb') - - -def resource_exists(name): - """Return true if the given resource exists""" - try: - if os.environ.get('PYTZ_SKIPEXISTSCHECK', ''): - # In "standard" distributions, we can assume that - # all the listed timezones are present. As an - # import-speed optimization, you can set the - # PYTZ_SKIPEXISTSCHECK flag to skip checking - # for the presence of the resource file on disk. - return True - open_resource(name).close() - return True - except IOError: - return False - - -_tzinfo_cache = {} - - -def timezone(zone): - r''' Return a datetime.tzinfo implementation for the given timezone - - >>> from datetime import datetime, timedelta - >>> utc = timezone('UTC') - >>> eastern = timezone('US/Eastern') - >>> eastern.zone - 'US/Eastern' - >>> timezone(unicode('US/Eastern')) is eastern - True - >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc) - >>> loc_dt = utc_dt.astimezone(eastern) - >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' - >>> loc_dt.strftime(fmt) - '2002-10-27 01:00:00 EST (-0500)' - >>> (loc_dt - timedelta(minutes=10)).strftime(fmt) - '2002-10-27 00:50:00 EST (-0500)' - >>> eastern.normalize(loc_dt - timedelta(minutes=10)).strftime(fmt) - '2002-10-27 01:50:00 EDT (-0400)' - >>> (loc_dt + timedelta(minutes=10)).strftime(fmt) - '2002-10-27 01:10:00 EST (-0500)' - - Raises UnknownTimeZoneError if passed an unknown zone. - - >>> try: - ... timezone('Asia/Shangri-La') - ... except UnknownTimeZoneError: - ... print('Unknown') - Unknown - - >>> try: - ... timezone(unicode('\N{TRADE MARK SIGN}')) - ... except UnknownTimeZoneError: - ... print('Unknown') - Unknown - - ''' - if zone is None: - raise UnknownTimeZoneError(None) - - if zone.upper() == 'UTC': - return utc - - try: - zone = ascii(zone) - except UnicodeEncodeError: - # All valid timezones are ASCII - raise UnknownTimeZoneError(zone) - - zone = _case_insensitive_zone_lookup(_unmunge_zone(zone)) - if zone not in _tzinfo_cache: - if zone in all_timezones_set: # noqa - fp = open_resource(zone) - try: - _tzinfo_cache[zone] = build_tzinfo(zone, fp) - finally: - fp.close() - else: - raise UnknownTimeZoneError(zone) - - return _tzinfo_cache[zone] - - -def _unmunge_zone(zone): - """Undo the time zone name munging done by older versions of pytz.""" - return zone.replace('_plus_', '+').replace('_minus_', '-') - - -_all_timezones_lower_to_standard = None - - -def _case_insensitive_zone_lookup(zone): - """case-insensitively matching timezone, else return zone unchanged""" - global _all_timezones_lower_to_standard - if _all_timezones_lower_to_standard is None: - _all_timezones_lower_to_standard = dict((tz.lower(), tz) for tz in all_timezones) # noqa - return _all_timezones_lower_to_standard.get(zone.lower()) or zone # noqa - - -ZERO = datetime.timedelta(0) -HOUR = datetime.timedelta(hours=1) - - -class UTC(BaseTzInfo): - """UTC - - Optimized UTC implementation. It unpickles using the single module global - instance defined beneath this class declaration. - """ - zone = "UTC" - - _utcoffset = ZERO - _dst = ZERO - _tzname = zone - - def fromutc(self, dt): - if dt.tzinfo is None: - return self.localize(dt) - return super(utc.__class__, self).fromutc(dt) - - def utcoffset(self, dt): - return ZERO - - def tzname(self, dt): - return "UTC" - - def dst(self, dt): - return ZERO - - def __reduce__(self): - return _UTC, () - - def localize(self, dt, is_dst=False): - '''Convert naive time to local time''' - if dt.tzinfo is not None: - raise ValueError('Not naive datetime (tzinfo is already set)') - return dt.replace(tzinfo=self) - - def normalize(self, dt, is_dst=False): - '''Correct the timezone information on the given datetime''' - if dt.tzinfo is self: - return dt - if dt.tzinfo is None: - raise ValueError('Naive time - no tzinfo set') - return dt.astimezone(self) - - def __repr__(self): - return "" - - def __str__(self): - return "UTC" - - -UTC = utc = UTC() # UTC is a singleton - - -def _UTC(): - """Factory function for utc unpickling. - - Makes sure that unpickling a utc instance always returns the same - module global. - - These examples belong in the UTC class above, but it is obscured; or in - the README.rst, but we are not depending on Python 2.4 so integrating - the README.rst examples with the unit tests is not trivial. - - >>> import datetime, pickle - >>> dt = datetime.datetime(2005, 3, 1, 14, 13, 21, tzinfo=utc) - >>> naive = dt.replace(tzinfo=None) - >>> p = pickle.dumps(dt, 1) - >>> naive_p = pickle.dumps(naive, 1) - >>> len(p) - len(naive_p) - 17 - >>> new = pickle.loads(p) - >>> new == dt - True - >>> new is dt - False - >>> new.tzinfo is dt.tzinfo - True - >>> utc is UTC is timezone('UTC') - True - >>> utc is timezone('GMT') - False - """ - return utc - - -_UTC.__safe_for_unpickling__ = True - - -def _p(*args): - """Factory function for unpickling pytz tzinfo instances. - - Just a wrapper around tzinfo.unpickler to save a few bytes in each pickle - by shortening the path. - """ - return unpickler(*args) - - -_p.__safe_for_unpickling__ = True - - -class _CountryTimezoneDict(LazyDict): - """Map ISO 3166 country code to a list of timezone names commonly used - in that country. - - iso3166_code is the two letter code used to identify the country. - - >>> def print_list(list_of_strings): - ... 'We use a helper so doctests work under Python 2.3 -> 3.x' - ... for s in list_of_strings: - ... print(s) - - >>> print_list(country_timezones['nz']) - Pacific/Auckland - Pacific/Chatham - >>> print_list(country_timezones['ch']) - Europe/Zurich - >>> print_list(country_timezones['CH']) - Europe/Zurich - >>> print_list(country_timezones[unicode('ch')]) - Europe/Zurich - >>> print_list(country_timezones['XXX']) - Traceback (most recent call last): - ... - KeyError: 'XXX' - - Previously, this information was exposed as a function rather than a - dictionary. This is still supported:: - - >>> print_list(country_timezones('nz')) - Pacific/Auckland - Pacific/Chatham - """ - def __call__(self, iso3166_code): - """Backwards compatibility.""" - return self[iso3166_code] - - def _fill(self): - data = {} - zone_tab = open_resource('zone.tab') - try: - for line in zone_tab: - line = line.decode('UTF-8') - if line.startswith('#'): - continue - code, coordinates, zone = line.split(None, 4)[:3] - if zone not in all_timezones_set: # noqa - continue - try: - data[code].append(zone) - except KeyError: - data[code] = [zone] - self.data = data - finally: - zone_tab.close() - - -country_timezones = _CountryTimezoneDict() - - -class _CountryNameDict(LazyDict): - '''Dictionary proving ISO3166 code -> English name. - - >>> print(country_names['au']) - Australia - ''' - def _fill(self): - data = {} - zone_tab = open_resource('iso3166.tab') - try: - for line in zone_tab.readlines(): - line = line.decode('UTF-8') - if line.startswith('#'): - continue - code, name = line.split(None, 1) - data[code] = name.strip() - self.data = data - finally: - zone_tab.close() - - -country_names = _CountryNameDict() - - -# Time-zone info based solely on fixed offsets - -class _FixedOffset(datetime.tzinfo): - - zone = None # to match the standard pytz API - - def __init__(self, minutes): - if abs(minutes) >= 1440: - raise ValueError("absolute offset is too large", minutes) - self._minutes = minutes - self._offset = datetime.timedelta(minutes=minutes) - - def utcoffset(self, dt): - return self._offset - - def __reduce__(self): - return FixedOffset, (self._minutes, ) - - def dst(self, dt): - return ZERO - - def tzname(self, dt): - return None - - def __repr__(self): - return 'pytz.FixedOffset(%d)' % self._minutes - - def localize(self, dt, is_dst=False): - '''Convert naive time to local time''' - if dt.tzinfo is not None: - raise ValueError('Not naive datetime (tzinfo is already set)') - return dt.replace(tzinfo=self) - - def normalize(self, dt, is_dst=False): - '''Correct the timezone information on the given datetime''' - if dt.tzinfo is self: - return dt - if dt.tzinfo is None: - raise ValueError('Naive time - no tzinfo set') - return dt.astimezone(self) - - -def FixedOffset(offset, _tzinfos={}): - """return a fixed-offset timezone based off a number of minutes. - - >>> one = FixedOffset(-330) - >>> one - pytz.FixedOffset(-330) - >>> str(one.utcoffset(datetime.datetime.now())) - '-1 day, 18:30:00' - >>> str(one.dst(datetime.datetime.now())) - '0:00:00' - - >>> two = FixedOffset(1380) - >>> two - pytz.FixedOffset(1380) - >>> str(two.utcoffset(datetime.datetime.now())) - '23:00:00' - >>> str(two.dst(datetime.datetime.now())) - '0:00:00' - - The datetime.timedelta must be between the range of -1 and 1 day, - non-inclusive. - - >>> FixedOffset(1440) - Traceback (most recent call last): - ... - ValueError: ('absolute offset is too large', 1440) - - >>> FixedOffset(-1440) - Traceback (most recent call last): - ... - ValueError: ('absolute offset is too large', -1440) - - An offset of 0 is special-cased to return UTC. - - >>> FixedOffset(0) is UTC - True - - There should always be only one instance of a FixedOffset per timedelta. - This should be true for multiple creation calls. - - >>> FixedOffset(-330) is one - True - >>> FixedOffset(1380) is two - True - - It should also be true for pickling. - - >>> import pickle - >>> pickle.loads(pickle.dumps(one)) is one - True - >>> pickle.loads(pickle.dumps(two)) is two - True - """ - if offset == 0: - return UTC - - info = _tzinfos.get(offset) - if info is None: - # We haven't seen this one before. we need to save it. - - # Use setdefault to avoid a race condition and make sure we have - # only one - info = _tzinfos.setdefault(offset, _FixedOffset(offset)) - - return info - - -FixedOffset.__safe_for_unpickling__ = True - - -def _test(): - import doctest - sys.path.insert(0, os.pardir) - import pytz - return doctest.testmod(pytz) - - -if __name__ == '__main__': - _test() -all_timezones = \ -['Africa/Abidjan', - 'Africa/Accra', - 'Africa/Addis_Ababa', - 'Africa/Algiers', - 'Africa/Asmara', - 'Africa/Asmera', - 'Africa/Bamako', - 'Africa/Bangui', - 'Africa/Banjul', - 'Africa/Bissau', - 'Africa/Blantyre', - 'Africa/Brazzaville', - 'Africa/Bujumbura', - 'Africa/Cairo', - 'Africa/Casablanca', - 'Africa/Ceuta', - 'Africa/Conakry', - 'Africa/Dakar', - 'Africa/Dar_es_Salaam', - 'Africa/Djibouti', - 'Africa/Douala', - 'Africa/El_Aaiun', - 'Africa/Freetown', - 'Africa/Gaborone', - 'Africa/Harare', - 'Africa/Johannesburg', - 'Africa/Juba', - 'Africa/Kampala', - 'Africa/Khartoum', - 'Africa/Kigali', - 'Africa/Kinshasa', - 'Africa/Lagos', - 'Africa/Libreville', - 'Africa/Lome', - 'Africa/Luanda', - 'Africa/Lubumbashi', - 'Africa/Lusaka', - 'Africa/Malabo', - 'Africa/Maputo', - 'Africa/Maseru', - 'Africa/Mbabane', - 'Africa/Mogadishu', - 'Africa/Monrovia', - 'Africa/Nairobi', - 'Africa/Ndjamena', - 'Africa/Niamey', - 'Africa/Nouakchott', - 'Africa/Ouagadougou', - 'Africa/Porto-Novo', - 'Africa/Sao_Tome', - 'Africa/Timbuktu', - 'Africa/Tripoli', - 'Africa/Tunis', - 'Africa/Windhoek', - 'America/Adak', - 'America/Anchorage', - 'America/Anguilla', - 'America/Antigua', - 'America/Araguaina', - 'America/Argentina/Buenos_Aires', - 'America/Argentina/Catamarca', - 'America/Argentina/ComodRivadavia', - 'America/Argentina/Cordoba', - 'America/Argentina/Jujuy', - 'America/Argentina/La_Rioja', - 'America/Argentina/Mendoza', - 'America/Argentina/Rio_Gallegos', - 'America/Argentina/Salta', - 'America/Argentina/San_Juan', - 'America/Argentina/San_Luis', - 'America/Argentina/Tucuman', - 'America/Argentina/Ushuaia', - 'America/Aruba', - 'America/Asuncion', - 'America/Atikokan', - 'America/Atka', - 'America/Bahia', - 'America/Bahia_Banderas', - 'America/Barbados', - 'America/Belem', - 'America/Belize', - 'America/Blanc-Sablon', - 'America/Boa_Vista', - 'America/Bogota', - 'America/Boise', - 'America/Buenos_Aires', - 'America/Cambridge_Bay', - 'America/Campo_Grande', - 'America/Cancun', - 'America/Caracas', - 'America/Catamarca', - 'America/Cayenne', - 'America/Cayman', - 'America/Chicago', - 'America/Chihuahua', - 'America/Coral_Harbour', - 'America/Cordoba', - 'America/Costa_Rica', - 'America/Creston', - 'America/Cuiaba', - 'America/Curacao', - 'America/Danmarkshavn', - 'America/Dawson', - 'America/Dawson_Creek', - 'America/Denver', - 'America/Detroit', - 'America/Dominica', - 'America/Edmonton', - 'America/Eirunepe', - 'America/El_Salvador', - 'America/Ensenada', - 'America/Fort_Nelson', - 'America/Fort_Wayne', - 'America/Fortaleza', - 'America/Glace_Bay', - 'America/Godthab', - 'America/Goose_Bay', - 'America/Grand_Turk', - 'America/Grenada', - 'America/Guadeloupe', - 'America/Guatemala', - 'America/Guayaquil', - 'America/Guyana', - 'America/Halifax', - 'America/Havana', - 'America/Hermosillo', - 'America/Indiana/Indianapolis', - 'America/Indiana/Knox', - 'America/Indiana/Marengo', - 'America/Indiana/Petersburg', - 'America/Indiana/Tell_City', - 'America/Indiana/Vevay', - 'America/Indiana/Vincennes', - 'America/Indiana/Winamac', - 'America/Indianapolis', - 'America/Inuvik', - 'America/Iqaluit', - 'America/Jamaica', - 'America/Jujuy', - 'America/Juneau', - 'America/Kentucky/Louisville', - 'America/Kentucky/Monticello', - 'America/Knox_IN', - 'America/Kralendijk', - 'America/La_Paz', - 'America/Lima', - 'America/Los_Angeles', - 'America/Louisville', - 'America/Lower_Princes', - 'America/Maceio', - 'America/Managua', - 'America/Manaus', - 'America/Marigot', - 'America/Martinique', - 'America/Matamoros', - 'America/Mazatlan', - 'America/Mendoza', - 'America/Menominee', - 'America/Merida', - 'America/Metlakatla', - 'America/Mexico_City', - 'America/Miquelon', - 'America/Moncton', - 'America/Monterrey', - 'America/Montevideo', - 'America/Montreal', - 'America/Montserrat', - 'America/Nassau', - 'America/New_York', - 'America/Nipigon', - 'America/Nome', - 'America/Noronha', - 'America/North_Dakota/Beulah', - 'America/North_Dakota/Center', - 'America/North_Dakota/New_Salem', - 'America/Nuuk', - 'America/Ojinaga', - 'America/Panama', - 'America/Pangnirtung', - 'America/Paramaribo', - 'America/Phoenix', - 'America/Port-au-Prince', - 'America/Port_of_Spain', - 'America/Porto_Acre', - 'America/Porto_Velho', - 'America/Puerto_Rico', - 'America/Punta_Arenas', - 'America/Rainy_River', - 'America/Rankin_Inlet', - 'America/Recife', - 'America/Regina', - 'America/Resolute', - 'America/Rio_Branco', - 'America/Rosario', - 'America/Santa_Isabel', - 'America/Santarem', - 'America/Santiago', - 'America/Santo_Domingo', - 'America/Sao_Paulo', - 'America/Scoresbysund', - 'America/Shiprock', - 'America/Sitka', - 'America/St_Barthelemy', - 'America/St_Johns', - 'America/St_Kitts', - 'America/St_Lucia', - 'America/St_Thomas', - 'America/St_Vincent', - 'America/Swift_Current', - 'America/Tegucigalpa', - 'America/Thule', - 'America/Thunder_Bay', - 'America/Tijuana', - 'America/Toronto', - 'America/Tortola', - 'America/Vancouver', - 'America/Virgin', - 'America/Whitehorse', - 'America/Winnipeg', - 'America/Yakutat', - 'America/Yellowknife', - 'Antarctica/Casey', - 'Antarctica/Davis', - 'Antarctica/DumontDUrville', - 'Antarctica/Macquarie', - 'Antarctica/Mawson', - 'Antarctica/McMurdo', - 'Antarctica/Palmer', - 'Antarctica/Rothera', - 'Antarctica/South_Pole', - 'Antarctica/Syowa', - 'Antarctica/Troll', - 'Antarctica/Vostok', - 'Arctic/Longyearbyen', - 'Asia/Aden', - 'Asia/Almaty', - 'Asia/Amman', - 'Asia/Anadyr', - 'Asia/Aqtau', - 'Asia/Aqtobe', - 'Asia/Ashgabat', - 'Asia/Ashkhabad', - 'Asia/Atyrau', - 'Asia/Baghdad', - 'Asia/Bahrain', - 'Asia/Baku', - 'Asia/Bangkok', - 'Asia/Barnaul', - 'Asia/Beirut', - 'Asia/Bishkek', - 'Asia/Brunei', - 'Asia/Calcutta', - 'Asia/Chita', - 'Asia/Choibalsan', - 'Asia/Chongqing', - 'Asia/Chungking', - 'Asia/Colombo', - 'Asia/Dacca', - 'Asia/Damascus', - 'Asia/Dhaka', - 'Asia/Dili', - 'Asia/Dubai', - 'Asia/Dushanbe', - 'Asia/Famagusta', - 'Asia/Gaza', - 'Asia/Harbin', - 'Asia/Hebron', - 'Asia/Ho_Chi_Minh', - 'Asia/Hong_Kong', - 'Asia/Hovd', - 'Asia/Irkutsk', - 'Asia/Istanbul', - 'Asia/Jakarta', - 'Asia/Jayapura', - 'Asia/Jerusalem', - 'Asia/Kabul', - 'Asia/Kamchatka', - 'Asia/Karachi', - 'Asia/Kashgar', - 'Asia/Kathmandu', - 'Asia/Katmandu', - 'Asia/Khandyga', - 'Asia/Kolkata', - 'Asia/Krasnoyarsk', - 'Asia/Kuala_Lumpur', - 'Asia/Kuching', - 'Asia/Kuwait', - 'Asia/Macao', - 'Asia/Macau', - 'Asia/Magadan', - 'Asia/Makassar', - 'Asia/Manila', - 'Asia/Muscat', - 'Asia/Nicosia', - 'Asia/Novokuznetsk', - 'Asia/Novosibirsk', - 'Asia/Omsk', - 'Asia/Oral', - 'Asia/Phnom_Penh', - 'Asia/Pontianak', - 'Asia/Pyongyang', - 'Asia/Qatar', - 'Asia/Qostanay', - 'Asia/Qyzylorda', - 'Asia/Rangoon', - 'Asia/Riyadh', - 'Asia/Saigon', - 'Asia/Sakhalin', - 'Asia/Samarkand', - 'Asia/Seoul', - 'Asia/Shanghai', - 'Asia/Singapore', - 'Asia/Srednekolymsk', - 'Asia/Taipei', - 'Asia/Tashkent', - 'Asia/Tbilisi', - 'Asia/Tehran', - 'Asia/Tel_Aviv', - 'Asia/Thimbu', - 'Asia/Thimphu', - 'Asia/Tokyo', - 'Asia/Tomsk', - 'Asia/Ujung_Pandang', - 'Asia/Ulaanbaatar', - 'Asia/Ulan_Bator', - 'Asia/Urumqi', - 'Asia/Ust-Nera', - 'Asia/Vientiane', - 'Asia/Vladivostok', - 'Asia/Yakutsk', - 'Asia/Yangon', - 'Asia/Yekaterinburg', - 'Asia/Yerevan', - 'Atlantic/Azores', - 'Atlantic/Bermuda', - 'Atlantic/Canary', - 'Atlantic/Cape_Verde', - 'Atlantic/Faeroe', - 'Atlantic/Faroe', - 'Atlantic/Jan_Mayen', - 'Atlantic/Madeira', - 'Atlantic/Reykjavik', - 'Atlantic/South_Georgia', - 'Atlantic/St_Helena', - 'Atlantic/Stanley', - 'Australia/ACT', - 'Australia/Adelaide', - 'Australia/Brisbane', - 'Australia/Broken_Hill', - 'Australia/Canberra', - 'Australia/Currie', - 'Australia/Darwin', - 'Australia/Eucla', - 'Australia/Hobart', - 'Australia/LHI', - 'Australia/Lindeman', - 'Australia/Lord_Howe', - 'Australia/Melbourne', - 'Australia/NSW', - 'Australia/North', - 'Australia/Perth', - 'Australia/Queensland', - 'Australia/South', - 'Australia/Sydney', - 'Australia/Tasmania', - 'Australia/Victoria', - 'Australia/West', - 'Australia/Yancowinna', - 'Brazil/Acre', - 'Brazil/DeNoronha', - 'Brazil/East', - 'Brazil/West', - 'CET', - 'CST6CDT', - 'Canada/Atlantic', - 'Canada/Central', - 'Canada/Eastern', - 'Canada/Mountain', - 'Canada/Newfoundland', - 'Canada/Pacific', - 'Canada/Saskatchewan', - 'Canada/Yukon', - 'Chile/Continental', - 'Chile/EasterIsland', - 'Cuba', - 'EET', - 'EST', - 'EST5EDT', - 'Egypt', - 'Eire', - 'Etc/GMT', - 'Etc/GMT+0', - 'Etc/GMT+1', - 'Etc/GMT+10', - 'Etc/GMT+11', - 'Etc/GMT+12', - 'Etc/GMT+2', - 'Etc/GMT+3', - 'Etc/GMT+4', - 'Etc/GMT+5', - 'Etc/GMT+6', - 'Etc/GMT+7', - 'Etc/GMT+8', - 'Etc/GMT+9', - 'Etc/GMT-0', - 'Etc/GMT-1', - 'Etc/GMT-10', - 'Etc/GMT-11', - 'Etc/GMT-12', - 'Etc/GMT-13', - 'Etc/GMT-14', - 'Etc/GMT-2', - 'Etc/GMT-3', - 'Etc/GMT-4', - 'Etc/GMT-5', - 'Etc/GMT-6', - 'Etc/GMT-7', - 'Etc/GMT-8', - 'Etc/GMT-9', - 'Etc/GMT0', - 'Etc/Greenwich', - 'Etc/UCT', - 'Etc/UTC', - 'Etc/Universal', - 'Etc/Zulu', - 'Europe/Amsterdam', - 'Europe/Andorra', - 'Europe/Astrakhan', - 'Europe/Athens', - 'Europe/Belfast', - 'Europe/Belgrade', - 'Europe/Berlin', - 'Europe/Bratislava', - 'Europe/Brussels', - 'Europe/Bucharest', - 'Europe/Budapest', - 'Europe/Busingen', - 'Europe/Chisinau', - 'Europe/Copenhagen', - 'Europe/Dublin', - 'Europe/Gibraltar', - 'Europe/Guernsey', - 'Europe/Helsinki', - 'Europe/Isle_of_Man', - 'Europe/Istanbul', - 'Europe/Jersey', - 'Europe/Kaliningrad', - 'Europe/Kiev', - 'Europe/Kirov', - 'Europe/Lisbon', - 'Europe/Ljubljana', - 'Europe/London', - 'Europe/Luxembourg', - 'Europe/Madrid', - 'Europe/Malta', - 'Europe/Mariehamn', - 'Europe/Minsk', - 'Europe/Monaco', - 'Europe/Moscow', - 'Europe/Nicosia', - 'Europe/Oslo', - 'Europe/Paris', - 'Europe/Podgorica', - 'Europe/Prague', - 'Europe/Riga', - 'Europe/Rome', - 'Europe/Samara', - 'Europe/San_Marino', - 'Europe/Sarajevo', - 'Europe/Saratov', - 'Europe/Simferopol', - 'Europe/Skopje', - 'Europe/Sofia', - 'Europe/Stockholm', - 'Europe/Tallinn', - 'Europe/Tirane', - 'Europe/Tiraspol', - 'Europe/Ulyanovsk', - 'Europe/Uzhgorod', - 'Europe/Vaduz', - 'Europe/Vatican', - 'Europe/Vienna', - 'Europe/Vilnius', - 'Europe/Volgograd', - 'Europe/Warsaw', - 'Europe/Zagreb', - 'Europe/Zaporozhye', - 'Europe/Zurich', - 'GB', - 'GB-Eire', - 'GMT', - 'GMT+0', - 'GMT-0', - 'GMT0', - 'Greenwich', - 'HST', - 'Hongkong', - 'Iceland', - 'Indian/Antananarivo', - 'Indian/Chagos', - 'Indian/Christmas', - 'Indian/Cocos', - 'Indian/Comoro', - 'Indian/Kerguelen', - 'Indian/Mahe', - 'Indian/Maldives', - 'Indian/Mauritius', - 'Indian/Mayotte', - 'Indian/Reunion', - 'Iran', - 'Israel', - 'Jamaica', - 'Japan', - 'Kwajalein', - 'Libya', - 'MET', - 'MST', - 'MST7MDT', - 'Mexico/BajaNorte', - 'Mexico/BajaSur', - 'Mexico/General', - 'NZ', - 'NZ-CHAT', - 'Navajo', - 'PRC', - 'PST8PDT', - 'Pacific/Apia', - 'Pacific/Auckland', - 'Pacific/Bougainville', - 'Pacific/Chatham', - 'Pacific/Chuuk', - 'Pacific/Easter', - 'Pacific/Efate', - 'Pacific/Enderbury', - 'Pacific/Fakaofo', - 'Pacific/Fiji', - 'Pacific/Funafuti', - 'Pacific/Galapagos', - 'Pacific/Gambier', - 'Pacific/Guadalcanal', - 'Pacific/Guam', - 'Pacific/Honolulu', - 'Pacific/Johnston', - 'Pacific/Kanton', - 'Pacific/Kiritimati', - 'Pacific/Kosrae', - 'Pacific/Kwajalein', - 'Pacific/Majuro', - 'Pacific/Marquesas', - 'Pacific/Midway', - 'Pacific/Nauru', - 'Pacific/Niue', - 'Pacific/Norfolk', - 'Pacific/Noumea', - 'Pacific/Pago_Pago', - 'Pacific/Palau', - 'Pacific/Pitcairn', - 'Pacific/Pohnpei', - 'Pacific/Ponape', - 'Pacific/Port_Moresby', - 'Pacific/Rarotonga', - 'Pacific/Saipan', - 'Pacific/Samoa', - 'Pacific/Tahiti', - 'Pacific/Tarawa', - 'Pacific/Tongatapu', - 'Pacific/Truk', - 'Pacific/Wake', - 'Pacific/Wallis', - 'Pacific/Yap', - 'Poland', - 'Portugal', - 'ROC', - 'ROK', - 'Singapore', - 'Turkey', - 'UCT', - 'US/Alaska', - 'US/Aleutian', - 'US/Arizona', - 'US/Central', - 'US/East-Indiana', - 'US/Eastern', - 'US/Hawaii', - 'US/Indiana-Starke', - 'US/Michigan', - 'US/Mountain', - 'US/Pacific', - 'US/Samoa', - 'UTC', - 'Universal', - 'W-SU', - 'WET', - 'Zulu'] -all_timezones = LazyList( - tz for tz in all_timezones if resource_exists(tz)) - -all_timezones_set = LazySet(all_timezones) -common_timezones = \ -['Africa/Abidjan', - 'Africa/Accra', - 'Africa/Addis_Ababa', - 'Africa/Algiers', - 'Africa/Asmara', - 'Africa/Bamako', - 'Africa/Bangui', - 'Africa/Banjul', - 'Africa/Bissau', - 'Africa/Blantyre', - 'Africa/Brazzaville', - 'Africa/Bujumbura', - 'Africa/Cairo', - 'Africa/Casablanca', - 'Africa/Ceuta', - 'Africa/Conakry', - 'Africa/Dakar', - 'Africa/Dar_es_Salaam', - 'Africa/Djibouti', - 'Africa/Douala', - 'Africa/El_Aaiun', - 'Africa/Freetown', - 'Africa/Gaborone', - 'Africa/Harare', - 'Africa/Johannesburg', - 'Africa/Juba', - 'Africa/Kampala', - 'Africa/Khartoum', - 'Africa/Kigali', - 'Africa/Kinshasa', - 'Africa/Lagos', - 'Africa/Libreville', - 'Africa/Lome', - 'Africa/Luanda', - 'Africa/Lubumbashi', - 'Africa/Lusaka', - 'Africa/Malabo', - 'Africa/Maputo', - 'Africa/Maseru', - 'Africa/Mbabane', - 'Africa/Mogadishu', - 'Africa/Monrovia', - 'Africa/Nairobi', - 'Africa/Ndjamena', - 'Africa/Niamey', - 'Africa/Nouakchott', - 'Africa/Ouagadougou', - 'Africa/Porto-Novo', - 'Africa/Sao_Tome', - 'Africa/Tripoli', - 'Africa/Tunis', - 'Africa/Windhoek', - 'America/Adak', - 'America/Anchorage', - 'America/Anguilla', - 'America/Antigua', - 'America/Araguaina', - 'America/Argentina/Buenos_Aires', - 'America/Argentina/Catamarca', - 'America/Argentina/Cordoba', - 'America/Argentina/Jujuy', - 'America/Argentina/La_Rioja', - 'America/Argentina/Mendoza', - 'America/Argentina/Rio_Gallegos', - 'America/Argentina/Salta', - 'America/Argentina/San_Juan', - 'America/Argentina/San_Luis', - 'America/Argentina/Tucuman', - 'America/Argentina/Ushuaia', - 'America/Aruba', - 'America/Asuncion', - 'America/Atikokan', - 'America/Bahia', - 'America/Bahia_Banderas', - 'America/Barbados', - 'America/Belem', - 'America/Belize', - 'America/Blanc-Sablon', - 'America/Boa_Vista', - 'America/Bogota', - 'America/Boise', - 'America/Cambridge_Bay', - 'America/Campo_Grande', - 'America/Cancun', - 'America/Caracas', - 'America/Cayenne', - 'America/Cayman', - 'America/Chicago', - 'America/Chihuahua', - 'America/Costa_Rica', - 'America/Creston', - 'America/Cuiaba', - 'America/Curacao', - 'America/Danmarkshavn', - 'America/Dawson', - 'America/Dawson_Creek', - 'America/Denver', - 'America/Detroit', - 'America/Dominica', - 'America/Edmonton', - 'America/Eirunepe', - 'America/El_Salvador', - 'America/Fort_Nelson', - 'America/Fortaleza', - 'America/Glace_Bay', - 'America/Goose_Bay', - 'America/Grand_Turk', - 'America/Grenada', - 'America/Guadeloupe', - 'America/Guatemala', - 'America/Guayaquil', - 'America/Guyana', - 'America/Halifax', - 'America/Havana', - 'America/Hermosillo', - 'America/Indiana/Indianapolis', - 'America/Indiana/Knox', - 'America/Indiana/Marengo', - 'America/Indiana/Petersburg', - 'America/Indiana/Tell_City', - 'America/Indiana/Vevay', - 'America/Indiana/Vincennes', - 'America/Indiana/Winamac', - 'America/Inuvik', - 'America/Iqaluit', - 'America/Jamaica', - 'America/Juneau', - 'America/Kentucky/Louisville', - 'America/Kentucky/Monticello', - 'America/Kralendijk', - 'America/La_Paz', - 'America/Lima', - 'America/Los_Angeles', - 'America/Lower_Princes', - 'America/Maceio', - 'America/Managua', - 'America/Manaus', - 'America/Marigot', - 'America/Martinique', - 'America/Matamoros', - 'America/Mazatlan', - 'America/Menominee', - 'America/Merida', - 'America/Metlakatla', - 'America/Mexico_City', - 'America/Miquelon', - 'America/Moncton', - 'America/Monterrey', - 'America/Montevideo', - 'America/Montserrat', - 'America/Nassau', - 'America/New_York', - 'America/Nipigon', - 'America/Nome', - 'America/Noronha', - 'America/North_Dakota/Beulah', - 'America/North_Dakota/Center', - 'America/North_Dakota/New_Salem', - 'America/Nuuk', - 'America/Ojinaga', - 'America/Panama', - 'America/Pangnirtung', - 'America/Paramaribo', - 'America/Phoenix', - 'America/Port-au-Prince', - 'America/Port_of_Spain', - 'America/Porto_Velho', - 'America/Puerto_Rico', - 'America/Punta_Arenas', - 'America/Rainy_River', - 'America/Rankin_Inlet', - 'America/Recife', - 'America/Regina', - 'America/Resolute', - 'America/Rio_Branco', - 'America/Santarem', - 'America/Santiago', - 'America/Santo_Domingo', - 'America/Sao_Paulo', - 'America/Scoresbysund', - 'America/Sitka', - 'America/St_Barthelemy', - 'America/St_Johns', - 'America/St_Kitts', - 'America/St_Lucia', - 'America/St_Thomas', - 'America/St_Vincent', - 'America/Swift_Current', - 'America/Tegucigalpa', - 'America/Thule', - 'America/Thunder_Bay', - 'America/Tijuana', - 'America/Toronto', - 'America/Tortola', - 'America/Vancouver', - 'America/Whitehorse', - 'America/Winnipeg', - 'America/Yakutat', - 'America/Yellowknife', - 'Antarctica/Casey', - 'Antarctica/Davis', - 'Antarctica/DumontDUrville', - 'Antarctica/Macquarie', - 'Antarctica/Mawson', - 'Antarctica/McMurdo', - 'Antarctica/Palmer', - 'Antarctica/Rothera', - 'Antarctica/Syowa', - 'Antarctica/Troll', - 'Antarctica/Vostok', - 'Arctic/Longyearbyen', - 'Asia/Aden', - 'Asia/Almaty', - 'Asia/Amman', - 'Asia/Anadyr', - 'Asia/Aqtau', - 'Asia/Aqtobe', - 'Asia/Ashgabat', - 'Asia/Atyrau', - 'Asia/Baghdad', - 'Asia/Bahrain', - 'Asia/Baku', - 'Asia/Bangkok', - 'Asia/Barnaul', - 'Asia/Beirut', - 'Asia/Bishkek', - 'Asia/Brunei', - 'Asia/Chita', - 'Asia/Choibalsan', - 'Asia/Colombo', - 'Asia/Damascus', - 'Asia/Dhaka', - 'Asia/Dili', - 'Asia/Dubai', - 'Asia/Dushanbe', - 'Asia/Famagusta', - 'Asia/Gaza', - 'Asia/Hebron', - 'Asia/Ho_Chi_Minh', - 'Asia/Hong_Kong', - 'Asia/Hovd', - 'Asia/Irkutsk', - 'Asia/Jakarta', - 'Asia/Jayapura', - 'Asia/Jerusalem', - 'Asia/Kabul', - 'Asia/Kamchatka', - 'Asia/Karachi', - 'Asia/Kathmandu', - 'Asia/Khandyga', - 'Asia/Kolkata', - 'Asia/Krasnoyarsk', - 'Asia/Kuala_Lumpur', - 'Asia/Kuching', - 'Asia/Kuwait', - 'Asia/Macau', - 'Asia/Magadan', - 'Asia/Makassar', - 'Asia/Manila', - 'Asia/Muscat', - 'Asia/Nicosia', - 'Asia/Novokuznetsk', - 'Asia/Novosibirsk', - 'Asia/Omsk', - 'Asia/Oral', - 'Asia/Phnom_Penh', - 'Asia/Pontianak', - 'Asia/Pyongyang', - 'Asia/Qatar', - 'Asia/Qostanay', - 'Asia/Qyzylorda', - 'Asia/Riyadh', - 'Asia/Sakhalin', - 'Asia/Samarkand', - 'Asia/Seoul', - 'Asia/Shanghai', - 'Asia/Singapore', - 'Asia/Srednekolymsk', - 'Asia/Taipei', - 'Asia/Tashkent', - 'Asia/Tbilisi', - 'Asia/Tehran', - 'Asia/Thimphu', - 'Asia/Tokyo', - 'Asia/Tomsk', - 'Asia/Ulaanbaatar', - 'Asia/Urumqi', - 'Asia/Ust-Nera', - 'Asia/Vientiane', - 'Asia/Vladivostok', - 'Asia/Yakutsk', - 'Asia/Yangon', - 'Asia/Yekaterinburg', - 'Asia/Yerevan', - 'Atlantic/Azores', - 'Atlantic/Bermuda', - 'Atlantic/Canary', - 'Atlantic/Cape_Verde', - 'Atlantic/Faroe', - 'Atlantic/Madeira', - 'Atlantic/Reykjavik', - 'Atlantic/South_Georgia', - 'Atlantic/St_Helena', - 'Atlantic/Stanley', - 'Australia/Adelaide', - 'Australia/Brisbane', - 'Australia/Broken_Hill', - 'Australia/Darwin', - 'Australia/Eucla', - 'Australia/Hobart', - 'Australia/Lindeman', - 'Australia/Lord_Howe', - 'Australia/Melbourne', - 'Australia/Perth', - 'Australia/Sydney', - 'Canada/Atlantic', - 'Canada/Central', - 'Canada/Eastern', - 'Canada/Mountain', - 'Canada/Newfoundland', - 'Canada/Pacific', - 'Europe/Amsterdam', - 'Europe/Andorra', - 'Europe/Astrakhan', - 'Europe/Athens', - 'Europe/Belgrade', - 'Europe/Berlin', - 'Europe/Bratislava', - 'Europe/Brussels', - 'Europe/Bucharest', - 'Europe/Budapest', - 'Europe/Busingen', - 'Europe/Chisinau', - 'Europe/Copenhagen', - 'Europe/Dublin', - 'Europe/Gibraltar', - 'Europe/Guernsey', - 'Europe/Helsinki', - 'Europe/Isle_of_Man', - 'Europe/Istanbul', - 'Europe/Jersey', - 'Europe/Kaliningrad', - 'Europe/Kiev', - 'Europe/Kirov', - 'Europe/Lisbon', - 'Europe/Ljubljana', - 'Europe/London', - 'Europe/Luxembourg', - 'Europe/Madrid', - 'Europe/Malta', - 'Europe/Mariehamn', - 'Europe/Minsk', - 'Europe/Monaco', - 'Europe/Moscow', - 'Europe/Oslo', - 'Europe/Paris', - 'Europe/Podgorica', - 'Europe/Prague', - 'Europe/Riga', - 'Europe/Rome', - 'Europe/Samara', - 'Europe/San_Marino', - 'Europe/Sarajevo', - 'Europe/Saratov', - 'Europe/Simferopol', - 'Europe/Skopje', - 'Europe/Sofia', - 'Europe/Stockholm', - 'Europe/Tallinn', - 'Europe/Tirane', - 'Europe/Ulyanovsk', - 'Europe/Uzhgorod', - 'Europe/Vaduz', - 'Europe/Vatican', - 'Europe/Vienna', - 'Europe/Vilnius', - 'Europe/Volgograd', - 'Europe/Warsaw', - 'Europe/Zagreb', - 'Europe/Zaporozhye', - 'Europe/Zurich', - 'GMT', - 'Indian/Antananarivo', - 'Indian/Chagos', - 'Indian/Christmas', - 'Indian/Cocos', - 'Indian/Comoro', - 'Indian/Kerguelen', - 'Indian/Mahe', - 'Indian/Maldives', - 'Indian/Mauritius', - 'Indian/Mayotte', - 'Indian/Reunion', - 'Pacific/Apia', - 'Pacific/Auckland', - 'Pacific/Bougainville', - 'Pacific/Chatham', - 'Pacific/Chuuk', - 'Pacific/Easter', - 'Pacific/Efate', - 'Pacific/Fakaofo', - 'Pacific/Fiji', - 'Pacific/Funafuti', - 'Pacific/Galapagos', - 'Pacific/Gambier', - 'Pacific/Guadalcanal', - 'Pacific/Guam', - 'Pacific/Honolulu', - 'Pacific/Kanton', - 'Pacific/Kiritimati', - 'Pacific/Kosrae', - 'Pacific/Kwajalein', - 'Pacific/Majuro', - 'Pacific/Marquesas', - 'Pacific/Midway', - 'Pacific/Nauru', - 'Pacific/Niue', - 'Pacific/Norfolk', - 'Pacific/Noumea', - 'Pacific/Pago_Pago', - 'Pacific/Palau', - 'Pacific/Pitcairn', - 'Pacific/Pohnpei', - 'Pacific/Port_Moresby', - 'Pacific/Rarotonga', - 'Pacific/Saipan', - 'Pacific/Tahiti', - 'Pacific/Tarawa', - 'Pacific/Tongatapu', - 'Pacific/Wake', - 'Pacific/Wallis', - 'US/Alaska', - 'US/Arizona', - 'US/Central', - 'US/Eastern', - 'US/Hawaii', - 'US/Mountain', - 'US/Pacific', - 'UTC'] -common_timezones = LazyList( - tz for tz in common_timezones if tz in all_timezones) - -common_timezones_set = LazySet(common_timezones) diff --git a/telegramer/include/pytz/exceptions.py b/telegramer/include/pytz/exceptions.py deleted file mode 100644 index 4b20bde..0000000 --- a/telegramer/include/pytz/exceptions.py +++ /dev/null @@ -1,59 +0,0 @@ -''' -Custom exceptions raised by pytz. -''' - -__all__ = [ - 'UnknownTimeZoneError', 'InvalidTimeError', 'AmbiguousTimeError', - 'NonExistentTimeError', -] - - -class Error(Exception): - '''Base class for all exceptions raised by the pytz library''' - - -class UnknownTimeZoneError(KeyError, Error): - '''Exception raised when pytz is passed an unknown timezone. - - >>> isinstance(UnknownTimeZoneError(), LookupError) - True - - This class is actually a subclass of KeyError to provide backwards - compatibility with code relying on the undocumented behavior of earlier - pytz releases. - - >>> isinstance(UnknownTimeZoneError(), KeyError) - True - - And also a subclass of pytz.exceptions.Error, as are other pytz - exceptions. - - >>> isinstance(UnknownTimeZoneError(), Error) - True - - ''' - pass - - -class InvalidTimeError(Error): - '''Base class for invalid time exceptions.''' - - -class AmbiguousTimeError(InvalidTimeError): - '''Exception raised when attempting to create an ambiguous wallclock time. - - At the end of a DST transition period, a particular wallclock time will - occur twice (once before the clocks are set back, once after). Both - possibilities may be correct, unless further information is supplied. - - See DstTzInfo.normalize() for more info - ''' - - -class NonExistentTimeError(InvalidTimeError): - '''Exception raised when attempting to create a wallclock time that - cannot exist. - - At the start of a DST transition period, the wallclock time jumps forward. - The instants jumped over never occur. - ''' diff --git a/telegramer/include/pytz/lazy.py b/telegramer/include/pytz/lazy.py deleted file mode 100644 index 39344fc..0000000 --- a/telegramer/include/pytz/lazy.py +++ /dev/null @@ -1,172 +0,0 @@ -from threading import RLock -try: - from collections.abc import Mapping as DictMixin -except ImportError: # Python < 3.3 - try: - from UserDict import DictMixin # Python 2 - except ImportError: # Python 3.0-3.3 - from collections import Mapping as DictMixin - - -# With lazy loading, we might end up with multiple threads triggering -# it at the same time. We need a lock. -_fill_lock = RLock() - - -class LazyDict(DictMixin): - """Dictionary populated on first use.""" - data = None - - def __getitem__(self, key): - if self.data is None: - _fill_lock.acquire() - try: - if self.data is None: - self._fill() - finally: - _fill_lock.release() - return self.data[key.upper()] - - def __contains__(self, key): - if self.data is None: - _fill_lock.acquire() - try: - if self.data is None: - self._fill() - finally: - _fill_lock.release() - return key in self.data - - def __iter__(self): - if self.data is None: - _fill_lock.acquire() - try: - if self.data is None: - self._fill() - finally: - _fill_lock.release() - return iter(self.data) - - def __len__(self): - if self.data is None: - _fill_lock.acquire() - try: - if self.data is None: - self._fill() - finally: - _fill_lock.release() - return len(self.data) - - def keys(self): - if self.data is None: - _fill_lock.acquire() - try: - if self.data is None: - self._fill() - finally: - _fill_lock.release() - return self.data.keys() - - -class LazyList(list): - """List populated on first use.""" - - _props = [ - '__str__', '__repr__', '__unicode__', - '__hash__', '__sizeof__', '__cmp__', - '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', - 'append', 'count', 'index', 'extend', 'insert', 'pop', 'remove', - 'reverse', 'sort', '__add__', '__radd__', '__iadd__', '__mul__', - '__rmul__', '__imul__', '__contains__', '__len__', '__nonzero__', - '__getitem__', '__setitem__', '__delitem__', '__iter__', - '__reversed__', '__getslice__', '__setslice__', '__delslice__'] - - def __new__(cls, fill_iter=None): - - if fill_iter is None: - return list() - - # We need a new class as we will be dynamically messing with its - # methods. - class LazyList(list): - pass - - fill_iter = [fill_iter] - - def lazy(name): - def _lazy(self, *args, **kw): - _fill_lock.acquire() - try: - if len(fill_iter) > 0: - list.extend(self, fill_iter.pop()) - for method_name in cls._props: - delattr(LazyList, method_name) - finally: - _fill_lock.release() - return getattr(list, name)(self, *args, **kw) - return _lazy - - for name in cls._props: - setattr(LazyList, name, lazy(name)) - - new_list = LazyList() - return new_list - -# Not all versions of Python declare the same magic methods. -# Filter out properties that don't exist in this version of Python -# from the list. -LazyList._props = [prop for prop in LazyList._props if hasattr(list, prop)] - - -class LazySet(set): - """Set populated on first use.""" - - _props = ( - '__str__', '__repr__', '__unicode__', - '__hash__', '__sizeof__', '__cmp__', - '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', - '__contains__', '__len__', '__nonzero__', - '__getitem__', '__setitem__', '__delitem__', '__iter__', - '__sub__', '__and__', '__xor__', '__or__', - '__rsub__', '__rand__', '__rxor__', '__ror__', - '__isub__', '__iand__', '__ixor__', '__ior__', - 'add', 'clear', 'copy', 'difference', 'difference_update', - 'discard', 'intersection', 'intersection_update', 'isdisjoint', - 'issubset', 'issuperset', 'pop', 'remove', - 'symmetric_difference', 'symmetric_difference_update', - 'union', 'update') - - def __new__(cls, fill_iter=None): - - if fill_iter is None: - return set() - - class LazySet(set): - pass - - fill_iter = [fill_iter] - - def lazy(name): - def _lazy(self, *args, **kw): - _fill_lock.acquire() - try: - if len(fill_iter) > 0: - for i in fill_iter.pop(): - set.add(self, i) - for method_name in cls._props: - delattr(LazySet, method_name) - finally: - _fill_lock.release() - return getattr(set, name)(self, *args, **kw) - return _lazy - - for name in cls._props: - setattr(LazySet, name, lazy(name)) - - new_set = LazySet() - return new_set - -# Not all versions of Python declare the same magic methods. -# Filter out properties that don't exist in this version of Python -# from the list. -LazySet._props = [prop for prop in LazySet._props if hasattr(set, prop)] diff --git a/telegramer/include/pytz/reference.py b/telegramer/include/pytz/reference.py deleted file mode 100644 index f765ca0..0000000 --- a/telegramer/include/pytz/reference.py +++ /dev/null @@ -1,140 +0,0 @@ -''' -Reference tzinfo implementations from the Python docs. -Used for testing against as they are only correct for the years -1987 to 2006. Do not use these for real code. -''' - -from datetime import tzinfo, timedelta, datetime -from pytz import HOUR, ZERO, UTC - -__all__ = [ - 'FixedOffset', - 'LocalTimezone', - 'USTimeZone', - 'Eastern', - 'Central', - 'Mountain', - 'Pacific', - 'UTC' -] - - -# A class building tzinfo objects for fixed-offset time zones. -# Note that FixedOffset(0, "UTC") is a different way to build a -# UTC tzinfo object. -class FixedOffset(tzinfo): - """Fixed offset in minutes east from UTC.""" - - def __init__(self, offset, name): - self.__offset = timedelta(minutes=offset) - self.__name = name - - def utcoffset(self, dt): - return self.__offset - - def tzname(self, dt): - return self.__name - - def dst(self, dt): - return ZERO - - -import time as _time - -STDOFFSET = timedelta(seconds=-_time.timezone) -if _time.daylight: - DSTOFFSET = timedelta(seconds=-_time.altzone) -else: - DSTOFFSET = STDOFFSET - -DSTDIFF = DSTOFFSET - STDOFFSET - - -# A class capturing the platform's idea of local time. -class LocalTimezone(tzinfo): - - def utcoffset(self, dt): - if self._isdst(dt): - return DSTOFFSET - else: - return STDOFFSET - - def dst(self, dt): - if self._isdst(dt): - return DSTDIFF - else: - return ZERO - - def tzname(self, dt): - return _time.tzname[self._isdst(dt)] - - def _isdst(self, dt): - tt = (dt.year, dt.month, dt.day, - dt.hour, dt.minute, dt.second, - dt.weekday(), 0, -1) - stamp = _time.mktime(tt) - tt = _time.localtime(stamp) - return tt.tm_isdst > 0 - -Local = LocalTimezone() - - -def first_sunday_on_or_after(dt): - days_to_go = 6 - dt.weekday() - if days_to_go: - dt += timedelta(days_to_go) - return dt - - -# In the US, DST starts at 2am (standard time) on the first Sunday in April. -DSTSTART = datetime(1, 4, 1, 2) -# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct. -# which is the first Sunday on or after Oct 25. -DSTEND = datetime(1, 10, 25, 1) - - -# A complete implementation of current DST rules for major US time zones. -class USTimeZone(tzinfo): - - def __init__(self, hours, reprname, stdname, dstname): - self.stdoffset = timedelta(hours=hours) - self.reprname = reprname - self.stdname = stdname - self.dstname = dstname - - def __repr__(self): - return self.reprname - - def tzname(self, dt): - if self.dst(dt): - return self.dstname - else: - return self.stdname - - def utcoffset(self, dt): - return self.stdoffset + self.dst(dt) - - def dst(self, dt): - if dt is None or dt.tzinfo is None: - # An exception may be sensible here, in one or both cases. - # It depends on how you want to treat them. The default - # fromutc() implementation (called by the default astimezone() - # implementation) passes a datetime with dt.tzinfo is self. - return ZERO - assert dt.tzinfo is self - - # Find first Sunday in April & the last in October. - start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year)) - end = first_sunday_on_or_after(DSTEND.replace(year=dt.year)) - - # Can't compare naive to aware objects, so strip the timezone from - # dt first. - if start <= dt.replace(tzinfo=None) < end: - return HOUR - else: - return ZERO - -Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") -Central = USTimeZone(-6, "Central", "CST", "CDT") -Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") -Pacific = USTimeZone(-8, "Pacific", "PST", "PDT") diff --git a/telegramer/include/pytz/tzfile.py b/telegramer/include/pytz/tzfile.py deleted file mode 100644 index 99e7448..0000000 --- a/telegramer/include/pytz/tzfile.py +++ /dev/null @@ -1,133 +0,0 @@ -''' -$Id: tzfile.py,v 1.8 2004/06/03 00:15:24 zenzen Exp $ -''' - -from datetime import datetime -from struct import unpack, calcsize - -from pytz.tzinfo import StaticTzInfo, DstTzInfo, memorized_ttinfo -from pytz.tzinfo import memorized_datetime, memorized_timedelta - - -def _byte_string(s): - """Cast a string or byte string to an ASCII byte string.""" - return s.encode('ASCII') - -_NULL = _byte_string('\0') - - -def _std_string(s): - """Cast a string or byte string to an ASCII string.""" - return str(s.decode('ASCII')) - - -def build_tzinfo(zone, fp): - head_fmt = '>4s c 15x 6l' - head_size = calcsize(head_fmt) - (magic, format, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, - typecnt, charcnt) = unpack(head_fmt, fp.read(head_size)) - - # Make sure it is a tzfile(5) file - assert magic == _byte_string('TZif'), 'Got magic %s' % repr(magic) - - # Read out the transition times, localtime indices and ttinfo structures. - data_fmt = '>%(timecnt)dl %(timecnt)dB %(ttinfo)s %(charcnt)ds' % dict( - timecnt=timecnt, ttinfo='lBB' * typecnt, charcnt=charcnt) - data_size = calcsize(data_fmt) - data = unpack(data_fmt, fp.read(data_size)) - - # make sure we unpacked the right number of values - assert len(data) == 2 * timecnt + 3 * typecnt + 1 - transitions = [memorized_datetime(trans) - for trans in data[:timecnt]] - lindexes = list(data[timecnt:2 * timecnt]) - ttinfo_raw = data[2 * timecnt:-1] - tznames_raw = data[-1] - del data - - # Process ttinfo into separate structs - ttinfo = [] - tznames = {} - i = 0 - while i < len(ttinfo_raw): - # have we looked up this timezone name yet? - tzname_offset = ttinfo_raw[i + 2] - if tzname_offset not in tznames: - nul = tznames_raw.find(_NULL, tzname_offset) - if nul < 0: - nul = len(tznames_raw) - tznames[tzname_offset] = _std_string( - tznames_raw[tzname_offset:nul]) - ttinfo.append((ttinfo_raw[i], - bool(ttinfo_raw[i + 1]), - tznames[tzname_offset])) - i += 3 - - # Now build the timezone object - if len(ttinfo) == 1 or len(transitions) == 0: - ttinfo[0][0], ttinfo[0][2] - cls = type(zone, (StaticTzInfo,), dict( - zone=zone, - _utcoffset=memorized_timedelta(ttinfo[0][0]), - _tzname=ttinfo[0][2])) - else: - # Early dates use the first standard time ttinfo - i = 0 - while ttinfo[i][1]: - i += 1 - if ttinfo[i] == ttinfo[lindexes[0]]: - transitions[0] = datetime.min - else: - transitions.insert(0, datetime.min) - lindexes.insert(0, i) - - # calculate transition info - transition_info = [] - for i in range(len(transitions)): - inf = ttinfo[lindexes[i]] - utcoffset = inf[0] - if not inf[1]: - dst = 0 - else: - for j in range(i - 1, -1, -1): - prev_inf = ttinfo[lindexes[j]] - if not prev_inf[1]: - break - dst = inf[0] - prev_inf[0] # dst offset - - # Bad dst? Look further. DST > 24 hours happens when - # a timzone has moved across the international dateline. - if dst <= 0 or dst > 3600 * 3: - for j in range(i + 1, len(transitions)): - stdinf = ttinfo[lindexes[j]] - if not stdinf[1]: - dst = inf[0] - stdinf[0] - if dst > 0: - break # Found a useful std time. - - tzname = inf[2] - - # Round utcoffset and dst to the nearest minute or the - # datetime library will complain. Conversions to these timezones - # might be up to plus or minus 30 seconds out, but it is - # the best we can do. - utcoffset = int((utcoffset + 30) // 60) * 60 - dst = int((dst + 30) // 60) * 60 - transition_info.append(memorized_ttinfo(utcoffset, dst, tzname)) - - cls = type(zone, (DstTzInfo,), dict( - zone=zone, - _utc_transition_times=transitions, - _transition_info=transition_info)) - - return cls() - -if __name__ == '__main__': - import os.path - from pprint import pprint - base = os.path.join(os.path.dirname(__file__), 'zoneinfo') - tz = build_tzinfo('Australia/Melbourne', - open(os.path.join(base, 'Australia', 'Melbourne'), 'rb')) - tz = build_tzinfo('US/Eastern', - open(os.path.join(base, 'US', 'Eastern'), 'rb')) - pprint(tz._utc_transition_times) diff --git a/telegramer/include/pytz/tzinfo.py b/telegramer/include/pytz/tzinfo.py deleted file mode 100644 index 725978d..0000000 --- a/telegramer/include/pytz/tzinfo.py +++ /dev/null @@ -1,577 +0,0 @@ -'''Base classes and helpers for building zone specific tzinfo classes''' - -from datetime import datetime, timedelta, tzinfo -from bisect import bisect_right -try: - set -except NameError: - from sets import Set as set - -import pytz -from pytz.exceptions import AmbiguousTimeError, NonExistentTimeError - -__all__ = [] - -_timedelta_cache = {} - - -def memorized_timedelta(seconds): - '''Create only one instance of each distinct timedelta''' - try: - return _timedelta_cache[seconds] - except KeyError: - delta = timedelta(seconds=seconds) - _timedelta_cache[seconds] = delta - return delta - -_epoch = datetime.utcfromtimestamp(0) -_datetime_cache = {0: _epoch} - - -def memorized_datetime(seconds): - '''Create only one instance of each distinct datetime''' - try: - return _datetime_cache[seconds] - except KeyError: - # NB. We can't just do datetime.utcfromtimestamp(seconds) as this - # fails with negative values under Windows (Bug #90096) - dt = _epoch + timedelta(seconds=seconds) - _datetime_cache[seconds] = dt - return dt - -_ttinfo_cache = {} - - -def memorized_ttinfo(*args): - '''Create only one instance of each distinct tuple''' - try: - return _ttinfo_cache[args] - except KeyError: - ttinfo = ( - memorized_timedelta(args[0]), - memorized_timedelta(args[1]), - args[2] - ) - _ttinfo_cache[args] = ttinfo - return ttinfo - -_notime = memorized_timedelta(0) - - -def _to_seconds(td): - '''Convert a timedelta to seconds''' - return td.seconds + td.days * 24 * 60 * 60 - - -class BaseTzInfo(tzinfo): - # Overridden in subclass - _utcoffset = None - _tzname = None - zone = None - - def __str__(self): - return self.zone - - -class StaticTzInfo(BaseTzInfo): - '''A timezone that has a constant offset from UTC - - These timezones are rare, as most locations have changed their - offset at some point in their history - ''' - def fromutc(self, dt): - '''See datetime.tzinfo.fromutc''' - if dt.tzinfo is not None and dt.tzinfo is not self: - raise ValueError('fromutc: dt.tzinfo is not self') - return (dt + self._utcoffset).replace(tzinfo=self) - - def utcoffset(self, dt, is_dst=None): - '''See datetime.tzinfo.utcoffset - - is_dst is ignored for StaticTzInfo, and exists only to - retain compatibility with DstTzInfo. - ''' - return self._utcoffset - - def dst(self, dt, is_dst=None): - '''See datetime.tzinfo.dst - - is_dst is ignored for StaticTzInfo, and exists only to - retain compatibility with DstTzInfo. - ''' - return _notime - - def tzname(self, dt, is_dst=None): - '''See datetime.tzinfo.tzname - - is_dst is ignored for StaticTzInfo, and exists only to - retain compatibility with DstTzInfo. - ''' - return self._tzname - - def localize(self, dt, is_dst=False): - '''Convert naive time to local time''' - if dt.tzinfo is not None: - raise ValueError('Not naive datetime (tzinfo is already set)') - return dt.replace(tzinfo=self) - - def normalize(self, dt, is_dst=False): - '''Correct the timezone information on the given datetime. - - This is normally a no-op, as StaticTzInfo timezones never have - ambiguous cases to correct: - - >>> from pytz import timezone - >>> gmt = timezone('GMT') - >>> isinstance(gmt, StaticTzInfo) - True - >>> dt = datetime(2011, 5, 8, 1, 2, 3, tzinfo=gmt) - >>> gmt.normalize(dt) is dt - True - - The supported method of converting between timezones is to use - datetime.astimezone(). Currently normalize() also works: - - >>> la = timezone('America/Los_Angeles') - >>> dt = la.localize(datetime(2011, 5, 7, 1, 2, 3)) - >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' - >>> gmt.normalize(dt).strftime(fmt) - '2011-05-07 08:02:03 GMT (+0000)' - ''' - if dt.tzinfo is self: - return dt - if dt.tzinfo is None: - raise ValueError('Naive time - no tzinfo set') - return dt.astimezone(self) - - def __repr__(self): - return '' % (self.zone,) - - def __reduce__(self): - # Special pickle to zone remains a singleton and to cope with - # database changes. - return pytz._p, (self.zone,) - - -class DstTzInfo(BaseTzInfo): - '''A timezone that has a variable offset from UTC - - The offset might change if daylight saving time comes into effect, - or at a point in history when the region decides to change their - timezone definition. - ''' - # Overridden in subclass - - # Sorted list of DST transition times, UTC - _utc_transition_times = None - - # [(utcoffset, dstoffset, tzname)] corresponding to - # _utc_transition_times entries - _transition_info = None - - zone = None - - # Set in __init__ - - _tzinfos = None - _dst = None # DST offset - - def __init__(self, _inf=None, _tzinfos=None): - if _inf: - self._tzinfos = _tzinfos - self._utcoffset, self._dst, self._tzname = _inf - else: - _tzinfos = {} - self._tzinfos = _tzinfos - self._utcoffset, self._dst, self._tzname = ( - self._transition_info[0]) - _tzinfos[self._transition_info[0]] = self - for inf in self._transition_info[1:]: - if inf not in _tzinfos: - _tzinfos[inf] = self.__class__(inf, _tzinfos) - - def fromutc(self, dt): - '''See datetime.tzinfo.fromutc''' - if (dt.tzinfo is not None and - getattr(dt.tzinfo, '_tzinfos', None) is not self._tzinfos): - raise ValueError('fromutc: dt.tzinfo is not self') - dt = dt.replace(tzinfo=None) - idx = max(0, bisect_right(self._utc_transition_times, dt) - 1) - inf = self._transition_info[idx] - return (dt + inf[0]).replace(tzinfo=self._tzinfos[inf]) - - def normalize(self, dt): - '''Correct the timezone information on the given datetime - - If date arithmetic crosses DST boundaries, the tzinfo - is not magically adjusted. This method normalizes the - tzinfo to the correct one. - - To test, first we need to do some setup - - >>> from pytz import timezone - >>> utc = timezone('UTC') - >>> eastern = timezone('US/Eastern') - >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' - - We next create a datetime right on an end-of-DST transition point, - the instant when the wallclocks are wound back one hour. - - >>> utc_dt = datetime(2002, 10, 27, 6, 0, 0, tzinfo=utc) - >>> loc_dt = utc_dt.astimezone(eastern) - >>> loc_dt.strftime(fmt) - '2002-10-27 01:00:00 EST (-0500)' - - Now, if we subtract a few minutes from it, note that the timezone - information has not changed. - - >>> before = loc_dt - timedelta(minutes=10) - >>> before.strftime(fmt) - '2002-10-27 00:50:00 EST (-0500)' - - But we can fix that by calling the normalize method - - >>> before = eastern.normalize(before) - >>> before.strftime(fmt) - '2002-10-27 01:50:00 EDT (-0400)' - - The supported method of converting between timezones is to use - datetime.astimezone(). Currently, normalize() also works: - - >>> th = timezone('Asia/Bangkok') - >>> am = timezone('Europe/Amsterdam') - >>> dt = th.localize(datetime(2011, 5, 7, 1, 2, 3)) - >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' - >>> am.normalize(dt).strftime(fmt) - '2011-05-06 20:02:03 CEST (+0200)' - ''' - if dt.tzinfo is None: - raise ValueError('Naive time - no tzinfo set') - - # Convert dt in localtime to UTC - offset = dt.tzinfo._utcoffset - dt = dt.replace(tzinfo=None) - dt = dt - offset - # convert it back, and return it - return self.fromutc(dt) - - def localize(self, dt, is_dst=False): - '''Convert naive time to local time. - - This method should be used to construct localtimes, rather - than passing a tzinfo argument to a datetime constructor. - - is_dst is used to determine the correct timezone in the ambigous - period at the end of daylight saving time. - - >>> from pytz import timezone - >>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)' - >>> amdam = timezone('Europe/Amsterdam') - >>> dt = datetime(2004, 10, 31, 2, 0, 0) - >>> loc_dt1 = amdam.localize(dt, is_dst=True) - >>> loc_dt2 = amdam.localize(dt, is_dst=False) - >>> loc_dt1.strftime(fmt) - '2004-10-31 02:00:00 CEST (+0200)' - >>> loc_dt2.strftime(fmt) - '2004-10-31 02:00:00 CET (+0100)' - >>> str(loc_dt2 - loc_dt1) - '1:00:00' - - Use is_dst=None to raise an AmbiguousTimeError for ambiguous - times at the end of daylight saving time - - >>> try: - ... loc_dt1 = amdam.localize(dt, is_dst=None) - ... except AmbiguousTimeError: - ... print('Ambiguous') - Ambiguous - - is_dst defaults to False - - >>> amdam.localize(dt) == amdam.localize(dt, False) - True - - is_dst is also used to determine the correct timezone in the - wallclock times jumped over at the start of daylight saving time. - - >>> pacific = timezone('US/Pacific') - >>> dt = datetime(2008, 3, 9, 2, 0, 0) - >>> ploc_dt1 = pacific.localize(dt, is_dst=True) - >>> ploc_dt2 = pacific.localize(dt, is_dst=False) - >>> ploc_dt1.strftime(fmt) - '2008-03-09 02:00:00 PDT (-0700)' - >>> ploc_dt2.strftime(fmt) - '2008-03-09 02:00:00 PST (-0800)' - >>> str(ploc_dt2 - ploc_dt1) - '1:00:00' - - Use is_dst=None to raise a NonExistentTimeError for these skipped - times. - - >>> try: - ... loc_dt1 = pacific.localize(dt, is_dst=None) - ... except NonExistentTimeError: - ... print('Non-existent') - Non-existent - ''' - if dt.tzinfo is not None: - raise ValueError('Not naive datetime (tzinfo is already set)') - - # Find the two best possibilities. - possible_loc_dt = set() - for delta in [timedelta(days=-1), timedelta(days=1)]: - loc_dt = dt + delta - idx = max(0, bisect_right( - self._utc_transition_times, loc_dt) - 1) - inf = self._transition_info[idx] - tzinfo = self._tzinfos[inf] - loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo)) - if loc_dt.replace(tzinfo=None) == dt: - possible_loc_dt.add(loc_dt) - - if len(possible_loc_dt) == 1: - return possible_loc_dt.pop() - - # If there are no possibly correct timezones, we are attempting - # to convert a time that never happened - the time period jumped - # during the start-of-DST transition period. - if len(possible_loc_dt) == 0: - # If we refuse to guess, raise an exception. - if is_dst is None: - raise NonExistentTimeError(dt) - - # If we are forcing the pre-DST side of the DST transition, we - # obtain the correct timezone by winding the clock forward a few - # hours. - elif is_dst: - return self.localize( - dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6) - - # If we are forcing the post-DST side of the DST transition, we - # obtain the correct timezone by winding the clock back. - else: - return self.localize( - dt - timedelta(hours=6), - is_dst=False) + timedelta(hours=6) - - # If we get this far, we have multiple possible timezones - this - # is an ambiguous case occuring during the end-of-DST transition. - - # If told to be strict, raise an exception since we have an - # ambiguous case - if is_dst is None: - raise AmbiguousTimeError(dt) - - # Filter out the possiblilities that don't match the requested - # is_dst - filtered_possible_loc_dt = [ - p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst - ] - - # Hopefully we only have one possibility left. Return it. - if len(filtered_possible_loc_dt) == 1: - return filtered_possible_loc_dt[0] - - if len(filtered_possible_loc_dt) == 0: - filtered_possible_loc_dt = list(possible_loc_dt) - - # If we get this far, we have in a wierd timezone transition - # where the clocks have been wound back but is_dst is the same - # in both (eg. Europe/Warsaw 1915 when they switched to CET). - # At this point, we just have to guess unless we allow more - # hints to be passed in (such as the UTC offset or abbreviation), - # but that is just getting silly. - # - # Choose the earliest (by UTC) applicable timezone if is_dst=True - # Choose the latest (by UTC) applicable timezone if is_dst=False - # i.e., behave like end-of-DST transition - dates = {} # utc -> local - for local_dt in filtered_possible_loc_dt: - utc_time = ( - local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset) - assert utc_time not in dates - dates[utc_time] = local_dt - return dates[[min, max][not is_dst](dates)] - - def utcoffset(self, dt, is_dst=None): - '''See datetime.tzinfo.utcoffset - - The is_dst parameter may be used to remove ambiguity during DST - transitions. - - >>> from pytz import timezone - >>> tz = timezone('America/St_Johns') - >>> ambiguous = datetime(2009, 10, 31, 23, 30) - - >>> str(tz.utcoffset(ambiguous, is_dst=False)) - '-1 day, 20:30:00' - - >>> str(tz.utcoffset(ambiguous, is_dst=True)) - '-1 day, 21:30:00' - - >>> try: - ... tz.utcoffset(ambiguous) - ... except AmbiguousTimeError: - ... print('Ambiguous') - Ambiguous - - ''' - if dt is None: - return None - elif dt.tzinfo is not self: - dt = self.localize(dt, is_dst) - return dt.tzinfo._utcoffset - else: - return self._utcoffset - - def dst(self, dt, is_dst=None): - '''See datetime.tzinfo.dst - - The is_dst parameter may be used to remove ambiguity during DST - transitions. - - >>> from pytz import timezone - >>> tz = timezone('America/St_Johns') - - >>> normal = datetime(2009, 9, 1) - - >>> str(tz.dst(normal)) - '1:00:00' - >>> str(tz.dst(normal, is_dst=False)) - '1:00:00' - >>> str(tz.dst(normal, is_dst=True)) - '1:00:00' - - >>> ambiguous = datetime(2009, 10, 31, 23, 30) - - >>> str(tz.dst(ambiguous, is_dst=False)) - '0:00:00' - >>> str(tz.dst(ambiguous, is_dst=True)) - '1:00:00' - >>> try: - ... tz.dst(ambiguous) - ... except AmbiguousTimeError: - ... print('Ambiguous') - Ambiguous - - ''' - if dt is None: - return None - elif dt.tzinfo is not self: - dt = self.localize(dt, is_dst) - return dt.tzinfo._dst - else: - return self._dst - - def tzname(self, dt, is_dst=None): - '''See datetime.tzinfo.tzname - - The is_dst parameter may be used to remove ambiguity during DST - transitions. - - >>> from pytz import timezone - >>> tz = timezone('America/St_Johns') - - >>> normal = datetime(2009, 9, 1) - - >>> tz.tzname(normal) - 'NDT' - >>> tz.tzname(normal, is_dst=False) - 'NDT' - >>> tz.tzname(normal, is_dst=True) - 'NDT' - - >>> ambiguous = datetime(2009, 10, 31, 23, 30) - - >>> tz.tzname(ambiguous, is_dst=False) - 'NST' - >>> tz.tzname(ambiguous, is_dst=True) - 'NDT' - >>> try: - ... tz.tzname(ambiguous) - ... except AmbiguousTimeError: - ... print('Ambiguous') - Ambiguous - ''' - if dt is None: - return self.zone - elif dt.tzinfo is not self: - dt = self.localize(dt, is_dst) - return dt.tzinfo._tzname - else: - return self._tzname - - def __repr__(self): - if self._dst: - dst = 'DST' - else: - dst = 'STD' - if self._utcoffset > _notime: - return '' % ( - self.zone, self._tzname, self._utcoffset, dst - ) - else: - return '' % ( - self.zone, self._tzname, self._utcoffset, dst - ) - - def __reduce__(self): - # Special pickle to zone remains a singleton and to cope with - # database changes. - return pytz._p, ( - self.zone, - _to_seconds(self._utcoffset), - _to_seconds(self._dst), - self._tzname - ) - - -def unpickler(zone, utcoffset=None, dstoffset=None, tzname=None): - """Factory function for unpickling pytz tzinfo instances. - - This is shared for both StaticTzInfo and DstTzInfo instances, because - database changes could cause a zones implementation to switch between - these two base classes and we can't break pickles on a pytz version - upgrade. - """ - # Raises a KeyError if zone no longer exists, which should never happen - # and would be a bug. - tz = pytz.timezone(zone) - - # A StaticTzInfo - just return it - if utcoffset is None: - return tz - - # This pickle was created from a DstTzInfo. We need to - # determine which of the list of tzinfo instances for this zone - # to use in order to restore the state of any datetime instances using - # it correctly. - utcoffset = memorized_timedelta(utcoffset) - dstoffset = memorized_timedelta(dstoffset) - try: - return tz._tzinfos[(utcoffset, dstoffset, tzname)] - except KeyError: - # The particular state requested in this timezone no longer exists. - # This indicates a corrupt pickle, or the timezone database has been - # corrected violently enough to make this particular - # (utcoffset,dstoffset) no longer exist in the zone, or the - # abbreviation has been changed. - pass - - # See if we can find an entry differing only by tzname. Abbreviations - # get changed from the initial guess by the database maintainers to - # match reality when this information is discovered. - for localized_tz in tz._tzinfos.values(): - if (localized_tz._utcoffset == utcoffset and - localized_tz._dst == dstoffset): - return localized_tz - - # This (utcoffset, dstoffset) information has been removed from the - # zone. Add it back. This might occur when the database maintainers have - # corrected incorrect information. datetime instances using this - # incorrect information will continue to do so, exactly as they were - # before being pickled. This is purely an overly paranoid safety net - I - # doubt this will ever been needed in real life. - inf = (utcoffset, dstoffset, tzname) - tz._tzinfos[inf] = tz.__class__(inf, tz._tzinfos) - return tz._tzinfos[inf] diff --git a/telegramer/include/pytz/zoneinfo/Africa/Abidjan b/telegramer/include/pytz/zoneinfo/Africa/Abidjan deleted file mode 100644 index 28b32ab2e0b9053f39a91d9f28b6072e41423954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmWHE%1kq2zzZ0GvP?kCG3nVP561uh|5!kkv-tRiFt`J82nmM#2LhZ1aRE&;-~s@2 CSQl9U diff --git a/telegramer/include/pytz/zoneinfo/Africa/Accra b/telegramer/include/pytz/zoneinfo/Africa/Accra deleted file mode 100644 index 28b32ab2e0b9053f39a91d9f28b6072e41423954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmWHE%1kq2zzZ0GvP?kCG3nVP561uh|5!kkv-tRiFt`J82nmM#2LhZ1aRE&;-~s@2 CSQl9U diff --git a/telegramer/include/pytz/zoneinfo/Africa/Addis_Ababa b/telegramer/include/pytz/zoneinfo/Africa/Addis_Ababa deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Algiers b/telegramer/include/pytz/zoneinfo/Africa/Algiers deleted file mode 100644 index 6cfd8a16e16ec08c7cd83e6c0e1f9e1bbc5dc18a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 735 zcmc(cy)Q#y6o=1kuUi`BDmsWkq>~`2@DC^s0jTmZVtF=Cg8YM;% z1BOV56Q39_`U7bWbVr9mP^H=`H76m zSNfgZ%bc@!HL3QuKAnR?pDLtI)M2(+9S!=`@p?oRM>bXQ+;VQJmQ$L)$d+E43gw+) z`(az;aW!B+%>D_ zc4ssxhUs$uIHuL0k7?b$A0I?-yD7 B^{M~> diff --git a/telegramer/include/pytz/zoneinfo/Africa/Asmara b/telegramer/include/pytz/zoneinfo/Africa/Asmara deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Asmera b/telegramer/include/pytz/zoneinfo/Africa/Asmera deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Bamako b/telegramer/include/pytz/zoneinfo/Africa/Bamako deleted file mode 100644 index 28b32ab2e0b9053f39a91d9f28b6072e41423954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmWHE%1kq2zzZ0GvP?kCG3nVP561uh|5!kkv-tRiFt`J82nmM#2LhZ1aRE&;-~s@2 CSQl9U diff --git a/telegramer/include/pytz/zoneinfo/Africa/Bangui b/telegramer/include/pytz/zoneinfo/Africa/Bangui deleted file mode 100644 index afb6a4a8fb17b0d4670b8ea1b38f5cc6100244e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmWHE%1kq2zzbM_vLGzfwz}YAPe200v{lX*7Y4qsU}RuoW?*2}hw28ZVdr4rU|`@A xVBqud4PkHxVr>HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#0|N_42?K|ZZwP~~ ffgyuCkY->6p%4;G{tpBo(?LcNZvz+5G6OCE+{Pmd diff --git a/telegramer/include/pytz/zoneinfo/Africa/Blantyre b/telegramer/include/pytz/zoneinfo/Africa/Blantyre deleted file mode 100644 index 52753c0f87bbfa457ada89d400908a3d6537ac0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmWHE%1kq2zzZ0GvP?kC(d2gY3y>q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Brazzaville b/telegramer/include/pytz/zoneinfo/Africa/Brazzaville deleted file mode 100644 index afb6a4a8fb17b0d4670b8ea1b38f5cc6100244e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmWHE%1kq2zzbM_vLGzfwz}YAPe200v{lX*7Y4qsU}RuoW?*2}hw28ZVdr4rU|`@A xVBqud4PkHxVr>HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Cairo b/telegramer/include/pytz/zoneinfo/Africa/Cairo deleted file mode 100644 index d3f819623fc9ef90d327380fad15341ec1a0e202..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1955 zcmdVaZ)nw39LMoGe-G^V-ra*L3BUZ7pvu|6G|i=@<8oQbzt(p;QznmXNO4 zL87pXustvmRJ5YhGPA6O3K`U#)lS9~6!a`2m)ShcP1()0&imNI2ztIf3v&OeEVgu zZhv*9(eJL&-#s6iV=uiIbd^6GcJ-D7=SH@K=Q=|D%lV8w|M3&z*Y+mcy{SiB_+X#3 zYYNqG8-KDr%a@6Z4U=T=oDHh4cB{NJC8jQyAG4PSlcN8+Y}w!0CNlk%_V>dB>dL7@ zcHpgb>W>3Ga`1)yLTo9tkySsdtfpmh$l|ydT7Oy&o3UMFFNoRUxg9E}ASrXYM~LXK zXKb`Rp+=q^u(^9bSEG)U%4=Th6Qg%NV#hpPq+&0>F2~l^iE&TH<@kzDG2!08UYj>w zP2AROCuXWep4?&cPBp4Yy$O5$A)zLBNEzQcTTI!%!``4)sr*eRDITcIALZNYkhlFiz$zOs4I$&hZPswgUXyw!^-dX26uM75zg7y93)zw z4ex4R9n5{_!LX{~{^0J7Yt6j620j0&It{-E#+*?n2(R*S&4zeIsuidFRMm zN8UT~=8<=gynW>TBMl%OcwGxf4_?;<(go56(g)HA(h1TE(hJfI(hbrM(ht%Q(vjD- zg!JTfO(9(&Z6SRjjiK+1FSN$F-hk$i?vVC4`a>Gz=n!cU>Cx+&7FuDz~pq;I5gq;sToj^2^xk?xW9Ir>L7fMW-c zEkO1F*#wSV@Vae4_QC5m0@(>(K7*$iYiknKSB1KAK{N02Q+_5|4!WLLayTabP6 zx{X102H6^9Z;;JFb_dxWWPgwiLUst*B4m${O+t3b>$VBmC$HNmWT%j=LiP&TEU()w auiGwUzr1e4yl%(v|FmUMH)~mZN$5l{B`ll2!c#XC?l+xCu56QT8 z9~rBkC*$y2Dl{OB!k&(zu=EiXR%K7&FR!KWNnhG#JV@IzPb7w*`%V>Z39<{%( zh%zics*JDKt2uTvUUO&6Gxt2twV&l{divpANU+ew&K@*4;Lggi_w5D1Ath{V(~fe?zRrQ$$ZET)zVgkX3e83@rp$Ob|< z5YmAV4-e!6As|yr2tq`rmJx)IAfyB#CI~q}2ns?{rWTc{Wd$KDQ%eg%T&9*6guqNK zF;j~SLS_&`Gqu!AEj9?bnOblVk~6jFAY^B1;Xz2x)Z#O>{2&Uz)Fl8>1g0(nh(a)R zDL@p1smlSPAWU5n5Jh3?vVbTIQhggoAXAqRL=l;~j35dLqLd(t38I`J3d+%1*IdfV!5cW9}u?jp?SWV0>$(b7;t}y4GkevCxe-Y;V zWXW0J`~lAe{*v?XPfsfBkGMrwWzbJi-UQFZUa#|9 z93wf8+1=suW9ua6@xff)m&}u#w(?1yZ9hoP(sc2?^qk}*qxemklJlfG$M;W~Bxl)V zB>(+Wo*8^z-rB))`D4jhQIf*v6}^(P(&EPZN|Kyaepx(MrAp2-ZeD!5K0lu%IUAhH_-nqf|hukISt;RIo-x`&i!#VeO f9=^Eo|Ne2C?T_GpyEprv(D`rZA5-5IKJ)(u0)S^{ diff --git a/telegramer/include/pytz/zoneinfo/Africa/Ceuta b/telegramer/include/pytz/zoneinfo/Africa/Ceuta deleted file mode 100644 index 850c8f06fa7918684e67e9ab8192ac933dac90b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2036 zcmdtiUr3dA9LMqJsm>Iq?(0}J%Ca)EKPNqEx@wx$Nu^C|=BezDnPpbX&Q50~+DcC( z{X^t?Aw@)+QCP@wZP>c7h@v7XBnXR&2#WqPN>))r#Qok6gI(-uH@n#H;W@8|0|(B< z^ZxjjuCGY7{&B+0H{6_BbMrhTWIn#XwI%Sw5&z0o*Q-G5Q@?ex^LS6&@l@-l=J|nB zyFamh-n-TMCEyA5CIn~pUVIe@-%uY4zp}CC!)K9JWLaKMRA+W5y7BkGM>h*Yl6W*Q z>`aR_vOy(o+YT938I;k>SId}upTuY8$XIuVB*Z4lxX4c>(dE^|w^MZd;{=`1Jwzv7 z4%12P@6~D2pWGOf2tr=Ro6=l6;=v!ztCI;QK4U9&X%P^`|Zh}T*5(`0t8OTFd8Wlq9V&GGh0 zuB%h?;vY!f%O^5-;02l6bydE2bX2}Ro5EnK`$7gVm% zqR%STm!GE#htAVQ?g_f+okxoY=SuPIk9Eo0@v`*SH@d7lLY6njNJ;x|vZC&_ls0wA z%F=Er+x|jVWuKANCD*k)?x3v6YSW7F)4Fzq>e^=qbX{Li*I%vCukZVG!--{Dd1aGq z+>@pLmLmCPU7QAXrOT$mObJ#Dm2Xo<%I2IuWJ`=CRmp?3s^_6pN59kR8<(W!d>R zEh9Z6O(R_+Z6kdnjU$~Sts}i7%_H4An)Z?Y9nA(HJAiBfvIoc}AiIEU1F{duMj$(Z zYz49x$YvnB;b^u4*$+pvA;^v(TY~HfvMI=}AlriM3$iiD&LCTZ>B2*3sO9ooEGsXevbCe diff --git a/telegramer/include/pytz/zoneinfo/Africa/Conakry b/telegramer/include/pytz/zoneinfo/Africa/Conakry deleted file mode 100644 index 28b32ab2e0b9053f39a91d9f28b6072e41423954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmWHE%1kq2zzZ0GvP?kCG3nVP561uh|5!kkv-tRiFt`J82nmM#2LhZ1aRE&;-~s@2 CSQl9U diff --git a/telegramer/include/pytz/zoneinfo/Africa/Dakar b/telegramer/include/pytz/zoneinfo/Africa/Dakar deleted file mode 100644 index 28b32ab2e0b9053f39a91d9f28b6072e41423954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmWHE%1kq2zzZ0GvP?kCG3nVP561uh|5!kkv-tRiFt`J82nmM#2LhZ1aRE&;-~s@2 CSQl9U diff --git a/telegramer/include/pytz/zoneinfo/Africa/Dar_es_Salaam b/telegramer/include/pytz/zoneinfo/Africa/Dar_es_Salaam deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Djibouti b/telegramer/include/pytz/zoneinfo/Africa/Djibouti deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Douala b/telegramer/include/pytz/zoneinfo/Africa/Douala deleted file mode 100644 index afb6a4a8fb17b0d4670b8ea1b38f5cc6100244e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmWHE%1kq2zzbM_vLGzfwz}YAPe200v{lX*7Y4qsU}RuoW?*2}hw28ZVdr4rU|`@A xVBqud4PkHxVr>HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#VxM4$sA zZjO<1!_OqfhyB$Cr%$6UV zF+Hp8+aC`$wtqNke|7SbGj{hKdt7a&Gk!zm>}zhK1iv0ih&ZkivYIHd@O?@gzeYQJ z>S>4VZ8C2>NaohZ$UOP7vcx7)(nD#KR5+!QTD&Rw>5Y`U5J@}DhiPZ=XUh7ZMOnub zrKr1GgL?Z1swUeXmcjcJ2ssm+22RiyyzGVgFSJ3g8b z@QaPJ7-8!pj8J~BH!T0`K)3!lJFuuPc!2MN3ry_;E;s?g3lMIA@B@S+AUt7eS3vl} z)XsqL2825x`~l$*2#-Lx#MC}9wNoIxVrsWQ_{G$Yf$)r}U1MtBKsX1&JEnGzsr>`t zAX9q?!bPU`5rmUW?Ij2|nc7dLb`*rCOzkQNUzyrj5Z*GiyCD2!YKK91%+xM}@R_Nd z2H`bRyA8r`5RQZJoT*(0;X6}155jw;S-rtS=g-hk*1i2i`+5QrWzb(cW&iK#mU7H=yhrc-j3s4#`OBwliUFeAQ{ zhDgq`zAc3PviVE}9&xo2b5L@YXD2Jn17Nvq^+SV-mX|71-OuQ&F6#dG65$=Otw z$LmcuC1=8~)(+vo^`u>EFQTJ2y$rt_c&*-6qM|({_%}_e@L93uUkK{)G<7 z*=ru+xwk@cUS22v3zw}Q^ZFMHQ~dca!zE|m@DX0`_mP}e8pC=2ibrw|INUrBG)vB_ z+qUxgt4_%|^yp8#KNK%Hzn-!3JRBl9ulE^z{`!0fuaCHLc^(;%oSy7eyzV(6IY%SU z^Zuw!a*hQ&#q(ISq%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Harare b/telegramer/include/pytz/zoneinfo/Africa/Harare deleted file mode 100644 index 52753c0f87bbfa457ada89d400908a3d6537ac0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmWHE%1kq2zzZ0GvP?kC(d2gY3y>q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Johannesburg b/telegramer/include/pytz/zoneinfo/Africa/Johannesburg deleted file mode 100644 index b1c425daced454f53d7d18fea807bf8d081cf97e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zzf)bvMfN%*#IP(+|Fm5S=ZBWc3ytLxxT!H^L2p*jLb|x$iN`w093#r zDgjchWxxm|WfT}#e0)O~f*pfH7(z&}>OTa}0vc4x1SA0EY#XPnJp z8QJhjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Khartoum b/telegramer/include/pytz/zoneinfo/Africa/Khartoum deleted file mode 100644 index 8ee8cb92e72d9c507ad0ee06dc6a38406ab06f34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 679 zcmcK1yDtPm9Ki9}d%0V8IInXYHlYv~iCh#K@yaFQkxX*ipfJ@Pg(!3qm#dJ7#vh;& z5>aWi8ja$;?cBe0o?}a$Z7-wrl zhPFRUc)6k@s%oNRFFH1%OkJ-hh>~_izi}USWxa`+jub zO_|<%Qq7f9-LiVCT4(dRZRT9H=T~HA;6QaG4|O)ap|Yr WK&F690+|Lf5&WO2xOO~41@{Xi&4u6q diff --git a/telegramer/include/pytz/zoneinfo/Africa/Kigali b/telegramer/include/pytz/zoneinfo/Africa/Kigali deleted file mode 100644 index 52753c0f87bbfa457ada89d400908a3d6537ac0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmWHE%1kq2zzZ0GvP?kC(d2gY3y>q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Kinshasa b/telegramer/include/pytz/zoneinfo/Africa/Kinshasa deleted file mode 100644 index afb6a4a8fb17b0d4670b8ea1b38f5cc6100244e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmWHE%1kq2zzbM_vLGzfwz}YAPe200v{lX*7Y4qsU}RuoW?*2}hw28ZVdr4rU|`@A xVBqud4PkHxVr>HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Lusaka b/telegramer/include/pytz/zoneinfo/Africa/Lusaka deleted file mode 100644 index 52753c0f87bbfa457ada89d400908a3d6537ac0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 zcmWHE%1kq2zzZ0GvP?kC(d2gY3y>q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Malabo b/telegramer/include/pytz/zoneinfo/Africa/Malabo deleted file mode 100644 index afb6a4a8fb17b0d4670b8ea1b38f5cc6100244e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmWHE%1kq2zzbM_vLGzfwz}YAPe200v{lX*7Y4qsU}RuoW?*2}hw28ZVdr4rU|`@A xVBqud4PkHxVr>HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#q%15z%dz`)|;8^Yl17{U-jf+7Eb0H;A*K+|-M FxBzz~6CMBn diff --git a/telegramer/include/pytz/zoneinfo/Africa/Maseru b/telegramer/include/pytz/zoneinfo/Africa/Maseru deleted file mode 100644 index b1c425daced454f53d7d18fea807bf8d081cf97e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zzf)bvMfN%*#IP(+|Fm5S=ZBWc3ytLxxT!H^L2p*jLb|x$iN`w093#r zDgjchWxxm|WfT}#e0)O~f*pfH7(z&}>OTOThjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Monrovia b/telegramer/include/pytz/zoneinfo/Africa/Monrovia deleted file mode 100644 index 6d688502a1ca80f2e57a6de2790ac3193879d248..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmWHE%1kq2zzdjxvMfN%(*PtE#OKUmJ{6V6$i)2r|JNrB3}BK4NKOJPVqoC#@eN_{ i1!8v~4k5w#|3DB`wQLTE23Z5Nje!KKxPbNhjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiURq3(N diff --git a/telegramer/include/pytz/zoneinfo/Africa/Ndjamena b/telegramer/include/pytz/zoneinfo/Africa/Ndjamena deleted file mode 100644 index a968845e29b8b2b47d4a73f74ae04ef681d7d485..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMVm=~+XHP+DPuu-d%@MkYoE20j^(Mm_-s76t|x1x5}Z-w=jy gM-UDUVF)3?HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#HhV*`e8#}I}P5^VYp1R&c$G{{B}4YCzPlWsE?(0W}%E&#-3N{@?jVZM*EQs%vx@B49?`FM-@HCOyP9P_uv%;tl)i^Sd8iFF@8 zl9lyqtMa(2t3%_edRW%a*-O7=ds5o9@5=rd(5+AXe%tM`Y%d@C9p?`+R@(48_if#^ zQ?I(WUZkU@RnN+%?#*4PcsimJsj#0+j>*1>Q{T0e5n`? z1y|&!-*2qu3%3vrOPnO;gv@@MEK$d^Xq=h##8hU1#S} z)P`MK+$q)`qW0yZXc{GOcU*3^$JA}*$-Y|)tNXcdF<5Il zp~|7vd9dQBa-S95OF6OhkTWpc>%=1gXE1$cCHx6%q8xNG>2;Zmx2oC9o67kws`*$` z} nNN6NB5*&$+gh%2d17I-(WDv+Okb$`Bq2Rv{CZK0Dk_>zRbScYW diff --git a/telegramer/include/pytz/zoneinfo/Africa/Windhoek b/telegramer/include/pytz/zoneinfo/Africa/Windhoek deleted file mode 100644 index abecd137b1fc3220637b22ffea0e7256a58e9377..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 955 zcmc)I!AsL&9LMqR%nh5Mk6I|nF@gftvuMYOMwIr)^XzF zN$lXk4jlx=lO6@32$Sei&_Q-G!c!0-BI)z~{Q-g}zp}kRFY>%U&gghLEN5QS{?%tc z<=TGDB)&fTvYOSqOPR@P--lc<`)RCxyizs1{w<;I|Elq+SBGnIDw6o6ZRSw73n;xRf(d1lK5Gux(-Wq_x5#j@5f(#|NUjtv+-T`&K#J&g-!k7@viBA`A!eC zubGEq8!}imZ<4V^aSB(}P+(ey4vb34BT4PNP{X-48Ch|ZC+||8H~*YJBC5c?gFsKpBi)hqNPlDl$PSP#AbUVIf$Rd=2C@&1jUYSW*b1^2SGyTxH?DR&$bPts M*pS~2DH!&j0&cX-R{#J2 diff --git a/telegramer/include/pytz/zoneinfo/America/Adak b/telegramer/include/pytz/zoneinfo/America/Adak deleted file mode 100644 index 43236498f681cc06f64ca2afa613880331fe6fbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2356 zcmciCZ%ma{0LSsm-|Iy&Dj-Bm!Hl7RhrdBt9tc86oi^IjQM+~Y*&*2zbbYMq#bZoSBp@5Baf)v>D*mc{?KkJo0^@vqt7j*K)_f*Qz7dmyMORerXqQyXsN^AUFrngI#QPeLpD-u*z z;!c^J5v-nYdu2{syvVthEpz9B#FOV@C(cetPtrc&0yEuYLc7kS()1Z~s}9 zUv^19TmOkFSpAJIEa+2(zuu5VDIbfXi{r95{E#Rf8IdJ3&EomNZ}s}`4ye+ulX}Bf zuc)#u1KK%SqRQ9o)*CyLRYhEt_Es)b-nm>|nRQcDUagdymWGQ>XLID{J2yo2N3rt7 z>2a}T|D1ejY(&)5Ps^=C?}*yc+q&-HNwqEIvfkb}pz6cNbVJc@)i85hHzro8#tZv& zlRH;64cF`DYm3#ZM|?e%fCW* zj|Cb zzK@T~_1P7d%kOV+T)}>Sdu_lx`(0pviLm!bzOER*zqd7DiM=mcU+Q&js4#Dpc^$7S z-`w*Hyso@;=CaOQ%n9Jb`Rn5S?~#R>Kk#ynn3sFJ-<-8){usyZgVi<2=#b%A&G?W3 zq8%X@hR88v1O|zW5*a2kPGq3SNRgph%~+AaTFq#Y;UeQj28@gt88R|vWYEZ{kzpg_ zMh1?I92q(?c4Y9#=#k-D&G@Y*07wLo5Fjx?f`CK;2?G)bBoIg>kWe78K!Slp!)n5T z#KUR=fb2@Mh(BsfTPknkY!v6=uO5kf+Q#0Uuz5+x)| zNSu&BA(28tg~SR877{J12^SJCs|gqqF{=p~5;G)dNYs$9A#pIBoav^lt?U*U?R~(!imJwY66Nx)M`SC#MEkn zibNF&D-u^Eut;Q)&?2!#f{R2K2`>^~s|hd?VXFx-5@V|gG7@DZ%t)M(KqHYxLXCH0 z9UK@EdauXrnRg$bziVB+?f+@^KheH>3o}7a6Q=0Nr5UN|sUo>FEiE-IRfPQs4Q6_I diff --git a/telegramer/include/pytz/zoneinfo/America/Anchorage b/telegramer/include/pytz/zoneinfo/America/Anchorage deleted file mode 100644 index 9bbb2fd3b361ea8aa4c126d14df5fa370343a63f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2371 zcmciCZA{fw0LSqQ0v8C15>k=qB=Y>=0R^EbF9-r60dl(ukxF8BSP2AU_(W1Vaw{?0 z9L`3^IkwuQo#|G(7TvVgCgupY9>$_{YbHzAdYVOYJKvM57d7AI|G)G99PW7g`??!i zp3HIl>j^WzaCr8a!#!oE`Hb$#^NlC`+&11+EPo#_Q!^(vxcqOdkdA>;SHO!YGO#<@ zHLJZu2Q@AC1=l9&kfKDNGdol}UtZ@6i<;75!xOIXAI|FAz8UpJe0f<$`i6bCpB$BU zym`hIb#PeTx#y_st}Xp?cFSH@bbY&wsc3WET~H_Iq^@?&UC^rMg)MQ#2G;7>^ysMA zAB*+;i-{_3e4)PQlvBkY3(@x;zN|!7fxNGGR4wq#mkFD`6AN>%%fyvuL{iMxGCA$2 zNS>M2so{G?>f~2CZK_SAkG!ul&cCEG2M_D4;#%***zEp@Jt`Ej#F{-qRIF#U_T|Ko7^z{KaGP$%gJ-#sZF+83&q9Xcdjty8*a z*E_1X`mA2wd{C7vdP|pAR2}u z#M%kO?^ky6Pf4q2Jddw9I5rjGOyZrWxw_&S19i% zow~)Du3CmYdefyy_0)k5`Se(tc&6(SxmibuR?kw|)_+yB=gpJPwvLI8m}%KreN1%v z=jg8dbE<3dH{Cr~tL~8rz2(||wRP}4z3q!mwY}$cz2k&O^{nmH&kf|OfWTP+LBThB zLqeUm@O3yoyykHD{T=HaL4JR4TR^D&M%Z7X>^+9BBi8Tl-x&~Z?+L4_+>W9;a~?IP z#+-8gC@*n4>bX>!OHrk{nJ0h`&tDh!e{U_^`~!#Q6?3?!_|3EI)b&qsM_*AnvOQ#f zRB1(*dLfNDq)EAYDM(fb;=r1kwql6-Y0TW+2@_ z+F>>QKpJ8-9YI=x^aN=N(iNmFNMDe~Ae}*4gY*Vz4$>W@JxG6$23bvqkQO05LYjnh z32773C!|qGr;t`5y+WFWbPH*h)$|K#nALO)X_?jZ3~3tDHKc7w-;l;3okLoO^bTnr z(mkYoR?|PEfmYK&q=i<~L!^mF7m+q1eMB0GbP{PL(o3Y7NH>voBK<@fYBe22T52^t zMVe|gT}9f8^c86=(pjXnNNJ z%NeDEoH2H4!Pm(kNNDF01QwVW3$G>#M4m*fzz*g1-7)A^&FI>e|gYshj+%>-8g}!Y%#q{irnD-O!CM zYE0wtxN2(uYnpb}mAsph*4kd3e6CDbF{V=KNK%C#s!i2P+t!F~zgRQr%#7+dJdwvA zoAr|oYdYUl>!&N9q^s{#_kS3Y0o9?Wt+nx3$=|rO+u0zn_9P!Gw{|xhRQB{nL5n2a z69!QUYq}p`Srl2->y|~V6aN9y?nXxd diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Buenos_Aires b/telegramer/include/pytz/zoneinfo/America/Argentina/Buenos_Aires deleted file mode 100644 index 260f86a9180677d86fc3280b06f01d6a6cd91c94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IKWLLd9Eb5YHqjUg1{4$#sn}8y5sox5;-B(LhN`qn4Je%yL?{S~gB3)hi<1zL zAP6mu?cyMK4%O0Ddm3wOhZ0Cd1c!j)rl`aatM>doH#vz;=H=ecL6#T3Pj+DNRKok? zuUr0IEoZ|Od5zu3s|OeR{fC?9_2kyPKe=>PO>MknrY6Sqbo0JHbMuvY^!lB7d?BJ| zr#Ja#Ag`Z{jQCIYmQ?9-#XQ^6&~wLw{@hAjl@Fv%xpq&zxLWWR`flrmkJrp%pH++c zgQ><+dMR_#)c1!~eY9b|hEA(SBI-BZkLu?7jA@pu>RWxtU#{-b-%Ba;W9EqZdAHwR z8BQsCJ?XC*Kdi&;XNubk6S_TGD0Za3=#Ee4i=Ernsm{u=V$A!jVtP+8UaP7dw~n@@ z8_)Ib*|_c*TC02B+Q6!ozkEW$-<}qC4_P~(^gL@z6)$LQ*?3`V zUseLG*1oL;qTIdK-oC7tk+V}J<#fqOc-engMn2M>8@|(vEQG9tEQPG)l*N$MoU$CU z9*xjnBZm(HoF&6n-e#F&|G+}AU=UaCj0-rC0(V`g@G zi?#!K^JHX1&mAbK(v^yRx~*>JPlomUYL_Y>PT6v8Ts^;5&?GDu`z8M<*NF+GNiv%_nPmer2R2-O#QsquUChY z%H8PEYsZe5X#3gX&ccLgZ!Hu%QlCx7#|y>I9UD|<L>5I>MV3X@MHWU@_R7-8+Fn^4Sshv4E9)Z#c%=fQ1f&L}2(MIul!4UY zl|qn8yiy8M3sMYH4N?wL5Bh?5k%}bvfRd1!kfM;Pkg~i|7gCs4Dnm;1N^MASNOeeg dNPS3wUa1f%(JM7#PJZqG6d5}xZT5D@egU`L>mmRE diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/ComodRivadavia b/telegramer/include/pytz/zoneinfo/America/Argentina/ComodRivadavia deleted file mode 100644 index 0ae222a2f8bb2fb1b7abe17d08e076674c51541d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IJ!n%=9ES1RG|?Cf78Dc_sn}9d6%J`*#E)_$LqW_^14<_c5rl%`U*xjnBZm(HoF&6n-e#F&|G+}AU=UaCj0-rC0(V`g@G zi?#!K^JHX1&mAbK(v^yRx~*>JPlomUYL_Y>PT6v8Ts^;5&?GDu`z8M<*NF+GNiv%_nPmer2R2-O#QsquUChY z%H8PEYsZe5X#3gX&ccLgZ!Hu%QlCx7#|y>I9UD|<L>5I>MV3X@MHWU@_R7-8+Fn^4Sshv4E9)Z#c%=fQ1f&L}2(MIul!4UY zl|qn8yiy8M3sMYH4N?wL5Bh?5k%}bvfRd1!kfM;Pkg~i|7gCs4Dnm;1N^MASNOeeg dNPS3wUa1f%(JM7#PJZqG6d5}xZT5D@egU`L>mmRE diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Cordoba b/telegramer/include/pytz/zoneinfo/America/Argentina/Cordoba deleted file mode 100644 index da4c23a545b3603bcdd4555ba8feba117802e7b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IKWLLd9Eb5YZK5$03@9if60xNuA{=RB#6RVg4plKr4Je%yL?{S~gB3)hi<1zL zAP6mu?W%YV)zU_L8f&ye38W%|LqKs;L}G|ld;Xqt7oE(@y`O_DFMOZu(D13G_s3tK z{JTod`YZC9xRF;6E)Mz+H@(!;Tk`(&(pfdL;gXq|n$)wc`+n)>YxU^Od-M20M9|wz;Y2j|ctvm4vG7Pn$~po_cY$;4fru>xEC(%woo>Mg7s# z;winlZoJu zSD(CHFK6>*`MGl=rygDy(2usgFvT4?U0gYht9#9lQqumMJ*Ix$9nfoI zN#(Bh>J5`eOr+yXVP}5Ibj0$7&eRvv`FW(!6(6!)@7|bQtJie5*O3>B&#TFxHTE2l?JA{hLKJ>(PJxv_giWEW%`WFKTBuk3_u<(0jV&5+%Y?U4PD z4Urv@Es;HuO_5!ZZN0KDvawfoMz%)w_R8kS?q1m**&k^D>A)*3AUz;Wc%=)Z4X^Zp zG=g-3w1V`4G=shy9;6+~JfI(>A*3UuC8Q^>G=+5Km9~(+kj9YCkk*jikmiu?ywV^;E&fq z`MN^Rx-0Uyb0e!BTnKr>EU7hWN%RwFVEYjn`&m}cv#OYcc{{#lr2^6spnU7dbaPjnf-Xp&h*ULjm z<*s+@m7|AEwB<~GYp!5g;<a%J6bUxp{WsPcoS;%i|YtxBfNhQpld`G3Mw%)?PakcFoTxELSWSTS1xllvnU*3`M@4La%7{Q2h)9D~^t~eiroh#WO z>fGXdsL8pvl~A1bpy^0oI_QzJQ+3PflG7Qa|6v~aOLuO_>_BEhrb6aICiBW{$aG$r z519~|5t$O16PXm56`2;97nvBD8JXHEb0d>`Wp-qGWPYzCfMnp66p$Q{B#1K1f37GvY;3lIjC;LXtwVLefI=@=9VzW?o4R$qh-)E7>9G hA^9N*A{inndL>6BNv~vy8TrNklxA$7M0%hr_6wX??F|3` diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Mendoza b/telegramer/include/pytz/zoneinfo/America/Argentina/Mendoza deleted file mode 100644 index f9e677f171b3900dfd978eb8ea2aa226557d2c5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)I&r8#B9LMpmy44a71A~4LA*7g#;KSTnP{~I;7||{^3Osd)A_$5O8T4ay=@blN z1cBnpYtW~_61DcJWh)^W%!r`FqUcsctYKDudOtsZK-8&y`Rwr>8{-SFXJ+{L(RlF3 zSD$>lO3vC#@^l`ksiYk2mbM@Cnt3M|I=lxLIl+uuJ8t`d%N=%hes`M=5E4&K^*|ZV%~| z(WG)$6MFT;J`?FYS=f@FG@a3Wp)2*(bbUEf=-!af-7iaK>+&(%WA>U@?Va9s<4{Mc z@yzTj#*8-YrvG4{9UNI>2H$2ovd+1t=9a&_!XYmI<50LI+$^8M&K1uGfpeuw5OS_; zgNSplDot(9y{|Pzxewa*r!&ETob4(hr&mrq$o#`H@{sQI=so#==U50?30VqR%PWf^ zt9fNPWIbd-WJP32WKCpIWL0EYWL;!oWM!``jjZjJ#gWyK<-M{#Qh-+~KuSPrK#K56 z6-XIK9bPE}sl+R#AhjUHAk`q{AoZXxh#RR$f)6MOsR=0xsR}8}D|I1-d8IO>G^949 hIHWqHJfuFPK%_#XM6c9{S$VYoQ)KIIX|u1l^*1v}?Na~% diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Rio_Gallegos b/telegramer/include/pytz/zoneinfo/America/Argentina/Rio_Gallegos deleted file mode 100644 index c36587e1c292673fa537cecf729f4c873d375c9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IPe{{Y9LMqBbgN}TgMuO?j1-v>JWSW3Kk|eJA@X8_!czwcf}rS-LDcHfDHy~E z0>zcrpr^nRXZh5!l?NG2iJ-%v=vHK`vHtn=eLts8QKx=Cp1poQcKN~QogO}UA`$%Y z>XU!h$k}vRer{jSss|T_^uw($&Gfdco?bemW;S26GgFhM(72~(Z@f~EUca-C&$pPl z!WM0Zv*yY8xPH2?sEU`$_E}rq%pVWy`PFV!I+(Jh>Rt8XN=`2f-ZBdxuiC{yrxwiz zTZt#lQvZmp9f+#hMBRRgoKp2fOxNE}n8wC_+bC7k*V?H5R@rU77nAnK>=E_z&X8Um zODcD*SFfEsWTG9X^E+}=rX!Ziccwm@&QIs^UE4RPuJW;bJXle2vp3&et*D(hkG7}k z&&{5>Zlen;N-wXs1EcHAz?)2a#yJ;iYW~YR68`OOjx@g7iNuBOmF`j@=(Y7D84+mO|F@%3{cBURe%V z4_Odd5m^#h6Im2l6ahR- diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Salta b/telegramer/include/pytz/zoneinfo/America/Argentina/Salta deleted file mode 100644 index 0e797f2215ab398bc59b8e4d60e5260fcb365c3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1048 zcmc)IKWLLd9Eb5YZK5$03@9ifQn95ZA{=RBM5~-+D2Q2VKdkRuHW&PC`I} zAha~vwdgriOIz*PSfd?EAQ2H93W}Q|5<~pg^Y@&)=;Zry@8^YgdExuyhfbYL1%JHy z5`q9nlQ7i`+Dx?EA{C0JNx)T%*@Yj z)^@01o{Wv@g?(jJzFf6Ww>Hh<@vvT8O{&VljIFHPQ_rs!^-}J(S^9X*F6W$DHXm#) zkv8@03ES8oRgLkc{SrB?nyI*Mz8^QO4O!c&)YR9;u>MxtZN8V&_Q%{2^>cDiua2gb zyWXeQP8>4Pt}~?_#VOMjFP6G9pH26t^QE5c>s3$nSSb-WoiKY#$(5SgdFyCrruoe5 znNOO5;dN%<%}D2nb1u}@{+D+o{QGXhb~zE}7AAwhxpFlKJ6Ewm)VY_{P=|AGS3+^# zgO0gduH=_?(hMLCmEFgq}QzAP9;FD~Lufo`iq| zL1;;|SHbBaTGD7n(;6*U0;!1LA)t6uRAPv)j`NLo(UZH({ucJK@c+`|Cr`w@KVApz z*H!kczic13uV=-*3uE&Bre|t)OIFSyPt>!v2vV7yEc<}12et15tDsvm9 z9?z;rQ&aNs-m0ixs_Xg9ZMASLAQx7mqBfGywdP&%^h!=Hrf#Xl4_Eb4%7`WPUN;8g zYB_maxAuobE8o^%f~Q0~7Lo0DdDU5$)SX&Gd~Hq0Z;f5*do`|ql#hs?cgEz(WL%hQ z!*ccXAr;#|7c%BW z-Pdi*o2D=eWH4A~R&(*H1z{Y!Ipa;A*TgiM9Zg-qtOvmw(t?R?0D z$c)I8$ehTe$gIe;$h^qJ$jr#pPCGX;xzo;$Opna(v4-AEW0h9Oad;kCd diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/San_Luis b/telegramer/include/pytz/zoneinfo/America/Argentina/San_Luis deleted file mode 100644 index fe50f6211cff908f21257cb42259fe2692abdc4e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1102 zcmc)I&r8#B9LMqR+`ed|!=Rv_bV;#<;KST9=+{SfSV_B>hw#)Pf*>e7WDsRtIt7Co zos_MlTi_?kl4<#|Wh*H$W<)ok=vGkJuu?z0pPxS<>eRk`_V}_N@P*g2r}xy!Wbnt= zLHV{(&ic#pcKdo>J-pDZA8meV#l-f+>DM~BV$(mg$K^PPJ9{-b?zK5iz* zH)`9PH&a7H`sv<^s$810&$iah^zoRUUTss=&JJ5$7*Q{-6!c8)mYMl-)z0Renl+zo zZD-odXHM9~{Y`4IShwH9)2g0K==!IkSz4E|OVyhCvDmMF)^?j;m9$-+II33ecI(x_ zv~t%{x^cM6G&P?oZ7+K9GApIg;xMGIDmQl$>@s$)M*SMv`ZBeS;$pkg<@#kkOFg zyfPj#pjSpjhD63h21Q0ihDF9j21Z6khDOFl2KUP7$nahn9|-`7z$+mjF?b~iBnl)9 zBo40xf<%IZ;+0sCV7w9y5)Kj%5)cv*5)%5DxRIcw{eY;Du#mWrz>vti5*iYlSAs*L nL&8JiLjpu1L_+jRj9v*6i4qADi4zCp5&vtT@qLo&f%f=s5oP)$ diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Tucuman b/telegramer/include/pytz/zoneinfo/America/Argentina/Tucuman deleted file mode 100644 index c954000ba9b28204cc3223628d13e9dcfa4b6eb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1104 zcmc)IO=uHA9ER~tnrI9K0}6_WRBS1U2tz-N_{B&LRWX+uPPzx7axJIspYPQV);%}l8*+Ml{0e=b7P2jS7e~y#$)hbmdm@`6yCT~n`yv}7J0n{odpl)wWOt`*kL-^$;FJ!K7M#)p(ge~4 z(uPy|KpH_haY`#lFHUI&=>};B=?7^D=?Gm*JV;NHzDH9?S4dk(Ur1w4=?rPjDZL@h qIi)+KJ)}RRL8L>ZMW^(LH0hKskv5S&F)N??zZ#A1lv;OpMt=cv@bzl| diff --git a/telegramer/include/pytz/zoneinfo/America/Argentina/Ushuaia b/telegramer/include/pytz/zoneinfo/America/Argentina/Ushuaia deleted file mode 100644 index 3643628a24723239a13d61b2215c907cdba03985..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IJ!n%=9ES0mHqjUh1{4$#sn}8)5e{i%#E)_$LqUu~4Je%)L=XhU!3v_)MI3~J z1VLzNw5#ChP_1n>M`Mk4D1o$y;80N96qOj_SC9YaOirSc_j2;Pmn;|FC)a=Ccry6o z)g%9|khAWR{M^1ir0$*X)Au*NFq4~y^yI>6HMRbNothXk(~WUGbK|9Y`0A~FbS`F! z(;KwyA2N?eM)Z^2B~`juu}?SE&Fs;zo?T9;^1ie!FWptoFX#1K_LiCZaK+ANotiiA zZM8FH7Ba_dZEsZ7M(g%V?Gl$gAJAHb2 zIHlaRZoP7BzlpY;Ds0V9n6`Mn(4PKm+CQExbZl9pIx0sBoxvy7X?7J7OI5Y)=Hb?K z{h8TWOc?#(pwf%0ZSTM;)BAd`bnc?xP$U?|U$Wq8!URexT%`3|x z>mdswDmmyyD|=;WWNoi3j;xL>@0InD0=!ZIQUX!~QiNBkK*~Vs z@Jb;_C0;27sRbzpsRk(rsRw;QJV-?nd_YM^O-NBlRY+N0sS7F0E0rOod8Ia_IHWqH eJfuFPK(ADYl<1WjaZo<({}dV9BW?C{#eM;(o9jXV diff --git a/telegramer/include/pytz/zoneinfo/America/Aruba b/telegramer/include/pytz/zoneinfo/America/Aruba deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-d_QKn3%bH!#A35{g%JO`U5= z;k^9hXl}Jx@m7}Q55)(6saypcR-0}nzcrnVY*{hKx6kwY2ekE5>zsW*kF#@rI@{;@ zDzB>9GBfaxufy)w5$*{#yW{he-&-D?al?#!IG~B$4R-YDYnpWNFZ=lJAsN$}ZN^rG zHQ2D;CU2Uj<0?*?@e9A!C-T$Ggp7wWF|F99jLOlJ!Df?sXQQOu?68x*{Y55!nQy0j zGFj8l)tad%%4AyeIs0V%us*e+*GykNQlFl`$~+T|$+Lwq^W20w%^1;XG9J7y&-WiU zFKCfwc7I`KTp1-<7cQ9Wc#?+RzGZXD`c>-2nQ(5k%v>3=k+GLFnisUuKf5J&;(eRj zcUSWsT(|jM-|4I$FWVQ}+BMeMYi74!mO1q;w&27WnOpU-G28b@VST@ODe{rdTiRph zkAFuOgfE!|zr}UYr0?yb-ep=esP^TKs4l+oo_(b&CdD6kwxlgxN{{y0vSv?~#D~n% z(pp)z{dTi=TDB&PHd}vglIp&DrlxbXZ0qVa zuYb5pYuhfGH;z?m-Jw>qy*j4xZSA(cC|!3fe%J2I@U$Uwr+qVdiZmux*v5e&*>xw+ z?(VrQP1n-w9=$4iJ95pwmh-Z|HPsx5ACZGQ2F*dw^M)lnk}&+gd=K}$Zw3MZ|8$?f zvVq4u?}wg166Y{?@=b9`d7#ie*%Eb6BvAe@Zo2!EUd_?u7Uageiyz?DBR3zp{YU{w z1xN`<4M-746-XIK9Y`TaB}geqEl4rGt{S8qq#mRoq#~pwq$Z>&q$;E=q%Nc|q%x#5 zq&B2Dq&i<$9#S7tAW|VxB2pt#BvK_(CQ>I-C{ig>DpISjD;BBN*OiOZixiAhjFgPj zj1-Mjjg*bljTDYlj+Bnnjuekn@9WA(>i2aEfUE$r1jrg7i-4>GvJA*NAPa%41hN#! zS|E#otOl|i$a?s?1wmE>SrTMTkVQdO1z8qkU66%ARt8xbWNnbeK~@J@9%Oxd-2x#i zov+(~|L5W*&UV{Z5OrsyAgd%Sp>RlYB&#G6it-`xZ(B1OTmS$7 diff --git a/telegramer/include/pytz/zoneinfo/America/Atikokan b/telegramer/include/pytz/zoneinfo/America/Atikokan deleted file mode 100644 index 9964b9a33452f4b636f43703b7cdec4891cbda5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182 zcmWHE%1kq2zzdjwvdlot(*Pv8za+k3WcvSqYXJiTkd$Cx`Tu|C1_llv-w+08Aa)H7 ZVF)3?%>O`;*{&u4qKPq^3uu8U7XX0%EG7T| diff --git a/telegramer/include/pytz/zoneinfo/America/Atka b/telegramer/include/pytz/zoneinfo/America/Atka deleted file mode 100644 index 43236498f681cc06f64ca2afa613880331fe6fbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2356 zcmciCZ%ma{0LSsm-|Iy&Dj-Bm!Hl7RhrdBt9tc86oi^IjQM+~Y*&*2zbbYMq#bZoSBp@5Baf)v>D*mc{?KkJo0^@vqt7j*K)_f*Qz7dmyMORerXqQyXsN^AUFrngI#QPeLpD-u*z z;!c^J5v-nYdu2{syvVthEpz9B#FOV@C(cetPtrc&0yEuYLc7kS()1Z~s}9 zUv^19TmOkFSpAJIEa+2(zuu5VDIbfXi{r95{E#Rf8IdJ3&EomNZ}s}`4ye+ulX}Bf zuc)#u1KK%SqRQ9o)*CyLRYhEt_Es)b-nm>|nRQcDUagdymWGQ>XLID{J2yo2N3rt7 z>2a}T|D1ejY(&)5Ps^=C?}*yc+q&-HNwqEIvfkb}pz6cNbVJc@)i85hHzro8#tZv& zlRH;64cF`DYm3#ZM|?e%fCW* zj|Cb zzK@T~_1P7d%kOV+T)}>Sdu_lx`(0pviLm!bzOER*zqd7DiM=mcU+Q&js4#Dpc^$7S z-`w*Hyso@;=CaOQ%n9Jb`Rn5S?~#R>Kk#ynn3sFJ-<-8){usyZgVi<2=#b%A&G?W3 zq8%X@hR88v1O|zW5*a2kPGq3SNRgph%~+AaTFq#Y;UeQj28@gt88R|vWYEZ{kzpg_ zMh1?I92q(?c4Y9#=#k-D&G@Y*07wLo5Fjx?f`CK;2?G)bBoIg>kWe78K!Slp!)n5T z#KUR=fb2@Mh(BsfTPknkY!v6=uO5kf+Q#0Uuz5+x)| zNSu&BA(28tg~SR877{J12^SJCs|gqqF{=p~5;G)dNYs$9A#pIBoav^lt?U*U?R~(!imJwY66Nx)M`SC#MEkn zibNF&D-u^Eut;Q)&?2!#f{R2K2`>^~s|hd?VXFx-5@V|gG7@DZ%t)M(KqHYxLXCH0 z9UK@EdauXrnRg$bziVB+?f+@^KheH>3o}7a6Q=0Nr5UN|sUo>FEiE-IRfPQs4Q6_I diff --git a/telegramer/include/pytz/zoneinfo/America/Bahia b/telegramer/include/pytz/zoneinfo/America/Bahia deleted file mode 100644 index 15808d30fb1bb250cce195a00a2d7dca321df7d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 zcmbu-&r4KM7>DsYQ%OvvZCV7WuZf5?*4x}kr?R0{B%~1x^kdN~By&-V7PSa#(Ml+a zNU+UWMYM>s3C18Wu(XvCMPNEkO{;2=Q8tXl={)BTD8hR=d=__eIp3#na&RCM{q@QF zudDo7f9Ju_mSRJ1x%m5l8DGrm zKI-g`HK$~0;j7d*C($0ZN*ht~@BeE0#}3&ey|-l8IhRbV;7vOB?qd`=S1Bh}JNM>gwDxdcA?nnOKXFv}kNfw( z;@AM$A?UY2_CPj4b_x1zkbQ!FBV;FJD`YQZGh{bpJ7hm(LuALG-xAp~=r=`nMYg3b z_C+>Ec1E^F_C_{Gb`SdPk^O_d0i=VVZvp8c=$k;gK-xh1KpH_hL0Un2L7G9jLE1t3 x3HpYRj)J}=q$i{)q${K?q%Wi~q%)*7q&K8F9QJp&g6a~vK! zjkuTzxg}tO5x?q(i2oR5Z2dJZw#_WDw)ejgJNg2xokQIsq2sHytE)vMR=={6TD#=# ztUfEbv`MB!Ua|J16wB1$dMj;Rl1!WJG}8Un%Ji{1W8aj&%oxZsGDnsw*R?RQ|G|vP zYFsR`FTGa>O4iGq%0ZQzv`D%$yHsB27nvX4qz;M^S+M-NSuoiv56xUK3!it&!|#gB zBlmj5(Z`8qQM)CIudXpY<+Y-uIYAvucZ=gip{g`2PMk>aRo*2zvTXH~Dw~d#C+CJ$ z`Dlnd_2I6ncsMI7hhLbdJ11n-?K@_5%`<7XwVO4C^q8&s%h<*?S9cm3BDl&A0D9O+gq9#L6 zh@uQlA*w=jg(wTr7NRahUx>mEwZ;&Y9crB+N<*}Us14DZp*TcyhUyUAA<9Fvho}$H zACdqh14s%EbquGom^h^TN^bX3IVh^WBdO?$QU diff --git a/telegramer/include/pytz/zoneinfo/America/Barbados b/telegramer/include/pytz/zoneinfo/America/Barbados deleted file mode 100644 index 00cd045ac86d6060e9e8b8dc0460caa49d2479b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 436 zcmcJLJr4mv5Qb;(j)=(N=yZC?#i@lv=@N;pG&Cd>Dp9G_D!D|Vm-rVtlJl_*^@e}o z53GHCR4Rqpyz^|PnrC>c-z(BrMfC(hC3Wz3=9P6fyoF*POy%K{7srV&Ps^1M-9g8u zyR?IuNG?dGMnjPvU!}tvBGXo#tXpoF%wUFPM9kMyYL_EY>MWS0)OJI$Pe1Nh;x(`P zoT@dQCZ~eyc`gxS#NM6%u(;BNW9#H1 z>ZBAzC~b^5>LU0NQsulSe?Y{9<2Qs5-Y3CYWwR^_uabS*WzWFDX&Baq7L(e~g08+(QHiRoZ&cV|pf%{;eM0si=wmYh5+QxK6?C8q?Sl!kFj8={Dy6 zPV_A;2VzQ43CAdLT`ZE=U`s57G$f slxtfdy>e|cq#M$Xzehi$A<_|PiS$I8V%5G|P}On&=<3bcy7RvG3)4mZiU0rr diff --git a/telegramer/include/pytz/zoneinfo/America/Belize b/telegramer/include/pytz/zoneinfo/America/Belize deleted file mode 100644 index e6f5dfa6a8d82523e8cb12f17cf73560bc09a383..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1614 zcmdVaZD`F=0LSrr_h2?!T3U;wk(kh$3@M{aNz}!SLz!brc)>;PPakALTT^3JQh^uL|+x!wA|`+cu(&E}0+u792( z<`)i6o;iFUbt1R5vbcDTKEH68Dk&VJOQ&rW7jhk4mh2T5=SK9!o-E;?_*?p)j1-p> zw7gv2FRn!I$-t=;bv0Bb%XjsuYyJbWVr7e}Jh(|-&#qQg`9=Ch>RENuyHVfl-=}V6 zF4DI;&xq;~lXZ3dUJ>jau5TBw6*a9HvbHcs+zAeob?eea{ppYLZceOd*z;80n>1G4 z7uV&3p>EYU>x68KcBxQ$g${)pRg?ReZu0xpqsR{Z_~--CJa3#1kMGM5KP>RJWPFz` zt&5dP?2sxrMYVR<$+k1esy*DMJNERd7ge?Tr3kCeLdHS{c zhIo^;K);Ec6w%S^Wwc?pc>BX6yGvG!ckL78`$Id`hnfWWamy;zbGBQ4T0C3z7RBq& zlSivB8>0Ga;xEEHN7CsSWP!bJ66*V z(h$-S(h|}W(iGAa(iYMe(iqYi(i+km(wxvpkBD8HaeIt!q|Fh1M%%4DZ(jQVtZcLk+NKml(84lWO}P<^ z>4Kf$r3kq-7D7M+8bhLtbM^rY?l7E%fnEM@ysh2sS?A9uZclsc9z4Cy)cldncmHQy zXd&X($$7czeZKY z7!z{4tdKE&;TT6M1&S-F&77D?~8S)|Ns9#BQp~-^Z)<5CNMC7NtXZr zj~`&<`2T!iQ4YCwyHv`CG5Dl^%MArl5 ONOAy|4bT~Ord$Be(?9Y6 diff --git a/telegramer/include/pytz/zoneinfo/America/Boise b/telegramer/include/pytz/zoneinfo/America/Boise deleted file mode 100644 index f8d54e27479149d35f6ddff12f709096f08bfac3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2394 zcmd_qeN5F=9LMnkf>&-loJtGg>|+#tLH8etT8ceMf^x@jSvf@1u| zSvs;zy7Z*2nToF2*bvunZDn(~HLZm`*DM=7Y_fRxb>8RS{_4NBzIVUh>$m&==k41N z*pwswaW3-@5BHLJ_)b>l)%*79&}#$nI`l$%sPC0)`tYjql#jcj^~j<>TSgiZ#9!l2 z%88$9#pJ~rIrUM2m_C&+ox{mO>`#(mozo)xsVO;QYody1n5!czC)Lct3GK=nRj&R8 zI!X?ys3XUvd+c>}-B*1&`qF6;Gt{nQKj;&2?}Vg$xm{e}^_;w6SChCg&?slGFA?#j ztK^*gG;vefZ8BkDv6>s@mWko1D)INlI_aAzl{_*_&pY$8nt%L?UeI?=r3B~6o1Z=8h5LrB`@mYqK&Hfk6~HjX%!_G z4$0D(dQp0!ORl+ED9T=aOP7CEr@XD5`u_J%LRI z$v9n|dsbC{pCN1Ke=BN-W99nkVX@)OG5O%=AyL;cA|D#;67`KI<;LDE;$iQ3-O$;o z9`W?+NBsfS7_(0|m6WNbt3mBg^(g6dmhKU;HFrvGYd$Hq$6uB^Dtg3@=?NXo>sP_iFZIrZeQM|6X}xPasGjca)6ZP2 zRjqC9diSXU)mGo6_v}wr;bG<%IU^!+=6~wvID6xSaGZlWEW&ZRm6+u??}oyn?OXD{ zm~Fok%Dp~OS!ABIKH;q~Po;VIHve&9_6@#&F*(OveMI6AGCgE|$OMrYB2z@>h)mLI zW{FG_nI|$)WTt4R$_I1h%w&Pta!ePQFEU|d#>kYBIa|%7ky%^Kw2^ru6GvvwF?D3_ z9Fs?8&oO;u{u~KFGQg1nBnKQxK(c_O0m*~aBm&67P9+EyJe@Ft6 z3?eB+a)=}m$)eSy5y_*~BofJ_)ua;1B}X!mY;vR%$tOobk&JSr6v-))R3xiNT9Le3 zO=6MET1{$^+*(a?k?bPrMe@s$U?jsFDMoV4kz^#x9BD@KY&D5SGHo@fMsjU6$wsn` oq#MaMl5iyBNXqelmUFkM{Bl$I4DZs+oXo5YZ+3QOc4n6QZ`ww0CjbBd diff --git a/telegramer/include/pytz/zoneinfo/America/Buenos_Aires b/telegramer/include/pytz/zoneinfo/America/Buenos_Aires deleted file mode 100644 index 260f86a9180677d86fc3280b06f01d6a6cd91c94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IKWLLd9Eb5YHqjUg1{4$#sn}8y5sox5;-B(LhN`qn4Je%yL?{S~gB3)hi<1zL zAP6mu?cyMK4%O0Ddm3wOhZ0Cd1c!j)rl`aatM>doH#vz;=H=ecL6#T3Pj+DNRKok? zuUr0IEoZ|Od5zu3s|OeR{fC?9_2kyPKe=>PO>MknrY6Sqbo0JHbMuvY^!lB7d?BJ| zr#Ja#Ag`Z{jQCIYmQ?9-#XQ^6&~wLw{@hAjl@Fv%xpq&zxLWWR`flrmkJrp%pH++c zgQ><+dMR_#)c1!~eY9b|hEA(SBI-BZkLu?7jA@pu>RWxtU#{-b-%Ba;W9EqZdAHwR z8BQsCJ?XC*Kdi&;XNubk6S_TGD0Za3=#Ee4i=Ernsm{u=V$A!jVtP+8UaP7dw~n@@ z8_)Ib*|_c*TC02B+Q6!ozkEW$-<}qC4_P~(^gL@z6)$LQ*?3`V zUseLG*1oL;qTIdK-oC7tk+V}J<#fqOc-engMn2M>8@|(vEQG9tEQPG)l*N$MoU$CU z9s#^KxUfIg>*r9A|hfIcss2ibvH}IZwtqG z2K_Js8s8bWb$#dB3;CH)qPjf`+!yh5JUPMw``>>w}U} z`J$Runk&Eat|{_4+VLB<8;%`@U1yKZtl+a-7Syve&4mPG@fnEb14+Sj|!_)pjB zf{us@99W_YBb};f$27e-xJ50g9o5C&m((*w-|3}kd8#Dqp_JSmlhQ|DkCdhUSW|ZK z7xnD-XJuKx$2@oTh%Dd#vw8mTURlw4*{tk5tY4_^H>;ZW=<{#cb~C z(y#kYnKvR)-8|`_X$e;8misXi@%nURsNO`=rs(MLGPCvWuXu`S*AOhTFl-<**YaDIVCmavGM(-J)WL6&X11c3;CZDNhFSaoRsRG>50S< zBNG#eulkdedz0brGq_6*cbW6|dp_w41SGUBETI~E*4R^C&Lb?VIQB(_Jt9f&huq_z zYxqC-kg&rEh!+qy9IYP^Mk!8#L@Ny(iBJA6-ZkceStKF(HTf<7`=fs zhtVBKdm#ORGziimNQ)e8k04EQv|WO<$4$?bF^B~=W|L693Y%7=AOy-9c`-}X6{7_+`ztA7> F`~~dE4qX5M diff --git a/telegramer/include/pytz/zoneinfo/America/Campo_Grande b/telegramer/include/pytz/zoneinfo/America/Campo_Grande deleted file mode 100644 index 81206247d9efa7d3dd10dd9e88c669718d86cc50..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1444 zcmcK3%}bO)0LSsitwc60f*_oJ@W*tx_CUrgYpBo4&5qn@>rVs^7}Ah7TgW z^QqiY{8EVKR=G8&(bT;m&GebJ3>z3FyJ}q-^=Bn*| zF*@(;I<@27Bbi_ETkUL*mNI=p6|_dm!ZiybP&p_Av*V(u{D>}^2&C!vb#qN=Iy{GxHD(ktR%MZ7xiu+Z1Z)vC4*K%G~CX}fC#}3K^PKpW!vSsi~v8eLJ z%BsGNqQ(=JHP5}`&}2x~-k1_~V`p^ziT9$(bzM)yqJ19M?eBLS*Bu^=Sn9gH+KDPF z4?1Zg(_Ft3{EsTkLtV;1>adzZkV=qJkXo#!7^E7jDF>+sDF~?uDG8|wDGI3yDGRB~ zY6?RtvzpS7+K}RCp6ZbDkou4UkqVI#ks7V0NTf=uDHEyFY6?XvwVG0qT9IOrYLRl0 zdXa*Wijk6$nvtTBs;#DMq;9Jz9H|^B9jP5D9;qHFAE_T%0I~vP3CJ3dMIfuNnq?sC zu$qM+E3ulTAZtMugRBNw4zeC(LCA`bB_V4<7KN+|Sr)P`t63PbGOJk{vNo$(9I`rO bdC2;Z1!9wFWzjYm;rYi3y?JJf`F`&oyD`xr diff --git a/telegramer/include/pytz/zoneinfo/America/Cancun b/telegramer/include/pytz/zoneinfo/America/Cancun deleted file mode 100644 index f907f0a5ba77b9ec845c450cd535ee589b46c5ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 782 zcmcJM%PWLY07vhbk!MjJ8=}s}r(2JnZ!3P02`EtrK}$5K&)|6TO)fy^pU>-%&)wE(=uu zT7?+ceNlt+Ibvw(S`Ckv%aPfn8tu%IR%~5am-oWsP2>1Hza6jEd)q>2y|W>FTA!sn znOY|gL@uu46Q%-(q=frF5V#SApRf@As!(vT^gSdrx33Uw=Rue zhGU3lh--*%h;xW{hgy(v8s$(vQ&) U(h<@U(i76u)Bq$V%jwvBoAJiOJ$h0>xa!J+`v3p`GcqwVF*E=Ff6#$} z0Z6(qu>AjjUpu=2qD2vpz;4R+oc>pG{|lc4YD8T b6b6tRKs3l5Ai5qXN2*)6Y=ADZGvNXNDu777 diff --git a/telegramer/include/pytz/zoneinfo/America/Catamarca b/telegramer/include/pytz/zoneinfo/America/Catamarca deleted file mode 100644 index 0ae222a2f8bb2fb1b7abe17d08e076674c51541d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IJ!n%=9ES1RG|?Cf78Dc_sn}9d6%J`*#E)_$LqW_^14<_c5rl%`U*xjnBZm(HoF&6n-e#F&|G+}AU=UaCj0-rC0(V`g@G zi?#!K^JHX1&mAbK(v^yRx~*>JPlomUYL_Y>PT6v8Ts^;5&?GDu`z8M<*NF+GNiv%_nPmer2R2-O#QsquUChY z%H8PEYsZe5X#3gX&ccLgZ!Hu%QlCx7#|y>I9UD|<L>5I>MV3X@MHWU@_R7-8+Fn^4Sshv4E9)Z#c%=fQ1f&L}2(MIul!4UY zl|qn8yiy8M3sMYH4N?wL5Bh?5k%}bvfRd1!kfM;Pkg~i|7gCs4Dnm;1N^MASNOeeg dNPS3wUa1f%(JM7#PJZqG6d5}xZT5D@egU`L>mmRE diff --git a/telegramer/include/pytz/zoneinfo/America/Cayenne b/telegramer/include/pytz/zoneinfo/America/Cayenne deleted file mode 100644 index e5bc06fdbe5a3062f90274a6b5cc5c56e2d74d5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198 zcmWHE%1kq2zzdjxvLMVe@r(9^--k^P)c^ngpOJ~_|NnCz7#RNlKYoCL<^TVy7Z^Bv pd_x#?4NQR8m?4A&gMsG#2N@2cK^B0VQx9Ylw1mqBXqBBY7XW&PH?RNz diff --git a/telegramer/include/pytz/zoneinfo/America/Cayman b/telegramer/include/pytz/zoneinfo/America/Cayman deleted file mode 100644 index 9964b9a33452f4b636f43703b7cdec4891cbda5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182 zcmWHE%1kq2zzdjwvdlot(*Pv8za+k3WcvSqYXJiTkd$Cx`Tu|C1_llv-w+08Aa)H7 ZVF)3?%>O`;*{&u4qKPq^3uu8U7XX0%EG7T| diff --git a/telegramer/include/pytz/zoneinfo/America/Chicago b/telegramer/include/pytz/zoneinfo/America/Chicago deleted file mode 100644 index a5b1617c7f70bfc77b7d504aaa3f23603082c3cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3576 zcmeI!TTs4l=1wj+F14U?9S&?zpP%=nM8G=GAw+LbysUe+MCSs)FY9gtova(V; zpca}P2#i$7LQ*ouYDzJJtQ}CHS>!6rNNMlZ^KY7Irkk2>x@b9@A2N93#ru4&8F@F3 zlD|BE`x8FA@9c-~gSGuqwlPAled8CkZuua+{;31%x%Ud>`FnmgWfH9bPJLEhaz9~S?p`LZ)Gam@O*!&vS(d4+o+wqtmzvGb%+{~vW~%C? zm+RM)$EhtdN9e6#!_>9}KV8$$qiTm9)U};$YP+AWY~Q_8z4=wAyjAHobq$TOV@18G zpV2IDS0$*OB@fE3^b*rB_cnPa`bM)m?E(Gn;44jIr`kSUj-Likex8~z%A4~JuADB<#wn>Xrn%1B-(%SZ@`P8#TAE;kwK69_qpTGEs za@Q5PTgqHfwjOA6D$tKQ7y#y7SBR(b=Wyr}X9e*!Vp4bM$=O zbK$+_m%*v}ctEZ>-jgdQ4yBmhmK6E5G2D1+!o|BO(8%gQ@hLrG`Yb*oeHRQ=zBwmp zzbW6VeiOR1f6Pb9|DiD5f5>a9f5r1Mz&x%_YFnuXwpN+I`bBzB?PF%}i;u~WH3jD6 z`wQfhq6~9tUWS~O6>ox4;^p*9Ld+Q>LnQdzvFgl#UJ2=QrV9BnSPyMKp@!`}uFrb= za}~PzGd+C$4s~|nU^(aR_3GSdKgfui-ZJOKHOcv@Yt02gTO{nFyG@v9uO2yIjv48$ z))yU4GU0Vk=!m8pRAkv=9aTL^MHgr3n3Wf(*xW)HwJ<=9PR^8zuRW~d!p6y%QSYm< z{=+1G=phr|>5)rL>@nkZx5=dkUNH%ky*hFG!{)LTZaw~KWhUg;>&r_XQdguurzg(M zSCgVkbkd}2R8sdgNsheLBsZ;*l)!Y8QoTe{yJF2%&#cl{H&0e+ON;d6tuZQnX11R4 zEZ1{#v(?O6lRl~)m= zZ|eL~-TY*V-14E<+*%kew^g>A{ER?RD|VR$aYy9#{0(Md&|WD>FEs_8E?pR3t_s~B z>N|p$t2^p8>!P0d>dvy2dPz&FT3WnF->#if2vN%T^CkeSH4LpT2+k9bdmc{pIic zuJCL{OUB9Oq^stQ(cl|KNF|h&lH#4 zHv4@3!1WJy(QDtVzMgf+J|Y{5>?E?4$X+6wiR>n_oydM78;b0xquo+uPaW;1BD;!g zE3&W1#v(h5Y%Q|4$mSxui)=5lzsLq7JB(~Gvd4~glaXC^wA+mAGqTahP9s~5>@~94 z$ZjLsjqEqF;mD37TaN6xquq35*B$M)Bm0hQyrbQDWb2W=M>ZeXePsKQ{YM($Xgh$k z0OW@JxG6$1|c0nT7>k-(KZR`64EB5Pv|s?Z|D@ywu(oukY@4d7Sb-HUr57{ zjyc+vAw6@nP2$t zJ|c}oI*GIr=_S%k9^FLR$)lf0LwR%*X(^AMI@+cpU3Ii=Mf!>~7U?X~TBNr~bCK>M z?d8#5q`^EojI@|XkC7(x=(3}2Gmkzajpos5q}52Tk!B;^M%s<^8)-Pwairx)&mC>k zd34>;ww*`c9c|-zbRKCv(tD)&NcWNUBmGBi0OSrpZUN*TaI`l8au+z-+knS?;An3I z9(MwAEAY4%keh+W-GJNEy(SH n+%L!tga6+#|L%?%V9%T}_S}g`8yz(&DkdT=Ha03YDrUfMOLBGg diff --git a/telegramer/include/pytz/zoneinfo/America/Chihuahua b/telegramer/include/pytz/zoneinfo/America/Chihuahua deleted file mode 100644 index 8ed5f93b02001a63ce43dd221ff48cc5faa6752e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1484 zcmd6mduYvJ9EZP$_EU!;az1*wtoj*t|t68r~^U zb1M;+w$v_eQ|ZnE?h16`Am|A#+!vbk0rS)%q+TbN>Yw|FpJyw%95&wCUt+iPRr>s>6@FhC;F&a z>M7RCBI-;=WU9{i)}}I@vvlUuTD9WMD7~^TUu8X*Dc%!_D*K94avCP9RjqGj^~QLU zTYgvaQYRT-)@8|`@!b?ewaOZKVG1W+)P=8ZnYBX)wg2`RQ#4Sn*Il}))^}&?4F^uE z;$v)U`?XRNb;7y>m*3s*i4v`X7f>Lxf)%o>!^H zkIAy@`f9cN(RA5!uEI2RiZt)bF?$cc7RLzANc%V)|Mpnc$yO1|I%ULZSsiC(tYuy6 za)k4CsNdhSygu>f`6Qj|vETOf{e>l#Jxqbv0xO`;*{&u4qKPq^3uu8U7XX0%EG7T| diff --git a/telegramer/include/pytz/zoneinfo/America/Cordoba b/telegramer/include/pytz/zoneinfo/America/Cordoba deleted file mode 100644 index da4c23a545b3603bcdd4555ba8feba117802e7b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)IKWLLd9Eb5YZK5$03@9if60xNuA{=RB#6RVg4plKr4Je%yL?{S~gB3)hi<1zL zAP6mu?W%YV)zU_L8f&ye38W%|LqKs;L}G|ld;Xqt7oE(@y`O_DFMOZu(D13G_s3tK z{JTod`YZC9xRF;6E)Mz+H@(!;Tk`(&(pfdL;gXq|n$)wc`+n)>YxU^Od-M20M9|wz;Y2j|ctvm4vG7Pn$~po_cY$;4fru>xEC(%woo>Mg7s# z;win*Z?FJzYy~kH1kXl+@2R8ET)|xJUt*lefon0jl69aG|LYq zXdb$GfsvUB3jhD#Xw1L>CRzUf-?@R2^Z)E8 y*{&;TUnEwh?1e>Z!>f;O263unD-INixJ;k@{Lne+Wm*Ia0n zlKJ(cRN(iE2nHrbAY=wYMyCJ&r@dof`2TL6| z7(z&J%6}k;W8v8VqCrjq(I97mXpqxDG{|`%8stO}4RR)!209f)gPaSZK~4tIWICG* I=zeo90HHg7VgLXD diff --git a/telegramer/include/pytz/zoneinfo/America/Cuiaba b/telegramer/include/pytz/zoneinfo/America/Cuiaba deleted file mode 100644 index 9bea3d4079f4449d5bed22b81404b5ee2ab6394a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmcK3%}bO)0LSsitwgpgf*_PPd-r^$>X|B*y<=C!NZ^$miNwj#gl~GZ=eP(*eRAwUM2v@@$%#;r z_;EL0|6I~2ruA7lvphr1R^`jtzK>$gJE7;gVpTMRl7SJ^dLIw!VC_yb3!e|A9RCTGgrfeR{cdPoPlZ;7qrQ!@WXzS`Cw zuM5tsRomY_l7*GORZ&~4EM7e?f>nbuI5RFvDvs!qiKr@D*&)kbJ`p?SlrFz>P3#=G zsdu$JR=a!8>x#pzs`7rd-c#Nw_O_mrRmo*)-?0O7zmu*)!8{rIQYxx_39`C>gQ)dH zWo?&N9Gncxy6X{9KXzIlI`LjKyRPerS)kA3y6+kt$90DXV-~w^pLXJQSA?8Qkz=mk z3H?V4=9w-%AWc|J7f2gOA4nrs(+Sdw)%1cigLH$mgY<(mgmi?og!F_oWi?$PZCOoU zNMlH6lum0%Z%A`UcSw6ke@KH?(;?EL)%1uoX*FFUZCXvANTW!nNUKP%NV7<{NV`bC zNW)0SNXu5!Gt#uxbd9u)^o=x*bdI!+^o}%-bdR);^p9-7YIcBZ!D{w^Y{F`GfoucW z2eJ`lC&*Tiy&#)Gc7tpO*$=WIWJk!BtY%NhrmSXH$hNFzU&zLgogrI8_J+--jRjg< QjOQPl^A?!p75cq@02#u{;Q#;t diff --git a/telegramer/include/pytz/zoneinfo/America/Curacao b/telegramer/include/pytz/zoneinfo/America/Curacao deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dw{wYAPo(8`Us^A=q^*3WMtIuIsLqRoy+m zpc8pdC&wpsPkK!E##6d46w|4|grwd_rT?*41}YI5yljx6(+^1>JWFQ#PKGzGWMr)@ z?&6WSPcyFLuk-!-1dMqtDP>IMM)|+b>Vwm045$4Ur9%0Fr!sEN?yQ=!ccmPM51if~ zsu;T{!=esS2&sgWLTc5t#gJ-9Iiwy^5UGfiL~0^Mk*Y{pq%Kkzsf?6HY9qyK+UiJo eq&_kMWD3Y6kZB+jL8gLCrsls)Cuqmz2EPDo&71K6 diff --git a/telegramer/include/pytz/zoneinfo/America/Dawson b/telegramer/include/pytz/zoneinfo/America/Dawson deleted file mode 100644 index 343b63227d2185863cd720bf449de000bbc794d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1614 zcmdthNk~;u0Eh9XIix5YNKNf&$0>($$Q;uqQ$%wrpQffwNTC!-gKbI_B&aA4Nx7)( zC~cyRVn$#I1qI=v(rA$(5rh#*WkFWr?R*znwF+8wALss#cje;$x$2tsr8@q2e9RXP zPlGw!W7^HD_s-bBwUH>TpZX2-^^doYosJtAkNVm&z9&t7OKw$jas9$O<%3$%bXW$| zR*AqOmki3z6ieeXWN>7q3h@b+q3-D_bT&;dd$UwI$Afg({qHJ#a857pn^dxOrHtsf zry{HUWmNM^5uN@~#uVQbD*~U%*o0mYyD+2UecMI+)DxZXey>Dr84wczIr^VVw^D<}h zvB(_=*6T)YiuGMT^oGml#KuFf^rp@mDzAK0Z*DxR@{_eL$UmqGzD&r%#51CBVptaW z9}q=DJ+gSFOl-L_q)T2jtI`t}b=mC_RbGEwS6oU`TXSpmwpO7kojKZ7HLG0nVY)i~ zxvGAS}lMu&j<2hxuaee)d~f)>Y*ez}*pI-@|x2B1%eKj@o>4D$Ut$P6dCP z4;AJ^g|XDg<7U5qUtE8&G|N0EPo2MoEDl*6vOHvc$O4fS+L|RIYeW{wKf)@JWg_cD z7K*GCSt_ztWUrM^=t39a+1rSv<0OTeEy* z{YU{w1xN`<4M-746-XIK9Y`TaB}geqEl4p)HMXW4q#mRoq#~pwq$Z>&q$;E=q%Nc| zq%x#5q&8bq98#UFDG#a7))a_Th?I!bh!lxbiIj=di4=-dij<1fYHNx`sH7Dumjbn)ly@Uf1E)E1<+6Y}RjA#>RkYqK{Wudrez*P!CtxCusH+fnF zZM1`J3L=RJ1SxD2L2a6-oG#OJvti0ovoy2$dY_MWt=#nu=ll+{{XfaWU8xP`pKpc! z!{KYv!+TAyUPGg|{iU&ld(gkur`OGOr#oxp$^IGp)E4B~=EruzJd{l0md)g@%k$zR z|6<(9%j`}6>gqL@y>r38?my>pL&yD_u5S1C}pOU4CBlh?G7AcN4+wjVIF&FD?aJEunCn{`7 zPo0Z**80+5+QoucNnEdG|4PGfpv9Ol9CSfk-@T?RdRIobaGR=Ktx40iL(e`v2lTWh zO{>27g1>ii8LNc)wQoDN1z87K2w4ePDpJ=%7DHA;mP6J<7L3#tktLBekwuYJk!6u} zk%f_!k)@Hfk;ReKk>!!~kpd#M0;GgUtpO{*jWAtB*Xe_ diff --git a/telegramer/include/pytz/zoneinfo/America/Denver b/telegramer/include/pytz/zoneinfo/America/Denver deleted file mode 100644 index 5fbe26b1d93d1acb2561c390c1e097d07f1a262e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2444 zcmdtjeN5F=9LMnkqQH%ZQ;8vb6-0*B?gOursm&?$b8b?d7UesOi|Njd(VTTGm*mXq%nh`_OY8GIv2h@FWtq%9yq zpPH0YHYBL9x|w=v#e|wxIIhF9MpXC}}?Nyq3Ob5vJb$tvNO`8x57 zNR>1kp=X`^LCrpNN#EFeTFvp#k~i%*sOGK=%6aQP6gTI7E^k@(wwNFHo=i^FA~|qD zr#Lo>l#!D<^^!~6I=EM-oo!U<-OuTaBb6$%*{ic&TBNeQtuklR47IRitz1+&rgD?- zlegu3q85jz%DluYBJbNMnLmDB6rB1=-u~%;Skmv%cMR+nOFMqlckbFQ3L8GsceU

gzP=p8ch855>q;fg!QFZ&W@w zvR~A+4$FrI+eK~tQMsmjy?EGpM%T5qsYlWe>qoslRUh4{JtbwzbJ?%G$?3{_+O2)z zvC4O#K(G7eXSKeoT0V9rMm+A%mrooV6%AF1vaw@WY{;FI8yk*_O>r0G=JGDFIWVsM zd54vMqPHC@P|dX-y?tkr3Jv-5Z%WwTuYY~@ z-y00>?i3;ze5)rU%)Dz6Vc(~8rXEg;xDrhw&L~3X?MMSE|QAVVWNFk9*BBexXi4+s5CQ?qMo>o&(q@q?+QlzF< zQ&gm?9A!o7%28OPvK*yFYRgevq`F9Xk@_M9Mk;JIB}Qs&HAP0MY&B&@>WmZ`sWeBa zky>*U8>u!&xsiHv6db9z)s!5mxz!XMsk+sa9jQA~c%<@3>5<=_tm4^cV&BhG ze+*UWKQByI$<q#B=s2d`FJ$YrJ$_lvkjdRn~P3}~ko#z%)CXDK-iK$}hFD^Oln^BQ-=|?&J z%teuV>|=TJ!DHf{qvW&*>Qpo>MckUeyJKn^nR1`_k=dQ10PZ zWZ@5U)U1IWvS=_QihCo{b7HnA>0BsF_hyT-a9Edb`dw7`1N!!*Ukh)+EIqq?K+H)= z*Ok-0RFxw?>$$OaRn_@Rdfr#P%GdvsyyKlG)SY`ik$1hYURAdpm-D-}iM#9f$(p8h zqP8R|>uPI6-B_RY7q3dJLRI!szq>Xi(LFxo~U0PluLS& z#J#=}x%80{u`DN3h8ix2P;5*t_Z|_;zniF6c$Zq;byD9y z(5lun?bmC27b_8bQ?A?5BGwo8$Opnf(UjgUoBbuCd9+c63o=FcBcF^UkBP|ZxpL#k zr=q2&O1ECTsveBy=!g0TRa?WjmU~XBhrLQ~YTK_iXPwns>O0hy@hdV~*(0LEXJmVJ zyJ#OcBs+d<6p!|H%g2U%VryquKK^#D=v)(!+n#qsLgF<^iP!!|KJobR8IBW=AAQM5 zipNjA;Y^6fKRBI`X5S3^PF@rYIW@~dP966?bC;M~8)67f!ryP`UyLSh4#PplgA526 zk<|o&EM#2B!1ybS3>li$j13taGCHdn9x^^;fXE1uAtGZ$28oOk874AL zWT41Mk)a}EMFxwE78$P9j29U&GGb)N$e58qBcn!!jf@)^I5Ki%=*ZZS!CTGfk>Oj- z_>ll05kNwK!~h8b5(Oj-NF0zrAdx^qfy4p{1`-V<999z#Bp^sckdPoTL4txr1qllh z7bGx9WRTDxu|a}kHPJ!BV>R(X0%SE2LPCVZ2niArB_vEpoRB~vkwQX+#0m+P)kF&k zm(|1z37FMH3<(($GbCt8)R3?taYF)!L=FiZ5<4V#Nc52KSxx+q09s81kq}x<43Qur zQAEOs#1RQ35=kVKNGy?HBGE*`X*Kag0%|o8MM7#dF-3xkL=_1u5?3U!NM!MU8(NpC Xu-DYLC|Kbs_mma|%gQ`uo>JFeUM!I( diff --git a/telegramer/include/pytz/zoneinfo/America/Dominica b/telegramer/include/pytz/zoneinfo/America/Dominica deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dgR%o+LOR8etU34pxx5GcCklf+M=| zTN$&N1S(-`g{f;cHqabyJ?PwVxvWIX%4XTWs3lG<2AKs_EIC}Cz_u&C~b1>BO?08vv{9DcLmwuEz?Nim>#!sX#`mzAI<*SSoL|uMSUjWYZX#EL#C8oGgI@&B{Y51ge9Dl zaDBwM)Ude5cAM)j^h(6pVHJ6#O`<;RRnc$vX#IMdx}kH6zHwu{y2)For>!VbF~tw+ z>A6Yj=A=7x?3_X~BiyBD1}B=Czs-}lZ^KQzKTKvF{mIOJ|FYcDd%`66X6ajB7%+1x z0(x%aXX>`>Z}ja;-c$2pj_Jf?QHc}hCCRl`C5;}Hf7(ku*3);V#1s+>y^I(Pi6$~*pxzWb{| zwP?Rf?it*r7H|7m?%lar`1^=1N^Ma^r~7qr zWQ{65*r}IX$yX(B^vTjQ)yCb@ArF7B*et8|N@-Vud1O(El=*^9d3>sPD!wzGYtd4f zecV)jpQ5W~UsP2iQF_J1h+4UKOg}o>ud3Vq`mv!-Ra19Juj*c-9(SLT+KvwMMCt)~ zvcYTWBDYI@QHiO);**BNRMT*xO1z=b#`|ubH2(0bS<|&#KXocfJ?+cY&kT&Irph?o zyv?tk&HhWTZ91gZ#hlmcmvyQ26XW8`IbeLF=VU|dcC%q_n6ba=twOVpRcqEDKo|vM^+2j&^Ct+K|N|t3#HDtPfcrvO-6@L}ZP~ zA|352k!2$5&fT9L&ft3{UUXxED@7+EpLl94rYEE-ug$Fh-ib1WQLImgnG zwR0>USv|6RWc`k|07wOp5+F4|ihxuBDFadmq!36YI7)%kf}Bc$b&&EP^>MTXLMp^jBBVwfMMA2?Q6{8L9ECzE zg_H`Z6;dpuT8_3{NWC0w!H|kM+L9qP<0u+ZHKc4v-H^f|l|xF0)D9^gQawjoKBRt* zwtz?l9c>Ab8gdj7sUk-ikveh|5~(CdDUn(t#YC!!loP3^qb(>>QAb-+q^6Fxs7O_j zvLbcmC@fN0j?yBvwrbMk?%RON`Xm(H0r0GE!!w&Pbt=N+YGl|7WdT ZHsT9y%v0Q1X_;y1DejDnw2ZWL*WZc*ZxH|h diff --git a/telegramer/include/pytz/zoneinfo/America/Eirunepe b/telegramer/include/pytz/zoneinfo/America/Eirunepe deleted file mode 100644 index 39d6daeb9b6dd3b225b0ddacceb6984e8204ac7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 656 zcmb8sy-Ncz7=ZEgY(Y-ZMFbJQI$Q9oAR>~ngJLQ-iGx$YKR^(dtAil8IEmuoY9}XY z9VA=P>0C=&E4Wz%@vF3!nCEaV^g?(7fjjQ^dYe0$InnWn+uNAk{inI3Y<@5!^6f2M zcvtGSdLm1U52{ojR^^d)P%iAsN^4eCFPieHa-(YAy8JvF4ZdRg@>^%s&%lyyq*JO< ze3ni3Eofd1=~g|ievi-f*nLio?|tZr%XBa~|Ei}B4pqGGMNjKTmB`d&YTH*EepLI$ z7$?$k^*D|(cUd8fDe6enn8LN_ zwW0WgwMG;tiWS9+Vn%VJ*irl_h7`xJ){^2G)|ygWDYkZY0$++T#hGGF@urwl+$r`H Yf65G~Z{OW<3K6HPIk?Mq8Y@Zn4_D+KfdBvi diff --git a/telegramer/include/pytz/zoneinfo/America/El_Salvador b/telegramer/include/pytz/zoneinfo/America/El_Salvador deleted file mode 100644 index e2f22304aad56062cfb66d23f3a8c296689286ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmWHE%1kq2zzbM`vdlotv-s*V1%+969w^4l4p6Gv_dwaEHh_r{2><_Ilfc06|NqVn zj4c2EuU^2w;o}>^;Oqj#!66JGBv|nu2td{Vtz!UL1)@RLfoPDGAeu;Pxqx9Fak&n6DH46gd6Zt(c~ zb>BpnIz#PmPhD)@EZ^sCl}lyGaycPGM!orJZ1EN~9-pMfr_}UT z)~!{`6S8#V%3`^GUZjo+&XlO>3=@5Exx@@EGqE54E-QLw%nh$z5Z$m^-+1sNSy>XU zSJiy0;xd2IH|2k*ZjSg;$0v5G_}NL55Z0m+hCern6T8*wz8;fwu33^hj~dUZU9zV6 zag%a%qoh_H(P{N@lJ4E7Gm7U*W_*dxN*kB8q1ie+W{%1pi_+`<98>GhUe!4lK2;mu ziZr);@VdIS?GJO?i-*8W6WK-d*tp#hm1F_P`op*=)90r z$s0PT^Dixt%`crY1z*>Quc^b_(_0{gJNKKSV;Nhr-n$dtfe5^u0@fPo>BD?lX_p_NwqI9&opHBOT+LLb0G4B9OxS`jWezCL}#~oa;Q?8n%m7& zr#DG+S-pAsg+vJo4hp^|IAo5!{yV=w;7Ebv1OhLM6A}otwK&)E9<;!{m3uEO@cA8I zvEM1;c{jL0C7 zQ6j@c#)%9R8L6usDl%4AJ6L42$Z(PIA_L~j88I?sWX#B*kx?VVM#hZ{92q$>bY$$v z;E~ZI!$-!C1i;ls00{vS10)DY6p%0=aX0NT85NA)!KI zg#-(U77{KbUP!=PZN!j}x!RZ^K|`X3gbj%s5;!DsNa&E*A;CkUhlJ17#t#XgtBoKM zLRT9@B#1~9kuV~0L;{IK5(y;|OC*>`G?8#3@k9dZY9oq-)YZlm3974&DiT&Cu1H{! z$ReRdVv7V9i7paeB)&+1U2TMs5WCtKBSChxQAWay#2E=R5@{sVNUZUHAM7w&^K4u5 UBwxBG&6ASkOHK8pdQ!sv0^LWd&Hw-a diff --git a/telegramer/include/pytz/zoneinfo/America/Fort_Nelson b/telegramer/include/pytz/zoneinfo/America/Fort_Nelson deleted file mode 100644 index 5a0b7f1ca032be1adf8ba91f863e727c5ec6a026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2240 zcmdtiUrg0y9LMnk^7jb&%{)qk^`s_(DTW{@nyEO#KtK)_ty#9B;c99Y*4Bs?t>_;Q zI{UMVzS&KzCNgHTkqmEKt*i;lM2yHv7)mNy0ura^{abfkb=P{%p6B)3x&OR__f}S< z`~GpF+&^4Sy}NuT)VQbd;30j#EnvT@OVrNUm$!9po-5y#T{OqdpnRX%Wls3MmhQj- z)7`gEPEH)to(?OgdRz5}rcZ2d`yTzV?sePOxKn?s+-6T#m+Q~@8|*Kea`e}f40|T; z9@9UyL7wna^S{<3US4O8)=vYjxjEy{Rt`1bl_=$(jpI_W569@B5=%ZBe zy_I6ZUW$|OrzV?8+vnMc&B+>B;zp&pE^`CNOmb?Y zBu@-!ioZrudcW1w!3Sl2dyC%MRc#kE?$(8^57@NoCw0;8)%LbWcA4}YbL`^0Crn0Z zl+8@uXqKc8*sSPmlbsYP+5L%T>D7K&c4XY-^n5AH_b2FzwvXlZ`Y~Pk&TDeV)>FEw zw#lw8YS%Rny<&6IRM+M{X4hWoGIG0OWO=!6s1jS6l%v72VH+Huso`PalOo*n-}ps_La&bce4)^LHY_3( zs;}|Ic;9i}E4;pG1%*Lhajv_i?%wTganM)jzByrkzrlYopO8D7R#d%+%m|qhGACqG zo^Dpiw2*lr6GLW(OwH5H4VfG=J7jvu{E!JEGeo9{%n_L+GD~Ed$UKpWA~Qv%>gnc+ zOxDxQ7MU(GUu43_jFBlLb4DhO%o>?CGH+z!$jp(cBXdV4kIdfFO&^&*k^m$F=%s)U za=>v(0J4Cj0m%cB2qY6oDv(?t$w0D!q{GwY14#&y5hNu@PLQM^SwYf*-qmpuiv#e G%l{V~?gk?O diff --git a/telegramer/include/pytz/zoneinfo/America/Fort_Wayne b/telegramer/include/pytz/zoneinfo/America/Fort_Wayne deleted file mode 100644 index 09511ccdcf97a5baa8e1b0eb75e040eee6b6e0c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1666 zcmdVaT};kV0LStFL*AOuCUenZx^UBrvdmhxOs$2dygYfS)X7^*(Gj&G`CoXy!A)V7 zj1gwph`4Am!&tLPDN)B;GiE#Fg4v$G^F7?Tvbk}do&V?GbJNZ5`vh`JHYPfMoH6Db zE@z#&yhpm`(ReP#J$385Y}z-$J$<5IK3qA&eb}2J9~}s~PolrdCq?6QSLLwtH1(tI z&gph~rg!RRNjIEcr$zTg9C!NEQT;sF>h^bR(=P@Z+?N-Q$bt46ckp0^RE>G=tCE0x zT{q8tlQ~DeEtuxM|1w2?F#kQ+7OB1So^l$3+PD9eN{g?O>1hi@`f#((h%HnZU59jL z*nE|FwM;Mk6s;DWJSZ3UqzZp+sm!`QLuBXs<&ydku{0%KE~^|8%Ok^OAm@Py{1}!i zk}irB?+0V$3-!H%Z{Pi3)V$|q=@bSEsWXJKmn^$}xo_DFq8EfCi+vg;n z&ScNK-{G6O*dK5fq?x7iA@!2N?{$g&PIRztwO~~w!=^^t&CWy?? zYNm+H5t*db%o3ROUKeyIY$N9GOt=_6mHE4_Ah3eW^kll?b<)3P?{SV6`5GvV$ z>!b*k&+Kq~Q$+Gb87Ym4p_I0xOJ23EwRXGy^t!dad$cUAiv?G$);E_{!}vtZ>XZHE z1g+RVikOdTsvu92NRj5P=m+f{;YDv|wwq zt@m%>3RQdyXvm>JA0njZ|CORoX73;G9l>KjLYlrMUI($NLKZywc2WBZGAyeTAc`m0Wt zQHd`e(JOKeOTxSXy)tRH_YT}oMXpi?gztq z)BZPQUfV^R{_IYfU;T|;(7fN=96VxgS@oQ`HRV;Cv8cghOns=C{!)`UwnrEJoM~eQ@L_eggC1`P}>l*Qf4HRol&BWc9LiUDI}2 zs?uULTzyo+mnL*=aG$K5h_N+u2TjdqXKiF^uUUWKxZN<;Wj5|OXlsY+OkI7iy}!TM zXvJIlzzenVV0Mo_)L10-iOt$jnl25K<=U7LD~(?Uv?+c zW9>8T5=`ts;9x zHjC^Q*)FnQWW&ggku4*8MmCM?8rim|+c&atPq%Yq>&V`b%_F-jcsYq!&mtkZvIDK>C3+1nCIU5~L?cQ;@D8ZSiz{K^o)fI)k*v z)Aa^v4$>W@JxG6$1|c0nT7>inX%f;Uq)nc#Pe`LYU8j&%dAeR9%|g0`vkw>1`{M-4z&U$vX@B8DMxwydN_~QvO|KY<^ zYCe2#w`XIaqm#E{VrS2H4T*ZIT{=C|(7;<7`th80Z9cBu?jF$Ymv(5_nX?jpv`!-S z?w1~wDv=eNq-Rly^qRdudT0A2DkVeuIFlthJVyGq>nbrp=^FESpvFFr)_zT0wEvYh zI-vfmI%{5QT=fHu-*Q6}R-aK<{xMC=y)W*Pdhw(-$iT@vB`IQ`B)iKbxy3Jo!>V=g z<9RaVN2v}yn=Zp1=4eW7o~AYo)wHc6b@-le9Z?XaBex8ZQJF!So*yQoqhD%9dW&QR zUDvFrCzAF4g^UTjAY&S@$=K#YGOq53WZyim-l|F&fApYEC@z+Xm78_a^zAyiXrWFS zRHVM_ES(xUUZ*+x>9j8{%?ZhroO>O0#^+d>dFqqSY6_Ow2RcY@{X3cC|0sFYjWRc{ zN#?D8qw|NKmIb*tH9vBBBE7v8DeY*7UEG@daQkHE? z)#BPIvb-=-S8Pp^m6KAWq##sQCH9cj8Q)}02Zxj)I2w zvaYc~D|TPh^>zDnLwSw*tNiM>EGwXOtH6K$*UGYPZ*({;tLcuT_3wA{(}1>?#XH;U zbHuqk=HoV}7ZC94<@<|kH9ySaVtKe)9q&TEHq&%cPq(G!Xq(r1fq)1y+B~qrXsS_y_sT3&{ zsTC;}sTL_0sTV02sTe64sTnES)>Ms@ZENaA3P&nON=Ir(ibtwP%17!)764fRWC@Tp zKo$X61!Ng)%{m|pfvg0w6v$d2i-D{LvK+{IAPa)52(l!|njnjUtO~L$wq{+Bg|Rg& zgDefQHpt>2tAi{LvOdTH+5T566r5s~Da~Wv?lh;@6Q30CN{Dkiy@{@0UlW6W0s=0- ADF6Tf diff --git a/telegramer/include/pytz/zoneinfo/America/Goose_Bay b/telegramer/include/pytz/zoneinfo/America/Goose_Bay deleted file mode 100644 index a3f299079aebb8524bf77e7f92e0a7e6d0a7b6fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3210 zcmeIzZBP_-0LSq=QA894A5kbpO;7;=CC{f2!#EIRQ_#Z{SH%cC(+ov(!)%-pW}0!v z0ZntzCP(o_V~UfPO@t+SIaUiKok_S#kkP5&Q>ZJ~|GU0n##hYrvc1{;{MozV|fWj>3-)o~c(V%Tn!Cj%dr-EqBVl`*@J$`^=j1a|I79 z)zd1<&#wq@To_(j?wozk@k5W3VHZbTc3iws5_ZXS+EF{^{`y+E#aUOB;QA%m%X;~( zajyFD&DLM7J}y_E3)U;4t*$G79kX5y=xw`NTkULU%(wl1Y^}Aia*^#?ahg>tv)HcZ zMq6(bj<7W)4YK~ROt&?MJ+QVU2D<(n7~s4)?y>7;`#oo?cY~|7=CbqemP(iV#A)ZP z^M_owzpb?1IsT2U?cgD6`>unwyW2jr-dnfbc7J)c^+DPy+rzlmtp7}!YwHMG>FoGz zjqTBZNcpkPJoRx$vi9U=gsQv3wWrPjYNw;W<~iD~n)bG7=ACtFkAhmwYkiy4Q@$hj zl4>RI*)?+Ss8f>9s0z7{{~pQLR4V(nZI=3K1#-VDC8}RlrriIlP3nMS8#VuZHZ`Dl zu{LnabahbfG;MHZusS3uNE>SDRELE<)dEKcNyB}vX(P-}r4jAA9CZDf6kO9Nzi{@f z^x}ysdE}>`NuxH_>ml27?V~ds`k1_ehOx2x^_P-!+~bBQdgz2CcWCE6WxVf1_xQ%G z%7j)w_r%I&N_gD_dqi2B5?RsDKB+K7iQ0YJ9-T8wkJ+HwCnt{7rz|*Wj}0~JFUOSF zr+PQ(mY^@(mX>OL+LPVx>F1B?Gp^>lXC6PN%=&tf`;}csl(WLAt?!-D-Px2e)PO6xrC%YfH=N(wCq|^_!rz-Kv{4WPf zX-nf|sq~@r`pgh{!A6&~FxX2@&p0EcKWUa1#U79rHJdftiw@Oxu1U-6+^;Sc%KhA5&TF|Kt=SW<*?(?Q*KX>sy?wk|U6;|Ot>1N2eJ8e7+pw-!eRs$yEkA9I zn%`L?Z?Y6gn;I+R&4GE+=E_oeOGk?IURi;>)fFizg_-jE4u7dIYlggS?_J5^>{8UHJLn~pGr(UJ)VZcW*>2O8fO>h2A8>? z@$~n2F01Cj;`ddiK#!+MGY3C=laiWln!ixo3F4N-y*S+zFV6AeU3`K#7?=4OJf9uY zyD?B6ab?Y#ITjfzWUP?ELPiT2E@Zrr0YgR%88T$dkU>L64H-6M+ziFQAtQ$jouL>z zWblyDLxv9-KV$%r5k!X2P>dlmh=yVmkzqu}5gABiB$1&+#u6D!WHgcCM8*>tP-H}r zAvF|ZiVUiu7*%9gk#R)^78zM&Xpyl+1{WD!WO$MBMFtodVPuGrF*X!~jEpie%!Xo| zk%2}=8X0Od#+n0z%_T+~7;a>|kpV|W92s(C%#lGyMjaV;WZVtKz#}7%3_UXT$lxQR zj|@LD{zw3j2p}OqVt@nzi2@P^Bo0U*3`Hc6P$02Df`LQ>2?r7nBp^sckdPoTL4txr z1qq9xhzk-JLlGGyG)Qca;2_aK!h^&I2@nz?Bt%GzkRTyZLc)Z^2?>;;h!hekBvweU zkZ2*{LgIx442c*LG9+e5(2%GhVKWqQLjq?gB8P;|P{a-i9uhqyd`SF|03s1YLWsl= z2_h0jB#ee4jz}O4MI@0>8j4sV!9=2ogcFG;5>OGB0)u>ii8!3D-u{k5m_X( zh9b5|a1BLtk?DssBuY?HQK+aC6}4m#hKr(T@q$3c0Y<0{i&8JI7MzS=ITbsBR#6iT z(Ty<}G|`Y4FL7hgs*R17c#FDets!lU8hYx2g(>Y(HbxUZ&+~O}YSK+-^35-qNoMsv z|8PTeOQG}5SDXEHoPARE*pKcHE{`T3Xfl&KR_iIzfJyl*Tc-wn=EEfu^;F*kla}d| zX$iw-dfWt={xVU`_+wboAO5L6y7@pdF8-`$p1dftVvd=;w_7snpP4yZ`}EwRJ7!+x zVf}IHS(BCBrn5$GD_>&0_6?p<+0Pg2`TbogXCN%Oy;0@Av`F$gt5kk>niNDLrf~af zSr7=Cg=?QmQPzC3sIXrak54kiGal>Wp})HNzQ09p zXgq2*_O8(qx~w*JHOuDQ9`!{eB#lY!swt2sO~VZ;l9eoxpGs78+=xWaXQ-`1ze;n@ zQq%I_h29oRH(&M*=+^pCqYhosUlnPyz4fSWOTA@w)O6?_qrasjRc4_ZF zr8-_U$Tz+F)whpIWM^kY?Yf#Fog0E`_c2Gt#Et!r-tqtTk6ic1TE}tSt8q@e>;7=T zNpRhJH^xll)0yfY=Z_?SWPqgLX>&l5@U&SV zX&`wZi6EIEsUW!^$spMv=^*(a2_YFFDIqx_NqO3=khGAzki?M8kkpXekmQi;ko1uJ zkOYwokrX{`j!2T8HcKQ;Pn#!_D3U3XDv~RbERrpfE|M>jFp@EnvZu`%NgBx-NgK%< zNgT->Ngc@@Ngl}_Ngv7I(@p>~1Dd1(_FQVvw0ZrUw6KbCYPdr^s$kPH2I@(4U_ZDk$(5 I`12Fr0@ri+2mk;8 diff --git a/telegramer/include/pytz/zoneinfo/America/Grenada b/telegramer/include/pytz/zoneinfo/America/Grenada deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dh($ diff --git a/telegramer/include/pytz/zoneinfo/America/Guayaquil b/telegramer/include/pytz/zoneinfo/America/Guayaquil deleted file mode 100644 index 0559a7a4a4288d7e3c3b1bcf47c57a7ea82be2ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zzbM`vMfL>&;TU1Epd|27W;ESyYQJq{r~^}8JU@wng9Ras=>hU|NpiO z2A2Q-j~`&<`2Tc3(OammrB(9CyZS}yHNw1bTgHVm6jB5jH#bIBT=h@DgD2<0+F zE){-jXvqBB9lCtBp(*Ag*F>2l*ZulDKmYbe$2tAkcjvpu@9b=6|Gl2?#35-fM|uA7 zR5d^0TczsIIfqlj81N7U?a# zzS-S1??=a1a?!UtHO|@d{v3C&$aVI;mf`MqraK45cXkg3k8lok80$N9JKKA>uJ9c` zA-zXt|167}-^eJIS5-;oaedVNUL8v+(8rtPsUM;j>r&5rbs{87pU|1=WZ6`CYW)OJ zR+u7B=L{4&H&&iWixEF(HZ?m(0s2z;9d)_dS$(~(uefLrub+0sB z-#=7SRTR|F{m$@^@KP6pLzZk$lM6ECQS4%ZGy(iXhJd z8FX#3ctlTyF#ttlNnl0@Z>TP?fJcwKbs`>uR`M_Zr|Mo;LsajjQ)T?|D3OqrBKvHuBl-@Dm14n7(XVq;**~*X3}{$cCMInciFeP- zfzeCF!1Dom@Dl}U@V>J;xni*zvU}2^?L9ob9?Ifoyx-KdOJm6R5Di8Pv5Bd-O```Eb_eqb(??0vjs`&i}eV#!3 z`BD2lI6fiK)3v*K2bgz|c}1cbDvu|?eoK6SZS$LleM2@5**RqEkiA1T57|9r`;h%Z zHqdHz5ZOYj*+XO#t!5XIZAA7F*+^t3k*!4b64^{-H<9f`_7mAqWJi%LMfMcgRIAxl zWLvFfUy+Tqnw>?q7TH^5bCKOewinr7WP_0%Mz$E)V`P)9W|xs|wwirLHX7M!tJ!K~ zuaV70b{pAlWWSLOM|Rw5wj9}WWYdvdN46c=cVy#{okzAF*?VO3k=?hN?ML?CY8rra z0BHfz1EdK^7mzj}eLxz4bOLDw(hH;+R?`in9Y{ZrhM?^TA7}}W=?Tyjq$@~UkiH;| zK{|u92I&pb9Hcu|(;lQhNQ00LAuU3Bgft2164EB5Pe`MXP9d#AdWAF#=@!y1tLYcg zFr;Hh%aEQSO+&hdv<>MS(m14ZNb8W^A)-HL1|kUQI=tf)edtf=$1?pl|xD|4?{(JNE*?u4_}s_>UJ zX32ydGuJ7x)5Chtc&pqydP?u>tC6^#!y4a_BMC?LYhrVvB-IzHyE0poA6cU*S@C+m zwOu^RFUsoVld}3okEBKnY3kI7J}~=%K6v(HO&fbv*K`fb+E4drdfO?%XYm=ib!9cY3sF{D2m}a8yhBO0~4SQ6KHF zv@GWhd90;GcEmTx~ zOG8JMG+s)O1I^je)Eg$vrSbCIk)Omb!2jPqf&PDaf&%}$$LH%Fbh*seb_M!;=WM?a zpYQZZ*Ze=f{Mnr2b!{~7x=iyHc+GlTEKoDkEh^>WmZ`sWeh*q}E8W9Zj{7 zayy!OBL#Of6-P>r)Ep@~Qgx*4NZs-OTli2Ddyk2JTuw?tazavKQe2MPo!~Y%cj)ib CX!a@q diff --git a/telegramer/include/pytz/zoneinfo/America/Hermosillo b/telegramer/include/pytz/zoneinfo/America/Hermosillo deleted file mode 100644 index 791a9fa2b38729ff10032f9e1f021754c76c87f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 416 zcmWHE%1kq2zzg_+vTQ&svUJ;vg7u&B9&AesOW5vwDPaeXS;CILZ68j*DonUBamxn= zhJpse^cDf5VzCBe_cQ^MpdSsU>K+1SMzk`5cLV|Pu0|Cg%AR6Rs5DjuVhz2h^bR(=P@Z+?N-Q$bt46ckp0^RE>G=tCE0x zT{q8tlQ~DeEtuxM|1w2?F#kQ+7OB1So^l$3+PD9eN{g?O>1hi@`f#((h%HnZU59jL z*nE|FwM;Mk6s;DWJSZ3UqzZp+sm!`QLuBXs<&ydku{0%KE~^|8%Ok^OAm@Py{1}!i zk}irB?+0V$3-!H%Z{Pi3)V$|q=@bSEsWXJKmn^$}xo_DFq8EfCi+vg;n z&ScNK-{G6O*dK5fq?x7iA@!2N?{$g&PIRztwO~~w!=^^t&CWy?? zYNm+H5t*db%o3R-DV@{%eY?zBUH7p6`TTcudiDF_T~hY^ zO!>=&*l&2aJ@(-}THBBMeTjPSC%>tNnz6cZ&jt1M>pp#U+K@V15^B!potKU&r_Fb% zN2ODmO;=Q%boIPtzV{v07f!4<7rS@qOZ(qc-K|ynhpp>WPko{8E%U0r>I{9^GfVwg z9H*}oq?`WCbops^thpK=D_3t$G}r9^eyx4j{M_FszjU;jfiK$R`te>h*xaMd-c#zv zwv&2jX|1|7Tq?J(ddx_tM}Ge@!T4Gd#Q%PTk=+pzP&;Twy*wy^Yr|Dg$rv4+dtKf2 z#DES-{ziqo5wAldKT@Fw-jy)(wi?s3Lx*=AG!Z8%^w?wD&A9#BC9hD=-asd+HX%B)Qtlyz&~GwY+;r97wBl=}vBWm=P}>^`G6MAxVdt%r2g@Jh9@ zeuv)FnWZ*YSLjz-5><6^fqr%OST!oZ{saa&c)jya@ZWrY=fCZ~4gQBe`&a*(-~ZuP zB7Xm|g8@N){|5~++P#On&qzLH!k^#I%l68XbL_LwJ_Yv4^~zlP&IPzn@cxJO`Rx@4 z`WlcGB1=Tph%6FWC9+JXT_>_oWTnVbk+mX=b=uV;%SG0UEEriavSeh<$fA)|Bg;nC zjVv5lIkI$Q?a1PtcJ;{eop$|50gwtHB|vI`6alFMQU;_BNFk6)Af-TRfvy<5Pz}zO zgQFfuK{zUclmw{>QWT^rPFohFE>2q*j>;gVL282(2dNHH9*+7T1>&d>QX-BTAw}Y- z5>h6PIw6JPsFc%|3aJ%RETmdUxsZAx1>>j~QZkO3Aw}b;8d5fnx;bs(kjf#YLu%)= z#p9@+)0U5;eok9JjtU|rL~4i>5vd|lMx>5NA(2WVr9^7!w8ccK>9pnKsHf8wl%t|Z zNjYkY6qTc@NLe}RiWC;9EK*vewn%Z2>N;(Ck@`AqfsqP3ZHbW@BSq$@GE!!aIwOVV zs5DY)j#?wd=BT#QmK&+J(-s`5xYL##sX0<~r0Pi7k-8&=$NyL5!|X4CS@xGfV)kQ6 RGn0}Nvr|%%Qj(Ix{s3RXO|k$0 diff --git a/telegramer/include/pytz/zoneinfo/America/Indiana/Marengo b/telegramer/include/pytz/zoneinfo/America/Indiana/Marengo deleted file mode 100644 index 1abf75e7e864625b975feebdd0b232d0de624b27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1722 zcmdVaUucbC0LSt7*lf0p5t>?4b|E$U$0SXWtu-8mgBj)-o6~G~Ff;$=T==^ZN}Hsa z$lo;oLt03qB-tYQvpvQ}7|+ZF$$35Br*PrQrT6W9KX2#aT>ZX}FRyq>s`J+sZhqn6 z@|%b6*noM}9!m%uy7o=hZR-;_eBhb9w<8#6ivJ>;3L^CLmYTqelY3-a<+#AB?9uXd z{*XZX@EF;VmF~RhKT5wH7U#VEJV?JY|Mu?TSN*=D&G~TdsqSpN?R?yOU4N=qf#8)` z?H+fPQxnvl?Jrf2wMvJ`pa>N|WX~KW!p67C@Z?(}eAi$Z5q(}poY|)%^)``_R4y|! zCW_4N6FO_eLY38ArL&_ZsO$@+dQxY+ntX7lobq_Q@NO)TQ!ft{)8>0+PIai5o}MIU ztmzOlWBWVZy<5&sJ0)hf_tm*^jVkwcm!2Cuq4JJ4>v=6zYW|i>dO<^}$}gzmbzSwSs#Us5a6lwP%>My(!rOP5Vsr^?O(rKw?4{o zT=(i(PpIpju5)_X@80$u&D$B^x_54PVy1X~&cqD!%rws&^W^xPO!J*-e&h1kH~9Wx zg08vpLxOe46p=Y1lSF2TOcR->)l3wbDKb@LuE=DO*&@?L=8H@inX%PO8JV-yOd6Rr zGHqnu$i$JEqn$b*%$>_j9+*8cePsSf0!Ri(3P=uClLV55)ue&sfh2-tVl}BCxmZmy zNH$iJ4w4U&5Rws+5|R^=6p|H^7Lpf|7?PRQq=w{XHOV2_SxtIKen^5yhDeG?j!2S7 zmPndNo=Bodrbwztu2z#QlC9OGi{xuH2_qRJDI+-}Nh4V!X(M?fi6faKsUx{tP4Y__Ap*4#5^wY1K!@B6uk#4G8p=kPnP-!^vl zyg#Ytwufh%t4^N&hKJLo5AVf+e)Ydz7VP}+4;k1rHF)*q@8q-RGQp`C7v)rIzWXtK zdw6zckqkA|nxC$}#SNF1nBfaIxpVs8=T1(zpND=hzr6je8##W=oPY5nH@Ytj+|EyA zY|GK$!p7HRymNPOaaphY+PEqB?T$A2y>eA>X>!_Knn;_=!wL82>4f>~#4MLNILkzP z;?C@dn^^CtoAkt}$y!q=*{xH8oTl@VJ9i|=tNK#%UMLOnr@bTjdv}=vw@s#mTZ6)H z_Ph9zwZYUwFS)wPZmF+ZAob%Pn1=Gzu3>PqOT_OC6YqAoyGLe(_q_7F%=>z-O|Ea3 z`S0Is8aF?g(2}YuOvV zc4fYo?pyn8_nD8Sr>MvF9Ns1CCYtS&{m;r%r)%x{4QYA$V2Rz(l8}u%jGYwIPgb^v z*MEOcD&jT(h$-S(h~Zf zc%dndcEzDBq%RJQA)O(uA-y5Zd9^#FJ+Jo1p+TfWq(!7hq)DVpq)o5($)QoNc8av> z)n1Wik#3Q8k$#bek&cm;k)DyJk*<-pz1la@xK}$zTK8)2Nb^YdNc%|t$Oa%gfNTM> z2goKMyMSy1vJc2ccy%X`t?=qzAe-UU-9WYj*$-qxkR3s`1lbd0Q;=OjwguT2WMjO# zGsxC>b#IW(@#^j%+k@;6vO&lWAzOs(5&oYxsX(WyMyIMQIj3TFMO9g{y1JseqN?C; Daf7s4 diff --git a/telegramer/include/pytz/zoneinfo/America/Indiana/Tell_City b/telegramer/include/pytz/zoneinfo/America/Indiana/Tell_City deleted file mode 100644 index 7bbb653cd7ce09d88f8a1701853bfe92ac7db0c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1684 zcmdtiOGuPa9ER~TnU<0gQsF{u(IUdp9Ls2-s}vb?vdd(pre&Ji%~PqQm7YatyNIA0 zp$1k&bdkU;LPG5V5z&Q^f-p;yYVxE8Mm1mOIS>V{YS~}D`F#v7-)H=J6`PaA9~W-E z;o*wS!+WeUAI)dos^!KP+1ePTPM>@s?;i}R$8*Nz|LSMb|IMXxQ#xht zV4PSR_f)UFEyTKDht6vo5cw~U>w*KlVtrSgE-X1Kikb`b24A<@*i~YldcK|RL$ZxS@ZIt+7r_#_jI+Z+KF;m*L+0n?aPw;>Z{fMPPeQt zT%#I}N6ASp^A|GN!t1ZUQ1c!Z8W#HNn}2(AoJ;40aGdK`T$3H=LaUf+->2En>3oVA zj&t{xE9UqA$@clhl63Q|GS3?GEcc5H^PXY8<6QRh=ZpU@mgkr&3<2F zvPfi=R`wALV<_HLms;5`X_`4149?FT>s>1wwaElEvDsEwykBhhsyHO zZH)*sijpD{%CHVn5fwxPN)ZG$1$h%j>`+Aen(tgY1znoQywBsEzrRqpYk#rx$603n za5?qn^6iV8*XYf>_|?ZhMdHab20;rYdKLbyen8Rdv5dZ(GP!+pk=Z)$hwh zaG+b(JX|koTY|DK;T83z1#-u+dC}m@lA*>|BJ^`fHkI5KO$ z8?$=%+^}jnGpYAX_o&v65#2UgqeSq5Y#-_td-D@=UnD3xGGnr{vOsh$gk@wymWaG5 zm0jseqU&ysJTU)GbSF0Jp0|tYVAQ7%-J4atjY%ypPO8HtN+0PRQAgK3(|xu5sxSFT zMmHrybndB)WyeHp`nv4@9u_A?&&rb@O2xonM4p<;5reH&^7JJ~qP<;Y|3{DO zPLw&0>pq(BcwP6-n6un|ue9$qyq&eK`|^n=yE+(h$}7xmFn6c9bs;BUz60hT$7A0R z`im%Bb6QiAV@MoGAV?%gC`c?wFjf-{5)RsU_#hyTi3kV@i3te`i3$k|i3v*sK{UBFU

CW46GxWPD@y@$16ZL!ZukRmz*B|;`IUg@P*ZrNholn<$ zcAo9eWqhYvd3!*wikw#Y$Gdbv_kLBlyG^g|C|5=6oAsK|LM8l{ zWN~AeSUasrt_%9b`cbvABs)oz^ykaqlo%1bpDs&>ghgpIr769+vRQ4Ja7$O`ZBx~em$GJ7lc?#wDQjbEMQz71x$S$t*dE#= zcRWiMJL`gSS9`puE6S1e2OTlUXRfGd3$MRlgNK;Q{AlI5TWhJ2!l>x&DANFVoyh&Ar0hivmuTdCxMxaeVd(fxj4@XHHR6qy-ru zGD2jC$QY49BBQjLVIt#128xUn87eYXWU$C+k>MiaMFxzF*lLE1jM-`ijf@%@HZpEx z;K<04p(A5Q29Jy$89p+8v;pu!1RN6r5Cakf5{1=-!66Q-2?UA6YC=I`L4rY|LBc`e zK>|V|LPA1fLV`k~vYN1vxU42HBr>ZB4T%j24v7v44~Y*65Qz{85s48A5{VKC6N%Gm z0!1RVnoyBgttMC`S|nT~UL;^7VkBfFW+Z4NY9wqVZmS6#iQH;JM`E{{;F0K&@R9hD n4*>EJKt2TcUw;fnm~TXy`9>u8rzfT+CMWn)QW8@VlSljl+LDCq diff --git a/telegramer/include/pytz/zoneinfo/America/Indiana/Winamac b/telegramer/include/pytz/zoneinfo/America/Indiana/Winamac deleted file mode 100644 index 630935c1e1a8c1a87e728fc1dcc4c930e81b30e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1778 zcmdtiNo-9~7{KxKsHrhEK`fd_SlCpnq=-BT(WWV$@zqe&F*eoW&>^j%?!vsmrbVPG z#28AbBGMp&L`0~X)iIT*a;g?Y@~-ngTQ)46n|ppYZ}k@6|BGbhE*&au>T$ND5X|B z7S-lS?>*v)-esOfYrJPy3e5Ay3h%|SovN{})O)#YwbGSyyjQsq^}1}d_aK4KlBMl*zx_s+SkXn-$gddSzzRs2w$WRf;yNS61kP-q%dwh7N0qL=UAyb|a;F&q)&Qtdn4zBBDRB_h)7 zcbff;6L2>~{$ebBd$QX{tB~jVFT6%uPV ZON!kr3E^RhLlcK2gp-pKlM{!;{sIlvy2bzi diff --git a/telegramer/include/pytz/zoneinfo/America/Indianapolis b/telegramer/include/pytz/zoneinfo/America/Indianapolis deleted file mode 100644 index 09511ccdcf97a5baa8e1b0eb75e040eee6b6e0c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1666 zcmdVaT};kV0LStFL*AOuCUenZx^UBrvdmhxOs$2dygYfS)X7^*(Gj&G`CoXy!A)V7 zj1gwph`4Am!&tLPDN)B;GiE#Fg4v$G^F7?Tvbk}do&V?GbJNZ5`vh`JHYPfMoH6Db zE@z#&yhpm`(ReP#J$385Y}z-$J$<5IK3qA&eb}2J9~}s~PolrdCq?6QSLLwtH1(tI z&gph~rg!RRNjIEcr$zTg9C!NEQT;sF>h^bR(=P@Z+?N-Q$bt46ckp0^RE>G=tCE0x zT{q8tlQ~DeEtuxM|1w2?F#kQ+7OB1So^l$3+PD9eN{g?O>1hi@`f#((h%HnZU59jL z*nE|FwM;Mk6s;DWJSZ3UqzZp+sm!`QLuBXs<&ydku{0%KE~^|8%Ok^OAm@Py{1}!i zk}irB?+0V$3-!H%Z{Pi3)V$|q=@bSEsWXJKmn^$}xo_DFq8EfCi+vg;n z&ScNK-{G6O*dK5fq?x7iA@!2N?{$g&PIRztwO~~w!=^^t&CWy?? zYNm+H5t*db%o3ROMnJib$2dy-9&dm+y96rkCaC7}x*#9=m=AQ?$SzN#O&*yDDYJS=6#qOT|-k=yFxg@sS|2sp)lb`L<@x5}{P}kJ+$wn_ zGhknLk+WKGHEtFReyD|)wo7Q>Z4DoPMwC_>6s`8zBU!-|FlcKJ#CgA4d}Ad2hH-{KXk=` zeP(6bm-=Ma0a;ahRG(_uE0sm>XjSD#sk${}qoEEHy>!@CXEmGZ<2`ouwUwsk{g3t8 z%S{sN=+-qSmrHF^QtSHj(6)R()vWmD*wzLH2vOIDw@Mr)+?T1pea zkR&cNYI5QfNq$qoAewuT(Ly?5B`DE-^+Y&&6gWnZzo zYx~Xau{)Y7J0_`-@3eh-pR^C2(Kkj@^5(!H-SbtWbad|5y{9Uqv$<9Gy_YK)<1+3W z|3CcQeSPWlCm)O(pHBCR$xNpYs>$--ObtaMrmnIwZtB*?O^lEGKmH${ZQ31A7kR$O z6Xtlv$Wun1Gslxgo;AnQMxHnF#F1z2bx$36?#Pozo;~vPk>`&jfMkHAfaJiD1d;_u z8b}^qmk5%H*QJ8w!jTM;4U!I$50Vg)5t0&;6Ot5?6_OT`7m^r~nb)O;kDlDXHVj^yrj$s^fwq>tp!F#*U7a7+O*2ON`t%mOkE$UGnufy{*0O$9O+UN;%Y zY2&h6g2+0|*wv~66jcHPoD(`EYAW|!+$`=xf- z%BF!K`9#E+K{1m4AVthS8AUMi4;ErM9Y$2FUqO2iEq=BjX>d=!_b>EGPuAnkIOlaZ zJ8VzR`;%(RJXP!c>j>H>oE$y&h&RaBb-Yh`*H|DwF2*N!8MqFOSwo)T-x3WpzoRO0|9=sfls5 zCib4J`S}*Jw)C7{d-keXmph`{-X1oOkL=Uy_nb6O40h@b?T5_9{*_viAF54zJ7jZp zpL#N#l=gydwIv>rEn{seT~R3MFY8ri%D7}cC{-z zu0_88{+r%RejAu`{W)(|N26X-OUi3nZO_I=F7|vZO0-Ux2qy(e}{tHDQRUl;`bs&Wxl^~@cwK#1tNHs_~NIghFNJU6VNKHslNL5H# zNL@%_NM%l28d96n7Kc=al!w%Z6o^!al!(-b6p2)cl!?@d6pB=el#0~qw8e5zEmAI0 zFH$g4F;X&8Gg35CHBvTGH&QrKxzm=8)b6y!Bh@=?`AGf90w61ZECI3x$RZ%CfGh*D z4#+|vE8(x3*6vQo%WA#3Hdi-oL~(=HdXUQWAU$ciCL nhO8N~XvnG|%ZC52bqm@pjM*8iO3n}0hND%(@dH diff --git a/telegramer/include/pytz/zoneinfo/America/Jamaica b/telegramer/include/pytz/zoneinfo/America/Jamaica deleted file mode 100644 index 2a9b7fd52d37a1ffe9fc589daa04d88c6c71a6e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482 zcmbu*F;Buk9ES0uk_c^)3WDIMQyobh+%-Uyi7q;I5!0=!3o!vT4otAP8W#r=1_s#J z{0waO1x)S}c>W(|V`9AK?l*LL-sn2%HPo-CDu1(bgL`?##rfCvsGjD7w>UqY7}q?; zo_eWXN!6g5cEMyW!| okUFFgsYFVVTBI1MM#_&qWD#T){QokpOmyOY16WpdI{*Lx diff --git a/telegramer/include/pytz/zoneinfo/America/Jujuy b/telegramer/include/pytz/zoneinfo/America/Jujuy deleted file mode 100644 index 604b85663672d83658f331a69cc8f41cf2a2b5a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1048 zcmc)IziU%b9ER~5+h`0O0t$+V6l|%D2#2&WqE(J$D2Q2Vq0q@e1VK<7T0u0rIEWCC zAP6mub`?Di)zDUZY^lZoJu zSD(CHFK6>*`MGl=rygDy(2usgFvT4?U0gYht9#9lQqumMJ*Ix$9nfoI zN#(Bh>J5`eOr+yXVP}5Ibj0$7&eRvv`FW(!6(6!)@7|bQtJie5*O3>B&#TFxHTE2l?JA{hLKJ>(PJxv_giWEW%`WFKTBuk3_u<(0jV&5+%Y?U4PD z4Urv@Es;HuO_5!ZZN0KDvawfoMz%)w_R8kS?q1m**&k^D>A)*3AUz;Wc%=)Z4X^Zp zG=g-3w1V`4G=shy9;6+~JfI(>A*3UuC8Q^>G=+5Km9~(+kj9YCkk*jikmiu?ywVU^9er*|PNcBz}FB9RnGrW$zbm4qC)*I0=T}<1! zFcjI4rlMAPLeV8|<&sxIu2+{Sw|6MyK6Fxee$t`o-yKo0U!U{FeY9KMa^i^h)^`pI z@4nsM+jfP-?VDS@GnzJu_}aB1q1-R-C@S#IOwSjIb8AAg+=(J7A}^Gb8ShQ{d8wX# zae+!6nXm6WBS;iZMLX#6*E&nF&v@A)r9u};0lDI8y{LHOW4-c=4pq7Jpnmwh zI#pG_Q&;aTR;!AG`jPe&Rg+w<{Q-ya|COl&c^6gS`-kM}l(1NRCPUUvTo82wvGVb; z)1rRIHTlHgfLPNwET8P}5l>ZK(G5KZ)zjHSdTns0YK+r*U0G1AyShgQ)5=xwT$^rk zWvZq_0lofGoO*U|t9))aLpT8}nz$P0hoiHDO$~RUH*=;hDO< za7eX}{i!<=wdxo=uQ&g(M{Vis6-(J2u+zjIMu!Z~YQ~3l zfLs_Mj~OB`Mr4r4D3M_z<3t9EjMQp|ij38228)aq87?wjWWdOXks%{vMh1}uVBnU_pkT4)|Kmvh80tp2Y3nUm;6AdIB zRuc~-AV@@zkRUNZf`UW^2@4VzBrr&1kkBBpL4t!s2MLeW#0Low5+Ni+NQ{smAyGoY zgv1F66cQ;UR7k9lU|CJHkZ@T|ypVudO~jCpAu&UOhC~es8xl7pa7g5k&>^uyf@d|+ zL&9e@@k0V=H4#KYh{O;HA`(R;j7S`jKq8SuLW#r@2__OvB%D?gPb8pL6Hz3jRufYs zs7O?iup)6q0*gcz2`v&^B)CX)k?>kge31ZKO@xsUTTP6SAR|#m!i>Zj2{aOEybVwUxvTJ(v;m$ubN&=(eTmKJJj-=H&#qc1x@xN1P!yEL z@}ch6c?0B|!k+HAltgLr=Q!UE^qKFHea`n?9CO}&`}v<6%@0kD&V>`Truk5;$)$#j7a?`$Ly8ZTNOotce=!8|X&CO-^>syNarsI@8 zx>H_?={&fd?vm2hB=$;CUE4I9ZY|oXZa>CJ_l9PbbnJrkIB-{lTVhXQI=e5uU#r>oh`1rkb5P@zw9q^R{3RkXt=bDKU_^D2gz`A0A7M@o{+ zqt$hKL4MfCnmzilY;6`UsMN)s4w^;d%Jib}St%J*p-SoxNNHlJDy?}<%6=_SPgE_J zCr{?6CFLP`YOha~7v{;*7aY|h>aS1B=l;2%H=J*L|6iRWk?IYO6N&75 zD=In?d2^%F#yhw3o*j5P9V3yC_D3a-2?U)HBkeuW-VfS)e9+0Y=Un@aE6RI9@L&A% zBlaq68QzE#0I2{{0;C2=5s)e%W$?6hKnj6W!qb)lsfDL422u^A97sKof*=(^N`lk` zDGE{*q%25XkisC9K}v(v1}P3w9Zy>xq&`T2kP0CsLTZE*38@lNCiLpWfkJWFN&%%p zYK0UFsTNW$q+UqDJZ;60l6l&iAw@%~hLjDd8&Wu=a!Bcr+9Ab5s)v*hsUK26q=HBZ zks2aJ^t4q(%81kvDI`)!q?AZ4kzyj%M9PWO6DcTCQKY1vwx&o?J#AHyvLbaw3X4=0 zDJ@c4q_{|Rk@6z-MGA~m7%4GQW2DHQw#rDEkvb!VMkY^2&qxsiG!1xG55 zl-$$S94We|tvXV6Pg{4S@JQv6(j&D;ijPzuDL+zwWC4&BK$gJMt^u+Lo^}EQ+UH6=YdF?YbZf<7rn0SsG+*ki|h( z2U#9ueUJr0RtQ-lWQ~wT^0cdjER&~QCuE^K?Mfj_g{&2_SjcK2%Z02L{=Z=S3M^Qh a9ky&cY^j0%X<2C*se#PQw9K@OxIY1Es=sdl diff --git a/telegramer/include/pytz/zoneinfo/America/Kentucky/Monticello b/telegramer/include/pytz/zoneinfo/America/Kentucky/Monticello deleted file mode 100644 index 438e3eab4a6e581c6f5e7661098bf07b1a000593..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2352 zcmd_qUrg0y9LMp8Kk`oyC@Dmuf|4QsF%pRi1I93qkRAxg@BuM~P&_6gl;O`!Gu@h- z655|3%+{>tB#EZANLw@4pCz)I&7xlyvF3(8L>CoyPS5+f?W)#Y-?QI&{m!}nyuJ0U zPvnWeU8wnohwC#B?}?828h9haIr{$ZYVi35&d@7Y)aNhwoJ%Rcs!NUG`o`r7@#lqf z?K_<={KuoTI1(iS_FmV4+pmkDEs-j?alSLJ>Y55Eo_0cWE~?NMQ=RabQ!4z#F%hBr z)O`1l6Z!41jyk)|S#ai{j(+o?hKT!;`9pnI z{)D)D)lt19Zd9Z$-KA4~<4#({Mx8b};w-&drqjn>axx~GROY~TCu^isW%t%OIfs_0 z+?Fnpw>3;HE3Xyzti7u8Qwzntd0(jIp^2hk@z1(o$|qLLeWnY~kL&xc47f!nuc`Y# zaNGwDTvQLf+T<2@oKh=y@01T!_NkKQMp>HOrOHZ6WZCR-U7oqYEuYx0E237q6(j9> z)pUyc@N4_j>TimrXGfb_^Io#7T<=j;{lW5)qHMKx+mu|F7^}S17o>M?R@LOcC2M|) z*0o9J-P%uPbe-?GTmSlmer$5TyMFhmetfLmZD>BMHw>(CrRQDMxVKFGQOxQ}&bWslqG`$2UT^vkZvbE-SOTXv5fQ$4@c%V!4msAn%1$sN5d>bcWN zvbUi^?K~o6V1W4q1zULi*PoDi|JldyAMO;w?>{pf5bXEAJt)HLd!+r2@%ukL8?caf z5x?5w6(yzSS!bR{%~RzSW#)I8`OO8`Z}9$ujrq+r1o;M$ts#3uHizsE*&eb#tJxs3 zLu89+_s9#ISSWO3z79c%9nt*fxX#>&+jz%Dz;AjQX3yx+W-QZ}4 z)${{th}CoiX$jI3q$x;OkhUOwK^lW}hNCq|Z#bHRbcdroNPjpQWHlY)Xc5vQq)AAZ zkTxNGLK=m13TYM6E2LRSw~%&OO}~(aSxv`~mRU{DkftGBL)wP)4QU+GIiz(+?~vvp z-9y@EHT^>xXf+)~T4*&rM4E_n5osgRN2HNRCy`bny+oRcbQ5VO(odwJR?|_WrB>5Z zq^VZZRiv#*Uy;Tlokd!U^cHC@(p{vzNPm$ATTO?N7F$h^ktSPBmytFjeMTCMbQ)-DV@{%eY?zBUH7p6`TTcudiDF_T~hY^ zO!>=&*l&2aJ@(-}THBBMeTjPSC%>tNnz6cZ&jt1M>pp#U+K@V15^B!potKU&r_Fb% zN2ODmO;=Q%boIPtzV{v07f!4<7rS@qOZ(qc-K|ynhpp>WPko{8E%U0r>I{9^GfVwg z9H*}oq?`WCbops^thpK=D_3t$G}r9^eyx4j{M_FszjU;jfiK$R`te>h*xaMd-c#zv zwv&2jX|1|7Tq?J(ddx_tM}Ge@!T4Gd#Q%PTk=+pzP&;Twy*wy^Yr|Dg$rv4+dtKf2 z#DES-{ziqo5wAldKT@Fw-jy)(wi?s3Lx*=AG!Z8%^w?wD&A9#BC9hD=-asd+HX%B)Qtlyz&~GwY+;r97wBl=}vBWm=P}>^`G6MAxVdt%r2g@Jh9@ zeuv)FnWZ*YSLjz-5><6^fqr%OST!oZ{saa&c)jya@ZWrY=fCZ~4gQBe`&a*(-~ZuP zB7Xm|g8@N){|5~++P#On&qzLH!k^#I%l68XbL_LwJ_Yv4^~zlP&IPzn@cxJO`Rx@4 z`WlcGB1=Tph%6FWC9+JXT_>_oWTnVbk+mX=b=uV;%SG0UEEriavSeh<$fA)|Bg;nC zjVv5lIkI$Q?a1PtcJ;{eop$|50gwtHB|vI`6alFMQU;_BNFk6)Af-TRfvy<5Pz}zO zgQFfuK{zUclmw{>QWT^rPFohFE>2q*j>;gVL282(2dNHH9*+7T1>&d>QX-BTAw}Y- z5>h6PIw6JPsFc%|3aJ%RETmdUxsZAx1>>j~QZkO3Aw}b;8d5fnx;bs(kjf#YLu%)= z#p9@+)0U5;eok9JjtU|rL~4i>5vd|lMx>5NA(2WVr9^7!w8ccK>9pnKsHf8wl%t|Z zNjYkY6qTc@NLe}RiWC;9EK*vewn%Z2>N;(Ck@`AqfsqP3ZHbW@BSq$@GE!!aIwOVV zs5DY)j#?wd=BT#QmK&+J(-s`5xYL##sX0<~r0Pi7k-8&=$NyL5!|X4CS@xGfV)kQ6 RGn0}Nvr|%%Qj(Ix{s3RXO|k$0 diff --git a/telegramer/include/pytz/zoneinfo/America/Kralendijk b/telegramer/include/pytz/zoneinfo/America/Kralendijk deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-d$EO?$HA-PJ4g|NsAIWMXFi|Nnpt0|S_3`Tzf%4kO3^ z|Hlt7@c8(KFgOFTQ*a1_u7L?d2nqH8jsBn6E}a6RLH2=Zpq)VL8R~%?Qtaij0orb7 G!UX`MNklgQ diff --git a/telegramer/include/pytz/zoneinfo/America/Lima b/telegramer/include/pytz/zoneinfo/America/Lima deleted file mode 100644 index 3c6529b7567f032882863fcd48b790e0988261bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmWHE%1kq2zzYO{vMfN%(*PuTN*t~nKJOK9#PzYmQGKrq$K-Vn9Ou`$aQr|20Xc~y z0doDZ4)Q<0Ur;ERa6on5;|pq{H3xK6nFDlZ&vB^#|NlQDGZPAA{{Mgb5e5bzxducY zKfuWH|NqVn3>-ebAq=_(CO~Y;5JG|@fp+}QY**d`qCt)Y(I5wdXpo~pG|1r~8svBo z4e|h(26_VMM+T5bz%)C&^T@pSt6t2f|j^+Z|8g7_Ntdn&ok%woVoAs_m?@lATPo5 zkEetEg~RiiJ=}Yg*-zDbDdz3{A!447GFxB2F5j&SGj;v0Ev=hBKppiK&pB4MQ%-ol zl9RQfPBpwMKkxWZ8fwQePZxx8$@x>9jOTGt$qtA!uSwYl%e zAL*~kpJSer>w`9kPN?7Yq zbNA_95?Qi-YBU}E>olfk7=n79q&BtHKYolw+Yh9np3p&1<| zF(OM3OK6ti0ZBS3yn{+Q8>UCxI;%z=x~)f@{8o+L6>9F^|ABg-;-(q%#(MQ&;VCn= ze20unuQB5nz9bU{8#8gj5}A0lUMI)AsFLgV>eS%HDs|6hJ*j1?n*8P-Gv(+aNn5?q zO#Nhvq|aGlrfrIq>7%pFj1nao;iF9E%vQ;~-P>d({v=svM(SC8uBcgGhwE%_y_&t< zs~>LItLBt9>PKoetDJ=g_1vmeYF=7{nZI_UEQqN!kLItCg~8iQZgRHdwv?Ovh*6S% zIL{OW^p=91DP~cVPafNps}~;$S4&Eg_2boERhSj2msT{YWy3n_<%I`TQAmp}PT#JI zeSxMVsa8rF&YP8?+hk?UVY8~OT%N3|HcuVPlhvh_=IMPYQkqj_)@+HAc7FD4@9*IH z-+6t$$^jma&-a%2`TKkoWu8v%-o<^@l(bCGvj%7D}XDFjjpr!56i3#1rGHIQ;3^*{=OR0JsrQWK;oNL7%sAay|sgH#47 z4N@DYEe=v0r!5asAEZD?g^&^e}- zkxC+^L~4l?6R9RrPNbelL7lduNJ){JB1J{2ij)+9Jh8s*9A@Y3qv= z*l8<_lo+WoQe>pcNSTp3BZWpPjg%UxHBxM(+DN&PdLspQ+KMA3M{14~9jQ7}cBJk| z;gQNCrAKOy6d$QRQhukcKe7N$y8_4(IPDrBi-4>GvJA*NAPa%41hN#!S|E#otOl|i zPP-n+f;jDpAWP!3Yl18avMR{3AnSrG46-uF(jaStEDo|d$nqfTgDjBKt`M?BPP<0P zB023UAVwUxvTJ(v;m$ubN&=(eTmKJJj-=H&#qc1x@xN1P!yEL z@}ch6c?0B|!k+HAltgLr=Q!UE^qKFHea`n?9CO}&`}v<6%@0kD&V>`Truk5;$)$#j7a?`$Ly8ZTNOotce=!8|X&CO-^>syNarsI@8 zx>H_?={&fd?vm2hB=$;CUE4I9ZY|oXZa>CJ_l9PbbnJrkIB-{lTVhXQI=e5uU#r>oh`1rkb5P@zw9q^R{3RkXt=bDKU_^D2gz`A0A7M@o{+ zqt$hKL4MfCnmzilY;6`UsMN)s4w^;d%Jib}St%J*p-SoxNNHlJDy?}<%6=_SPgE_J zCr{?6CFLP`YOha~7v{;*7aY|h>aS1B=l;2%H=J*L|6iRWk?IYO6N&75 zD=In?d2^%F#yhw3o*j5P9V3yC_D3a-2?U)HBkeuW-VfS)e9+0Y=Un@aE6RI9@L&A% zBlaq68QzE#0I2{{0;C2=5s)e%W$?6hKnj6W!qb)lsfDL422u^A97sKof*=(^N`lk` zDGE{*q%25XkisC9K}v(v1}P3w9Zy>xq&`T2kP0CsLTZE*38@lNCiLpWfkJWFN&%%p zYK0UFsTNW$q+UqDJZ;60l6l&iAw@%~hLjDd8&Wu=a!Bcr+9Ab5s)v*hsUK26q=HBZ zks2aJ^t4q(%81kvDI`)!q?AZ4kzyj%M9PWO6DcTCQKY1vwx&o?J#AHyvLbaw3X4=0 zDJ@c4q_{|Rk@6z-MGA~m7%4GQW2DHQw#rDEkvb!VMkY^2&qxsiG!1xG55 zl-$$S94We|tvXV6Pg{4S@JQv6(j&D;ijPzuDL+zwWC4&BK$gJMt^u+Lo^}EQ+UH6=YdF?YbZf<7rn0SsG+*ki|h( z2U#9ueUJr0RtQ-lWQ~wT^0cdjER&~QCuE^K?Mfj_g{&2_SjcK2%Z02L{=Z=S3M^Qh a9ky&cY^j0%X<2C*se#PQw9K@OxIY1Es=sdl diff --git a/telegramer/include/pytz/zoneinfo/America/Lower_Princes b/telegramer/include/pytz/zoneinfo/America/Lower_Princes deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dMAEcXI#!@YUl`1s6J(E9ahH@6k$)NLP3FD2>+@`>+$ zm3;1&N4a^CjvUJLV@YP3?(Ix+Ok_VBWo|Dg?y|cw9}kJw9c{l=O^Em7N%`URtKvw9 zET&FG$#Z3w_KWKCsak$*E-T;pmh>;&s%F(=i_nE?S(}lqwPz|Y+F%DB=0scg#csP! z3Awr`yL`hc{C;Xj5*s3VSCG+{LD74r?bxhWt!S;?t}TQ%4#*bB9>^w+W*1}|N3##I5wa7q6|xtynWNba*$&wcSIoba RxuDDa$A+GmS+XzW`2pmRDPI5p diff --git a/telegramer/include/pytz/zoneinfo/America/Managua b/telegramer/include/pytz/zoneinfo/America/Managua deleted file mode 100644 index e0242bff6e5df45de8b235abb4a8405fbb8ba6a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 430 zcmWHE%1kq2zzYO`vTQ&s(f}m(>Uz9jb1q5XTw)g>82ICX;FbIUVZ&Pwgtr6+XeLfn z&^qWHpzVD3fziqL0ZtJI9yr@)2DpUeHMq)0CNMHFGqC_6GnD@Sf4vL?1CW$pVEO-l z^#TTt|NnPxVBi6fi~fKr5DoGdhz9u$M1%YXqCtKH(I9_DJvrI;s8ae`rgL{9Z;bKW*wZtn zQg$3;9&X)kd#|0u%4*=GMaEuPC-}!w`!T~>WO1ZjjVwpjBLyOD1*Alzt$`Flsvu>M zI!Ga;5>g7Og%pdl)sS+LwjNRtsmOn%BvKP8id03)B6X3%NM+31cg1#gy+2C(bGG+< G*8c@X3!5-j=;1ext3M?f^lLZH12AWK0s$rf_~U0}im01JaW A1poj5 diff --git a/telegramer/include/pytz/zoneinfo/America/Matamoros b/telegramer/include/pytz/zoneinfo/America/Matamoros deleted file mode 100644 index 047968dfff4dba196d5c9695e79cf28395d9a0aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1390 zcmc)J%}Z2K9Eb5UB`A^wfe<7L6}V`coH5Nz%W`Va98DcxQZp-c)L!P4Iyh!_AkkI1 z2uvs>MB9W|o56@AtTs_d5F#yvk|YA@1VuvP?L7An2wFtH%jL7U%lmu-HLcAa@yDw& zU)P&=!+7+4!?ucVVtd8r#()l`>MsBvM(|tHCtqQUdz3)Ng`|OXPvdQCieY$ zrL%v;sQu9yoin+ra-ZDP2L>0^!8=mtg`TNHeK+Lcl2Mi47?R%Ied>s}R36>btb9At zrElfBD2Pdv1@ld!aPg-+cK5a_nk$z6o`@=rrpc1JT6KJAvn`JJwQDa47jsID3NC~6mmbzR>JadNs_*Eil5r$*{@gMUVy9*D>@ zyT;VnV6ALS=vPg?T-mhTqJpU@GWe!YwZ=MR>x5IaEiKA(Lj~getfeo6cZ!RXA9ScP zLCC@9`clSMaXB=u+Y{f4j*$ zvo*1C1ZQia;|LFl4+#*75D5{95eX8B5(yKD6A9GTM2duJYhvXH*49MJ5iSxhN5DwL z93dkya|De3vHPIvC+nV^10U#sb7y>c|jzJ)!;1~un4vv8! zBjFf|tr-h47+W(MWH`2FJjj5M5g|iD#)J$C85RDwVa1z;WtxRK{pl``E6wS5yIpQq GTKsS6)nhdP diff --git a/telegramer/include/pytz/zoneinfo/America/Mazatlan b/telegramer/include/pytz/zoneinfo/America/Mazatlan deleted file mode 100644 index e4a785743d75f939c3e4798ebe7c79d38e4cfd08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1526 zcmd6mduUH#9EZPSF0IvGatV=GiPmn+W@hYSGxpoYew!J#-)4R@cC}d>w!LL3a(Smj z%}^pNZHZYf3JBG^py1dz*TL~{c-Tq5G-J=HwpM0wF^t9X<6sgo<#my?bdYWFG>{UxPjOd63 zYt+*4UL84Mk&2r2O``h!)v}*&BzkzVS>E+RVmkfIiaTu*d;GIm+1ezlDxR6R=2jh_ z-f0qw8gydF39~veUnd3BnB>4XojlT_Qhev@l%8s}_QN>6t~*PmKAk77(~&Ceny;kS z%}^OfKFa!n2(zK|p=8EQH}2G{k~R0c$qqUq8|AIZnR;30^xZR?h7ar9`{&H&*QI*P zmAh)|%`}~N=&Z^=8LB-cS`{?9q%fsWZOaRhqJ@cSd+a3fPWG7M*gY$RCTX+YGqYXQhSW*z zkE5zCAXn;oD^&fL7}_4H_f(f#T$yf3uy2NaU5RdS|AjP`J;Z^?0}%)!5kw@1woDMA z;BTprvr_X!E{I?d$snRZWP=C?kq#oBLt8$GfD8#CA~Ixz2+5EVA|^vlh@cQjA)-QL zg$N6g79y@gTV9C34sD4cB12?`2+fchA~r*Ah~Ny#A)+&6hX~J*9wI(Nen*jOZlqmokZVbBSa4{N MD=aiPA~@9V7s`x}c>n+a diff --git a/telegramer/include/pytz/zoneinfo/America/Mendoza b/telegramer/include/pytz/zoneinfo/America/Mendoza deleted file mode 100644 index f9e677f171b3900dfd978eb8ea2aa226557d2c5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1076 zcmc)I&r8#B9LMpmy44a71A~4LA*7g#;KSTnP{~I;7||{^3Osd)A_$5O8T4ay=@blN z1cBnpYtW~_61DcJWh)^W%!r`FqUcsctYKDudOtsZK-8&y`Rwr>8{-SFXJ+{L(RlF3 zSD$>lO3vC#@^l`ksiYk2mbM@Cnt3M|I=lxLIl+uuJ8t`d%N=%hes`M=5E4&K^*|ZV%~| z(WG)$6MFT;J`?FYS=f@FG@a3Wp)2*(bbUEf=-!af-7iaK>+&(%WA>U@?Va9s<4{Mc z@yzTj#*8-YrvG4{9UNI>2H$2ovd+1t=9a&_!XYmI<50LI+$^8M&K1uGfpeuw5OS_; zgNSplDot(9y{|Pzxewa*r!&ETob4(hr&mrq$o#`H@{sQI=so#==U50?30VqR%PWf^ zt9fNPWIbd-WJP32WKCpIWL0EYWL;!oWM!``jjZjJ#gWyK<-M{#Qh-+~KuSPrK#K56 z6-XIK9bPE}sl+R#AhjUHAk`q{AoZXxh#RR$f)6MOsR=0xsR}8}D|I1-d8IO>G^949 hIHWqHJfuFPK%_#XM6c9{S$VYoQ)KIIX|u1l^*1v}?Na~% diff --git a/telegramer/include/pytz/zoneinfo/America/Menominee b/telegramer/include/pytz/zoneinfo/America/Menominee deleted file mode 100644 index 314613866de53e1457f6cbf2fb617be7e4955edf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2274 zcmd_qT};(=9LMn=X7P|bWKxI(r6ofSN5IgdNm0TWs{+K2BHZC~97JbK>;S7m=kg6?_cw0ylItWQk4A}8u%&2Y=;PRqEcQm!u$bo|>Ki zjm(KoQ@$y`7~jRPntS~#Gw;i@>b}$OnE40Cbm50DnEN|N^aHPoDQY^VA8g+#4^_Ob z7c?|U@yvEzQoKY;F2AozbJm;E;k~LXzQUCCZ&Br=dFJ8QUe*gwEfW9MR=wy*u2j_4 z=*7Dy%98nodTGlQS(cWe1ItfIU@S^k77R$`&*`cv{iLb-LaFNT5wo)Qys8=5V`|$! zSC0&CHme#As7JeN&13#gb=~$>S)J3X>w`7Y5Z|HKloraG(X~35nIXY%%XMRPlr;8v z^ty`|dE5~vp$rqo;rBWG*!kc>HN$*UGSTFrs++yA$dqOS8O-U;ZYs(^-5^u zxZae~A)5vd>()Ql%I5Aaz2*CI+1l2kpB?Z>+o~1%xjhLoF3SFH81J}|@Bj7}iS(UO zDiS$*C~ABp^7eieF@s+`U7e~v3-`=XN7$h2ULmu=Azsi0{>!05qotT z%j%IiA(KL8g-i>X7cw!Yof$GUWNyghkl7*ASt8R!=7~%c znJLFqk-2hA7MU%_bdmXTOc?BnZh6k|L+g5l51oHcK37a@ss`BnrtCk}4!uNV1S@ zA?ZT$g(M8g7?Lul%^8w3r_CBi+MG6T9En3R$B{ZDcO1z>vd57=B!5T(kqja!L~@8E z(P^`Yq|s^fh$PZ!Gl`@U$t6cJk!*6L6Uiq>LXnJeq!h_1M^c?Ot4La%Hm^uxoi?*b pYLVO`$wjh@q!-CA{tpryXA3>smfGW=<<0lzdi;5L-aKz^++RkMdw>7{ diff --git a/telegramer/include/pytz/zoneinfo/America/Merida b/telegramer/include/pytz/zoneinfo/America/Merida deleted file mode 100644 index ea852da33a81478433a389e2b4da03b425d9f764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1422 zcmd7ROGs2<7>Ds!GcpT|LWwRQ5r|-;j+e~Jv{46hCT~-km6y`7Ny|?yvGVePA_!%< z2qFoh=tiNnNHSYUkc&uyLMovMiileD6-ZLnbe?G+v&ue7Mo3ASGj#FF{ON{$WjBLFzZSq?eNWt-Erm!kn7uAoL;>`Kl;~FsA zqCV>FvF)ZrCUj}USylRaNbi_#Q)OfQdgo-h+I4MEcHhoW<-Mn*;`Az2>1&dzj&!pp z*CTt&qfB*7iqxbB8}H&GUAywFsr{0q_x%_*z9*4-|I4eU?$(U1AAg_@^iS!7m#?UX z#(P@z^s2_P7t-XdQ_X3kawx~G4lg?=Epa~6@~u-^!}Cn*RFxe0w8k90>yTrQ!%f@8 z3f(^V*>rSe>dwBWCMY;)&Odnk{_C?M*7v-G`28a{C6sT_!r#BR+#YchdnDU>9a$dn z{6!3Y>j;8$hyn=%iGw2$BodBLkXSf^L89RZC!iG%M?gqKNJvObNKi;rNLWZ*NMJ~0 zNN7lGNN`AWNO%FQ_&5R#Xhp~oA`&Ar^ZjQi_$T>nsV&@1RiJl{TBz|N70j&`rLqNuW3<4PiG7MxK$Uu;h zAVWdM!Z8?RG#tZ0#=|k7fYyjOh7`~m6UU&CQE?0l85c4zWMuf?3@yxBU5d51Sa*^w Q!4@CuPKdW9+v3B10g}OV)Bpeg diff --git a/telegramer/include/pytz/zoneinfo/America/Metlakatla b/telegramer/include/pytz/zoneinfo/America/Metlakatla deleted file mode 100644 index 1e94be3d552ea0d6e29824469866c2a51a187be9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1423 zcmb``Pe>F|0LSs!b#+}`Ybwn@2YD1|n#pMR?-Y_`Vv^#zshMSIV*mV$Xr(C_<{=zC zgdMU6VbsAKI@uwMqEjg5A&NjMh%P1_Iuw!9`;K`k^wJFPeTLoZ_ZO&cI-RNg^?21U z9G+5j_)hFo*UO;*ZjJ>D^i5C?lL`f$uhJo!VJsz zNw2YFdav)7K7G#gO+FD3UoA88{gm{-ozt`qru5DC9|-;Ch`wdW6kEI7^=(aU zBD$(Vk0}a>?RhzRY)ZC>+fi=D`{G1`JIf4aeU!mZ2aU=^TU5SCHL6xVi0TQyadh#y zs3|xgYX+8$V>1(?wtn6?K0YW;lq^|wgLmY~J#*Hn#;dYE%CZ^?8)d_nQL8bzNH)%P zSWVvDvS~7Co%tLk&)#S^&dsNa^PLsO1aa@Um#ljyKwx=S6$WPov}TL(yT! zTAjIbvUBl=)fH#Su96z(9T1iGmdPcet@f%E;ZIl22 diff --git a/telegramer/include/pytz/zoneinfo/America/Mexico_City b/telegramer/include/pytz/zoneinfo/America/Mexico_City deleted file mode 100644 index e7fb6f2953d123efbc2550f450decd794b017d4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1584 zcmd6m*-Mo{07pkNDie $G*m5W&p7va(E5OmA-2B`dv}Ub9R`EwQp3Pz0e&AEHHs zDB38p9x}}yBv3FC!-&dAE)*g4&=g40_BH4G1A?H3e#6Z9;CnsCSy17Q5Pw{W`8(U( z#aDW#WY3o>Pp>=Ibw6R;)#n%E>igNMhbHg1hR;T2jO2XwjC^>LG3Gt&8M~jGF>yY? z^SZwIX?S_Ph^U(@Ba`z*RMrO>9TY2KBA&>#bC!#B%ck_Y_rJvY?~irtw2#`*`$%u> z`JpxqwClLjlWKF9)LW_tReZ}Sxi$TgN+_+CiECQaw!}=C4<8=*&v#s;CvkC*pKTic{<@4AiB; zF`_KaTe}x#sqz)?borMkweRP!t{7jU_P@EV58RzmmBT~wVCRIYx_V7k*F03RwN2I( zyb`tU1F|l5KpaYP$iqv!Mg5uzUH`2~H2A0LhKXWv^g)6+Hs-GzFBhrfw?FF> z&GD+K{h9Xi=FBq3$A0UE} z^9=7H?lb&{G{EQp(gLIhNE47QAZ#HMKl(hH;+NH>sn82vyRVsr#)iP00J zDMnY2witau8iRCZ)3gTZ4bmK>J4kzw{vZuPI)t|Fvgd6L*Y>JH!zc8Xg)J;s_55jSda-{SB~`;=}*| diff --git a/telegramer/include/pytz/zoneinfo/America/Miquelon b/telegramer/include/pytz/zoneinfo/America/Miquelon deleted file mode 100644 index b924b7100438cfacf25c56c494fd1abf3f262100..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1666 zcmd7S-Afcv0EY3=#6pS!2_iwPOtIY6-Obv{wqs>dy6s6a}cD!cYTZyKprji>#{ki0UcjvL-KH)O-!d+LRxn_GOZc#14zdooKn^(<8C7 zGg0q)HKgjAEM4FGKs6M9(7R7}sXbXw_1=b7wQu?jy+2f;_K!c2P5u_qG;&=w&nOqo z{m10Np8?U*9g(eXlSEs4zC83WTC`WXWXFY3(P7(mRP=;@a!1?ti&D$7?Pto0vF#yk zO|tEe@1rIc1jANNQP^^2`po0C!hbO^^Zzy@V~z|B85=S2qlp3u!_mZn1cF3@go4C^1cO9_goDI`1cXF{gyd*q zLV|KMQ6XVDnz)d_kjRkGkl2vmkm!)`kob@QkqD6xkrs+EgYc`M+FoUJe9`4TaW^3-qe;(c+uR< zKlx`kt4XdkG|ZtSVgj1Zc?2cR#G?qBfr6TuT)Ca^)648d?|RX>JJ09Yv-|q@O`4PW zcDU_tr@Q&W<(xK`cmGx9v8sK)C@NbccO1MV$|pvt@_j??hXMoCp$*;a6&)^hc=2_6 z<;4bdWaba{@5?LI(TK0?RogyO)qOs;*W^4<$9zuNkH6Zkj<=TBPxMPyC+bgDo-DX3 zYlq&;sgWU!Q7#AR}rF+VzVJ^`eJ4j#e?XPYGd+1;KT~jx^H|m@B zYt*mJ)w-qrOVwIatZyBz7Pm{PWZRBn(Y|h%ytA@U+|4eK_vS4Wza?hL`_T#F_mT1P z!LUsAN3SsX&}*7{_+YB;xDl#c=R@?P?Uf3*Wgn5gzqNdZQCfl+m?za^%B|R!UpA2nJRl8$XC5~=V{+9v(;0Dvvlue<5i!` zak_76pz@0wp!-F2s;7rN(*8pRi2h!eb%4hs5zy|E1Fl>afoB@zGe?eyfqN_Epq-zK z!FhLd(1&ZpkmR#^Xx2{kY~+6Z+|-ZM^M0S|;1LT{aObyjxOc1?-mqDYxH(+CP?6>g zsT<)4EsA!AmG*FqToL3Pwf>f4bjBoC_#)+qh#lk_GvgaaWUzss+uv6t&W)C0<9#uG zVvwAXcV5g4^pr`-2SrjxlT3-+E>fC2^z4CUYIaqlPVL;P=6q4DUvF@#xvPtHT1C1_ zpI@lw71@nDx1?HSC%5UGb;atv$XdN9yHG9m+oy9A7pmOOGcrG_K;$=+%BB8U zVrj)jx$Hr_c)w_gTz)=GI9H^~56XPSiu4I`<>ouW!?Vj{eqFmg=Hui3_+R_xb{~zg z+1%~}du(0Z?sLk+%k4f^Y3pIV`&!Sw@d-(`DKnF7lVi-qS>}o)Ga}}A{Pj%w7xUvb zCw*Y+cgPGOQ-sVBGD((ZmXK+(H1mW^6f#rDR3USPOcpX*$aEp|g-jSSW5|?QnmI!z z&C<*oGHuAbS(=GMW)7J;WbTm3LuL<|K4ktZ%>*Jdh)f|ehsY!%vxrP1GLOhaA~T6h zB{G-DWFoVPOeZp*$b=#@YH6kvnNws^ky%Bi6`5CLVv(6erWTo7WO9+&MWz>-Uu1%j z8Ahhq(#$b3$;d1t(~Qs zN9G-wcx2|0sYm7>nS5mSk?BX~A4vd`0VD;MCI?6okSri+K=Obj0?7oD3M3auGLUQ_ z=|J* zB_vHqo{&T#nL<*9|JBPq5tIYyFfX|jx@+0x`0Ni>pa qB-Kc+kz^y;#{bj(kLmU{O&??09+D6d79KV#Bw_UEu+d?oy#EAS!oC#% diff --git a/telegramer/include/pytz/zoneinfo/America/Monterrey b/telegramer/include/pytz/zoneinfo/America/Monterrey deleted file mode 100644 index a8928c8dc94aaebfaf9cdeb93011d41b482a4cc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1390 zcmc)J$xBsX7{~EP6EX{g!WIo67KmWi+04o`&5P;H)N7iRL+Q1vmakf3h7Aj+E$5g304X2Qb{cQppqg}B{}|~S~+`(q%3}KQr`WNRo@?& z)af9-dib7MGxS5R9qlt|$6o1m1InZ~j_Hh^qiTKrIh|SEsIp>u^oFbgl^yES?&S%} zJ<}~YL18NAd5vs*^Ht^Ea!cOC0^>OuC7Z5IoBX!9viZm}Q&1JD3mZmEQN|qY%^NVq z;UD#u=nhjN6S_3)lq!8aq_qWR)EFw89*`ohgSNg_`#B z6}n?^%5-*T=&rseCLr*iJeGC#i-=`iJtIMuHF7;5IM3si%p&{E@{0E_e!gWN0H6W_ z4hR$s93UttIABn4Z~&no;eg`Q#=-%Hf`$Tzf`ToKqVe2RsTs4uBMd91tlOIY3fSQeaYWQh-vBQlL_>QouU3(Nf@2@KOL%5OYAL zVCDc#LCpc1f|~<41vv+F3U&&3r#5;Dd4V5w4Kg(e2u8b?&Y4}W#(y^?;omZsLQkdcx}+% zCg^A0ktJ{P&mL=PYgL2(^B>7MUDSBM{@DPHU3E@3^c_QY&BBxAak z*>NY+W!&?pr`=sG-uRj-5kI)olQ<()B!-*psY79rRNG)DKN%1yD=X}jg9~Na;Y4prF^IFbLK5l21*URjo0#8m>lE`s3+Vc}~<$Sr!Uhtw{qqbhykZYg-j9@5yy4nrr=)t%*)xZ=t`c>4UR& z%N&1j`;_3if>?ib@O^N7`j9^)oM7l%uRpAma!rVt_}hC@?4;N@eUG2+aroeG?pUSn z1uV;)F-mnuEw@tld#ou+J#t*BoR&VHJ$6k~>eUO^Or_oqyFBCXm4T2I(9c4#L_hiY zf>S?v<}FXZE!J{Ga7c7Wcu0ImfJlT$ zh)9e`kVuqBm`I#P9Vikh5~@+hiUe!a(IVj*b-YNxMjbH{GMbp#A3<~JsDZGJI&LIz zqmCR29f{qjgGZt_>hO{HkpUnhK!#w{V?YLhi~<=3G7e-Q$ViNOD9BihdN9anjCwf8 zc#r`hBSMCRj0qVOGAg4U7BViQ9vCt*WN66Pkij9NLxzWp4;dgbLS%@@7?D9DqcrMa TVp#u2|Lr&v3iM=GX`rV^4u^l-@waFg%5>%F-Ky$v zfxf-JOx(#oA@%A4QJuL<-d&I_?xkeO`xEDh2eE1LVV|+$QAmP(+;Oh@%O_Gk@f@R` zJRYrUuJ=|?&qnBHM_VfA9)IoH=u)<9r*>O%S=E}WbZzMr?&6uOGfWAOs7tbL=mFu` zx7UxyZiaWYj%{~=z z__*%p@lR)ZkT1<&e`+!k(Tihwg4GV#nF#uq<~mJTgR%m{TD}`uobb z_@g4O=AIlCo+n0K^U!-;n(IH|=Rf2Q`_zK6bkuu5So=Do-N=~adC6cou^z>uZ>YY@7 zJtMzNrNle6%q&pvhATZYC0ot%JD_LB&Qr6Umt< zeE)2uNY8M{`FmQ4j0rJv!Iw5s%k6poYP&zrum4lOb-4;w*laG>kzzM@m#c7_&C~ks zZGAQzVvn;8=x^SU=6%b&!{W?%*=%msN8EFap36KlZ>LovI;YY?F2>=oSBm_tdkRTvYK*E5;!O{c* zi3Ab~Bo;_8kZ2&`K;nS}1c?X|5+o)_P>`q~VL{@81jf=t1_=!k8zeYrMTakhhsVSR z2oMq>Bt%GzkRTyZLc)Z^2?-PuDN7S7BvweUkZ2*{LgIx442c*LG9+e5(2%GhVMF4E z1P+ND5;{v0J0y5W^pNl&@k0WLL=Xuf5}O(dL1 zJduDR5k*3Z#1siC5>+IuNL-P?B9TQxYiVMO1Q&@e5?&;}NPv+DBOyj&j072pG7@Ga z&PbpwO{9@fTbfuS!L~HfM#7E68wofPaU|qO%#olYQAfg##2pE|rHMQedP@^~B>0vl z`bhYZ_#+1Zas(iU0CEf<2LW;vAcp~R93Te*awH&!f~7eYkb}X}91Y0fU}=sAM-##_e!|ATe{f01&0NPcCmNu8r(HF)a!2+Ad$WR diff --git a/telegramer/include/pytz/zoneinfo/America/Montserrat b/telegramer/include/pytz/zoneinfo/America/Montserrat deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dX`rV^4u^l-@waFg%5>%F-Ky$v zfxf-JOx(#oA@%A4QJuL<-d&I_?xkeO`xEDh2eE1LVV|+$QAmP(+;Oh@%O_Gk@f@R` zJRYrUuJ=|?&qnBHM_VfA9)IoH=u)<9r*>O%S=E}WbZzMr?&6uOGfWAOs7tbL=mFu` zx7UxyZiaWYj%{~=z z__*%p@lR)ZkT1<&e`+!k(Tihwg4GV#nF#uq<~mJTgR%m{TD}`uobb z_@g4O=AIlCo+n0K^U!-;n(IH|=Rf2Q`_zK6bkuu5So=Do-N=~adC6cou^z>uZ>YY@7 zJtMzNrNle6%q&pvhATZYC0ot%JD_LB&Qr6Umt< zeE)2uNY8M{`FmQ4j0rJv!Iw5s%k6poYP&zrum4lOb-4;w*laG>kzzM@m#c7_&C~ks zZGAQzVvn;8=x^SU=6%b&!{W?%*=%msN8EFap36KlZ>LovI;YY?F2>=oSBm_tdkRTvYK*E5;!O{c* zi3Ab~Bo;_8kZ2&`K;nS}1c?X|5+o)_P>`q~VL{@81jf=t1_=!k8zeYrMTakhhsVSR z2oMq>Bt%GzkRTyZLc)Z^2?-PuDN7S7BvweUkZ2*{LgIx442c*LG9+e5(2%GhVMF4E z1P+ND5;{v0J0y5W^pNl&@k0WLL=Xuf5}O(dL1 zJduDR5k*3Z#1siC5>+IuNL-P?B9TQxYiVMO1Q&@e5?&;}NPv+DBOyj&j072pG7@Ga z&PbpwO{9@fTbfuS!L~HfM#7E68wofPaU|qO%#olYQAfg##2pE|rHMQedP@^~B>0vl z`bhYZ_#+1Zas(iU0CEf<2LW;vAcp~R93Te*awH&!f~7eYkb}X}91Y0fU}=sAM-##_e!|ATe{f01&0NPcCmNu8r(HF)a!2+Ad$WR diff --git a/telegramer/include/pytz/zoneinfo/America/New_York b/telegramer/include/pytz/zoneinfo/America/New_York deleted file mode 100644 index 2f75480e069b60b6c58a9137c7eebd4796f74226..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3536 zcmeI!Sx}W_9LMpa;)WucQm$lLAu8a83sO>PgnGmjT+r~zKnAt=mx@@5l_ud#S(Afp zgQ+NJDQ=jk;TkzM<%0WykET>6`Y0}>c}~ywz3nD0y6a`$^Et!dc=!AM;}TLQ^>F>; zscV13%X8Jfd~fl#{m5MvC`-5fp}tz+l4YO&q?RXNloj)S*LjoI$;$A2wQA%6lOK?+ z3VMEH3Opnbtdl%(plWh2bG+#$MfQ!leVGemFr@17u536ZLKXyRx;OQN?XeNpZyywcY2o*Ygn>_($mdA&IiTdbB#=7bOQy_ESH;Z{$eFTXIC* z*JU#8--7(j?+@S9SL)p`SMD6ue^iv2 ztH-zK%F-fpZD*OfUU)>z(js+Z(Pp_hcZsS>%aL0XW~tk;8FFX9ICVEHL8?2=)PMR% z%Do0-^}Xsb=KgQ}^yv}bEu!YeeWlHXO4au8RcW{TpbFgZvpl+N zgKD4dGLOCUiRuu4(R7?#s2>mCXPy}Rv3@dOl?m!RO$T}QO0aLd4lZ9Qov-xKT}rZ~ zYgwEM$xW5eO}$lE<`C)jNlVo|CB^i3rcvex`4m)4FfP zb<^+u4joZ?*z`Y>t0N1q$y3|k)=w`wBm=&fsH4(0$}{uls%K*t%X3LDtASzZGHBp) zYEV^yi4K{dqstbW7{6z9%%-VkaAik521wxg=IP|-eY7@k$yc~n>W&y=xG6a%=FkE*j6qh*H5C|M!1 zsuR?kx$ntaCnMGD%oLfkHBem`HU8;7i8vfMrso_7U>3{Iw{k_+_E!XApdVkne z%g5_2Uhit)d~fW0HXZ7Ya}643-;wqmZQtQ>cFSC@TFysY4K~ngpTs)mBV-GaJw!GU z*+pa4 zZ;{PKb{E-RN4vks20PjvMz$E)V`P(&T}HMU*=J;8OBau!btwef>G!yA2 z(oRR)Po$xawxdW(k)9$=MY@W#73nL|SfsN^Ymwd}%|*J4v=`|w(qKp1VWh=KkC7%L zT}IlB^ciV1(rKjCNUxD*Bi%;Y?P&XrG~Cg49BH|u?K#qPr0YoAk-j61M>>zR9_c;O ze5CtG`yFlnksH9#-T}xh;Armwa!WYcdjh#B9PM3!+!n}vf!r9#oq^mM$i0Ew9LU{)+#bmNf!rXD_6|XA5l4HE zAUBDly-SeW1i4R;8wI&jkXr@0SMdLv<=@{dzV?&}wR diff --git a/telegramer/include/pytz/zoneinfo/America/Nipigon b/telegramer/include/pytz/zoneinfo/America/Nipigon deleted file mode 100644 index f6a856e693420d6d989c45acff2c48b60db69186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2122 zcmdtie@xVM9LMo5L`4TAloTnELB$T?_+h7LRG6^q)DzOfluSPwcta@Oq1~Vib)lsH zxJJWT?OJKg+8mkLv>KCg=GMxT)%rF1)E0kK*vIG(7aP~-`T4iM`m3$)?elqjcmLc! z_j*Q~+csBu|9Gyn-*9;P?csgqPJ1Oz49F|*|EkVj4mW=KtdR>vf64_lrPJq($TiuA zl+QaTKC?$<=-ra}&1*XAR7|ct*{5f``HabaakI|Z+iI@sxnE!37BVx}mgwBD-`ub; zQ{U*%FnNW3byNC;nU$KMX8qxl**{LG{PVxb%_HYj!O_pxOP|ws5s+lYtsI)*QMl_1$ypqzm$$fRav4<0>|d7^4?`q zF_5P!V=+_J^_Q9#3Y$Auey@VXbIts!VYT4uEK@!ETU|Z=yIDB(p{ zu@19zqDU`$d9N)0s#Zm|x66w63RT0ph%^qSsRwGxW#tp&YE^!YM4Qg4=+vaF4!)&U z|2)I2DLA9ooW5k%P9N6IhepjqWBc^FzT@WM;SRmN<$&3cSfN$qUD?>*t~Qko$|JFe zYRT%B)=;@>ooJR=agK_8RwHd!O{%sd1+r!QOVvKO$aI{$q#upvo5zMmb!XGGk^RT@ z<3Vk@IuGcrxgVKr4Ly3>^bZoR9#rwM5$VqBR^7t~rRQ?9dNQ$Fp8C2*ZSRfA(-0^pPWibOD0dg?WMc-Om}B5k3_xtI(t^x^PoMA zQTvznycBmu|HTxN_UXE~s}`9AG7DrH$UKmVIPFaMA*O=N1(}S~&IXwdG9P3@$c&IF zA#*||h0F?>7BVknV#v&psUdSiCWp+EUypf3`Ge@S5%pI9LGJB_;J~DqK0Z0ar z6d*Z3l7M6ZNduAxBoRm^kW?VKaN1-b*>Ku)Ao+0GgdiC~Qi9|JNeYq`BrQl@ki;OF zK~jU{#%Ys-WXEaKgXG6)6NF?4NfDAGBuPk?kTfBALK1~!3P}}`Dl?TA^CIK1R@!9+7u!=M3RVP5lJJG gMKgp!e)-z};S{j)bs0vh+Mk*@@eShD0|-JD?=?ku03 zjJfW3&I6BP%iH zE0L7)rj;}`sglEAvXaNYR4G^5tkjYBRNC1WWcuJim2tF1F6@0yiC1iy*(60)N0!Vk z*(MfMMa!I|29cZfi(Kq{Smeb|xAHFCEAl6Qw(k2dT-<;DbE`llst1NoSP$+FRZI5o zw_L4PRN?lwWl_b)>Y;{xvUuTJYH9H%x%9^iq9lF5Dmi;fltyl}N{DtZDo@Tu>G|dnu{yzTt^OcI zR8L*7YK9`jW81WiFO*>>mq*9F~O|tRo z9@UgoDw{s(RL#y**?ice)?JE}Pw(vz&-hZT^cb0gz)n|3Y zOp4BmBUYz>rs~Q&BfG|bQ{Ayjc8{D_8?Nn<8wcN2o6dF0&AqRwp5q?byQWWV8OW0X zfztwlf^YVOggS5G<8T~naX9?`M%xkO_jl9St1wK$kB@gR*5VVStqhkWTnVbk+mB2Vv*Gv^>UH*A`3=V zj4T;hGqPx8)yT4ubt4N$R*ozkSv#_LWcA4Mk@XvO0gSo=NC}V{AVol`fRq8L15ya2 z5=beKS|G(hs)3ZlsOy0g#HcHRlmw{>QWT^rNLi4&Aca9HgOmoT4N@GWI!Jkt`XB`| z>IxwxLTZE*38@lNCZtYCp^!=;r9x_j6bq>qQZA#e7g8{zt{74>qplfJG^A=s*^s&+ zg+nTbln$vKQaq%3NcoJqen!N6c`+Gv&R`0dJCVwlie`!KZg6Ca^JXxlk|Mzo>rcnk(D9R M$}=-FGBZT@pJb_W#sB~S diff --git a/telegramer/include/pytz/zoneinfo/America/Noronha b/telegramer/include/pytz/zoneinfo/America/Noronha deleted file mode 100644 index f140726f2a6945357b325f0bb452dce7557bbff7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 zcmb8sJugF10Eh8=FG5N7hdF}`Ba&Jz4!x`?)^N-K1EQg&RpMHB=XHU!a!O~)?Eq0Om3afOv zS7!3d;$}q4o6@t)wqNROdR*jcjWWM`Aqu%IS&WCo>rPp}HB5>3qZ#?pDpX~u#@y~XjxpsIx89ihqSG`n8FvN-hwKSE@qZMt zA2n1#%J|wkNFk&WQp(rXLW=p?YDhVx9#Rmgh?GQXB1Ms^NLgQ77b)y(D diff --git a/telegramer/include/pytz/zoneinfo/America/North_Dakota/Beulah b/telegramer/include/pytz/zoneinfo/America/North_Dakota/Beulah deleted file mode 100644 index 246345dde7cada7b0de81cc23fabc66b60d51e79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2380 zcmd_qZ%oxy9LMo<5xsH)JDC^)LTO1LAfOnK6a_?(1mP7>2%|v6{_yh;B!+lOg7Khv zmWxp)L7~-}33GEeHqy-2R_4Oyv^Gjx|19dR++u;;&ig#=QLSfv?|%37yZ8C$?Ok8L zF-83C+~yx1uFE{UM=JBxb0|ZC>K)~ z%H*|=GQ{*yy7a#lDMWXKbahONfTyp?8C$|tVD$n$vv^9)%9_;fq%q}wAyo&-6DsKN zp!E1&SJ!>rr-LuPCqho_(Q}UXiR<6)mGb31;)c%WH`X%RF1m5%jn6|rL{b=(!NSUU2Cjz8z9gr4Vh;?WYdthG)j z?OUOe>)K?>w)tv#-bT5iWJ0CJ+%H$Ae4*}e$H}yaKSbL1!7_dFqR9C07kTHG!(!Ed zM`sQn5O?kUS>L^Xx5#SvRNvFKU)@_YptEasshnjm>fD?SD);x(GB2S;)U3 zTxYC_soZC<8RJHAQP#Y8)GX9`rk+vU_Bj#rz%`^I^^D_=Zu*6(|=X{mVX$R%Gx zX^3$4p7L!;{Z(vjIOy9J`kB~X)ZyDcJ+7M4`kkh+vua0JyR&0tP&H3%a(4FYQ_ox| za9Ua$)vlpk9Py4`?ylz6aZH z>GvNWaLwaggsm?0iozW8tT9iSd5XOv+x*Tpzd4uv2Jb&uo8MejVDJF4I%Ijs`j7=8 zD@2xPHETo`iL4S?CbCYn3+08Ca%QQ(S~(VrtQJ`=vR-7t$cn9I$;g_mX3@y1k!2(6 z=2$qga*m}VYv))zvU-l?BkSiV08#;t5+F4|ihxwXYRZ7r0VxDh38WNAEs$a$)j-OD z)PtiSNJThGg4D!nih@+dYRZDtg`+S?WsuS!wLyx5R0k;!QXiy1NQICRAvHpZgjC6D z%7oO3qfkhtI7)@oilbOawK&R!)C(yXQZb}tNX@LKXh_wprff*vtfp{C~W3& diff --git a/telegramer/include/pytz/zoneinfo/America/North_Dakota/Center b/telegramer/include/pytz/zoneinfo/America/North_Dakota/Center deleted file mode 100644 index 1fa0703778034551487b7b55d80d41ad220f2102..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2380 zcmd_qUrd#C9LMqBLG%EDJ((235lRa}0Rh8+q%0r?c?9*yA0Y~Z5X<=a2NX5>l?>*E z`D`x2{t!5FwK8OGhI1p#YOQoGY|hq3YRego^{l*bf#>P>{@r%vMOS^#p7VOnx&OQa z>ziK46#qJ}{)dO_)(`JKrN4TQwN3}*9O&s zb*{B=(^>IA))ni)l21iq^cgE@ku8#fKikO>TSfBtX?xK`Kr9}4Q>9$kAX9sHsI=n+ za!GrYN8&({e+u^(Cmlx{ER} z?NQ}f!?OI>cu^VugH!pH5LLkw&ia9?qI$f?spblzG zhSUM|d}Fn&kLpwnMR~Gea-(WYN|24`N>r2ABb$z?kuP=^i;!|ks;M~bEDkVdrg?hDTwhZy;&x}w5ueb4;HS<6FxNd)#5U%@ipC{CH-?K%Sd5<*T zmg}B8=9$a8h*{|m2!DZo*6OE1KV<=tuYc$3-<-#ML*O>n=IW~rjXZ{|4p|T>M|g8Knj6W0x1Pj3#1rGHIQ;3 z_24K7QW1`lAT=@Sq99c<>arkp;V2AJ8Kg8wZII$1)j`U`Q6Hp092G)J#8D%pNE}r% z>N0WE2`LmurI1n~wL*%8R0}B=QZJ-nNX3wnAvH7VqH$EssLRGtH=`~bN9B;xA+LMalH0m;P)X}I5$x%t9lpM80ipfz;q?{b}L<)*j6e%fE zQ>3U!RgJo=NL`J(ut;T%y0l1bk>YYx7b!1CeUSolR2V5SM~#spb5z-=%Z$|7s0)o$ v+Nev7)EX%^Qf;K%NWGDQ>3JbO85tC(BY#_DMO_Wi(IYdh`DcF8G5y!9*q(Fx_NMvrj^|J41?wES zaPz0)&h#ttuEKZ3qVTgaF2NFUQ(sx}zHK6Y^o*5oEg%*Ty>2C5P&%pWMJxGaiC)rD zXQdokrc>)$W!m<+dTIVfxvXSdr^h`km#2N9@9`$cjHo|E#`mEzbKnQtd}xeMzsnxLW!5H0#x;QdCi8g)Z)xt=6o_)+LSC)!OJ-9awi!1tvYZ zG<`soej6joV?MXbKNMp9)G2#I?^RJT+G$s|ejpwl+GSVO921XqRoajH-`Cac&FYDy zUj1ZUg{lc_)3y28s&;IXu8WIRb>|9oz1O4ak0L?ITpLgsnO^d}dC$88H zrC~xHJZo=F|5a>jc*EWv{+ZZO)Nb#X8q-Y~y{c*SyxtknrgjeX>*n!IYFF1Gz57z3 z+SAggpBqS2EmdWDZ|5u((7ko|G~#`y2pfYoVU9@!H#pp5;NWRQ1@Fp z&gnkS9NtC5Dt|!ubIr5XJZ0u74u~A{JIDOyJnlCH{=wQDb5+5ieaPyNw)mMHY;#*wrin z!BG&TA{-?_YT{~&f>g!Tlm)2^M`4i4Af-WSgA@m;4pJVZK1hL(3LzyzYJ?OCsgkQH z6H+IRLLrsnC>2sGj$$Fz;wTqVFQi~d#gLLAHFGsZ0 zqMM{d) z6e%iFRaaA1q^_=}ut;THO=*$ZBE{vXE>d2O`XUA9s4!Aujv6CH=BTo(DKk=MS5s)D v(ype|NUf1#Bh^OAjno?{IR3vX?lWOuZUUd^Uz(hjoRa8IO-)WsPVxN((J^&k diff --git a/telegramer/include/pytz/zoneinfo/America/Nuuk b/telegramer/include/pytz/zoneinfo/America/Nuuk deleted file mode 100644 index 0160308bf63690a6dee51c3c8849f494a47186c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1878 zcmdVaYfQ~?9LMpKG}g?%E(w*86i#(IN4ZrlsfLd0K`zN92??PUaTqhR*1opJ48yc$ zMmz`)a+&KGVzXv$W172}4Rc%j`@R3>kw>1`{M-4z&U$vX@B8DMxwydN_~QvO|KY<^ zYCe2#w`XIaqm#E{VrS2H4T*ZIT{=C|(7;<7`th80Z9cBu?jF$Ymv(5_nX?jpv`!-S z?w1~wDv=eNq-Rly^qRdudT0A2DkVeuIFlthJVyGq>nbrp=^FESpvFFr)_zT0wEvYh zI-vfmI%{5QT=fHu-*Q6}R-aK<{xMC=y)W*Pdhw(-$iT@vB`IQ`B)iKbxy3Jo!>V=g z<9RaVN2v}yn=Zp1=4eW7o~AYo)wHc6b@-le9Z?XaBex8ZQJF!So*yQoqhD%9dW&QR zUDvFrCzAF4g^UTjAY&S@$=K#YGOq53WZyim-l|F&fApYEC@z+Xm78_a^zAyiXrWFS zRHVM_ES(xUUZ*+x>9j8{%?ZhroO>O0#^+d>dFqqSY6_Ow2RcY@{X3cC|0sFYjWRc{ zN#?D8qw|NKmIb*tH9vBBBE7v8DeY*7UEG@daQkHE? z)#BPIvb-=-S8Pp^m6KAWq##sQCH9cj8Q)}02Zxj)I2w zvaYc~D|TPh^>zDnLwSw*tNiM>EGwXOtH6K$*UGYPZ*({;tLcuT_3wA{(}1>?#XH;U zbHuqk=HoV}7ZC94<@<|kH9ySaVtKe)9q&TEHq&%cPq(G!Xq(r1fq)1y+B~qrXsS_y_sT3&{ zsTC;}sTL_0sTV02sTe64sTnES)>Ms@ZENaA3P&nON=Ir(ibtwP%17!)764fRWC@Tp zKo$X61!Ng)%{m|pfvg0w6v$d2i-D{LvK+{IAPa)52(l!|njnjUtO~L$wq{+Bg|Rg& zgDefQHpt>2tAi{LvOdTH+5T566r5s~Da~Wv?lh;@6Q30CN{Dkiy@{@0UlW6W0s=0- ADF6Tf diff --git a/telegramer/include/pytz/zoneinfo/America/Ojinaga b/telegramer/include/pytz/zoneinfo/America/Ojinaga deleted file mode 100644 index fc4a03e36931bbca9b00e0344d3659e4af700be5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1484 zcmd7RT}V@59LMp;vJ^2rL_wfLP#}aZH}f^KOlz4gOV?Uvx=J0((v>o-e0d;Ay7DPR z0#gc-E+Q0>WdwF*cZGQo7Gy#8LKIjK38ts#d%6#TF8Uu1pTl)#6RM4WMCX+l)oo->Zl+!2+XQ^v9JE264q z)Tkb)7e3ddI^NYKPo#{flm39LS=FOzoyD^DOGx>XQl$ThR|TTtW#C?pI`#gmtnWQ+ zoSuvmXF}OV!_b^)^duO;?kRCLebG4AI4YXfzBZamdqwlof(m7g$k5Cy)v~Tfwv0bg z=jTK6!oV$caoQ_e+b^lMu^ic6RjWF#C(4LrJj?YH8S!r)(;P??!ZiDph&0W+H$}8* zJ{XROi%Lv=l!DZP6oXWQlw+yup*;%Xs0b;Eqb8&% zq$;E=q%KQc7*ZKWX-I7x#Ua&kl!w&EQ6N$wQX*0#QY2C(QYKQTr7jey)KZs<)QS{~ zREw00)Qc31RE(62)Ql93RBfrtM(VcIg(H<)>e7+gIf_TB=O`bkpJM^Y3OJU4tN~dB zvI=Aw$T}?bLXeeM>ZKrSvDAw}R)Z`DSr5m8kQH$(30V`zqL5W_EDKqerCu1aGE2QQ vWNnsuamebBO`;*{&u4qKPq^3uu8U7XX0%EG7T| diff --git a/telegramer/include/pytz/zoneinfo/America/Pangnirtung b/telegramer/include/pytz/zoneinfo/America/Pangnirtung deleted file mode 100644 index 3e4e0db6ae0a5a704f8cdd549071cc5b7124e2fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2094 zcmeIyaZFWJ0LStBf|Qu7oKYx92^pa9-UEAuh6Mq=J?+U2AEsb>H0TYXIGHjqLt8q_ zfBd5cZZ(#}Kb);u8LrmGpfzi?)>WdlYH+H>A8oWlwB<72?R-zQ+S=OM`m_6X?&t3Q z^R~D5`zD&(AFgozb}cY(I9&V8;kjgJqVeE|&wMQ|7XBzNiW%>xP`LV1am(nU_SNe8 zEqPvcELYu7neuKdzM^spa^1xlQ))@zio0a?7m@qbl$&>9QY;-CbMs#t6*rw2a&O-I zgIe~~VYi^^6IHnDgm+8ioLau_Y46tTRT(q&*g~nWKvh`{#mSw#`JBCUy4Z4N_~69h`3{ZmafeGTvbk|oYj{< z&}+_L(06_@sH)CtgovT^>e3m zv~0VIjvv*rtQr+N+o{*h6sq+vJ}dA0v|7aXcgPLr3Pi)!xODr|#r;(w*|>LFY|L9I z6HV`n#N|1;DRNqD`u1AAIsd%c{O+vYk~*cDPmb%B$)jrPp<(^tNT=G?I-s`?Zcrlr zhTL(aL+mW;mk%Z5qBW~WwnamtZKhc!ix!IHM^&4_dMu*!?ydp#c=kJbPeZTTlbVv1T$|v8<7k%4e^1yLNqy?@@OHaG{A3yWv|NhCi=8vcUK>luysXMyj3qXO62H`%-iEcX4kUUioG{>v{p%Xkhf|Lgm*aM-DNAmP-w=4>>lu8v#6 zspps%wH)*8I05^Wzuo?PPW+2m_~v37$UKmVSj|k3sUUOVyO<0z8)Q1je2@ttGeV|> z%*kpdh0F?>7BVknV#v&psUdSiCWp)pnI1AfWP->Ht!9eI9Ia-O$Sjd*BJ)Hhip&(5 zDl%7OvdCwdyR+h$B1uKH# N(s+4!usj&f{1s}GV)+07 diff --git a/telegramer/include/pytz/zoneinfo/America/Paramaribo b/telegramer/include/pytz/zoneinfo/America/Paramaribo deleted file mode 100644 index bc8a6edf1331b6912c26e81078b00c8e5d87ffdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmWHE%1kq2zzbM`vLGxpk+pBfJ}vJfmy&e0NbfV;QUCw{e?}%|7MB12&v-B}07)AL z5Zebto;$(7@&EtT3k-Zdz99?&K&)$EY-|7`8A3>~6llkPki{SxWI2ciIRNBjkRw2J RJy47+hj7^donvRr1pph+PYM74 diff --git a/telegramer/include/pytz/zoneinfo/America/Phoenix b/telegramer/include/pytz/zoneinfo/America/Phoenix deleted file mode 100644 index ac6bb0c78291f1e341f42c34639458fb385bf8ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328 zcmWHE%1kq2zzev6vMfL>&;TUnEwh?1e>Z!>f;O263unD-INixJ;k@{Lne+Wm*Ia0n zlKJ(cRN(iE2nHrbAY=wYMyCJ&r@dof`2TL6| z7(z&J%6}k;W8v8VqCrjq(I97mXpqxDG{|`%8stO}4RR)!209f)gPaSZK~4tIWICG* I=zeo90HHg7VgLXD diff --git a/telegramer/include/pytz/zoneinfo/America/Port-au-Prince b/telegramer/include/pytz/zoneinfo/America/Port-au-Prince deleted file mode 100644 index 287f1439266639f9564149d6e162feaba3fbed86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1434 zcmd7ROGs2v0Eh8AWkP}r7lNf}Z_#uf+EkRmP-9XwdDBsHG&_|xjG7)F+1SGh3ZYF% zr7l83o-{pf>KhmpQ-7T$s&#|6nL`sKEH= zmH6}9N-fVRr}c5Ax%K;^^ljsd9iM%RXFi$f?Z$Xp=In^IceF{>*7aNa26rhDxF`4b zG>QZHeez&9AnKB%vcAM6>OY2LI6FmzUlhqm;;e|=%9IV$uSH|uMyu)Nj5^$zVKom; zs+NkFB`=JqRJzJ@v`clw-pS4leWG(}Oh(h9B073ac6|wnV}s}9@i#@{ zM0Z%8e2^)+Yq!f&1BP(SpZDudSn&7#`|Vtw;_*jr!_cSSNQlRuJ$5XN#~F$uf~R_yqz%BC*w>Ah969AkiSvsnb!bRzNN`AWNO(wmNPtL$NQg*`NRUXBNSH{RNT5ihNT^7x zNU%t>NVrJ6wmM)WVkBfFW+Z4NY9wqVZX|FdawK#lb|iQtdL(=#ep@{NWCXT)2*?<0 z^&pT@Aj3e$feZv02{II9EXZJx(ICS?#$&4ogp9~m4+$BQtsWFIDr8v5xR8M%BSVIU c|K-?{^zOWRe|dpIXMxk57w~wT9;Z9$2O7SHqW}N^ diff --git a/telegramer/include/pytz/zoneinfo/America/Port_of_Spain b/telegramer/include/pytz/zoneinfo/America/Port_of_Spain deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-d zly}X7TU&aNwbrPtk9C83W#4Ud=4A6KbRQeH^2yt9KQG3EujHZot&Zf+(6Vahva(%$ zc0=ba2#X`C(@M+Vvr9E`Uy_ptA8P747o-+m)%5X+Ob@)M?2a!t{e<$h*0vS1`fOWk zwI_tuRb?f#uH1-z&f@>Pw(g0U*+sK6!s}sb^I^^8l%Fkd=ef3Z2=K7p! zR_43CAdLT`ZE=U`s w57G$f6l+@{y<%-Mq#M$Xzehi$A<_|PiS$I8V$QysP}On&=<3bex(iwF7t{3qt^fc4 diff --git a/telegramer/include/pytz/zoneinfo/America/Puerto_Rico b/telegramer/include/pytz/zoneinfo/America/Puerto_Rico deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-d9?>i#FV--H;{gCRu^=CFT0UfHeQbj1%&b zXL+DLp+(m}J>+lrQK}nnCHk9tua?cLUIaqEc{)^CBYy-lWqZw6WjEi`R>ec*TJP6x zIZb)yrRuQ6WYr;iwhXtTl{e~&^geD=5$$oZVW8cq)~QR zQ72-yhjeWIKGAhqy^b4NDY{KPsJqA3s2+ni>K<>`sh;5rb+z3!LF z-g^^NpL5x=Z~1rCZ+D_htn!HdYus``#y2r=)>Ans{*g#Zw{+5%t77oG&w5DfF){Sh zbv^7_lN#P~QYRljsz%gT>ycZxs8I)2>d{M8Au?Vj>+z>wi3zu(b>_}yk#(v~XRkafCRPWvEWD#8tvIi9d}q|; z{QY`L+%A=yQYUlYm#e(keKN1jE%M)1%4t{Di0Q!+IitZ+1-0Yl%$r^@%fD66K73aB zb6WMBO?%Ybl^S*3_*>htX0U!||As{h0njnxU z98DNV9OwkX4kLA%P*0A)z6$A;BTh zA>kqMAps&0I+_rX7#&TJNR&vJNSsKZNTf)pNUTV(NVG_}NW4hENW_jNWF%%JXe4SR zY$R?Za3pdhbR>2pcqDoxd?bEk0FV(th5#7@WDt;1a5Te!i~}+d$VebVfs6$*7|3WK z!-0$kG9bu^AVY$TiK7`5WK_)KM73kLj7&m*mXG2{wMlfKEtyLlR7n zCfKLsnsXgG@tYGi@pQ9JI(5iSe&vu!I@)BfeWJ-sX^GhD8X{(Db)iizD>T}~Jb zn+k@^&E?1Joa8r6=G=WYb8Jwv5;ohc;a;74vBb{ne_De>nmievKn%gl) z^6H}|e`mZbD6KNLuD&P*nM=&V{4ZqD_%u^E{byTvVazPP@|j)o$vJb|x3Afy$49m3 zodfpvgTs2qvtsXTJgs-NJt=oryr9eK8l`xCo0b%>l9E5(GNswuZRyZaQw6{f9oXnMq+8SMXc#5oATBNI+FUgv;3=OY6E8)?AMhf~Q@>9C0 zO8?4MeQZqi*h#yt`v+4qe8kqaePr(K-)+~|y=3m|thF0LALz#R7TJ{Dt()s>q%Lv4 zZYeF2EhAgCJ~Kn=zbw~=@d0UgJ*e9*{3s6`E;A1f#O(IyJoC`;^R_XPXyo9B_ThqG z&5p(w?at&u(^S!Jo5n^oTG%bo;Q`&1vR`)f_h`%It+KnbL-%}LE_+*>_0hhdw63qz z$Bs;rxIkRss&U-^=@W~+TxCowcIrf6TrBqL^CsTEPxN=v@=(|;D|Tm%JC*LN47lH#n20)7(+9NY7E^V$}zO_bm~F$gD42m5TYVPM~IRPEg@<$^n@tN&=jI7Lsw6y zEJRyRr!GWah{6z!Au2<3hA0iu8lpBsZ;0Xy%^|8YbcZO<(B9Lj&(I%|07eENDS+ev zk_1Q=AZdW)0g?zvCLpPRgOLqSmkvfgJY7N<8G)n(k`qW$AX$N=1(Fv?Vj!7; zqy~~3PnR4>c065r82RyZ31Vajk|IWqAW33m36ds8o*;>WWD1fhNUk8sf@I6nr3;cT zPnR%A#ynlhAUT61jgd7-+8B9*B#x0eNa`55gCviUJx`ZDNd7!s0wEdnbSZ@75Rybl m79nYb>Hf~)63SJ7Z-K~8>7ZZMRWmy?&1oA5XN!8Dlw diff --git a/telegramer/include/pytz/zoneinfo/America/Rankin_Inlet b/telegramer/include/pytz/zoneinfo/America/Rankin_Inlet deleted file mode 100644 index 3a70587472c633aaa178c7f979b8b3884d604372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1892 zcmdUveN5DK9LGOLPK}M6DT+vtkAxn_9fqC{q{T4?ilm$bLS~90n)31K5FhaC#`GV{ zKb%j~;lt2>s+nV=u0@umTdO%nb8QvVTGrf{U#%^tjPv{Y`M=qpzHfK0?e2E>&*$}u zF0NmhD^Fdc`*fgtCjYs2o&1|KZ|Q^A8gCA(uaMzYVVhhWmJ!oa?Z~WDNf|p$M-A?i z)Pzx*dOyXCzV%4cx*nQWzWP$dqp8<|-51KT$(^6C zZNEu%&tY5B@{PRPxz#SIJtOb6*4U+`Uz_*$H0iSJHnY60T5FSbn-xX*x}tZLsmsjJ zx*tkR{UD##f9f}@Z~v}q4i?J?*W-3=Y_hC7d&@RdB#G|3V%O*0l?@H2?Z)IT*;Ka2 zZtCwfv2dHldaj!-DZ6z`=Q-2#V3lrd-EX%2RHECPH=7+7{o1_b9kcWBOPb(Ic;Q(e z1D^St|M`ng4lf?RcG))|9{>1+ycmyP`plQas|;x5Erh{F(% zAudCFhByuJ8sfG`=QqT0kIr+3>mHr&4Cf)7e_ zgh$s2NGm+LUO<}R(RBmT4oE*34S{ro(Go~c7)^n6h0zvBUl@(?=sE*wjYroTNOL^8 s?m*fD=?|nqkPbmw1nCic9-EZt0yWhI%3nGqkQ)g4OGBYRC=g713}O-J3jhEB diff --git a/telegramer/include/pytz/zoneinfo/America/Recife b/telegramer/include/pytz/zoneinfo/America/Recife deleted file mode 100644 index d7abb168a7434579852b7533fe062fed9297ec2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 zcmb8sy)T170LSsC^%5z;Vld!nBnFa8OG-3iBw>(rz+e*6iNRnIF_PUH{KC%F^0A5;gMUCATqHt^7hF5K z5s4sImBW-o-ctz1OIfDJyk9wlE5VNMb9AR0SH8o0K8Ime^L)c~(HBK>;#@M{a5=^1 z@}BkTp#6HRuUB^_((Lz*Rqls^2hPW`Lbp%db>g{K-MAZa5?2bW#P?n2({6_KIet0v zwK?4#sNZr1Yc}1X`|Xk8!U=ceX0J1vy6h7SJ!;nJl3)J^^zSb%GB@9?|GbIW^Zl)Qq0P3PSX3`Y zpWA<5KGsVQOYP-n`FiEfEqk^6ky^_rk@eeoYW-iXY^}O#duCF0hLh?-;M7k_>ZxBJ z{rIBibu5c`-rKG~s(IIv?!Slpr{XD@6_sJBEH$^*+^6PNho!{4a{|ZD@EQJm&m00E z5s(l_3?v8=1qp-1@il>vNWLZ%5(^22L_@+M@sNN>L?k2<6A9{Tq9S3DxJY1M6B!AO z#72T6(UI^-d}IK=W(3F(kTD>GKt_QK0~rT05M(6CP>``8gF!}v3O6*LRG7d!-P)%6Mh diff --git a/telegramer/include/pytz/zoneinfo/America/Resolute b/telegramer/include/pytz/zoneinfo/America/Resolute deleted file mode 100644 index 0a73b753ba597f89cd57cc3875e7869dc133778c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1892 zcmdUvT};(=9EZP$TpAm-OcIeGEk*QjI0r+ICY5p+gG78eA_%^u7-AY<-wvWiKQ^XU z<%P>^I!uS&lvBrin~Tg+H>Yz9b#5Bdn$Eege!XnDU>twX-y5!+UiM*!=j@z6y%=gL#p;J$s)y^^o@Hk*HQW-fg+uHnvWb>%XwGHjEJ!}9u+R69H?RZ>Pz))7Pc zBsF1#rv8;;M&9k!w9fy`8$bQ3>BnxHjQv0Ao3U49RP%lvJ^xR6Yt;|-?Yu4-Q?k#F zO+GG}fUXuTYiy69h>dqnnSXrwc0K%J!d}J-l)s6+syLXDy>P{W>ysC>x!P0rZzJ} zYp;};y1_oJJM1^B?*Fcz>?)SkSL1d~Y=V4xrS)fW{GZX+GsvI<=3Xg3(V)cU)BU)!VAy( zGibn*1K&UE-w$~1$>GK07ti_z#pB<7D=)?4r;qxQ_${wK`6N3Sl!8bkDh2c0%dyZg z`W$x~cjI;;_Y1jU{yBHdxMj#aGj1Ak*NoeS+&AOKJ-R!G+&bjmAvX`Xd&uoW?w{cR z!~=#45FZ#$K)hhM;nDd4am1tZ1mX(B7l<8SIzJhXdUT#LT=nRDWjG7*7UC|% zUx>pHk0CBYe1L68nXS_J74{697+(FJOf3zWZfVjwpV^p}Q0flwfr_!wfe>F)pl diff --git a/telegramer/include/pytz/zoneinfo/America/Rio_Branco b/telegramer/include/pytz/zoneinfo/America/Rio_Branco deleted file mode 100644 index a374cb43d98bfbd06c82ca306a74f96993fd5657..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 628 zcmb8sJxIeq7=YnRY(b*vB7%roy9@pmL_{)nP`pYead0ZQxd`GC9dvQ&B#Mh`J2^?~ zbX(CWb7`xBn?(@+N@_Xp4bFk)INpPUgzt&BwVR(8u}{j}Ce0o^FP#?3!+BBeZmY_> zly}X7TU&aNwbrPtk9C83W#4Ud=4A6KbRQeH^2yt9KQG3EujHZot&Zf+(6Vahva(%$ zc0=ba2#X`C(@M+Vvr9E`Uy_ptA8P747o-+m)%5X+Ob@)M?2a!t{e<$h*0vS1`fOWk zwI_tuRb?f#uH1-z&f@>Pw(g0U*+sK6!s}sb^I^^8lYxU^Od-M20M9|wz;Y2j|ctvm4vG7Pn$~po_cY$;4fru>xEC(%woo>Mg7s# z;win9Fak&n6DH46gd6Zt(c~ zb>BpnIz#PmPhD)@EZ^sCl}lyGaycPGM!orJZ1EN~9-pMfr_}UT z)~!{`6S8#V%3`^GUZjo+&XlO>3=@5Exx@@EGqE54E-QLw%nh$z5Z$m^-+1sNSy>XU zSJiy0;xd2IH|2k*ZjSg;$0v5G_}NL55Z0m+hCern6T8*wz8;fwu33^hj~dUZU9zV6 zag%a%qoh_H(P{N@lJ4E7Gm7U*W_*dxN*kB8q1ie+W{%1pi_+`<98>GhUe!4lK2;mu ziZr);@VdIS?GJO?i-*8W6WK-d*tp#hm1F_P`op*=)90r z$s0PT^Dixt%`crY1z*>Quc^b_(_0{gJNKKSV;Nhr-n$dtfe5^u0@fPo>BD?lX_p_NwqI9&opHBOT+LLb0G4B9OxS`jWezCL}#~oa;Q?8n%m7& zr#DG+S-pAsg+vJo4hp^|IAo5!{yV=w;7Ebv1OhLM6A}otwK&)E9<;!{m3uEO@cA8I zvEM1;c{jL0C7 zQ6j@c#)%9R8L6usDl%4AJ6L42$Z(PIA_L~j88I?sWX#B*kx?VVM#hZ{92q$>bY$$v z;E~ZI!$-!C1i;ls00{vS10)DY6p%0=aX0NT85NA)!KI zg#-(U77{KbUP!=PZN!j}x!RZ^K|`X3gbj%s5;!DsNa&E*A;CkUhlJ17#t#XgtBoKM zLRT9@B#1~9kuV~0L;{IK5(y;|OC*>`G?8#3@k9dZY9oq-)YZlm3974&DiT&Cu1H{! z$ReRdVv7V9i7paeB)&+1U2TMs5WCtKBSChxQAWay#2E=R5@{sVNUZUHAM7w&^K4u5 UBwxBG&6ASkOHK8pdQ!sv0^LWd&Hw-a diff --git a/telegramer/include/pytz/zoneinfo/America/Santarem b/telegramer/include/pytz/zoneinfo/America/Santarem deleted file mode 100644 index c28f36063bd264e932a1910861e884d2a1669b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 602 zcmb8sy-UMD0EY2v8>Pgr#l=DE$Et8x2N97|mkufj0l~>h5nOapT#ByUMbN=foo-CU zAzKj~1ks^1wToLo1wTrwocH7pD4Gy{fk49h#9Q559~Ui;jD70ZtMlk`Kj-(Xy!t;& zy7)GyuBwN!JbA6kQCd~{UV=(-Lx#-}RXvI1Q+Tdw-mH8-?g~EIw&Z89qrSRFb$xkC z)l2uX;nsr2MZ0cBz3RJA(ET^3YGChKXU^t>!EC694tCXW$D_`zZL7R7CYfk)rc%b- z`a&4J#5JuxP;_L>mhKc?C*8s;LCW9@8YIx-(g5Nk6aDPnC7BngrQ zNrU7;5+Rw8R7fr)S**>5q>Hurkc3D^ek3W8oJdk6E0Pw;izLRpeMgJPiR9lhyVExK HOxFDc#B>P9 diff --git a/telegramer/include/pytz/zoneinfo/America/Santiago b/telegramer/include/pytz/zoneinfo/America/Santiago deleted file mode 100644 index aa2906063f3bb2be42e800c208c6a4453a610031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2529 zcmd_rZA{fw0LStFy_ctWNDwkXEu=6Cgacd!4Dm>kl7hFuC%TfU13?2Mz)}ijE3vjn zMr%!**`vCq2dsc4^QFmUiGf6|K0z5?(WOG{l2HP ztg0f@`o|Mue&OYL$-I1z*=de;vAAHL_nhZI^S1*Bt*u&>ZyV^^HeYrxw0y@`*Xg$s z+@7~{UX|~-Dt$e1qq^tpK2PseukIU+_Y6(1kwaU)^L=rqU4J?4lp4ugp+}lJrM`<(6L`- z*r5v|ynj?iwE4vKFZRhvo4Q5hlOsB6NvD`x(W9f2nncXqFX$VhI@FX|JM@%`4Qgu8 zT0M1mrHVbjK*x1ut7)Hm<@CLAYR0iVc~jk=>gM)%8QOt-Dm}^VK?S{RSm=?w0BK z1uA1*mCT$!D%^6ZoD;T4%#G-kSwSNz+nOY^KM4^z-zV#NM}81@o{G@9PYsFrhsJeY z^--~)ZCJ~akJZ9W$8~<@o9eD5&*??c&!~d*Zdveiomw2#DHo4B#gdCnvhYN$xMz5^ zT-s}?WnJ@R(TBmJ*wd<)??0+M`KR^0&Cja)l7EvW-g;FU_LW?fbzZDG_M={N<40mm z`)9iB(h+fgte!D z#%9XOixH@zsO&;d&U5=Q)H{iUXjfryG6E(>=)TEvSVb+ z$exi++nQY?+eY?{Y~0rD9N9XucVzRnX7|YU(cV8_XaJAt0MG)Y2S^i;E+B0{`hYaT z)^q}C1=0(o8Avyfb|C#g8iI5LX$jI3q$x;OkhUOwK^kLgI)k*v*7OEx4$>W@JxG6$ z1|c0nT7>inX%f;Uq)kYlkVe^>P9d#AdWAF#=@!y1q+dwGkd7fOLwbfZ4e1)vHl%Mz z&9pV$MB0h; z6KN>YQKY3vPm!h~T}9f8^c86=(pjXnNN;UTbCK@an)V|7MH-BB7-=!mW2DJQmytFj zeMTCMblTRm8tJvIX*SYrq}@ork%l83M_P{b9BDezb)@Y`-;u^6okv=a^xoDqAL%~Q zex(1%9e~^e$X$Tk2gsd(+zZIvU~AIxf4m<;bIr|>=Qd|%UP@6;YI^G2v4zc diff --git a/telegramer/include/pytz/zoneinfo/America/Santo_Domingo b/telegramer/include/pytz/zoneinfo/America/Santo_Domingo deleted file mode 100644 index 4fe36fd4c11f998ba3f626c5e23d97bd82d12791..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmWHE%1kq2zzYO{vTQ&s-2fzZ-FHg(#o>M7*Yd*-|CfKfz~Eecg0cPb1;$_X9?S(F z0+{czc(4Xs3Siye>A`NWC4hawo)eq`(*i7i?|ERQ`7nWznTeSN4q2I5|Nq}t!N33{ z6Btj(iu}6==_zZx~4N^xBsjd@jR9zpF?tV?FW0bt4fT`t(N}#elb4QE(1Qd z_))z~{#x#K1l8P^2sz(!NyQG_kZ~8@t9Z557Vbfn zaJfNli0W5~l`HMUiPIt}>${ya+$WL~Vr263NtI$v%al7UV&kV3_NJ3AmD=-Erq%U| zbk!y|?|CR>(?z)@)~j5Hgzb(vAu>EZnGt*;GDW=19IRJaGp}t==QWW%J}I}|OjXE=i_Wt5q;=q;TvP@*FgGUQxxwT&T@>8VmqeoOE zM#+kSb)qtSN>+Bvi>ir1S$*9v4*P5En))8m=r~Sz*n)lGjx$_hS&lOq2wUtp@7`ET zOT0cSO}g}TTfYCOLO;}@45SXDE(ECrDFvy;sEa|WG3s)VdXR#Uijb0!nvkN9s*tjf zx{SIoq%xx}4XF(&j^?QjDG#X+DG;d;DG{mBsEb6ZH0m;uI*qzeq*9|U6{!^|7O55~ z7pWI17^xU38L1g58mZc-%SP%p>cWxAkZi+k@AuHkp&oV$vAuBWLr6FrG>ct_e dLzaiE4_P2K>Q)wPgJI$SSYc$2-eRsh@()nK$prua diff --git a/telegramer/include/pytz/zoneinfo/America/Scoresbysund b/telegramer/include/pytz/zoneinfo/America/Scoresbysund deleted file mode 100644 index e20e9e1c4272adc40c07562bb3d6767cb056b550..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1916 zcmdVaT}ah;9LMp$Q#gNnQ{olndcD0+qjvQZqJe&*R z`~LW9wl(Ki|9H(Zf8oum!@PNxLmzo!BfV`a=jA1T5ta0$KRWW3#spu{n1>fNcJh?Y zzt*n{zB-_B=PyY7;E*I7J|YXdRT6i8EYG#J%kx_rWRb@wN$wI!b`(fTe5x#th?UgP zVojaS)3hH`G<_mkmwYozGlu_A$FW~Dv-hTE?YXSkJI<@K@ua$HZ%9u2u;dnwNZy7| zBtM}~3UWH7V8$;?<9c=J?Rr`EXNNAoP%JNgU#;$eIxQSou0?xS>54<~y0SS*SM6CQ zt4l+*xG_%Fr2MQU#WPYG`kj^~{UBviKS_D;B`F^tm6s-u%G#k5;u-r~y*=GhG5DFT zYipDB-Mh8&<^8&$wMjR=(5gO9nQn@y&?-l|Ry}rVbyTTTU!SY5Os7fB*+;s0B3!og z&ym{U-{n>RL#gW>m)Ghhq<+`0`ud7<(oj35jfn@Psc=Y}!_Mf|1l6tgKGl|)cHK7G zrElE!>6^zlYwNe$<*j{%+BUFJ-fl_MclPGX_DZ+3H^<1ku7$Fr(iM@A0cY5C{Z46Z~vQ=zshZ5(xa( zVp)N}aAKe!(h_V=?D#^D7;{Po-8^;wzD9P@Tr8BQmkm=~X!2g~;_F4+9D0j`*@ za>>XwBNvTaHFDXu=DLv!N3I;XbmZERi$|^=xqRgMkphqkkP?s@kRp&OkTQ@uY)v6Z zB}geqEl4p)HAp!~JxD=FMMz0VO-NBlRY+M#UACq$q%vDm8d4il98w)p9#S7tAW|Vx zB2pt#BvK_(CQ>I-C{n4dDHW;J))b3WiGvJA*NAPa%41hN#!TG*P!Kvn};4rD!$1wmE> zSrTMTkVQdO1z8qkU66%ARt8xbWNmEC;vlPIYnBIDA7p`$6+)H>StDeTkX5q%-!D_R d+bmX*%WXER$l=Y+%Fl9UI~`t^(|&S=KLvok!I1y} diff --git a/telegramer/include/pytz/zoneinfo/America/Shiprock b/telegramer/include/pytz/zoneinfo/America/Shiprock deleted file mode 100644 index 5fbe26b1d93d1acb2561c390c1e097d07f1a262e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2444 zcmdtjeN5F=9LMnkqQH%ZQ;8vb6-0*B?gOursm&?$b8b?d7UesOi|Njd(VTTGm*mXq%nh`_OY8GIv2h@FWtq%9yq zpPH0YHYBL9x|w=v#e|wxIIhF9MpXC}}?Nyq3Ob5vJb$tvNO`8x57 zNR>1kp=X`^LCrpNN#EFeTFvp#k~i%*sOGK=%6aQP6gTI7E^k@(wwNFHo=i^FA~|qD zr#Lo>l#!D<^^!~6I=EM-oo!U<-OuTaBb6$%*{ic&TBNeQtuklR47IRitz1+&rgD?- zlegu3q85jz%DluYBJbNMnLmDB6rB1=-u~%;Skmv%cMR+nOFMqlckbFQ3L8GsceU

gzP=p8ch855>q;fg!QFZ&W@w zvR~A+4$FrI+eK~tQMsmjy?EGpM%T5qsYlWe>qoslRUh4{JtbwzbJ?%G$?3{_+O2)z zvC4O#K(G7eXSKeoT0V9rMm+A%mrooV6%AF1vaw@WY{;FI8yk*_O>r0G=JGDFIWVsM zd54vMqPHC@P|dX-y?tkr3Jv-5Z%WwTuYY~@ z-y00>?i3;ze5)rU%)Dz6Vc(~8rXEg;xDrhw&L~3X?MMSE|QAVVWNFk9*BBexXi4+s5CQ?qMo>o&(q@q?+QlzF< zQ&gm?9A!o7%28OPvK*yFYRgevq`F9Xk@_M9Mk;JIB}Qs&HAP0MY&B&@>WmZ`sWeBa zky>*U8>u!&xsiHv6db9z)s!5mxz!XMsk+sa9jQA~c%<@3>5Bz0( zYMX;ubev<=mU^aJ=~`I2)|SZ#w_fI=bZeGV*Lqux8))@)K9yl(%-|NuSuMerXFHSr0@9k1I96RLP_{Kru z4D51l+8GizZ)kU>wX7Ej^(&mjO24?Jq{x|`UMP}M>q0YPlSFbvK`1#h!AbsMk)C;e zo=O>;t7o0?sM&|3^{xB9Q=+p(-qv$Ur3PloIcvTZa|^D@c}qVMX^CG+U&folH#wox zy)TON@h^48#Ws;Sd|YRpd0u4??$bF()~W^F&uaNnt;!85nb)4D@+-E<+v^fkfv;H> z=KZ3IJon1tlxd>)!hBgW@w2$&L$AE^>}R4>r|G-iIVSE7#ps25_lkR3FY2=GZ>vQ$ zAM3@<1FC$%LA|7WlUnlQIa!h2FDlNQl$G%tMdgtpx%6_QsCxN*z3kI2RlRw?zW=QT zRnxdb*X}7*%S(g$fzH{gE~QfY11{zNJyQn?&a1#T_sNyB!(!zJ8M1!zoM;$|lMjub z6ph=j$cKlA#H!{|`N&|ec(nSGZtC5y9?Krlj|X?C=6J1FR|M7S%e!^ZSE+)hJ9LXD zQ?(oj=rtGO)suVL;_1#6a;=UNt$`xh)^|m$E1V(Mw~mVT#0l9^b69kQr|Zt* z5!E^Vo9;@|s%!YH-tg;gwQ=xWz3KA~wYmFMz2#^?b+78t&-527cf=HTuXZ9DXI_jckf9-Cvzo!79UT{j$799^ z3=kP1GDKvI$RLqXBEz(raUuh?nvo(yMaGH@78xxvTx7h+fRPa+Lq^7o3>q0VGHhhr z$iR`2BSW{Ev0KgHk*^Ruf(%zE%@pB*I9Dkr*REMxu;_8UKoNWJJ`p-sl+5 nb$tJ>bC2(TZ}dNr{`2Cc-6d2!t2#d?FGpro=jP_*=1A`!V0=z$ diff --git a/telegramer/include/pytz/zoneinfo/America/St_Barthelemy b/telegramer/include/pytz/zoneinfo/America/St_Barthelemy deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-d`^gCP5U`+bZv-ep}BY;${S9wkjrz@V&n5bgwR^MMy}GWhrN~q8T=CXJi%T{=?R(7`biKZ&r~8d% zEv|Lu1^1gqt?R96J$!GcYw)1IBJHpcDhebBS(ht+X4j?JF^eFFLWr`K5ro=#C;jcF|o-WQ_| z_5VqHEy9(G_(B|xZBU1gm5C#r!sLPU z?y9;w&ssg=&ZwyCyNaIrza={4jEFwfBzt|Y#8vygmREngRa{fKMPB>bTG4yn-oSN* z*~aw~D+7J*&;P3Lkmm#a#!UCebek85y%4)X z7oPPG+ffp@<;WcWtrgYg@NF6X+g28vx4)9;ACXsR-mz?~F)|~^ywgZ9QU;}(sVSX} z)YA(BX#?Z^X$K|;Mz`PL+0POn?e1WHhl02|7a`%zjkKBKx0k*mWNDGi2*y<)A zT|nA^^Z{uE(g~y$NH1)4GmviB>UJRgKpKK{1ZfG<6Qn6fSCFad z9Hcw8x;;pLkOm}Lpq1F4(T1zJfwR_`;h+G>INbmL|TaS5NRUPMWl^LACX2PokUuR z^b%<%(oLkDNI#K=+UkxXEk$~YG!^M8(pIFeNMn)CBCSPwi!>MMF4A6G-Cv}^wz|Ve zi;*59O-8zmv>E9$(rBd9NUM=vBh5y-jkFu-H_~uh-EpMlNY9a`BV9+@j`SUAJkoij z^+@lL<|Exl+Hb4-k8A*2y#tUfV5|24vI&q~fNTR~A0Qh6*$K#2K=uN%8Iaw8YzJGt zACL`Ut9JylC2aMcKsE)kE0Ar0>K%e?5nH`S zkWFH%cL}mhkbQz|6lA9$TLsxG$Yw!y3$k61{eo;5TfJkDEn}B;d)@d*Rc6BFYT;}ar(2eU89 AC;$Ke diff --git a/telegramer/include/pytz/zoneinfo/America/St_Kitts b/telegramer/include/pytz/zoneinfo/America/St_Kitts deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dPnf?;Jz(0@Rq17TML+sa@`EoPIj_O2KNba#TXX#lUSMSY}+(~!w(sW;H($D71TY6sJU&m(9Y0B^+ zGNWokKI$VoKDZXYn6U{jG3D#}{idBe?Ta{fRr7r3&aBMEcPie7Eeo6ZQ1Tl(1)Uw8 ztx(qWCf?5u|54Lvs0yhIsSK$NsUB17Lli(XKvY0UA>oEWT diff --git a/telegramer/include/pytz/zoneinfo/America/Tegucigalpa b/telegramer/include/pytz/zoneinfo/America/Tegucigalpa deleted file mode 100644 index 2adacb2e500e2f9621b2debc1756d308747b67f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmWHE%1kq2zzf)cvdlotv&6^SMPb&R2Z}MX1C*-vJy5o(4RDF=dEj#G#04fsF#P|2 zoecxS|NlETFtYsrzj^@!hmUUvgR=_|2Zu0(kYL$=AOKkiavI1|5Dl^zM1w2`(I5wa RXpkeoG_elh0=mYG3jhxjJc9rL diff --git a/telegramer/include/pytz/zoneinfo/America/Thule b/telegramer/include/pytz/zoneinfo/America/Thule deleted file mode 100644 index 6f802f1c2acf9cc73481ae86c9e099fcfc28cf25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1502 zcmc)J%}dll0Eh84Lqak z9KPEAEAD%jjt^F;o%5A?*NjW;p6-+hbB9IZXoa*-#*3t$ESY>WQaJo&+S&41q_{J5 z>hU)sEpEHskTh`IFwadQa_t`CMmwyrD8Dw9IyZ~N|zLaKSW_>tUP{cK@@EZm%idh!nYcfC21p~Bp9YkcTB0$7fZTqZAg`m&FG57 zfU4{r*HzQis=Bsc*NkQ>>AfRQT(1%*6Z_;Tzf06cHOM-5tf*V@NPk?o@V~ap`iP*Y zzZ)$ZmS;udtpt7c?UFjzwn?9#m{Co|U-X56an+pmUbi&$tBWxYbZbGQYF&FL+tT|) z+v23`*wP?6rUzx`PmkytZI|5(b`j_)lb5HWMNf5xymI@iSQqwBZz%Mn*JcZa=2h7G zP-yn4ZG*?_v*qNPSLCz#{vwi)IfOz|#DWBaM1zEb#DfHcM1+Kd#DoNeM1_Qf#DxTg zM23Wh#D)ZCX`(~IL*hdML?T2&L}EmOM508(MB+pOMIuE)wKTCJ!CIPVk#H?dyhy-E z#7M|U%t+8k)JWJ!+(_U^+u}hBJ4O#$tb04}v!3;+XRUL0&g<@O_u=Oq zX=vVB=KbT$u)lCQPuR=%tUdPWo3~Sc^0<+6dB4dyHKo0Qm`Uh8uM;2CW}3uQlKNAo z*J+hJcSJ5tKdgM-A@P~VREpjwsb9aS(@sX^vJ*Xe<|~hy^k=r}jQvgK@~+$U70p32 zYkh&v4EfEKi&OPg{uGmy>sMDNPng+>DQfnwKACfVLS>)*S*{s5qjHXZCfB}jRL$-1 z%yo|(RJrv(n7nO6dS3Y{bN%us^$nRXoBV?9IzRTI_Wg{ruFg_Io3Ql-(TDQo{jEeM9pjjO*?<@xi?!m?qt=#n&3G3OgyG5(8LJoUa_ za{8>f>DxhFIdnm8es7PyrSEgO_1P_YY1><}tY^2nt@bIYYTT@CmxOvwR{?uXt|1to(AR3h!!>Rqy1g+6`f;>rYa5R2IwX2gcQ!>MP=o4bcw^~S~lvuSXZR^hj0 zb8m~SIRwj_do&wP|k~(AzUVFgt3y^p4o~(oxZ`I>tt%Gpkc|4j+=P-y76JgZpIX zSCwj4cT^sJH%E1E49V`NJ(Z9+Eh%yOf8rC5zaH_tc>J~Jy`*^j#G77nJpR$igjDyY zyLZ;gaKx)x6Y*-eciLNLZ?*lKJqdrmk$*9jxIOI`_7)9Fak&n6DH46gd6Zt(c~ zb>BpnIz#PmPhD)@EZ^sCl}lyGaycPGM!orJZ1EN~9-pMfr_}UT z)~!{`6S8#V%3`^GUZjo+&XlO>3=@5Exx@@EGqE54E-QLw%nh$z5Z$m^-+1sNSy>XU zSJiy0;xd2IH|2k*ZjSg;$0v5G_}NL55Z0m+hCern6T8*wz8;fwu33^hj~dUZU9zV6 zag%a%qoh_H(P{N@lJ4E7Gm7U*W_*dxN*kB8q1ie+W{%1pi_+`<98>GhUe!4lK2;mu ziZr);@VdIS?GJO?i-*8W6WK-d*tp#hm1F_P`op*=)90r z$s0PT^Dixt%`crY1z*>Quc^b_(_0{gJNKKSV;Nhr-n$dtfe5^u0@fPo>BD?lX_p_NwqI9&opHBOT+LLb0G4B9OxS`jWezCL}#~oa;Q?8n%m7& zr#DG+S-pAsg+vJo4hp^|IAo5!{yV=w;7Ebv1OhLM6A}otwK&)E9<;!{m3uEO@cA8I zvEM1;c{jL0C7 zQ6j@c#)%9R8L6usDl%4AJ6L42$Z(PIA_L~j88I?sWX#B*kx?VVM#hZ{92q$>bY$$v z;E~ZI!$-!C1i;ls00{vS10)DY6p%0=aX0NT85NA)!KI zg#-(U77{KbUP!=PZN!j}x!RZ^K|`X3gbj%s5;!DsNa&E*A;CkUhlJ17#t#XgtBoKM zLRT9@B#1~9kuV~0L;{IK5(y;|OC*>`G?8#3@k9dZY9oq-)YZlm3974&DiT&Cu1H{! z$ReRdVv7V9i7paeB)&+1U2TMs5WCtKBSChxQAWay#2E=R5@{sVNUZUHAM7w&^K4u5 UBwxBG&6ASkOHK8pdQ!sv0^LWd&Hw-a diff --git a/telegramer/include/pytz/zoneinfo/America/Toronto b/telegramer/include/pytz/zoneinfo/America/Toronto deleted file mode 100644 index 6752c5b05285678b86aea170f0921fc5f5e57738..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3494 zcmeI!Sx}W_9LMp4A`+sAikYIhq=?EQh6_?+E`)l-1x#^!H1rH&^5lY8h%GMZ)G&<( zl@6x3Vul+gX$Wd+)08OgDL$H#3+RJ;qUZE{-`j5Tu8UsgJ)bkox&D3saS2IN!)*U} z>X`rV^4u^l-@waFg%5>%F-Ky$v zfxf-JOx(#oA@%A4QJuL<-d&I_?xkeO`xEDh2eE1LVV|+$QAmP(+;Oh@%O_Gk@f@R` zJRYrUuJ=|?&qnBHM_VfA9)IoH=u)<9r*>O%S=E}WbZzMr?&6uOGfWAOs7tbL=mFu` zx7UxyZiaWYj%{~=z z__*%p@lR)ZkT1<&e`+!k(Tihwg4GV#nF#uq<~mJTgR%m{TD}`uobb z_@g4O=AIlCo+n0K^U!-;n(IH|=Rf2Q`_zK6bkuu5So=Do-N=~adC6cou^z>uZ>YY@7 zJtMzNrNle6%q&pvhATZYC0ot%JD_LB&Qr6Umt< zeE)2uNY8M{`FmQ4j0rJv!Iw5s%k6poYP&zrum4lOb-4;w*laG>kzzM@m#c7_&C~ks zZGAQzVvn;8=x^SU=6%b&!{W?%*=%msN8EFap36KlZ>LovI;YY?F2>=oSBm_tdkRTvYK*E5;!O{c* zi3Ab~Bo;_8kZ2&`K;nS}1c?X|5+o)_P>`q~VL{@81jf=t1_=!k8zeYrMTakhhsVSR z2oMq>Bt%GzkRTyZLc)Z^2?-PuDN7S7BvweUkZ2*{LgIx442c*LG9+e5(2%GhVMF4E z1P+ND5;{v0J0y5W^pNl&@k0WLL=Xuf5}O(dL1 zJduDR5k*3Z#1siC5>+IuNL-P?B9TQxYiVMO1Q&@e5?&;}NPv+DBOyj&j072pG7@Ga z&PbpwO{9@fTbfuS!L~HfM#7E68wofPaU|qO%#olYQAfg##2pE|rHMQedP@^~B>0vl z`bhYZ_#+1Zas(iU0CEf<2LW;vAcp~R93Te*awH&!f~7eYkb}X}91Y0fU}=sAM-##_e!|ATe{f01&0NPcCmNu8r(HF)a!2+Ad$WR diff --git a/telegramer/include/pytz/zoneinfo/America/Tortola b/telegramer/include/pytz/zoneinfo/America/Tortola deleted file mode 100644 index a662a57137b69e8ba445e899566222cdd422a764..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmWHE%1kq2zyK^j5fBCe7+atL$T|JZ=)fiAF9nwp-dS61!b6&sk{JuY)g+&F) zu78{!_CH)st-XBr+hU)}gNNn4Ly_j|t*zW(dW z8Ondqtpgv%E7z_#9rRLf6}+LB?zU#Q>Ao~pddv+sJ*S3ANa7_Es@o(?eQUy9^%8z% zpXt?HExnuSRYYBd-0@kp>a%CBR&Q0PJGUC$cVmgVt7xt6mpflYW_Wb}3FFm(xN$ma zaJIQSEJ{ZQrfjyXp!mZtIYIkjoLlxW#w)>pfe&?s*IYQdS?4< z_3%42GVA0DleuZT%>Hzi$y&5t=Ilr?b0_7>Jg+j@F=^tN-(fsm10^TrgvmKSQ7;&D zRW0};M(6q))xzC={iyGNT2$7oAFJJ|@|GXfiz_#(C7C{1`r>x8Y*d{*Ubx;YkEoJ- zcdp5AFO$O9G*eh#AVnbqP0=SaWW|Mmd16PtUip2DTIF@?C#(IcI44f8E^k(A#`V%` zi;t?3$acLhYqwe#2orB=o$>nGWJ6Sy*-%?A8#~L))0Nfo%&7viskB0#JvhUZ=9S3i zUGXM3D7ag9`}{Zm0)bcGbh!e7cTG_DK%jh!E7bY!?YwGMrpGlW-QH|_AF+3i$NtJ) zt{~^}{EIpS?8%$#y@XT(DFspsq!^C28b~>idLRWsDuR>*sR>dPM_U!7EJ$6D!XTAF zN`uq}DGpK{q&!G{kOCnULP~_x2q}`ItrAiuM_VVPP)MbaQX#cMiiK1QDHl>Nq+m$J zkdh%aLyCq}4JjK^H%D7Iq;g2ksUuQI zM_Wmxlt?X+Vj|T<%8AqyDJW7=q@+kqk)k41MaqiQ6)7xISw~x1q_#+Lk?JDlMe2(b z7^yH)Vx-1Mk&!ARWk%}kXbX*0+R>I8sWnn;q}oWik$NKqM=Fk#9H}`{bfoG?*^#;< zg-0syXiJaO9w|OjeWd(I{gDMgRsdN7WDSr-Kvn@+24o!^?Lr_c;b@lvSqn$I7|3cM z%Ym#1vLMKcAWMR*39=~2svygPtc#;v7-VG}?b0A?<7gKLSsi3~ko7?p2w5RyiI6oy z771A;WSNk4LKX^HDM!0h$XYqt#X?rg(JmLVUdVzWD~2o?vS!GlA*+Tg8?tW5!XYc? zXqOIIJ4d^C$m%)Tn-T>HGQvYrE;2hx zn`onm5m-V&LAa=V(IP`42qTcnf-J??`7X3-6}0L;&ix(l%EkY)H?$l{xBc<>m@gcj zCUdyQb(+`Uy{VC#6ERvp_Zt}+p6;Bwm@qOO^Q~?AP=@@T)~*&4hQ(6E0kNj#qzrJ> ziojC449d+G!HHQiB)Ue0`h>_Z_e>SGkfGPU3s&LNK|12$4;4AOsMih6DA}GYqq-ib z=sJHH)B0M(W`2@!W%tGUz!x$;X;8$k%pNdtROHRv-8ZVU@UnjHDKd5Z)9GMgDDRSn*WbV?O$a}IZ^Jkul zf{_rtW#W$5+WS*)yM9@0Kk-KI=)SEAt0wi%=1Zz5P3z*KE>bXZ8r18-2JS#aJgQ7K}bbNNk~mdQAkxtSx8+-VMt|2 zX-I9BrZ}WJOH&?FpQR}fsSqgsS+s@sS_y_sT3&{snyaHi&SfA%0=q6GzB9S XBPAm>BSj-sBW2_NRkyE+KHK*fniI0_ diff --git a/telegramer/include/pytz/zoneinfo/America/Winnipeg b/telegramer/include/pytz/zoneinfo/America/Winnipeg deleted file mode 100644 index ac40299f6b27043e8f2454ac594b0ec184c1a237..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2868 zcmeH|ZA_JA9EbnMHxNb5#0UvYD~2Khis1`osbi1`>Jd@M)G)*{9^X)W!GDX1ep0i1 z8Aa--yvPWN?JTU&l{>tnyO`?;R;d^qQPIoIW# zp7&au{A+&Z35Ojthx=Y?E~TFP4W&m9rk7Rj&<_vZb&hNwsYla_-EY^n>FNG=oYS*9 zyESX)%9+VC-Lq59N^Reh?)UNg<(yQwbwS&tu3@8l-hABo6WQ*C`Yrn7t_@CoRiXa0 zZlTjqmZcZ-yiQ|LqFzcK=3Jf;u2&+1ou;Hw@^jCj?$wAexq7R&d(HfsYt=38^`=I- zQG3j7K7LGo+4!Y@~V>eavRDec_jum;E;4RCjAmxfK+weBkkJiJ7^ z9oQk=cfBJ$w!JNntXnKS3+KqAc^T4cT9O1MCrIz%k@8q%hy?c=r;q!$N=Um14Y?Jp zeHuG!X!SjP;_K@gw&#L|?>MP_3%bjbZ&hf&DZfhpdEd!X@ip>v%IES-&=(RhV1q=o zRcmCxLW!*3s{^iiWnk5N8dc-ypt7YJy?2rhE}Et>n}%v^Zh^!t>a9bPGG*wb7LAV> zA;aQ+&}aQZC85u6lF-y9!|&J0h~o`1^86tgRne-6hdz+!*4OLvrK@Fh{$YKgxKv+E z+o@x6R_WNm#X4^6L}%RX{gO0jzME9DO_BrC+~mD0B&9jled+xzI=*&-le)4{C+v%H z(z3HOeREG|;;2NOwB(L6IW%0oQ)(S=t4A~9E1irhVUihk#?AajWLn!kcY1lFWYuqV zvx|?(%T>$W895)zD`na4%+w<~tEAAGJ*Zq?&CPOh0@vuAq(o;<^IXl12zPQ%rf8m@ z$I1IFO6N6Qa$et@EN`6hx$_GK%9|Az-TaI|an>Jo7sTI`h4~-3i-M|UaaxJHxUE?W z63U%|`ct|rc#X5HYL6DS%ypKRZPFFzQk<1VOLSFblv6Zgs;=JJ&1vVcul61Oy7}|% zgRUJr{kN|NeaFK*^ZCkei1>U5c6&Pbe4lO?e|z86UVHrW`S?_?j2UarWOJsPlkPSD zZV`{iVVG^T;r24WnDbs*+}*au=Dh=m{~g4hURB#4zDW`fws(ijS2DNAE2h^-*T zf>;Y;E{MG#27_1(Vlqo(GljbQ`#}r{u^_~R z5F1(=BSNeQF(bo{5JNI7$uK3umJDMutjRDZ!=4O-GAzn4Da57>qgooPLd*)WE5xu6 z%R)>Gu`R>65bH9`%djuQzzhpBOl)av3^B5$u`3Q){*VGdDgY^grKtg<2$rS_ zj51i7Ixq?WsRX1HkXk^B0jUP09FTfI3IeGJq$HN6CXk|7nyN6$VrlBaC=8@BjM6}A z!zd1H{edq(YDqL23jklBKBHrfQI~S(>^*3TJ652Pqw-c97yhss||_q<--K0{&gV029YyCX!L9L!#rN PW1>=HW20lEV*>sFRXeOf diff --git a/telegramer/include/pytz/zoneinfo/America/Yakutat b/telegramer/include/pytz/zoneinfo/America/Yakutat deleted file mode 100644 index da209f9f0a07625ec83d4ec84917216347f5687f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2305 zcmciCZA{fw0LSqQ0v8C165>IklMf(I*TYrdQk0<(ArcVRn-Ezjc88T zNJX?1>d5*O6;+n3o$?Rme6e1~Bz&P_4xf>+Ka8mxz8=+apHGSScZc{!-^$>=3zQCv@J~7gYYxLG3!aSuN>(PRp0;RDoYfcc)t|t$bE4Ye-auS*^0j z{i`Z=-X}fjbA{)^Vp%fti@5WHSb5jfr=nD6>bu`QF7DYAt(PA-Ant9ysLOV~rB>8_ zq*u1?SLI9I&=uv|RmD%|WM%%Ks62a0R>f}QA~=&5lF*fj65} zZSyW&x35&ym-_XCfeh7;5E&sdL}ZLs zGe~5VRx?awoX9|tks?Dy#)=FU87(qgWW30Lkr5+9M#hW`8W}Y*Y-HS4GjOXJIWlx) z?8xAe(Idl0#*YL5i2xD;BnC(jkSHKwu$nj^fv}oLAfZ5Ffdm7I1`-Y=9!Nlth#(; zY9fn-)@ovl1lMY!i-Z@6FA`uR!bpgb7~?-djtGyu=Ie~Qj_=hX_n4mkFI~PGW@~I& Zb%VErZs*l3b-7(Kucn~DRp64be*u!YOXvUq diff --git a/telegramer/include/pytz/zoneinfo/America/Yellowknife b/telegramer/include/pytz/zoneinfo/America/Yellowknife deleted file mode 100644 index e6afa390e879f97cef351e382ae62daf183e8d77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1966 zcmdtiZ%h<)9LMo5V5v9M-)0yJnrV7M+;N~JkeL;9Azc`rh(wqM-cBn>-OUW~*TOL# z@m)FAX0n_%AGDIxwVE5vHGEPwcW$je3s-4vmJO>-HkIr5{yptckJ|dWyO(2QJo&vp z@s{Maa{0&I>3+h8+v`63f9^HJb3={;m0Z5Y~#Qvde5`s7!wHrCyzPkpr7Hnb*n-QYs|^s0JoOl8~kg&~b^xNPIMW@%H! zIooul#56DXNt#DznoX0V(sFFvJTrD&T6@#x*^z!}Yd>u^5ABfWVi$BvU!UC?I;qbm zlD2)uLG6gv+m34~O$0+WalToTQ)k)a`_;Pr=j(RI;70Spg_-hVs>-}{Vq7|#3QX5P zT3)WWZFY8^mR)(5%@!Ui*nfKnwn&Q8wWip3VaxI(tTXO-+Q{=pp19XNbL6Qb&z(QQlSiIC$J0liKSu&c z21p7>4qlf8k_D0mk_VCqk_nOuk_(ayk_|^XNIo11AsKmHN=Qy#mlTo}M_Nc;NMcB4 zNNPxKNODMaNP0+qNPKOI|lk$UJ%7L?JVUOcgR$$Yde2g-jRzPv$G&hHa(Ww368J RaCtaV5-TeUmxUvNzX6=<+*SYp diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Casey b/telegramer/include/pytz/zoneinfo/Antarctica/Casey deleted file mode 100644 index cbcbe4e339d934f57725542db534641292fd077c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 384 zcmWHE%1kq2zyPd35fBCeULXdsdH%_rY4Ezmrr_Ow>Vx0PIST&HXD38(SvN#2;TMSB zDdG^*E87q|&D9_-`6xqt#R8l9|Ns9pGBGkS12G6PflLE|f(8Z_28LN17&wsGx&{Ue z+6ER3+J=T8MT`(4gaiiy?fVaMBuFR7p&%OMSP%_zFo*^@8bpH}4x&Mh2hku8fM}2> eKs3lBAR6Qu5CC}yL{r66TsA;Y+UXh^asdDb$3#&8 diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Davis b/telegramer/include/pytz/zoneinfo/Antarctica/Davis deleted file mode 100644 index 916f2c25926bf444b7c366110a29a3fafb17fbc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmWHE%1kq2zyK^j5fBCe4j=}xd7jU4VEE>KU*MnnY6h=cA_m^me>wQATxH|3Pj5(I9t# mXpmb#0OTGJ4RRBR2DuAFgWLw9>w#*?aUYir(8YGT=3D^3yE`2K diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/DumontDUrville b/telegramer/include/pytz/zoneinfo/Antarctica/DumontDUrville deleted file mode 100644 index 920ad27e629e350c1baac8537bb639a59fd19039..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iUF~1IS_MS-`;J;~T=DZD_y{Lf9l`pcWA3 z1sd`{Y+Douh%S1&WCF;ndLWyT31|_-=*|xeEMN;bK^6q~LT%x)0orA!YiPg)0AN8Q AvH$=8 diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Macquarie b/telegramer/include/pytz/zoneinfo/Antarctica/Macquarie deleted file mode 100644 index 9e7cc687d76b00d8f112245d5c5d2f20a2a61814..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2260 zcmds%TTGU99LK*e@Qxr9Er)HLbH-)ANRONBcqDnRrq&DjPK8=Cg{)Dbmc)b&8!^Z?WHHDc)0P@x7aE)@ZWL z4i;F#aHJ(RCfQxb1~jKCU~}z)&0Bq3cQ^m2d*L&;QO|8DAktrjB5FbD9d~9oK^&Wv6WkTw5sM~TU`*;19=0MpYggC zBp$Uw?}J(sep+jO?^e-MCow58nE*B08trAw@AWwQBw zaaKMj(ki0Itzz;gd*te6tGsf-sy-UB>Y;vn^z0>Vcwv27imZ;zjzX-}NI&9)uBZco|| zw!P(RYuOsE)~Y|Wqj*d^bHCEAln=E#ZcuFzr_^@+HSHNYr1mdOPkqp;y>Hj)>46U0 z*B!9^N48twK&?HqW4(DoLc=`X(ErTi{LU^fb}3AHrQZ`F-8$d8|NAE0=hLc!GQac6 z_w&)?;QtrXnRM9ajS*%-m>FTFgqag&QkYp`riI;kabRNL%nUI#U~a(VFtY=u=h~Sc zW`eGr8DggB+Lo0+0qw9)Lt(G6AFllM5gj zm~3Fu0ptUd5FjI%l(=?sf=LRH6--(%dBG$GlNlg2nA`x#!DI(W4<56B*nJ|KTU0)Y$yDFkweNup~fiz0y3{r~^}8JU=1nnDs7SQr@G0vI@S4Gb8x4a|Vp flp%x!gMsG#2N@1B5o7^~Cdv{n8=zHox~5zJ1PSA%fg=bv;M3<`l~;Bp6&Pg?QG|tvwhyj zAMJ9$ReW=AkU!LC?{4+IxLf=he^>vZV;WF`h)Mv65!F;auiZ<3IynG({!T0$KW656~-Zu-t&CT*T2 zH}_nSuo6F+{Q96w$rzTY8{6fUSRV~9{Y0k){h+rx9Wvc(fzAjxD>Kea)0xA4GOO2H zBR+1H$UWf__0|@Nt{)@LhP4v2=Dfs~TkmS#5^lCvXHb8F5>UR8kJRXn0gB|`4b zP+gYlFRtmmx_t5l$@hL!^RExeJ>Ng41>d&IiW8=~yI+!f_tnYDmNvcbg%T-zew*H3 zo+V;d>H~R^vMMuQi(&%g!I_D=I`E7PN|efC%&c|jC|Sf>(g4LiBkP#kv`TF zDvuvZ*C$#N^vPYTq-INq);^OaPno}|m&0q+KfT_g$NZkQk?UT@Vc*hRe9v$=4A;Nd-gWDl-1kd7cN zL3)BT1?dXX7NjpoW01}utwDN&G{@HJ4$>Z^KS+a+4k0ZBCSMvi8K@GCelt@tDi_ik&YrQMS6-f73nI{R-~^;W0B4x ztwnl^G#BYE(q5#$wpN3Y4kIl_dWjO zBP~aIjx-(VI?{He??~g3&g1`~^}hTCM90PFy3<@yIZ4jB&e*7&InFp|Y|L!mKLIzD B3|asH diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Palmer b/telegramer/include/pytz/zoneinfo/Antarctica/Palmer deleted file mode 100644 index 3dd85f84ff48fb8553b4a964659872ae1fb7d33d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmd7ROGs2v0Eh8=$4LiGDqN)YveI6Yb##1;=Hui8P0P%YlrV^bNH3xhrKTQc>goQF_|a7(e|BUF=TwG_ zx>lil$<3D+ihdOzxm(8%?o$fREu6T$i?Rp)smh9xwK_kEjyDWlUgIh@@9`* zkuxP$R=tzS39m&;(9$WB_r(sVmYQx1Q zy>ah8C61nuX+@y{pUjRNRzYi?3_kOTocDg6d-H?X^dwg2 zoqR6xZ;a`JrfyN#KB#5rsoH$tjxGvxsp9erx+LzjDowjBOTVx<$ z^f!x&!FpNQXQ{2dxpLd%C{b12rnh%=tLmarz2nGPRpTF*p@xI1Hs+PA%MOdWo{ze| z__%6y9LHnNUfu|=Jty*?FRz#X%Ca11KwDnN8GdQ|9OvyDdoE|ooM)cQzH9kXg|JdZ zhPeag{vCmB&wPxr_Ak;fzsMmESCa^miK|Hk$puLU$p%RW$p_tpIGBw1GNgp$gd~Mz zPSA%fg=bv;M3<`l~;Bp6&Pg?QG|tvwhyj zAMJ9$ReW=AkU!LC?{4+IxLf=he^>vZV;WF`h)Mv65!F;auiZ<3IynG({!T0$KW656~-Zu-t&CT*T2 zH}_nSuo6F+{Q96w$rzTY8{6fUSRV~9{Y0k){h+rx9Wvc(fzAjxD>Kea)0xA4GOO2H zBR+1H$UWf__0|@Nt{)@LhP4v2=Dfs~TkmS#5^lCvXHb8F5>UR8kJRXn0gB|`4b zP+gYlFRtmmx_t5l$@hL!^RExeJ>Ng41>d&IiW8=~yI+!f_tnYDmNvcbg%T-zew*H3 zo+V;d>H~R^vMMuQi(&%g!I_D=I`E7PN|efC%&c|jC|Sf>(g4LiBkP#kv`TF zDvuvZ*C$#N^vPYTq-INq);^OaPno}|m&0q+KfT_g$NZkQk?UT@Vc*hRe9v$=4A;Nd-gWDl-1kd7cN zL3)BT1?dXX7NjpoW01}utwDN&G{@HJ4$>Z^KS+a+4k0ZBCSMvi8K@GCelt@tDi_ik&YrQMS6-f73nI{R-~^;W0B4x ztwnl^G#BYE(q5#$wpN3Y4kIl_dWjO zBP~aIjx-(VI?{He??~g3&g1`~^}hTCM90PFy3<@yIZ4jB&e*7&InFp|Y|L!mKLIzD B3|asH diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Syowa b/telegramer/include/pytz/zoneinfo/Antarctica/Syowa deleted file mode 100644 index 2aea25f8c210369e0b805d0dd5f0e899190c2340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMWHRoZMz{r~^}85tQEw9kO_Xc;iD`1pn}Xd4(agpgno(2)Ni S(?Dk6Hj&E)Xs(^EF&6;xtQkK5 diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Troll b/telegramer/include/pytz/zoneinfo/Antarctica/Troll deleted file mode 100644 index 5e565da2f6b138b70179cb7e72347163ab44b6ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1162 zcmc)IPe_w-9LMoz{(&({z6Ro9ioUZkkLh=Lj(yL5fW5L2U)-OucN2v&@+B}J$nv2JfC;8b1)Wk zR$Y1K35Tn}9PZcDtqnVMp?t0HT`vt=7PYZ{MMC*+G+g>o!b=O%l>0)OGBdJk{;up! zKal3x=Ng$9mzIZjbWb87d&jS6>w)XKFW#qZJK`E`4(tBn7H#)$)AnUoJBmWm@ot?S z{JB*+pZw6SIiDQ5T`1j;zslj%cj>v2kt02Ga&+vA9^3g;j(5*$Z^@+e)uc7%o!0)1 zs{NmD>cGmd4$dU?#D}P!yx*nq*F$pZT8$>A+T`>=iJrMyB}1*%G8`+Gvw=-=uJMsFd zvMaJJvM;hRvNN(ZvNy6hvOBUpvOm%Q(t*{qfb?KBO(0z$Z6JLhjUb&MtsuQ1%^=+% z?I8Ui4Iv#NEg?NwO;bo$NLxr>NMlH6NNY%MNOMScNP9?sNP|d+NQ+31R?{TXrPZ{F w^ocZzbc(c!^olf#bZh+&?fR-s$+fQe4%U_h{gKM@s&cm?;Ex1cdspfE4Vhy68UO$Q diff --git a/telegramer/include/pytz/zoneinfo/Antarctica/Vostok b/telegramer/include/pytz/zoneinfo/Antarctica/Vostok deleted file mode 100644 index 728305305df3d82fca7829ff3e9581583758dbeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMXyGNQAg{{R2~jEpe#AqfmDx&{Ue+6HC}AtaatG~_?XG>|#C OP2{oxnro+P#svVn4Hz*1 diff --git a/telegramer/include/pytz/zoneinfo/Arctic/Longyearbyen b/telegramer/include/pytz/zoneinfo/Arctic/Longyearbyen deleted file mode 100644 index 15a34c3cedb7c9ca519c195f5ec0ce9d8d1885a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2228 zcmdtiUrd#C9LMp4h!iXLF@<9RLLs4r{0IL8nH_C^m}%m*Ry9k7iT-4 zcWA|i+8p2CPPBW&hx3^G@O5K9#*!!t)WmCH>KQ8zjHx8*Ju6N5 zT&06wX;I{xTGZF0n+9Ic;?9>;*87G9ceQHCf#>Yzh6dfzy3Ll}_NXnZUuWgB>n&7P zYPb5A*z)w5wtO_pDq>4i@$qGL`^XHfcigfP10_yE9$h`i(a+$iDv7+e#+{`!nUEO+3q|Yvb*-LwEA~9>h7II*3eO| zd+L(x-W~bcxU^8=TEFhgo~BL3KkNQUJ~d{>TI0|cYMMA|O~>C+^WZ6a;FS(-?(4QK zyWg^{oqO!T=6%+(tHs7ejEjgI{|{Hxg#Z5X`C_KH|FAD1IbyueH&MQe|GfY4=CAi< z!H_RdT+S`THzM3Y_YnFQi}}r+@Zj`%WI3L0J;;KP6(LJP)`TnySrxJ@WL?O@kd+}z zL)L~Y4p|+tJY;>y0+AIWOGMU)EYj1hl3&g;k#!;qMOKO|)zhsNSuC^a9z2vFUPNvF-cJcbx^(Lq&tA`-jWPEAr2B^+M9g0g z&!4*;R<6BMzGvTOCfCA8gD*1GwOMaX#pgiH=E|Ge)#Z#Y@AKBp4`uwh5v~6;s15IL zY2)i&ZF=6NfrlYIHc+q4eU*AV{!3dzYkI=Jq$dx5)1Z4tf*U{O)bfv1>(jTX)`hQf zdTv%i(^)z5VN%*ACgkj^F*!FpBH??3(tdkfBTpXb`R-xuxN%)O8*XYe(yq~+n8y6q zBvz=?i(6so%57^rUnPl;KI#6lA<3~V>3O#(a=###UasqCu~>8+Jn;MBE-QH)Dld6a z`Ucl-W3IIDzg$ikdwut@tHPM{9dl&wdGC(P-r4_|zN={y3U}J`f<2>rjd3|g`X4IT zA6}#cQiE@y2vP+p<7n$3g^)@}DWn!s45@~cL+T*~k%~x3q$W}nsfv_E>N?uONM)q7 zqpgh;N2(*`k^0C2kQE?HK-PdP0$ByJ3}hY1LLBW%kfk`4| diff --git a/telegramer/include/pytz/zoneinfo/Asia/Amman b/telegramer/include/pytz/zoneinfo/Asia/Amman deleted file mode 100644 index 5dcf7e0977ba0faf59f44e2891d2eb39b11ba1f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1853 zcmdVaNoi>O_NFxzP^Ct6|PNui#`}3DnRd^kLe1-q~cAb&W zT`z|2Tb+U%w6nXx*Ci%Zy1ad2xx$yK>sBvcTX)y>%*l-@RZg6 z^gS7HB*Ge4_f-b%RNvs>Q;AyKSW@+TA5T^Et3zxu68Q8?%f~;Ha{VTi{ccEQqe@lSQ>{AMBuigh*JayJ$nqN@T_Jm9 zWoWmqN?#(YYb$llh+P^Czm`W#vLa$^={qJ>Xwar-|427 zcd|JcDO)bMbnEQ5vTc8>c5wdf33J*{$1t8BpWD56X!rM@fA!n94oB1(jw>$F+0o(X zKiCoR>z!}%^8JpitU})W{L1O{|HF~CnHPu3e{!tI!6HX%YYrDVUR!g($Ppulj2ts^ z(8y6Ehm9OJa^T33BZrP0J96;I(IbbC96u5O5&;qd5`(P?0*L|%1Bn9(1c?L*1&IX- z28jj<2Z;v>2#E*@35m(p1cgL}goVU~1cpS0goeb11cyY2gonh31c*e4gown51Ziuc zM8dQ+aUy{tks_fYu_D1D(IVj@@ge~u5hEennwXKGZB5ij*ht(+;7H_1=t%5H@JRGX z_(=T70N9!lK!(89i~%wTwq_KNVX!shfD8mO639>>V}T3?G8)Km7#xfTG9bu^AVXqn z#snD@TQe%iu-KY$K?VjH8DwaXu|Wn086Es@hR0F$7zTe=c0P^i7Fi(TA^cu35g%~OJjSbH0}N_TY@Fh9D6U# zKjx&RVqUkrdoEkQz17>MveG*HOt;<3>-ML&bmIJk?if3xlLs#7&a*dVd(UCrwg0qq z*QRw(*FM?dY0$kj-O~FxC4H_s>3bQGogciCdaz8=Gx0+ICHvTjZQGWs#O1ck)3d1P zx!7CuqWFzJug%9iN=*#$$7ZLMTdBOZyh=TOrIvEG%z5Gd?`z9^W-O8qE>otCX^MWd31F^Gj^zL?-1+m=&28nb*-wjLeKojm(Wqj?9iskIauGfMkHA zfaHKAfn@An73aAPFHEAt@m_AxR-wA!#9bA&DWGIhxdv z+#F4ENOnkiNPb9yNQOv?NRCL7NR~*NNS;WdNTx`tNUn}1StMIWlP;1kk}#4nk}{5% R?f>JPeQhT7L?jmX{RF};8GZl& diff --git a/telegramer/include/pytz/zoneinfo/Asia/Aqtau b/telegramer/include/pytz/zoneinfo/Asia/Aqtau deleted file mode 100644 index e2d0f919541fc9d64f01d8c6128c7e73a94869cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 983 zcmd7QK}ge40LSq+bvmYmCpTT%Ds$?ZuIa|CY3m{$COqN~f+)O$32CRH)FJTD2!ak# zp2UM>bchIrJXz48W9U106ZIXubm?GSWRCT|f0Y8ec%%w zmmUx4@Pj^md8|`k$%XXQOr36xmh`pI58YO~rz7548ToZ2+xLsAFIxAdhbq~WOLr662m4{a!fijrP4bo z(^t%?o1cT`)^?Z5yiKb9^?Eh%G@x!T9H{KQLv^Qc!OWG*Wlu$ARfRph)qkBezT+3Z znzN_ZD@1qL{tY6X6;(nEO5qd2QKF7#k9|J9bA0Ww@BaB@ZrBmgc&~jV?IXq42;t#y z&Jn==?lKXO5PS(SkRYx$3K9m1g9Jh%A)%01NH8QC5)O%n1VkbtA(5C!P$a6W4U5D@ z0=wGCNN6NB5*&$+gh%2d1F#52fD8c{12PC?6v!}K?KqHuxZ05*LqW!Z383H9wzzvsKPExKB$LoA#W>;I7kn_#^-zUy52xKN6`E9&K)d2Lyr z($;5Vdgakwb!G>&ZM<8rjzsiYx?ZnG%i13KsU6{O>ITB%{yCP;gQG&%;#Q&S^H=Hq z@JXVt_vFU*uJpXvl-}Zs+C}Ovj10aH>Fs?-(l1>Zdh3_Wlk+mXb|BdYKjco)=yIh}@%d|NYy9>I)Kwklf>j@? zFYrEKOk$P)C6zU`#^l5VjmZ~$^}HJUT74!bK6~}#nd3vkiT1_qGhv@eZf1Vp@ zLnE<~;GQ-*5*~?<3;-DcG6ZA{$RLnWAj3e$feZv02{IH35vj6}9 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Ashgabat b/telegramer/include/pytz/zoneinfo/Asia/Ashgabat deleted file mode 100644 index 73891af1ee95a4d5d602967fd5a965c8a7ec1327..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 619 zcmci8J4?e*7=Ym~rq$YZaM51krCy^Jp@=OCMLLK$ROk>8#Gz9`a1|YdE^g{Sa1gqQ zLl(D!{s4z>cW`lWaZnf063^ROaBy+)gp)U%gmBK|ZSU3=<=4^wy_z=VdEhd2wszfU zl?L4te<*pINkndPiTBrwZ&&urMM|JM7QLP6-VA(yLD-`~5_4e7eNTJEDmP-;6 z@v2rtWW6Hs?!CKWnU&5rye)5flFcodSuiut55zJ?;6(g1}FJpkPoy+BGN? z7zz#rh=N3cqF_VUC diff --git a/telegramer/include/pytz/zoneinfo/Asia/Ashkhabad b/telegramer/include/pytz/zoneinfo/Asia/Ashkhabad deleted file mode 100644 index 73891af1ee95a4d5d602967fd5a965c8a7ec1327..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 619 zcmci8J4?e*7=Ym~rq$YZaM51krCy^Jp@=OCMLLK$ROk>8#Gz9`a1|YdE^g{Sa1gqQ zLl(D!{s4z>cW`lWaZnf063^ROaBy+)gp)U%gmBK|ZSU3=<=4^wy_z=VdEhd2wszfU zl?L4te<*pINkndPiTBrwZ&&urMM|JM7QLP6-VA(yLD-`~5_4e7eNTJEDmP-;6 z@v2rtWW6Hs?!CKWnU&5rye)5flFcodSuiut55zJ?;6(g1}FJpkPoy+BGN? z7zz#rh=N3cqF_VUC diff --git a/telegramer/include/pytz/zoneinfo/Asia/Atyrau b/telegramer/include/pytz/zoneinfo/Asia/Atyrau deleted file mode 100644 index 8b5153e0545f550c6f9b0da9ce134a8a7a4ebe36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 991 zcmd7QKWGzS9Eb5YH8CM6l0llZX*AW^*u=JH(u7pgKQ6%`!b=Ghq=k2g1;t4$3M~{Q zIJwnHIutDq;*f$)Ds<>5_BXf{HG_+bL+cW1N#EzHlEKBz9GB1K0!PU6tz5r3pHY8( z1%KVmTDd;?qI*TZUJvQ6=m#ef*hoa0du!2eyY=p!WTNN&y?X3zO?qE0NPKl(PCc5D z(@R$+aeGYqW>a#e9Fw!fPU%k{$hp{e83=umq%WkCKYr@-U-!-6{by$I<7b`P+Sch; zoBG1~h8}wSR1eoy^vKLM?w7AsRNBefGiT4dJx6(z zQXXEFe~4gzdy5cA3?4!dB#NsIgTz4sA(4K#-BR+Myt0K?Z}21{n@A c9%Mksh>#&6W5Q+I;=dYHuxN)iF_a4)0w=fGaR2}S diff --git a/telegramer/include/pytz/zoneinfo/Asia/Baghdad b/telegramer/include/pytz/zoneinfo/Asia/Baghdad deleted file mode 100644 index f7162edf93c28193436cc379c0dfe4185e368a42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 983 zcmchVKS-2u9EZQBUWiIXi=KZoQ#&i&8QFj%aO{-8uXUz!?TXmk0bUc0a^v5X~&{rIWzeOIpUKGhrR542_J zqquXk()xN|+n()e`;(A(cl)(tU`9F<&!wyNlXgee^k!L5dd?n7uf%mCl}ZINGXGpz z!BbpWBH{P;tw{Kt*mI2|*&=Z-kazN&=u1kcy=$GQy_tYG838~dfP}z^Ax#?uBnn0tkT@WLFd|`u0*M6@3?mvyIE;800YM^y Ygv5x65fq%TuKs&eIj)VYEfUQ+2Jt=1sQ>@~ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Bahrain b/telegramer/include/pytz/zoneinfo/Asia/Bahrain deleted file mode 100644 index 63188b269d077e29f48a42a03c2a52aefdb61320..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMW}@Y7ramaa($>i_@$&&b5Yz+eyn(rDqpz{0?wWx&AU;~T=D kZD0b##tb1O7z{M;Kge*9X&?(gG*Omt*#NDw(>3M-02;d=O#lD@ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Baku b/telegramer/include/pytz/zoneinfo/Asia/Baku deleted file mode 100644 index a0de74b958e42ade6f93ec0c4bc06ae714d5c606..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1227 zcmdVZPe_w-7{KvoZL>KocCk5^{WDwYinTvBOKs(!q0O4Q6ry5>k_w3xb(8!-B^{)o z@DO?K5Eez9QelUVCIlTa34Ra}6xAsLE7tG()*y-w9ec*R@4I*7h3)w)bMVAaSpFEd zxx&G;nS*oBZQn%un-}HjiolKhs>=D0*YzafU9#P)d@Fh1Vk}fOcPa0mxu#c7UC`B& zXZ4!#V|wk#fL=G8)awt$^@dcPu4%5)8-r!K*8f#+DtoU3PG2_g$(`N&x-?h!Y$;d& z_+4(>;RR3l%tKG4<&H%AE=#QXsx*dAO5?ZF68Dd(_{)B2`f^y5 z2UY7>MD4n{QYD5e)b5K7vZv#-YCF0@+G`h7N83l~bWW>e%~MH!crIP`2huhFNV;E5 z$lkk?lA61(daj(8eN)%f{!>{=kDpV$gCnYMcu@6kKcX_JCUwA@Py<1~8Z8tGHv6(4 z4~O%YN6GRMhpY7GJH=1@rFN06GyJ~wA#sRwrfn{fctXmH&&4Y?bLIEXq;1 zW{xPg5t}vEsr4%J534obsK9zDE9Ne&nX+oix~-a(Q`SydJ!SnA0u%-m3KR|$5)>8` z8WbKBA`~VRDikghGFA;63LOd`3Ly$33MC3B22u>H7-(5FycmdCHOv^OQMfUXqp+jU zqwu2;q%fpVWZ+04$-t6AlYu9Ns8z$1fvQ!*m4Pe+TL!umz6^vZj2S3XI8#VdSW{?I WcvFbeQDfmhVD4@^wW36~AfDmz_sE&#dsBrE^` diff --git a/telegramer/include/pytz/zoneinfo/Asia/Barnaul b/telegramer/include/pytz/zoneinfo/Asia/Barnaul deleted file mode 100644 index 759592a255408caf354d0c4a2e406ee5a1638181..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1221 zcmd7QUr19?9Ki9joNmkr3CcffWtlCT=5f_rP3@E}(M^LGLS+!zAA_P1LVqYUw0emc zQ4kS?IC_e>(4U78I#ke$^blqbA|>@w4;3XrXsq+SHs~RO-a3c7&)wm2Vf%d(yAB-) zia##TuhS+Y_XR^Q&&=K*o+@4BKbl%xloo~V;ex{NulkGLkEeAtmT~xtQE10RTlYdtqe?A<=z+8s{ALG-}y=Ur{Boc?_R5l z6Bks)*kifo>0KGPmzI_3tFr3KdAWA*xUBB&k=4)Q{c_(yRkP=^4jwzA*VXsx^*grf z4ZaQ?3fAb*kFXAV+f{h7L~optYSYN9j!Z06b+?LD{ljV1aBfCzzCNzh{z`u3ikDgR*m!dim`zkxW zeNcUwOvaHjZ*Dttf7^Mk>=(By@2}hGayo@*ALM^d>=oud+3#=*(UlbQv!B@$hdG&h zrYp%k&&1mjA|ONN3Y)8z*9eDM!jbrgshS^>VY0|{c?%Oprp#i}wr1MM#F449m^?Cl zqyVG>qy(e}qzI%6qzt4Eq!6SMq!gqUTT={D4N{J+sRthSY`>hg4^4%0ue2H3cFSA|)aIa`fm4n z23OZ^%ys_pdd)tKvEPK9_INi`ZH~3>lH*HvsuRUMI+C7Y-pgpy9rxcb9oM=gnz_eB zM?G?N(x>WbztYzR2hCt_Sgv2(Y=+wJmf@}wYNVx1kBh8Tv02aS@g*^4!sHJnF0DYt zUB9B^W9v-(#oJ}l&%C; zGcz2cQx2V1vziLz-ecJ&b=7B*R#TIcjF#EZ3P%y~*sGsIz+d zRQB;x;t6*t@9sBc&ej9Ux1mXLDmzVX;0-;u=ymgeXN%5DZZ!D`D|P;@pedZOMhXY= z&4V`=$h@<6nTIZ?$oxp2DmpV(7Q8%N6(9al9uEDk9@*{ah5pN`r22dPX!=>@FZoJ8 z7JF2crbcw>&>6EhX0Kk{_qKWbhdRBav(1!!^NIxauQN+e*2=Pm5+nPT$nsT6J+a9n zD+(5>VA)hzIXz94XZ<4Ozl~QFcP8kH@1p9-(P6#1_kw!r+e>=Q(Vt9Z&qsP~+c{Ge zKB!gI2WH)lZdqTv&s0~2Wy8z{6Dn$!jd8E4n&b+piPo#9Z_Sh1bIVlSK$1Muk)t+! zU8w8#-k};IiTc@^U)6Ja{?MD3-)0&^SM(O&4YSq%g>IVGZ<@UW(mZn7Y@71AY}4&# z`_Mbm($#8O`(Bb4+TT(;Iz#f}_7>H)zf{JK`44>@XVxP7A9gZ*W5zj7Qi?ONED&`3 z{uLZSC-^V6@Ta}Xd%yey*#xo+WE;pnkc}WaLAK&*_kwK3)$Rt_j;q}dvLR$g$d-^j zA)7*W#p|&xWM9a}kewl0bG3UzHs@-0hinhoAF@GYhsYL@JtCV#c8P2g*(b75WT(hh zk-Z|DMRtp97uheeVPwb1mR;?hkxjeWT_f8@_Kj>D**UUxWber4k=-NPNA{020O^3M zZ2{5)SK9=n3rHJ~J|K-iI)StT=>^gZq#H;(kbWQyK{|r81nG&ZZ3@yASKAh(FGypM z&LFKpdV@3v=?>B!q(4Z5kPaa&LVDzCn}l@9)wT)g6VfQ8Q%I|jULnmwx`nh0=@-&4 zq+>|Ske(q;L%QZ_+lKTFX&llyq;*K|kme!XL)wS*4{0FML8OI950NG!U348e?cUQz u?r9{_Nu-rXFOg;<-9*}n|F?d|+lYE{$!T_At~bx?o8!w4F|0LSt7+AT#5rbFqpYEx-jZR)tSfAa4t7$dq6vc?b{B1&2E;HgvTp)iCF z(J?$M_~#H^3c5sp-a&8=UV;t-vd|$BFB#HB7F*wUOp5T*seR0TmVsTC`TjE3vm;Tl z@2mXR-I$io#tU=8cr&|U9}Pdq9jke5RF}W9s<&33)~tWcnJcDod^wv7y}PGsUyiD} z8CTUm=~pN2w<&8ZqE6nZRi_3ItJCd2RYPP$HHN;ZGnJoIIJlh(|M-?V`(aj{TT9sI z7w4_Wl4)O2xeKyPa=~PS=#-c0VsTsXLo)&%>>JZ{KIPxA@GOER{+! zaA5ZY5B_lu$=|0;{&fNYA^N`aaIRbxK_PC9OZ|85PcjF?}WU z73Zr$N+~k`@bLA2EfD~Tz;6%&iGc+1)lrZzNE{>(5(x=~#6p50(U5RRJR~3z5ebRJ zM1mqweRWtQE)p1tjD$vFBf*jANO&YZG5}-*$Pkb*AcH_g;j4#%jKfzC1Q`i36l5&O jV35%u!$HP_3)Bq&f=kD2c>UNLD8P-CHGgFOLTq+To!N|l6gbWNpH-HKl zyxl;meIpn+7#N~67^~5w?UK*{(az6b z8-Qq#8$dM39UvOy7BCHT4~T}kiG`Vk8Rn><3m``Uod$Fi&}lqirwM?Z=7HfnE}%>9krVx=B%^C<%mKD2o_TR1g{TdKmFL z6hU+_dV`QIy&TM23Kb$$o~(mlhq{Deq817Y#(U z{AQglPFHBfX;I%{moDl4qD$jbTG=$NRlyPUd!OpE;`dtq|%3}$s-y4;N-3K&uG^VSfow|CfU)Pk^Xk(~A8-L8z zu(wLW?`JgfJzv%iz0szLkJ5bivqT49Ny~-T(t2Y+*6n*F>(9N=4Qoz2N;~ZH#eV~c4%|HzrvH}&ql;wuay4w zf_^T)+CPqTcz>8zPIir}-%qV;d(*1xOTX;?+@}KX-Lj|bQrFjxWN)dV`kwB}{`=q5 zz{HLWMn9=gS6L2v->R^C+Y0~MvLlCYtQ!Zf?VDd$tZ2Pt57i!6u}a>KZzQe6YQjzy zFImHj^J*lYkfW1vm5N=Iw*r23+xJtBpKO{F^^ZoT_Q9O27{=7bx;eeNY|Ipw%-KTD zxSPwG+0>ws3x>>lfvlK2+p!;ZzN;>@EvbdyFRG6kS+(A5HXTm4>-@K~P2Wx-N{>0M zWhLA~C_}Uh@zNANA<9*UpLd6T=kxh|K}3^d`bq2Oj))E?Md2S#TmQi(C(f^N>d46> zr;ijsDj+408b}eO3Q`8CgA_t4A*GO7NHL^ZOI;4BhZICAA|;WUNKvFJQWmL;6hr!EJ$Z#%XIIr^!v`U`Pj?IXSr#5n@#x|uLk|}K=-#pGy8I==4y|zif-%k z``2;iyrCBIr}1LxMlH>RUUvCRWd{{6H&If#PQe>V40%K@diI|riW5nZK+9zLz?b5# zy+^+If0*`TPqbFdBNLIC$W&ym(@sWaJMDC2K9T^*fTTcjAW4ucNE)Zj!x|DHnUGXS cE+iR}4M~UOLlQb|MkJ;4e>qdO>B@TQ7g9&Lh5!Hn diff --git a/telegramer/include/pytz/zoneinfo/Asia/Chungking b/telegramer/include/pytz/zoneinfo/Asia/Chungking deleted file mode 100644 index 91f6f8bc2e234bafd484146986bdb289082c3588..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 561 zcmbu*zb^w}9LMo*sb7uZVlR)P+QDKF36WCLNNV90hMH8mol%_2lQ6N|X0Wt&|A0ig zYPz+k{ufWWnRwpMAJ9ZRm*-x?E%$l*C$;LT3_WT4b=2NzZC~|=C*8PAnz!SMZcTj$ zt?sL|$L>r!EJ$Z#%XIIr^!v`U`Pj?IXSr#5n@#x|uLk|}K=-#pGy8I==4y|zif-%k z``2;iyrCBIr}1LxMlH>RUUvCRWd{{6H&If#PQe>V40%K@diI|riW5nZK+9zLz?b5# zy+^+If0*`TPqbFdBNLIC$W&ym(@sWaJMDC2K9T^*fTTcjAW4ucNE)Zj!x|DHnUGXS cE+iR}4M~UOLlQb|MkJ;4e>qdO>B@TQ7g9&Lh5!Hn diff --git a/telegramer/include/pytz/zoneinfo/Asia/Colombo b/telegramer/include/pytz/zoneinfo/Asia/Colombo deleted file mode 100644 index 62c64d85dfbdc41ae8e78bc8304ceb35b16b4255..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 372 zcmWHE%1kq2zzaBmvK&Ax(Eudc=kAp`9VMf2W~%mrOQj7KhRYZkd#ogVz%V76u022#^sW35NQNXZ07XEuKE5Fg zzCf&PU}|gtB+Wn=$O<9Bfk6BJhi#jw0-`|<1<@eKf@qL~K{UwGAa{Wr4x&Mh2hku8 XfarRl8mfDO%LeQ%J6%&NV*@S#78_5_ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Dacca b/telegramer/include/pytz/zoneinfo/Asia/Dacca deleted file mode 100644 index b11c92841068c12a5d0102402127ff4537a66899..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 337 zcmWHE%1kq2zzaBlvTQ&s(*Pt+-EP}(hP6=Q%v7TlcUN4R;bnb9z}x1=g!=#g|1&Z% zGqJF;urM%$904k22)e)kWJhN(08Q|XVBiCaCoqUGFeDW)O8EGOFn9p5wt<~zhz06V-;=l}o! diff --git a/telegramer/include/pytz/zoneinfo/Asia/Damascus b/telegramer/include/pytz/zoneinfo/Asia/Damascus deleted file mode 100644 index d9104a7ab8cb13b4c87eb25ff633b03ec12ddd81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2294 zcmdVadrVe!9LMn=AVP>}q$@82QW4|w0FQtL2o@4y5B3Wl92b=zQ4$h#6e1LmD`WBBdXH@7F7-Rln+Z&lxF_T+RLy(>n{zNl*BP|Vln;gAmd%5%kH zxbih)q-lvcnzhXsD}C3z8tu2QWn2)~CNu2uh;POCg_HKg_ygk4kN+^P4}LCg^fVhc z`-M5Vqt6xx(?me+pb^+vYX%iY*i)*1GlLUeu&3t5h>-BljL=0TX6Ww|c3ALE5q92V zOuG~=!rwn;Oh1!rM%0(vGmbQgnZ9R@J4R25IfD(x+<{eMUjMKWRk~0_?-?>;3bu+n zch9jGWH*b2^#|=m-jB@K*nDI0?3HF*aGVi$wbEMh)ARDKi}O_c+1WDTXo5=YuF^?e z1(s)TyG(9xQz;Ga>C`HryfRHM&AF)3QXiM;QJ<=e$S%Ds@KtNs$a#79P_%W=H-4FU zxKDjcfSLH|EkoSjYSOxJp zvS6%46;9iv3(rkht9}WT4<5f=`Of?(i}sJJ)rU{ZhnfeK+t(L@0*>ZW7)fzruwhpAKEx+EB zTTk4k+Rj*ZK5vSL?f$;g^rokb(7=2$keZe-!e$~l&f ztes==$m%(kkF1}g07wNmN`TaWqXZ=4 z2~rcJC`eV1vLJOq3WHR})hP{98>Bc$b&&EP^+5`RR0t^%QX`~DNR^N>A$39ug;WYD zm8(-Lq*$&_wUBZl^+F1UR17H@QZuAzNY#+CA$3Cvhg1$JovTwjq@T$Ir`-Sm diff --git a/telegramer/include/pytz/zoneinfo/Asia/Dhaka b/telegramer/include/pytz/zoneinfo/Asia/Dhaka deleted file mode 100644 index b11c92841068c12a5d0102402127ff4537a66899..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 337 zcmWHE%1kq2zzaBlvTQ&s(*Pt+-EP}(hP6=Q%v7TlcUN4R;bnb9z}x1=g!=#g|1&Z% zGqJF;urM%$904k22)e)kWJhN(08Q|XVBiCaCoqUGFeDW)O8EGOFn9p5wt<~zhz06V-;=l}o! diff --git a/telegramer/include/pytz/zoneinfo/Asia/Dili b/telegramer/include/pytz/zoneinfo/Asia/Dili deleted file mode 100644 index 30943bbd0a8251404c407cde82243294071baf22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmWHE%1kq2zzbM`vdlotGwGSck<&AcK5)M>D6l*{lcE0q|No3kj7&@n45fQOMiewK zurM&xO<>^g@eN_nHn0FXJlkxFunoOW8uKS;^P~_plx8n5JG}UKtuk6 SOaqyL+e9uKpt*LsCR_liP#c>7 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Dushanbe b/telegramer/include/pytz/zoneinfo/Asia/Dushanbe deleted file mode 100644 index 82d85b8c1b387ffc54acf509ac75220465b64d4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmWHE%1kq2zzR5^qLM%w#1>d3+1#*o@p}VN_1Xnu5}O3XIW{VY|9m<@;^mVClD8!V zq|Rh6kls^vKxR$)0ogeb2jqG@56IV89Z<;8KA;#Qb3n<5_kgnXzXK}T?+>U-KRBSq zeer-g%U=ieFYg>QPW1=W|NsA=k(mhsSy+)E8#_A#gF^$zL2dyIEDQ`u1&lx^g(NWW zz(|+~BTxjS#>Y2=LEFF-h|Pi645XET5ePy^@G#Il|3RJx(IAh5%m8^FM1ukVM1ukW zOalV~M1ukYM1ukZM1ukaM1ukbM1ukcM1ukdM1ukeM1ukfM1ukgM1ukhM1ukiM1ukj sM1ukkM1uklM1ukmM1uknOalWGM1ukpMArk|PV+$JvH=FQovtYt0Q1Os`2YX_ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Famagusta b/telegramer/include/pytz/zoneinfo/Asia/Famagusta deleted file mode 100644 index 653b146a60e5e5641a07bfc82f9590dad1ed69f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2028 zcmdtiZ%kEn9LMo5|YK$W8Ab<;+SwjLrfF`|J?@? zbB?*M#@1ZAE`fk>4WB&$86P_ur_n98msd@+9Mx0pL*`@echd% zdmsJYpXkbUu>#|<>v{JMZ?2v0oA1eAew-d0K5)$O_wTop{;qLGe0Rwl*0; z-5#){d8wVecF20M(lN(GCOBim7tC>)=dJN$y3Le?BUZ}24)diupI8%qs5f8!zRn7K z({868-Q}bmjM?d(wNA$NH|&WG<<6wl+4d_HSx#p02zzoc$(a%uw5KKwIMWi7%xU-i z*7TnT%&hBot?Y}}%o!*9tYG&EGpFmgmD~QQnYU%X6{=}A^Q(HDg2+BQT%yj*oXvJ& zT9Y%YpjBoK#br*QOXu{jk)nTEbndsM^6JfZwYaBROU};Kc^wOM{^zOsS}ao+v=_<3 zMTuHk6Oh-Z{HkT8L$WCGik4^IlJdb{WO2fIS=@J7-n?@}-s=5YDz2W>$lgv_a`dn+ zZEO^?bC)h#u}9x-sMX5Z4H~T|*LPBu=<;BeE`Jcx6?WWy@BVaIdHPRXb;B>Kzetv< zlXvC)_&ura>JzK_hOF7~yM8eLjI6D?sx=u0q_(72V=tW2x-`|g-#*j&p(b5-xlPyi zNA<(6R%yefO|oHki8l6B%EtN({ivf*HZ3cbrdWz>&QF&uWq(O?vLP*bS~=NS$a5|Nryho$Qb?^5LC|NYp4RtK=hU%m{_~mCtAR9ua+tqyDdZaoqU}jy!Vg zk)w|se zBrhZ}Br_y6BsWhtIV3wIJtRLQK_o*YMI=WgNhC`oO(ahwQ6y6&RU}tWH(4ZGPd8m8 zUnF59VncO-cvdrvogB!5qL0+1O%rU01(WD<~BK&Ani z2V^3UnLwrjnG0kxklFBbrvsS}WI~V`L8b(m6J%15SwW@+nHOYYkeNZI2ALaVa*)|U zrpMErA7p}%8A7HAnImM9kXb^e37IEkqL7(FrV5!WWU`RiLZ-{poiAjXrVN=g oWYUmXL#7RxH++UB&hIW?P5~PjjD&Lwb3=LIU?e}}eVgh353H8ZHvj+t diff --git a/telegramer/include/pytz/zoneinfo/Asia/Gaza b/telegramer/include/pytz/zoneinfo/Asia/Gaza deleted file mode 100644 index 266981aaf2e79f5ab7f494c21b785b3531c0bc59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2422 zcmd_rYfM*l9LMqBKai`nh-}VOAn^hMaue}V#8oiKqoR|G@I^(k5Gl<((Zyn~nhUhKu86bMZXJZEl_UU8-}qNqv!A?>_m{dH0t`{!m|i9-_MT?^1SiqxyPx zjp}~kTlcB57We6jK-HVHT6(WUTYZ5aNMDcG=lh2}{jKZOg|1r9#aAw?OD#vMJliI_ ze>rg0bGaa24Qw1&b!F~e_u#UIs;d)&+}D!2J=aDu)zDa%HEWaIf z+V6Uc?SHjT0-6%+;Gch&yTb})-0){|@4iYI|Ix2{!j>KO#FHZuA_uL|r{9yXoL2k3 z;y3i9(3MtrL99$xDb|$03wp{oXYHxyyY#eAysU`R{Sw(6Vo(3DOh-NMvZG&{uVYG@ ztr^u3IyU=LJNBHfj$4x@@u?k>kTFdX!y08~cz`6i_QyGbqR_}(Y z`Tb*cc3Y60b7EL8I5bZldMBoSVQIUa`_fST!!tXqMVo#QtMGtcoYyW(G7ecw6B{Hi zr@~q`rduzMD3axa9iA1z$7MxNqbGmhh!nK$@f4ogCq=KP>y<}WS;afINXd&!M2hpJ zw0eT9O4+Md7mUc7kV;)P=ZviNtCR9^$E3V>y*x7frmX8+B#(aotUUHsoKze;tk*vm zER~0!(HqJKrD|8XetdC|eqwcw-WWTeH>FS4Pu|(1H%Dd5W>1^mIyzFe+6VQv{?W3% zrCL{?xF|agzA80u+p=@pcB!pF(=Q7-Q zGGFBVi}{V1qo#d93o=7TGeu;M$Rv?jBGW|XiA)rkDKb@LuE=DO**co(BJ*`L6Gmo? zOc|LoGHGPi$h47pBNInvj!YeyJ2H7>_Ks%y$ow5m0+0+KDd3k%4jfGqkSri+K=Obj z0?7oD3M3auGLUQ_=|J*|6ZAjjb z#5tPGA*n-hha?Zl9+EyJe@Ft63?eB+a)=}m$s&?QB#%fUkxV+8R3f=_G|5D=iKG+B zCz4Pkqex1ToFYj@vWlb?$t#jrB(q3rk=#0(NUj}CvXN{%nsg)iMiP!>97#Enb0q2bKhJuA>3(!P{{fr!EJ$Z#%XIIr^!v`U`Pj?IXSr#5n@#x|uLk|}K=-#pGy8I==4y|zif-%k z``2;iyrCBIr}1LxMlH>RUUvCRWd{{6H&If#PQe>V40%K@diI|riW5nZK+9zLz?b5# zy+^+If0*`TPqbFdBNLIC$W&ym(@sWaJMDC2K9T^*fTTcjAW4ucNE)Zj!x|DHnUGXS cE+iR}4M~UOLlQb|MkJ;4e>qdO>B@TQ7g9&Lh5!Hn diff --git a/telegramer/include/pytz/zoneinfo/Asia/Hebron b/telegramer/include/pytz/zoneinfo/Asia/Hebron deleted file mode 100644 index 0078bf01c3db4a013a599ca995fbb72b81460a47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2450 zcmd_rdrXye9LMqB2?PXd5!sAXAn}3%au+W}Tm=(+RCIC?zKTc|>M6||ASX4u;B>CG zWLdgNET&tObFMk7=_*r;CzM*{*3Ul@64aA;3CigAeq8m3{?q!i=h=B)56{lw560(x z3X7L5O;mRrf#wM>j$P)({ZNm&oG5BjC;FS!7pc2Fr+@19e0ksx_0{KLs%^(+Ww$h` zuea2x_E*34oT+T}oUIO0T`3jPbuGra81$iB>=3)Vr{CLiXt}!7R`0$1##MEt^`P6k zezfP8ofo`Ui;Gn6s!;c}soOl)=k0Od7#ZxjnbPjPIgqXThWWYs&Yx8MeUrVvjm@wE zY8!OGtyX*Rjf)c4oM?yq{JT6DQ6i!JpUETJSIF>>e$^w^*4iUa4@j6aTH%}Cmx#PW z_M>HQ>rvqgt;pgy8LiT+F+rE~m~SrFW4qh*xKDhnsIxsXz9r0_aHLX4zv8lE-kh#u z%Ui68HBmY)_ftEr(_hCg$&rNgW0IIPPLd*;BsnrrQe0bQ(nE_R)i29R9jMcjuZTV6 z$8tTjEyJGnak_r&y-54nkD;zTUO3}-|3vL zmG0?1!*uQuKP&H4zn-yonmoDxoSwPpsGa|M-|m9sV^%?FkS^GA&YHFQ2eC?a>e+=y zWlq*!Yi`mWDa@<3<_&Gv^P@^-{`F(t1tBM8K}VCfsP}*rAKL0IIkR0#-^$br4=%RK zHm;TO*XD?n70IHS5wbXKo31DxkR@R&bmf%uvNWJUszQ%TRo8NPs{b8Xc4C%1{ryYw z%)9YYeSDu@{&I+{*!QAdS#@39o2&G*vxD_>6?uA9T(4f8IYB>ve}`TZohxg+hxNK4 z<7J)QsMq%lkqxaiy5`hnscqaPb^C4ExPF7wH+&#Iu6zDDeSPk7`VBIte}I1g-g)}? z4tCDFee^#(Q!2dE{OhanaV}q_hKH-ce_d}sG1q&YC&A8p{=Vm(GxG~ocJ^E@g(@%0 zyv)6Fx%ho9!|glsL*c&|;($5oj?Q=(8Kk2bB{EE8oX9|tks?Dy#)=FU87(qgWW30L z9nFZ5Av>BeBZEdpjSL$ZH!^T!=Mvn|189x#LM-u@g1db*KNDz=H@JA&K zjwTLBAdpBPp+I7R1Otf%5)LFDNI;N?AR$3wf&>ML3KAA1E=XV;O=OVJAhAJ$gG2`j z4-y|FKuCm;5Fs&gG(ke5goFu+6A~ySQb?$fSRui3G|@uBg~SU97!olgWJt`ApdnF1 z!iK~R2^pZ98K_$=po@l;)etfi69a}B!);39ZeJ+O&F0lB7sCAiG&h~B@#>| znn*YuO+1l+I+}_OxNMMo3BB4cMiv$;mE)rfOzDR%_O@xsUBQZvT zj6@j;GZJSc&`6|_P#YAnMuLq*8wocOZzSN3CgMoQ9Zk%Upd(R7!j8lp2|N;c{NIN@ f(0mibB=DUOou3$+6q_`0QbKfoe4KNekR13Y1KJ9C diff --git a/telegramer/include/pytz/zoneinfo/Asia/Ho_Chi_Minh b/telegramer/include/pytz/zoneinfo/Asia/Ho_Chi_Minh deleted file mode 100644 index e2934e371b6d5cf80244d8d55594f7094bc9cbb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmWHE%1kq2zzev5vTQ&s+R@|OFmbUq*ICnN4HqNN6@gplAYppE}QP6N>(=YeRD6G1e{nIIbER1ghvE{Fy>8AOAe4FVvigFrn{ Qj0(=@vH^O=PS>0Z0O4(7+W-In diff --git a/telegramer/include/pytz/zoneinfo/Asia/Hong_Kong b/telegramer/include/pytz/zoneinfo/Asia/Hong_Kong deleted file mode 100644 index 23d0375fba3377a3d513d849c0d29c82ad2add64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1203 zcmd7QU1-f=0LSs?e_rh{a>4}{MD&!#u^Z16wn@Wr?82!@F;a5cyrs<5X_8uzPOZ35 zlB;KwMCrM5X)}@6?btZ;wsRV0UUrThHplb*N4apbJI}v!K2QJ4|L+^$p4eL{{&|AG z->$cEpK~&?C)FKW5$W!4kKBzOKKAHhCiS?fxAjT;HuJRhn(8^S%Jiwm)Q!q-i<4?>_HJ49?^4D5I{AIDR{h8{>hb$K&BU!5{qt(IDP35k#hHc1 zN&2;BQ0$sAK(5XV%%W`T@hnjZ#w48aqN`((L zYscgDIUaw&KAu013GDw1@PE zH0WwOL|R08M4CjpM7K>Y^vSV}0-YkQBE2HbBHbeGBK^ABhLMh4ZOcf{NYhByNZa_o L^&PV1SE$f0t#TGS diff --git a/telegramer/include/pytz/zoneinfo/Asia/Hovd b/telegramer/include/pytz/zoneinfo/Asia/Hovd deleted file mode 100644 index 4cb800a9187901afa985f1ae67565d654021cabc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 891 zcmcK2yDvjw7=ZDwR3b{8MdDVi`=#nm1+Dvn4wBPKh!7EROLXYy@(m)0;TyW?aE68s zhBH`*MS{sjEJh5($R?@t_yZ&cZ}a6hG-;amdG*Pqg% zgDQA*h{D?`Rdf-M#V3B{jJsvYflIm8)1-UVrb?%tW!dPHD(}9L6`pHVS(KDjj$>7A zzZTUWS6a>Uv8cU2((11Egy(cut52*5Z){m>*ba)u=$O_t-zb`=7gbAeRJQs@RGZf; z+nx2QBPUmOy`5N;znTm(Cm$F8h!H{X1_jb3=GYfgKY!GP({Bv?3{6j z@{(paV@-xX_sxlyak-UBrEKYGfBd8i5qf7)`Yh{+NL!{5a}kj}7Sx5Osl$4m*7FJd z4=c6)Ns6`dyQ~&jFS25WH6yD=){U$jSv#_NWc^42BmdHJCYvBkDLH<2FNKO=YX8V VkJecrr-A=`9$7uskp#THtZ(&(y9)pS diff --git a/telegramer/include/pytz/zoneinfo/Asia/Irkutsk b/telegramer/include/pytz/zoneinfo/Asia/Irkutsk deleted file mode 100644 index 4dcbbb7ea21e193cd2e2578a17bc955caea227a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1243 zcmdVZPe_wt9Ki8sxz)13A)40Io>E(Dt=24?I{(zNvE~lNh!`1ISqL3QL>NepOu9sL z$PUusN9HX$X@m$Ocqovdj0y?*d+8De5k%Q~zOM~Nbm`dp^1h#U&&CUTf8RHm>Nyou zf8AF9UX2*JF3FhF?c$=T@sT-Ksi!NmrhI6j%sHgWzCB8pe|(r(JO5*0-E1P`o=i!_ z-MCcV3QN^hzpU@85`CsfHXK#5G5%Sqo8C!{Z%%66GqP#bxOnVuwdc!Iz4^nFfVcNT z!24`WZzymwf1_7#yWFFK7{{XyORx^;L$w{=hJJp<2WZ~KUjoOmGn zY6o?DWLWk)PU~oOpF}?mNUY$P#2$Ccf%k0^zZaE`*-o9f=9Ogpg{wcC&DxgP3zl}r z^1OY8Gw(zG7k^%_bU2jinl?|Avt6mIhH>Q9m8&C> zR55e-0=v!8V2%*CRm!%sCI4Z>=AUH`-@nv~j2am?w`1hU*pbm&n(-qEAQ>PjAUPmO zAXy-3AbB8(AekVkAh{sPAlV@4Ao*CDgpiDolq^k7NK!~vNLolbc_B#vZ`q>lZjf&VD?!mxP(TKu8HUwI%CVE_OC diff --git a/telegramer/include/pytz/zoneinfo/Asia/Istanbul b/telegramer/include/pytz/zoneinfo/Asia/Istanbul deleted file mode 100644 index 508446bb6aee2841ab88e82607a6ad6e748e7db7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1947 zcmd_qZ%9>V0LSs?`nP+{bz_9Pc+<9~_m!HcdDyFf5t&8ib5V%gGPe4 zDT7wf8GBJA#8%G0%Gwr#Q7fV`boS>^w&_xK##j)+dz&KFpX24? zi_7!<#nx`!TqvHq()1_XTs!s6b0DtYtbL=0+9zk2?YA!2M>jP($98{cbkxSpFUrr_ zojD=rc&Nqh3Wv?E-yYc~#`ZcVFBcd+d&-?&&&x*d@=^20YjLBmuiLz_&1>8|T4MHB zCK$J(>CR8HcH2MajW`3|4fbHdL38kKx&6z%ubtaB)*D0pwmJM&neo^CZ_WD+BgW{K z6!SrL#7=4YRHl|3mZ{_K3vZxVdhI^p8+%u#efc;(d9XsJchtpS>O3WhR!&dcWf2>VMI-DFOXULcc|>J%F@nCx#(P$Ec+-&mbVL8 zu{l*PZVRd03~S5>X%Ehh5vi}YR0f7O>qUr(RhKSb;W4!HhJChEWG89)v?a@bwvt0(2&@W;P_pM4hhdy$Hzs0t~x?4LPTOjf<&T3!bIY9)qx_By6RAoSdn0n zXpwM{c#(jSh>?(yn3157sFARdxLtMNNaRT9NbE@PNc2efNc_kEAR~Yb0Wt>2ARwcF z3_)KLRyMcD F_XJx;ZRG#} diff --git a/telegramer/include/pytz/zoneinfo/Asia/Jakarta b/telegramer/include/pytz/zoneinfo/Asia/Jakarta deleted file mode 100644 index 5baa3a8f2ed24f68c49bd7e3ccd4772a8e734414..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 355 zcmWHE%1kq2zzaBlvg|;t&;TS>Rv`g&Zj0+JvKYZD`D1(s+DBS!4q_J)SNV;_f0|%JR=i?i~ nplx6Y!o~&+;hrH3AtYGx9|%AefoPCrAev+gxq!CnT5)}0ASznEwjORPnc1y=ZvwV@dUv>5msW0ETB3++HNcY~I;%{k^ zuXfeR$rpRo*X6CMr!rW23q7j$T!DNW_nA5sUM8n!UX;^AemT>YBz+&I$ywti`L3-? zooo778TB>teSV$%&|aZ_OevM08Z%Y@b<1R++LZGlC)Hr|VKq3oRa~|8hU;>xKXB|9 zEvPxsKkMf|wAoQb+MJ0K+Kqdvw6OL|#@r`r{o$QgwFvEXZ{&`nT2$8i{+mh~jhiEj zz4Hn^+AXrg8y!4iM1M2jzjdhFxb2fE-k6?YZGKCHf5EYGBlZP{f8oAWhP$-I8@DaS z@MIqM$7jB3BrLA-CdR*~CC$0wPY!9&l7DUXE(&m|McocPb*xm~-ZyUC;UCfOJW`}B ze&djycIaL0uAMvdCA9~QrJGaqW!@$&J$tV#&&<_V#BP#|@G*1c^gLNPTxPBsPL|B` zqk2~7^|HEszj^oDn^MBv23PNMdf}VA1?P#!B*tT31^^BPJAJC+@t=lZAOPA7Dj+$EWJXu%Qr9Y77 zl(OPh{lTz7DNlP@uW$ya^>eHB_5B0tq09ODhT|RT;j^jwBX7Q?Do^;#jnC{>Rm~~p zqvcgffA$7*)0%pDtbD@U?A|EV8GYvCGjgTIJ+9YWh?gxh&gfgZX33KmKGL@y9+hof z2ld+5LR8(6r}d|5epB1`l<5xVHB%-(0h6zjZ&RI9ozq;?T=?I7M53J5|GGq8k^qtU z9*0XLEK;V6q%L*L{QEDHf6dPE$!hD#T46nTXuXIy91gzzdJ*yci^W~FF8_m1Cy?bK z>$9~Mh^!D$SBOjI0=0GO}i5(a5Ti zWh3iG7LKeOSvs%T3>*pwdtyKY}1V{~#A|O>j%7D}XDFjjpq!dUkkYXU! zK+1vC11Shn5u_xxR!xwiAXP!ig46{m3{n}SG)Qfb;vm&Q%7fGgDG*X2q(rt>jgTVQ zT2(^IgwzQs6jCXqR7kCmVjQb${>kVqwwQX;iPipfz;q?{b}L<-7LQKY0C zHARZbQB|a@NL_8M!XlMLN{iGMDK1i7q`XLdkpd$XMoP?4W2DF&RYuCpQDWSv ywMxxVYoyp5)keyV)Eg-{{y!@oWEFm4f|dH%oJ4n$J1H(9B{nDC<4$&ag8l@yuiuRT diff --git a/telegramer/include/pytz/zoneinfo/Asia/Kabul b/telegramer/include/pytz/zoneinfo/Asia/Kabul deleted file mode 100644 index d19b9bd51d701d8bfe7b3e95c9f6f3c736a9aeee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208 zcmWHE%1kq2zzdjxvLMXY03;MIs_6H6$BL& zNYsit^lwDO4iyv&JdEfd;UNgD;0sC6C3dKT6|CR;S;B%Y-Fn9MdiFd!JnZw1?>MkO zWc~G+_xozEk@H3Ow_nLVxt*M>@cJT4s}ku-&#v*xnajbd&zBJNnmb!HFxI23XM(!+Sc3-lE>=4cmeA2^Y3r_+b=!W(`nnQnw?9bx z&uQtXnbD5-FJ;5`ce*i?lFq3Y+I2Up-Oq1n`0PFHNuJWkzH{1p`lf6eIH-MlPD+1s zOb7aQ%jU9H9c=2C!7ou6Ds7RW*8$n`(Ie4E3nexY$_-!49~sT(^RCj8!pmJ&^qMob z=u7d9KhMq2+?F+#;g3zGEpz^K(&e$N)VQ@^_H*`%%Umh^hP$!hxaGHP^KLWm!eB0r z_&@At9zV~9$c{V)TOxZRn>w0Zk!_KEk&Tg^k*$%vk FzW|Yw7d`+0 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Karachi b/telegramer/include/pytz/zoneinfo/Asia/Karachi deleted file mode 100644 index ba65c0e8d31cb1d59935878af29baac722f45936..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 379 zcmWHE%1kq2zzev6vTQ&s+gbN#$(gA}D=w7?&A7Yb!3rk-)dn`}T{qay*I3};xWK@Z z+kJuOle-39Z>kj-nHZT_S-_Bi!R`*o0^bM*76yjs3`Q;n2DbnP0S1PU1V#}sThhlj zghAWD)Yt$>nt@4Eh5+y25Fio45JG|*{{sQYogf}v1BeFs155+`0-~wyA1>Kf4i diff --git a/telegramer/include/pytz/zoneinfo/Asia/Kathmandu b/telegramer/include/pytz/zoneinfo/Asia/Kathmandu deleted file mode 100644 index a5d510753f02d5dedcf1ffac153558466dca9b99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmWHE%1kq2zzdjxvLGz5@KbGzoP@!O`v3p`GcqwTF!=ugY4nX?U}0bgxWmBZ;~T=D qZD49_03uCH8A3=f9BAf$kOd$UL6(4MqAlXG0b6LNYiea;$^`(OKO|oO diff --git a/telegramer/include/pytz/zoneinfo/Asia/Katmandu b/telegramer/include/pytz/zoneinfo/Asia/Katmandu deleted file mode 100644 index a5d510753f02d5dedcf1ffac153558466dca9b99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmWHE%1kq2zzdjxvLGz5@KbGzoP@!O`v3p`GcqwTF!=ugY4nX?U}0bgxWmBZ;~T=D qZD49_03uCH8A3=f9BAf$kOd$UL6(4MqAlXG0b6LNYiea;$^`(OKO|oO diff --git a/telegramer/include/pytz/zoneinfo/Asia/Khandyga b/telegramer/include/pytz/zoneinfo/Asia/Khandyga deleted file mode 100644 index 72bea64ba83546e72ff9d6a4f9a9e64cc2d93b47..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1271 zcmdVZPe_wt9Ki8sy6K!5&mq%V=E|*QQ`=U_mQK@YGB+b?rz8;iYekIcP!R5g{@6j{ zIRwG*P?U9%@K7C=xAag*P`k)csW%2C7ar`u)Mf=t~2GFPN*%D z5mh(dqPC9JtNLqFxh`1Lw!u}kJ-(zG+UAwpH>*6(7ivf46Xmsjklybza%b+9tMSTt zSL6I+xohU0^gX*Rn;u=2&3DhqmK%d|_vM82pNz=B@v|y;DXR8F`qkbe9<{HoNwo&c zRqL4{NP^eJ2P{>f&Q0P$jPzX^NnbnjioG7HsYE~3l6kZf! z6lN4^6mAr96m}GP6n+$f6owRvW;I6&Nwb(HwUC1_{j!ssI20 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Kolkata b/telegramer/include/pytz/zoneinfo/Asia/Kolkata deleted file mode 100644 index 0014046d29a38e9b8006f746fea794d7f71eb479..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285 zcmWHE%1kq2zzf)bvMfL>)Bq&f=kD2c>UNLD8P-CHGgFOLTq+To!N|l6gbWNpH-HKl zyxl;meIpn+7#N~67^~5w?UK*{(az6b z8-Qq#8$dM39UvOy7BCHT4~T}kiG`Vk8Rn><3m``Uod$Fi&}lqirwM?Z=7HfnE}%JssI_(gPus;<25hEe#4|NC>vVw?M zRF~)yj_45alj;(lDx!lFMASi|BnvuZ5nUu=J>S;`5?#9WyuACod)}89w%<3k{a{zb z{B?PM?>1VwF1|52``i7&vA~?r;q=_nj4AO97MD!E?l1lHDm`!1YvzyaNc)Dnb;0dU z^=TAFk z&YJdR`i(!YJ#M!#+lu&iQ+tfP?(KJZjM>|33Ui;i9hcq7J!9-MxqVaE-jOn4iP|$} z&l+B4TvIFc4un?k#Qp|-yM3797R2*$CNHRz^jy4@6A0#0pBP1mxCnPB(DFC;M}GbA-6H%FTslAWVX z56KTn5Xlfp5y=rr63G%t6Uh@v6v-4x70DGz7RlDpriTV#D@XZGbF|`urM$r6@ZLLS777;kqkgGvw}g0fuU{!g9HOZK?8$~ zk8cP=Fc51Sn1is90f;mPk(NMg!4N`%LxJx2UuPD&3Pgh(45C4f2GJmggJ_WBK{Ut% cAR6Qekb6NM0nzn9F`9XX%LeErJ6#Jd04>j8q5uE@ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Kuching b/telegramer/include/pytz/zoneinfo/Asia/Kuching deleted file mode 100644 index c86750cb7d512966b1a8b19cb44531a618b00930..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 483 zcmWHE%1kq2zzc+dvTQ&sv9^nC!mf1pj{Vi@4f{V!2ONxD({S+Wf`G$z4;T(_yL{oO zf^fsp8SDYa8QmF-@%RkG$>{wTP96E*aN6c`!0Cw*43|7|Kh*#K|DTbW2?eq+ zGcz!x7l0g+S;4@ p>mVBBeGm-_0uT)f1~3f_3J?tn4v?Sgfov)V36~8pi0pJNxB$;9wBrB( diff --git a/telegramer/include/pytz/zoneinfo/Asia/Kuwait b/telegramer/include/pytz/zoneinfo/Asia/Kuwait deleted file mode 100644 index 2aea25f8c210369e0b805d0dd5f0e899190c2340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMWHRoZMz{r~^}85tQEw9kO_Xc;iD`1pn}Xd4(agpgno(2)Ni S(?Dk6Hj&E)Xs(^EF&6;xtQkK5 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Macao b/telegramer/include/pytz/zoneinfo/Asia/Macao deleted file mode 100644 index cac65063d0dbf48e37c547fba3b67f34110d5a90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1227 zcmd7RPe>F|0LSq+?!R0$N(#IcMKK|v1<`m2O@$@W40a(XO1(se)e!wZEJXxCBAxUQ zMIC~%s6$abB0QF4iDlZdnc5$B(b{~O+N$ODzC+zS)UA22pU1#n_V?Y$&W63~#k^18 z_uG(``;v1FyP7*gM^)$e)1hax54S(>nXBu%e7*Z+{VBWWMB08;eoOUky=nIysZy^? z!zxx(sbc9FJD%5T$7B8Wo74llU$)zCyGGT(wYVC*e@_jad8FR8w5s#__jpo?#7g9Of%&$A{ z%JK8X=C`tLYhqu%Il26fH5HmLzpsi~>80U6eU&w3vH+rnV>Vz3MyUEJ-cbM5zPpn|#hMCjX zX5~I^G8bPevGNZ!I)*1pAH(aL=NSIKK0@q2%+GG07G5Emt{Mx3h+Y)Ad>V`Xyysq3 z9T64bh$viFEDDQDL`7wUFABpDk$>1UqaSufwsqC}A{!$+BU>YTBby_;BikeUBMl%O zxat;=9$a-3NEb*ONFPWeNGC`uNH0h;NH<73NIytJNJp-^C8Q@;-4xOl(iYMe(iqYi z(i+km(j3wq(jL+u(jd|y(jw9$(xj{I5@{3Z6KNFb6x~+2&?}E_7U&jf7wH#i80i>k h8R^+oH;r`ds@q2TMjA&tM_R}Kt@oh*AFQ>4zW`b`JQ@H1 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Macau b/telegramer/include/pytz/zoneinfo/Asia/Macau deleted file mode 100644 index cac65063d0dbf48e37c547fba3b67f34110d5a90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1227 zcmd7RPe>F|0LSq+?!R0$N(#IcMKK|v1<`m2O@$@W40a(XO1(se)e!wZEJXxCBAxUQ zMIC~%s6$abB0QF4iDlZdnc5$B(b{~O+N$ODzC+zS)UA22pU1#n_V?Y$&W63~#k^18 z_uG(``;v1FyP7*gM^)$e)1hax54S(>nXBu%e7*Z+{VBWWMB08;eoOUky=nIysZy^? z!zxx(sbc9FJD%5T$7B8Wo74llU$)zCyGGT(wYVC*e@_jad8FR8w5s#__jpo?#7g9Of%&$A{ z%JK8X=C`tLYhqu%Il26fH5HmLzpsi~>80U6eU&w3vH+rnV>Vz3MyUEJ-cbM5zPpn|#hMCjX zX5~I^G8bPevGNZ!I)*1pAH(aL=NSIKK0@q2%+GG07G5Emt{Mx3h+Y)Ad>V`Xyysq3 z9T64bh$viFEDDQDL`7wUFABpDk$>1UqaSufwsqC}A{!$+BU>YTBby_;BikeUBMl%O zxat;=9$a-3NEb*ONFPWeNGC`uNH0h;NH<73NIytJNJp-^C8Q@;-4xOl(iYMe(iqYi z(i+km(j3wq(jL+u(jd|y(jw9$(xj{I5@{3Z6KNFb6x~+2&?}E_7U&jf7wH#i80i>k h8R^+oH;r`ds@q2TMjA&tM_R}Kt@oh*AFQ>4zW`b`JQ@H1 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Magadan b/telegramer/include/pytz/zoneinfo/Asia/Magadan deleted file mode 100644 index b4fcac18e3540f029f01bbf2751045b3983d96fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1222 zcmd7QPe_wt9Ki8sZMLNzB*;Ilm9}izQnzKx+FaH$v28SD5LrRcAE;e^LusOLRm05HJ zc|iBXMGjR&b+5NydcPi%K6{7sy^YA>kF}C`=8@!VNT)`Ml1bzjkGe=MV@PL6Ye;WMbC#w% zq&-X1AJQPwA<`n!Bhn<&CDJC+C(*v$ zbrTqPz-$2@-w+00Al5dp0AfpqaL*7&h7c00{0{^mYe6)~Y7h;w9z;{%1TK(6bS=05 Dzv?@= diff --git a/telegramer/include/pytz/zoneinfo/Asia/Manila b/telegramer/include/pytz/zoneinfo/Asia/Manila deleted file mode 100644 index f4f4b04efa2b6a442d4072b3899f4dae69bdd771..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328 zcmWHE%1kq2zzaBmvaCQX&;TS3&iT@CNX($%^rz+Xf0}q7l;~T;d-~zXJlkxFunoOW8uKS;^P~_plx8n5JG}UKtuk6 SOaqyL+e9uKpt*LsCR_liP#c>7 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Nicosia b/telegramer/include/pytz/zoneinfo/Asia/Nicosia deleted file mode 100644 index f7f10ab7665e94ca44fd8cd98a362cd4b304eff1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2002 zcmdVaZAeuI9LMqNOjy@ye{0UQ>qF(rhpu|nY}s_Jnc9QbPV-i-GEb#fYtAi8r(5m5 z$Yg}XzY-#9P-GGju7RsTPxL@E2zOwMF+w`6v5iOxDx!vL=X;=6dll@>&gI_E<)ZKY z-(P6e#&DkJUr&tl3vZr?^XB{bW1l8}H+J}I+dH(^ihWjRkGpWq7~flHPS|zFdZp86 zO6yW9Zo{ZKvC1|k1t;6D=3h4AQ!kmXP3kogqK}#h54()l@9s1w|JZ1}aiziZo$Is` zPwudj4u!4c?s_|A+d^wfQ@K5LO{O)iBEwEC8fU%fkF}@!MywgJ!**IstdaKEYo`A; zY-Id&-^{%FgE4bp(De6yV`TN5GP67P897_`nt{4jBe$mC&I|6b@{84;m9@nxNNTZX z=e5i1(TL3P_2`_TbyE0Oo6bF7B5&WS)}p>zEj~L}-|3pK^A0BJyWv!w-&rW{mBnaD zolh1_|3gblMx`v~do54BE#)J>%cAH@vS{$SEWUeGmh_*HiW?U-xVu{_Pae^w&COzT z@6cr{cj^00^;-2-lZGnFb$LRiuJC8*iYEcBjxUqypC{@EkJDw<=|{TyrdQS+j+2^! z`?5CjP-=Sy#jL$4>$cz1_4CfihMF5%mvTVri~BYF^0(TMq}uT3er+6W(T&$Tbkk5s zKRmu#o33q^kG?F{=DsTVxG_aP=_-)T%Zj8WoFH3rlVxk^Q)!L!NLx<4wmtY&+9y2G zcI&EijQpaXo$8a%2hZxZ1DADs|5y4&N3TY9NA#tr7kfpI`A=USPs&1WF*6V~#^Xtx z;u-t=lV2)=Ax~*(6(1q~Dk{qT2))2<|Lr{7H~-F!BX^G6I&$yG%_Db@+&*&uNCQX* zNDD|0NE1jGNE@zBA4nreCrB$uFGw>;H%L23KS)DJM@UOZPe@ZpS4dk(U#?DLNM}fE zNN-4UNOwqkNPkF!NQX#^NRLR9NS8>PNT04wqe!Q&POC_-NV7<{NV`bCNW)0SNXtmi zNYhByNZUx?NaIN7u1@Pn@2*bsNcTwlNdL$NAUl9;0kQ|kCLp_jYy+|n$VMPL;p%J! zvKOw-W+1zPYzML*$c7+0f@}%0C&;ECyMk;BvM4YD_`&gLMygKQ77Kgb3l zJA`ZzvPZ}!A-jZZ6S7apMj<Z91c0qO^C*L2;4Y=QCdH(?jq|NOB diff --git a/telegramer/include/pytz/zoneinfo/Asia/Novokuznetsk b/telegramer/include/pytz/zoneinfo/Asia/Novokuznetsk deleted file mode 100644 index d983276119c95872882589a9fdd829eb1f86f606..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1165 zcmd7QPe_w-9LMqBa+Wy>FXd{jmdln->)Wcin%b5v^IuS`gZ-iCj~EC^f2bf($O-T<4SkR?g&)8nio@b1)&pW;SV0Xm) z^_cnlYSPMi`7LiC_h4u&FgtW8Gsl}X6(vLE72n?sct5?)%pLWZc_TYAzTqC7f4fWl z*V}Z#g}5#}RjYwszb@K2ODo&+8jOF{#o;ln^1ak0p2r$;f059)_p)^SU84HLxkUA= zM^f|To`mmbr8aw2>aLuXWrN3M`OzMc{avzR*PuoY@79%#-MVUPQdj#oX*3el=+7FB z`4SR)Tc&G%RLa`n35}1srQwE08Xta?rZb;q-L+?uIPgM}r^j`D>aH~Jd#W3%E=#KU zrfw`gCN079+A?uTTZ?+N_4yHP`>X|HKA(3LO`CkUiwhppXB50C zyz%F`{g~UBEjj+!^d4i+`vzPk#_a7gWmB(HS6ueW2;3q>O&Dgq6YULr>M z5D|oI^b~QRKMyhJP(6+wLhNBkNxk$^QND!LI^SzT4?*YEIG^pwmKDDxMky;fisG7(pRU4RAb>5e1b=foJcYd_|GjFUl?_SILvzKN4 z#8YeS^ZQodLBJDq)%=hGlSQV>TbM91WfqgRHPc2Wj!d1!t6r69G~nqrV@kaBEIJxD=FMYg6S7BwM7Aypw|A$1{zA(bJe zA+;gJA=TNM@{sy$O@T;-NQp>|NRddDNSR2TNTEojNU2DzNU=z@NV!P8wx(dDVp~%( eQZrICQZ-UGQa4gK4w)wY-^$%Bruw$VX7^8#1sDDR diff --git a/telegramer/include/pytz/zoneinfo/Asia/Omsk b/telegramer/include/pytz/zoneinfo/Asia/Omsk deleted file mode 100644 index b29b7693118fb1ed214adb8a4e686172d0914b2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207 zcmd7QUr1A77=ZD!oHf*71f_FYmf5mp9apa9zuwd-%UL5vL>NR?A{0c3R1^~CT||UI zM9{?ODuS%M2%$IVDkKV|8xex+qKg#Dn=o77b8Il8o9;S?v){Mx91d*nlRVOMDrEij z$p5|DWMu6fej;PfM&|6*fy=2iMWfzA&*QSf@1JfIeV$4ArhMMwiJp{yyiZFWc5CU~ z!@BlbOxIni)w0t;UEf)v8{#XqyzZxN49;nV|ATJIf2RTWq6Fq=W%JCJMCJKgiOR{h zvgOrF2|gW_t@lTyYG_EdT^*F|y?tVz=$7iEH#KzbjP7XY)tv_qXie!M4Tq{Vycp4l zf4@Ygi*@&cEw$r6v~FsZ)Iab^!}ED*99)n+cixMfoR+-bX<>Wvi+OgpH8P;Im?z#cW&0Xd_~rm z>>GcsJ#M#UwY=uvZB1Baef+G;V_C^#Rzc=7v*R*5nP)8XOlIFIh<7Bdpj~6GUFHh& zGRw7eB>!Qg=GUYdD>7PM!+4PqBV%?nqejM!j2sy|GJ0hENCHR(ND4>}ND@dENE%2U zNFqokNGgsd7bF=Z8%L84k`Iy)k`a;;k`s~?k`1E-B#LB;q>AKZaI!qYxKp+L*LkaykNl}NugF}!y zM0JV|MbaT67SySt4jn~)hi+>84qZBQF*=CJ`o14Mbnw!x=izx?;~9+cyg&Zb<>{Q+ z^@!{|9aj0g??wE<>f6PZN_*-~tt0j+84bTqMmK&wiG5$M#aB|v&ZV2R#QUn<|MG_I zdOU3p+&yOx-accK*GBB2^J&{%PT0exR@;-=vPTlX-O-jGF6G4~^(!LBzHLhHt!L8v zlG2Y`tC&H8eIgHt6Z? z**TlToZdFO+&#~jY!B}xoi$CyOjJzR7+*rI+>PG9@sk)3*J*Jn5TEc1W+h zUIo2)wR(g&{C_B+f4)u)qzFGk6{HMOCr}F^m5@?MEuUngk@84=WC6$ukR>2%Pz;MeR)H)7SqHKZWF>*R6l5)dx)@|N j$a0YNAPYiPge(bJ6S62|RXD3n{;y?4O1ikQzI@~luEgG{ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Phnom_Penh b/telegramer/include/pytz/zoneinfo/Asia/Phnom_Penh deleted file mode 100644 index c292ac5b5f489a88bf10a1263b14e7412479d9e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMXU03;S=r5~yP|NlQD6B7eNoDEO}M6xh2Bo#1l`1pn}I03P? lfjL762?hhr`yaNg^$3UtSpYJ%9>^wW36~AfDmz_sE&#dsBrE^` diff --git a/telegramer/include/pytz/zoneinfo/Asia/Pontianak b/telegramer/include/pytz/zoneinfo/Asia/Pontianak deleted file mode 100644 index 12ce24cbeae404efe6921081d21289be452ff88d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 353 zcmWHE%1kq2zzaBlvg|-C-~GRjVVBr_hSQZxI4*^~E4XfRjN{gSHHI%+_jM@VDrsP3 zVrF7tVr64sNNWI@2qak;7&0pufF{&UVBlk5C}?010+V743`qqH(muW+3;{r_ZD4L} z03B{AP0kJkfT8~$l)M|(!}vx KKp*Iua{&O~6I|B- diff --git a/telegramer/include/pytz/zoneinfo/Asia/Pyongyang b/telegramer/include/pytz/zoneinfo/Asia/Pyongyang deleted file mode 100644 index 7ad7e0b2cf8fa4fc844fe8cc9c58e4f3018cead1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 237 zcmWHE%1kq2zzbM`vMfN%(|!HpoJr5beq7Q|EC@ZvR1o#1w1AO`nUR@+q4WvJi1H4Q z!nz3z9AJ{g$2Ww*J2-^F3y4EVu;@P!fGh*iAPYe>$Wo9UAd5jXNtSZ~U7>5q1ppt4 BHM0N! diff --git a/telegramer/include/pytz/zoneinfo/Asia/Qatar b/telegramer/include/pytz/zoneinfo/Asia/Qatar deleted file mode 100644 index 63188b269d077e29f48a42a03c2a52aefdb61320..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMW}@Y7ramaa($>i_@$&&b5Yz+eyn(rDqpz{0?wWx&AU;~T=D kZD0b##tb1O7z{M;Kge*9X&?(gG*Omt*#NDw(>3M-02;d=O#lD@ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Qostanay b/telegramer/include/pytz/zoneinfo/Asia/Qostanay deleted file mode 100644 index 73b9d963efc99f7af196755a6b2be015d9cb7a67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1011 zcmd7QPe_w-9LMqB=AZV*Ll^VUG@q<|)cLPD$J9C3Wq45dA_E~N>|jFBNfZSIAw~ps zt5bB4ln&8hLY*q`&@t$H@Fv>DO9UROi`baI_hW}0x^!y~+iQEC@i6xOCdg^Z$C;c2d>lld9@VzP}H_JE86~I zQLjFk(~gZPb?%SqwS2F3PDk{5szGl=%X%|%qFv#y>ehwD{q;ypFUlXd*VLiE>txk-Q`i zyLI#|En^=;I)30t>Xj=K@BNZ~7?jE9$CA1GU2YeRZdNK4pTDNI#&3_h`l{nnpz1^Q z1^Yf2G-iB<|0S6*wZ>${1dPcQd=0!B`&xY_D?WSm;+q#&`R4RY&P0de_Bmpo32tVL z&wJ$lA%^|^6@nm9xC>#BI7lE*8wm-8#6p50(U5RRJR~3z5ebRJM1mqwk+4WyBrp=$ z(}qT3Bf&jwbR;|y9~l5L0%Qot7?43AqdHdjZ1K-@S_BTl0t@B5S&Vjg%%1D zy6C1Z;t*6Eq(cgIQlW#R;BRmcinw$U!9iW5hV*?-hZMSWYeMdm+=YYW`4*>c&nC>i zkJ$gSNh|Mx*#@#j~y^V`o_*Ou>{{BXDCzOCq~wFT{d zI;*E2+|-`s>+0PZ)ib35Jv-s*xlEg$kJt5r`$KzUU)2xC#Q*(Q`nGq<{fm#K|C8ni z-hY(%#-?0+u`Yv;p2<+<5**A{lR*%W}lWoc0`YzWhMoaGJ>Pn;02stgSEslMLk2F1x zh9k`%_q>ldjxpIXe@;4YT8$})i5OEXhuSzAdu%>akWg@y^Z&lX)rlKL6CWP2ucUpY zxS25_9;SGJBK8L~sDhN?F4RE^A(aAcDWn!s45@~cL+T*~k%~x3q$W}nsfv_E>LP`a z%1G%zTN^2kR1dV}k^0C2kQE?HK-PdP0$ByJ3}hY1LXedpOF`BWXcvR5CeSViSr4)x jWJSo5kToHTLRN(=3t1PkFkG>14&Tb689Tk)U^2Q35s(4S?M3@|NsBb$i&RV#J~`x50nFuEDQ|M z84Mf@40RJ2_#kW_-w=i%Al5c8Gd2K{mJA^zSPHb`f7rIpiy#_gIfw>107Qcv0kXFq U$R^7nTsB}w+3A{D85?i`02U)YF8}}l diff --git a/telegramer/include/pytz/zoneinfo/Asia/Riyadh b/telegramer/include/pytz/zoneinfo/Asia/Riyadh deleted file mode 100644 index 2aea25f8c210369e0b805d0dd5f0e899190c2340..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMWHRoZMz{r~^}85tQEw9kO_Xc;iD`1pn}Xd4(agpgno(2)Ni S(?Dk6Hj&E)Xs(^EF&6;xtQkK5 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Saigon b/telegramer/include/pytz/zoneinfo/Asia/Saigon deleted file mode 100644 index e2934e371b6d5cf80244d8d55594f7094bc9cbb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmWHE%1kq2zzev5vTQ&s+R@|OFmbUq*ICnN4HqNN6@gplAYppE}QP6N>(=YeRD6G1e{nIIbER1ghvE{Fy>8AOAe4FVvigFrn{ Qj0(=@vH^O=PS>0Z0O4(7+W-In diff --git a/telegramer/include/pytz/zoneinfo/Asia/Sakhalin b/telegramer/include/pytz/zoneinfo/Asia/Sakhalin deleted file mode 100644 index 485459ce0397787a099064952c66989712ae707f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1202 zcmd7QO-R#W9KiA4+I-0hBFb4y)6|wNbz5bcFZr6RZ8TzZ=mmn_uw5RaLlo!(Wf4hq z=pPy&by5bCAd&*7N#-{qH&aVf%fO2m4Nj z<&U%U=V_NM&#Nz+=aaL+3A4t(Gf`ZUn(`K&e(9aR)n4*xG_f{2JGE}6GU3Yv)%yDt zs&r(PDjRaE4HxEAdFrF8IP_X=j6PMB&6BDsa8FhHhSjEJ*OcG$*!bth&E^l|Qgd#f z)V#WCwoIQlfk!E`_0C~adoyn8F14BZv-Ku;yxfG2#8miYN8KhFQGKh6*)l%d-wR$OnDT*nQ a88lNwQ&ckp*24cOyQsxtZ>tT3ihckL$`3{W diff --git a/telegramer/include/pytz/zoneinfo/Asia/Samarkand b/telegramer/include/pytz/zoneinfo/Asia/Samarkand deleted file mode 100644 index 030d47ce0785f3bdb75314e0d03fb20c3e0172d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 577 zcmcK0yGz4R6oB!Qw$ajdaM3o#M|?&sf{672rF0U93K;@|I5-sqS8)-#)JgvYhe9`T zh=ZGhtKe{U2NxFy9n?jz#QPON1Q!PrZhqlH!aZNOwOe11U$5S;UB}3K-5S pkUB^qq!LmJsf83nsv+f&dPu>prXo@j8|Li)S2I&Kc`I|J%qQNwd4B)^ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Seoul b/telegramer/include/pytz/zoneinfo/Asia/Seoul deleted file mode 100644 index 96199e73e73aafacd89e48cb2855a96d7a134e1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 617 zcmcK0&npB`0KoA#c14?7Qk#qN;~H)21*|8mE0H7m3)~$w^8n2PbF# z11Bj5<7Z79v(sWdtRCqDe zrQF$~?N;}4SK)_!eU|oa+)Z_Rl=SZWlDaRvd8LVN^{{vxJ~p>l{!C1ko3cUBW6PlY zZdH<#vT}N9J-znH=fe~0${ffUzTkLIj4PT-sxB!bUMdJ9HS;4`<_Paj~Sf_ zk+0|1^BW>A#EK*IS7G01i1zwqZHGN4*)daOrc5!aS7z80<{zH@sRvJ|JfA{9VTh_J zC>#_L3JZmX!b2gVFj1%|Tof`28-*^a=A#fs)r=HM3MYk>!b+h<3SJ5^g_-^%bwazR G2NGX5oeD|- diff --git a/telegramer/include/pytz/zoneinfo/Asia/Shanghai b/telegramer/include/pytz/zoneinfo/Asia/Shanghai deleted file mode 100644 index 91f6f8bc2e234bafd484146986bdb289082c3588..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 561 zcmbu*zb^w}9LMo*sb7uZVlR)P+QDKF36WCLNNV90hMH8mol%_2lQ6N|X0Wt&|A0ig zYPz+k{ufWWnRwpMAJ9ZRm*-x?E%$l*C$;LT3_WT4b=2NzZC~|=C*8PAnz!SMZcTj$ zt?sL|$L>r!EJ$Z#%XIIr^!v`U`Pj?IXSr#5n@#x|uLk|}K=-#pGy8I==4y|zif-%k z``2;iyrCBIr}1LxMlH>RUUvCRWd{{6H&If#PQe>V40%K@diI|riW5nZK+9zLz?b5# zy+^+If0*`TPqbFdBNLIC$W&ym(@sWaJMDC2K9T^*fTTcjAW4ucNE)Zj!x|DHnUGXS cE+iR}4M~UOLlQb|MkJ;4e>qdO>B@TQ7g9&Lh5!Hn diff --git a/telegramer/include/pytz/zoneinfo/Asia/Singapore b/telegramer/include/pytz/zoneinfo/Asia/Singapore deleted file mode 100644 index 2364b2178b03853b1771cb3f34b964fdb82e2e91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmWHE%1kq2zzaBmvK&CH&;TUbnp+p|O81*^fa^)Zsm)IlPEU;Ixa5)hL2Tkzj{5)q z|1&Z%v#_$Uva>TVB*p^OgGd$zhNJ?J5$OtyJRp(*NM=?r2r)3!O<<5qL% aJOOeq$Ri-S9wfUK)Z7$PHbQ=xXC96|^pmzBY9i$E(szYRd z4$)ym1r=JX=wK4zVTZs&5+Tw-ItbZ8f_MloMX;XlYl9J8y7j!g`@DPJmlw9*H#KtR zbV&VmmH*!DHF90~w61;m<-^IPD*v8jb!|FV<39Vo=KKAg+Le3By2bh2ra52IGZU2h zY@>MZZj{aAPT6vCRT|QtrSa$o*&3S_U-*@53p|k~Pe!(vUlYG;TKiWg^p52T)jWDw zH7{J%JKtQ;fvL3K_2`&xxt-9hm!rDve47rQY|z&6xP;F6rM)9?bLx&ha49Rj{iC|?#0@#v zG@|?aL=Kfi^nhr0Ei6yNys+U;^Fb*r9#cl?1e*ONJiTdB+oRZ;jX>^RI$;TdI~DeS9?cp{|&R?wUw zb6UJiIo4L{A4Y0^O`fqLqvbV>7a1`!W?M6AWZcNek+CDAN5+pNfMkHAfaHKAfn~qC>H1bDSuWlg({)8fn$xc`;|*J#xO-p(2CBaxpP! zF58!Nv~NpYRXxacS-!d{2uRF3r{YPoI{q3ox6z>79b~EdjZFQpRBayLKICa?-8_d% zwdsFX=^riPejHU9;SufGSur^$GurEfl`F}W{HseO5V!v$N=u5!(73pHy0X6C_!}MZ zH968?Z(pq=&L1)*?CISxxkxS~8Ilc2hvY*NA{mjCNX{>9QY0&q7RiewMlvI*k=#gf zBs-EG$&XBc%z#XR%<-k21epby2AKz$2$>0)3YiO;jM~h`VLD_!WI|*{{KqLhcGr>$ F&nGsEUL61c diff --git a/telegramer/include/pytz/zoneinfo/Asia/Tashkent b/telegramer/include/pytz/zoneinfo/Asia/Tashkent deleted file mode 100644 index 32a9d7d0c9cbfa841cc4e922fd1b486a34b57370..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmWHE%1kq2zzR5^qLM%w#1>d3+040h@p}VN_1Xnu5}O3XIW{VY|9m<@;^mVClD8!V zq|Rh6kls^vKxR$)0ogeb2jqG@56IV89Z<;8KA;#Qb3n<5_kgnXzXK}T?+>U-KRBSq zeer-g%U=ieFYg>Qp1cjH|Ns9#BQp~OvaljSHgurM$r6)*yw6q3Ne z10!J~j6e~P8Xw;f25kdVAT|eLGmusWMj!|w!NWlJ{0Dg&M1wpIG6Up!5Df|d5Df|h zFbxa{5Df|p5Df|t5Df|x5Df|#5Df|(5Df|-5Df|>5Df|_5Df|}5Df}25Df}65Df}A s5Df}E5Df}I5Df}M5Df}QFbxb$5Df}Y5M2*+JIw=?%LW+KcDklq0A1~R=>Px# diff --git a/telegramer/include/pytz/zoneinfo/Asia/Tbilisi b/telegramer/include/pytz/zoneinfo/Asia/Tbilisi deleted file mode 100644 index b608d79748884c4a8271e695fe4ed992a91fea65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1035 zcmd6lJ!lj`7(i#^Mej!oi)sFjuBWEvF(!Nw+utl!(@z{1AP9d_O@J1p$JSDm^xJEZ=4 zWwv+ALe_1!WO|Za_P%`B9kdnh*?k@FbbIoxX15Qgg-kAA`P^ zFHHY48w@;pXa?^-2#zh*&GGpgMqj<8Ph45D!=tld>Ed`;?yA|5p`LK$mv8;N(S9Rm zPi}PUQ>&Zy^jbn!9%s$y%4a=R-!NyEUu!edFsAV;9AE7GF>&_m+(HyZUQ1%f)@a@N z$JmujwaDPf_T%QUR5FoJsu;<1>PQ|e53X@=jM(lJ6mUb!i8m_c$)!~FAGF92W22lsm2T=&o2vG^q z2~i5s3Q-Hu3sDTw3{efy4N)Fbv_sTG^g|MWWB^G4k^>|ONEVPZAbCI%fn)+n1(Hil xNd}TlOi2fl4YUzr!H#S&G{{}x>-pT*~ diff --git a/telegramer/include/pytz/zoneinfo/Asia/Tehran b/telegramer/include/pytz/zoneinfo/Asia/Tehran deleted file mode 100644 index 8cec5ad7de2f2c14dd4b0a6c375d7198ef7429b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2582 zcmchYYfP499L66Mg&7_KDr+uXW>A2@`>OBl2?$bdK?RXRc}!GL6DS2rJfewdKnY<& ziidzg(ZV2aQb-f(1|iY#6i{Yv;wHmQ+)Ul*xi06|`jWw(_s5^>8hp8adm?8ojk6g4 z__fGP9n5D)!Sc2GT&uGlaBROEnCulWC_BJsQ0KUSC!^xE^P~{vV!Nqby`EC;!&0?- z>u&Y(>0SDjnirHuS+n%q6D_>5d+U)Kwkq!=oAg;wEque5X-nWOWwmtaQ6o~+XxHI# zOy5#5=1#P>U2jw3!Zta!evlYfHBgQ}m?tI_1nP;ITKTO`mHr72M8KR<{c4!E3iKbT zgM2Gg@YQ-L&xeUAb<<_Y(H0R}lCG!bJE&<}D&_RF@gi(VhYp{esAhyF=!l88)lAQQ zGIFS!i0a>>qm5ZAy30|<{E;nQKbs}rsC5yuj-1f5izll&x!rPZMw*zH;-=@vA5w3| zl<5T_78NV5$b}wpV$pLhda+Z!T6{lP#&tG{`1VG*q&Y$?t?ALr$~UXG_FCogoGOu! z9;Xvm-c(7k1@i5vVd9-hL*)uvu~_+1giaoONu@Ys$dr3dBDJGRzuW4s-aGB5S5?=l zw6Ya)b>UsHCaYMl-Qc0tC0>^67t9xSz3hA2_v-UlzYW7jU>)YCy z3=v>B8-~BMd-~z;r%sDBBF)Fc$7=O4KS!I-C_LsB`R^-hc(k$}^9xG@u{Qj7EDpr# zKrD|m*9T&OAXW%si6GVpVv(e|N)XEgu}%;R1+h{PO9io35Q_z|S`f)q_|*i1mXo0E7h~OaNg62qQpP0m2Lr zc7QMhge4$M0bvUWV?bB~!WF?^q_0JH(Iu{ziGrX?7#0)$9!|& zQ0AMv?=sK0zK;2pi)(p*(9oOrR#ndawu3hIKg_?zJTr3{^Q_f_nP(>yGXH4qZr&e< z59htzubKTE-)Q!8huJg##CaR@JUbinyxY~xcl>#r_oo*EdGD;f#eV+LRQ3x>hBN z;^D~tH_uiwuW%aAyyD&s=9QgE%)f0<fZ@1tFKpLp#g`zLL|?Ef&LoB651Y0PUK+?m(@b(neGjYGVDY_szIso@Iy zx;l>i`Z8DM4TS~FPiF-)Z(QHRyeX-X_s_8rywA)q*l!NXVE?Se%KY4mRm^{JiD%x@ zr;~ZhotwPRcdX!j;o?yCFV+{ce@V?`e)+&<=D+4=FmKItV&1mq3Eu6A+j;*s*Ps30 y!|MK*x8&n}2S57j|INF&-vqv){k*K>tUl(?=KI;tGsHI5+cL^C#4^Tevftk%?{cC5 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Tel_Aviv b/telegramer/include/pytz/zoneinfo/Asia/Tel_Aviv deleted file mode 100644 index 1ebd0664aa29c0abd722661f761031ec0304631c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2388 zcmd_qZ%kEn9LMqRO}t*foJ^@^$e%I_#mnWdRFaCKU?8`+F@%6)1ZE)}lgJeEm)c)U zw-&XgqFcm9rNg4O=Au^9Xd}28i&V_hACLmvX$1;P?Yz(0SnE+wdeZmq+}GLVDWA8m za6?6+{Nn)}0ASznEwjORPnc1y=ZvwV@dUv>5msW0ETB3++HNcY~I;%{k^ zuXfeR$rpRo*X6CMr!rW23q7j$T!DNW_nA5sUM8n!UX;^AemT>YBz+&I$ywti`L3-? zooo778TB>teSV$%&|aZ_OevM08Z%Y@b<1R++LZGlC)Hr|VKq3oRa~|8hU;>xKXB|9 zEvPxsKkMf|wAoQb+MJ0K+Kqdvw6OL|#@r`r{o$QgwFvEXZ{&`nT2$8i{+mh~jhiEj zz4Hn^+AXrg8y!4iM1M2jzjdhFxb2fE-k6?YZGKCHf5EYGBlZP{f8oAWhP$-I8@DaS z@MIqM$7jB3BrLA-CdR*~CC$0wPY!9&l7DUXE(&m|McocPb*xm~-ZyUC;UCfOJW`}B ze&djycIaL0uAMvdCA9~QrJGaqW!@$&J$tV#&&<_V#BP#|@G*1c^gLNPTxPBsPL|B` zqk2~7^|HEszj^oDn^MBv23PNMdf}VA1?P#!B*tT31^^BPJAJC+@t=lZAOPA7Dj+$EWJXu%Qr9Y77 zl(OPh{lTz7DNlP@uW$ya^>eHB_5B0tq09ODhT|RT;j^jwBX7Q?Do^;#jnC{>Rm~~p zqvcgffA$7*)0%pDtbD@U?A|EV8GYvCGjgTIJ+9YWh?gxh&gfgZX33KmKGL@y9+hof z2ld+5LR8(6r}d|5epB1`l<5xVHB%-(0h6zjZ&RI9ozq;?T=?I7M53J5|GGq8k^qtU z9*0XLEK;V6q%L*L{QEDHf6dPE$!hD#T46nTXuXIy91gzzdJ*yci^W~FF8_m1Cy?bK z>$9~Mh^!D$SBOjI0=0GO}i5(a5Ti zWh3iG7LKeOSvs%T3>*pwdtyKY}1V{~#A|O>j%7D}XDFjjpq!dUkkYXU! zK+1vC11Shn5u_xxR!xwiAXP!ig46{m3{n}SG)Qfb;vm&Q%7fGgDG*X2q(rt>jgTVQ zT2(^IgwzQs6jCXqR7kCmVjQb${>kVqwwQX;iPipfz;q?{b}L<-7LQKY0C zHARZbQB|a@NL_8M!XlMLN{iGMDK1i7q`XLdkpd$XMoP?4W2DF&RYuCpQDWSv ywMxxVYoyp5)keyV)Eg-{{y!@oWEFm4f|dH%oJ4n$J1H(9B{nDC<4$&ag8l@yuiuRT diff --git a/telegramer/include/pytz/zoneinfo/Asia/Thimbu b/telegramer/include/pytz/zoneinfo/Asia/Thimbu deleted file mode 100644 index fe409c7a2a40294af6bae4523492be88d38a97bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmWHE%1kq2zzdjxvLMWN^_ggiVxsSg`v3p`GcqwTFof`cH2Ov`umIT!3|v0GAq?6E krp5+9(u^U51fzi_{s$QkG7n@0h$hk+E*qd_cDiO<0C>Y6?EnA( diff --git a/telegramer/include/pytz/zoneinfo/Asia/Thimphu b/telegramer/include/pytz/zoneinfo/Asia/Thimphu deleted file mode 100644 index fe409c7a2a40294af6bae4523492be88d38a97bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmWHE%1kq2zzdjxvLMWN^_ggiVxsSg`v3p`GcqwTFof`cH2Ov`umIT!3|v0GAq?6E krp5+9(u^U51fzi_{s$QkG7n@0h$hk+E*qd_cDiO<0C>Y6?EnA( diff --git a/telegramer/include/pytz/zoneinfo/Asia/Tokyo b/telegramer/include/pytz/zoneinfo/Asia/Tokyo deleted file mode 100644 index 26f4d34d67b46513491f26c2e661c6e653cc130d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 309 zcmWHE%1kq2zyK^j5fBCeP9O%cc^ZJkbvvel>u)1J-1zaU;O1HD54YJFKHOd_`{B;B zM<4F?{Qtnr$OM5549(0y^$a}=7=fDWCNOY7NFU!21}_&N4h{iHGlFmk36A&=1gVFX r6o6=uW56`fK_D9BC=d;D7>EWr4om|b2%FikPnRzXoXRXO%NnJw;gZrHZ~Dt-CNe7~J;ut>LmBT#uUhq> zL;3Ess@2z`YR$zOResW^)^;vc70I0PN580b!D&_LeXZ7eUaElkSp??Ziwz&&$*OZ# zW!3l#QT^(X2tLk=n(R$cd*h1OICxfUI@2q}u@15M@SqBvKBBhN_o%J=8`U=79u*Gx zRrqJMig=}nOckr`^A%#p$gGM^nj&`3BVuDSs{YxWY`8oxcix_m^7xc&9QbPOia(c4 z-Q!ks<&cawJ+*ch4#*b&Evseru9YabU?s*bS*;&Bt>nXQt8MhWY`-42Qps=PK;paA zm&@fG1xpqVvoP-{a^`)>yWw&c{c)I1(=?2I_xYbwM-6?Q?svEhqbqF`FMclWIP}iK zGhJz}d8VyBWduc7pAmi5@fyRSm*GhL!&LPT$uU`Ey1a!6BU5HEX)pOsRk*>R@Z|Rgj9r-gw%u-g;a%hSY`>XRE72%Cpt=Aq658A|)a<+Ug>aDv>ggI*~$=N|92LT9IOrYLRl0dTn*V jNX53gWTa-KXryYSY@}|aaO~4f{J)jEW4ii=+B)|yku?`w diff --git a/telegramer/include/pytz/zoneinfo/Asia/Ujung_Pandang b/telegramer/include/pytz/zoneinfo/Asia/Ujung_Pandang deleted file mode 100644 index 556ba866933d37f3cfcf8042045d64e209bae30f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254 zcmWHE%1kq2zzbM_vaCQXy6{u%gk54+C!Fq(ZMYQnu7Ht=87P*v$ zbrTqPz-$2@-w+00Al5dp0AfpqaL*7&h7c00{0{^mYe6)~Y7h;w9z;{%1TK(6bS=05 Dzv?@= diff --git a/telegramer/include/pytz/zoneinfo/Asia/Ulaanbaatar b/telegramer/include/pytz/zoneinfo/Asia/Ulaanbaatar deleted file mode 100644 index 2e20cc3a438bb2076bfc1856045075dd041cad3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 891 zcmcK2yDvjw9Eb6@N+K?CNhEG{Ek#vJ>)zI-HPL|-iOWD5i5R?t3E>^Ygq*>E7|vj0 z)0DwzFxXrMF*0<5mk2Ok>zJ@WjijE6*~p0a_vJ_ExoJi=|@>J{Ge)kFQwgiq3X(xWPSFb za-=5=$45dmyc`&f_Xncsdev~oS4H#5jN#g!5iMIm!@Uv|o>-O9IyA_3_Q_?6F9 zDcfxosw3ATdy?mRZ~RR2pPcG}{XH$XwX657NUcA%tPe!Tw88MG9`gCLu-&T<*+%on zi;nb(j5#^+xvo#XhU8`{m9kjV{`g}RBIaYLdC43bnk7>RsYK35Br2SqPV?(Fzb+B| z!=&bal44f=F4H3OA`>GsBU2-DBa_TBsr2DNsr`5P5?Or)zI-HPL|-iOWD5i5R?t3E>^Ygq*>E7|vj0 z)0DwzFxXrMF*0<5mk2Ok>zJ@WjijE6*~p0a_vJ_ExoJi=|@>J{Ge)kFQwgiq3X(xWPSFb za-=5=$45dmyc`&f_Xncsdev~oS4H#5jN#g!5iMIm!@Uv|o>-O9IyA_3_Q_?6F9 zDcfxosw3ATdy?mRZ~RR2pPcG}{XH$XwX657NUcA%tPe!Tw88MG9`gCLu-&T<*+%on zi;nb(j5#^+xvo#XhU8`{m9kjV{`g}RBIaYLdC43bnk7>RsYK35Br2SqPV?(Fzb+B| z!=&bal44f=F4H3OA`>GsBU2-DBa_TBsr2DNsr`5P5?Or>Kf4i diff --git a/telegramer/include/pytz/zoneinfo/Asia/Ust-Nera b/telegramer/include/pytz/zoneinfo/Asia/Ust-Nera deleted file mode 100644 index 9e4a78f6a547de2a91e728ac4ab5ffa4884105b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1252 zcmdVZPe_wt9Ki8sZOdsLBuZCnr7c^w)NPr}HrL8jY+H%#lGUj{P$T#e9i$+G{y=1Z z4$)ymf2gp)!h=Z+4?7eck_eFwR;TJ9K|I7RMX;XlYX^}o9eZA0KJT9QWw7`6U8e8o zk+AyfTKs3X-N?23-usXFXZK&KW&S-<<<1tWUB}*5|G1NI&)n`=K0R4jG2!d+j0a`q zaIJW6Es<4&c3FM8C^gwHQoHY+tVzBSU-Y@G4Lp`QPfpe?x-5RjnD!Tk^!m?3s=j}> zs-L=~H@rBd1EX2J@xgxGa3if7&&PGs$tE2G&yrlj@fjNURnCei8FI`(i(d?WBAw2d?(+3Y*SEdWZEu$DH&NO7sY;Shy^@*;>-3e9>^fddWRiJ3P%4#d3mkUq za4wv4l)2`7n0sUXb-B~!QtDzSuUO71hf-&*E3=au;Z;8>*ed5=+syvpUA2_E%G{lO zhMiP8qXNN*Ia^gA6f$SnoI$=?DckJI{KJ^duPZTXWZW!9j*J}{y`>pHk^qtck^+(g zk_3_kk_M6ok_eIsk_wUwk_?g!k`9uOrAY|Mh($`4CMP5*Br7B>BrhZ}Br_y6BsU~E zBs(NMBtIlUOOqjzqNT|ZNfOBtNfXHvNfgNxNfpTzNfyZ#Nf*f%Nf^l(N!ilmj3jMo hvPRNI@oJF!he>0Ma=vO;tio-#cvx!EIt4L diff --git a/telegramer/include/pytz/zoneinfo/Asia/Vientiane b/telegramer/include/pytz/zoneinfo/Asia/Vientiane deleted file mode 100644 index c292ac5b5f489a88bf10a1263b14e7412479d9e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMXU03;S=r5~yP|NlQD6B7eNoDEO}M6xh2Bo#1l`1pn}I03P? lfjL762?hhr`yaNg^$3UtSpYJ%9>^wW36~AfDmz_sE&#dsBrE^` diff --git a/telegramer/include/pytz/zoneinfo/Asia/Vladivostok b/telegramer/include/pytz/zoneinfo/Asia/Vladivostok deleted file mode 100644 index 8ab253ce73555cbbf42ec67a8560acef080fc480..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1208 zcmd7QPe{{o7{~EvI%}xGI^?XSS!&CcZ9hxVoGWK@tT~9-C2KI~AL1=K*dO$PiU=e+ zbSRMsOzL3KAq~cEStkh(br~J3ymSen4jIJyzJE3t(WP6Tq3=1zK%ZA?aOiBC{B>2o zUyYdZ-2LcyEIm6lx3b9>&D7LhT=cj`mOQIZj@Evj$!yLqEpAz`GTvNRx6TB#Zn8n^ z$2aP>D`na+{8hJ~Sk@hhciPzfTATbYwAuSucUIj~pYxUYzRk(5B;6KeEp>yoVYKC&P+@1)vQLMH>B^>q#kY_l4#$k_E#h% z)|k=Qmvb5~k7)c&T#tNe(!^804lMLa^411TC9*4Hg+jqmUbgmdR+K!}tuOhZ^cR0V zcR8IRm#6sej@}mYeBznIB{Kd%s*B%?uQ<#r#rKGLPw{oB9!RFdAM6spWtp!aA102q zrT*ch=C3JmR?2Dl7|u&MG3Cs5&8aEprktE|cFO4~=cf>$FrZMNaG;Q&u%OVO@SqT( zFriSfYq&7TP}tZtbQpXXgcyt%lo*^Cq!_Fiv?#nN#3;-t)F|BS8gdkNb`3oWKL$Yx zLk2|#M+Qj-O9o8_PYO{6QwmiER|;7MTf2rXg|A&hn8KJsnZlVtn!=in83X@0?aGj` K9&WXQmA?Rv8vN}5 diff --git a/telegramer/include/pytz/zoneinfo/Asia/Yakutsk b/telegramer/include/pytz/zoneinfo/Asia/Yakutsk deleted file mode 100644 index c815e99b1a8f2d4b9bd45d3a6f39c95db5bbf563..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207 zcmd7QPe_wt9Ki8sId!PS?^0UJTG!8Q%RUXJ=8aEH%pYw@d503Zpg*SN z*Xwj)x3C zyj2o;KdsRp1+seRjW&*dl%~61BsTa`n$NwKmTUd8X3v1EJ@Z^!+pkF5t_Ql#e_Yzz z(z-r>t8`Qz){d!T+L^aeJ6~x0^a`>^^bx+eaIT+ z&x~tkCI4Zh_GvPV6&Wpy@ggHe#_VWEjf@)^IWl%+^vL*;1dt4n6p$Q{B#|-y zM3797R2*$CNHRz^jy4@6A0#0pBP1mxCnPB(DFC;M}GbA-6H%FTslAWVX56KTn z5Xlfp5y=rr63G%t6Uh@v6v-4x70DGz7RlDpri5s(4S?M3@|NsBb$i&RV#J~`x50nFuEDQ|M z84Mf@40RJ2_#kW_-w=i%Al5c8Gd2K{mJA^zSPHb`f7rIpiy#_gIfw>107Qcv0kXFq U$R^7nTsB}w+3A{D85?i`02U)YF8}}l diff --git a/telegramer/include/pytz/zoneinfo/Asia/Yekaterinburg b/telegramer/include/pytz/zoneinfo/Asia/Yekaterinburg deleted file mode 100644 index 6958d7edddb85d298c5f2b890b21d9ca2056e9c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1243 zcmdVZPe_wt9Ki8s>S~)FBr2_Ct;}9^S!-=$)|{*5%$o2}gCLX!nNV~ol7a#uMg>I? zb*c`Q;i*3p>Qs?|N2}kVOGpLfB?1rCC2Xwc``TbcmySIz?>_IIjTiR*zRT=CJQ!4e zT{XXVlSZx=-#p;9ZZ8@yJ{~P3mRJ|_OY0|mb*|@ux*y+0>OX(YFPpY}%coA}y-#zp z;@*%n+!~aXm-fl3Gdsn1v|U#BwaS{bR~lnAvesXaChx4QtNtLCvszo$dB6^oHCuz41g&2L^`prv4EL9zQLcJI+dIPrrm41|$;f zlt>{ZQE#7)zH65Dxwzi)%p+T;eL6N~>5eBAI)2WrJ8#VB#Gx;GTTaRLu9rG_@T2T# zdZ@dSlag{?)!mI_(mi`$dMYnS&#No4>(de0{ot6Sr_Sr%%X=l0b|i+2#iFC4s&f8t zE+{(|y2{>^fAHtE+v!v)`j$r=dZDV!bwat6%48kpQKz)3Exnd@9A>BV9mY}GSGDQh zjMC=x2SVnIm@~|6m2%9l%s-6S{IVirMn=tI+}w_lBV$KKZ)?VnB!Faqq=4jrB!Ogs zq=DptB!Xmuq=MvvB!gswq=V#RYZ5{-LQ=9dIUz|QSs`g5c_E1*nIWkmxgp6R*&*p6 z`5_6~nhcQ?ZB33yl1P?Fnn<2VqDZDlsz|O#vPiZ_x=6lA!brwQ%C;tFBxzfdHIg=x aHWJw&Hg@CAvWC?e>P73=qYaxkJxxAySt_3&&wuzlXy1ILbb zslUFw%Xj;XT-QAaOzwI2s&;fu=w`m&zhL{S=aarA>#_g6$_ENjyJ7BpKKS&iterY1 zjkm^S-Ni9ke|EoYIGK`-2jj9S9hRm=nJ*;4aSLY{yQef8>=x2v4+%O$7v z`3I+M`nA)3Z{F#+_RQ&=c;svyzw6jXt~lL2mnE|AghU%pNvta)v7cFq2aoIco0x3- zp4QuE{j#IbsT233x@V?N_g?VnzR52-IrdHOoLDWp1`4|W@MqcGJgW!#U&x^6j!rdA zOX|~%3{~8ap@rMBckzr2Kg>yb?z+xgIwT`gr**DaELs&Sejk;dvSVe{@=2?&V@2?>eG)dYn^(%;zI&NB1A$& pVnl*OqI5N3B5}H!K#@q1P?1=XV3BAsXSV(?!}TUi$h{pA?=Ncm39_rDD-ymJyPY3ohdtjM8E;_GGpa zax}7r6t%1@F;2N8fw+ofx@cTYV4?mq6 zW&Q1^q4|U-KL^Z{_sz1*%lspSPVT_Dn)`c=^KM>{=9T8_!pwP^U${e@5#x1HP90s` z>w>;F_GK*y4U_lpE0MxF^|kQ)N3yJRy%c?5%W`Lou6Uu1tepFzd_2B~ev(n3tM2#d z=TT|8`o=@L+Fz+({Jc?%H|>HOJciT)ysj=ttQ(V7pXy`^o-3Y@fZW z`4)S3kZbS0ywa{ZzS#bu>NUH%G~3?u#bkT$$Bw&i@vH9s`Lo>vvnRSgW~8_WM-Fpq zhV*j}_3q*xj%x27Y17<28WL|GtKZo^cBP$t{A4rxM0JpT^1I*cQ(vC6e=6K#pMGbv z{qsv-*=MFNx6h7t>|dTNbI%PecF*@&=3a8h|ihHq1mV2qz1ozUpk#57>FuCF7 zTja*H^tGP(iqDsx<-7IK3BEQ7BYmNsQn5y)EYry0RT7o4N}}V| zNT(rhNKDH-=^QmxI$zC{t|1Gw>)t1%+m$KWeQh7PyE<88myFQ3HQlwx^S!iZZVR{z)!XO0ej@Fg9R{*(^wlCDV!{dHK=LHa;=m_BeZLX(60N%F47`p|{; z^6+Qp^^wYY@~G2DhOas$k7b^d5eq8h@e!5s#HGRK}&X*748B$b`YMl9Ad}CPucB zN%5CuawAKobPU!hXAa8L8!qb9t?Oml=_;MRVu{SCSfexF{zhjN<>|B23N&+Gre^y6 z{(w5y)w;g+H9qQ^@BQ_8jlUOEH}D@H_3AhHuO1Ej{-b9s%kMwj-n!oJKYJ!1(C@D~ zV42VV`kqhB5Lp4j=a%^nuY6+N#LJ(bCnP3W_n7Bh5s~H@W!)JOVLce1#8<2Uet7l% zWqAYyB>jsmH2)p`$Kpa1*+gU)JE`-p5LvXjVGB72E!CbFBzb|U+UY$&p$o@Pst zJw-MZ*;Qm)k$pur7TH;3YmvQ0HrLbaF0#GI{vsQU>@c#$$Q~n`jO;S9&B#6@8;$HV zvelkuuaV96G`o##H?rTJX2X#k_cU9M?764ebgVGD?rF9i*>_K~@yO15nyp9n-qUP8 zviqK9`;q-e8h~^FX#vs$qzRs;3rHJ~J|K-iI)StT=>^gZq#H;(kbWQyK{|r81nCLV z6r?MjrY%Tckj5aLL0W_K25An`9i%--e~<|SJWbD#rXgKJ+J^KEX&llyq;*K|kme!XL)wS*4{0FML8OI9 z4?Rs2kuD-_MEZy{66qw;N~D)aGm&m0?L_*CG!*G5(o&?So~EftS3ONzk-j30MLLVL z7U?b0T%@~5dy)Pk4MsYQv>53z(qyE|o~F%6pFK^ZkxnD6MtY4j8|gOEZlvEx!;y|7 zEk}BeG#%->r)fLVcTdxJr1MDYk=`TCN4k%+AL&1G10Z(*atk2$0CE!`cL8!6Aoqc% zxe<^%0l5{BdjYu_kh=l79gzD0xgn4{0=XrSdjh#Bkh=o8Es*=d)7%)yo#AP24dmWH zZVu$`KyDA@{y=UJj8AiIEZB IyU2Qf05%v+?EnA( diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/Bermuda b/telegramer/include/pytz/zoneinfo/Atlantic/Bermuda deleted file mode 100644 index 527524ed295aba41b9a0448ffd7993c489a2cb99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2396 zcmdti|4)@w0LSr*n8TY8DHI8qXoVPD;o?OE3^n8qggDB@_#$*N5J8C`K}e#Sl+rJ> z6ajCJVtn}lIo|cDxb9mfueZD{ zCZ^nSvZ`Cn?3u5BUNcAeE$x-_0}l(ktzFu0H;DW7KDl7*J>@@8AOpUsR}XwvrvpF8 zR}Z#6s~0vVsYMlQbx=W=3eE`C4<*j3hgaOwAb1i!B%SC*;B*{@`j-L0Y~ zj>wh2M5!nH8)fupx`^p4l8)XLVpa1x8T+nX#FZClXYH>d-kq#hZ@nN~k>NVQ`Gr_B z&#MzdhgIUt4ZZg636(T7sFTOvQ7JuIuKTW5J=OMuO#RTK)>n7Q4J|cdW6oRh>C#-0 zwrZE$l;#qfem|<+F-v`J_9$E%Fq5}kP~SZ(guFSD*VMRrrMeCAY;$l0DMMf)FO zOG<>?T6az4hWScQ-nYVYcUo?9b%<@#K01F%pUOWor3+?{s=}_bx@e+FZErcQi~CDd zNolKou5-PT+1>K_gT-P;%pv(gd8#P&uasr(2vK$`OO{9ait_UgSut-~RD2#ND`(D$ z-G`&~o{Lkesy;x!*mG7@=iSyXwH;SAu1mVMx>da#d{Xbt*{$}@j>!7NL!y47S2l!H ziiZAn+4yIcc%^fn+<(m>nwksbt9^l@xg=S>cI1wj<73WT-~Z(CdIytiHm`Ri(`T;r z?dSDgRz3^7-g5)Cz^rVKZDX#v#tmuans3j)Sen;76$eAok;NgaLzaiE4_P3xLS%`^ z8j(dJt3;NGtP@!%vQlKJ$XYGUVv*Han&l$vwKNMxR%~gOjI0@1G_q=B*(M}5){QJ2 zS-GWII7qDFIRgqzFhAkTM{3Knj6W0x5;1sRdFDq#8&$ka{2m zK`Mfj1gQy96r?IhS&+IQg|Rf1K}ut3YJ(I9sSZ*eq&`T2kP0CsLTZE*38@lNCZtYC zp^!=;rLr`&LW+e{3n>>;FQi~d#gLLAHA9MqR1GN`Qa7YLKMr z>W35%sUT89q=rZlkt!l(MCynX($Z8CDW#>UB~naFQ%$6tNIj8)A{9kSiqsS-DpFOX ztVmsv!XlMLN^5Cqixk(=R2M0)rKvAcV5GuGiIEy3MMkQOlo_crQfQ>oNU1GNt&w6| ynrb8Ewlwud3XW79DLGPer07W1@&8wLyUF`%llkcEc!$#w8=V~&=ZJH}+W!Xk<^WLu diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/Canary b/telegramer/include/pytz/zoneinfo/Atlantic/Canary deleted file mode 100644 index f3192156ff043a529461aa9004a8de9dda326f7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1897 zcmdVaUrd#C9LMqJ2qGUvpA-5QdEdkL@gpEV~`qiI@cUy zpEjm*<+A0Nb4p%NT_Cmo&X%&aWKPYs<;woe(acdgM!)ydO`BKVwE3{Z>ltU`>ihmg z*KTdh_wIVeyT9<^X>}jo6MJH7hcA?l%$yP_@}?HtR#L`qnl|M-CC8js^39Jl{n~qa z;M=2m@Uu6Ra%R9%Pxe~cTW{NpPFeb{JvOtc#b(uRwAocr%P20lhk`|xnVMyDCQi4k zxH4tkny2g^GnF$mO%H!DL67wPrQoq&G`IV*a`%0$yd7s0YB;5E-6hL!>9c~8ew(-Q zpcSSav7-DoD;n*v`6=C+e|5brxYMeI17-Hul^PZI)T^X_p%(2g)#5i(wWKjarTZ4x z;}vl#Ye=ytGOw$=Y}6{^zEWkz_f~o1CtDixi7g#GYfoN#*PiMV%qfLrdRqC1KWm*-?(W;S+q-A-Frl@wI5SQSBC=QVq)X|_n)z`KjeAt_ples znR)S^H^~AM|NCAQiF$KGVQ+PL)P1U>d>04={v~=3r#t2z&KEgh{sU*s!zm-@jGQ!b z*1qnvk@H4Q96593)RA*XP98aXgfR3U~c7%9jt%njv* LgOPBEw}gKHM@+Br diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/Cape_Verde b/telegramer/include/pytz/zoneinfo/Atlantic/Cape_Verde deleted file mode 100644 index e2a49d248de086306d2dd16a2b2d497a008c8f07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270 zcmWHE%1kq2zyPd35fBCe7@KF(vsDYuOr4`}sia1LTl~92{r~^}8JU<_SpNTi`GtYu z|NqAi7=Y}L9~e0hYz7V=-w*~}10x_dWME(fnFu06NU#`a&wr5RAR6QV5Djt!$SjaU TKy*D&jBLkn*#I49XUGKr1p`uu diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/Faeroe b/telegramer/include/pytz/zoneinfo/Atlantic/Faeroe deleted file mode 100644 index 4dab7ef0859c244b916d61b7489d7371881e0ca2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1815 zcmdVaT};h!9LMpFG}f>$R-qD-ila_ZNC~Ni%0ov*lJr0%6s?eE%#7B)w#E#TY0WH$ zi*S*Lc^s2wvt}NeP4jHcM#HS-_x`(d<;LdU{(Jp*F1q@>zs?oKMUifQJpIitygcRR z<$LhKjg47efgja-_zU%Mf2clRuIY%b^E&czgO0j&NPVwd6~AVe_#Zzhqia`L6cH_d2=$ znTG9spy6AusH5PVM&vw|$g&oh64xqImmZcV{}U1&St-%IH8S0|UZ;2F$&8;B8gn&4 zW7vq7SzNnEmt-E$r6q-$KCMKZDapFbCrvZ# zp_=i{p;=x@lJ#VmF7FAE6_>thc88~|Y#1szEuUmn%@@h7Zfz|8hBCH`m3&ecSP6qmTB?5DqY{{)D35{wdC#=*|Zc-8Mr4^rFN#&4lTKVX-%wrJ(>AlNvTtPL$j*_iTbjKin@4t!Y#-S_ z(g4x{(gM;0(ge~4(gxB8(g@NC(u$?&1!>08bc3{m^n)~nbcD2o^n^5pbcM8q^o2Br zbcVEs^oBHNX}Uw&vo!r74I&*PEh0T4O(I<)Z6bXljUt^Qts=c5&03mnk#;RjzevMK z$4JXa&q&iq*GSt)-$>&~=Sb^F?@04V_elGerhnuHAa?+{1;{->ZUS-_klTRV2joT| zcLKQ;$h|;r268u$+hJ+$2XaF!%^g8*335-6n}XaG$R-qD-ila_ZNC~Ni%0ov*lJr0%6s?eE%#7B)w#E#TY0WH$ zi*S*Lc^s2wvt}NeP4jHcM#HS-_x`(d<;LdU{(Jp*F1q@>zs?oKMUifQJpIitygcRR z<$LhKjg47efgja-_zU%Mf2clRuIY%b^E&czgO0j&NPVwd6~AVe_#Zzhqia`L6cH_d2=$ znTG9spy6AusH5PVM&vw|$g&oh64xqImmZcV{}U1&St-%IH8S0|UZ;2F$&8;B8gn&4 zW7vq7SzNnEmt-E$r6q-$KCMKZDapFbCrvZ# zp_=i{p;=x@lJ#VmF7FAE6_>thc88~|Y#1szEuUmn%@@h7Zfz|8hBCH`m3&ecSP6qmTB?5DqY{{)D35{wdC#=*|Zc-8Mr4^rFN#&4lTKVX-%wrJ(>AlNvTtPL$j*_iTbjKin@4t!Y#-S_ z(g4x{(gM;0(ge~4(gxB8(g@NC(u$?&1!>08bc3{m^n)~nbcD2o^n^5pbcM8q^o2Br zbcVEs^oBHNX}Uw&vo!r74I&*PEh0T4O(I<)Z6bXljUt^Qts=c5&03mnk#;RjzevMK z$4JXa&q&iq*GSt)-$>&~=Sb^F?@04V_elGerhnuHAa?+{1;{->ZUS-_klTRV2joT| zcLKQ;$h|;r268u$+hJ+$2XaF!%^g8*335-6n}XaGC^m}%m*Ry9k7iT-4 zcWA|i+8p2CPPBW&hx3^G@O5K9#*!!t)WmCH>KQ8zjHx8*Ju6N5 zT&06wX;I{xTGZF0n+9Ic;?9>;*87G9ceQHCf#>Yzh6dfzy3Ll}_NXnZUuWgB>n&7P zYPb5A*z)w5wtO_pDq>4i@$qGL`^XHfcigfP10_yE9$h`i(a+$iDv7+e#+{`!nUEO+3q|Yvb*-LwEA~9>h7II*3eO| zd+L(x-W~bcxU^8=TEFhgo~BL3KkNQUJ~d{>TI0|cYMMA|O~>C+^WZ6a;FS(-?(4QK zyWg^{oqO!T=6%+(tHs7ejEjgI{|{Hxg#Z5X`C_KH|FAD1IbyueH&MQe|GfY4=CAi< z!H_RdT+S`THzM3Y_YnFQi}}r+@Zj`%WI3L0J;;KP6(LJP)`TnySrxJ@WL?O@kd+}z zL)L~Y4p|+tJY;>y0+AIWOGMU)EYj1hl3&g;k#!;qMOKO|)zhsNSuC?&$W#G$T+h==07#j%>ON47Az<-3v*|i_tTA8l(k!B_8VXp&nPrY+MYE_ zM@}}&qNCLEr&p`2u-hi<#zOT`?s}E|LA+X(nQZ*mtEkm)PEcz`dFJEPxn}J%mCc6u z-ezOPug%8dy=K$Zjb`hshdHx2bptjX}dT{6W#nUUsy_u63p`_vx( zsR14Q(_LHmKRjOF|1tg^|4f~-{@JMJ-np_d-npVk@BGDZ@50d{@8V~dyi1!;c$c&C zyesc*^RB+-d!Y%7y=x;fyq^Z<_}9B-`!^C7`8S)0|8w-K{;h~qzp%tWzwmm{FF!v@ zRhUsuRh-(~=(HG9X;7r89IPP`iAAPLlV7E3^hK!_aa^jGI4ISxegd+(^aHWYblsZu^n>MU=z3v^ zQt#%&67yqSsh?j>9@8mujp*k$LX;hdw=XzG4xoVHcGK_5w6a)FLdU8|cUY|#&Q zovWMHTBw`FkJHU=r|T9`bEL(I{<>w+SZS4$s2@3&B&}EUlY}j;pq#4bn@Cux^Mb*-EU5we!gG6?mzj03}~}K4@^EFgKE#ygA=l4Na=Ml zw3f)wbMMHo+o>{q-*_2uA}B9>_?)Ed8LeM@BSA*4=%!y9R$E3*Z>mQ>+ghg%sjgp+ zyH}^R`$dnbr1aRvRb_1Glpa^%mW(^JOOL-&AQSu*dSc!dnKW;QOwL{?uZ&+N>9f)$ zaQ9s$N{0Q-S1J2@cfSATzY8y2=8jj{a{uA0xHuG}l>01cS4IU&7Z+a(sd9Jz{Kq#H zbU-U*KdbP!D)P^teBY^aP_?)32A-!HdU4h`)w5l6P&gSNE?toAdNsefwThY1=0+p8%R5lejp7&I)bzW=?T&lq$^I_7NjpoW01}u ztwDN&GzaMp(jKHgNQ00LAuU3Bgft2164EB8?GyjOXcP~f;-OVYuaIUT-9p-h^b2Vi z(lMlEPTMo2X-L*T+BPG7cG^ZGokm)X^crb4(ru*ONWYPWBOOOtj`SR9I?{EgZ9CF; zr)@mad8GA7?~&#s-ACGw^dGqakUIdm1(16Hxe1WF0J#m2`@m^$1msRYZUy9CKyC)) zZa{7aciO&T?A&mEQ}hObVC*3X5;d*O)NM6;HCOFLvurJE`hUluOP9Jdf0)lObDQ5cvUS(aW!4`r z->6ryGlzUr;hNonBBBmp){qnZ^h`zgWN8UF^^~1?}89g7= zvE9ezsS+s@sS_y_sT3&{sTC;}sn*q$i`0u0j8u&OzvLVfbs*;_NO)Hd diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/South_Georgia b/telegramer/include/pytz/zoneinfo/Atlantic/South_Georgia deleted file mode 100644 index 446660861227aa8ceb7084f48c3b2654ea64f4dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa(`2YXi0}KrR|37}fz~bW@!k}wl#1KM)NkBvX TXSV$Xnt{(mE*qe^c1BzPW>qAj diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/St_Helena b/telegramer/include/pytz/zoneinfo/Atlantic/St_Helena deleted file mode 100644 index 28b32ab2e0b9053f39a91d9f28b6072e41423954..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148 zcmWHE%1kq2zzZ0GvP?kCG3nVP561uh|5!kkv-tRiFt`J82nmM#2LhZ1aRE&;-~s@2 CSQl9U diff --git a/telegramer/include/pytz/zoneinfo/Atlantic/Stanley b/telegramer/include/pytz/zoneinfo/Atlantic/Stanley deleted file mode 100644 index 88077f110715ac1e349708821ac1e00f35bf6395..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1214 zcmd7QK}ge40LStFQn6NtgaW6jvr?RCzHK^Fr#(~Dmf@;hT!&^vXb@4iPP2lNAjrCR zsF0|jSCD1kuDvUY3)*vzjw&Y`$d&~T_K;$%&DjMwVV%~ z5}uM@((`g!m3>Qj%WobM8=g;jE6(?c${YJt)sc|&PIRj3NU7X7TBSDmmgMFEm#SIy zPS*M>MeU~T?~F*?jn6f3)-X?R?i&Uvzss zlaQxZN4wi^C3cK_iT0FTNc46;i}o%ZOY~LWjrJ|a|5$S^E*G^$wjUW<#%N# zbAxC6`J~p`Rj^{&Q&ec4k%5tsk)a*U*vR0>=*aNM_(%Xq1g0H} z5FAYmNDxRANEk>QNFYcgNGM1wNH9n=NH|D5NI;GzA|xb76B7~?5)~2_5*HE}5*ZR2 z5*rd65*-pA5+4$vqlpj+(b2?+1c^k6go(t71d2q8go?z91dBwAgp0(B1ng)cMnZNp fF(W}cny8Vmk+_k-k;pM=w)`(bcZW^(T|xJ6rOjxH*U)-=Z=%8eBQtBVe#Il5_B8|$WGX7+pDy6mRAdfx5#ynCJ|%$97M)faB@d)n--6Wy9! z)@*ZZ$mXtoP4nu0)ZKIUYJUEp=8s>~g2XN@7`~*$uwzQ>dPfV#g1Wc$GfVp7s4Z%G z%aXerET!~_`HvS_>gIA=+?ZxdmS@`1jZv01E8do+PS~;=vh=t}rCsE5}C)+fB#Y(RZS=n%}mG_>tN6uf?qi+ssbL)rN@?w`BYid`;wqts{^nfb!f~rcd zR&_$LYNB&gJH5fS`YLVf&qcOvG|TEfPqXcP_t+EfM%k06Z?&ghnXsqrn(e6n*6O#1 zs-f&RHRg{gm@%Sf5(l(1rboNN-qx;(Q`$XtR88NQ_I%o)XFsUabKQsS`Hp7WdwhpA zAE>Z>jT>x|@0S12BOSizF0xLZ^|@R9a=XI*I@c8hv?@D`qbMNxd<-u-us>jfzz%^edUksRHVNz!vrW%#pO}q$c00vv6|+~&W-+_PY!}!sX2X~r16v06 z3~bu7+cmIl&u-tq#yz{816v37j@dkB_n7Tt_K#@*rURH3V0wUQ0;UU?Hh6Y@z%;_M z>jb71m|kF-0dxas2hb0oAwWlfmSB1UGzHTYOj|I00UG1kbp~h+&>NsRKzD%l0Q~_P z1at^!5vE6&CSkgSX%nVTm_~Vaox-#V(<@A~Fx|qm3+NZnFrZ^V%YdE%O#`|Hv<>JR z&^VxTnAUlAy~8vQ(>+Z4F#W?c5Ys_S3o$*!G!f_`&_ZW=qAuk ppr1fPF&)LU6w_1ue{O0xbzPL|r?g20ss7~DWPeh@Qk1eJ{7;jB{Fnd$ diff --git a/telegramer/include/pytz/zoneinfo/Australia/Adelaide b/telegramer/include/pytz/zoneinfo/Australia/Adelaide deleted file mode 100644 index f5dedca59e2b220f7395c73f60ff26e610373e8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2208 zcmd7UZ%kEn9LMo<|1iFn62urI4dPFx6n`#Y3=9!PQj!aVHv3bXr5f#ZYxJjS>WTYaJ;N@Ix&FT1@@lHw`uTSGPL^3@Q$sFIP_e}N3y^)KW zb?K01_b=3(lOfIBRw3SljhffIUh}(3v|vfOE~-n>`;zBtVZNn>e@1K3yksf*YD$a$ zn3lz7$Mya%C#7WI2U&9FoRo$?kflckq-@h$vTX0DF0a@pQO zRUb}`*R`{qx_0^(edPL8t^fLpHcXt?#&<{b(No__)6ivUj=U!=IxLTc24&smh^()F zK^|Y#BO8i3WaGjn*_7my*7%UNIRe^tqfs|cuGYZC5^W#N){eJRbj#~;+WC^DPxRi< zCxhQ;usv3uYM7R;%AaIw@uY0ayd+QGeoneQ?@0IW1G0VUkc7VIlN}dA(sMc>J5Ro% zyN-qRnS1=) z6|yZ`voB<0wq|F@){wm+n?rVoY!BHVvO#2r$QF@3BAc`|yF|8WYxaq3)Yj}2*{ZGC zE3#Q+x5##p{URGic8qKp*)y_fWY@^Hk$oc@w>3LQwr*?oj%*&;J+ggd|40Ln4j?T+ zdVn+m=>pOQqz_0VY)vPSR@j_mK7>{X-gvbP#DF(nF+)NEeYdB7H;}iF6Wa zCDKcznYN~zNIQ{!A`L}4inJ8zDbiG=t4LcNZl~)eI{%I9?gRW~Fo~Us>r80fGl?~I gwL5D!Hip_}7cVaG@+`}j=grB>@n-oJL2g0J-ZwkLDAd!93UFxWH1eMHi!l}9ZUnA528U{ z0MQ_CfM}3cKs3lZAeVu>1foIS0s)ZMz%QKLjx`V Df+TPt diff --git a/telegramer/include/pytz/zoneinfo/Australia/Broken_Hill b/telegramer/include/pytz/zoneinfo/Australia/Broken_Hill deleted file mode 100644 index 698c76e30e91f568a29daca12993cfacbfdbf83e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2229 zcmd7Ue@xVM9LMqRaYww56hxho2Js_i6h96y28M_tDairhWd{Li{y;$T!om|xS$X4K zHb+)^b8MN8fvv4&1?t=yb4fYAAT7v|vGk6nrtE zg}+bB;yfn=ck=%W$T)zMiOCUyjwb=PZ4s zYf85TzSclXj67O9E$!t$%J#x>c`W^sJbv>z>2SX(9l!O-j)_AO{H$AcUI1Nq= z@&(;}ETm5y?9e?=HtF7Wzd9We*U@QN`+CiTY&}2VFi&+TY`OosR(O5ZveFe?*7z*T z;jn!Fzl|IC1atJ57w_jxn`8St-H08ozOQiCY-a%3jIG%XvK?EqA7n$wj*u-Od*Vyj z6tXL1TefCj$i{5V&XBFyn!O>LLw1L357{5GL1c%>7Lh$7n?!brY}3~46WJ)TQ)H{Q zX0OO*ZOv|x?IQa{HjL~T*)pUUMmBD1c8+Y_*6bbGysg* zGe~QY-XP7fHQhnlgY*Y!5Yi!}MM#g3CLvuy+Jy88X%x~aq*X|-kY?GMZXxYL`h_$M z=@`;7q-RLekgg$ZL;8j^4(S}yI;3|<^N{W#?XxxgLmG&55NRROL!^mF7m+q1eMB0G zbP{PL(o3Y7NH>vo+M0eM4MjSNv=r$n(p03YNL!JjxH*U)-=Z=%8eBQtBVe#Il5_B8|$WGX7+pDy6mRAdfx5#ynCJ|%$97M)faB@d)n--6Wy9! z)@*ZZ$mXtoP4nu0)ZKIUYJUEp=8s>~g2XN@7`~*$uwzQ>dPfV#g1Wc$GfVp7s4Z%G z%aXerET!~_`HvS_>gIA=+?ZxdmS@`1jZv01E8do+PS~;=vh=t}rCsE5}C)+fB#Y(RZS=n%}mG_>tN6uf?qi+ssbL)rN@?w`BYid`;wqts{^nfb!f~rcd zR&_$LYNB&gJH5fS`YLVf&qcOvG|TEfPqXcP_t+EfM%k06Z?&ghnXsqrn(e6n*6O#1 zs-f&RHRg{gm@%Sf5(l(1rboNN-qx;(Q`$XtR88NQ_I%o)XFsUabKQsS`Hp7WdwhpA zAE>Z>jT>x|@0S12BOSizF0xLZ^|@R9a=XI*I@c8hv?@D`qbMNxd<-u-us>jfzz%^edUksRHVNz!vrW%#pO}q$c00vv6|+~&W-+_PY!}!sX2X~r16v06 z3~bu7+cmIl&u-tq#yz{816v37j@dkB_n7Tt_K#@*rURH3V0wUQ0;UU?Hh6Y@z%;_M z>jb71m|kF-0dxas2hb0oAwWlfmSB1UGzHTYOj|I00UG1kbp~h+&>NsRKzD%l0Q~_P z1at^!5vE6&CSkgSX%nVTm_~Vaox-#V(<@A~Fx|qm3+NZnFrZ^V%YdE%O#`|Hv<>JR z&^VxTnAUlAy~8vQ(>+Z4F#W?c5Ys_S3o$*!G!f_`&_ZW=qAuk ppr1fPF&)LU6w_1ue{O0xbzPL|r?g20ss7~DWPeh@Qk1eJ{7;jB{Fnd$ diff --git a/telegramer/include/pytz/zoneinfo/Australia/Currie b/telegramer/include/pytz/zoneinfo/Australia/Currie deleted file mode 100644 index 3adb8e1bf7c6ec51f1c100538799271d7d7a6e6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2358 zcmds%YfP4P7>9qa=m=8Da*7C~R-8f(9w8M^990tI1%VV%Ia($NB3ZuOQ|oOlSDBkk zOOVFsiv#JPH5Qp|tPxmSN7EeD*68Hgn&nXGxt{vim%g>GXS=Wep6|cQTT)XwNB_v! zzyBU}%zL6a=h&xB_EJxxUcQiS$8EE`#bcIT09%_#QSOdGUWD~{=|njdxd zti8G?XFyS7HYqx`Q_;V@shFRKEcTOT#SNUZdtcw9_>=86`|t)O9Q0daLz!jKUw9R11GU%6yOmj|u* ze6N-CcG-hxE^5Q81KQa9j!K{I)TX9Zl~uo>%|-jPCA(f*Q_58yQ=p2_OjS;*vntOP ztNJzHs)y38=HvOc?QE1ibZV+SeBu_{{`@t2#D1_HwO?87wm{Vt|Dl~Z!>UicpoZ9f z?V8r3#-NjGymnl>ho4i^m!?NQsMDUew&=0$gZ6lv&-NbLVZME3_QcM07T_84AN{0f z!krQ5XqU%X#Ivmm`s=RA^=f%WIw#*Nc|7v|i(QR6G$iOd%)T%i!|V*RHO$^Ho5SqR zwX;3U{xBQF>=3g>%pNhD#OxBg+vLGM!PzKcr@&T$y<#>C?AEojUCe%6I~&IA*tN4| z%$_lu26hc>8`w9nabV}b)`7hPn+JC9+Sxv^f1m+C2V6TX0D9osX#%DTm^NVgfN2D# z6F@64y}&dB=myXZpdUa(fR4C!S_1S0(-fd9n6?0Y!88Ww45l?eZ!pclbO&e;rawS~ zFdcI3vr!cJodWC5g&@D{6fPP^bhUpllWkAndJ52++2DA<6 z8_+nQb3p5W-T}=6x`$~Wrhk|QVmgRvA*P3}ohD+sh-o9HkC;YcItjEA=q1ohpqoHD zfqnuF1v(0}6zD0?R7_W0J8i}E71LNuXECkC^cK@xOm{Ku#q<|wFwkM3#XygNCIek| z?X(%_Gtg+D(?F|%USpb#={Ba_`2W)HU`jqaIf<&r=O!m5Bqt=r=gvckbA$f`>c{K^4dO zc7qOPMkWYkV5nLFRL?MZ1tU;%>kI~dp3EU4p68-P>i_@$&&fL85DoGxmG$;(fG%y@MbUn~6O2dN71{^MSx)xR@rd$9)WNteE diff --git a/telegramer/include/pytz/zoneinfo/Australia/Hobart b/telegramer/include/pytz/zoneinfo/Australia/Hobart deleted file mode 100644 index 3adb8e1bf7c6ec51f1c100538799271d7d7a6e6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2358 zcmds%YfP4P7>9qa=m=8Da*7C~R-8f(9w8M^990tI1%VV%Ia($NB3ZuOQ|oOlSDBkk zOOVFsiv#JPH5Qp|tPxmSN7EeD*68Hgn&nXGxt{vim%g>GXS=Wep6|cQTT)XwNB_v! zzyBU}%zL6a=h&xB_EJxxUcQiS$8EE`#bcIT09%_#QSOdGUWD~{=|njdxd zti8G?XFyS7HYqx`Q_;V@shFRKEcTOT#SNUZdtcw9_>=86`|t)O9Q0daLz!jKUw9R11GU%6yOmj|u* ze6N-CcG-hxE^5Q81KQa9j!K{I)TX9Zl~uo>%|-jPCA(f*Q_58yQ=p2_OjS;*vntOP ztNJzHs)y38=HvOc?QE1ibZV+SeBu_{{`@t2#D1_HwO?87wm{Vt|Dl~Z!>UicpoZ9f z?V8r3#-NjGymnl>ho4i^m!?NQsMDUew&=0$gZ6lv&-NbLVZME3_QcM07T_84AN{0f z!krQ5XqU%X#Ivmm`s=RA^=f%WIw#*Nc|7v|i(QR6G$iOd%)T%i!|V*RHO$^Ho5SqR zwX;3U{xBQF>=3g>%pNhD#OxBg+vLGM!PzKcr@&T$y<#>C?AEojUCe%6I~&IA*tN4| z%$_lu26hc>8`w9nabV}b)`7hPn+JC9+Sxv^f1m+C2V6TX0D9osX#%DTm^NVgfN2D# z6F@64y}&dB=myXZpdUa(fR4C!S_1S0(-fd9n6?0Y!88Ww45l?eZ!pclbO&e;rawS~ zFdcI3vr!cJodWC5g&@D{6fPP^bhUpllWkAndJ52++2DA<6 z8_+nQb3p5W-T}=6x`$~Wrhk|QVmgRvA*P3}ohD+sh-o9HkC;YcItjEA=q1ohpqoHD zfqnuF1v(0}6zD0?R7_W0J8i}E71LNuXECkC^cK@xOm{Ku#q<|wFwkM3#XygNCIek| z?X(%_Gtg+D(?F|%USpb#={Ba_`2W)HU`jqaIf<&r=O!m5Bqt=r=gvckbA$f`>jMJy=;jG(DAOR!g&UdfG30@Y}OA zHhYj3g(Y2V6@(E)Wl=8LO2&|gh$I?qQCSxuUG!T7{RXkq@BL@jgWU|~!T;+VypRLm z_b0qK+A!Yv$Dd91uY>GwQnP)0_*cWzgixVLc(Ff{m|iQ1J!>P$fvJ+*Q5H$*OP7%? zZX~rkPCnXtP^BF^Yd)@4D!sW!Mon+kqodEwm{IHX*y3Nz*jK;njLZ&`arZ}^8NbhD zp8sBd+P}tRwO?1+y?bSxe5Zn)tL3w*X5}6(5wEsHjc*J}PJU2Ls2C;_Q-`TZ-fNlk z?zIY~3^Acc_f>AkGnst-ikk9UugN>ztMjj2HU(c_)>DrkHH9mW>Sj>LOjzQEp0)g}E1$c|G=?K~WjU3)6j?#8*YI~I!t;s*Tv8W{H%U!1A^X0eS4O^^6D zoGWs&zV>X9)AhCIi=42pJ!9mQk#j~)8aZp^w2|{hP8>ON}ND@dENE%2UNFqokNGeD!NHRz^NIJeYA0#1Pn-P){k`s~?k`SW@B#mT^q>bc_B#vZ`q>ki{B#&f|q>to}OaL+i$Q1b6IY1@>nFVATka<8R0+|V9 zDv-HACIgubWIB-fKqds45oAhy?VKQ!g3JmsEy%ne6NAhQGBwEDAd}mVWe@;)8%zVe k4gw(WgK1z8fB+~MKmZgJAQ}`LAest6!UYN(T|)yd00dHiMF0Q* diff --git a/telegramer/include/pytz/zoneinfo/Australia/Lord_Howe b/telegramer/include/pytz/zoneinfo/Australia/Lord_Howe deleted file mode 100644 index 9e04a80ecea45473faabeb609eb06cfe62193d48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1860 zcmdVaT}ah;9LMqh$-1zMYO%zJoU;d<)6V|?tT>jMJy=;jG(DAOR!g&UdfG30@Y}OA zHhYj3g(Y2V6@(E)Wl=8LO2&|gh$I?qQCSxuUG!T7{RXkq@BL@jgWU|~!T;+VypRLm z_b0qK+A!Yv$Dd91uY>GwQnP)0_*cWzgixVLc(Ff{m|iQ1J!>P$fvJ+*Q5H$*OP7%? zZX~rkPCnXtP^BF^Yd)@4D!sW!Mon+kqodEwm{IHX*y3Nz*jK;njLZ&`arZ}^8NbhD zp8sBd+P}tRwO?1+y?bSxe5Zn)tL3w*X5}6(5wEsHjc*J}PJU2Ls2C;_Q-`TZ-fNlk z?zIY~3^Acc_f>AkGnst-ikk9UugN>ztMjj2HU(c_)>DrkHH9mW>Sj>LOjzQEp0)g}E1$c|G=?K~WjU3)6j?#8*YI~I!t;s*Tv8W{H%U!1A^X0eS4O^^6D zoGWs&zV>X9)AhCIi=42pJ!9mQk#j~)8aZp^w2|{hP8>ON}ND@dENE%2UNFqokNGeD!NHRz^NIJeYA0#1Pn-P){k`s~?k`SW@B#mT^q>bc_B#vZ`q>ki{B#&f|q>to}OaL+i$Q1b6IY1@>nFVATka<8R0+|V9 zDv-HACIgubWIB-fKqds45oAhy?VKQ!g3JmsEy%ne6NAhQGBwEDAd}zl+Ln#;nuZlq_dEl60C@%<WR zO=e7y=IElrXial0qS{y?vbyL%nxh+|+FUmkGqeBasmpGkU1S;z5 z7U>_E^!L{noq3;P?r2~4p|$s>YB2DQ1_x^G{Fzc4>dLbV9YGC;l5Ao_rcIhWLt#bn z7WPYqg{NM(@Gs&lBI>e5y!*RFUhTJ=PsHk$ufEmf)@zz__5($2zNA~*IyE(`Pq(!k z(CrB)HLa{p)Bf16=!|?tUpZkhvuiA7;8~0FXEnWeNN#pd5sVT+p5ZSnGDwq(O}OP`rwOa0@v^tvn~K3W+Uzq8D)F}t_- zW4mu;M9WT%YWcZdWgYxT_a8g16}6|da{oD7l@rqH>UPUcdChWGw_0x48s*Kcw7g$C zm4D@B6?}O>4-D+mnlp7;+qF@JNAtBVl&J@s7pSPA#MW2l+J>TgtawGD1=3=zWLBh= zMqRVg8$a2`tCy_o@&zj&?z4)%)ArEWi+cF=A#G}VUz?xn)|QqIRc<@1N6Pl8DnF>| zj2hJ>mZ&x+S9MbxZL6=!w*Fjf+eWjk{Z?yJCB_JK|049zU+;u|sP4+O+4BMm_mnm7eN(!Jh7HwY^7n zSnIw@d!}igPKY@z=SwFrbw=*&M|I$>&qxUu@%sBN%-! z2D1^*ZYP+ncy@ciYzDI%%yuyQ;m_C*W=Fu5Ji9$%Hs#sv3bQTGZeN&y4v1ZWDTE10%m`T{h@v+E4d8lX2obAavu?E(4& zGzjPr&>~EaFipaA3DYJ_pD>N`>^g;M6{c61W?{O8X&2BhpkYAAfR+J01DXbO4QLzC zH=uDq=P<4F?0Sc39;SPk_F?*mX&|P9m=!odjCx+4T}=CeTfw qoj^ZjxH*U)-=Z=%8eBQtBVe#Il5_B8|$WGX7+pDy6mRAdfx5#ynCJ|%$97M)faB@d)n--6Wy9! z)@*ZZ$mXtoP4nu0)ZKIUYJUEp=8s>~g2XN@7`~*$uwzQ>dPfV#g1Wc$GfVp7s4Z%G z%aXerET!~_`HvS_>gIA=+?ZxdmS@`1jZv01E8do+PS~;=vh=t}rCsE5}C)+fB#Y(RZS=n%}mG_>tN6uf?qi+ssbL)rN@?w`BYid`;wqts{^nfb!f~rcd zR&_$LYNB&gJH5fS`YLVf&qcOvG|TEfPqXcP_t+EfM%k06Z?&ghnXsqrn(e6n*6O#1 zs-f&RHRg{gm@%Sf5(l(1rboNN-qx;(Q`$XtR88NQ_I%o)XFsUabKQsS`Hp7WdwhpA zAE>Z>jT>x|@0S12BOSizF0xLZ^|@R9a=XI*I@c8hv?@D`qbMNxd<-u-us>jfzz%^edUksRHVNz!vrW%#pO}q$c00vv6|+~&W-+_PY!}!sX2X~r16v06 z3~bu7+cmIl&u-tq#yz{816v37j@dkB_n7Tt_K#@*rURH3V0wUQ0;UU?Hh6Y@z%;_M z>jb71m|kF-0dxas2hb0oAwWlfmSB1UGzHTYOj|I00UG1kbp~h+&>NsRKzD%l0Q~_P z1at^!5vE6&CSkgSX%nVTm_~Vaox-#V(<@A~Fx|qm3+NZnFrZ^V%YdE%O#`|Hv<>JR z&^VxTnAUlAy~8vQ(>+Z4F#W?c5Ys_S3o$*!G!f_`&_ZW=qAuk ppr1fPF&)LU6w_1ue{O0xbzPL|r?g20ss7~DWPeh@Qk1eJ{7;jB{Fnd$ diff --git a/telegramer/include/pytz/zoneinfo/Australia/North b/telegramer/include/pytz/zoneinfo/Australia/North deleted file mode 100644 index 74a30879bc6180d588a706451226cb4c95faf79d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmWHE%1kq2zzSHPqMSe)#O7-N5_9}ccFfsh#&J4i7su)2IUQ%V$99}u?$>c{K^4dO zc7qOPMkWYkV5nLFRL?MZ1tU;%>kI~%Tj6xbd4;gu z&K#f(40RJ2fz}l?FmOUhAKwrL$8Z-A5gfw6z{toDLV~0J13}3$u_quJ+uWJp!UZo&nJy4}n|)@)Vc`dJIH^JO`$M9t6=KPl9QnM?o~mvmhGeVGs@SG?)f@ U97KaW52h&%04`8a=vr_A0IP9yp8x;= diff --git a/telegramer/include/pytz/zoneinfo/Australia/Queensland b/telegramer/include/pytz/zoneinfo/Australia/Queensland deleted file mode 100644 index 7ff9949ffa93e44835ab133998b89e440094f909..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 419 zcmWHE%1kq2zzSHPqJlsg#O7-N5_9|xHO$$g&u}_qGsEfQsSRhghc%pC?%r^2K{3Pm zcFhLn0?P&#UKs{e&$A4w?_V*fYp!QdKYxZnlXD`2=K6IE%#2K^kb$AU0%$J7tPPAn zOL`VCa6(8Q-w*~zR~HZw9KyiB$jA^vf>ZwkLDAd!93UFxWH1eMHi!l}9ZUnA528U{ z0MQ_CfM}3cKs3lZAeVu>1foIS0s)ZMz%QKLjx`V Df+TPt diff --git a/telegramer/include/pytz/zoneinfo/Australia/South b/telegramer/include/pytz/zoneinfo/Australia/South deleted file mode 100644 index f5dedca59e2b220f7395c73f60ff26e610373e8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2208 zcmd7UZ%kEn9LMo<|1iFn62urI4dPFx6n`#Y3=9!PQj!aVHv3bXr5f#ZYxJjS>WTYaJ;N@Ix&FT1@@lHw`uTSGPL^3@Q$sFIP_e}N3y^)KW zb?K01_b=3(lOfIBRw3SljhffIUh}(3v|vfOE~-n>`;zBtVZNn>e@1K3yksf*YD$a$ zn3lz7$Mya%C#7WI2U&9FoRo$?kflckq-@h$vTX0DF0a@pQO zRUb}`*R`{qx_0^(edPL8t^fLpHcXt?#&<{b(No__)6ivUj=U!=IxLTc24&smh^()F zK^|Y#BO8i3WaGjn*_7my*7%UNIRe^tqfs|cuGYZC5^W#N){eJRbj#~;+WC^DPxRi< zCxhQ;usv3uYM7R;%AaIw@uY0ayd+QGeoneQ?@0IW1G0VUkc7VIlN}dA(sMc>J5Ro% zyN-qRnS1=) z6|yZ`voB<0wq|F@){wm+n?rVoY!BHVvO#2r$QF@3BAc`|yF|8WYxaq3)Yj}2*{ZGC zE3#Q+x5##p{URGic8qKp*)y_fWY@^Hk$oc@w>3LQwr*?oj%*&;J+ggd|40Ln4j?T+ zdVn+m=>pOQqz_0VY)vPSR@j_mK7>{X-gvbP#DF(nF+)NEeYdB7H;}iF6Wa zCDKcznYN~zNIQ{!A`L}4inJ8zDbiG=t4LcNZl~)eI{%I9?gRW~Fo~Us>r80fGl?~I gwL5D!Hip_}7cVaG@+`}j=grB>@n-oJL2g0J-jxH*U)-=Z=%8eBQtBVe#Il5_B8|$WGX7+pDy6mRAdfx5#ynCJ|%$97M)faB@d)n--6Wy9! z)@*ZZ$mXtoP4nu0)ZKIUYJUEp=8s>~g2XN@7`~*$uwzQ>dPfV#g1Wc$GfVp7s4Z%G z%aXerET!~_`HvS_>gIA=+?ZxdmS@`1jZv01E8do+PS~;=vh=t}rCsE5}C)+fB#Y(RZS=n%}mG_>tN6uf?qi+ssbL)rN@?w`BYid`;wqts{^nfb!f~rcd zR&_$LYNB&gJH5fS`YLVf&qcOvG|TEfPqXcP_t+EfM%k06Z?&ghnXsqrn(e6n*6O#1 zs-f&RHRg{gm@%Sf5(l(1rboNN-qx;(Q`$XtR88NQ_I%o)XFsUabKQsS`Hp7WdwhpA zAE>Z>jT>x|@0S12BOSizF0xLZ^|@R9a=XI*I@c8hv?@D`qbMNxd<-u-us>jfzz%^edUksRHVNz!vrW%#pO}q$c00vv6|+~&W-+_PY!}!sX2X~r16v06 z3~bu7+cmIl&u-tq#yz{816v37j@dkB_n7Tt_K#@*rURH3V0wUQ0;UU?Hh6Y@z%;_M z>jb71m|kF-0dxas2hb0oAwWlfmSB1UGzHTYOj|I00UG1kbp~h+&>NsRKzD%l0Q~_P z1at^!5vE6&CSkgSX%nVTm_~Vaox-#V(<@A~Fx|qm3+NZnFrZ^V%YdE%O#`|Hv<>JR z&^VxTnAUlAy~8vQ(>+Z4F#W?c5Ys_S3o$*!G!f_`&_ZW=qAuk ppr1fPF&)LU6w_1ue{O0xbzPL|r?g20ss7~DWPeh@Qk1eJ{7;jB{Fnd$ diff --git a/telegramer/include/pytz/zoneinfo/Australia/Tasmania b/telegramer/include/pytz/zoneinfo/Australia/Tasmania deleted file mode 100644 index 3adb8e1bf7c6ec51f1c100538799271d7d7a6e6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2358 zcmds%YfP4P7>9qa=m=8Da*7C~R-8f(9w8M^990tI1%VV%Ia($NB3ZuOQ|oOlSDBkk zOOVFsiv#JPH5Qp|tPxmSN7EeD*68Hgn&nXGxt{vim%g>GXS=Wep6|cQTT)XwNB_v! zzyBU}%zL6a=h&xB_EJxxUcQiS$8EE`#bcIT09%_#QSOdGUWD~{=|njdxd zti8G?XFyS7HYqx`Q_;V@shFRKEcTOT#SNUZdtcw9_>=86`|t)O9Q0daLz!jKUw9R11GU%6yOmj|u* ze6N-CcG-hxE^5Q81KQa9j!K{I)TX9Zl~uo>%|-jPCA(f*Q_58yQ=p2_OjS;*vntOP ztNJzHs)y38=HvOc?QE1ibZV+SeBu_{{`@t2#D1_HwO?87wm{Vt|Dl~Z!>UicpoZ9f z?V8r3#-NjGymnl>ho4i^m!?NQsMDUew&=0$gZ6lv&-NbLVZME3_QcM07T_84AN{0f z!krQ5XqU%X#Ivmm`s=RA^=f%WIw#*Nc|7v|i(QR6G$iOd%)T%i!|V*RHO$^Ho5SqR zwX;3U{xBQF>=3g>%pNhD#OxBg+vLGM!PzKcr@&T$y<#>C?AEojUCe%6I~&IA*tN4| z%$_lu26hc>8`w9nabV}b)`7hPn+JC9+Sxv^f1m+C2V6TX0D9osX#%DTm^NVgfN2D# z6F@64y}&dB=myXZpdUa(fR4C!S_1S0(-fd9n6?0Y!88Ww45l?eZ!pclbO&e;rawS~ zFdcI3vr!cJodWC5g&@D{6fPP^bhUpllWkAndJ52++2DA<6 z8_+nQb3p5W-T}=6x`$~Wrhk|QVmgRvA*P3}ohD+sh-o9HkC;YcItjEA=q1ohpqoHD zfqnuF1v(0}6zD0?R7_W0J8i}E71LNuXECkC^cK@xOm{Ku#q<|wFwkM3#XygNCIek| z?X(%_Gtg+D(?F|%USpb#={Ba_`2W)HU`jqaIf<&r=O!m5Bqt=r=gvckbA$f`>zl+Ln#;nuZlq_dEl60C@%<WR zO=e7y=IElrXial0qS{y?vbyL%nxh+|+FUmkGqeBasmpGkU1S;z5 z7U>_E^!L{noq3;P?r2~4p|$s>YB2DQ1_x^G{Fzc4>dLbV9YGC;l5Ao_rcIhWLt#bn z7WPYqg{NM(@Gs&lBI>e5y!*RFUhTJ=PsHk$ufEmf)@zz__5($2zNA~*IyE(`Pq(!k z(CrB)HLa{p)Bf16=!|?tUpZkhvuiA7;8~0FXEnWeNN#pd5sVT+p5ZSnGDwq(O}OP`rwOa0@v^tvn~K3W+Uzq8D)F}t_- zW4mu;M9WT%YWcZdWgYxT_a8g16}6|da{oD7l@rqH>UPUcdChWGw_0x48s*Kcw7g$C zm4D@B6?}O>4-D+mnlp7;+qF@JNAtBVl&J@s7pSPA#MW2l+J>TgtawGD1=3=zWLBh= zMqRVg8$a2`tCy_o@&zj&?z4)%)ArEWi+cF=A#G}VUz?xn)|QqIRc<@1N6Pl8DnF>| zj2hJ>mZ&x+S9MbxZL6=!w*Fjf+eWjk{Z?yJCB_JK|049zU+;u|sP4+O+4BMm_mnm7eN(!Jh7HwY^7n zSnIw@d!}igPKY@z=SwFrbw=*&M|I$>&qxUu@%sBN%-! z2D1^*ZYP+ncy@ciYzDI%%yuyQ;m_C*W=Fu5Ji9$%Hs#sv3bQTGZeN&y4v1ZWDTE10%m`T{h@v+E4d8lX2obAavu?E(4& zGzjPr&>~EaFipaA3DYJ_pD>N`>^g;M6{c61W?{O8X&2BhpkYAAfR+J01DXbO4QLzC zH=uDq=P<4F?0Sc39;SPk_F?*mX&|P9m=!odjCx+4T}=CeTfw qoj^Z%Tj6xbd4;gu z&K#f(40RJ2fz}l?FmOUhAKwrL$8Z-A5gfw6z{toDLV~0J13}3$u_quJ+uWJp!UZo&nJy4}n|)@)Vc`dJIH^JO`$M9t6=KPl9QnM?o~mvmhGeVGs@SG?)f@ U97KaW52h&%04`8a=vr_A0IP9yp8x;= diff --git a/telegramer/include/pytz/zoneinfo/Australia/Yancowinna b/telegramer/include/pytz/zoneinfo/Australia/Yancowinna deleted file mode 100644 index 698c76e30e91f568a29daca12993cfacbfdbf83e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2229 zcmd7Ue@xVM9LMqRaYww56hxho2Js_i6h96y28M_tDairhWd{Li{y;$T!om|xS$X4K zHb+)^b8MN8fvv4&1?t=yb4fYAAT7v|vGk6nrtE zg}+bB;yfn=ck=%W$T)zMiOCUyjwb=PZ4s zYf85TzSclXj67O9E$!t$%J#x>c`W^sJbv>z>2SX(9l!O-j)_AO{H$AcUI1Nq= z@&(;}ETm5y?9e?=HtF7Wzd9We*U@QN`+CiTY&}2VFi&+TY`OosR(O5ZveFe?*7z*T z;jn!Fzl|IC1atJ57w_jxn`8St-H08ozOQiCY-a%3jIG%XvK?EqA7n$wj*u-Od*Vyj z6tXL1TefCj$i{5V&XBFyn!O>LLw1L357{5GL1c%>7Lh$7n?!brY}3~46WJ)TQ)H{Q zX0OO*ZOv|x?IQa{HjL~T*)pUUMmBD1c8+Y_*6bbGysg* zGe~QY-XP7fHQhnlgY*Y!5Yi!}MM#g3CLvuy+Jy88X%x~aq*X|-kY?GMZXxYL`h_$M z=@`;7q-RLekgg$ZL;8j^4(S}yI;3|<^N{W#?XxxgLmG&55NRROL!^mF7m+q1eMB0G zbP{PL(o3Y7NH>vo+M0eM4MjSNv=r$n(p03YNL!J zly}X7TU&aNwbrPtk9C83W#4Ud=4A6KbRQeH^2yt9KQG3EujHZot&Zf+(6Vahva(%$ zc0=ba2#X`C(@M+Vvr9E`Uy_ptA8P747o-+m)%5X+Ob@)M?2a!t{e<$h*0vS1`fOWk zwI_tuRb?f#uH1-z&f@>Pw(g0U*+sK6!s}sb^I^^8l7hdF}`Ba&Jz4!x`?)^N-K1EQg&RpMHB=XHU!a!O~)?Eq0Om3afOv zS7!3d;$}q4o6@t)wqNROdR*jcjWWM`Aqu%IS&WCo>rPp}HB5>3qZ#?pDpX~u#@y~XjxpsIx89ihqSG`n8FvN-hwKSE@qZMt zA2n1#%J|wkNFk&WQp(rXLW=p?YDhVx9#Rmgh?GQXB1Ms^NLgQ77b)y(D diff --git a/telegramer/include/pytz/zoneinfo/Brazil/East b/telegramer/include/pytz/zoneinfo/Brazil/East deleted file mode 100644 index 13ff083869a9ac8476775587de8476654a100017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1444 zcmcK3%}bO)0LSr1D=96lLqvr-CL)&Fv}?MSnI)!{Ynh9wNeGo_9f~NbLugbI2!*`~ zb{RT^bcp9rq(Wd}sV7T}Ky=;o)Qgl+x~$gIeCH1+Iyeu*=kQ$S@csF!8|qxvUr&wx zyGY;V_a2>j(iu}6==_zZx~4N^xBsjd@jR9zpF?tV?FW0bt4fT`t(N}#elb4QE(1Qd z_))z~{#x#K1l8P^2sz(!NyQG_kZ~8@t9Z557Vbfn zaJfNli0W5~l`HMUiPIt}>${ya+$WL~Vr263NtI$v%al7UV&kV3_NJ3AmD=-Erq%U| zbk!y|?|CR>(?z)@)~j5Hgzb(vAu>EZnGt*;GDW=19IRJaGp}t==QWW%J}I}|OjXE=i_Wt5q;=q;TvP@*FgGUQxxwT&T@>8VmqeoOE zM#+kSb)qtSN>+Bvi>ir1S$*9v4*P5En))8m=r~Sz*n)lGjx$_hS&lOq2wUtp@7`ET zOT0cSO}g}TTfYCOLO;}@45SXDE(ECrDFvy;sEa|WG3s)VdXR#Uijb0!nvkN9s*tjf zx{SIoq%xx}4XF(&j^?QjDG#X+DG;d;DG{mBsEb6ZH0m;uI*qzeq*9|U6{!^|7O55~ z7pWI17^xU38L1g58mZc-%SP%p>cWxAkZi+k@AuHkp&oV$vAuBWLr6FrG>ct_e dLzaiE4_P2K>Q)wPgJI$SSYc$2-eRsh@()nK$prua diff --git a/telegramer/include/pytz/zoneinfo/Brazil/West b/telegramer/include/pytz/zoneinfo/Brazil/West deleted file mode 100644 index 63d58f80f556e1c3135d6dbcb2a2dfc6f61b4fc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 604 zcmb8sJ4?e*0EXctUMTUlxHza)*A@=8gNVrSGO8d11b;w9aB|VD=-OQb9URr^jIDGy z;85H|(V?`pi(5biFQrwE=gkj@m=K;oAmMuxY;NbKo!BF3pLW@+|L8KmTpZeXE&i;j z(%YiEY8;B{Oj%aLgshFc=vrw@)H_qMaT1EB`nhZd3*!BFK!5b?h)=yMzXnsPwLU9b zm3z_lo4S3`t2*JZ{4NyK=*_7d+kaN$XG=PnsjG>DJvrI;s8ae`rgL{9Z;bKW*wZtn zQg$3;9&X)kd#|0u%4*=GMaEuPC-}!w`!T~>WO1ZjjVwpjBLyOD1*Alzt$`Flsvu>M zI!Ga;5>g7Og%pdl)sS+LwjNRtsmOn%BvKP8id03)B6X3%NM+31cg1#gy+2C(bGG+< G*8c@X3VoGa!& zHI{QoS}|75PF-NFp|$>w7LwIi!*Xr8vVU`K=IqZ{{oYSqcGF!wXM0{}zpLls_jyOw zZEGp={_(l+@6%mQ{?8bg8Q3^5dHl@2sh9?b4(XXUr2V~nG&ZAM*=GdjZVOd9+lACiiE0U(!H7o40;@^~#{i9N6f2lzH zQ3cMvskD=?=wLo?Z(Okg__fCMb0UOL(^87@S&=*zp|>+-`dLfw{7LbnB8>pd0Q2G$*RZS zQqAr@t3CLvZff0XIn%2##yRKD{>MGqDsMXrw0XiLEEFPv)~;a}~6 zWY0SDQ`C9pYwJonudZY7S@-EFJ^1RdJv1?@?R($Rj@Y0c?jBOl?jFT?7sUS;ex8?P zUV`Uk%!_l^_OCZ5p>SQq3xy-@FfK0gFE0Kc?&0$Lx&a^~K!$*f0T~1`3S=0_IFNxL zBSD6Oj0G8t`#_J;Aj3h%gA5265i%rXOvs>+Q6a-Z#^vhXKLIQ?F3<(($GbCt8)R3?taYF)!L=FiZ5<4V#zAk!5 u_T diff --git a/telegramer/include/pytz/zoneinfo/CST6CDT b/telegramer/include/pytz/zoneinfo/CST6CDT deleted file mode 100644 index ca67929fbeb05083c63e8319dd9ebf65b3d75e4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2310 zcmdtiUrg0y9LMqJpvE6LNEU@iQd%$u zEKB|4CfI*?xV`q_JGSePPak;iPdV5Yqq|=`CtvLh>eDl>$m!}xbM0!a8olHv>ItR4$_0N5;M_k%wPZ zQRaD>c)mkNe}B|?Pd4h96MM|0H}HtY1>e3{)+p))#W zN@i`7%Gwkma|+AV-AgY^cJcx>H|uMe7oMncrd~2R7lUg4=$B@}=jYWu=iV|4kBsTu z_n$NO?jF+jy(DH){Yia)>n?eq0-#w)TcF-ZrOpOL_rN0(*yN!c&c zRQa^;O!;R@RRoWll|4VH%ArnE)%vM=xPPlzRdYx^(p6<1E&fQh`v>v^two+=m zZF+5CuB;tir)!gwr1qO4T^H_=y2C!b{^HN__&&dS;#A0NXi8O29=TxZ%e+c!zF@X&O4Ex5T%}mi}Y9`RY2^+SQ@Aoh_2>Esgr=KA*I# zTA`omj1iCWsIYPN^#xAS z7*og|gF;4y3=0_-GB9LhPCGPYY{=k{(ILY_H$FZXAZJI&F+^mH9D_tgi3}4NCo)i{ z9Vs$YryVQDV3E-x!$ro63>XaRa|{|8HOH`#adQkD89B$$k+E|O-f2gV3?CUk z5&$FuNC=P^a0CH~0!J8-IB*04i3CR|oHiCnFq}3TNH~yqAOS%lf`kN#2@(_}DjZ=! z;=&OaBr+VKL1M!Z9H)&AM|hCcyR;_i5LM^KTda)cF$D@S0F$Z~`h zi7iKPoi@5ic%3%BNPwL-!bpgb7$ZSOqKt$Yi8KE10v%<$J=gZzS3D;@D?P(koSB)P InVu2#H&QWvHUIzs diff --git a/telegramer/include/pytz/zoneinfo/Canada/Atlantic b/telegramer/include/pytz/zoneinfo/Canada/Atlantic deleted file mode 100644 index 756099abe6cee44295a5566ad6cd0c352fb82e64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3424 zcmeI!dvwor9LMqB+*&Iv3>mrB(9CyZS}yHNw1bTgHVm6jB5jH#bIBT=h@DgD2<0+F zE){-jXvqBB9lCtBp(*Ag*F>2l*ZulDKmYbe$2tAkcjvpu@9b=6|Gl2?#35-fM|uA7 zR5d^0TczsIIfqlj81N7U?a# zzS-S1??=a1a?!UtHO|@d{v3C&$aVI;mf`MqraK45cXkg3k8lok80$N9JKKA>uJ9c` zA-zXt|167}-^eJIS5-;oaedVNUL8v+(8rtPsUM;j>r&5rbs{87pU|1=WZ6`CYW)OJ zR+u7B=L{4&H&&iWixEF(HZ?m(0s2z;9d)_dS$(~(uefLrub+0sB z-#=7SRTR|F{m$@^@KP6pLzZk$lM6ECQS4%ZGy(iXhJd z8FX#3ctlTyF#ttlNnl0@Z>TP?fJcwKbs`>uR`M_Zr|Mo;LsajjQ)T?|D3OqrBKvHuBl-@Dm14n7(XVq;**~*X3}{$cCMInciFeP- zfzeCF!1Dom@Dl}U@V>J;xni*zvU}2^?L9ob9?Ifoyx-KdOJm6R5Di8Pv5Bd-O```Eb_eqb(??0vjs`&i}eV#!3 z`BD2lI6fiK)3v*K2bgz|c}1cbDvu|?eoK6SZS$LleM2@5**RqEkiA1T57|9r`;h%Z zHqdHz5ZOYj*+XO#t!5XIZAA7F*+^t3k*!4b64^{-H<9f`_7mAqWJi%LMfMcgRIAxl zWLvFfUy+Tqnw>?q7TH^5bCKOewinr7WP_0%Mz$E)V`P)9W|xs|wwirLHX7M!tJ!K~ zuaV70b{pAlWWSLOM|Rw5wj9}WWYdvdN46c=cVy#{okzAF*?VO3k=?hN?ML?CY8rra z0BHfz1EdK^7mzj}eLxz4bOLDw(hH;+R?`in9Y{ZrhM?^TA7}}W=?Tyjq$@~UkiH;| zK{|u92I&pb9Hcu|(;lQhNQ00LAuU3Bgft2164EB5Pe`MXP9d#AdWAF#=@!y1tLYcg zFr;Hh%aEQSO+&hdv<>MS(m14ZNb8W^AJd@M)G)*{9^X)W!GDX1ep0i1 z8Aa--yvPWN?JTU&l{>tnyO`?;R;d^qQPIoIW# zp7&au{A+&Z35Ojthx=Y?E~TFP4W&m9rk7Rj&<_vZb&hNwsYla_-EY^n>FNG=oYS*9 zyESX)%9+VC-Lq59N^Reh?)UNg<(yQwbwS&tu3@8l-hABo6WQ*C`Yrn7t_@CoRiXa0 zZlTjqmZcZ-yiQ|LqFzcK=3Jf;u2&+1ou;Hw@^jCj?$wAexq7R&d(HfsYt=38^`=I- zQG3j7K7LGo+4!Y@~V>eavRDec_jum;E;4RCjAmxfK+weBkkJiJ7^ z9oQk=cfBJ$w!JNntXnKS3+KqAc^T4cT9O1MCrIz%k@8q%hy?c=r;q!$N=Um14Y?Jp zeHuG!X!SjP;_K@gw&#L|?>MP_3%bjbZ&hf&DZfhpdEd!X@ip>v%IES-&=(RhV1q=o zRcmCxLW!*3s{^iiWnk5N8dc-ypt7YJy?2rhE}Et>n}%v^Zh^!t>a9bPGG*wb7LAV> zA;aQ+&}aQZC85u6lF-y9!|&J0h~o`1^86tgRne-6hdz+!*4OLvrK@Fh{$YKgxKv+E z+o@x6R_WNm#X4^6L}%RX{gO0jzME9DO_BrC+~mD0B&9jled+xzI=*&-le)4{C+v%H z(z3HOeREG|;;2NOwB(L6IW%0oQ)(S=t4A~9E1irhVUihk#?AajWLn!kcY1lFWYuqV zvx|?(%T>$W895)zD`na4%+w<~tEAAGJ*Zq?&CPOh0@vuAq(o;<^IXl12zPQ%rf8m@ z$I1IFO6N6Qa$et@EN`6hx$_GK%9|Az-TaI|an>Jo7sTI`h4~-3i-M|UaaxJHxUE?W z63U%|`ct|rc#X5HYL6DS%ypKRZPFFzQk<1VOLSFblv6Zgs;=JJ&1vVcul61Oy7}|% zgRUJr{kN|NeaFK*^ZCkei1>U5c6&Pbe4lO?e|z86UVHrW`S?_?j2UarWOJsPlkPSD zZV`{iVVG^T;r24WnDbs*+}*au=Dh=m{~g4hURB#4zDW`fws(ijS2DNAE2h^-*T zf>;Y;E{MG#27_1(Vlqo(GljbQ`#}r{u^_~R z5F1(=BSNeQF(bo{5JNI7$uK3umJDMutjRDZ!=4O-GAzn4Da57>qgooPLd*)WE5xu6 z%R)>Gu`R>65bH9`%djuQzzhpBOl)av3^B5$u`3Q){*VGdDgY^grKtg<2$rS_ zj51i7Ixq?WsRX1HkXk^B0jUP09FTfI3IeGJq$HN6CXk|7nyN6$VrlBaC=8@BjM6}A z!zd1H{edq(YDqL23jklBKBHrfQI~S(>^*3TJ652Pqw-c97yhss||_q<--K0{&gV029YyCX!L9L!#rN PW1>=HW20lEV*>sFRXeOf diff --git a/telegramer/include/pytz/zoneinfo/Canada/Eastern b/telegramer/include/pytz/zoneinfo/Canada/Eastern deleted file mode 100644 index 6752c5b05285678b86aea170f0921fc5f5e57738..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3494 zcmeI!Sx}W_9LMp4A`+sAikYIhq=?EQh6_?+E`)l-1x#^!H1rH&^5lY8h%GMZ)G&<( zl@6x3Vul+gX$Wd+)08OgDL$H#3+RJ;qUZE{-`j5Tu8UsgJ)bkox&D3saS2IN!)*U} z>X`rV^4u^l-@waFg%5>%F-Ky$v zfxf-JOx(#oA@%A4QJuL<-d&I_?xkeO`xEDh2eE1LVV|+$QAmP(+;Oh@%O_Gk@f@R` zJRYrUuJ=|?&qnBHM_VfA9)IoH=u)<9r*>O%S=E}WbZzMr?&6uOGfWAOs7tbL=mFu` zx7UxyZiaWYj%{~=z z__*%p@lR)ZkT1<&e`+!k(Tihwg4GV#nF#uq<~mJTgR%m{TD}`uobb z_@g4O=AIlCo+n0K^U!-;n(IH|=Rf2Q`_zK6bkuu5So=Do-N=~adC6cou^z>uZ>YY@7 zJtMzNrNle6%q&pvhATZYC0ot%JD_LB&Qr6Umt< zeE)2uNY8M{`FmQ4j0rJv!Iw5s%k6poYP&zrum4lOb-4;w*laG>kzzM@m#c7_&C~ks zZGAQzVvn;8=x^SU=6%b&!{W?%*=%msN8EFap36KlZ>LovI;YY?F2>=oSBm_tdkRTvYK*E5;!O{c* zi3Ab~Bo;_8kZ2&`K;nS}1c?X|5+o)_P>`q~VL{@81jf=t1_=!k8zeYrMTakhhsVSR z2oMq>Bt%GzkRTyZLc)Z^2?-PuDN7S7BvweUkZ2*{LgIx442c*LG9+e5(2%GhVMF4E z1P+ND5;{v0J0y5W^pNl&@k0WLL=Xuf5}O(dL1 zJduDR5k*3Z#1siC5>+IuNL-P?B9TQxYiVMO1Q&@e5?&;}NPv+DBOyj&j072pG7@Ga z&PbpwO{9@fTbfuS!L~HfM#7E68wofPaU|qO%#olYQAfg##2pE|rHMQedP@^~B>0vl z`bhYZ_#+1Zas(iU0CEf<2LW;vAcp~R93Te*awH&!f~7eYkb}X}91Y0fU}=sAM-##_e!|ATe{f01&0NPcCmNu8r(HF)a!2+Ad$WR diff --git a/telegramer/include/pytz/zoneinfo/Canada/Mountain b/telegramer/include/pytz/zoneinfo/Canada/Mountain deleted file mode 100644 index cd78a6f8be1dd55ac5afd25bbae39bd5706e42d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2332 zcmdtiT};(=9LMn=gaRiXew7#s8fi%oAPgR%o+LOR8etU34pxx5GcCklf+M=| zTN$&N1S(-`g{f;cHqabyJ?PwVxvWIX%4XTWs3lG<2AKs_EIC}Cz_u&C~b1>BO?08vv{9DcLmwuEz?Nim>#!sX#`mzAI<*SSoL|uMSUjWYZX#EL#C8oGgI@&B{Y51ge9Dl zaDBwM)Ude5cAM)j^h(6pVHJ6#O`<;RRnc$vX#IMdx}kH6zHwu{y2)For>!VbF~tw+ z>A6Yj=A=7x?3_X~BiyBD1}B=Czs-}lZ^KQzKTKvF{mIOJ|FYcDd%`66X6ajB7%+1x z0(x%aXX>`>Z}ja;-c$2pj_Jf?QHc}hCCRl`C5;}Hf7(ku*3);V#1s+>y^I(Pi6$~*pxzWb{| zwP?Rf?it*r7H|7m?%lar`1^=1N^Ma^r~7qr zWQ{65*r}IX$yX(B^vTjQ)yCb@ArF7B*et8|N@-Vud1O(El=*^9d3>sPD!wzGYtd4f zecV)jpQ5W~UsP2iQF_J1h+4UKOg}o>ud3Vq`mv!-Ra19Juj*c-9(SLT+KvwMMCt)~ zvcYTWBDYI@QHiO);**BNRMT*xO1z=b#`|ubH2(0bS<|&#KXocfJ?+cY&kT&Irph?o zyv?tk&HhWTZ91gZ#hlmcmvyQ26XW8`IbeLF=VU|dcC%q_n6ba=twOVpRcqEDKo|vM^+2j&^Ct+K|N|t3#HDtPfcrvO-6@L}ZP~ zA|352k!2$5&fT9L&ft3{UUXxED@7+EpLl94rYEE-ug$Fh-ib1WQLImgnG zwR0>USv|6RWc`k|07wOp5+F4|ihxuBDFadmq!36YI7)%kf}Bc$b&&EP^>MTXLMp^jBBVwfMMA2?Q6{8L9ECzE zg_H`Z6;dpuT8_3{NWC0w!H|kM+L9qP<0u+ZHKc4v-H^f|l|xF0)D9^gQawjoKBRt* zwtz?l9c>Ab8gdj7sUk-ikveh|5~(CdDUn(t#YC!!loP3^qb(>>QAb-+q^6Fxs7O_j zvLbcmC@fN0j?yBvwrbMk?%RON`Xm(H0r0GE!!w&Pbt=N+YGl|7WdT ZHsT9y%v0Q1X_;y1DejDnw2ZWL*WZc*ZxH|h diff --git a/telegramer/include/pytz/zoneinfo/Canada/Newfoundland b/telegramer/include/pytz/zoneinfo/Canada/Newfoundland deleted file mode 100644 index 65a5b0c720dad151ffdcba3dbe91c8bd638845c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3655 zcmeI!c~F#f9LMp+gDcHj5j!juuoOA8R?9L2`Du%oBL1}16dpAygY5AtBXOLLDRsuQ zV=|z!f|Lj;T{Skl>`^gCP5U`+bZv-ep}BY;${S9wkjrz@V&n5bgwR^MMy}GWhrN~q8T=CXJi%T{=?R(7`biKZ&r~8d% zEv|Lu1^1gqt?R96J$!GcYw)1IBJHpcDhebBS(ht+X4j?JF^eFFLWr`K5ro=#C;jcF|o-WQ_| z_5VqHEy9(G_(B|xZBU1gm5C#r!sLPU z?y9;w&ssg=&ZwyCyNaIrza={4jEFwfBzt|Y#8vygmREngRa{fKMPB>bTG4yn-oSN* z*~aw~D+7J*&;P3Lkmm#a#!UCebek85y%4)X z7oPPG+ffp@<;WcWtrgYg@NF6X+g28vx4)9;ACXsR-mz?~F)|~^ywgZ9QU;}(sVSX} z)YA(BX#?Z^X$K|;Mz`PL+0POn?e1WHhl02|7a`%zjkKBKx0k*mWNDGi2*y<)A zT|nA^^Z{uE(g~y$NH1)4GmviB>UJRgKpKK{1ZfG<6Qn6fSCFad z9Hcw8x;;pLkOm}Lpq1F4(T1zJfwR_`;h+G>INbmL|TaS5NRUPMWl^LACX2PokUuR z^b%<%(oLkDNI#K=+UkxXEk$~YG!^M8(pIFeNMn)CBCSPwi!>MMF4A6G-Cv}^wz|Ve zi;*59O-8zmv>E9$(rBd9NUM=vBh5y-jkFu-H_~uh-EpMlNY9a`BV9+@j`SUAJkoij z^+@lL<|Exl+Hb4-k8A*2y#tUfV5|24vI&q~fNTR~A0Qh6*$K#2K=uN%8Iaw8YzJGt zACL`Ut9JylC2aMcKsE)kE0Ar0>K%e?5nH`S zkWFH%cL}mhkbQz|6lA9$TLsxG$Yw!y3$k61{eo;5TfJkDEn}B;d)@d*Rc6BFYT;}ar(2eU89 AC;$Ke diff --git a/telegramer/include/pytz/zoneinfo/Canada/Pacific b/telegramer/include/pytz/zoneinfo/Canada/Pacific deleted file mode 100644 index bb60cbced30763c08b6cf73554c8d6651ff387d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2892 zcmd_rZ%oxy9LMns{^5_3Cle(?JE@7FBA_UWW*Y8_iKtvj%S61!b6&sk{JuY)g+&F) zu78{!_CH)st-XBr+hU)}gNNn4Ly_j|t*zW(dW z8Ondqtpgv%E7z_#9rRLf6}+LB?zU#Q>Ao~pddv+sJ*S3ANa7_Es@o(?eQUy9^%8z% zpXt?HExnuSRYYBd-0@kp>a%CBR&Q0PJGUC$cVmgVt7xt6mpflYW_Wb}3FFm(xN$ma zaJIQSEJ{ZQrfjyXp!mZtIYIkjoLlxW#w)>pfe&?s*IYQdS?4< z_3%42GVA0DleuZT%>Hzi$y&5t=Ilr?b0_7>Jg+j@F=^tN-(fsm10^TrgvmKSQ7;&D zRW0};M(6q))xzC={iyGNT2$7oAFJJ|@|GXfiz_#(C7C{1`r>x8Y*d{*Ubx;YkEoJ- zcdp5AFO$O9G*eh#AVnbqP0=SaWW|Mmd16PtUip2DTIF@?C#(IcI44f8E^k(A#`V%` zi;t?3$acLhYqwe#2orB=o$>nGWJ6Sy*-%?A8#~L))0Nfo%&7viskB0#JvhUZ=9S3i zUGXM3D7ag9`}{Zm0)bcGbh!e7cTG_DK%jh!E7bY!?YwGMrpGlW-QH|_AF+3i$NtJ) zt{~^}{EIpS?8%$#y@XT(DFspsq!^C28b~>idLRWsDuR>*sR>dPM_U!7EJ$6D!XTAF zN`uq}DGpK{q&!G{kOCnULP~_x2q}`ItrAiuM_VVPP)MbaQX#cMiiK1QDHl>Nq+m$J zkdh%aLyCq}4JjK^H%D7Iq;g2ksUuQI zM_Wmxlt?X+Vj|T<%8AqyDJW7=q@+kqk)k41MaqiQ6)7xISw~x1q_#+Lk?JDlMe2(b z7^yH)Vx-1Mk&!ARWk%}kXbX*0+R>I8sWnn;q}oWik$NKqM=Fk#9H}`{bfoG?*^#;< zg-0syXiJaO9w|OjeWd(I{gDMgRsdN7WDSr-Kvn@+24o!^?Lr_c;b@lvSqn$I7|3cM z%Ym#1vLMKcAWMR*39=~2svygPtc#;v7-VG}?b0A?<7gKLSsi3~ko7?p2w5RyiI6oy z771A;WSNk4LKX^HDM!0h$XYqt#X?rg(JmLVUdVzWD~2o?vS!GlA*+Tg8?tW5!XYc? zXqOIIJ4d^C$m%)TA5;gMUCATqHt^7hF5K z5s4sImBW-o-ctz1OIfDJyk9wlE5VNMb9AR0SH8o0K8Ime^L)c~(HBK>;#@M{a5=^1 z@}BkTp#6HRuUB^_((Lz*Rqls^2hPW`Lbp%db>g{K-MAZa5?2bW#P?n2({6_KIet0v zwK?4#sNZr1Yc}1X`|Xk8!U=ceX0J1vy6h7SJ!;nJl3)J^^zSb%GB@9?|GbIW^Zl)Qq0P3PSX3`Y zpWA<5KGsVQOYP-n`FiEfEqk^6ky^_rk@eeoYW-iXY^}O#duCF0hLh?-;M7k_>ZxBJ z{rIBibu5c`-rKG~s(IIv?!Slpr{XD@6_sJBEH$^*+^6PNho!{4a{|ZD@EQJm&m00E z5s(l_3?v8=1qp-1@il>vNWLZ%5(^22L_@+M@sNN>L?k2<6A9{Tq9S3DxJY1M6B!AO z#72T6(UI^-d}IK=W(3F(kTD>GKt_QK0~rT05M(6CP>``8gF!}v3O6*LRG7d!-P)%6Mh diff --git a/telegramer/include/pytz/zoneinfo/Canada/Yukon b/telegramer/include/pytz/zoneinfo/Canada/Yukon deleted file mode 100644 index 9ee229c0eb82c6aea7359e0b38677264f3cad8ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1614 zcmdthNk~;u0Eh9XIix5Y$T_v+lv8S%vo@I`np62SpJ@|PC`Hm>n-T>HGQvYrE;2hx zn`onm5m-V&LAa=V(IP`42qTcnf-J??`7X3-6}0L;&ix(l%EkY)H?$l{xBc<>m@gcj zCUdyQb(+`Uy{VC#6ERvp_Zt}+p6;Bwm@qOO^Q~?AP=@@T)~*&4hQ(6E0kNj#qzrJ> ziojC449d+G!HHQiB)Ue0`h>_Z_e>SGkfGPU3s&LNK|12$4;4AOsMih6DA}GYqq-ib z=sJHH)B0M(W`2@!W%tGUz!x$;X;8$k%pNdtROHRv-8ZVU@UnjHDKd5Z)9GMgDDRSn*WbV?O$a}IZ^Jkul zf{_rtW#W$5+WS*)yM9@0Kk-KI=)SEAt0wi%=1Zz5P3z*KE>bXZ8r18-2JS#aJgQ7K}bbNNk~mdQAkxtSx8+-VMt|2 zX-I9BrZ}WJOH&?FpQR}fsSqgsS+s@sS_y_sT3&{snyaHi&SfA%0=q6GzB9S XBPAm>BSj-sBW2_NRkyE+KHK*fniI0_ diff --git a/telegramer/include/pytz/zoneinfo/Chile/Continental b/telegramer/include/pytz/zoneinfo/Chile/Continental deleted file mode 100644 index aa2906063f3bb2be42e800c208c6a4453a610031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2529 zcmd_rZA{fw0LStFy_ctWNDwkXEu=6Cgacd!4Dm>kl7hFuC%TfU13?2Mz)}ijE3vjn zMr%!**`vCq2dsc4^QFmUiGf6|K0z5?(WOG{l2HP ztg0f@`o|Mue&OYL$-I1z*=de;vAAHL_nhZI^S1*Bt*u&>ZyV^^HeYrxw0y@`*Xg$s z+@7~{UX|~-Dt$e1qq^tpK2PseukIU+_Y6(1kwaU)^L=rqU4J?4lp4ugp+}lJrM`<(6L`- z*r5v|ynj?iwE4vKFZRhvo4Q5hlOsB6NvD`x(W9f2nncXqFX$VhI@FX|JM@%`4Qgu8 zT0M1mrHVbjK*x1ut7)Hm<@CLAYR0iVc~jk=>gM)%8QOt-Dm}^VK?S{RSm=?w0BK z1uA1*mCT$!D%^6ZoD;T4%#G-kSwSNz+nOY^KM4^z-zV#NM}81@o{G@9PYsFrhsJeY z^--~)ZCJ~akJZ9W$8~<@o9eD5&*??c&!~d*Zdveiomw2#DHo4B#gdCnvhYN$xMz5^ zT-s}?WnJ@R(TBmJ*wd<)??0+M`KR^0&Cja)l7EvW-g;FU_LW?fbzZDG_M={N<40mm z`)9iB(h+fgte!D z#%9XOixH@zsO&;d&U5=Q)H{iUXjfryG6E(>=)TEvSVb+ z$exi++nQY?+eY?{Y~0rD9N9XucVzRnX7|YU(cV8_XaJAt0MG)Y2S^i;E+B0{`hYaT z)^q}C1=0(o8Avyfb|C#g8iI5LX$jI3q$x;OkhUOwK^kLgI)k*v*7OEx4$>W@JxG6$ z1|c0nT7>inX%f;Uq)kYlkVe^>P9d#AdWAF#=@!y1q+dwGkd7fOLwbfZ4e1)vHl%Mz z&9pV$MB0h; z6KN>YQKY3vPm!h~T}9f8^c86=(pjXnNN;UTbCK@an)V|7MH-BB7-=!mW2DJQmytFj zeMTCMblTRm8tJvIX*SYrq}@ork%l83M_P{b9BDezb)@Y`-;u^6okv=a^xoDqAL%~Q zex(1%9e~^e$X$Tk2gsd(+zZIvU~AIxf4m<;bIr|>=Qd|%UP@6;YI^G2v4zc diff --git a/telegramer/include/pytz/zoneinfo/Chile/EasterIsland b/telegramer/include/pytz/zoneinfo/Chile/EasterIsland deleted file mode 100644 index cae3744096402e8a452336544edf96ca9ae5ad8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2233 zcmdtiT};(=9LMqh;W^+W2$|3mS{MbwkLQQ*_=_YVJxF=dBs5cpfF(pgrWD3jVzpF8 zYi_!%R{7BM3tcFi%-?#l2P@BoBU`MSbgMNPJy}}*`@R3wRqLXgF8ZJI`@jA>7w6*a zeBPmkmZn1IZ&$4Sgv0f$Jv^swwzrYvy8pLurM@(9LEIA`8>iz7@paXk2wf|YcNdtb zjBJSxEYdNKUt$xE>ew$QB<@m*zU)|7;>Ul~3470}#L+SB??0(7-#wzIG!Lt!r%svV znn5+S>99%3>Q(n4;#JsL%Fs2O;c6)hTK;3yqTBs zoK)uz>+0{@Wq$IYo<9+xY9_mN?a?-MNBADS;D{p&hbnaNy;xOO-)9!>Iw4h&J

rD5BGI`|PpxN+wx;*-7 zp4s?zsoL~pvgvsxO+B_gS3ll&QT5g(>0Z}$eNhpS|M-fI`7d8FuDf%C<9PQd*FCVu z7w5XWw>yb{-4E<>>?b4QOIjEVIo0;eRwee7+EZ-*_dXx*KM4Jc&Dfv8ZP`*~zuSJh z-43!J^ftr;JL0li0``P#3fUF1Eo5KF#*m$P+N~jbLpF!(4%r^EKV*Z*4v{S)dqg&g z>=M}~vQK2A$WA@&R*}7W+RY-nMYfCV7uhhfV`R(7o{>!>yGFK+>>JrQvU5+nb!6|z z=8@ea+eh|~Gyv%U(gLIhNE47QAZ<YUfHVT>1kwtm7f3UZZg|>uApJlZf^-CF3DOg! zDM(k4wjg~$8iRBOX${gFPum=%JD#>ZNPmz9Ass?mg!Bk$64E84O-P@RMj@R-T7~oq zX_lw$7Sb+H+b^VHNXL+tAw5HyhI9>S8`3wVaY*No)_L0AA<gr&-9y@k^bctu(m|w! zNDq-FB3(q<i1ZO@B+^Nwl}Im<W_sFgBJD)_i8K`HDAH1-r$|$gt|Dzk`s!)Z@qcY> ee5LJgpv2yb13AI+-2B{<yn=$9V9}pX@xKE%a7T*( diff --git a/telegramer/include/pytz/zoneinfo/Cuba b/telegramer/include/pytz/zoneinfo/Cuba deleted file mode 100644 index b69ac4510784f23ee794cd6d11e62315a7318e5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2416 zcmdtiUrbhY9LMp8zcJLJ6#qmLCJ7b)9_Vot3TG2VL*Pq1n({9)1c3-M69XaFN}{#V zMQJAEqB7=IVPpfV(OH5rNAnxxu+f--ana`1az;e*^n0Jx)<rk1uKJyw^ZM=Ff8O50 zs)}US-=5$<{<a&rhaEJZ_jY`7@WYYUq<e3PHCR-kgCqIY`LvCC{<B(pD0-(1wddJi z_0N;xwCncB+gIi5h*5jGEJ&w)P1elXOr4o5uzwv()7j2L)^7tR^hV1rYp&O&zRpS8 zb!0;QS_W+Y`hE@AJz)iweJ4SlMRu@tMs8ZX(F%z=E+Ny6cBrdGLVv8b=3TCm`QIF} z7ueZy^XXmoE!}TPSkp1<*8Cszwu&Bmp?gRdZ8&O&7d@|wS2fy8(#mydaEZMvVuLQ5 z%CK*rjn#+?#nv5Tp}M>)-HL1|kUQI=tf)edtf=$1?pl|xD|4?{(JNE*?u4_}s_>UJ zX32ydGuJ7x)5Chtc&pqydP?u>tC6^#!y4a_BMC?LYhrVvB-IzHyE0poA6cU*S@C+m zwOu^RFUsoVld}3okEBKnY3kI7J}~=%K6v(HO&fbv*K`fb+E4drdfO?<IObKWqDa?g zdSv~zS2QbniDZ3yLN*1=$fl1(ADSGK?DksOJT$F2FScs#=ee3!e_7@ApCy0ILV5U^ zOHvSVUA$X95%27%Z1uFu)+^U^TV$_p>%XYm=ib!9cY3sF{D2m}a8yhBO0~4SQ6KHF zv@GWhd90;GcEmTx<CPgwzMxokX04E&SJS03CO|5`jFPJ0@1^Q}f2p22CpCv7^@$6U zTD#AsPj(Gx-IfX6-PWm3dHQuvU8_DF{;uxLtI@r4XJlXMOR{hLxa?nAE&Kag<k?>~ zOG8JMG+s)O1I^je)Eg$vrSbCIk)Omb!2jPqf&PDaf&%}$$LH%Fbh*seb_M!;=WM?a zpYQZZ*Ze=f{Mnr2b!{~7x=iyHc+GlTE<Zo-KUkd4oT`@CUSxU5`j7=8D|9qVMAqnN z7KyCV(JT{LC$dmvrN~l|wK|%`BCB;Y%SG0UEEriavSf7D%!NgB%&LK9BkM*Mj;tJ6 zI<j_T@yO~O&GM1;JDLI@6+lXW)Bq_0QU#<8NF9(uAeBH$fz$#i22u^A97sJJO+k=~ zASFR+f)oX*3Q`uNE=XaJ${?jdYJ(I9sSZ*eM^hi9KuCp<5+OB0iiA`NDHBpBq)<qu zkWwMFLW+e{3n`bQsTWc(M^iDRWJt}Bq9Ij7%7)YpDI8Kcq;!s^c1ZCYP4$rSIhy(* z1w<-{ln|*QQbeSRNEwkjB85aMiImdO)DkJCqp2oRPDfKuq@YMek&+@cMT&}46)7uH zSER5=Ws%Y%wMB~SXsU~p*U{7$DX^odFj8Wq#z>KoDkEh^>WmZ`sWeh*q}E8W9Zj{7 zayy!OBL#Of6-P>r)Ep@~Qgx*4NZs-OTli2Ddyk2JTuw?tazavKQe2MPo!~Y%cj)ib CX!a@q diff --git a/telegramer/include/pytz/zoneinfo/EET b/telegramer/include/pytz/zoneinfo/EET deleted file mode 100644 index cbdb71ddd38be8f4a23e57bdb4b86e52195e9f89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1908 zcmdVaYfO~|9LMp8DKlB=AB;-^ViJOMfWy^GkQyAA3Yc;-;u0xWjZl#on97{Yxn%6; z#%!*fIvI1#$QPt9@G`s27O^(R9F=R!Rd$(cGe?)P`Fx*x*Q;JRpR;p5XXnLv_5c2Y zRa={~oc}yC-Cua~>~`OLpZ(Ziz2+RAZ*zMCvW`53HzX?}7^8V*MfQ01Uy4lpMN#v< zQS^*26n*!yVs5>!C$68={Ev<(_TnXrJ3DOg@0_p&eX@i-`)y%syFFP`Z;MKUmY5%~ zr+j&q6qjs^AC0l(&_X5O%TdbrNm?=yrPNPm>FJR_<Qx1+X#-y=z4vovbX=6b;XP&6 zPFhy`h-DXyTF%C!mK%TE^0K-tZ@R~p#SUoM&CRy_?@q0_RA|q9U8($`Iu(qr(6a|u zYvtQ<TGf=O)xFE@x#Cb2HpE&{(vJ!hPFr#4XDUhj)=KXDU~6VvwKe0{Z0)Vnwr==c zD;>L{vi?3RKYL2++uH2;z609u;+xvo+Ng@9tqPWw=!NKVZSpPArYXNFBa5x_Muc9v zmts}te^>QHnAMyNx7v|k?d6`kRyQziuhdQ0=Dj~_%gPH@UpuCTgk#oNFs!CWK2USK z)O`CbwM@5b>$PreyBXB0XR6iu=}vp?P=VTpDr|d8f_5CtwVfOCt-UGQc4aQGj=-Px zdbneq8IkJz?K|t5Go`LCKep~WliGc5$le$q)t+M?YVYuI?du*?Pk)a>oCo!CoVbNf zsN<yiL)?S==aXLofr_9L2n3ywf1lw0I9vCh98MQGU*v?5Ge%AsIcMagk+Vil8#!;} z#68_JM@}6%cjV-evqw%JIe#PpBm*P`BnKo3Bnu=BBo8DJBoj|J6(koV86+Dd9V8zl zAtWOtB_t;#DI_Z-EhH}_F(fljH#H<TPd7OvJ0v|MKO{jULnK8cM<hukOC(JsPb5(! zQzTU+S5G%tBwJ56T_j&5VI*TDWh7@LX(VeTZ6t3baU^pjbtHFBH+dv`Pd9xee`ErX z89=50nFC}JkXb;c0htG6B9NItrUID@WHON1@N}mGnGa+_kQqUy1ep_LQjl3erUjW7 zWMYt+L8b<o8)R~j+3|Fz2bmvFcY=@^LZ%3rBV>}0Swf}>ACh?rbGIrji`DX#WvAz+ P`!lkAWto2OTTa-0+xVLQ diff --git a/telegramer/include/pytz/zoneinfo/EST b/telegramer/include/pytz/zoneinfo/EST deleted file mode 100644 index 21ebc00b3fc096035b9810519d778d04a3562a44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 lcmWHE%1kq2AP5+NDp>yi-?@Q-!8JI9A%rYlTtKa+TmXY14MYF{ diff --git a/telegramer/include/pytz/zoneinfo/EST5EDT b/telegramer/include/pytz/zoneinfo/EST5EDT deleted file mode 100644 index 9bce5007d4dbb871974a69cb0f68151c1ee22556..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2310 zcmdtiZ%oxy9LMnsqUZ$@Dhd@Tp<*Ea4Mn8Fgb~^m(u*OPel_q8p*X4CpbTrFq+4@N zgZ}N7WoB)T%xqeXsX23NX3A>zXLxLjwTe1M51iz^o%iu!k9yWRcjvr*yU#ywe?#-? zBF{h0So;r`^Mbv6Cx-T<sn<UGMPA?RQM+C~C7(ZM)Y*(b)ma(UmoL?u(Vyn%zs{AK zD<`Mw@S#)_c{`##y<rp8eL+V*uFV(~mKeFB#@1>XSAJf_795k<=O?KNiMwUOzMaNv zHcOn|q~gDMLnj;wsA~>)>51<=YZ6~tt&_GinQJ@l(bqLsnd_Hk>*VSzbHnU-ePdRf zNlDL=N#lpj<mfn={D)UfxiBoLXMR;vkDiva{a>h?cI}s&gB~;OnH`c|_k+n;)2DAK zK5lNEzg^#!yvJl_uhW^~PgRz;PG=3irLupXrKk6ItDFJ9<c6A6-hr8t-?=~)>`0Nq zK)@8Oy(}}Ts?F_-zL(<6>1Jk8zs!nFFeOvI)g?o}o7tnE=+YBs%$)B+x~%Uvb;rj~ z>pQo7sqT7Zl`e04U(M~>D0kQFRP!2FN=1H9RaVTC%8|XMDtCph8rW*86Xxpb1FdHM zaE4y+>K1kH*X82d(4rQ8m@YNTe5$r5M(!)iSBoASlEtY>%3pU<{G%gkN%6a~<j0Ao zKJA3AKYr0H4e!+ry9dkzgIo2ou7l>m{#L!bvDd5!E!5)sK&|X<kyW`p>Y;#78WY-8 zQ&qk+4L7JjW|9O>m8s@&BhtJtO|2RFN?LkKP3!54`r%-zd8BVZx7CG>+ICPsTCC05 zwqCt1`IuQ>)1lXg&#Pcbj|2ygs`iw2Y43kibzEwYCqkRlljq80LuWuebtp|bmshKe zFM1@(6Xl5>W1s)hFA_QYo+l;}`RquP$Gzj-8-Mlx$_l@ya<RSl+gs~rB2iZ#|GyYh z#2yBPj0zbRGA?9b$jF>_Xvo-*!6BnVhKFu^d@w+c9U(A8WQ@olkx?SUM8=5>)M-bG z4Ap7JiVPMREiznWyvTr&5hFuJ#*7Ra88tF&WZcNWk&z=qN5+l}-f2gV3?CUk5&$Fu zNC=P^AVEN)fP?{w0}==%5=bbVHWo-QoHiOrIFNWC0YM^yganBR5)>pVNLY}#Ab~+5 zgM<c&4H6uujSdnXBtA%hkO(0mLSlpj35gOCCL~TsppZx*p>o<-A;EImXd&Tp+IS%W zLn4NR42c;MG$d+B*pRp(fkPsPgwAPWhXl`QqlbjgY2$|k5Q!iXLL`Pr5RoV%VMOAH z1QLlP5=tbNNHCo?nn*aEHl9d8oi?IKNRgN#K}Dj9gcXS^5?Ca%NNADRBEfaq=px~D y+V~;?cG?IdAx2`11R04k5@saM_`eIZ*LJ(u_B+QnBd;j0Ajel&m{*ur;Qbp6U$$!i diff --git a/telegramer/include/pytz/zoneinfo/Egypt b/telegramer/include/pytz/zoneinfo/Egypt deleted file mode 100644 index d3f819623fc9ef90d327380fad15341ec1a0e202..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1955 zcmdVaZ)nw39LMo<r_Onu>Ge-G^V-ra*L3BUZ7pvu|6G|i=@<8oQbzt(p;QznmXNO4 zL87pXustvmRJ5YhGPA6O3K`U#)lS9~6!a`2m)ShcP1()0&imNI2zt<iz8ChoY!CM2 zyg%vXkFG2ee|tm!`fZbw@1vCa(c1ixY0Wh1BUdh%wy(PM(WCE&U!S>If3v&OeEVgu zZhv*9(eJL&-#s6iV=uiIbd^6GcJ-D7=SH@K=Q=|D%lV8w|M3&z*Y+mcy{SiB_+X#3 zYYNqG8-KDr%a@6Z4U=T=oDHh4cB{NJC8jQyAG4PSlcN8+Y}w!0CNlk%_V>dB>dL7@ zcHpgb>W>3Ga`1)yLTo9tkySsdtfpmh$l|ydT7Oy&o3UMFFNoRUxg9E}ASrXYM~LXK zXKb`Rp+=q^u(^9bSEG)U%4=Th6Qg%NV#hpPq+&0>F2~l^iE&TH<@kzDG2!08UYj>w zP2AROCuXWep4?&cPBp4Yy$O5$A)zLBNEzQcTTI!%!``4)sr*eR<c;;4#niQRa@xXt zQSi`oSy-4-HzmH1)3aX`Me$3v=<H{zSTx$=wkkEFbB~<4t3i|;{=wd|;ca#6wsB_G z<GH%Dsn3)x>DITcIALZNYkhlFiz$zOs4I$&hZPswgUXyw!^-dX26uM75zg7y93)zw z4ex4R9n5{_!LX{~{^0J7Yt6j620j0&I<p{As~6rk*CfXd1dB@Z%_8$yP#w!M)t|)l zV*8_6@=8l^&&fzwQ-4s`tbf4NyqO77tM4$W-RpJg<bg0<urA1o{70W5S^xaG`hSIp z*Sgmyif2XKQ=FVHMqK?~votM|$rL}*{5_KX7w>t{-E#+*?n2(R*S&4zeIsuidFRMm zN8UT~=8<=gynW>TBMl%OcwGxf4_?;<(go56(g)HA(h1TE(hJfI(hbrM(ht%Q(vjD- zg!JTfO(9(&Z6SRjjiK+1FSN$F-hk$i?vVC4`a>Gz=n!cU>Cx+&<ml4t+C=(98bvxq zTIJ{!X%^`gX_up4q+yPZk(QC3y{>7FuDz~pq;I5gq;sToj^2^xk?xW9Ir>L7fMW-c zEkO1F*#wSV@Vae4_QC5m0@(><D>(K7*$iYiknKSB1KAK{N02Q+_5|4!WLLayTabP6 zx{X102H6^9Z;;JFb_dxWWPgwiLUst*B4m${O+t3b>$VBmC$HNmWT%j=LiP&TEU()w auiGwUzr1e4yl%(v|FmUMH)<ti(LVwBTA^S7 diff --git a/telegramer/include/pytz/zoneinfo/Eire b/telegramer/include/pytz/zoneinfo/Eire deleted file mode 100644 index 1d994902db21814a626e42639d7a96b18ee73756..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3492 zcmeI!Sx}W_9LMpO5Cz;G%`H)R3^xq%fJ;gexe|)xa#GR^Ek#W-Bx6J~aw<#5asD=z znVOGFN-ntJisl;XWG*Q#xbNb=CN5~t_r2(*tEMix=sk1zoB^(w;rIP}dks#GbpG-L z*zfS>IcMK|uJnohGOgZ<Nz)FNt}uO%$IO_!)qFpFhM75~x-p-x)3eeao7n@V>p7!# zn7IjS%nvb-^t`%#_5AvII;&xr&bm@$7C8IOg8a&QVc|`^XnTrYyz@7mJwHJ&F&Sp* z7aqOrtG#A<YP?yIw%p`&2{$X_dg)ajWmZR~n>FQco3#}a&Dt9W%(_BH=N`VL*B|Yv zH>}yOe_Hm$Z2Wem-ZU*pZyqs4Zy8yvw<c!jZC&S@pIh|Sc~P77_QZo`dqIFX5O+%F z*Q#v}CM?p2!W!tq;S=@Y2Vd)ga;duD%2j>zd{uMom)-jKwg=`!&Si5lyNW(Fd6zkz z_NqBEWVk-tt)V&R6>~najJXiKSYHeZ)t4%H&80`Bbz$)^eYxP8zH+>~zPfIYzP93_ zx&GZUePhf(b93fseXC!bxt*D$?{rMocavJ{d$r4&-(y1bedmTL3ii`Qr}mi#PcG_* zn^u@dmwe6RQ43A+_S{t8)I41xG*SC?$<ieQqI4<GcwMTnh7M3EI$*~W9e6WAmtJ1o zl-cXiWhXs0<(5{|<%gXxYHGNt&~=lkn4!$G5wlID#M`EF$Q7y5B2TLN{wmd~Wl6QO zOXRtt@lt*3Bndh_U)5NYt!i!_r)o_Q6+CN*(kW@G_L$D<`FO7i>DO4*32UzEcI+he z%2ZSJ8#a>q_nJy*V0j76uObZ#Z^;WeMH04izciX$AmKl*l*Yr?i)Y*viRg1gMW*Gb zCegX7X}51wvzjwh^T-jZd2yy{5j0s^9Pg)EK1!FU4Q<s+M-!#hyd;Uvjgptgww9P_ zHRP4#5NZ8cOVy@>pS0~Cq}tWJDDB%8s}6oUB{t-wioJe8#rbYlal3b@j%VknPT31o z{H~4CdEx|>uxOUNHgu@!GT~F{+H<^gOBo>DUrZ5ie5|})H9>lWg-Vb69!ad+K_wol zC~w?rpn9#kE4}jr)tjb*>XUO-y_I=KB~9L~`X=S8exonT+cB%vJAHOZ|KO==Ky<bw z`>&LNHAMzqnj(XW(`4|D40-psSB7NumXy3<>b)<cW$3)_>it2%k~+4T8rHRyN=vS) zK8UELhPQv9KCIxV^v0DX{pJ}pqTGEMv3HxwxPC-NE}f@F?aq~trf!kZ*)!zhjJc9I zF;je=DdGF)%df=0{PHhZ>Ob&$`t)HP$FX0_J0%>)KiJ3Lamp#5GIoj_N4cNvcO1vZ z{p`3ub^PNyd!2Un9oOCKw6X74``P}E`|#WL@$qrIe`EWe+NbBfz+=b;y4oE?wh-Av zWD}8HM79yxM`R<BokX@0*-K<Ik=;bL)79=LvZ1baN0BW>_7vGvWLJ@GMfMfhSY&6B ztwr`0*<56Ik?lqH7ujG}yTiy9BYTW&GP29aHY59tY&5dd$W|kJjchiu+sJk!`|WBs z9NBSX%aJ`tHXYe@SG(=VzPs9uM|K|BdSvgB%|~`0*?wgIkp>_gKw5zG0BHi!1*8qG zwhu@nkWL`2Kze~R1L+3R4x}GQLy(RjEkSyMGzIAj(iWsIuC_5qXOPw)y+N9TbccTx z+Jp25X%NyOq(!c_M@W-gZI_TXA$>v`g>(vO71ArDSxC2#b|L*j8isTXX&KTpq-n0U zYe?IWz9Ef6I)}6l=^fHMq<cvFkp3YJL^_DH5a}V(L|5BIq>Zk&k4PhtP9m*DdWkd> z=_b-nq@PGbk&YrQMS6-f)zx+tX{)R4E7Dk`vq)=^-XhIKx{I_I=`Ye?q{B#yksc#W zM!Jl&+12(LX|${DG}3CM*GRLGZX@kR`i(Rk={VAIq~}P}k**_cceQ;-8t-a5kF*}? zJ<@!n`$+qd{v$U4at9!{0CEo?Hvw`NAh!W>A0Rh^tGyGDTLHNjkedOy8<5)pxgU@l z0=XlQTLQT!kedRzE0Eg)xi63#!`0py$gSaO?+xVUK<*CY_CW3r<OV_R5d8lZv3~^h d&u)=Gd#W^wuy=|ltaF4Xyji%l2{euf`~${g?PmZ0 diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT b/telegramer/include/pytz/zoneinfo/Etc/GMT deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+0 b/telegramer/include/pytz/zoneinfo/Etc/GMT+0 deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+1 b/telegramer/include/pytz/zoneinfo/Etc/GMT+1 deleted file mode 100644 index 4dab6f9005bea50a065c685ec8260b0da2bff921..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yi|M-D{LD#^LA%rYlTsA<xc7|L4sK^bF diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+10 b/telegramer/include/pytz/zoneinfo/Etc/GMT+10 deleted file mode 100644 index c749290af2f6b5fe22770c34eb1e8fc87cd85aff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp>yiFHT@!&^0t*2q8-smkm&_ouL6209yeIqyPW_ diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+11 b/telegramer/include/pytz/zoneinfo/Etc/GMT+11 deleted file mode 100644 index d969982309e5ca7d32979a7dad814ca307d2cd8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp>yiPYqyT&^0t<2q8-smkm&_ouMHY08u0hYybcN diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+12 b/telegramer/include/pytz/zoneinfo/Etc/GMT+12 deleted file mode 100644 index cdeec90973be28ee4075eadd22b8b574db2d7a5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp>yi4|iZ-&^0t-2q8-smkm&_ouLsI07pj)Gynhq diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+2 b/telegramer/include/pytz/zoneinfo/Etc/GMT+2 deleted file mode 100644 index fbd2a941fda996f4abc1f0e09cdf99c271f5a1e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yifBb-fLD#^DA%rYlTsA<xc1BzPpCk=f diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+3 b/telegramer/include/pytz/zoneinfo/Etc/GMT+3 deleted file mode 100644 index ee246ef56f18de61105af0c14d201fd090f74905..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yizj}dzLD#^TA%rYlTsA<xcE(%)m4FQ( diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+4 b/telegramer/include/pytz/zoneinfo/Etc/GMT+4 deleted file mode 100644 index 5a25ff2a6afda2cb09b9e147ad20610bc1923444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yiKYoCLLD#^9A%rYlTsA<xb|zc^i`)$8 diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+5 b/telegramer/include/pytz/zoneinfo/Etc/GMT+5 deleted file mode 100644 index c0b745f1cc44d03a00f8bdf127c154392e3baf27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yi-?@Q-LD#^PA%rYlTsA<xcBWhaf;bGY diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+6 b/telegramer/include/pytz/zoneinfo/Etc/GMT+6 deleted file mode 100644 index 06e777d57e0267a0635b6b284729fddcfe6221dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yiU%h~VLD#^HA%rYlTsA<xc4k}vc$5ry diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+7 b/telegramer/include/pytz/zoneinfo/Etc/GMT+7 deleted file mode 100644 index 4e0b53a082f11f9b9debf5e110b97b1b0473c9a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yipF4qpLD#^XA%rYlTsA<xcII3FZtx61 diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+8 b/telegramer/include/pytz/zoneinfo/Etc/GMT+8 deleted file mode 100644 index 714b0c562889a8a774d9aa27810d8400164d00e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yi?{8pW&^54N2q8-smkm&_odp*FWlRhR diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT+9 b/telegramer/include/pytz/zoneinfo/Etc/GMT+9 deleted file mode 100644 index 78b9daa373d2aa2856eafcc92ebc6d899cafde5c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 ncmWHE%1kq2AP5+NDp>yiZ!BP7&^54R2q8-smkm&_oh26lTc``r diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-0 b/telegramer/include/pytz/zoneinfo/Etc/GMT-0 deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-1 b/telegramer/include/pytz/zoneinfo/Etc/GMT-1 deleted file mode 100644 index a838bebf5e7bac3e1257eeb0a61c1b83feb1324c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8_yiajv<(ayLdep^Wdqb}r)$Us0B2POH2?qr diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-10 b/telegramer/include/pytz/zoneinfo/Etc/GMT-10 deleted file mode 100644 index 68ff77db0d95c7d054ef33c05e05ba71bcbbbdd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 pcmWHE%1kq2AP5+NDp(j8dKNG+Xd4<Zgpj3+%Lb^|PS?<Y3joWn2!Q|q diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-11 b/telegramer/include/pytz/zoneinfo/Etc/GMT-11 deleted file mode 100644 index 66af5a42be440f1fb8fec3b915afb49b356f63a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 pcmWHE%1kq2AP5+NDp(j8W^G_#&^9z=2q8-smkm&_ovxuF7Xa3A2*CgV diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-12 b/telegramer/include/pytz/zoneinfo/Etc/GMT-12 deleted file mode 100644 index 17ba5057727dd73bd5f6234cc5b239b71a861945..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 pcmWHE%1kq2AP5+NDp(j8Rvchp&^9z;2q8-smkm&_ovxt~7XaZi2>}2A diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-13 b/telegramer/include/pytz/zoneinfo/Etc/GMT-13 deleted file mode 100644 index 5f3706ce64cadf081a6c56abd7ba423575a4abb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 pcmWHE%1kq2AP5+NDp(j8wq0Og&^9z?2q8-smkm&_ovxuV7Xa(^2|)k= diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-14 b/telegramer/include/pytz/zoneinfo/Etc/GMT-14 deleted file mode 100644 index 7e9f9c465ce6211c65d617f60472c9b55b5052c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118 pcmWHE%1kq2AP5+NDp(j8jyzys&^9z-2q8-smkm&_ovxt?7XbFR34s6r diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-2 b/telegramer/include/pytz/zoneinfo/Etc/GMT-2 deleted file mode 100644 index fcef6d9acb247deb539fcc4b30149802572ea642..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8WE2<}v<-|HLdep^Wdqb}r)$Io0CCs`bpQYW diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-3 b/telegramer/include/pytz/zoneinfo/Etc/GMT-3 deleted file mode 100644 index 27973bc857b4e618218ca2790acacb81f7c7bb82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8v<w&+v<-|ILdep^Wdqb}r)$gw0DM~pwEzGB diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-4 b/telegramer/include/pytz/zoneinfo/Etc/GMT-4 deleted file mode 100644 index 1efd841261a977ae218d408f9cc308c3e312a5e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8EF2gZv<*xcLdep^Wdqb}r)$Cm0EXTM^#A|> diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-5 b/telegramer/include/pytz/zoneinfo/Etc/GMT-5 deleted file mode 100644 index 1f761844fc44f8228bb748235bfd30be6c389cd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8+yWRFv<*xdLdep^Wdqb}r)$au0Fhw_H2?qr diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-6 b/telegramer/include/pytz/zoneinfo/Etc/GMT-6 deleted file mode 100644 index 952681ed46cb60e59baf76a2c43b49d5f67255d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8LJ}Alv<=J{Ldep^Wdqb}r)$Oq0Gs3obpQYW diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-7 b/telegramer/include/pytz/zoneinfo/Etc/GMT-7 deleted file mode 100644 index cefc9126c691060225ff2eee1241b1e5e9825fcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8k_s3Yv<=J|Ldep^Wdqb}r)$my0H$XLwEzGB diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-8 b/telegramer/include/pytz/zoneinfo/Etc/GMT-8 deleted file mode 100644 index afb093da00685297cb11347c4840acf3a8e2e2bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j83K|#~v<)m6Ldep^Wdqb}r)$9l0I=!@^#A|> diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT-9 b/telegramer/include/pytz/zoneinfo/Etc/GMT-9 deleted file mode 100644 index 9265fb7c2071ec0e66c657ad2ae42d5dd525fe97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 ocmWHE%1kq2AP5+NDp(j8>LxHSXd75Egpj3+%Lb^|PS=tP0K07nH2?qr diff --git a/telegramer/include/pytz/zoneinfo/Etc/GMT0 b/telegramer/include/pytz/zoneinfo/Etc/GMT0 deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/Etc/Greenwich b/telegramer/include/pytz/zoneinfo/Etc/Greenwich deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/Etc/UCT b/telegramer/include/pytz/zoneinfo/Etc/UCT deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/Etc/UTC b/telegramer/include/pytz/zoneinfo/Etc/UTC deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/Etc/Universal b/telegramer/include/pytz/zoneinfo/Etc/Universal deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/Etc/Zulu b/telegramer/include/pytz/zoneinfo/Etc/Zulu deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/Europe/Amsterdam b/telegramer/include/pytz/zoneinfo/Europe/Amsterdam deleted file mode 100644 index c3ff07b436aedf662eae60f50668f5abcdb172b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2910 zcmeIzdrVe!9LMqJQAndBe#Hb*yhVf;#0OAPlavt61VVixQt*<9T4adCur#`;wffC{ zb1ZXe-VoFwqTy|T_ghoRYLuF;O;>g?ax<saT>ai>Yt8jftv~vwb2z`(+2LP!fBX|> z<;Q#ea<#Kxc)059!+XzH?Xkj%y|SYD^PH7ucRQ;p_BkI^MLDaNg*t0A*{m(O>8vY^ zH0!g@ITe|KW<z3wQyDeUR7D>!8z)^do7${5n_4C~n=dai)zKH7>SHs^mLKz-nyS%e zYtwR5`(|IWZCf|9z3?&T!=>Y#y39V#j=77>PM_oK^6znWw{LOw3@<f%emd>chtDzf zXRerrTN|9l-A!iixie<p`?Y3&W3D++y3`!3I$;i;&&@fMU0is$>~P_cL0N@Ir`))D ztY^{H<0H0bp1eFpPHhU)rgbUu@zMcu`t>OJq$pHA%^0rjDyC`hJDsKd!c>*wMD36l zts(w!?U+77?oIebJ4ODWp&id_SnE?7c5SbQU)rkoeYQqBA9`22G&D*?b&Yggxmvmz zmG1LjlO6>{(lck8+@I!`$f2q7Kun@UMMTSkPPjw|rfBr_0ov>9DDB-Gs(p^M(Y|#z zHKyV_?N@$IV;7##{>2UI%iE%Hxo0K5s7?}+>Se&V_hew#RgxHADv7ttWKfrK9du!; z4F0u5hcu?h!(V3W(8|f0R6j%?S)8oHRz&FV{76k+I9MJX8K^0FU1UVmWlc@FEh7Uz z(ovCL$*7iZWOVBTGP>!wJbr1tjH%fyX(#t-`Vu1<)ob;M*|R0nyrE;CdRxa8OxN)b z6{tULls*}jp;<A#HS2~?vpbEH>@Py}>Fd2@!p@&{Vsnt>tmz=Rbys9k*)^G5-Xv2d zH_Oxo-|I8OcFDBdlbY9kxlB*0(fqbMbVgUz8Q(6`nYW8{*714z>;=C*w{fBt9GxT2 zzm=r3E62+VGrMcy;(;<}>`*Dn50klZ-K046mb}=(BPIPiY01@#QW|_iOHUt`c`ax4 zrP@k)xv5^~FF&XYYF6nh^D4A#Ntw3twrU;lH}5uqciB6r?cMHukMm#M9#25I{WtW4 zRCoj2_qKPw2h@5T`(?j3<c~90JmDTsL=SJoohRSZ(*2%{@qSOTwXe??@3Uu|JyTNs z_OZXPm)BnQ;OEcF>-GPGaQtSkRv}{xkbt<_h#(<BVuAz(i3$=HBrZr`kjNmRL1Kdh z2Z;_69wa`lHb6*(kPx}r7$HGIqJ)GAi4zhiBvMGIkXRwXLZXF)3yBvJFjpHfBxJ5O zW=PPGs3BoP;)Vnci5wC-R~tJdcu4e+@FDR-0*FKq2_X_gB#1~9kuV~0L;}g*0g*&P ziNq2KCK62~oJc&8fFcn^LW;!H)dm%bDiT&Cu1H{!$ReRdVv7V9i7paeB)&+1kq9Fp zMq-Qv+0{lF2{RIBB+y8tkx(PCMuLq*8wocOZzSMI#F3CAF-L;#YNL*X-POh&2|N;c zB=kt^k>DfIN5YT99~l5-1dt&>#sC=vWE7BLaJA!r41}v431ldcu|Ng`84YAOknunU z1Q`)zNRTl>1_c=vWLRA7xF7@LYDWec8f0vc!9hj`86ISOkO4wQ2pJ+|jF3S>MhO`v zWSo$Ja<wCc3>7j~$Y3F(g$x%mUdVtUBZdqaGG@r2A)|&28!~Rlz`5FyLx#@PjvX?1 t$mk)%hm0RGfXE2q|3eu3?}jkQj%B|%)-onNA$DM_FD5<Chy4?Reh2>ZkT3uM diff --git a/telegramer/include/pytz/zoneinfo/Europe/Andorra b/telegramer/include/pytz/zoneinfo/Europe/Andorra deleted file mode 100644 index 5962550392fa78514061582e9371c32b9f1d929b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1742 zcmdVaU1*H~9LMqJm=i7OKjo!thM5`1UTtPJ%yG7`jqN;aUS?kA*f_?HS(~>*){@rx zUo2WIL`&kN3>PC2B3dpi&5|OoNs5KEc)pKIS8kMNJ?C>;=koXcn=7k)DaId<)A|oD zkKcOv9<ks0c%0K`M4k^x)bSHCu305|&jA_l56Fbo8)Ra>DKS|dndC~B*vL4UJZy}_ zIo%r9KSQUyiq-g@Q9AX`5S`ZfO<iZ+>Gam8n$Y+_6Kk(&QpE*LF6)()K&PZ;cgc+6 zBa#-?F6k-tl0FcWjIphn@ob&U`cbEu*WEI^uT---$~C(yQ|C10=-gwGI?orQIgPVq zey&s96=P*V>>KsC2PD^dU-M#~OWw!VvM}tXEbP7~i(a0V#i4VO|L}$uv@}cM`BS>2 zx>}YtAJn3f!&>a$sNR`=HS_ay+2}%D?uystUz4<SWUiDx8KEosr^w1HUvyPZxU4=I zA!VH(WKHn1l(%-v+VUP*xA(oSpL<m{ls(jn=r-Ay9a7)W%epB_b<^9US~(EVs(TH( z`I)I(&aP7b-5S|?C|j#Lyt1t_TDLc)Nlj6f1bm}qNAftS^?a9|5r)(yj?}tOFQk6> zSFL|^TN*z0>aL3&vb(!W_q5&8y`gs9*KkIIEkSh*3LE^d{tUyxPIv|z#&9u)8b;)J z$FSeu^9xL)#A6z6`}Laq%;B&<%)c1mPwUy2eyJ51A`fFk28oOk874ALWT41Mk)a}E zMFxwE78$OsHC|-E$cT|4BV$Gejf@%@HZpEx;K<04p(A5Q29Jy$8NRJGek1@}D*_}0 zBnBi1Bnl)9Bn~7HBoZVPBo-tXBpM_fBpxInTPq?YBwH&cBq$^*BrGH@Brqg0Bs3&8 zBse5GBs?TOBtTm$LL@|6D@G(pBuXSqBu*qyBvK?)BvvF?Bw8d~Bwi$7Bw{3FTPtQH zXe4SRY$R?Za3pdhbR>2pcqDoxd?bG403b&IIRv)WF+dK2t#uTT!+;zI<Uk-t0yz}O hvB3Z4V1!#oWO}mO<279csR?NbNv?wABuq>V{{?7%e9iy> diff --git a/telegramer/include/pytz/zoneinfo/Europe/Astrakhan b/telegramer/include/pytz/zoneinfo/Europe/Astrakhan deleted file mode 100644 index 73a4d013fcb82c2beb6f885f359b9ca20da054e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1165 zcmd7QO-R#m9LMqBa&xwI=uo+Ivj>~Qht-;!teQ5Pb6683h6gK<9)uw1OYBe(7-XI* zD1wM0f<}j)4|U37gqKJ?b+h0Hoq8aCmk5$tzxQ9e1<|elpY8SM#~5RucX8zCSX}+_ zTE2AJYvp<6-9WDNwCo>R9l2In6PSzD*Ue_?zq=j>KB~&v`DCnN=3J$5>aq^rKdVDG zO1kO%5xws8uwH*GuQ%*Z>5YX}-JEXFn>zeD9Q2tM|AL8lA`<x;kk&V!N86@9N&Azx z(ovq3=#>|;dE$|5DczFT;Y+ghz?6xfxN17PZ<@s3QzjWYW4hw|OxKTtCe?USQt$fA zwuKSd{=CKPm`_XkPFA{~HA&B8Sax1lk{R<#??lk-%6*XRp`WHN{91BZZTdYAB;Wkf z<iET!12ttcFn7=Fem`mUJiK5EGq+{%;&D?fghq$It&;I-wd$%_ws^ZeOLwie*6Zf& z@3-6Qb}JQK!5`ZeQ}(%k$W^CQDx>V#;`{Ftmpxhh4CjTxqH2$I+E>!P5`2MjaTWhz zKl}5mY>4d0m#`(WC$g!d-4)pu*%#Ru*%{dy*&Ep$*&W#)*&k^D=>TZ~=>cg1>B7;r zf%Jhif^>qkg7ktkgLH$mgY<(mgmi?og!F_o<!HM?+H$mgA&nuOA*~_3A<ZG(A?+dk zAq^rOA}u04B26M)B5gX_K9NQpZKp`9NUun<NViD4NWVC4xBuT7_N8soJ<){k7jTRa A6aWAK diff --git a/telegramer/include/pytz/zoneinfo/Europe/Athens b/telegramer/include/pytz/zoneinfo/Europe/Athens deleted file mode 100644 index 9f3a0678d766881389e129c93def7fffd74f14f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2262 zcmd_qUrd#C9LMqJ2qYerzDWE@Kr|xMj&S(vpCF>+ftd$GI2x%42BH{|5-~`OV=>np zbKe%LoS9aPIcCQ$DCdw`R!$4Zt+8h1V7W5OIhHzW#p?HdYHO{lZo27r_B^lWJmZ3k z^LhK%)z@WO|2R(bhKsY;Ts+SXnA?HCyugX}%i||bA3JyU$C~kv<w)t!rIj+==bIRL zJ#PHd7wWf<MZH=*cHCJy?aIdL>2cSqrw<3M8H0zcnLTR)5nYvmTRRsAWZSP2S-W#$ zR$1RfRM9ilw`H9wjZXeSW1_y&*pOk3z4orgT|B0@e|A`BfAGA<pB$8gqd`f0<&ezj zQb}sxEpr>1WZs4vxx?p|<UFt3>B^OqgjBg}Mx3OEc{KG#w$A@1MHh_5=)#Yt>fQZ6 zsjKI*F6#b5(>g!V^yZW5u6<K8D$h%1Q@>>84@h>|i;|OgP;xU{C3mtzmc)1Kk_($; z=`St1Y|tb3d|9D+y;Yh&uuSjWQ>X<867;^hWG(DmD)$$Ksi!txmZw}&uV+$<!p>@O z@|YA~{Z>|lye}(8&d387-;kBTKJg8`rzQKkWYy8v^ufkPDeZbzS3k5@%NjOo`Qirk z`-*i<>?&RBTA*uhy0s#@NGd*`r4QejFYDg9uIopgvf*%~RQ6wyjUCsds(VDLt43wh zuJ84cg5y$CIi$5o`(<-}Q0v0q)-8#uTfTcqw@x-`{h2o1cEPWY9$Bvqr+3I>&*f`l zZ@E0aHA%Pc$&nqa^Q5URR-VY1BhB8Q<;h4(TGFGnW#VgTjku|;=RTCStLOEpw|Zsg z$bh!*Kc%~Z2leT;9_`rIp^lI#AydLa!$OU>Otn9}2??L}Uw<qsCeQq6xrbWTLboH_ zvJ&Q6Gc7AZtVo_6(SLvc*WdU`{8pj4yxwwde#>KD=F1<)Va}hAmc!xy7d!gRoaTa; zmm_;ZHf3veg=`Di7qT&AXUNu&y&;>kHM`@#VtdH`Y|REa?2yA2kv$@tM0Sa66WJ%S zQDmpcR*}6To3%B&MYfCV7uhhfV`R(7o{>!>yGFK+>>JrQvU6nX$lj67BfGaX+eh|~ zGyv%U(gLIhNE47QAZ<YUfHVT>1kwtm7f3UZZrGZ3ApNj44M94Bv;^r1(iEgCNL!G; zAdNvfgR}<e4bmK>J4kzw{@9uZAsw<cEkb&PGzsYv(k7%&NTZNWA+17sg)|H47Sb-H zU$&-UNXKkV%aEQSO+&hdv<>MS(m14ZNb8W^A<aX&hqMprAJRaigSMuHNDq-FB3(q< zi1ZO@B+^Nwl}Im<W+L50+KKcNX(-ZBThmgcr?#f4NLP`zB7H>~i*y!gE&gA7>oi4P Ylu4UiC0S`XY3}qaS4oE3e#&<K4n&?MSpWb4 diff --git a/telegramer/include/pytz/zoneinfo/Europe/Belfast b/telegramer/include/pytz/zoneinfo/Europe/Belfast deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/Europe/Belgrade b/telegramer/include/pytz/zoneinfo/Europe/Belgrade deleted file mode 100644 index 27de456f16ab549627b284a39e2265cbdb4ad8e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmdVaYfQ~?9LMqhL0H4S7@-o9T#oJ~DdMPHDwhrt$t4vMLdq?-nOU<hYs@g$HH>%= z9>mObA-Sy?W{kPcS{sI0<M;k=Hk(JD*!)lDyjrdG<oo`(Gv?)lS${mO%ujgptT1oB zZ@bQX+-w&4y!OplxqZw_>khf(&W;GVyCFdC9W0aksqxz7<tgp@;DC0!vR%E;Ul5-Y zmEya1zjQBC@msxKdgK>M&*^idSF&6DV-uveGfDz{0;NxzE)wYB(!kFV+V@p}_N(u# z{jass0aahsdE}iAEPt#)n{H|Fvhx~}eNsa+A4ynYm4wGtOT@&T66w27qQZ(Ls;N|> zy~{QF=`0!iy+~s&xMawabd9aZ(zxmv9lCkA4%_3S@j3oFeA8eVk?5hWY;PGE@J16{ zO_JzwLzDcUNm9dW8QuJnjIOJZF)t6x*vjLQTzgSdwv|chiGw<>pg_i#ZPW=<w(7+E zxtca8U){+`I>{?lCp-J;<S!wb-YHSiA9m2GpZiM2*-tvH-czO@XfK&nA7n=9N69L$ zlbKodGHcCyojvTF%*m|PY`@(yH?C51TA$HeU)9{VyELz<Q0LW@==`T{U2t@o=3ieT z3%A5+K}DJ@%Jb93n<Hh(gjgxe@sg#X-DO$AH(B1^lA_>FTGaSLiranB;=5O+q~VdS zJY6BH>Z*11?#sHSa-Xg(IijW8O4ZS#S#$g4(ehuuEURO*xhkujSS@~i`t)$LwyfVj z`E7EF+j1rFPIH?-a5(tlaX8$6al=2%Gb6Tf6mrYRJtH@b+%<CB$bBO>j@&u_nOjHh z9l3er?vdL^?jLCY=>TZ~=>cg1=>lm3=>us5>BQEwg7ktkgLH$mgY<(mgmi?og!F_o zg>;3qh4h6qhID3YT0?rXHO(R2A?+dkAq^rOA}u04B26M)B5fjlB8?)QBCR65+L~sO zZf#AwNWVzKNXJOaNY6;qNY_Z)NZ&}~NaslFNbk0$d8B(=(>~HavH{2rAX|X!0kR3m zE+E^0>;tk9$W9<zf$Rmc8OUzfn(aXL1KAK{N02Q+_5|4!WLJ=FLG}gN7-VOVtwHt% z*&JkdY|Zu{`(tZ12-zWIi;z7+HVN4!WSj7R-zQJATLVKGE@w)3P-IYuGbJ<xgTp<4 E0<)*Xw*UYD diff --git a/telegramer/include/pytz/zoneinfo/Europe/Berlin b/telegramer/include/pytz/zoneinfo/Europe/Berlin deleted file mode 100644 index 7f6d958f8630cba512d8e58ca8edfbd516291522..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2298 zcmd_qe@xVM9LMqR@gs$id#k~jfKV49cEa&9G05x$W^kHv8L5N>f)-(%#-KDTnRAWV zyT))1$saLh&944HTSIO3YqXF=yX-92mMgNFb2F#aSbd(~wrcCI{^_4Sci->hd-vz< zUQhqZ^$j`JKTeGKhKuupxp<zo)!cgHoNIbR=PvIF2eq&Nu)h4Z>fX*>IvmrYBje?A z@>Qpf9`MR1FXhRp=h9>>kRW46daTn`$G4u|zf{iDCF-Z;E)5lqrG^^EHFR*J^;yQC zoOREX&trNd9C1R!7u#gIZ;{-T`-{fAe$a&JU#c_mq&lx0*BKYy(3{Wf)0ywRsEJ39 zNz$R9B=6ZPv$|ALHa{z=O#!)Ob)DQ=;TKm?iQMKckhG+9xjk-%q(^%-{c4`h{w7T` zLJ2zOgGqYF;B|HPU(~tX=QOkPW6j!hR6X^tYj$;5asq>rTRbFr%X=h0xlamm+N5Bj zL*^xR>%8-|Quu4D&OhdryS}K>qJbJM9-6OrZ(pbjdXw~?2A3}EER=goqt#oVD2vj@ zwZuCirO_X0nd>VlyY#IrjyxfYM~CIU3$I9d@HMFzc}IOayJX3s{kpWdS?=%Jrpq3D zUY9qm)yg}Y)L&7i4>*_T3U`LCxaQHS_)@7lJ53+DI$KsA`AJuWVrBKdsZu@oqdeSk zMQXZ7Wlc>;YPWo^k1Tjg>Z(VyK4rJ8Ee>kK<ipyStlIe9E?qYf(DlRZ`sjJTK6Y@G zHhtJ4kMAhf=7CDtur5WP*q$#f%Zen>;FOKovt(1r&+_C{OIowywe|AX(l+Iqww-!k z+AoFmsW%7Y>CqwGy!&0<6717w+WWO*XNN{OA|j*yd__BWzQMjnO`81Qzbq?mhxs>K zNvVz~%bFtm!T<g|$K$xsvi`XL^?ilUZ+S~9xpA3K<~SVY?cm8Ahr|Cb2KAdc8xGW7 zM}}o<#)S+F85uG(WNgUbkkKK-L&k>;5E&sdL|ZdPWRSLIl*llVaUuh?H6yh(Lq*1l z3>FzJGF)3TUVaG%jEoo=GBRdl(6(mO$gq)dBLhc9jtm_cJ2H4=^vLj$@go61B7lSd zi2)J>Bnq}B3`iW1Kp>GoLV?5r2?i1kBpgUQkboc&K|+GW1PKZf6<ZS)BrdilFi2#O z&>*ovf`dc{2@et<BtS@nkPsm;LV|=u2?-MtCtDLJBvQ5}R7k9lU?I^$!iB^O2^bPF zBxFd;kf0$^L&Ao{&DI1CiJYwo9TGbvcu4e+@FDR-0*FKq2_X_gB#1~9kuV~0L;{IK z($<6$i6s(DB$`M#k$55jMIwrX6p1MkR3xfMSdq9Qfkh%~YeI{}*46|Ui7paeB)&+1 hkq9Fp#{X4}V@;^%W|M5UFE=wk)8qDKdoU|E_IIABaYO(B diff --git a/telegramer/include/pytz/zoneinfo/Europe/Bratislava b/telegramer/include/pytz/zoneinfo/Europe/Bratislava deleted file mode 100644 index ce8f433ece44f0b96b18d3b5780730e7f9cad9f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2301 zcmc)Me@xVM9LMqRfyj?3d!GhJ^0SDLI{6KuL1rf~k~5XdNG1G%sCAKC#-KDTnRAWV zJ7YSBq!nY;jQRs>4XyQav=B|b>@3%oE7O{DGpE&9eV*T_(ei&kU+x~?@3FD5@p=y5 zl68&w*8fhF`GgnedGq4lx!JsRPjW5q4xYNWC)BS!y$AHA_f+?G?9!p=W*t5|PY%EC z(vep@a%4xL9DO!Jz6`|6v6Yc>d~=eXs5&MmUK~gZh6U1s)g|&()_|OJPm`~scS^{y zUP9+u#o3UlQ+x?J)jL;iDEM9D(tp<Yso$zA{II$%y{`#p-qIV7@6%}?zo3Z+4@uJg zeo5Z5S5i7vQa3&$Y5suRv}~2!T<w$e(sH@QT`U<%nR4r-1j&r>Xy)ZYo&H0HW(DK* zwofDV_JOPF?mee7x=v|!#}}Hj;h^R=ys3G0A;}L6NI}`46fW8+Maex<oZl+NqwP|X z*rg?>SIEpiT6ESSkKFl9t(NxHYuVr|y=&_no!y<JcQ>Z%oQ|1tPep`!8WLr0##t@* zj7mks=USQmom5`<QL4f}l&X;-x%bSgGOzy)sUH47z1urw{{ENszNRKw(78nyKJc6_ z@~_sKJN)XauGITo^L4R1OBY|s)!MiUsXaMGAG|zWmb~+;E)B-WvVBge8~8;YYQH4) zT_du*J}4_To!6DK-<4H$!`hI#TUM9#Yh%;_U6ZW3=BHh{b~K>thT8Pu(>{IVwWaF+ ztXUr2R;EpTHS*ZnR9(NdNSYUxN}$mtkLRVxhVtL!38y73IdR%@@q1~Fy`rs0KasWz zA${`gK6z?nP&e-WNH_KO=+kYz+P=MA!yIAZ6UJW=W6u*Kug7Isled|_W-BSpF~PE8 z#ftv#y=6HjkN>3F>$5!NHN5$(O7mcj!@-w*91h>LcVvDnKiWPzb|3erIVn{;uA=|Q zd0TeHGuuM;g=`Gj8L~BGZ^-75-67jU_J?c`*&(t;WRJ)uZOtx`ZQ7cBA{#|^ifk3x ztF75Aza6_pwu|f+*)XzWWXs5&ZOx{UT_f8@_Kj>D**UUxWber4k=-NPNA{020O<hI z0;C5>6KqWvkTxKFKpKH`0%--(3#1uHH;{H9{XiOmbOdP$(i5a9wx%mcTWn2Vkj5aL zL0W_K25An`9i%--e~<<t9YR`!^ayDZ(j}x#wx&-=qijv5kX9kRLYjqi3uzb9FQj2e z$B>pGJwuv?bPZ{nt?3)mI9t;>q;*K|kme!XL)wS*4{0FML8OI950NG!T}0Z5^bu*K zt?4AvN~D)aGm&m0?L_*CG!*G5(o&?SNK=unB5g(biZs^NbQWo?t?4b&T%@~5dy)Pk z4MsYQwAc}D+8Z(cnmG0x8Ff9be`0KsY+`JZZ2sGb73=Q+|9fwO>m2`GlDyy=SsveI bb01@hJtL2HyS)Y3McKJ-Z(c6u6vX@mfqZ3V diff --git a/telegramer/include/pytz/zoneinfo/Europe/Brussels b/telegramer/include/pytz/zoneinfo/Europe/Brussels deleted file mode 100644 index 40d7124e5346af056c75e2f7012a51d94e8154b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2933 zcmc)LdrXye9LMoPfFK%v(EtV1Q$(mCcSue0g6L52N=n|)MASq=G6v>#G1GMJYjdeX z<EV(26wN>}yyP91lBH-`&P|uvW%iUgt>*0aewcI9+WgPw;W@8!*pKlKygwNur{u&s z9=n>E@9=Qdn1}ZP^UTZ2KB@Ccn}_9>He9q;U#QeI<<<Jh9@Vmv6<St*H`+CBgSB=} zZT`B*me%^2X;yi{d~3tl(EN(91Z!h>skNz-o3+_%wzc_YeEyc}S=OiL2U}Z@_p-K~ z*l1O547Ikq2IcSgz|;C{(Nt?^ewyp^f}O6a!I7@&)RE3z(c;=2Tj;83@rSGC{#DnW z-_E+eIJekY``z!(y>(Zd`)bZO_m?el9avfI{IWFId9Y}O^U&ml&cm6-&LcgdTt~Z% zavtk;(0M%boa^|-rmnjFZJZ~s50F!v1GRo#vV8qfyqwM(tKSp^$hQM~>X~Id<?Mh^ zeYSjp`YsNV79}YvGZNJ=J6v04wANPXedM{gztunV7Y%6jg9f^v(!g8$we{8Q+UAT) zgAOm#;Jy1KWNW3gU0o*amZ-F!HD8>$1@ipp3GzZ(hJ<!ckryKpB`hRdUTWT2!ab8U z{C2!{_%Td7HUwy=qh9)Q)m@D!|4BP9zo?NVUuo2gy&9dpO=HGfkl2DMiA$=L_@N(5 zLfbWx7+WNX_vT8M;N{xo(s=3mPoZ|(mn^S-pQ+s|vNWl-o4&T7m-Z+P(VjV>+N-3i z^zQ4a$=SiuC+xbWB;S+1o+q?l=w<15^O~f(AC%PkI_ZCPy$q<_B55ZNX!_D6GH~l! z9h8?RgO|LoLq>e4LvttUuvc<5Bdwpl9ym~kM|9NTccL}ZzprGTZ>ewG?jR#~{i&lG zd}OrCPsUXJDr4u~lC0(RGA^q@#?Ss)-|VqlCX6|$+3i=!#H32i@vhcMZB-}TSfP{e z73h?@VtwmUhEClyN^_4*muU-=G_PWqygj+S<}XN)=|j3pK~A8&6Vpy+r2HlC`Z=U9 z%3lkuD^lcpM~hA$k>Z;dbmoo<d9S`!XRSJ<vn$u=oZ@nwyL7I)J>&U|yNA1ncN4EC zzB%7`H}!d1x5MEPWd1d|JRA;Rad<l%Ax^jEkG^;FdvK=l<ij&*=^2g@DH)Dr`x(Xu z^I|SHH}hpK`v>jMZW;ew4D;I@JY0Ls!~3B7<{G~1@->`xqt1a81gVIvDG5>&q$o&L zkh0jCx*&x?Dua{;sSQ#bq&i4>koq76LMmiyN`%x1DH2j8q)bSikU}AqLP~|y3Mm#+ zEu>sXy^w+-6|*%ZLu!T;4XGMZHl%Jy;gHH9r9*0m6c4E$Qa+@9NCA-w+L{s~HAISt zR1qm7Qb(kaNF|X{BDF+{$sYmLM9PWO6DcTCQKY0uO_8D^RYl5*)YaA$7O5;!TBNo} zagpjG<wfd?6d0*6QevdWNRg2$BV|VFj1=0|R2nHYQfs8xNVSo2BlSiKj#L~eIZ|_^ z=t$L(vLkgz3U6yFkCfil)E+55QhlWSNd1uoKvn=*0%Q%4ML<>oSq5YskcB{20$B=M zvlhr=*qYTqmIGN2WI>P>L6!tr6J$}4RY8^oSr=qskd;A}#@4J2vN*P8b&%yj)(2T2 zWQC9=Le>abBxIG4WkS{oStw+skflP_3Rx^$vs%b<A?t-K7_wr>k|Aq`EE=+E$g&~p zhAbSia>&vlYlkeJtyw)}`E1SlAq$ACAhLwW8X}8`tRglpqk9u~4^I!zre5Apd~?3> z_G$LC?g!Io2GUHUxy&^B3gcwPPab{m=KtVK<H?6-7|7oLH<FL7B%A4DwzP9hvf0sy S^ti}`$modlm}rcO^Z6GUA-G)t diff --git a/telegramer/include/pytz/zoneinfo/Europe/Bucharest b/telegramer/include/pytz/zoneinfo/Europe/Bucharest deleted file mode 100644 index 4303b903e5e007484c0d8e1eea43a35e9b53f38b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2184 zcmdtie@xVM9LMqR1y}6)u{RQd0iu$LcESOoXy7jbvN%OKja1}oA!41BsO(6TTRO*z z+&g2HrJF9ttQql#Wrjv78Ex*QAFj2={4QIy=B$mJYmU|D`R$L^Tz|CnSMS^1<HoOz z?OxB&iuDco)(w|nzQU91Fi+kmM$OBy-p><`&l$0g|Gdxs;NlB*-}&wKiNQCWlkdOg ze0cbIr~lP9=cA4d&Z)?1=XB#@=S<B!XW+p+=i{P@&e`lF=abZUC-Lpi>@n3&RZ{TQ zp*stP?BuNB=v_&%Xi8jvG-aeWntJJAbnF*<qIaL!5luVsj+K7ofR(ZDMQdE=vsPx? zCTo1S-kPv#nKiMp+{!ARY2D+`wkD;IweG#mXHE7NMkinO+1cM+x2Jr0*`7KuY~Ob@ zX8RBI+ta#w?VO!2RpoBpV+ZQDROQw7IQh+6hYCviWct#Vr7&Z^%*c<(jL~)}O6$_1 z;WaYrdW+6JSt|E`Q=`S*bz0IlTj%VWuXA5d*Le+DI)CRZSy1lP()u)cVA2m-Ryr!> z-cPk6>!MU#`Cb;rosxyIL3!xX8}e|^TT*$hUxT|lWzms?y11!HsycV*l1HA?rQx+& zJu|GK$_ibUvPhTvr|9x)0j)_cmzuA}=%ZJ&WyO2H>dFf~S#=;$YL8x))$Jov*A)}H z?t-k@_LDv~_n16hdrs>!_sZIm9&Jc?SJ!2zuKV#7Z5(aZ^@FYY#BfNTJiJoFXE(}* zjuLI^u9l}7Gxh0Rg|cx;u{1ZN$fmq;vbpRx*^+2UOK!5Z{PL|t?zpCrp#f>V^0hwG z+bvsTecHD7v~KI!uiIM>Y5VSW^>}ZJ`}-r_8-K&s{`v{N8}GgOmjC);S*a28QROw> zvZe++=F^)#-n#vt-d6@g)&lb^E34)uWPa7-`SZ!2@pwZ2VkEzt3!irqkg*_xaW$hs zhJ%a;84xleWJt)EkU=4%LWYHm3mF(PGGu7T*!T|^95Om&c*yvW0U{$rhUjX>hzt@L zB{EE8oE!t?7%9h4ImXH{SdP(h43}fP90TSUF~^WO#_Vbajf@%@HZpEx;K<04p(A5Q z29Jy$89p+8BmhVRkPsj-a5X_dqTp)6fW!d_1QH1(6i6(PU?9;z!hysC2?!DqBqT^o zkf0z@aW!E<;^JxogG2@i4H6q9I7oDm@F4L)0)#{e2@w(_BuGe<TuqpeIJufYA(28t zg~SR877{HaTu8i-fFTh>LWaZ)2^tbLBy33BTutDR$RVLaVuu6|i5?O@Bz{N$kq9Co zL}G{p5s4xaMkJ1|CXh%ZT}>#FSR%nhqKSkPi6;_J{9i}ZXUaM)pT_!w1v!N|f!qRr LFfZV~o9_DqL0CBH diff --git a/telegramer/include/pytz/zoneinfo/Europe/Budapest b/telegramer/include/pytz/zoneinfo/Europe/Budapest deleted file mode 100644 index b76c873d9256e1d73c2ea672140b813f15657bc7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2368 zcmdtie@xVM9LMqFS0*#}Cc{$!v51gD;P{moWOe~FI1{;yR00!GljtO3M`@Tc*Bo<i zi@^v;MIvi<s|{)mUHPLXi_kJMY!(w0Y|T;RtTn99^K1XL{^+0n==<G$AK&}l?w{Me zp6(SJ%93n<J0_VYyf~W7i~C!)noG+Rdr8Z{*|F9^pSE>$>+2t??x}0iy{{dYeWf+p z?wKX~U5(N^-d)l+k}IFTW!L_8r<{BxMNVytmM@yULtpOf^_*VXsbA%WZXHNJCSS*P z$(fmN$TyQ4WzheK4qo=k`St^2!}&j#T-f%eTy$L5OI6opBxFcO-9s|^(@~w~iW$2t z`8!W&)Jf0uX&-y+0q=S2<L#aq!>@W~p04xEI<{NG4jq>8gFcC9-6ON>RU$XPBy-9u z<@Qx;<c>VIL}g^jopEUr9UdchO_?DvK~9ainxb<rMr-WAbiMnNNqSG$uNv2JS?4vJ z)%d#4G@<H{CYHXVj-o+Hs_c^F%pOTu)+nhFZIYJcm9&XknIG1m^Utl8^xvy>!C|M| z_idqObQWu7&jP)F$0A+W60Q%FMd_ltba^m4NS&o&@=)}MW;rJ$JE&K4qP~}$E0<(( zz!6#8e?lG}-Y>bnw<WLdBX#Ynmn8@H=_3^tvb27?=0CPmmzA&8f_ux=otLAJ+L!3^ zxL93&El~?Yv!(FNt@`-Yxw7K@Uv%X_u&mk}B1K(4$`iHYQryrlCB*}>ddm;`<iZbR zO;MkgMmEdZOrMtB+^y>(RM(9*>H3LE-Eg8tpE~E(jR#h0`KO!Y>6bILqO(ArSs$sM z9jUS@KSL_Z?DDK*wp3;PCeMY~q&gu~tH;iZcj`6uo;ofyR|fU@cRS^U{vO@jd{npi z+VsVm4z1l)tNwoe0fBzzYR<p_bDH<&$&I_ep5OS?Kj^0a?zGuLE|`D4Eqsn&pv^W_ zY?J@#KF{U0IkO75xcP>kIndm=|Ha1sFvmvw?p9=LmS%6r=8)YX+e7w;Y!KNYvPEQ% z$R?3pBHKjviEI?vDY8{$ugGSR-6GpX_KR#7*)g(ZOS5NW)5xxoZ6o_eHjeBZ*}A3K zJHI5GM|O{FAK5?B087&Wqy<P1kR~8qK-z%x0cix%38WQBFOX&+-9Xxb^aE)K(h*D3 z5~L?cQ;@D8Z9)2iGzRGm(i)^UNOO?xAnif=gER=~kfmu6(j!aLB&177n~**sjY2ww zv<m4J(k!G~NV|}JAq_)1hO`XnnWbqO(ltxdHl%Mz<B-lFtwVZ;G!N+>(mterNCS}$ zA}vIEXla^=bkWkZ5$Pk+NTicUE0JCz%|yD1v=iwk(om$MNK28PB27iQYH8Yv^c86= z(pjXnNN<tmBHcyWi}V+1Fw$Y9#Ym5lCL>+8G;K!uY-t*ebQ)<j(rcvINVk!8<Nv+i Z!6xbR9K=1&l^mZMpBU$IBw|8x@Sl-7wT}P* diff --git a/telegramer/include/pytz/zoneinfo/Europe/Busingen b/telegramer/include/pytz/zoneinfo/Europe/Busingen deleted file mode 100644 index ad6cf59281a1046d9dcd045fda521585e3e33e06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1909 zcmciCX-L$09LMqhq+=x|UkjT`&1!PZnmp6((5_jPcE=8#Ej!E(waeVJGVQV`Btqi5 zAVpMc%Z5ah^}y<Z9c&jJCJUQH7eUcw5kZ9=Nd4abC5WxZ{r}9ohV0?@{qfIST%2Tm z^*GJH@Zni)KK$;!(R^KTEwQfLFSD+;`>f`(xmK9_nfB^=M_mEe)b;AL_I_|g`~164 z`=0w<!%v=)h(iq$x#th*SE~}WZj<ycDVG7W7sx=LU)*UKGRTuE(GfB7L$}@%<Me9G zo8db6VYJ4!_R=92I_uEJx9ZvdREO2w(zq>GHGbtuO(;C9iTO7rsk~8=)0<>?&JIb5 z+$*U`m6F;~EhEC~bj00xGV()(jymO)(YNz7t-e6hn?~uFn(;bzcZ7~BcI)^pBV|IS zQ@w@Z@>BF<&G2?ert`99x$jBVi$^js;BT4Oa!G!E@R$73a8P{BXEb|ztxP)fr%o;{ zl_|BGb?WqOnp0Awxj&Yu-<PGox+du~PpnRBPtd%uOv$^^Lub4hEHjV4)>*B=GJ9XB z<TpN-In}SEpsq#c7PQK|^=&$T><L+r->ijEyQC<+L5sT_(}j_$3!m)NMIGh3_)?WF zx$D=Z2WDx>#WGp8HC;>VbLF>1QM$Y)Marh8NqMnLRwVY5l^O43Rj4Hu@nKr=^1f7t zv}@%*=cVe!O<i-eUe>lW>AGEKb$!EL-B7h(tG8EcCx>|h0>AfbSzXLgSyn`UN1$be zh}HGW-@a_W<;}?D%g_IEIP5R~w{JGc{E-h&rTOqX^rLwOy=>cvW!HmhkQ=r&cZ}RJ za?d>6G;-I-ZQGjrMs6IrbL7^Mdq-{_xqIaHk^4s)KsrELKzcx$K)OKMK>DyXjUb&M ztsuQ1%^=+%?I8Ui4Iv#NEg?N2O(9(&Z6STxn#PdMY)xxOZ%A`UcSw6ke@KH!he(S^ zk4Te9mq?pPpGc!fr?#e5q*q(hEYdB~F48a3Fw!y7GSV~BG}1NFHqtlJIMTVTX&vd^ z)-;cFkF<~Uk8A+41IQL2dw^^LvJ1#IAp3x91hNyzRv>#}Yc>Pf4P-lz{XjMZ*%4$* zkUc>*1=$s3TabN0HU`-lWNVPUu{E26?2fJ39%O%z4MKJZ*&<|*kWE5%$q~@Wyn)W| z{eB*%p!b#;CNocFr$WT){^f7xX~O>|>c5RL-@#_Hh9$CIp6ukfl(+;>c47j?CkKB5 Dj=i{v diff --git a/telegramer/include/pytz/zoneinfo/Europe/Chisinau b/telegramer/include/pytz/zoneinfo/Europe/Chisinau deleted file mode 100644 index 5ee23fe0e59f044598675db44d53c20590b88934..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2390 zcmeIye@xVM9LMqZ#jg{Q&Nbq<8YYmQ{1n9!J;lQm&_fW(1g|7SK?@bb1pUl8M)PN4 z&59LkO=VMti%|+1l_@KNtFcu>>$^4kJ^ik7w#?P%`6brc`lJ5nzh1YyCwzBv&iQ^6 zty*7^CVxA}A5Zt|@^ie>A1_tC9P)a{NA@#wfApTY-r_y``F?Nr;7)H(Uz>66jTemb zC-xd|9<4Wen>HG6)s-1<S1vQ&DRCJW9!xdf&5ScHCf;RSitso3dYZlOJ=x&t|0>t< zL0kBtfmgB}gNJ80d`k~`!xE1B?vA+Z3bzk?!hgB5H{#n+U*vGME2_WQ7v0lp#+-b{ zjBVL%PT04_oLFCOPO4sE-m^N#jLVy4PM(u!-s_Asr^E!C@ndh9@!5GsLO_N}xDl@s zuZFAphQ9Ysy)fvR);ZvHzIxg-{YZy5X-~5!dFx?sN_nj(wY1$x+q}b<o^?uQEN_yG z*n=`NZG+6bT_c&%jVkj>q0GKjsqR0QArA~MQFD$JsH|?Mn%kJJ=DirIToqwz{+?;F zz<pC?myeT$Q$AJh?CX*f(5)6t{!kWO`$QJ|y(WtX&dQQ6o|WA87Rl>Bt@01-l%*$| z)v`@q@$9Qr1uN^-^6HhUa8{Wr%A2iLgu7I+Gg=kjj8i3HnNsqxzk29Kl&tFdQawEM ztE_JIlhV%5q-@t!S#zXUylXDWBMq0;qx0HiZRvScK5?%+mer~%?8nu**xhQ~*H5d; z+vRF~UzOT$B}Y}A$XC@D*UHAGWVPv-TOO|*ubya3keY%d*<3M3wxl{_tNS~tb^IXP zl7rMXb4cn!zfpC*F4=zVef3nwQQ0wYO4UE#sT$f3s-4@PQ@ak-DBB$Ye*S-b1&#@_ z2ieC4kGw+0{rL*i-wX`+?_MI&cKv@?qJ9#8k%&6czfDcCg^0vVlJTRTBTqsd62=o- z<mhLn%Qk`UOWf$^=#$YuAuqp3vh{m`e!Ja;eCP+(TmO<@xKO`y`3u-=BX8@6qJMFM zzv&fs_5DmaManr+PSUDAOUh|d&XaPYlryEAD&<@$Crde7%IQ+hmvX|CGp3v}<(w%e zO*w1IX;aRda^jRTr<^+f7@Rxh<SA!wRi8fP{3!%b7@$x<;ebK{g#`)?6dou<P?(@l zLE(Zz289i)nhpvdRy83MMktg}IH8b2VTD2qg%=7j6lN&YP`IIx!(fL&4}%}8nji*4 zRy9QojwmECSfbEG;fX>Ng((VE6s{O#QP^V8Md6D<7=<ybnlc7wRyAo1))=%gc%u-< zV2(l^gF6a&6!s|eQTU?}NMX>brjWv+RZSv=MFx!&9vMV3m}F4N;F3WmgG~yZ3_dA@ zG8m;$O5xP1CY8b}g;ol$6k;jNGN`3+%OID+E`weMzYKyI3^OQZaBNkR%wXB7rkTMr qg=hxT6sjp)Q^=<Ov;B|Q4%XkAo(A*I{Pd)Zq!ed<Y6?wG5B?oV8;IQi diff --git a/telegramer/include/pytz/zoneinfo/Europe/Copenhagen b/telegramer/include/pytz/zoneinfo/Europe/Copenhagen deleted file mode 100644 index 776be6e4a6d5a054152135a1ad149576052f49a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2137 zcmciCeN0t#9LMo<xm+<)_9caD@nj*PcI6rJ0W`Y;Q@NIMReV4+5ygn)rVL7@Oj&Ep zeRVA7&}rpZHKG1M{R4%m<!~WVcE_xoEm!t5b2DcTWA%F<$8go({?5*MogK!;AAH{a z<&_l~)_+cz`NGZFWp3U_ziJ*W@lC5+f?fSR-ldy*ug{kY@4L0{9j{#6n<f1_lH{{M zw0u56^iusU`68xGgR9ykc=a0@C_JwN-DhNsZ;p)3{8OV6f7a+RU#r`BLEX2yblkNM z_2Em0^^voCH0I<fi9OmbaR&}be3MGT*4JcwRX`qHxmF%4@=IcFzD)4sNK$OFJU(ii zB)hztd^<}gewU<^g3&tplQ4av<9GEO`BA4df2k=uKh@ODCpE3SP18#+OGcnWGV?km zYtdVh9d}T2GU_B}utBEAH0#tWYh>DAwL1NjS7v-wqPeYQn%6m9XEx5(SuL^pR7IlB z-Z@R4E^w*0JVxduUDtf?pcJ^yYhmJ5DZKH$%ypiYxqUq{@7lXEzx@L#>g`tF?j~7q z^lg2nx>}xXdR-STeNz`zt<&NstJGgqsEgeTbctt@F1eGYB~b-ZGVqWtyFF2sAOB5P z1jA+J;Yca%_(fJV+>)~9K3QEBlr`Ia(&uKKkhP_~TAr|9*5$Qp#mHm2K2CN05Bqe( zU_dK->UHB4zdrxo3avU<BQLy>r`4^+^5TXB-PD*ZH4AekP~n!B(&J@w{vWa>(vsTL zD6PHut<*)_(YpSRrT)fcefgtSd8MyYx9&fq+u9H6_WC2*u)9G+93c*8=slNf#Qnc4 z>%m_0ziGvecZ6D2gjf#@J@0-Q{$AwsTi*O)9)5nqVGf!X|Nk#xr1>E?r_tQJNB(V2 zW#h#TtQqxd7P1Umvkqh-$V!l<AZtMugRBNw4zeC(LCA`bCE1!aA&as#tKyGiS;)GO zg&`|LmS$_#hAa+Q9kM)ReaHfl6(UPS)`%<;StYVeWSz)Dk(DA#Mb>I-7K^MFSuV0( zWWmUaktHK*Miz~%8d)~7Ze-!e%8{ibYqvFvM^<lZmXE9-DF9Ldqy$I}kRl*eK+1sB z0VxDh38WNAEs$a$)vz_?K<Z&@3W8JwDG5>&q$o&Lkg_0kK?;LZ1}P0v8>Bc$b!<&} zkowq~0wEPbN`%x1DH2j8q)bSikU}AqLP~|y3Mm#+Eu>sXy=+avkcuHCLu!T;4XGMZ zHl%Jy;gHH9r9*0m6c4E$Qa+@9wx)nc1#L|Uks2aJM5>6C5ve0mNTiYuXNWT-)OF7_ zBJBQOLzy(OG?_F}G%bq|YxL0b?w8^3b}auD%;931w{TO@De3go<I7CRPD%6l($g?C GGyET=z$0G( diff --git a/telegramer/include/pytz/zoneinfo/Europe/Dublin b/telegramer/include/pytz/zoneinfo/Europe/Dublin deleted file mode 100644 index 1d994902db21814a626e42639d7a96b18ee73756..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3492 zcmeI!Sx}W_9LMpO5Cz;G%`H)R3^xq%fJ;gexe|)xa#GR^Ek#W-Bx6J~aw<#5asD=z znVOGFN-ntJisl;XWG*Q#xbNb=CN5~t_r2(*tEMix=sk1zoB^(w;rIP}dks#GbpG-L z*zfS>IcMK|uJnohGOgZ<Nz)FNt}uO%$IO_!)qFpFhM75~x-p-x)3eeao7n@V>p7!# zn7IjS%nvb-^t`%#_5AvII;&xr&bm@$7C8IOg8a&QVc|`^XnTrYyz@7mJwHJ&F&Sp* z7aqOrtG#A<YP?yIw%p`&2{$X_dg)ajWmZR~n>FQco3#}a&Dt9W%(_BH=N`VL*B|Yv zH>}yOe_Hm$Z2Wem-ZU*pZyqs4Zy8yvw<c!jZC&S@pIh|Sc~P77_QZo`dqIFX5O+%F z*Q#v}CM?p2!W!tq;S=@Y2Vd)ga;duD%2j>zd{uMom)-jKwg=`!&Si5lyNW(Fd6zkz z_NqBEWVk-tt)V&R6>~najJXiKSYHeZ)t4%H&80`Bbz$)^eYxP8zH+>~zPfIYzP93_ zx&GZUePhf(b93fseXC!bxt*D$?{rMocavJ{d$r4&-(y1bedmTL3ii`Qr}mi#PcG_* zn^u@dmwe6RQ43A+_S{t8)I41xG*SC?$<ieQqI4<GcwMTnh7M3EI$*~W9e6WAmtJ1o zl-cXiWhXs0<(5{|<%gXxYHGNt&~=lkn4!$G5wlID#M`EF$Q7y5B2TLN{wmd~Wl6QO zOXRtt@lt*3Bndh_U)5NYt!i!_r)o_Q6+CN*(kW@G_L$D<`FO7i>DO4*32UzEcI+he z%2ZSJ8#a>q_nJy*V0j76uObZ#Z^;WeMH04izciX$AmKl*l*Yr?i)Y*viRg1gMW*Gb zCegX7X}51wvzjwh^T-jZd2yy{5j0s^9Pg)EK1!FU4Q<s+M-!#hyd;Uvjgptgww9P_ zHRP4#5NZ8cOVy@>pS0~Cq}tWJDDB%8s}6oUB{t-wioJe8#rbYlal3b@j%VknPT31o z{H~4CdEx|>uxOUNHgu@!GT~F{+H<^gOBo>DUrZ5ie5|})H9>lWg-Vb69!ad+K_wol zC~w?rpn9#kE4}jr)tjb*>XUO-y_I=KB~9L~`X=S8exonT+cB%vJAHOZ|KO==Ky<bw z`>&LNHAMzqnj(XW(`4|D40-psSB7NumXy3<>b)<cW$3)_>it2%k~+4T8rHRyN=vS) zK8UELhPQv9KCIxV^v0DX{pJ}pqTGEMv3HxwxPC-NE}f@F?aq~trf!kZ*)!zhjJc9I zF;je=DdGF)%df=0{PHhZ>Ob&$`t)HP$FX0_J0%>)KiJ3Lamp#5GIoj_N4cNvcO1vZ z{p`3ub^PNyd!2Un9oOCKw6X74``P}E`|#WL@$qrIe`EWe+NbBfz+=b;y4oE?wh-Av zWD}8HM79yxM`R<BokX@0*-K<Ik=;bL)79=LvZ1baN0BW>_7vGvWLJ@GMfMfhSY&6B ztwr`0*<56Ik?lqH7ujG}yTiy9BYTW&GP29aHY59tY&5dd$W|kJjchiu+sJk!`|WBs z9NBSX%aJ`tHXYe@SG(=VzPs9uM|K|BdSvgB%|~`0*?wgIkp>_gKw5zG0BHi!1*8qG zwhu@nkWL`2Kze~R1L+3R4x}GQLy(RjEkSyMGzIAj(iWsIuC_5qXOPw)y+N9TbccTx z+Jp25X%NyOq(!c_M@W-gZI_TXA$>v`g>(vO71ArDSxC2#b|L*j8isTXX&KTpq-n0U zYe?IWz9Ef6I)}6l=^fHMq<cvFkp3YJL^_DH5a}V(L|5BIq>Zk&k4PhtP9m*DdWkd> z=_b-nq@PGbk&YrQMS6-f)zx+tX{)R4E7Dk`vq)=^-XhIKx{I_I=`Ye?q{B#yksc#W zM!Jl&+12(LX|${DG}3CM*GRLGZX@kR`i(Rk={VAIq~}P}k**_cceQ;-8t-a5kF*}? zJ<@!n`$+qd{v$U4at9!{0CEo?Hvw`NAh!W>A0Rh^tGyGDTLHNjkedOy8<5)pxgU@l z0=XlQTLQT!kedRzE0Eg)xi63#!`0py$gSaO?+xVUK<*CY_CW3r<OV_R5d8lZv3~^h d&u)=Gd#W^wuy=|ltaF4Xyji%l2{euf`~${g?PmZ0 diff --git a/telegramer/include/pytz/zoneinfo/Europe/Gibraltar b/telegramer/include/pytz/zoneinfo/Europe/Gibraltar deleted file mode 100644 index 117aadb8364cd7901388098503f4538c7b445aeb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3052 zcmeIzSx}W_9LMo<Ledz=9-ROa#K#N~Lj+uMBTGc0fLu<ByQZQhk&+R(WKCvG<2e5s ziX0#xlh851C0udI4L3v)A;nD7+#R#bQB$<%`!-!w7v6N$`_6kl&zza}?ws$>Gi-87 zl<RL-L;DR6SGj#Sw|K{X<hCs~xwYOp?_h+<FW6ze$jdj2a#|Sk{zknx<F5H~LY`hS zbB`%VT5rDUeMc7tkI_p*%Js7LVS3r+TV}bd+AOc})n8w{ri*r`>f*hJb;;5sy~1Rf zl^;atRi7L(tEVQIZ_-zr(*EIQP5dyuHbR+oQ5k0aqraLB&63TApO2W07hSsS=r4NH z@gaKi`f9yp)jhNI^ELY0+yK2TGe>WqQLlF-XX%{-3e2u<!*zL&ZF+Zdt=V1oh}q}Y zR`%r#mHkygQt?(#Ik3tlm1C0CK{+jl(nd*Dx}U1L6QvGMzNf3lg_<Mrr*utFYg5yw zUTRLBQng7%y7r!zIU2q|AHDslu4^(?*ImA%kDv87-|w%~CwAU8Ka^fFCrkYFsq6~# zWBT*v^pxrP%)s{MCy$u3QH{;H=wf|7AXHy?%wzn$4v7EtEz<nV3VFQRNQ)gGOTebh zDzK<Twai<jg64~AHS1laQ`1%J5#!Vo2_Ds^SFCz6tg8wR3{h<xH&-FvF%oj4v$Q)K zETJ`i(tcM%d8+i5gcV$r4%u}QK6k%#oW4mS#urQE$YUxhy;OCIE>oQcex|y#%vW8b zGF8|5IjUPgwsbo&R&~FVAw4$7sGi4@rB~r-i7x9Q&&+#Hdglhpvng%lxw+j{pMKsF zGd@7|4L&ciG4-mS_g;x>b5g}!J*VQmwyF5aJ?e!sOVo=c%T+?fR!LkiUnLbSmY34f zRR8(!$$%jr$-vYJGU(}4@g&5_%l=6+I4o2K-;9uC-+n6jyJj-<MmsfZ?Qb%?#z&1X zP1VTKD{9o7>uPj%r5ZE3Mva|)NnYu_PK_H`A>&&uR1>00B&E?Bnb=Zf;)NWURG%)B z_hiYdCp<D`*>FiMpQc{>Fj~?I2dUR5wUVjxx~ORbda3jjfAvOWkeVKQTfN!Tr7}AD zO2)O*DznK=$vm`EWnDccGgcO=nU!TSYvFd8U6L<vXBEht1#{#f_k-SE?!Ru{^!lgw z8+bqb-`@A{|9;p0!Cg(1y8*fyxm<zT-I#A&O`WsvpXY=`kE_ohk1G!Q+Cxk%k3G2D zZnu4$*WGT<1GMG7y@pizG(Z~TXgh<n2I&pb9Hcu)dyxJh4MIAEv<T@D(j=ryNShpO zpO8j5+D;*@LVATX3+WcpE~H;b!;p?4Ekk;SG!5w*(l(@TNaGxB=aAMRy+fLZbPs7C z(m$kuNC%M?B0WT!h;$KYBhp7l+eoC7j<%IZFOg;<-9*}n^b=_)(osj-QlzIyQ<1JB zZFRJLb+nB|I_qd#i}cpfHW%rxqirwJU!=iEhmjT|Jw}?0bQx(g(r2X6NT-ojBfUnN z%}<<eJKA<5{YDy&bR20p(sQKgNY|0JBYj62k8~bsJ<@xm`AGMX_B-1CBO8G10I~(h z9w3{5>;ke4$UYz&f$Rjb706y7n}O^GvK@|gKadS^v^#=q39={1rXah5Yzwk4$i^T$ zgKQ15H^}B7yMt^GvOmZMIocgUw#d=$5wc0hE+N~5>=Uw4$W9?!h3plwS;%f7+lA~G zvSE&P$B->^w0nkZ8nSE1wjuk5Y#g$4$kriyhio3Qd&u@7`-f~GvV+JLI@&!%HWArH zWE+uvL^cxHNn|ULy+k$>*-d0Sk^MwA6xmT^OC9Z=BAe=HcNN)IWM7euMRpe1T4ZnW g|IPjP&GoTc+#!-N4omD5-X%ODEHN?yJ9hH<16u-G00000 diff --git a/telegramer/include/pytz/zoneinfo/Europe/Guernsey b/telegramer/include/pytz/zoneinfo/Europe/Guernsey deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/Europe/Helsinki b/telegramer/include/pytz/zoneinfo/Europe/Helsinki deleted file mode 100644 index b4f8f9cbb57450549933f83ac90dd56a2ca75344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmdVaYiP}J9LMqh%+NyL(Hv|u%xpNev#Z<YHfM9$FguvbY}n>D!>~5Db3~GszG{&W zvX;c`!B9r-BI~5Igq9-LB!!R`zxUr0<&h`KIi3IOv`%~UeSbXjSCl4Nf4n-GzwqHz zX+C@p@tH^6`ZZzq{JBLfS6>u`Mz#5R_4NB3fmeKvkBz?G&(CU~2gkJUjeQz+>9T~M zZjgw>N2OnlO5~R9(!Z=i1}t1E1G7C6mFAW~&QysGkCDM$drM4EhQ@qO*4P)(I;6Fi z4!zY`hc$gwXWbheUi(<%cHYzY4VTnad`1%r9!X+FlO&}#OY*G!k`i%5QWL8rwcRTt z!)kS8+hQ5@y;4VC&X6%r@-?l#P}7@7>)2frbljnE9bX!y6LyZ0iJ3u~Q5+_dqF<>y zqg^tC?rK)lQ^|V&Ql<o6lPUf?GWGchnbvShvRkfb&fXfCe)_o1C@+_pH9ItS?jD_0 zR-$<$%G8scrL!H=b&hk0&iUff{LoCvf7nCkeU6p+=RfI!)?it9EJO;L-pL~GM=7lJ zOHpB~EZ+K7myEk0OAA`GIP##Bq&H}3mvg!-LUq~e1G>DuLRZ|W)|G7@U3GGSmfc<_ zt9Pesd3~O&SstltccsX>+%%~ub;$aJezL*+O*V#DQW+nrl^>o-RrfDib^oSRzkj5g z8tY}Vzgf2&ysldtj_9`PI`!`LYCvEI``t0<U%oBNQDP2?XGhB#>I&#$S>gSyZohxe z&hc22&ByJ|<Kf}=RzSe7r{^zD_lJ4qT^xJ}Ibr0CkyGYBa?Z#}BWG=EP8&II<iwFP zM@}6%cjV-evqw%JIe#PpBm*P`BnKo3Bnu=BTayQp2$Bhs3X%(w43Z6!4w4U&5Rws+ z5|R^=6p|H^maWMPNzB${hNOn%h9rk%hopz(ha`w(h@^<*h$M+*iKL0-i6m-kGDT9g zHMt_mBH1G8BKaZ-BN-zpBRL~UBUvM9BY7i<+nUUg)NM`fNb*SbNcu?r$OIrWfJ^~0 z2goEKvw%zkG7rc^ATxnXg{_$jWHON1K&Atk4`f1+89}B5nG<AEkXb>d1(_FQVvw0Z zrpDIH4Kg{lW_FP2LFNaUAY_J+DMIE5|KmvtHXAiOk+pK>B*mq~x#E+YISDTNTXOJE D35>6g diff --git a/telegramer/include/pytz/zoneinfo/Europe/Isle_of_Man b/telegramer/include/pytz/zoneinfo/Europe/Isle_of_Man deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/Europe/Istanbul b/telegramer/include/pytz/zoneinfo/Europe/Istanbul deleted file mode 100644 index 508446bb6aee2841ab88e82607a6ad6e748e7db7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1947 zcmd_qZ%9>V0LSs?`nP+{bz_9Pc+<9~_m<WCx4Je{%S>!HcdDyFf5t&8ib5V%gGPe4 zDT7wf8GBJA#8%G0%Gwr#Q7fV`boS>^w&_xK##j)+d<I!_QucgLuUc=`>z&KFpX24? zi_7!<#nx`!TqvHq()1_XTs!s6b0DtYtbL=0+9zk2?YA!2M>jP($98{cbkxSpFUrr_ zojD=rc&Nqh3Wv?E-yYc~#`ZcVFBcd+d&-?&&&x*d@=^20YjLBmuiLz_&1>8|T4MHB zCK$J(>CR8HcH2MajW`3|4fbHdL38kKx&6z%ubtaB)*D0pwmJM&neo^CZ_WD+BgW{K z6!SrL#7=4YRHl|3mZ{_K3vZxVdhI^p8+%u#efc;(d9XsJchtpS>O3W<v@M8F`FpAn z*n36>hR!&dcWf2>VMI-DFOXULcc|><jcUgBI`!)MW$Lx)0yVQVtX`iJP&q-L%1s+r zq2!PZjlLqoe|Y7r{(t1`@9)XHvqN%D$5lCZU#ptec3Qr%^D~*h@e^6F;k+t*@1QDL zbVL=e`ar!oJ+9^#u2b_LHK<7DE*TkKs!GP!%7x$Ms<&>J%F@nCx#(P$Ec+-&mbVL8 zu{l*PZVRd<Rd;1&<72fnr%zT@I%-*RuT?$sSAX@Ncl<Spm;5!?di=|OKjweCyUQQF za?x6GVA!fX`MLE@Q<LA=-)gN~^RcyR`zC93<bppIt+whiYOVURBUb&xd~411c}B~` z#Dpg?;YD6a$w`SRssH^o$@|<ZDM^UTus%j2bBoZ=v-3TZgb2<M={$?6LPRP&)BgSW zv)_rXh>03~S5>X%Ehh5vi}YR0f7O>qUr(RhKSb;W4!HhJChEWG89)v?a@bw<fkzHK za`2JEj|6~3fP{d=;HrZ_qCmnx;y?m%)sY~fAh969AkiS<An_mpArT=VAu+k?ppdAL zu#mWrz>vt0(2&@W;P_pM4hhdy$Hzs0t~x?4LPTOjf<&T3!bIY9)qx_By6RAoSdn0n zXpwM{c#(jSh>?(yn3157sFARdxLtMNNaRT9NbE@PNc2efNc_kEAR~Yb0Wt>2ARwcF z3<EL_u6iJlk#N;Rfs6$*7|3WK!-0$kG9bu^AVY$T2{I_is360Fj0-X_$jG?rp+UyR zRSym_I>_)K<AV$kGD64@A!CFL5;982Fd^fF3=}d_u6n4DvBDO82LBg><tx>LRyMcD F_XJx;ZRG#} diff --git a/telegramer/include/pytz/zoneinfo/Europe/Jersey b/telegramer/include/pytz/zoneinfo/Europe/Jersey deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/Europe/Kaliningrad b/telegramer/include/pytz/zoneinfo/Europe/Kaliningrad deleted file mode 100644 index cc99beabe4ffc5107c4719d1201d6583b9ead03a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 zcmds$TS${(9Ebn&l-kr$=sahdhn$wpL#wo`In5JTu8U6Kg-AtETF{Neiwuc~S_kPu z%MdYw6rux^30-s%WabJXTY(S}cv0U?L`Bs5d}<MNQMdg*_C6oq24n1byu0?dIpvRw zn}6D*$$h5MN3Yf1(mVCi=$jt#UmCpazkJv4@AqBwKZ<JikH4$8J-KH0PYk$iPtTRu zCcCn2fsO=Qp!3*FpvTwp?BsCM^PP8Q;&ZP~CM3S9v_}S;>|f#@B+kZ8CI#J<$>ZlI zQ|@0eQm>yi()zlL^ixNSWu2`?#=#xN@;%i?X2V(|t31zGQIKS0rzS~`HCl4ROI7a2 z9F_Ohu2xP(s{DtamE+c|Di{o^!VBYSm2XU~K02a`TKm<Sy^qD&F(kzm!&0*Sgp{V8 zkuv82Df`wgu9R-&dfhDRzV1`&$4X^Guuhc^ZBrGa1!`k=vD(xdt18>#)n=bVws<}% zcWaDPWlt-Q`-4=6kE)uifYiKyA+@15rFP<;)V=AE`jI|q7{8$!PahX=gX3_BB_uTL z=M@ni5xHpYii)1U7F+-Rim{3;b?LuEQZp=JBC$4!`u#q?$P&X7St8=9v?Pe+7fJHM zx$xxIXY{-2bDwW$^orZl%;6OeHy(Y{*j%Mw_2MWh=4&mMxznQO{RfS{>m3@{y%nO9 zNv#w_D~MVUy&#G~G=r!H(G8*;L_3Ij5dBPQ1x;!VAu2+2<X501L`#U8CbgarMIo9( zRE6jYQ5K>tL|ur!5QQNcLsW+73{e`QHA8KN-X^u;5X~W~Lv)8I578c?K16>=0+0+K zDKK(iB*Dmnkp?3VlR6PbCMI<%j9eheFtUN91IY)H5F{f=N|2lwNkOt=qy@=~kr*U1 elR7m<ZYFhdjO-ZcG4g{X$o}&Tqjjc*&gdTt^LtAG diff --git a/telegramer/include/pytz/zoneinfo/Europe/Kiev b/telegramer/include/pytz/zoneinfo/Europe/Kiev deleted file mode 100644 index 52efea88065b220e44fd876de3bf3090fe62cc79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2120 zcmeIye@xVM9LMqR1=MvKzHQNY0YZ|7cESOoXpq=>FoQFdlSn1BPShe&GzO*dT{+hp zxwn(mT)M0nnscl_(EcFTkGZlsZubvsja;o(HrCvCtLDtH`aHkw=9+)C^=F^&zTe09 zjxqk>^^9%U(Oz!daAufqxH-M%=6#NB9;X5);%5c}o4*(<NhK~d$k@AnO}-tL<k-ob zIrpAO-8t)<TEBN<jsK4sAI+M~P6Z~ywRa^Br*c0y?&O_5<m4ZH(V5-9+bP%^cM3b! zJ9lrYbLKQIaf+&ooqK`-XKtR)nK$j4Gw;>H?}~r)+w;f0_JZ?Q?fXV1?cm^qy>K95 zm+U`hm+l$1L-9B5veqxH@~#o7s2P<->t2(}{A04Xyhj#K^+{FkfL2}HB1^Az>#}oU zxqqTXtB2aOW^|c8aA1Wle<M$8+lzF?{-yF@U6zL9xw3NZC5?oqq%P}otuOjk>M#E! zt1>>8RmltT&=2oO!|*9-Onj)(gZ;Al^jrFHXQ!;`e@WLq@`|qO*s4uSIyBZ;uj~D* zwK+Ino3DhlC8thWzPeo>{k>Q=oc&EVj{9WO@tM*(@{2sy_p7uGBxQ5kxNO-ssgEyz zU!G`9XuRNvY^@pAcJCS8man?)=ht=nRG03!(5p{gjOkM+H)_YHyX5JYYqWEyNuJqW zpgRv#%C5E5($(&l-DR_7PvojRJJXWx(j4t}zL%ctE83GfFTIz)*5}?ElIN47y7$N@ zx^MWHzR)|UeFyv0b5q96nOT_`xBPv1Z@qr`rcF;@w`Kq5mu2N0HUD2cGtINm@><q{ zkTu=1vc(d<@!XMqR@Hg}>7S<GJ^9yv>!0t9(U{d>?noqNMVlI|urVBI;=@YwrFr3a zJnro=$Nijscs#LxF_AybX+HZ|9GMC-7i2QXY>??7^Kms3;;%9zWJ<`KT+O7ASs~Lx z=7mfQnHe%QWNyghkl7*AL*|D}5Sbw|MP!byW|GJ(k!d3HL?(*N6qzbAS7frtY?0}5 zm@hJ6WX8ypkvY4XNh7m%HPc4sjZ7SwIWl!*?#Sek*(1|O=8q%*$pDf9BnL<mkSw^G zG$46!HHknnfusV-1(FOT8%R2kd>{!yGJ>Q8$qAAaBrC2aEl6HmO=6JDAgMuegCqyZ z4w4=uKS+X*3?V5(a)cxa$r6$#Bu}m;QAnncR3W)Sl7(aoNf(kYBw<L#kdz@gLz0GM z4M`i4H&>H5By+ANbx7`z<RRHZ(ud>^Ng)28L65274H@*Ael9Ghm%(U7No7f>v?3TS K3%PHLe18GfyY{F6 diff --git a/telegramer/include/pytz/zoneinfo/Europe/Kirov b/telegramer/include/pytz/zoneinfo/Europe/Kirov deleted file mode 100644 index a3b5320a0bd139c07b8642c4efd7b98f57c6e8dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1153 zcmd7QJ7`l;9LMn!oAj2%!9^R}q&{jI`t+JKCDpV>(>AoGP$W21h2o<i2>y`{3PKCk zPAVvZiXw`^#pj@tRf4#PZ6~)X9B}f1oW;eW@qDj#au7G~&Ap#+Ng(9+Esh=;PpChR z8vBHcBWo|-ueznWr=FBTBdg<A%WJ|5Zf(tcw)UIvQTU@OuU$;Jb#rIR^|Kds<lbo= zy*{BE&K}n5PVLd_kLLA;eQCY1(5xFXb$U}<NXH^U(-c}V@jzVSU&GS;=JQy~%TLn! z_^q^+=B548bLp6VD4Qp4ihJmSY}r3+62~u_WX}!Lx%;F^MW;+xVz24?e!!&bPe}S* zpXpv2m95X3%(lghWbWjo=V^oV&ctN<H6__`M|Mm{%+CG~k~{dr^u=CFe@>f$z<tR# zzA*VOugqXo$qX*sHAC-b%&rILOkwV}44*${iiPOdWTjH^Rjv4S`UAhuYNy)qbNl=0 zcO0cUuGs%kwYbW!)WC?({;TP%TDg2*e&VxF_)KBAs9N2my;An-RLW;x_CSu}KWt}z zeue#z4f#GhB3mMRdfH8qU6E~(eUXikosq4Py^+n4-I48){gDQc4v-d*9*`zHZ5K!z zNFPWeNGC`uNH0h;NH<73NIytJNJmIZNKc-&DWoe;+ZNIn(iqYi(i+km(j3wq(jL+u w(jd|y(jw9$(j?NQr)?AI)6+JJbc(c!^olf#bc>UA^Z%{gV8)i++nx;m1PZbYN&o-= diff --git a/telegramer/include/pytz/zoneinfo/Europe/Lisbon b/telegramer/include/pytz/zoneinfo/Europe/Lisbon deleted file mode 100644 index 55f01930ba92ff6852ae4745e78adb5f96c5b057..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3497 zcmeI!cXW++9LMpSM$F)cN)Y2ARidd3Gb&ZmL{+ThB1XiB2!cn9hSH>!s*;oQqiC&q zM2wS0tXL)XiV>?RF>k3A>roWdH2u9Fe^gKZsGigQ*C!|UbzQ+<KJTx+_s|%B+g~16 z^$FiR4)x7@k0kXn>so5kf{r8f1-~6gTDb6zz9@H!{&ix!zBpr}X++ueB{K^3w6+EM z(vhR|bWc}vS(h|3!{wfy@i5g~mAk^sTH$G~HoE9*K65eGem>k>r`OZh$4}F<yOz*5 z`G@M8D<0H07jMzG+{@6n9o%H*Y+h+@UmT_H_-eYjbG~WrN{BOe$3~iaB08IU+qmfa zTKby%Tg^2OL}cm*3NGonPCk0>eN8`fd7ycCLzsE^_EN`@q>hdw=hMuibDEk*_jK%^ z7oX<H+qXkMHqOmFmN~?6JS^F9eAyEHg#C$mBBg`lq<@U#<fl{gQ@!?@-*wnv<_Db6 zzW3U#ovyxKJ5wfIJM(moR&aBwcDCRH?OblGc797Q?ZUcb<6_!4<I=1c!|_Q^<A?b7 zjLQQ9j4NS{j33*17+3vWjcav_jO!j=+KqBGv>VULX*Uah)e6tv(QX~OsNMedn06;) zr*?P#TJ7HSG|im&g?4{fvi8%U)y9Kx!+01p(|FY66XR#kF~;L6(Z&<!?#7b`A%^QW zjcnx?c-Sh;aJ5xTZjmI$HcYA%T|KF?y_&2N^epMsCcnt4p0}i1mGjcw`KWY%uw7QW zl4DdqWyl(PW=fB3IihA}mZ-IGv8bIQMV(0>3oR~Q)a^S+)C;u>&(^`>HSgx4e$58r zb=PX5flH8V@TjS5c(uOt%6F5E_LY)vWIvJK={Mz@v(L)L$%m!S*lp4`dX@BxED-+j z*`i6{R?#$Uu4q<kk!bFpAet8?ik2R;Wy=c#M8LE0vQ<uyc<Wq*Y`r{625xO7-=5l5 zwpmb9z7yjq+a?Eyb|Ee@DB453TmQNY4k{8MF1fP3=Oxkp?lsZDd6(#rcTl{4WvS?x zl_5fp?2w&OQ$**?C9=!Nk)mtL6xprkbQu;mScbQVllIW|vb%d{*~8mQ_IT_oBdUgo zh|`s2uSX3<?~M;-pZv0-uTe=vX5SY55+8`D*?FRWRK6H6xlj&lvq=n!JR+m(%oBqH zvt&%^4RS~=DTmznQpOg=%b^D+$YB@ka`?(VGH(B9F=ASv9JxGPjEb!z^{LIo=x(h= ze2lvo<5ydZ4Sp)dRkDfkK2_y-^Rh^&@K`1s-zz5EEszt}EEgZ<ZIzSe?Us|X7Riq$ zq|3zAL}@GO^onzdm;7>3KlkGIOa8f1fA=qxE@OR_E&1<%6&F7qq$b8zK{%B_TbVz< zuc<ke|KoF8Wqy}e^|{-NKN;G|ZtEEw&I{k_S3&Ac5TB^WNj=YBtiS8|d;33_!D99F z3v{i8%po#~mTDG}X+-7`nMh<Nk*P%H5}8b7Hj(K><`bDvOEsg&lp=GAOe!*~$h0E! zicBmrv&hsUb8D$47nxmTdXf1>CK#DvWQvhFMkX1VWn`L>c}6B0nQ2Qk)yQ01s>w!X z8<}oPHQ&gDTdEmHrrc7^IWp;%YSxiyw^Z|vOuVI<d1UG>)!ZYKZ>eS<nSNyckpv(a zKvIC@z)~dv$pVrFBo9a;kW3({Kyra31IY%G4kRB)LXeCgDM50AB*ju?1xX8%7bG!A zW{}h%xj~YHWCuwPk{={NNQRIUAvr>lgk;H5rHTJu<cWtw@sKGVQibFSNfweVBwa|p zkc1%_LsDj`a)u-g$r_S2ByULKkjx>eLvn{C56K>qJ|urg0+9?NDMWH;sgj6f5lJJG zM<kI*CXrMkxkQqQWD`jzl20U|NJf#AB005GNky`1snUw%6-g|TStPYcZjt07*+tTe z<QGXWl3^spNRE*tBU!doX-4vFsS=H38c8*hYb4o7wvlur`9>0sWE@F3l5-^KNY*V? z+L63ls>CCiM^caE9!WlueI)%z{*e;^IRlVW067PclK?pjkkbG;4=mM*fSd`)seqgd z$jN}54an($oDaweft(S@DS@05$Vq{m7079UoEMhr#6ZprOLb}>=LT|eAZG`1dLZWq na)RLhXNYxf{8wj4S#`1aa=LhTYSOq_V_zRX?@oTcyes=VR=Xml diff --git a/telegramer/include/pytz/zoneinfo/Europe/Ljubljana b/telegramer/include/pytz/zoneinfo/Europe/Ljubljana deleted file mode 100644 index 27de456f16ab549627b284a39e2265cbdb4ad8e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmdVaYfQ~?9LMqhL0H4S7@-o9T#oJ~DdMPHDwhrt$t4vMLdq?-nOU<hYs@g$HH>%= z9>mObA-Sy?W{kPcS{sI0<M;k=Hk(JD*!)lDyjrdG<oo`(Gv?)lS${mO%ujgptT1oB zZ@bQX+-w&4y!OplxqZw_>khf(&W;GVyCFdC9W0aksqxz7<tgp@;DC0!vR%E;Ul5-Y zmEya1zjQBC@msxKdgK>M&*^idSF&6DV-uveGfDz{0;NxzE)wYB(!kFV+V@p}_N(u# z{jass0aahsdE}iAEPt#)n{H|Fvhx~}eNsa+A4ynYm4wGtOT@&T66w27qQZ(Ls;N|> zy~{QF=`0!iy+~s&xMawabd9aZ(zxmv9lCkA4%_3S@j3oFeA8eVk?5hWY;PGE@J16{ zO_JzwLzDcUNm9dW8QuJnjIOJZF)t6x*vjLQTzgSdwv|chiGw<>pg_i#ZPW=<w(7+E zxtca8U){+`I>{?lCp-J;<S!wb-YHSiA9m2GpZiM2*-tvH-czO@XfK&nA7n=9N69L$ zlbKodGHcCyojvTF%*m|PY`@(yH?C51TA$HeU)9{VyELz<Q0LW@==`T{U2t@o=3ieT z3%A5+K}DJ@%Jb93n<Hh(gjgxe@sg#X-DO$AH(B1^lA_>FTGaSLiranB;=5O+q~VdS zJY6BH>Z*11?#sHSa-Xg(IijW8O4ZS#S#$g4(ehuuEURO*xhkujSS@~i`t)$LwyfVj z`E7EF+j1rFPIH?-a5(tlaX8$6al=2%Gb6Tf6mrYRJtH@b+%<CB$bBO>j@&u_nOjHh z9l3er?vdL^?jLCY=>TZ~=>cg1=>lm3=>us5>BQEwg7ktkgLH$mgY<(mgmi?og!F_o zg>;3qh4h6qhID3YT0?rXHO(R2A?+dkAq^rOA}u04B26M)B5fjlB8?)QBCR65+L~sO zZf#AwNWVzKNXJOaNY6;qNY_Z)NZ&}~NaslFNbk0$d8B(=(>~HavH{2rAX|X!0kR3m zE+E^0>;tk9$W9<zf$Rmc8OUzfn(aXL1KAK{N02Q+_5|4!WLJ=FLG}gN7-VOVtwHt% z*&JkdY|Zu{`(tZ12-zWIi;z7+HVN4!WSj7R-zQJATLVKGE@w)3P-IYuGbJ<xgTp<4 E0<)*Xw*UYD diff --git a/telegramer/include/pytz/zoneinfo/Europe/London b/telegramer/include/pytz/zoneinfo/Europe/London deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/Europe/Luxembourg b/telegramer/include/pytz/zoneinfo/Europe/Luxembourg deleted file mode 100644 index c4ca733f5345df24e5286b70464a6c0498353372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2946 zcmeIze{{`t9LMo{o5dFMHf*xtTWbx?es3Xbm~mwD^TzU9X2{}N+S!EVv?H?Pymg$U z(XN>dF{=61hA_;DrI=7o4o6BI-IAjp$I<8c)sNH3KlM-l^!=Xi_whaV$Nt)VzFecH zXU94Ib_AGLxHxv2i|3%CvMseG9g8QHG@kRWzFK)<P5CPKo4Zv@i&kpsg*vxq;(G7e z1(!W#QGwodbEivr;zIBG@hMsnp6IQN*yPyI)#=^X^bOf~Ey2C%%1p;wXUg20j|_Ee zIa=whs*H1N^$c-ud%2C{?Ikn3@3>=Ld^dNoyLxDpXZyg>){YqQ?2OCv>}vVFXV>jZ zp55P`^1OF?sa134JFB+-qP1t&No#NE63@QZw_ER*gjoCYS6T<AEw&D37FcyD(Vj!y z$5@AZ?zfI)p7tC$=jW;K8)F^2GDwbZ4AzFSRQd4L1UWHln|zcTBp(m%ttXfFmQ#bm zv_<)34OrS<S{9|L%t_K#SrHoOYNxF;`bnGkpS5k+*BaFNGY$4RuEE#$YP(BY_5PC{ zZGUjNhSctn(9KoSVRfl=ELQ0>Z=qN@x$?l+$?{;jOTv1k$wQGz5*`{M4>xZo5x%J! zaU(%HUk=wUjX~P=P*Z)R`bUi{|4O^9IHyrXA87QPT8+utqOs%7N?dNW#3$EC!tmE5 zvBMfkip!UzTZPg+WQBG=KS_H0lBYfQq{?HTW@@jB37TBfQy*W{M^j2dwRd)y_9^Ni zef#@rYF3E!3%{aiskfxR?@=8P_PGqW_Js`e*)Ibd>g9<`>ts;XCP_cGPcxPk%izsx zb;!(_GPL+59X9F}9iB5qM?9LNuJi%=Wbj}e8QDcg-i*=Aw*4jZlR$myMrRql;|Cqn z=r3bEtz=yFH!{BPx=dKnAQLAv%B1;M_34zIGI`uF&FZvDrX*KscC+m|wS(%^FIVcc zTe&*DzCfQj@6s6?#%Rvr+4Ah-WSv<tLS{|tr0zwDGJ9Ar$;}Ry=VCj`oV1_h`Bo0e zi*BoV-iwkSa8vV7)Jeg$vpRQMg}l&Eqw`iB(D_wsbU{J67A`B)dz|L<X>zac-Oi@X z?snei=kMolzT?xRd5iz*bU1wL_>G2I&L-wpDh|KDJ_m@i1@Aiof4|>(#eCAdV!mbG z{p0@IUr5hzIa1R`aC13E@i59fn8WGhGIt*SJe*F~KZwX}a}W|FCblLhNK}xpAaOwg zV{0OVga(NX5*#EtNO+L=AOS)mgoFr*k*x_55+x)|NSu&BA(28tg~SR877{HaTu8i- zfFTh>LWac5)&vcS8WJ`nZb;ye$RVLaVuu6|i5?O@Bz{N$kq9CoL}F-bf`~*B2_q6m zB#=lXkx(MBM1qM#6A35(AH)+0C=yX5q)1GWpdwL4!ivNd2`my>TN7F&wn%W1=px}o z;)?_ri7*mkB*sXPktic!M&gVF8i_O#YFiU)B-lu_k#HmNMgopR90@rRb0p|U)RC|w zaYq7=L>>vft%*Gnd|MNJB>YJHkpVzP02u;g43I%UMgbWHWE_xzKt=)?3S=yh!LT)> zfeeSO84qMYkP$(K1Q`=#P>@kUh6NcHWMGhyL52ny8)R^7&FCP*V{66-86aeYkRd|G z2pJ?~l#pRU#t9iHWTcRxLdFUiEM&Bh;j%U3g$x)nV#tsoV}=YGGHS@MA>)P&95Qmq z&>>@o3?4Fi$ne>k@k0jC){G!Bgvb~ogNTeGGK|PL;{OAAXD0vkK>C|0?H0?ZMrOoE PB}T<WX2ix|biDts=f9={ diff --git a/telegramer/include/pytz/zoneinfo/Europe/Madrid b/telegramer/include/pytz/zoneinfo/Europe/Madrid deleted file mode 100644 index 16f6420ab7efc7ceac3b0e42fe37836185cfc463..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2614 zcmeIzZA?{l9Ki8|NFqM{KoRSrXhdl7jF6awhF~W6cvBLIt0HP4U5T9d1Wu;r82i<% z#>&KG*hs+&a|^8{kTQWRWsS;WxiZV_Hf8q2=>I*vY2}+<^`>)n?&q9yxBKF5zwghv zbZvf|^^aqSdBer=s=2s#l$noL-f8vhuTHY6)!{j?y`Zel=SO$l<wEO7+aXW=p*X9d zdY=2}u2I$-&up^ZbjOsvRkYjPI6t85RNGZgb5@Sk@=%JWWu2p^HL2KjdO~*D>E24u z+1@W)?a#0GbTrR#byjz|KRg)eI#+hw{n3sQu8&<+?(-{ta$T6d+1))Zy{G$1w#Ro^ zN00A~=iGyTXz1~~Q0yMk6x=iPb%$qIO_FQ)o<7fr@-&s=MD@=L*8t}j9ho^(M#cZC zfuY~)=#igmkavd$U4K`{Tx!tU&sXam&Cjai#7PN0TrXo^-Y?@SR6@3Fm+=KfGGW<D zxiiBlq0`dju82el3l5jNhmMhOpHvO+ovahT4%10pqxGKFL3(fF&l*v4MI-lqs!`=< zHM;nO#^fE**qm;OD{7SZ<R+QC@C8X2yH671HcDb&sZ4R~)hQR3OVV!}bn3}ex&N~) zomQKx$xT!Bft@on<)vVqo*$|+%9G^5bRSL4bI8oF%bJ$jC+R+IIxF-`nRV?OneF|) z%<eoR4_`VcbL#6Pqy0V2tgMi^hY#qy!a|u}u|pRu*`o^!R_UVq3e=e~OCJfEtBWHh z>EfF)niZHXS)T;xqrDSl>DxCnyUS0ORr^a$<5hXA^t$Bk?G#sTmn`4<ovuhZE-Q1| zH7}$}RwdVKzV9(zJyvz~w=e3Nz9L<FrbHjV=+q}(%hrMq*2}ux$y!*uNS<61qVAmu zvVOrdDasF$r((xRaoR8Pw7(@Aq62k<=PTJb{HAU^*CHj?x^>ghTG`y$q+6;^>DKyv zx~-%}ODjutpx1!Eo!(vpZu7atImmag-+yuT_y1mDhQ%5#UIWn@Y+1qMy@vheK7enn zAp89-?lUr-){?YEd~lhkRGw1JlVy4FJ6`6nfA7x+=f4=_esgR~JZ2#SjSMw1*vN1r z18!@E92s<E*pY!ph8`JwWcZN)AQ3=9fW!a^f~|=H5(Xp=wk8lrB#=-bu|R@>L<0#2 z5)ULGNJNm3AThBuLE#@HDz+vpNL-M>Adx{rgTw|24iX(CJV<<y03i`VLWIN!2@(<| zTN5TEPDr4TNFkv@Vub_?i53zrBwk3skcc56Lt=&m4T%~OHd_-nBydRNkkBEqLxP7y z4+$R<KO}%i1d$LTF+_rhL=g$2t%)NNNLv$0B$P-jkzgXxM8b*06A361Q6!{DOp%}> zQANUv#1#pwt%)oWT3ZuaB)CX)k?<n%MFNaO7zr^FV<gB(l#wtaaYh1dYa)$=+SbGx z2{saKB-}{6k$@u+M?#Lo90@uSbtLRa+>yW|kw-#rYhsTCABjE^ekA_L0zj4kvIvl6 zfGh-LDIkjhSq{j8K$ZluC~VELKo*9rSsKXVK$ZuxK#(PZED~gy;QtH7{0X;OD1K(w XM8>jpA~NHn5~5-vGGk*fI^ORO@U!br diff --git a/telegramer/include/pytz/zoneinfo/Europe/Malta b/telegramer/include/pytz/zoneinfo/Europe/Malta deleted file mode 100644 index bf2452da40314be196f61e6a7cdd48eaf5c426f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2620 zcmd_rZA_JQ7{~F$lLDz-_yDE{wa^en9z^gV(Uj1c2TM7bsVFL<7SSykL8%<eTs7vd zmf{$g_z=sc)D(4v)-q`{kr2bwaygw@+3eoTkSW^#dpqq#uX@v)?laEkaFmzV_vf9x zEO)r;A5UlV4L>|P%@3cu`pjF!sOwfmYvZ}f`lI&d1Fr1%-nwONsdAsI%6{M8x_Wo^ zwz(s%?Vdi_4SC6S<E5!`Y-fZvZA+7H^t?9b&(q@t;nL!1u}_Rk)NiM>NNZ%9buwm? zee$wTPlc38d(u9;{q&J@H{31OjZbRrn>TB%j`A}5*2QM~_G^1BSN$H_Z{bGEzjC$} zF!5z8@Qp${Xz06kr#?wm=g@&xa74Hjd}f3d($-T$4|nz5ck)l|vh{ag*Zrro+nVFP z`^!F6S+Lg^R#>UsCv5cfNS`A;hwO3nin^@fJ$}%LfMXhQ^)u~#;Uj(EM3wgW@*VAa z@Q}E7)ktLJ7U@^2(tqU|8IV^X56)dA4|%*2H8x!a#-_+1ceFg*rME-}rD^oF5jyzd zAdP7a*CF+tb!hE%jotB!4%=`><JKP4_!S2=A!oNHW}TMd1+|itS|=lBlu2^rW=R=d zBq<#wGOF(e9d&krjJ{E*V-BUsBj3-`vDNc6wQh`#TQ@<+SGe`j+$f!}cC<{K6r^c6 zedV!1moz=CLnZ}%t&^jEl*w0qk|_aS$dslAdHlk5nOgIqcp5*~jP<25ZP!+PB0pcA zEPY+4KmE4O$Xl$LBlFbjnXFGmOw*aMF*>s?L1*=vB(uKjuFqT>EVK9jp>tY8Wo}iN zWYzvA^GdGD{0&W#J-<~Jtol_Kjz1uavKlp~f4MA9t<l`zeYzx4b;-}0bZJL{E^8>( zXU}@|xew-O-dD@z`8QKFzdBQ1SlVB$b;+`P`dBH*jgS`;`^k#*c6lkxC57?5w9q~; zMcvx8sQHK#UpcL>d|WNBHr46M^255SX0xs?-k~MyOEkdG|K2Mg(7gQoc{i`S-ucb> zmwW%yKd94x{WAAdY3|A89^e<~a&;3|$ldol-~9c(C&TMXOV8xZ%U}4J2h9iXzqsDp z=CKZ)$U&~y(Ofce&B#R~SB+dYa^1*<BUg@GI&$sE#UodbTt0IBNCA)vASFO*fD{3# z0#XJ?QwO9FNF^LiDUez?nqnZ;K+1tmJ@`ODcuYlrk{~rfih@)HDGO2;q%cTj98GDE z+91V2s)LjVsSi>hq(VrEkQyOHLaKz6iHABNg+eOjXiA0D3Mm#+Eu>sXy^w+-6+=pf z)C?&aQZ=M(NZpXaA(cZ)=V)q&6c4E$Qa+@9NCA-wA|*s>h!hd2B2q@Ajz}SqN+P9n zG_^#E>1e8nloP2ZQc$F#NJ){JB1J{2ij)<pD^ggbvPfx>+9JhuG}T4Q>uBnW6d0*6 zQevdWNRg2$BV|VFj1(HFG*W7$)=04(O|_A7JDPeU1xG55lpLu!Qgo#1NZFCPBZWsQ zkCYy%JyLw6`bha5P5qGtKvn=*0%Q%4ML<>oSq5YskcB{20$B=VEs(`PRs&fMN3$Nt wf;gHLL6!tr6J$}4RY8^oSr`2OElj8xoneX0Pi#g~Tyk7OY(`=N#wUgT1<GOd9smFU diff --git a/telegramer/include/pytz/zoneinfo/Europe/Mariehamn b/telegramer/include/pytz/zoneinfo/Europe/Mariehamn deleted file mode 100644 index b4f8f9cbb57450549933f83ac90dd56a2ca75344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmdVaYiP}J9LMqh%+NyL(Hv|u%xpNev#Z<YHfM9$FguvbY}n>D!>~5Db3~GszG{&W zvX;c`!B9r-BI~5Igq9-LB!!R`zxUr0<&h`KIi3IOv`%~UeSbXjSCl4Nf4n-GzwqHz zX+C@p@tH^6`ZZzq{JBLfS6>u`Mz#5R_4NB3fmeKvkBz?G&(CU~2gkJUjeQz+>9T~M zZjgw>N2OnlO5~R9(!Z=i1}t1E1G7C6mFAW~&QysGkCDM$drM4EhQ@qO*4P)(I;6Fi z4!zY`hc$gwXWbheUi(<%cHYzY4VTnad`1%r9!X+FlO&}#OY*G!k`i%5QWL8rwcRTt z!)kS8+hQ5@y;4VC&X6%r@-?l#P}7@7>)2frbljnE9bX!y6LyZ0iJ3u~Q5+_dqF<>y zqg^tC?rK)lQ^|V&Ql<o6lPUf?GWGchnbvShvRkfb&fXfCe)_o1C@+_pH9ItS?jD_0 zR-$<$%G8scrL!H=b&hk0&iUff{LoCvf7nCkeU6p+=RfI!)?it9EJO;L-pL~GM=7lJ zOHpB~EZ+K7myEk0OAA`GIP##Bq&H}3mvg!-LUq~e1G>DuLRZ|W)|G7@U3GGSmfc<_ zt9Pesd3~O&SstltccsX>+%%~ub;$aJezL*+O*V#DQW+nrl^>o-RrfDib^oSRzkj5g z8tY}Vzgf2&ysldtj_9`PI`!`LYCvEI``t0<U%oBNQDP2?XGhB#>I&#$S>gSyZohxe z&hc22&ByJ|<Kf}=RzSe7r{^zD_lJ4qT^xJ}Ibr0CkyGYBa?Z#}BWG=EP8&II<iwFP zM@}6%cjV-evqw%JIe#PpBm*P`BnKo3Bnu=BTayQp2$Bhs3X%(w43Z6!4w4U&5Rws+ z5|R^=6p|H^maWMPNzB${hNOn%h9rk%hopz(ha`w(h@^<*h$M+*iKL0-i6m-kGDT9g zHMt_mBH1G8BKaZ-BN-zpBRL~UBUvM9BY7i<+nUUg)NM`fNb*SbNcu?r$OIrWfJ^~0 z2goEKvw%zkG7rc^ATxnXg{_$jWHON1K&Atk4`f1+89}B5nG<AEkXb>d1(_FQVvw0Z zrpDIH4Kg{lW_FP2LFNaUAY_J+DMIE5|KmvtHXAiOk+pK>B*mq~x#E+YISDTNTXOJE D35>6g diff --git a/telegramer/include/pytz/zoneinfo/Europe/Minsk b/telegramer/include/pytz/zoneinfo/Europe/Minsk deleted file mode 100644 index 453306c07566a94c0c391024fb16ee36245a0a40..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1321 zcmdthPe_w-9LMqRbk<sGJ7n|8txPk`&9Y5x)wI=|qMJ}{b%+WNLn=DhpJU{Y5a|&8 z@03u47)c4ypF<{e=n%GS9Rg!@iU>T^bBU;k^?R=t9Xiyt&%^e9d_?^B`XzSm>+{JU zSMu{|M3?&&O23U6V}ZLPM(@;~{&ebH)baGX^UU<su;baeCP&&{&|6k}ExoAxRmf?} zY;k^B@UVQgG;L(Up~b25>52zeOxKOGrhE9fx#Z+wbLr8fS$SZ)xolU5SruDrRtM|M z<zB<AaoNq<kLCS!Z=Gu83!AEc^jUfC%qs6lMm1bWsa1oMYW0zE)tJ1j*6e;FzC)wp zZ@no^+fGQc`?R$9_DjpRgA%A1Qi0cTS@(6XT0hw=8#3K0IJ#A}PIy(@kY8=QRH{OK zWopx)M>a=3sc^DH+G}2_Nce+v6iukk>gUq=enz?qZ%fzoed&I4QKI9+5=-4uJ*SSz zKsKAT6co;}7Fml5=l-~C^L}0S`G0p67mFA(`fn@7W3h_3D#a#J-zfTdsY4t*u`JBL z2SOGj|JnS##r@k?RmFM|(xYFIh;BS8Vcl>f&Ij%Kp}z4n`uTQZvGCGM{DT7hOJ{f7 zo2sK|popNTpva);pa`KTp-ABeKnq0-MNO_+4n+?|5JeG15=9e56h#$97DX3D7)2RH z8bupL97P>P9z`ETAVncXB1I!bBt<1fCPgPhC_^bjDnlznY_3`@LvF5GFGDazF+(y% zGetB-HAOZ>H$^x@IYl}{J4HN0Jw<-5T0i3i<f=~r<0LRn1LH(cP6Zp#|KtDTWY~lH Ku(UP!?cV`C@iFcI diff --git a/telegramer/include/pytz/zoneinfo/Europe/Monaco b/telegramer/include/pytz/zoneinfo/Europe/Monaco deleted file mode 100644 index adbe45d1c1db7287345b1826a356732075a38c51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2944 zcmeIzYfu$+7{~EFDvEe}&;%0{lMprJ4yj3Mh-3nyo|IHX6H$|BC3Da;baG71aUL7S zGBi*TZ-`cS3%uX(Mpk2{<<#h;T^##2=GbM-_PdkQi(dJvH|@@z&+fiBug>}Yykn;2 z#ajP5S~zdGIQBXh_aXD0j}=8NhLxrknbHdp_UgJ7_L_rFoA;LdX_uAvH02XEn6>j; znst$F&H6cMrXpd1*)TrTt_({s8^cS@rmSxE=4SKE=9}^Mmg_lY>xE2Hbz-2|c5<Vs z-WY1Omk+adyy0WsUozc%V0X1YEL?2YWHzyP4v8|mqORGyW7F(CZR+hk_x9R*f7)Vy z^!Wr+`^9lnSHIir+q22+FUzqHytC9CeAk#mMfv9N)NFGkE5jV^?_(e99c_;HU1?5a zRoN#lH7`FEIM$rH;{E3IwrS?f^`UZhbD%b?OO}t{ikEXsem0*J2FRxw1GGhLAFJio zAk(U1lD2-cqqJF=qB1j4{d2>$t+#`=OCKZ;#r>}BLx0qOc3){=<FgugYrl55x?Las ztXw-DS)oC7`y{xkS~{&Rlg`UjLgp@zF8PJ>$hb+;HO(ub{Zi!7h(rks4wr5%J4m=s zvWDM|*Y4kjX^-;(+Vfa5eXQnJji~rudzD<$$c3jhYG$2A=Wf%O?28gxSR-*swGuyK zr6hD(BZ;v^l6bdRdIyzg@5>XV&mXh2@4jSt;>#@USDB+pwSD!;MFX{eX|N8+3)O)O z`^Zy+eKa{YNCt&n*OcVDGT7&&riOkksW-35kj95(NJG6mb9KE8t==MOXAWrk@@0}y zwN{4}6iDW>*LC>lr8**ivW|Q_U%hFm`fOl^j*94^qwYj&R{Oz{b)l_3ce}fc+4YN# zJ?|&u%Kas~=7x+fz9l&&4Kg9;yiA<;gFfGXw@k`Dqq!lgWO7oq=K1c_DV<cOeD}6a zy<4c$>SyZ<m%TcD(^$<vK0{tyoTLSnBju&3A!;v5kQu}KNnu`~yd2Y6W~TfmulQRs zE2_QDGFPOi^&KrbcT{HIyr^?_RLZLjwK{jzVVzgKMqis<p~cIK)zh?5;|DvN_}o8z zoB1~P`>#&RYI?)@e_D-VtR|M#T0FiyS*`Bh2Y2!K-+$xW2k_nsvaEmZ)6%_GrgM!> z8OaBi^OVd}vh!qF9*_G4f5W}U<9&dB+;ffs|FROKBS=eJot_{~LArvp1?daY7^E{u zYmnX`%|W_@v<K-A(jcTmu1<@P9wALax`ea|=@Zf@q*F+%kX|9pLb`>t3+WfqFr;Iy zPRo#<Ax%TNhO`ao8`3zWb4cru-XYCHx`(t6=^xTSq=T+b3y~hWI!#2nh_n&uBhpBu zlSnI(ULwupmq9l<XeZK7q@hShk(MGoMVg9q6=^HdSER8>XI-7vBE3bLi*y%hFVbJ6 z!AOUZ79%}Inv8TAX*1Giq|r#Hkyg7py+)dibQ@_m(r={UNXL<uBRxl&j&vPqJJNTg z@kr;9*1I~rN1E^IbRTIy(tl(FkR3p_0NDd%6Odg%wgK4(WFwHBK(+$e3uH4~o!vmT z!`0ajWJ8c0LAC_h6J%46T|u@5*%xGEkexxc2H6{Ab6lO>LAJ-$*&k$skR3v{2-zcK zlaO6Pwh7rMWTTLsLbeLoD`c~f-9om@)!8p(!;l?AwhY-bWYdscL$(dsH)P|GokO+` z**j$OkljPJ&(+yKWCLBD9YnSe*+XO#kzGW#5!pxle<L4!BmJED>=nb1Mx@6@CPYR@ Lq{l>KRGi;m_j$vq diff --git a/telegramer/include/pytz/zoneinfo/Europe/Moscow b/telegramer/include/pytz/zoneinfo/Europe/Moscow deleted file mode 100644 index ddb3f4e99a1030f33b56fad986c8d9c16e59eb32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1535 zcmd_pOGs2v0Eh8&eK(rb!qglm&2)U$sA*0)IyEzjsUbNPSW%Qng3+OZ6j}@+wy8i0 zTv(<w>Y|s6YLnFvfkYNAS_B#d(ZT{b1Q9Ad&UZz$TD9&D_x_Go1;Ov{Z)$BR5`SH5 z^c!xj-TLO770{2~!?v;O6<<2~a%X1yzByZObnc(+f7>=aAe@1L@*#I{^@&i>RWve~ zaC~IY6&@P4`5GPslaD0WhbPu1O}P_eCL0pxR)vy2#ZM$pdfe;AuS}$j_ABe{Zk2lN zys}+9t=6AwR%vZ}Rr<jywV`gS$|%oP8}pM@rq!adV&|1T(k|^^lVtYC#6V8_(?HIf zIhp(Xv&_3cCG&%?WWm)Za#QC$x%o`LbToI%!b78~=v0p?cJ-+(dpcA}YCx419Z;p; zkE*hic3Jk$tDN&qa@*r9wSBT&mJfNP>yb@XbY;rQULoBr(Q-$pRqgamOV6<%%A5I8 z`aJJdRpcF6o$*Xn&%97I;XzgN`j*=Dp-a`?y`<{KZ_4`1CzZc0^@tH379J565g8R7 z6CJf8Dth5#iCy-ITe<9u<=^=89B&aK!>RufJR^iCykNxW^I6W7Jw}`mWo|?Nw{jgK zVewqmU?dA+O%tiVzt43T>5K2n+)F>t@7C4(MLl<;zP&sez51>dd5#j{^ZE6yUz(S} z(^$BcUYI8#{QuC`Pkrrs7#c%5Ls~<6Gu6!@-68EE{h8_pkq%9Di%5^Ax=Ex<q)q-* z`a~K<IyKd;BE2HbBHbeGBK;x_BON0xBRwNcBV8kHBYh){Bb_6yo9f<?=8^7Ab^A#F z$Oe!dAX`B8fNTQU1+oofAIL_KogiC5_F}3xgY3psZwJ{AvLR$g$d-^jA)7*Wg=`Di s7qT&AXUNu&y&;=Jc4w-$hwRT(ZxGobvPEQ%$R_cB-=#%wxuDqc3lb5KyZ`_I diff --git a/telegramer/include/pytz/zoneinfo/Europe/Nicosia b/telegramer/include/pytz/zoneinfo/Europe/Nicosia deleted file mode 100644 index f7f10ab7665e94ca44fd8cd98a362cd4b304eff1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2002 zcmdVaZAeuI9LMqNOjy@ye{0UQ>qF(rhpu|nY}s_Jnc9QbPV-i-GEb#fYtAi8r(5m5 z$Yg}XzY-#9P-GGju7RsTPxL@E2zOwMF+w`6v5iOxDx!vL=X;=6dll@>&gI_E<)ZKY z-(P6e#&DkJUr&tl3vZr?^XB{bW1l8}H+J}I+dH(^ihWjRkGpWq7~flHPS|zFdZp86 zO6yW9Zo{ZKvC1|k1t;6D=3h4AQ!kmXP3kogqK}#h54()l@9s1w|JZ1}aiziZo$Is` zPwudj4u!4c?s_|A+d^wfQ@K5LO{O)iBEwEC8fU%fkF}@!MywgJ!**IstdaKEYo`A; zY-Id&-^{%FgE4bp(De6yV`TN5GP67P897_`nt{4jBe$mC&I|6b@{84;m9@nxNNTZX z=e5i1(TL3P_2`_TbyE0Oo6bF7B5&WS)}p>zEj~L}-|3pK^A0BJyWv!w-&rW{mBnaD zolh1_|3gblMx`v~do54BE#)J>%cAH@vS{$SEWUeGmh_*HiW?U-xVu{_Pae^w&COzT z@6cr{cj^00^;-2-lZGnFb$LRiuJC8*iYEcBjxUqypC{@EkJDw<=|{TyrdQS+j+2^! z`?5CjP-=Sy#jL$4>$cz1_4CfihMF5%mvTVri~BYF^0(TMq}uT3er+6W(T&$Tbkk5s zKRmu#o33q^kG?F{=DsTVxG_aP=_-)T%Zj8WoFH3rlVxk^Q)!L!NLx<4wmtY&+9y2G zcI&EijQpaXo$8a%2hZxZ1DADs|5y4&N3TY9NA#tr7kfpI`A=USPs&1WF*6V~#^Xtx z;u-t=lV2)=Ax~*(6(1q~Dk{qT2))2<|Lr{7H~-F!BX^G6I&$yG%_Db@+&*&uNCQX* zNDD|0NE1jGNE@zBA4nreCrB$uFGw>;H%L23KS)DJM@UOZPe@ZpS4dk(U#?DLNM}fE zNN-4UNOwqkNPkF!NQX#^NRLR9NS8>PNT04wqe!Q&POC_-NV7<{NV`bCNW)0SNXtmi zNYhByNZUx?NaIN7u1@Pn@2*bsNcTwlNdL$NAUl9;0kQ|kCLp_jYy+|n$VMPL;p%J! zvKOw-W+1zPYzML*$c7+0f@}%0C&;ECyMk;BvM<QSAUlI>4YD_`&gLMygKQ77Kgb3l zJA`ZzvPZ}!A-jZZ6S7apMj<<eY!$LsuFhs5yXER^7qVZ-h9NtKY#Fj=$fn`{b=SPk a%w^><c>Z91c0qO^C*L2;4Y=QCdH(?jq|NOB diff --git a/telegramer/include/pytz/zoneinfo/Europe/Oslo b/telegramer/include/pytz/zoneinfo/Europe/Oslo deleted file mode 100644 index 15a34c3cedb7c9ca519c195f5ec0ce9d8d1885a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2228 zcmdtiUrd#C9LMp4h!iXLF@<9RLLs4r{0IL8nH_<d97{PFsRSw_7EzvzL26Vw=Nh?h zja80ZR*qRS?1J(Ft@X!73&~8@tXx~HtTZB#=+9XF-cM^?)J->C^m}%m*Ry9k7iT-4 zcWA|i+8p2CPPBW&hx3^G@O<e*?$(|*;A=m*xw_|2u)6omVjX+YZ+*`P^uZH(I{rwi zJ`Bg{#F}WGJ(z6g_Lu3qr;9YWGeh4uC26Qm`k^91=S$CPc=muUq@C1=|EPY{kd0<e z(CE+!n;cxIDY?H`Y|2@SoBWyiBafMX_;s5)aL_LQXs=!I_Tv_R_=pk?bSm+gXEm+W zl(gkxCD(^_<*K#1sw||G!eUKNFHmYiny$WNs?wqYmNt@SGrml<nf-Bg&CzJPw(BQL z-}jBpYWu`8w!d$gn+{u6&C8Zuc}h9qF69<=D{tA8%1_**f}AE5jJ0S^e4EWVy;^gB zZM1nu0=n+g3M=fWvZC&JcKwb8HorZ=Zm3PM1>5K9#*!!t)WmCH>KQ8zjHx8*Ju6N5 zT&06wX;I{xTGZF0n+9Ic;?9>;*87G9ceQHCf#>Yzh6dfzy3Ll}_NXnZUuWgB>n&7P zYPb5A*z)w5wtO_pDq>4i@$qGL`^XHfc<q9%?2pl^y^~bgbxwD*46CZGPt{fZTD|pK zTQmQV)>igfP10_yE9$h`i(a+$iDv7+e#+{`!nUEO+3q|Yvb*-LwEA~9>h7II*3eO| zd+L(x-W~bcxU^8=TEFhgo~BL3KkNQUJ~d{>TI0|cYMMA|O~>C+^WZ6a;FS(-?(4QK zyWg^{oqO!T=6%+(tHs7ejEjgI{|{Hxg#Z5X`C_KH|FAD1IbyueH&MQe|GfY4=CAi< z!H_RdT+S`THzM3Y_YnFQi}}r+@Zj`%WI3L0J;;KP6(LJP)`TnySrxJ@WL?O@kd+}z zL)L~Y4p|+tJY;>y0+AIWOGMU)EYj1hl3&g;k#!;qMOKO|)zhsNSuC<zWVy(Ckp&|w zMwX1M8Cf*4YGm2Sx{-w=D@T@&tliTs9$7uId}RGd0gwtHB|vI`6alFMQU;_BNFk6) zAf-TR;pvKjRKwGi1E~j65TqhVNsyW#MM0{9lm)2^QW&H%NNJGTAjLtd<LSzS)W_2m z2&oWKBBVx0k&r4OWkTwN6bh*nQYxfYNU@M=dAf2T_40HDLn?-p45=AXG^A=s*^s&+ zg+nTbln$vKQaq%3NcoWZdAb536+}vi)DS5mQbnYUNF9+vB9%l+iPRD)CQ?nLoJc)A xT|tqGdb*M#HARYwR23;JQdgv~`2VbIj0^9qY!aLv%+1Kp$Vv}pXJKY;%<s!{R2~2T diff --git a/telegramer/include/pytz/zoneinfo/Europe/Paris b/telegramer/include/pytz/zoneinfo/Europe/Paris deleted file mode 100644 index 7d366c6098c49ecd546e1cc1538919e1414a3aee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2962 zcmeIze{{`t9LMoHHk+Awk7lwlSu-^Iy@f1v&654XjfL4{hAe$8IU^c}Bjh-59Y<+o znE5rM8m7(fUvpwqN1~jR6a6~+bmZvAk-pDwe^jTxoc`#azW02;kMI4Sd(Qpm-sj6R zdP-4(>mNsJ`w1sUoqcj2KF_|aD9Fh!PcJp)7ox2-4J)j*haNX?F8R$`SJBf{6l^l< z=LMJzF>TGp*%_uXdA`{+F2bscOg5XN%FUMCZq`=cxn}E)Bx~FCe6#&Rj;TI5(Cj$1 z+0<-~Fgq)<tz9qsn75WpHE&y8t#?WmTeUgf*6ty(W>4%jYi~k^RoAA`s=K?-+V}l7 z>)nqEO#R0vOhe;dv%hYOIj}C@I{5lhbLb6Y4wn|2Ba`#Y(cCO^tiO+Symy>A(RY<O znOkk0ywtLyu{i3=>FYz~%+?TX+TfP6uO`X4?9Z>9Tk?Z>zofl<kTpPC)%S4)Y!5Z9 zD<^2+D;=fH!Zei`DH>E5rENVOv|Z*Pxi9e-4UYI$+qe5vLz<t_kedgz!_}R7|A!UY z@#qQ-ZP+hi)iu&-%{uA4OeK8IeCbkLA`gt6AYC&&645VB9*j<r$gn8s7SKVWeB2s! zD@nV58L2(ax7VJ>ef6Q*pESDiYwfl2lEy52Ph)2^Xk6hAjnBI%2_?0Xm|8DM!&gai zr?rxjP%0^R%A|MbO6`4ly!82XruN<MmPbCx)qYj^np)piA6+z1`<I95fT9Q;xUi2r zHrPkqg`qMi^17zE@5o@EQ<@&}nWW#iCPSJZmLW}z^2F7RGPGu!WSl;znah_+R`q(# zo;FQ#mMzd>qn7IM;)y!q;bQe<r0bI*SvoShhmO1*r@6s{CHF#Ged<<s8NKI69dq7a z##RJLUhQ`>uI#4duWXWn{PQw??l<~$|GhFH@3a<%ua=3aHCp7iTPJl=o%GdfI{8kC zPHCK_&s_HC)GcGQ_{4O1c5$jss~RECO%7LUQL;=Q)=x@`Lge}Q&N3tIXL%vWB{O4# zb*A}3N&|0e>A7Pv>&8W$y{k%IY^v8etB>g1nzj1UtV%6gUZ!3?&6?la%iFJoZwud+ z-Yxz8{96V5S1*^VS-kz%<m&Bm1&Ws+@A%LD{oWPia)ovA3jWKrBcJ*6IrkZv9#@Wi zj!GNB#p7~2r}M^s>~eW|{c$=s&Np%K^77n6O77an)KleWk)$A5akSHd<ON9#k{Kj5 zNN$kiAlX6EgX9NE5RxGzMM#d2BstnyLehlf2}u-^DI`@$u8?FQ*+SBV<O@j{k})J@ zNY0R?Ioer6(uU*>NgR?nBy~vckmMoRL(+%j4@n@BK_rDp4v{1}+F3->h~yDTB$7!a zl}Iif?PMa^MAFGGf_!q2P$Z*BN|Br*Nky`Xq!r04l2|0ONNSPXI@-xavWuh_$uE*% zB*RFGksKpQMzV~g8Obw}Xe84}s*zkH$#%4}jiejNH<EB9<4DSpoFhp`vW}!3$vcvG zB=bn>k=!H6ceJyQq~FobKQaNx3?Ng0%mFe9$Sfe!fXo9j5y(s+Q-RC{G8xEhAk*P! z&j&Igj`oZoQ-aJ1GAYQcAk%`(3o<dt%pg;P%ndR*$m}4~<7m$hGC_{^3?Wm5%n>q4 z$Sfh#gv=8%QOHaoQ-#bGGFixMA=8D-7cyav_KYD@hRhi<X~?W0(}v6&GI7YvAybFU z9Wr^y>><;K%pWp=j`j>9Q|M^VAu@@`EF#m0%p)?9$V}q@Q^|w|{+p@vw>Py{yxU&b T=*+~J<e0eV%=kErP4xdAFa5mr diff --git a/telegramer/include/pytz/zoneinfo/Europe/Podgorica b/telegramer/include/pytz/zoneinfo/Europe/Podgorica deleted file mode 100644 index 27de456f16ab549627b284a39e2265cbdb4ad8e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmdVaYfQ~?9LMqhL0H4S7@-o9T#oJ~DdMPHDwhrt$t4vMLdq?-nOU<hYs@g$HH>%= z9>mObA-Sy?W{kPcS{sI0<M;k=Hk(JD*!)lDyjrdG<oo`(Gv?)lS${mO%ujgptT1oB zZ@bQX+-w&4y!OplxqZw_>khf(&W;GVyCFdC9W0aksqxz7<tgp@;DC0!vR%E;Ul5-Y zmEya1zjQBC@msxKdgK>M&*^idSF&6DV-uveGfDz{0;NxzE)wYB(!kFV+V@p}_N(u# z{jass0aahsdE}iAEPt#)n{H|Fvhx~}eNsa+A4ynYm4wGtOT@&T66w27qQZ(Ls;N|> zy~{QF=`0!iy+~s&xMawabd9aZ(zxmv9lCkA4%_3S@j3oFeA8eVk?5hWY;PGE@J16{ zO_JzwLzDcUNm9dW8QuJnjIOJZF)t6x*vjLQTzgSdwv|chiGw<>pg_i#ZPW=<w(7+E zxtca8U){+`I>{?lCp-J;<S!wb-YHSiA9m2GpZiM2*-tvH-czO@XfK&nA7n=9N69L$ zlbKodGHcCyojvTF%*m|PY`@(yH?C51TA$HeU)9{VyELz<Q0LW@==`T{U2t@o=3ieT z3%A5+K}DJ@%Jb93n<Hh(gjgxe@sg#X-DO$AH(B1^lA_>FTGaSLiranB;=5O+q~VdS zJY6BH>Z*11?#sHSa-Xg(IijW8O4ZS#S#$g4(ehuuEURO*xhkujSS@~i`t)$LwyfVj z`E7EF+j1rFPIH?-a5(tlaX8$6al=2%Gb6Tf6mrYRJtH@b+%<CB$bBO>j@&u_nOjHh z9l3er?vdL^?jLCY=>TZ~=>cg1=>lm3=>us5>BQEwg7ktkgLH$mgY<(mgmi?og!F_o zg>;3qh4h6qhID3YT0?rXHO(R2A?+dkAq^rOA}u04B26M)B5fjlB8?)QBCR65+L~sO zZf#AwNWVzKNXJOaNY6;qNY_Z)NZ&}~NaslFNbk0$d8B(=(>~HavH{2rAX|X!0kR3m zE+E^0>;tk9$W9<zf$Rmc8OUzfn(aXL1KAK{N02Q+_5|4!WLJ=FLG}gN7-VOVtwHt% z*&JkdY|Zu{`(tZ12-zWIi;z7+HVN4!WSj7R-zQJATLVKGE@w)3P-IYuGbJ<xgTp<4 E0<)*Xw*UYD diff --git a/telegramer/include/pytz/zoneinfo/Europe/Prague b/telegramer/include/pytz/zoneinfo/Europe/Prague deleted file mode 100644 index ce8f433ece44f0b96b18d3b5780730e7f9cad9f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2301 zcmc)Me@xVM9LMqRfyj?3d!GhJ^0SDLI{6KuL1rf~k~5XdNG1G%sCAKC#-KDTnRAWV zJ7YSBq!nY;jQRs>4XyQav=B|b>@3%oE7O{DGpE&9eV*T_(ei&kU+x~?@3FD5@p=y5 zl68&w*8fhF`GgnedGq4lx!JsRPjW5q4xYNWC)BS!y$AHA_f+?G?9!p=W*t5|PY%EC z(vep@a%4xL9DO!Jz6`|6v6Yc>d~=eXs5&MmUK~gZh6U1s)g|&()_|OJPm`~scS^{y zUP9+u#o3UlQ+x?J)jL;iDEM9D(tp<Yso$zA{II$%y{`#p-qIV7@6%}?zo3Z+4@uJg zeo5Z5S5i7vQa3&$Y5suRv}~2!T<w$e(sH@QT`U<%nR4r-1j&r>Xy)ZYo&H0HW(DK* zwofDV_JOPF?mee7x=v|!#}}Hj;h^R=ys3G0A;}L6NI}`46fW8+Maex<oZl+NqwP|X z*rg?>SIEpiT6ESSkKFl9t(NxHYuVr|y=&_no!y<JcQ>Z%oQ|1tPep`!8WLr0##t@* zj7mks=USQmom5`<QL4f}l&X;-x%bSgGOzy)sUH47z1urw{{ENszNRKw(78nyKJc6_ z@~_sKJN)XauGITo^L4R1OBY|s)!MiUsXaMGAG|zWmb~+;E)B-WvVBge8~8;YYQH4) zT_du*J}4_To!6DK-<4H$!`hI#TUM9#Yh%;_U6ZW3=BHh{b~K>thT8Pu(>{IVwWaF+ ztXUr2R;EpTHS*ZnR9(NdNSYUxN}$mtkLRVxhVtL!38y73IdR%@@q1~Fy`rs0KasWz zA${`gK6z?nP&e-WNH_KO=+kYz+P=MA!yIAZ6UJW=W6u*Kug7Isled|_W-BSpF~PE8 z#ftv#y=6HjkN>3F>$5!NHN5$(O7mcj!@-w*91h>LcVvDnKiWPzb|3erIVn{;uA=|Q zd0TeHGuuM;g=`Gj8L~BGZ^-75-67jU_J?c`*&(t;WRJ)uZOtx`ZQ7cBA{#|^ifk3x ztF75Aza6_pwu|f+*)XzWWXs5&ZOx{UT_f8@_Kj>D**UUxWber4k=-NPNA{020O<hI z0;C5>6KqWvkTxKFKpKH`0%--(3#1uHH;{H9{XiOmbOdP$(i5a9wx%mcTWn2Vkj5aL zL0W_K25An`9i%--e~<<t9YR`!^ayDZ(j}x#wx&-=qijv5kX9kRLYjqi3uzb9FQj2e z$B>pGJwuv?bPZ{nt?3)mI9t;>q;*K|kme!XL)wS*4{0FML8OI950NG!T}0Z5^bu*K zt?4AvN~D)aGm&m0?L_*CG!*G5(o&?SNK=unB5g(biZs^NbQWo?t?4b&T%@~5dy)Pk z4MsYQwAc}D+8Z(cnmG0x8Ff9be`0KsY+`JZZ2sGb73=Q+|9fwO>m2`GlDyy=SsveI bb01@hJtL2HyS)Y3McKJ-Z(c6u6vX@mfqZ3V diff --git a/telegramer/include/pytz/zoneinfo/Europe/Riga b/telegramer/include/pytz/zoneinfo/Europe/Riga deleted file mode 100644 index 8db477d01736445cafce8af7a7085d226d81f546..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2198 zcmeIydrXye9LMqBNhl)1uPr<fAS!avkUMGyi5-C%978w~sfZb%7Lm6cl)^J&W{t6L z#%iuiGDnvgwPv=4uFG7bL^}Vl*66a;ipH8dt(>zB>i2$>wbuHhtv~xcJJ0LkVQh@? zc?Z^SZ^|`)J2UMYKAd*@;W^c@w-?`gV(MsD&s5*R()PY{ol|d&|NQmPn+=;k-O^Y& zJYFv6U-Rn7F`s;PC|`n|DH7Btcf_Q<5}Y0TWwAG6tkV12%nxEGqJwc`zT#Vkp9#jF z?h7THcsi8$LT6}B_wG>AzJ}1;=5?WY8%sm;E0%_m3)4djGU7rh32`PhD$=Bd7dUAb zQ=Rnhz0ShX2xrmR%g(JQC!CDqW6t8<VJEZejFYuzz{zen>g3d&Gr9iLCa-8v@~fVe zg2W@TB)3(TOm@h!_+DLp#wWLr)oNjXy%r5F*E<fZ)D_PsXmL}ruI#!^?ko*gUqigC zN*UJ@-=vg=f1+i{-$>boA7u564`ubpklgj%%Thk@x>O9mul}BHS##ngy}PAFD!U)o zwf8)wRn3iBy|h^a6=k~4yGCm=7HZ9<Y^{wcmD<m5(t9ta%lcEl>W0xs+1NKr>Q4S7 zn>v1x`rZ-QTt6!J?Vr#sE8dm+>xQ)<>98~w4QNxuJGwPdb?cAM>9)yTx_zikANVSu z55BrVn?K$u4;?Jhmi}sacw3V0I8Y!v*A~jICa>(ynIn5jF3TgcjO@*d(Y>K>r8WAJ zwg%5i+l4Rm(YN~Lv5`UDclaaSKX63b+m35TPltN0vDb{S>%y<KS6IZA>-xy6*9}pB zy>5*DZ!cqJAG7~>+{27n@U|zyn1s0|%9usjMvRFTb2D!|vD5cu#h%3J?@m8^=Kc9o z)6W(DfT^;dit2zVDG3;Vb-D3beI@pVzj~E@X&>C<@fhQA&y}yQ-aVeczu3?3_SBp@ zzX{n9vL$3su69$%u8?gZ`$9H`><rl&vNvRNu6B3)l57vzAF@GLyF+A)$R3eRBD+Mk ziR=^ED6&&ztH@rF%_6%+wu|hS!-hHR*wt<s*)y_fWY@^Hk$oc@M|O^E9oajwc@Deh zuzh6zNCS`#xY`yVJwTd(bOC7t(g&mwNGFh1AiY4Efpi0D2htCuAxKADZA*}zxZ0*5 zT|wG{^aW`Q(ix;RNN<qlAl*UQgY*Y!5Yi!6+ajb#uC_@?myk9geL@<AbP8z|(krA{ zNVkx7A^k!chI9;R8PYRX+cczWNZXLUA&o;ihqMmq9nw6cdr13`{vi!SI*7Co>7lD_ zBGN@y+eW01NF$L>BCSMvi8K@c?<Ri*+suFHW~2@7;#>lo;m^w~$jr{l%kbx9yHEL% Fe*o7aBb5LE diff --git a/telegramer/include/pytz/zoneinfo/Europe/Rome b/telegramer/include/pytz/zoneinfo/Europe/Rome deleted file mode 100644 index ac4c16342b5bbfa4c58a26f57db33b95f5b3e533..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2641 zcmciDdrVe!9LMp8n;;^6(G=q%7MYlcAfRBPDFP!OEa6Gb8+b!4q9ZA3sc4xi$K1Cx zokJ5ZxiqC#m}_dS(nbTxFf1x3%S?;r&Sp*t(SGm47_<7@bM~Cq;W+>Ny+5AmiwlRl z{&V@8FZ{Unn;-8z*O*5|$_=Zcv95Xh$y$5I5m&+6uivtERz@^e7QAEcT79Ts_so&j z9(PzlO;NI4cWI)W+8?U*yVK>HH<RRa@ofFJq^~r%8|*V<67;+525F4EZk-*x)jr#@ zMt|tOMOqUN+pSF}n%g28thP_8?VC5Nt@dqO>{}O4+qYl-$a0<h(Ds`9mgT*Dy5%$e z1<Uu<QrmCHCc9hlCd<FaGCLq+lpWAA)eelCY6aG&+CjmoR?vIlcF$j%?cfdV_C2TC zEm<07g&aC>^~$?x_0E|f_a^?WeWF{mZ||Qq)aR6jUj0=2U3g#bJ5#A)U%sK?$Bs+H z!77Q|zEk>_t3<6_D+7v3<o=oS<N>!wqDN=QgRvuIV8kGKD5#$d@=Mo2*OD~m;y@kT z*jFDu>90eoZ)oh^-*xEbA2n`WtqxmyOylPt(u7$}GQ6Z(64Q=KQtlQ>j@%(5hA)>9 z?PZb@zFAYw&5_i$QXO?XT^{*qnvUL=uW3g|>6rE7bZkY0K3W*9<JP6h_)I@dpC2w0 z242#P^mfVg`&zT2&r8;o-z3}TbIGo+k;g9Vk%?6Y#9jB9=4>pNNe6f7<Hg1DMENT^ zdFlquEn1*?Ba77I&eA7CC+U>f!8+x7yiV(rDbv31rB7Xpk?9}*r861>WoBiF%&PuV zW|v)+{LS@Jkl!eCR{f!K#~zV+v+8ty)HYd=R;7gjhjn43>cY!gby0hXF0NUjPoMMX zGw;sOqOX?7v#+IT@xDBHZc&t4>yu^4<k3=67%I;v^p~X>t+Fh{C8fjqXsP{+EDye} z%TJ$>6<3<{#Siw$OZ7)}<+d+$Rn-n%y<)GHZ7fr7uddt2*W6y-Jk8x{$6t3m{kq-# z+vVy}ZO)S`Vt|*g%M~oH?w!w$FJ0f=IUZMfMjj6j|HI2%XkI-3e|iJVKl0-`V1B%Z z+&0&kn9FXoj;*zj)9h$YG;*qulZ~8i<b)%q969O8X-7^xa_W(jkDPv_07wOp5+F4| zihxuBDFadmq!5m#5=beKS~!|wAk}a*<v{9z6a<}$@IpzrOih5IAXP!ig46{m3{n}S zG)Qe6O>vOwIGXYx^>H)>LMnum2&oZLB&146nUFdmg+eNYlnSYpqbU|rEu>sXy^w+- z6+=pf)C?&aQZ=M(NZpXaA(cZ)htv)!o};NAQa+@9NCA-wA|*s>h!hd2B2q@Ajz}Sq zN+P91YKaun(Nq&Dr=zJSQc$F#NJ){JB1J{2ij)<pD^ggbvPfx>+9Jh8s*9A@(bN|y zu%oFkQevdWNRg2$BV|VFj1(HFG*W7$)=06DY9r-#H1$Rb?r18GlpLu!Qgo#1NZFCP zBZWsQkCYy%JyLw6`bhba`XdYAXjTAO0%Q%4ML<>oSq5YskcB{20$B=VEs(`PRs&fM zWId1taWpG}EQzC86J$}4RY8^oSr=qskd;A}#>>aM>-P0Cx3>>Zb9dVD*B#Gp{&)ZG zoEkGYW@^l^m^}y<SI^F8$Cs|}3{LL9MyG3a%v+#YqM-?FQfy9QTyk7|Y)(Qv4oeLD E2jBk!N&o-= diff --git a/telegramer/include/pytz/zoneinfo/Europe/Samara b/telegramer/include/pytz/zoneinfo/Europe/Samara deleted file mode 100644 index 97d5dd9e6ed7fc924c9bcb514f991cc8b52061b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1215 zcmdVYPe_wt9Ki8sx_@jDUTVuf{WDvey4KvrtZ6gTVQnBWIz+((WzZjJh=&fr1T6?6 zLLw-Nkfc+H2RoUxL(s)`kZwcxL3D|T5p^hu^?cvir6B0o^X}pE?B(T!?f1=}x^O<K z{#agfhs_!=n{(5w>YaQ(=N;V=xL?}pFGqatH)-E@+k*dtDs8L8Bh4$<OD!*Er1ja9 zv^|`V?YG8c$F-BP^KwRZoleT`Y*5-$&9bM<D;=$#>R#`9HQ)#o0$=@weeZpfLG@Y% z-+t7gS8KX+v8=o1Uh3|<3pzYKtM^aL=*YP#ec;TzM8|JRPv0Ghowy|NwsA>BbCURx zmt@ODom@*u?|N1rT=vVMN?50!#&zFPlkUIa(}y2?*6FctdSH6992u(U!LwC4+Oe#M z23KX+@mOct7bWv)Nk$s)$w>K;9D8?Fj?Wh*yYi%vyM3ivtkr6^hQ|73cWhivm(%5T zHT?SeH=QoKU8(RF^Jl71M459kt=vitkJ>i<ezuwW^=Cp6oAo4jcs`rUtIkM|*)g-@ zO4-b(zBq2I{67rV{H_|qMFz|(7&0<wWZ0Hw;K<OC!6U;*0ze`_LO^0bf<U4`!a(9c z0zo1{LP26dg0VEwAmJeKSek&4h>(zwn2?~5sF1LbxD1NGkjRkGkl2vmkmxK;cu0Jf zCO{-YBt#@eBuFGmBupeuBv2$$Bvd3;Bv>R`BwQq3OA{~>v84$ai5UqRi5dwTi)Qx! OP28T8iNC))=J^Tns}05g diff --git a/telegramer/include/pytz/zoneinfo/Europe/San_Marino b/telegramer/include/pytz/zoneinfo/Europe/San_Marino deleted file mode 100644 index ac4c16342b5bbfa4c58a26f57db33b95f5b3e533..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2641 zcmciDdrVe!9LMp8n;;^6(G=q%7MYlcAfRBPDFP!OEa6Gb8+b!4q9ZA3sc4xi$K1Cx zokJ5ZxiqC#m}_dS(nbTxFf1x3%S?;r&Sp*t(SGm47_<7@bM~Cq;W+>Ny+5AmiwlRl z{&V@8FZ{Unn;-8z*O*5|$_=Zcv95Xh$y$5I5m&+6uivtERz@^e7QAEcT79Ts_so&j z9(PzlO;NI4cWI)W+8?U*yVK>HH<RRa@ofFJq^~r%8|*V<67;+525F4EZk-*x)jr#@ zMt|tOMOqUN+pSF}n%g28thP_8?VC5Nt@dqO>{}O4+qYl-$a0<h(Ds`9mgT*Dy5%$e z1<Uu<QrmCHCc9hlCd<FaGCLq+lpWAA)eelCY6aG&+CjmoR?vIlcF$j%?cfdV_C2TC zEm<07g&aC>^~$?x_0E|f_a^?WeWF{mZ||Qq)aR6jUj0=2U3g#bJ5#A)U%sK?$Bs+H z!77Q|zEk>_t3<6_D+7v3<o=oS<N>!wqDN=QgRvuIV8kGKD5#$d@=Mo2*OD~m;y@kT z*jFDu>90eoZ)oh^-*xEbA2n`WtqxmyOylPt(u7$}GQ6Z(64Q=KQtlQ>j@%(5hA)>9 z?PZb@zFAYw&5_i$QXO?XT^{*qnvUL=uW3g|>6rE7bZkY0K3W*9<JP6h_)I@dpC2w0 z242#P^mfVg`&zT2&r8;o-z3}TbIGo+k;g9Vk%?6Y#9jB9=4>pNNe6f7<Hg1DMENT^ zdFlquEn1*?Ba77I&eA7CC+U>f!8+x7yiV(rDbv31rB7Xpk?9}*r861>WoBiF%&PuV zW|v)+{LS@Jkl!eCR{f!K#~zV+v+8ty)HYd=R;7gjhjn43>cY!gby0hXF0NUjPoMMX zGw;sOqOX?7v#+IT@xDBHZc&t4>yu^4<k3=67%I;v^p~X>t+Fh{C8fjqXsP{+EDye} z%TJ$>6<3<{#Siw$OZ7)}<+d+$Rn-n%y<)GHZ7fr7uddt2*W6y-Jk8x{$6t3m{kq-# z+vVy}ZO)S`Vt|*g%M~oH?w!w$FJ0f=IUZMfMjj6j|HI2%XkI-3e|iJVKl0-`V1B%Z z+&0&kn9FXoj;*zj)9h$YG;*qulZ~8i<b)%q969O8X-7^xa_W(jkDPv_07wOp5+F4| zihxuBDFadmq!5m#5=beKS~!|wAk}a*<v{9z6a<}$@IpzrOih5IAXP!ig46{m3{n}S zG)Qe6O>vOwIGXYx^>H)>LMnum2&oZLB&146nUFdmg+eNYlnSYpqbU|rEu>sXy^w+- z6+=pf)C?&aQZ=M(NZpXaA(cZ)htv)!o};NAQa+@9NCA-wA|*s>h!hd2B2q@Ajz}Sq zN+P91YKaun(Nq&Dr=zJSQc$F#NJ){JB1J{2ij)<pD^ggbvPfx>+9Jh8s*9A@(bN|y zu%oFkQevdWNRg2$BV|VFj1(HFG*W7$)=06DY9r-#H1$Rb?r18GlpLu!Qgo#1NZFCP zBZWsQkCYy%JyLw6`bhba`XdYAXjTAO0%Q%4ML<>oSq5YskcB{20$B=VEs(`PRs&fM zWId1taWpG}EQzC86J$}4RY8^oSr=qskd;A}#>>aM>-P0Cx3>>Zb9dVD*B#Gp{&)ZG zoEkGYW@^l^m^}y<SI^F8$Cs|}3{LL9MyG3a%v+#YqM-?FQfy9QTyk7|Y)(Qv4oeLD E2jBk!N&o-= diff --git a/telegramer/include/pytz/zoneinfo/Europe/Sarajevo b/telegramer/include/pytz/zoneinfo/Europe/Sarajevo deleted file mode 100644 index 27de456f16ab549627b284a39e2265cbdb4ad8e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmdVaYfQ~?9LMqhL0H4S7@-o9T#oJ~DdMPHDwhrt$t4vMLdq?-nOU<hYs@g$HH>%= z9>mObA-Sy?W{kPcS{sI0<M;k=Hk(JD*!)lDyjrdG<oo`(Gv?)lS${mO%ujgptT1oB zZ@bQX+-w&4y!OplxqZw_>khf(&W;GVyCFdC9W0aksqxz7<tgp@;DC0!vR%E;Ul5-Y zmEya1zjQBC@msxKdgK>M&*^idSF&6DV-uveGfDz{0;NxzE)wYB(!kFV+V@p}_N(u# z{jass0aahsdE}iAEPt#)n{H|Fvhx~}eNsa+A4ynYm4wGtOT@&T66w27qQZ(Ls;N|> zy~{QF=`0!iy+~s&xMawabd9aZ(zxmv9lCkA4%_3S@j3oFeA8eVk?5hWY;PGE@J16{ zO_JzwLzDcUNm9dW8QuJnjIOJZF)t6x*vjLQTzgSdwv|chiGw<>pg_i#ZPW=<w(7+E zxtca8U){+`I>{?lCp-J;<S!wb-YHSiA9m2GpZiM2*-tvH-czO@XfK&nA7n=9N69L$ zlbKodGHcCyojvTF%*m|PY`@(yH?C51TA$HeU)9{VyELz<Q0LW@==`T{U2t@o=3ieT z3%A5+K}DJ@%Jb93n<Hh(gjgxe@sg#X-DO$AH(B1^lA_>FTGaSLiranB;=5O+q~VdS zJY6BH>Z*11?#sHSa-Xg(IijW8O4ZS#S#$g4(ehuuEURO*xhkujSS@~i`t)$LwyfVj z`E7EF+j1rFPIH?-a5(tlaX8$6al=2%Gb6Tf6mrYRJtH@b+%<CB$bBO>j@&u_nOjHh z9l3er?vdL^?jLCY=>TZ~=>cg1=>lm3=>us5>BQEwg7ktkgLH$mgY<(mgmi?og!F_o zg>;3qh4h6qhID3YT0?rXHO(R2A?+dkAq^rOA}u04B26M)B5fjlB8?)QBCR65+L~sO zZf#AwNWVzKNXJOaNY6;qNY_Z)NZ&}~NaslFNbk0$d8B(=(>~HavH{2rAX|X!0kR3m zE+E^0>;tk9$W9<zf$Rmc8OUzfn(aXL1KAK{N02Q+_5|4!WLJ=FLG}gN7-VOVtwHt% z*&JkdY|Zu{`(tZ12-zWIi;z7+HVN4!WSj7R-zQJATLVKGE@w)3P-IYuGbJ<xgTp<4 E0<)*Xw*UYD diff --git a/telegramer/include/pytz/zoneinfo/Europe/Saratov b/telegramer/include/pytz/zoneinfo/Europe/Saratov deleted file mode 100644 index 8fd5f6d4b881457d13fdcdd35abb6fc5429d7084..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1183 zcmd7QO-R#W9Ki8sxiy;|x|B0Fd$GB6T5E1HYuaqSV9k&i5mq3*2tm+~@K6vaWS%N0 zf`}rDMwea>b;@K!mq<Nzv)~V%dLe$7E=jHD`xj3gqFc{@&;Rr1*%)lUZ(-=fNW%QF zR@f6ZtIKYlSKT%3<Ijs#gR7%AN^631@#@OiZ1oS%)8J=Qs+mv4*Unrh)lOY?LJ!Y7 z;aj6l-Nob1x^w%T^(XtB4TsXs#(bkwpV_RNnrk!?3TQ*sf<}E&iGB}C<GZiJO|QR5 z?Ad#1F3w8JwQ1Qh@kF+c-jVpRE3)nIlqODJ*Vc~Pn%s9*Q{i!KOB~d;pGP!Zdq&b9 zy0v{_NVdOh&>iy`$=uIL$BR1YoQ%lMn?|xDe(9PB>8_qnk~{iKyCZL<C+BFd?~(M? zztX;MZ?wOnsQq&fboa+e-Sha8=4bB7z~xg~$cKjy<o3!~xm@;CEL*(1KKEMg=khM{ zx4YNx^%@g%|L>-_vCqAOo=RiVS+jEKzI5WTCySrq-TXko#Nw@Xr|eD|<FPLm5AG`b z!yxVNC^JlCpnL&CMFxuu*VPUf88R|xWZ1~Sk)b1lM~06CfJA_VfW&|Vfkc6Xfy99X z;%Xy7LP26df<dA|!a?Fe0zx7}LPBCff<mG~!b0M5wSgg#x!TZ>*pT3m=#cP`_>cgR z2$2wx7?B{6D3LIcIFUe+NL_8HNUW|lSR`5`TqIs3U?gHBWE``z|8HXsWNhS}Ey=)d D1dR{C diff --git a/telegramer/include/pytz/zoneinfo/Europe/Simferopol b/telegramer/include/pytz/zoneinfo/Europe/Simferopol deleted file mode 100644 index 29107a07cb2e192f0c33889e726e6fbddd2cb867..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1469 zcmd_pO-Pe*0LStFbgQ|{{)bAJTQ1FBOwH7En$}};ifxv;^+NPwU`8MwDxyQsLs^H= z_*c@UqBk)T=~P`TdO@OS=`NjCBm@Z_;yLz$)c?CM4;`X&|A+1K_=rRF`-|>6)KMz_ zI8s01#R=&b_qnEi8+7;k9`w80UfX-)v5Am;b2n3s-U`UEi{*0cx_^AZ@;mW`S?`QY zXQCzZ+q|b)lNoV$B48|vU5aNvx?y>SE?C~Hr>w<2N35I^?Ut`|o3&(jgOwYuwDM|J zSW64tR=&q&E$c0|R=#zb1tVY0Rre>&!v2I=)E6_0p1+6}cb_p<AMI`_X+LPJ*>fXa zdU!yT)eg(@*2{9O_nNFIJuWMzPD+1vpYp%oFRQ*EQ`L_Ha$RDlsu|d)YKN=U`m=Rv z!xfJ*I`ULqca_}O;820~Y+0W_p@M-a+2DAk8uLEL#?PN*Q|dF>H2P9*`gmK0?hVRt z?1^eV-y=5<-Beq;x@1ewX|;97In~;EKt(F|n$fVg&6bin%kHo{QfL3VoN2$W6xSSm z@jT6$KKE}|hEs@~FZwSpa>j*S2#-(bCj})UU5E_HKgQ#;&7Xev8#ed!cc<@d-kG18 zx%V4<0-qCZj*5tW1%px190`elHV};PMZJEgZ#Xs^FOJZ6Gl%EVe~9RZKHDFTIFXo; zppdALu#mWrz>vt2>d^QNhz$u2i7u%Q4~Y*65Qz{85s48A5{VKC6NwWE6p0iG6^RuI z7Ks)K7l{`M7>O7OIjN2r2^xtS2^)zU37kXZNa#rHNbpH@^ho$gb^OQxkP#q5K*oR! z0vQD|3}hV0K#-9jLqW!Z3<enuGMuD(Jjj5O>JcGBLdJv)3K<nLEM#2Bz>tw4Lqo=f N|J~qRdZ$HYuAkb?OOXHo diff --git a/telegramer/include/pytz/zoneinfo/Europe/Skopje b/telegramer/include/pytz/zoneinfo/Europe/Skopje deleted file mode 100644 index 27de456f16ab549627b284a39e2265cbdb4ad8e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmdVaYfQ~?9LMqhL0H4S7@-o9T#oJ~DdMPHDwhrt$t4vMLdq?-nOU<hYs@g$HH>%= z9>mObA-Sy?W{kPcS{sI0<M;k=Hk(JD*!)lDyjrdG<oo`(Gv?)lS${mO%ujgptT1oB zZ@bQX+-w&4y!OplxqZw_>khf(&W;GVyCFdC9W0aksqxz7<tgp@;DC0!vR%E;Ul5-Y zmEya1zjQBC@msxKdgK>M&*^idSF&6DV-uveGfDz{0;NxzE)wYB(!kFV+V@p}_N(u# z{jass0aahsdE}iAEPt#)n{H|Fvhx~}eNsa+A4ynYm4wGtOT@&T66w27qQZ(Ls;N|> zy~{QF=`0!iy+~s&xMawabd9aZ(zxmv9lCkA4%_3S@j3oFeA8eVk?5hWY;PGE@J16{ zO_JzwLzDcUNm9dW8QuJnjIOJZF)t6x*vjLQTzgSdwv|chiGw<>pg_i#ZPW=<w(7+E zxtca8U){+`I>{?lCp-J;<S!wb-YHSiA9m2GpZiM2*-tvH-czO@XfK&nA7n=9N69L$ zlbKodGHcCyojvTF%*m|PY`@(yH?C51TA$HeU)9{VyELz<Q0LW@==`T{U2t@o=3ieT z3%A5+K}DJ@%Jb93n<Hh(gjgxe@sg#X-DO$AH(B1^lA_>FTGaSLiranB;=5O+q~VdS zJY6BH>Z*11?#sHSa-Xg(IijW8O4ZS#S#$g4(ehuuEURO*xhkujSS@~i`t)$LwyfVj z`E7EF+j1rFPIH?-a5(tlaX8$6al=2%Gb6Tf6mrYRJtH@b+%<CB$bBO>j@&u_nOjHh z9l3er?vdL^?jLCY=>TZ~=>cg1=>lm3=>us5>BQEwg7ktkgLH$mgY<(mgmi?og!F_o zg>;3qh4h6qhID3YT0?rXHO(R2A?+dkAq^rOA}u04B26M)B5fjlB8?)QBCR65+L~sO zZf#AwNWVzKNXJOaNY6;qNY_Z)NZ&}~NaslFNbk0$d8B(=(>~HavH{2rAX|X!0kR3m zE+E^0>;tk9$W9<zf$Rmc8OUzfn(aXL1KAK{N02Q+_5|4!WLJ=FLG}gN7-VOVtwHt% z*&JkdY|Zu{`(tZ12-zWIi;z7+HVN4!WSj7R-zQJATLVKGE@w)3P-IYuGbJ<xgTp<4 E0<)*Xw*UYD diff --git a/telegramer/include/pytz/zoneinfo/Europe/Sofia b/telegramer/include/pytz/zoneinfo/Europe/Sofia deleted file mode 100644 index 0e4d879332d21c93c229fc25587205020eeb3127..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2077 zcmb`He@xV69Dv^wP`nuXpdl(C7?5ZuzwuWP837}~PC1BF;!H#=B0ZHsirmyW*O>c6 ztmev1%du*XXfykRT0eBnHaf1AYpq-?TDGRvX3n+7>V3Yz+Nl2X^YVV5_ulV~zwUW_ zn|3zmnSae}d&6eB?B+Z#XdlB@*U9H^CTQrjQW@zjkno`l`67^>8n^9N@0{9_(Ye)k zh3_t0JR0x09-cS%qg@FxBjJRbgTeV@r-KW=><uowa5$Lw{@W($>?xD{>I-I3&mog? zV2?>{Z8D3u)S7#$%1v6)N|Wx%G#N<?%)K#gvm~l8xa5}GpZVi;@6xZX`IlWB@!oeX z<oBF;$Gg05(4TeWMQ`@rUVl#0K5uTrz-V4ze>lH*ND4N-B!$T*Wkp_xtQhZ>Rf&DN z>dIDG{b#$b`Jhzp|F&L>`Wv-)XpKH_bgh=WlB5qdr|H@wtL35cC@pPDl!r5}YFX*H zlt+E06=~l~#f_h&GU8*Y3|*2(#$K0o18+&y@P}G`yhqlbJ*|(nwTZXqux@zldEMB$ zU29ghs;{a-YZKOMooA`m-Okba_;RWLdX7GRD^oU||6Mm<cFUGianf+^nmo~cQyTk1 z;%~exTf2VICrjRwZ4JZPlyXA07Y}H2^t-wvS#`(HFKf$qKzCm1)Tgfa^yxP?YwM@G z<(Xr}+SXqq&$guKuA_yrdqa@~niFJC?jqS+_J{0?Gt!<NukFA6ARV!{wIh5{I&XZV z&kgp={?L#fIPr;g4V=`2ooBTBc(=M{MA+quoLEsar>)6*=k2WMJH3pF|IYsAOj4?e zG$vL|G-p?gG0SpXaZ~pb=YMXhs(q%c%x6lSUBd_aFvjJwfA%pkE|>4WfBJ6wp3NMz zoAbiI?9`nPrNh95vH1`cAUZ&lfM@|x1EPoh9|lDrnm|;6=mJp&q76hHhgKhmLJ*B0 zDnWE&P>Mk-2DKRUVo;1hGX~WdbYoDCK|2QZ81!>!6@+LAQ4yjeL`jI25H%rsLKKB) z3Q-lJD@0j{wh(n8`Z}}<Lo{}1RfgycQ5vE(L~V%P5XB*yLsW<84pAPWJw$zo{*VMf zGT_jr0FncTHVKd{K+*uo10)fUOh8fr$ps`CkZeHG0m%m>A&`tXv?+n)#Gy?JBrA}# zK=J}f3?ws<)If3rNe(1Ckn}+E14$4hLy#0fa^%n^36dp9njm?CBnpx#NU9*Yf+P!) zEl9c``GO=2k}*iiAUShrlLpC}Lz^~8-XMvCWDb%#NbVrXgOk~djEIbw5jC-9&YHF+ z@13{Nv+wkp>Rw_C-Lv(x-HR1tyJzbsPW|Gi?rrz%dE&`8sbA&)6mVJs?MJr_<?=iN V>8Z}oD$L5s&i7R3<~XMU_umlP+nN9X diff --git a/telegramer/include/pytz/zoneinfo/Europe/Stockholm b/telegramer/include/pytz/zoneinfo/Europe/Stockholm deleted file mode 100644 index f3e0c7f0f25f0a7290e56281c91190e3611498a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1909 zcmciCYfQ~?9LMqhQ3q?ZZ%F8dB$uPBax1r^8ai$r<dR&HTS7<?G0n`HeQk|d3}elV zcn}`sGS@LQo1K~4Y|P!vhPkcrd;c3A@Yrep|EryP_<eu8(-##aT7P|<<{KV9Ys|y% zZ8w@%O+?k~8sGi*?LDKUL((@5j(VdV+dtG0zgrse;hc7QdR#l-*{@wL?a<IOXC>@t zorLe%ClOUDk>#7DYhkf;n>kOqXL%(mHC=kRQY1PoMtZjCBr#66#(e6py`DvDZ(m34 zbETE`t^cB~L$9=7^?i-4yrFTc&S-r8F-^$5CyB-Nl9bjU{U_~|<nX<cl2|G!O%*aQ zv|0x~nj?e0m+0WLZW;0*M^kI_G_7H<4&5?Bht-7X@Pa5EQ8`FPW;oTIA1b4wUue3! zNiv+*H8bk5WWIYYqx~+(=*DX@=IKEhTX#gVZk|`q_9_{7^ni{pDv}9Rn|0#UZ91uN zzGe?7RBu+MP7WETQ(V1u%IA2^3C@t5yX|z^r(QDs)JL7+3y_)ngCw{9t<0+UAbHh| zGCR*FbJoAsxx-G&yxg0bAGurRr`2ge>yx@5Ty??AUAnNTSQlL@)5VXxy5#T-Exfuy zmTpbcqS|a(wlGqcZ%LLF6H}$QAVgLsM98Z2ud+JGl9IS!EqVV$N&`P@>Fvu>_U@jp zJy9#`8XL5H_eEV_w^uim9ny;J73yf=@bmxwKb9qL%~e@}V)<KESXW2uUvIw2@^~$G zI#0Hj|8h9&m-pW{+tU1zhfk?__&w-{`FMT%s<C|X%DKo5+nPJ(pSfk^o{^hI?i#sm zTXWyYjU#uC+&Xga$ju{nkK8_T|40K!2S^J@4@eV87f2gOAGW3uq!XkSq!*+aq#L9i zq#vXqq$8vyq$i{)q${K?q%T|37}A-oX$|QOX%6WQX%FcSX%OiUX%XoWX%guYX%p!a zX%y+y*0hTBYHONBx<%SW`b8Q>I!0PXdPbT?x<=YY`bHW@I=3~gBfZ<2=8^7^_L2UP z4M27P*#cw_kWD~#0oev*ACQeeb^_T7WG`&ZW+1zPYzML*$c7+0f@}%0C&;ECyMk;B zvM<QSAUlI>4YD`3W^<6;u{GO+><_X*$POV}gzOQrN!Ywgel7f+|NrOrFhwv-fnqfe sQyY7p%$skRr)+zk{!CQ!Mwxej8LoZ_ESJlZ6q_6y@A4$XV_Z_ePe)m?eE<Le diff --git a/telegramer/include/pytz/zoneinfo/Europe/Tallinn b/telegramer/include/pytz/zoneinfo/Europe/Tallinn deleted file mode 100644 index b5acca3cf51e7f7b3176965748688ff41720246f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2148 zcmeIye@xVM9LMp`foInx=Z7{ri8~=lK|SF3rPd(P6PR&lC@1-g=m(+}p#nKbjqi#% z*O<L4Hs&gS6r&$A<{xTn*7`B$C@;EySZmaBwX(71zFRqG8>`RrTlbgikGB5o^WFFR z`0kDk{^0eDZ`sverfxcO%_rQP{pRL<fn^@YHWww1y)SZnU|3G7rF%xYcSpxhpS}F~ z#o@-pl?MIA+kTmNJ)*ySsX`}vE?v$lcr$s&yl-m!o~cIv?{hz%H|<MiPep3(OuU@T z`QU^dIQ60(eDzs-{$RJAd!WtE>)2@DwXM!x5M6HPR~6cKhqCR3fo!#Cj#m}9E3JZS zi>$&Q{np}1kG15&4QuI{X)81`Wfcu2tm1(Wt&*NGE8KS6Ds8!>%DP5XdG))hV#8ro z8GJ!4E9=$EX8QGtoFS<iZI$Zt_sV^TR>{g&0#ehSFRKRb(W~p+5^2lPYZhLS+Q^Kq zbAKlF`QPdKt3T<r>7VGe6XW{+AKuUnWAEr_;v<P28Pw}eos<VUJ9XpWbF%)y=Ve33 zc4=DPA@OLvZ1k^_=Fnnkz8;oMS#^5TSGUXNYlV8tx!+{#q*re{k*QnG{GuP~|5djR zP3VVPC-ox-r{&R=@9D={64I7?RBx{ylXlNp*%1`k@$<0koavHX<9+h@w{dymt*z2= zaj$;zaJ6)fHtF3vb7jw=O1*b|mF{Zy>+aI|x~KMrekxPxeI;44&;DNb`mRfF@`CQW z`n5cLdQ|V9I4=i|ekupYUXy3~Mx_5pzqn?lrMuj-Z%I!}Pn+%e>$=tZ_jTKxo30F> z+n4d*TuS*X%zqlsSxN=+Tpp!-T4ki3fjpI|)RM5uN`1Sc#+9A=B=znJ@-07^`gvC{ z8jGvAxg)hrJmRX>+_9zxbFVS)=0l}iE`GOx<GiLGE?4|tjO7n=n$IN?$Y>nRaFFpJ z142fG3<((%GALwJ$gq%cAp_&DV`RwCkg++M!6BnVhKGy~86Yx3WQfQZkwGG(M23ls z6B#HnQe>zc#>!!^j%KvTaFOvM14c%S3>g_SGH7Jf$gq)da~L>>k#iV2hp}@QyrUUC zGJItGNC1!sAR$0vfCK@F0ulx!4oD!7NFbqbG_gQ};b@|Pgae5O5)dRJNJx;FAVEQ* zf`kQ$3lbP4GDv6~O>B_hIGX4n;X&eq1PF-`5+Wo<NRW^yAz?z|gaitS6cQ>VR!FcM zO|+13A@M>2hC~br84@!jXh_tMupx0n0*6En2^|tUBzTS{dPw*jP5h7mA`wJFh{O;H kA`(UXKaBqnMz0BJQ5gjd#mb8-i^C=5p;&3yd8_dL31K4)Bme*a diff --git a/telegramer/include/pytz/zoneinfo/Europe/Tirane b/telegramer/include/pytz/zoneinfo/Europe/Tirane deleted file mode 100644 index 0b86017d243f1b7bbb41d6b4feefcb2b7edfc7d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2084 zcmdVaZ%kEn9LMoPdKbGQeZlZ%fLMs2U4hF#MM1M0pb46CRs4e-6A=^XK*Yd5v6fkD z%zYo+S+1PA98;&{8vSwTtfmepGS|kMI?L6{XpQbx&YZIRz0V^z*Mpw4b<WOxo!#Bt zN59WIv}#jbj`h&xG2ifTy=5NW$L=|rSKqhgZKwa{Lb-Irr<cAM(&&uBNc8V>Y_F#+ z;=SB-W6aQEC#Gk<J@%W;k=XDw`>_-E9BGNM<McZxzH-<e=X~irKKrDdF#n`8e%vRv zFEAMK-5a<u!3sMQuGQF2_J15nJat-<j&)1&hx=t>r%KB9H)K*nvpltCy*ynWlGLIS znd~o+w4`*I67z(ldxDyND^D|iO4F%><8|7(NA;PWztn$dNT-LdYUa+1n$>bjvulrQ zp!$mBH1|kuaj)bp-6Q$Q`=lVJO$tUjWM*PmXI@_?g?C$Z*6E<kzE-70T{T+VJ4@&6 znx}I=NYZEPQgz<WLV2##qruulc|Pr?mIOzn)N?`0Qoon7;h$x}sIO$fK%czu<43Zv z`>2%npV5llowDfIL0#O~C@*%tsY_P8t4kX;XyuFs4V9PaGT$Oy?w_j5Z)a;&La9`J z8?P&GWyq?}{?yfjURiS>PO5u;leHaxNKJS^?3zJYx8qlRY3}E;zPev)Q})V+;%=>r z{!}+6t8V<|J*^*U)=how`ttRVZa%zP8_qY$mUoJ^v8z&EsZUX7SH3hYDU#+opS&8F zC@m#-<h3|UTC)<gHS&YB#opGo%V(v1_=;{l(IwjkdUgBWuXRWFK7GCYkaq0u(5OfL z=^i!uKf5g}{(VkrtXQKhD``?x^n>r^6(K8F!c!UIS5Z;!N9bRi{J+h`=|>iTtN>Yp zt62ko&mvsSDv)Kknsp!xK~{n+1z8KS7-Tiba**{P3qn?eED2c?vM5)xDr8x%W?jg_ zkd+}zL)L~Y4p|+tJY;>y0+AIWOGMU)ED~9zt63(pPGq6TN|B`^Yeg1|tQJ`=vR-7t z$cm9ABWp$$jjY<$EE`$3t64a*a%Abq+L6U0t4EfPtRE==QURm{NDYu8AXPxhfYia& z6auM)t0@Ii3#1rGHIQ;3^*{=OR0JsrQWK;oNL7%sAa!vyg+VIgYD$CD1}P3w9i%)+ zeUJhn6+%jc)CegOQYEBJNS%;EA(e79r9x_j6bq>qQZA%kNWqYbAtgg<h7=8{8d5f- zZb;#f%DI}-A+>Wg#Y3uxln<#NQb44FND1-(T|=)a<n#cE^jG9&=4WR6D+1Y=mFv9^ D;y&Xi diff --git a/telegramer/include/pytz/zoneinfo/Europe/Tiraspol b/telegramer/include/pytz/zoneinfo/Europe/Tiraspol deleted file mode 100644 index 5ee23fe0e59f044598675db44d53c20590b88934..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2390 zcmeIye@xVM9LMqZ#jg{Q&Nbq<8YYmQ{1n9!J;lQm&_fW(1g|7SK?@bb1pUl8M)PN4 z&59LkO=VMti%|+1l_@KNtFcu>>$^4kJ^ik7w#?P%`6brc`lJ5nzh1YyCwzBv&iQ^6 zty*7^CVxA}A5Zt|@^ie>A1_tC9P)a{NA@#wfApTY-r_y``F?Nr;7)H(Uz>66jTemb zC-xd|9<4Wen>HG6)s-1<S1vQ&DRCJW9!xdf&5ScHCf;RSitso3dYZlOJ=x&t|0>t< zL0kBtfmgB}gNJ80d`k~`!xE1B?vA+Z3bzk?!hgB5H{#n+U*vGME2_WQ7v0lp#+-b{ zjBVL%PT04_oLFCOPO4sE-m^N#jLVy4PM(u!-s_Asr^E!C@ndh9@!5GsLO_N}xDl@s zuZFAphQ9Ysy)fvR);ZvHzIxg-{YZy5X-~5!dFx?sN_nj(wY1$x+q}b<o^?uQEN_yG z*n=`NZG+6bT_c&%jVkj>q0GKjsqR0QArA~MQFD$JsH|?Mn%kJJ=DirIToqwz{+?;F zz<pC?myeT$Q$AJh?CX*f(5)6t{!kWO`$QJ|y(WtX&dQQ6o|WA87Rl>Bt@01-l%*$| z)v`@q@$9Qr1uN^-^6HhUa8{Wr%A2iLgu7I+Gg=kjj8i3HnNsqxzk29Kl&tFdQawEM ztE_JIlhV%5q-@t!S#zXUylXDWBMq0;qx0HiZRvScK5?%+mer~%?8nu**xhQ~*H5d; z+vRF~UzOT$B}Y}A$XC@D*UHAGWVPv-TOO|*ubya3keY%d*<3M3wxl{_tNS~tb^IXP zl7rMXb4cn!zfpC*F4=zVef3nwQQ0wYO4UE#sT$f3s-4@PQ@ak-DBB$Ye*S-b1&#@_ z2ieC4kGw+0{rL*i-wX`+?_MI&cKv@?qJ9#8k%&6czfDcCg^0vVlJTRTBTqsd62=o- z<mhLn%Qk`UOWf$^=#$YuAuqp3vh{m`e!Ja;eCP+(TmO<@xKO`y`3u-=BX8@6qJMFM zzv&fs_5DmaManr+PSUDAOUh|d&XaPYlryEAD&<@$Crde7%IQ+hmvX|CGp3v}<(w%e zO*w1IX;aRda^jRTr<^+f7@Rxh<SA!wRi8fP{3!%b7@$x<;ebK{g#`)?6dou<P?(@l zLE(Zz289i)nhpvdRy83MMktg}IH8b2VTD2qg%=7j6lN&YP`IIx!(fL&4}%}8nji*4 zRy9QojwmECSfbEG;fX>Ng((VE6s{O#QP^V8Md6D<7=<ybnlc7wRyAo1))=%gc%u-< zV2(l^gF6a&6!s|eQTU?}NMX>brjWv+RZSv=MFx!&9vMV3m}F4N;F3WmgG~yZ3_dA@ zG8m;$O5xP1CY8b}g;ol$6k;jNGN`3+%OID+E`weMzYKyI3^OQZaBNkR%wXB7rkTMr qg=hxT6sjp)Q^=<Ov;B|Q4%XkAo(A*I{Pd)Zq!ed<Y6?wG5B?oV8;IQi diff --git a/telegramer/include/pytz/zoneinfo/Europe/Ulyanovsk b/telegramer/include/pytz/zoneinfo/Europe/Ulyanovsk deleted file mode 100644 index 7b61bdc522b5b7f4397fdb9246185f4d972f4b6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1267 zcmdVZPe{{I0KoD0%gwE=LkDY1H?wT6O;^9>CbOokrVeX@#PDDR(jOrRdV~jqz(MAz zf+C10qG)vK@1agvj_4Apr*0PfAUaeK?-GGh>wSOIQ<pmSetW;qkH=%M-}|mGd}1^% z{upcY3X_r5ljpiSqO<s{<Q-fWzFuDMpErH(xr}eoc;f#e${QAvX8r8>a>Mi$EAZf) z6}&lSHC{MwZ9F?<HJ!{^n+~O{&H0GcoUXUFw0W&iz@u8cODgOP%kX!<-1^B^jEv6| zBkvYv^z~QS`t-eQE6vH+)t7SH<YT#g?6x$IU6wlzPpkOpYpT8LmP#Brqmsd*>WCjy z9Y2q#RKvJTedtl0OT%*Kix#zOAuZGQva;)WqwJmv$=x@E%#6C^p2>jP+xuB&kN#3U zp|`R(YpFixLz!!SrE=fisQ#Lg>Yu-__I;dE`yX9Y`PsX2;L<5o$OlJ;e$>f{N~L1d ztg2oP=kitSs&%<n>)YR44wu6rL~KOARuMIYe(oDI+(M)>yz1(GWyR1d)jd(u&^rT7 zVl8`EXJ>w(AX?3KJ(GGS^wbAx=+E-td1Vy-;kfm$tZ?MWvGW}qJ#zd=0=7B>Bn2b~ zBnc!7Bn>1FBoQPNBo!nVBpD<dBpoClBq1atTb&Y;6Oxpz&I(Bj$qPvg$qY#i$qh*k z$qq>m$qz{o$q-4=R_BN$X{)nD(nRt^5=AmaQblq_l0~vb(naz`5=JscQbuw{lD5@Z kBWc^}yphC_%#qZQ+>zvw>~TW3@SmpdN$WpHcP!!g4SK2@ng9R* diff --git a/telegramer/include/pytz/zoneinfo/Europe/Uzhgorod b/telegramer/include/pytz/zoneinfo/Europe/Uzhgorod deleted file mode 100644 index 0eb21501d9241268b7e5593c81bd02272b77c9cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2066 zcmeIyZ%kEn9LMo<QtEm$`qHEearskZ{^<&re~Jc)UN6kxn#$EkC9)9zL?mboO5?1Y zYmMBuVl`J5mZLv&%m-8t$f?aWS~t2qV6Bm>)k?=2J6kztjn(gc)OygPHXr&sJNI>V zcDMWFzCZB|+gpmQ|D2o58*WaQxp~fU%*V)#c1@hEl`r29=;+%K`Re6TNgjUlhp&I0 za?|I$o4hsSyQ+Y1e0AWDn@?s;q$V@RBUQI0UP)$s((eY3zT{@V@vNKEyUWep)9mK8 zu5)kSRPEkTx5&+}nCspd%5-N3{q9{a7PxcHFYli>=5ywszUbUNG~tAfj5`bZ5>COs z<4)o3K_}e&mQ&RDg;m@!Bqf!@Qo8n4Da$@23yZsC;Z%>5XZ30Mna#5J&rV%(JR<ju zH)zE`lU5Ed(R=qV)1|KmwW=jwm+f0D%d1l~(wrqLW}nq)WJ;=2KG&N3@1<t)7g;&& zl&l;bk^6pnM`{N@kh;VPjUDKfRYwo&{cUZsy7xI<^S}$bwsnivFKX3zU5%~_tkU(N zdAj~WSR2x-rQw@f^uhCUWy43m>&7v^Z0b*w#-U&3p`PEQsc%#sZW@!#dnfdfr60?q zjR|eeJt$i$2erlbp>EAq-Fogd-8R*s+ef-}$C<c3_TENq{j6Ocf4)-N2I}RBZMnL0 zf0?wesgRDAfb1&Dk=@aY@?@GNorUSz>Ha8PsTZ^>d0M(BztyLX4an1@!@B3-r@D9W zkUrCWM0*bOsD0fubJ;1=FJ0H)@XuxYX812(H_p83mu1a5XZ~7Ns#w!4E0|~dt~{4l z+2)@&KWwF4`MmTdm}jN)#?IvXJpcQqE*7^UQSSP<6{}xi?piBqUPO6eS+?!nwmJOh z<zw6N|53tU=Iod}mVp$3RDqO%)PWR&RDzVkk5UU#3{s7!DF>+sDF~?uDG8|wDGI3y zDGR9!DGaF$DGjL&DGsR)DG#X+DG;d;DG{j=DH5p?DHExaL!n5eNU2DzNU@%#TBKZ0 zQ!i35QZZ68QZrICQZ-UGQa4gKQaMsOQae&SQaw_>r>P%V08g_5$PyrHfGh&C3dk}b z>wqi-vJ%KrAZvjv2C^E+a(J5cKo-Q)tO&9s$eJLFf~*R%EXcYb3xli-vNXurAd7>n z4zfJR`XCGBX;uhXB4mw_MM72yStewikcC243Rx;-t&qh+Rts4!WWA6D^E4}lESaZS tGi1?_RYR5ySvO?ikd?!0OXoLhxS*Ih48=+c$_m1TC81bR*n2AV{|%Yy?`r@6 diff --git a/telegramer/include/pytz/zoneinfo/Europe/Vaduz b/telegramer/include/pytz/zoneinfo/Europe/Vaduz deleted file mode 100644 index ad6cf59281a1046d9dcd045fda521585e3e33e06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1909 zcmciCX-L$09LMqhq+=x|UkjT`&1!PZnmp6((5_jPcE=8#Ej!E(waeVJGVQV`Btqi5 zAVpMc%Z5ah^}y<Z9c&jJCJUQH7eUcw5kZ9=Nd4abC5WxZ{r}9ohV0?@{qfIST%2Tm z^*GJH@Zni)KK$;!(R^KTEwQfLFSD+;`>f`(xmK9_nfB^=M_mEe)b;AL_I_|g`~164 z`=0w<!%v=)h(iq$x#th*SE~}WZj<ycDVG7W7sx=LU)*UKGRTuE(GfB7L$}@%<Me9G zo8db6VYJ4!_R=92I_uEJx9ZvdREO2w(zq>GHGbtuO(;C9iTO7rsk~8=)0<>?&JIb5 z+$*U`m6F;~EhEC~bj00xGV()(jymO)(YNz7t-e6hn?~uFn(;bzcZ7~BcI)^pBV|IS zQ@w@Z@>BF<&G2?ert`99x$jBVi$^js;BT4Oa!G!E@R$73a8P{BXEb|ztxP)fr%o;{ zl_|BGb?WqOnp0Awxj&Yu-<PGox+du~PpnRBPtd%uOv$^^Lub4hEHjV4)>*B=GJ9XB z<TpN-In}SEpsq#c7PQK|^=&$T><L+r->ijEyQC<+L5sT_(}j_$3!m)NMIGh3_)?WF zx$D=Z2WDx>#WGp8HC;>VbLF>1QM$Y)Marh8NqMnLRwVY5l^O43Rj4Hu@nKr=^1f7t zv}@%*=cVe!O<i-eUe>lW>AGEKb$!EL-B7h(tG8EcCx>|h0>AfbSzXLgSyn`UN1$be zh}HGW-@a_W<;}?D%g_IEIP5R~w{JGc{E-h&rTOqX^rLwOy=>cvW!HmhkQ=r&cZ}RJ za?d>6G;-I-ZQGjrMs6IrbL7^Mdq-{_xqIaHk^4s)KsrELKzcx$K)OKMK>DyXjUb&M ztsuQ1%^=+%?I8Ui4Iv#NEg?N2O(9(&Z6STxn#PdMY)xxOZ%A`UcSw6ke@KH!he(S^ zk4Te9mq?pPpGc!fr?#e5q*q(hEYdB~F48a3Fw!y7GSV~BG}1NFHqtlJIMTVTX&vd^ z)-;cFkF<~Uk8A+41IQL2dw^^LvJ1#IAp3x91hNyzRv>#}Yc>Pf4P-lz{XjMZ*%4$* zkUc>*1=$s3TabN0HU`-lWNVPUu{E26?2fJ39%O%z4MKJZ*&<|*kWE5%$q~@Wyn)W| z{eB*%p!b#;CNocFr$WT){^f7xX~O>|>c5RL-@#_Hh9$CIp6ukfl(+;>c47j?CkKB5 Dj=i{v diff --git a/telegramer/include/pytz/zoneinfo/Europe/Vatican b/telegramer/include/pytz/zoneinfo/Europe/Vatican deleted file mode 100644 index ac4c16342b5bbfa4c58a26f57db33b95f5b3e533..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2641 zcmciDdrVe!9LMp8n;;^6(G=q%7MYlcAfRBPDFP!OEa6Gb8+b!4q9ZA3sc4xi$K1Cx zokJ5ZxiqC#m}_dS(nbTxFf1x3%S?;r&Sp*t(SGm47_<7@bM~Cq;W+>Ny+5AmiwlRl z{&V@8FZ{Unn;-8z*O*5|$_=Zcv95Xh$y$5I5m&+6uivtERz@^e7QAEcT79Ts_so&j z9(PzlO;NI4cWI)W+8?U*yVK>HH<RRa@ofFJq^~r%8|*V<67;+525F4EZk-*x)jr#@ zMt|tOMOqUN+pSF}n%g28thP_8?VC5Nt@dqO>{}O4+qYl-$a0<h(Ds`9mgT*Dy5%$e z1<Uu<QrmCHCc9hlCd<FaGCLq+lpWAA)eelCY6aG&+CjmoR?vIlcF$j%?cfdV_C2TC zEm<07g&aC>^~$?x_0E|f_a^?WeWF{mZ||Qq)aR6jUj0=2U3g#bJ5#A)U%sK?$Bs+H z!77Q|zEk>_t3<6_D+7v3<o=oS<N>!wqDN=QgRvuIV8kGKD5#$d@=Mo2*OD~m;y@kT z*jFDu>90eoZ)oh^-*xEbA2n`WtqxmyOylPt(u7$}GQ6Z(64Q=KQtlQ>j@%(5hA)>9 z?PZb@zFAYw&5_i$QXO?XT^{*qnvUL=uW3g|>6rE7bZkY0K3W*9<JP6h_)I@dpC2w0 z242#P^mfVg`&zT2&r8;o-z3}TbIGo+k;g9Vk%?6Y#9jB9=4>pNNe6f7<Hg1DMENT^ zdFlquEn1*?Ba77I&eA7CC+U>f!8+x7yiV(rDbv31rB7Xpk?9}*r861>WoBiF%&PuV zW|v)+{LS@Jkl!eCR{f!K#~zV+v+8ty)HYd=R;7gjhjn43>cY!gby0hXF0NUjPoMMX zGw;sOqOX?7v#+IT@xDBHZc&t4>yu^4<k3=67%I;v^p~X>t+Fh{C8fjqXsP{+EDye} z%TJ$>6<3<{#Siw$OZ7)}<+d+$Rn-n%y<)GHZ7fr7uddt2*W6y-Jk8x{$6t3m{kq-# z+vVy}ZO)S`Vt|*g%M~oH?w!w$FJ0f=IUZMfMjj6j|HI2%XkI-3e|iJVKl0-`V1B%Z z+&0&kn9FXoj;*zj)9h$YG;*qulZ~8i<b)%q969O8X-7^xa_W(jkDPv_07wOp5+F4| zihxuBDFadmq!5m#5=beKS~!|wAk}a*<v{9z6a<}$@IpzrOih5IAXP!ig46{m3{n}S zG)Qe6O>vOwIGXYx^>H)>LMnum2&oZLB&146nUFdmg+eNYlnSYpqbU|rEu>sXy^w+- z6+=pf)C?&aQZ=M(NZpXaA(cZ)htv)!o};NAQa+@9NCA-wA|*s>h!hd2B2q@Ajz}Sq zN+P91YKaun(Nq&Dr=zJSQc$F#NJ){JB1J{2ij)<pD^ggbvPfx>+9Jh8s*9A@(bN|y zu%oFkQevdWNRg2$BV|VFj1(HFG*W7$)=06DY9r-#H1$Rb?r18GlpLu!Qgo#1NZFCP zBZWsQkCYy%JyLw6`bhba`XdYAXjTAO0%Q%4ML<>oSq5YskcB{20$B=VEs(`PRs&fM zWId1taWpG}EQzC86J$}4RY8^oSr=qskd;A}#>>aM>-P0Cx3>>Zb9dVD*B#Gp{&)ZG zoEkGYW@^l^m^}y<SI^F8$Cs|}3{LL9MyG3a%v+#YqM-?FQfy9QTyk7|Y)(Qv4oeLD E2jBk!N&o-= diff --git a/telegramer/include/pytz/zoneinfo/Europe/Vienna b/telegramer/include/pytz/zoneinfo/Europe/Vienna deleted file mode 100644 index 3582bb15cd7322088839b0134987ad10e717b6b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2200 zcmdtie@xVM9LMo5ASul3Z3buj5bY$SPJTmTklJ}Lg;SBsNF*>2wFu=h2Bn=pm~+Y8 zTgP+`Nkhh}>FFO>YiO;kMhgjNmtncKoWH6$Hgnb*tIzZMuj`Ng>5o3&eZP<IZo5Bj zyVujVa(#W4b<Y`ZKH=u<HaGA0Z#9q3iH@4i(52BsBSG!zIi+uXsCu~VfOc<;lcNhO zbs(xq2d|gNxpy5p)a{n9_vOg&_GCHlkCpQQk6fsY*KbOk8p=N_-=_A-MOU1B7qwre zcvD6n${wzXasHyQQ+`lK#5r}`{z#`@IiU|<IHGZ9_iFs<Gm>yDD2ay-%d~ctq|Mu8 zdV^nPtg4kqN`2xi@W@P8o+Kxv$fFadN=l?#Q|{#GtgFeI8j97&J|C}-_x-M}o|`(m z<C3PeeWmG}PHRTpam}n6kt~0oWEb{J&hq_|o7g3JSuK(`7LYmd9XjXoYRUhzS?8W{ z%ah+%XhCna7WU88r*<yTd7TM5zuu_}+VbV;qDXbu#mmCv>*{fjNm1mNTI~E$if>($ zMG>FKqM-p<eC4Q=1mBa=!H?Cur(KpDds~+_Hp;U09a{G6o4UMVjg~*rpuW;#ea5jw zSGZDj#oY|8h$)haix28^cV@}T_kYvMP_(Q%GFhtnewF6~x23vcNNTD>vU<zU`og>q zq_%2M>yi%2n!=#gPdKG(6IIvVIH2ps{JMUiRbRa9)0f_<)P^sb<mFw3+Spqz8`dT1 z#+|v+R8}DVdWXD{IZZZs{*YHEThg2!qs^l~Nz0_W+A{o^wB8!g*G~4z>qGsz`QWFz zCD^5JwDxFVPe8-N!Xw7rdxeEZ-uGW$mi0iH`R7^*)5FGD)+Di_{^`Bc>$BXRavnau z5oQjW7vI0w$zSGd=&nvj_F`)`gX{*`4zeF)L&%PhEg^eCHihg8*%q=dWMjzAY|Yk? zy&;=Jc86>a*&nh&eknUdwutNz*(9<{TeD4MpU6g$og!OB_KIv4*)6hNWWUIUksTvj zM)r(s8re0nZCkT%WaG%rk*y<pM>db_9@##!f209O2apyZJwTd(bOC9Dt?2{O2wT$$ zq!mankY*s=K-z)y18E4-5u_zZPmrb{T|wG{^aW{*t?3NX8e7vFq&Y};koF+`K^lZ~ z2x$@0Bcw@4myk9geL@;#YdVFr%GUG>X%^Bgq+LkAkcJ^0Lt2LP3~3tDHKc7w-;l;3 zokLn@YkG$?59uD#KBRw01Cb6QEkt^VG!f|{(nh3@NF$L>BCWJFy+oR6Yr2WF6X_?? kP^6<sOOc-9|FNmjCbP3M39ieVotB%H;qqo?V0w1+--?$-F#rGn diff --git a/telegramer/include/pytz/zoneinfo/Europe/Vilnius b/telegramer/include/pytz/zoneinfo/Europe/Vilnius deleted file mode 100644 index 7abd63fa608e0186b9f154d9fcc32472c28f6759..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2162 zcmeIyZ%kEn9LMqB4bdCX^^1iU0z@JK3E}cjZIi_KHw6^sihoHfL@Xjjb5I&*rOq|x zz7<=!bop=eXT*G9_8?n-<{H%v?-SM<wOXxgthv+5IonwM-Up~vkKB6L@9gk8I~N!a z_`Jg#ceECnf1O$O4L7ID-aKbH_RFzV=?y0ju6XIx&ms48YuDDHflE6-*^nEJT&|VR zUJvT<sS3IDe2GLuW#1%bzZ#vL^ksR_KiUxdZRYz)V}WS$Xhr!Qkr$#V7f!gT1JAi> z$DVfQ^zL@k_qDil+t<1oo2%VB>lV40OLE-1@{-+osmUfQKF(yvmO9y2vz(mogU<XB zzmt3Mx^wrrF(>cLsI#Ci;^ZHA*D2UD=!9BcaSEG1HAS8Mrnu})Q?ll;DNQ>r3yZpB z;dqZMPU+Ko4=vNBFQ;mGYo;zcaF5(q9jg^BDN;G_vQ|}$OLgqWT9f&;)Li*NmdAV` z%ZG<##doKrcJK|Ui@c}xM|x%Dz)4-z(IE}J&*<uP&+3}?ZQ8h~UBh)Xx;D5{oATys z)3uPUPpp>ppU=__S94_J`CoO@NStgwks!_Iev<opevvJG!?JbDh&-@=OdnkOwmj4v z(U$b1vaM`TTm5h8_B7S)Kfb7K<DI%=s9PWYDy)yZwn^JR+9i)3F4K<wMtQ6)U3VTT zm0hcsNM~zMb{Ec(JyqA`@dP7#3lep&`>k{Zu4z~FqI6&RLZ3L>FHa7?qx+72sQU+x z>r>rlwC6~V`fiGu78C2AK4bcf*qbM=xLYQ#_*?&Z1!e{$;Xk>I30|=OTIRO1W|}dx z+l=3sfS7n=Qs){mCO2em|Lct}iT8Y6T<%Mo`gH2qmofEI6W{aq-{Z@us}GxX_O7*e zMOD~T*}J~6);>4#P-$P-hl%6!8RMHgldt~##ODj&z;b@Kr|Ep85?K(kB4kO(nvg{y zt3sBAtP5EfvNB|8{8_9GSsb!DPrE#1eaHfl6(UPS)`%<;StYVeWSz)Dk(DA#Mb?Te zmcwc}EZ5Vn7g;c}Vr0q4nvq2#t45ZMtQ%Q4vT_bf=dgATi|4R<Wci+U{YU|j3Lqsw zYJe00sRB|4qz*_SkV+t>@U*o+is5Oifs_NO2T~BEB1lP)njl3%s)CdSsS8pVq%ufp zJZ)`|;&|HXAmu^ogA@p<5K<zfMo5v6Dj{V;>Vy;usT5Kwq*h3=JZ-g*av}9X3Wih+ zDH&2Tq-aRhkg_3lLkfpf4k;Z{JEVA?wt7hUJZ=4u0wNVeN{G}DDI!uu{J)GhTE;jV a)dfX_G_SrmzcfEoP@Gp^81kM<;{E^_i2_Uj diff --git a/telegramer/include/pytz/zoneinfo/Europe/Volgograd b/telegramer/include/pytz/zoneinfo/Europe/Volgograd deleted file mode 100644 index 11739ac271da2b623bef49ea64908820b7ca05fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1165 zcmd7QPe_w-9LMozxiy&`x|B0F`(ty>KeaX|tEMfd8`cDg(Lswwe}o|DOL!;<3^Gp@ z6hTB0MWai9i8^I5qD!QnylL=2hv+Zgi<hL<@BP>*g6P)s@a*;Qu#K_LyD)fUIA;Cv zsQ&e|+sOU$Tl3kur=^;K72&JpmHz2yZS_>T_M7XG|D#o|n@vPlO`a{+Ph7GC_s-bC z>mzo<xx@DAQ+w<+M|1Yty-9mrK4LefHt71+It>MU+ElZkVNY1X^L~lUd@eS>`Xnt+ z=A^YWC2f~q$i}gUvT5X|L=Rn*&HE-acKnLQJ8x+F?vt7bp4N`okam1Opvn3Zl6>2v zTNVao>+>evHk*>vos4uoYmlz-kZiwZNqX2TJH`Uq-TOf@2Y+Z!=#BJdY|VP^ORn*y z=DxnxzN(V;P2bg>@5Xi4gY%l7ye<6~j%gtuEDp_l(f#iq7e_0Vifc*L;_3D{=Ta}H zdy&7ry1j0%*Rmod{@v!N<+iM3n*TRD;9B<ky~{l3J^B8E)e?=HtKD1)K5DtlQTT`T z%nz@yAhIIg!IH?D$fAyBRb*LYU1VWoWn^h&ZDes|b!2&DeWU=S0;B|_2BZk23P)20 zQU_89QVCKDQVUWHQVmiLQV&uPQV~)TQWH{?qp1oh%hA+@6oyoWl!nxX6o*uYl!w%Z z6o^!al!(-b6p2)cl<8>dL<)5@l_I4gwIanL)gt90^&$o1sA=H;so0k?ZFjZBeLn&7 C+!|T{ diff --git a/telegramer/include/pytz/zoneinfo/Europe/Warsaw b/telegramer/include/pytz/zoneinfo/Europe/Warsaw deleted file mode 100644 index e33cf67171da78aa9e6eb02e50f9b9603da4c3f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2654 zcmeIzYfP499LMnskW^Coqaw;dEFzImK@b%oGeT#8sXQv;2@Oz-3=a{S#64xMnREXf z;t(k<WagCELR+G9cA70CJdk0dR<1mZOhdBPm|fr7wboi&z35%nv*-TYJmc;4{dsbh z7mPN4Id|J%_;U8zFYm#QeN^p>ZmI4Qlv~|;;rgz&dabEFq_4cA`fB+O-M#a$*^__F z)RnH!Jz4yvoVt`QpS%&I&99}(r`r;wrTmCFy?tBJnaxePXP4YAI+q@ytqTvzXTuxi zd`z%>-n&xTyiaJ`l@ht^Ib$y0XmqZ8z1O*Vy3*+wP-!}jyk)-MUu&-I+-`n2mt(H4 zd(PZwOg2B}%r%`AL(I*j38t$w$@wWb%=E3D<@mqU$J|ps){!+)PC(vZr=Q2q>7SWs z2E=zbff4P_!2Zonkk4@^sI$Qtba9U}`0O?(_`Quz$k8`V=z-lPY}d<X$d(NzeEn)O zv@p-yJAanBFT*1d$!T(bOrk`F4wDD^43c5KsXDAHL8HEn)ZwiIHTr{|`e5VF8dG~k zM^s$Uk>wxjs5M75Ht(RuEohU`s~ROfrAZQIR?3*L8c7^oB8lB)GA^V-$F(n(@xK;p z(y>&T@I|&J*DcbNrX+o6(<Gf(9jXr(MChdQ@$$%IUro&mkw+shX<BNxO!oaqr$l@u zQ?7j{Q+-az)aH}&*u~c+y?(!BoI0+VTerxx1AFxG6)R-=mW?`N&Ssrin6Fu53)Pb` zMP~&~)7de@b@r`T%?_L_+2{M|+^#6eIeb&+wff2Y-2t+ou|pP?b;_cOX31UDDvL|6 z>XM0X%M%MuX<m4h<fqhYL9au)G)#5r<sG`LdzCIfxmKTS_vlmm=4s)FE9L1IQ*=dL zmOQg8T#Gi1k(D!&WmQ3ttd1KZYtnv^X9J8Bj|$Y{>)%L;|1B+Pd0*CEYtwZNb@F_3 zldi9NS4-<_^o6yxTDG-Jy?nfVdieI}byrUxZ(sXz=TF}L;itFXfB!M2e}la{JbM@u zI@GI|G5%uu{`oyR)+>Nt%)mdMzyD`OrpL^&-_*1$9v+j%OPYP*c-dng?)#m;J^$ib z-?nG=;g;#h^+v9^tG(vPRY$Hna^;b0k6eA^`XdQIGJvE2$pMlCBnwCykUSuXKr(@( z0?7rE3?v&!I*@$0+Jqn(akVKya)KlU$%?B@3z8QkF-T^R)F8P*l7nOiNe_}ABtaZ9 zgrtZ=j*uiFSwhl;<cUL~kW3+|LUQG5lZ9jpNf(kYBw-vf#vx@Ka>gNP9J0nCZ5;B3 zBo4_Ok~$=JNb+25_K@@;`9l(jWDrRql0zhkNEVSaB6&m-iDVK<C6Y@dnXWdQNIG3@ zK9PhX8AVcx<P=FNl2s(FNM4b|BAG=}i{utbE|Og&y{<ODNP=B$hLIE_IYyF<WEn{_ zl4m5*NT!igBe_PBjbs~1x2w%Jl5kg>aU|tP&XJ@eSx3^2<Q+*ol6fTcNbZs3BiTpN zkK`Yj09QK$$P^%RfJ_213&=De^MFhQG84#DAaj9C1~MDSbRhGAOo*$U5oAhS?VKQ! tg3JmsEy%ne6NAhQ{y#N;J2ifGjz+{WOfi}9Bgc%4jmeCQ#ZmEozX3dW2`vBs diff --git a/telegramer/include/pytz/zoneinfo/Europe/Zagreb b/telegramer/include/pytz/zoneinfo/Europe/Zagreb deleted file mode 100644 index 27de456f16ab549627b284a39e2265cbdb4ad8e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 zcmdVaYfQ~?9LMqhL0H4S7@-o9T#oJ~DdMPHDwhrt$t4vMLdq?-nOU<hYs@g$HH>%= z9>mObA-Sy?W{kPcS{sI0<M;k=Hk(JD*!)lDyjrdG<oo`(Gv?)lS${mO%ujgptT1oB zZ@bQX+-w&4y!OplxqZw_>khf(&W;GVyCFdC9W0aksqxz7<tgp@;DC0!vR%E;Ul5-Y zmEya1zjQBC@msxKdgK>M&*^idSF&6DV-uveGfDz{0;NxzE)wYB(!kFV+V@p}_N(u# z{jass0aahsdE}iAEPt#)n{H|Fvhx~}eNsa+A4ynYm4wGtOT@&T66w27qQZ(Ls;N|> zy~{QF=`0!iy+~s&xMawabd9aZ(zxmv9lCkA4%_3S@j3oFeA8eVk?5hWY;PGE@J16{ zO_JzwLzDcUNm9dW8QuJnjIOJZF)t6x*vjLQTzgSdwv|chiGw<>pg_i#ZPW=<w(7+E zxtca8U){+`I>{?lCp-J;<S!wb-YHSiA9m2GpZiM2*-tvH-czO@XfK&nA7n=9N69L$ zlbKodGHcCyojvTF%*m|PY`@(yH?C51TA$HeU)9{VyELz<Q0LW@==`T{U2t@o=3ieT z3%A5+K}DJ@%Jb93n<Hh(gjgxe@sg#X-DO$AH(B1^lA_>FTGaSLiranB;=5O+q~VdS zJY6BH>Z*11?#sHSa-Xg(IijW8O4ZS#S#$g4(ehuuEURO*xhkujSS@~i`t)$LwyfVj z`E7EF+j1rFPIH?-a5(tlaX8$6al=2%Gb6Tf6mrYRJtH@b+%<CB$bBO>j@&u_nOjHh z9l3er?vdL^?jLCY=>TZ~=>cg1=>lm3=>us5>BQEwg7ktkgLH$mgY<(mgmi?og!F_o zg>;3qh4h6qhID3YT0?rXHO(R2A?+dkAq^rOA}u04B26M)B5fjlB8?)QBCR65+L~sO zZf#AwNWVzKNXJOaNY6;qNY_Z)NZ&}~NaslFNbk0$d8B(=(>~HavH{2rAX|X!0kR3m zE+E^0>;tk9$W9<zf$Rmc8OUzfn(aXL1KAK{N02Q+_5|4!WLJ=FLG}gN7-VOVtwHt% z*&JkdY|Zu{`(tZ12-zWIi;z7+HVN4!WSj7R-zQJATLVKGE@w)3P-IYuGbJ<xgTp<4 E0<)*Xw*UYD diff --git a/telegramer/include/pytz/zoneinfo/Europe/Zaporozhye b/telegramer/include/pytz/zoneinfo/Europe/Zaporozhye deleted file mode 100644 index f0406c12a6b519347d6a7c091424da9d0f241236..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2138 zcmeIye@xVM9LMqRSD@&u^Q{{K0Um`0>IugoiUx_Dzzi^ylSn1B5VeR9je%+0%DL8< zd$(B4mCKS*F~|A??GIx8a?R?x-9OYCxwdN2Sh??3&djm;Jim5ZTkDUu{_OkR_xt$X z_XhmK>lxm*uRUn}?U-lYaB&<m7tf0$<~EXfI&gL<bLS@`)A8tZt$g-oy2f4$No?Zt zuC)Bu;|nvss!UIvYDoWW-g_A{-gxFzsPfk6^YN^APC40Uo^x_ue#%)iaKP~$ZgX-w zwmP@%sCI6zTkYhP7dVUkna+}Ik8{V|YfgSjepmkGjIM(3eri}cnHpJk{%U0T*i6Jf zG!<Df7>yJjorn}29E}9pUWpX9d};-|$E2iUTvl#*K}vH@%Bo<mteWkYvaCTZySPi% zT<g)b6Ct^Cs#(j2TeV_*t=@HPy{>yHTPxf1bp6paa(8u#hT5`Z!;)#O3e8G&%Ewxh z_qEhq`d&6By)PSM7v!FAPfP9Sh}1>j)%xQDvgypLdT(c^Gz>hgo9}y8w{+~*#?>7f zuB*|l>6^64zf_y91hhG=TAIIDp!Z)ckZtFF(e0BS*>P&Vw2b{I5A^>mt%EVy**Yn^ zj?CzT>)w`!TB6$KJ0ZI(MzuZltnSHC-SfkXx_7o)_g(1IhcAZpkvFz$$4C3+(Pt{O zbGT6++w0S=W2LfxbGdZ4r^|ujMRKs}sysg5lAfY8?Qy=5Uhfs{jh~mkOJC}tw}$13 z*ti}(@qr#0J*iLj4Qc=JezoT$-H@D;oOI*$GxeswoSr#zZ~pVV#rvP1mX-Uf`6sh- z<5sd|W#?KR%UTw&yq4t^YXNWUg^7DvrJa@dHt}rxc*Fhr&nA9@?W?N~TgwB%V8E)X z3S0G!wN}U&s%qrJ2J_N9aM-qc+2(LRCvLVK{ufL6-5gElK1@XxgRBNw4zeC(LCA_+ z&64=5tO;2ZvMN`zEM#5C!jP3AOGDO%EDl*6vOHvc$O4fSB1=Tph%6FWC9+Icvrc58 z$V!o=B5Oq!i>ww|F0x)^!N`g^STeF^WYNf~k!8D@bt4ORH7iG!j;tM7JhFOZ`N;Z_ z0w5JYN`TY=DFRXjqzp(MkV3ecN+6|hHMKyBfm8!22T~8DAV@`!k{~rfih@)HDGO2; zq%f|gGDvA$O>L0kAk{(2gVYBp5K<weL`aR0A|X{m%7oMjDHKvEq*Sh^R!FgsY9Zx9 z>V*^xsTfi+q-IFbkg6eNL+XYU4yhbcI#*LWq<F5TdPw<@`XL2GDu|R2sUiMf#D6NH b$E0*ckaYU%OA1R114SkN`r?55w9@kj+K>BQ diff --git a/telegramer/include/pytz/zoneinfo/Europe/Zurich b/telegramer/include/pytz/zoneinfo/Europe/Zurich deleted file mode 100644 index ad6cf59281a1046d9dcd045fda521585e3e33e06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1909 zcmciCX-L$09LMqhq+=x|UkjT`&1!PZnmp6((5_jPcE=8#Ej!E(waeVJGVQV`Btqi5 zAVpMc%Z5ah^}y<Z9c&jJCJUQH7eUcw5kZ9=Nd4abC5WxZ{r}9ohV0?@{qfIST%2Tm z^*GJH@Zni)KK$;!(R^KTEwQfLFSD+;`>f`(xmK9_nfB^=M_mEe)b;AL_I_|g`~164 z`=0w<!%v=)h(iq$x#th*SE~}WZj<ycDVG7W7sx=LU)*UKGRTuE(GfB7L$}@%<Me9G zo8db6VYJ4!_R=92I_uEJx9ZvdREO2w(zq>GHGbtuO(;C9iTO7rsk~8=)0<>?&JIb5 z+$*U`m6F;~EhEC~bj00xGV()(jymO)(YNz7t-e6hn?~uFn(;bzcZ7~BcI)^pBV|IS zQ@w@Z@>BF<&G2?ert`99x$jBVi$^js;BT4Oa!G!E@R$73a8P{BXEb|ztxP)fr%o;{ zl_|BGb?WqOnp0Awxj&Yu-<PGox+du~PpnRBPtd%uOv$^^Lub4hEHjV4)>*B=GJ9XB z<TpN-In}SEpsq#c7PQK|^=&$T><L+r->ijEyQC<+L5sT_(}j_$3!m)NMIGh3_)?WF zx$D=Z2WDx>#WGp8HC;>VbLF>1QM$Y)Marh8NqMnLRwVY5l^O43Rj4Hu@nKr=^1f7t zv}@%*=cVe!O<i-eUe>lW>AGEKb$!EL-B7h(tG8EcCx>|h0>AfbSzXLgSyn`UN1$be zh}HGW-@a_W<;}?D%g_IEIP5R~w{JGc{E-h&rTOqX^rLwOy=>cvW!HmhkQ=r&cZ}RJ za?d>6G;-I-ZQGjrMs6IrbL7^Mdq-{_xqIaHk^4s)KsrELKzcx$K)OKMK>DyXjUb&M ztsuQ1%^=+%?I8Ui4Iv#NEg?N2O(9(&Z6STxn#PdMY)xxOZ%A`UcSw6ke@KH!he(S^ zk4Te9mq?pPpGc!fr?#e5q*q(hEYdB~F48a3Fw!y7GSV~BG}1NFHqtlJIMTVTX&vd^ z)-;cFkF<~Uk8A+41IQL2dw^^LvJ1#IAp3x91hNyzRv>#}Yc>Pf4P-lz{XjMZ*%4$* zkUc>*1=$s3TabN0HU`-lWNVPUu{E26?2fJ39%O%z4MKJZ*&<|*kWE5%$q~@Wyn)W| z{eB*%p!b#;CNocFr$WT){^f7xX~O>|>c5RL-@#_Hh9$CIp6ukfl(+;>c47j?CkKB5 Dj=i{v diff --git a/telegramer/include/pytz/zoneinfo/Factory b/telegramer/include/pytz/zoneinfo/Factory deleted file mode 100644 index 60aa2a0d695ba577ff87624d479f1eb25c8f1caf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 jcmWHE%1kq2AP5+NDp(+@bPWs`Ldep^Wdqb}XTSvjS7imx diff --git a/telegramer/include/pytz/zoneinfo/GB b/telegramer/include/pytz/zoneinfo/GB deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/GB-Eire b/telegramer/include/pytz/zoneinfo/GB-Eire deleted file mode 100644 index ac02a81440f47a67b9f01d3fbcdb085266d20894..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3648 zcmeI!X;9R49LMq95EV52K?)`wKO;4aaG^A_$UF%J^4N6K45`FSG9)9&jG9gz$NAb& zWNQ9AQu4qH%A`Zn@C-G>3?r{R@I=dm5bXE<&on*gkv-^9>@2UH9d?*uhVT319XUQV z#`TY{M)n<^d|k3nUI)Emzs>2i(#+ZKujlUen0bpgn-AyCGxPIW8}r5ny&&_dSvYpC zUNmK!S)8=qeAKH}FX=c&FYR2S^Seaq{4>>NnQNa}R@PE4udLE5wx;Qo+rQHVOOx~} zlWkVN<<V>2*<;qGCzy2^Yfa(cC{q+aQh(x6=F^x=v%dKy^I5RZe0Je`v!T+Ziw|7X zpC25dzgWLdf4QdKeD!{j-Z&>rZ_3KkU%yzVH~X^nmLZGHH$6t_lAfFNR^NWJwLHM= z2<ap{@*a_$d)vvb7w(mB*SMr~j8E;Bqq1k>W3o3RMD4AOQQwWP*ZZD`FyF@?)@AKF zn6f@~Qg-OD+Ml#S@2~eW2cl-`12ymJ@@DC}{LEQ>@OY^CX=kaf*ivJDE<9}x6@=)+ zxx37fj0erp36u4)p<T=`UNOgGnwk@_EA`2+2z{!B*PN<tqATkr>C@%s^qGob`s{|? z`rNud%=r)2=nJooGgb4a>WeAy=2A|w{&irg{w=w;zTDwP^LwvweZ_UbRJZrn)ra?* zn);LakB#e0?I}NVb;@#6x3xIkFTF%Ji12Cu!TGvjKu_JsGhH{TY@-8Inhw}juLG-+ zbd$BMP18LdedDaFrrGLX-F(u|M$L{gK|?m0;A~}Xie6}1_%4~2;b$bIM~Q^`eJib6 z<x8t$tK{bD>C$@hED1ZZRJB=ApxSPlrrOOA)qcSQrPDH0hgTETEeT!~p3+s_8rfZS z95_fiHEpFjcez74U%pEs0-H-jS%`G0yePL9R!ijeeR4-`xkP<jBwZ(eE}m(55<U7y z6_Zh@?u;!~cMW|{b!$6Mb&ttX-Rp8xkFZ?nQIVqVsm+w0U-VV?9`wn5OOhqFxToCz zW^d^=r;R+28ZNzGzen{M;4gij3{(9&o|OK5>(l`M?GhJ$NX4B$q2m2Esrb@uYT&U& zYEZ#4m9Xn8Nt`)DC9PN>4^5n?2G4j+hK!glL(|5}u)EX5n-C`thbGDJ$OsvJ#Us9! z1C;NFV0q;7ZEEBvzsabwK=r5zQlkscs>gDERmr)fYD{vON|}0E9`E(3dSdi0d9wX% zH8!?DQX3b^xV9qWPUXo{br~{#Tedu1;gt#bqa>|ll6vOtSedwFn0of9_LBZ)H#KR< zeJUe0R6Q5nPEGD#qn;0Psm!h|C9~?N%4&8+vi59I+2?<h7gsM)Q%Z~FrP*K0)Pi~P za`s}$nVBOuxUcu&=l<)#C;hJD^9>sQ^N0N#{@0Id*RB=W<K=3m+zrsx*yU=Y-A#GN zW#9Sx{e(oXtIsg6D-QeF7cRHkZJ*Ak+-~o6oJ;#lueBZ>uoF3(j`nmS=My=h$QeaW zDRNGclZu>G<g_B^6*;lUnMF=5a&8^%$wkhtqdmRI`9)4Ja)yypjGSZSBqL`TInBs< zMou(xrjb*PoNMG{BWK&uo^IrPBPSd=<H#vT&N*_@k+Y7RcI3PxCmuQT$f-xpJ#z9L z?b%08zoR|>NCJ=yASpm{fFuFQ0+I$tn+GHjNG6a}Ah~d~$#AsUK+@r8^MNG9(Pjim ziKER4k`yE>NLrA*Ac;XTgQNz@4U!xrJ4kwv{2&QJGURAegyaZG5|Sk(O-P=QL?M|% zQibFSNfweVBwa|pkc1%_LsI5wbA}`h$r_S2ByULK_}3(JNa~Q>A<09s=V;T1<j>J2 z5Xm5tLL`Ss5|Jz-X+-jfBofIal1e0(NHURZBI!i(>1Y#*WE4p$l2at9NLG=wB6&p; zi)0o_Es|R#xkz@A^dk9nv<XHs>}XSr<QPdZl4T^#NS={IBbi20jpQ0hHj-^5-AKM2 zZNia^JKB^ZIY*L?WF1L6l6NHWNam5$Be_SCk7OT7KaziB0w6QM(M|zm4mjFLfXo79 z8X)rknFz>CK&ApR7m&$-%m!pSAoBs45Xg*hv{M3^6OMLLAhQCQ7RbCnCI&Jykg0*p z4P<g4vjdqP$oxPi2r@&EDT2%qM>|Q7S%ORxWS$@s1(_+xR6*tnGFgz>f=m}=z916@ znK8(eLFSC3oixa-akSG0nK#J9L1qpzb&$D(OdkAy_V8Eu*Rv<k&LNMTMUjbjMs<tw Nbd8QojP~#<@K40&SY`kK diff --git a/telegramer/include/pytz/zoneinfo/GMT b/telegramer/include/pytz/zoneinfo/GMT deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/GMT+0 b/telegramer/include/pytz/zoneinfo/GMT+0 deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/GMT-0 b/telegramer/include/pytz/zoneinfo/GMT-0 deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/GMT0 b/telegramer/include/pytz/zoneinfo/GMT0 deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/Greenwich b/telegramer/include/pytz/zoneinfo/Greenwich deleted file mode 100644 index c63474664a289aa3c3c0d8b2ce06d484679754c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@+<ikBLdep^1=MQ51psD724w&M diff --git a/telegramer/include/pytz/zoneinfo/HST b/telegramer/include/pytz/zoneinfo/HST deleted file mode 100644 index cccd45eb8cb2f56b6a1b75e2d7b9530cb5abf2e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 mcmWHE%1kq2AP5+NDp>yiFHT@!@CXiJ2q8-s7f`FA0T%#a4Gc{H diff --git a/telegramer/include/pytz/zoneinfo/Hongkong b/telegramer/include/pytz/zoneinfo/Hongkong deleted file mode 100644 index 23d0375fba3377a3d513d849c0d29c82ad2add64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1203 zcmd7QU1-f=0LSs?e_rh{a>4}{MD&!#u^Z16wn@Wr?82!@F;a5cyrs<5X_8uzPOZ35 zlB;KwMCrM5X)}@6?btZ;wsRV0UUrThHplb*N4apbJI}v!K2QJ4|L+^$p4eL{{&|AG z->$cEpK~&?C)FKW5$W!4kKBzOKKAHhCiS?fxAjT;HuJRhn(8^S%Ji<OG3jNMCS9yD zebZ-|zGw4H|8T)PHxuT?y|l?(nyUtG=GDvN<LcGfJMwjXLcKY9MZR4gRfCP4Iy<96 z<<@rS+}K7lH2;(yN-s6<O8fPDQ*J&C)a&7MBj#gQvm9x8Xg=vJ@^ixt^QA2!zg9m` zqx-@#zu>wm)Q!q-i<4?>_HJ49?^4D5I{AIDR{h8{>hb$K&BU!5{qt(IDP35k#hHc1 zN&2<tfX{e$<g_oAHU2uE46O93;F7Eio>;BQ0$sAK(5XV%%W`T@hnjZ#w48aqN`((L zYscgDIUaw&KAu01<C)|mL{o^5eW+FVgh-~GDMGZgIpy3%`0w|dV{x$|%5N)w4RNu_ zUfkn2@kwl1vWGp9O<nD-$hOG7$i~Rd$kxc-$mYoIu6BE5e^=W8(gD%}(gV^2(go56 z(g)HA(h1TE(hJfI(v7Qa2kFPvHiUG9w1o78G=+49w1xDAG=_ABw1)JCG>3GDw1@PE zH0WwOL|R08M4CjpM7K>Y^vSV}0-YkQBE2HbBHbeGBK^ABhLMh4ZOcf{NYhByNZa_o L^&PV1SE$f0t#TGS diff --git a/telegramer/include/pytz/zoneinfo/Iceland b/telegramer/include/pytz/zoneinfo/Iceland deleted file mode 100644 index 10e0fc8190a401f67b861ced3b8b16401432a565..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1162 zcmc)IO-K}R7{~FMc3s3G76L_sdZN@of-OT3ifGF^)E;6NqAo5`K~YdqbStrh=!Nu9 zr>&mEQ}hObVC*3X5;d*O)NM6;HCOFLvurJE`hUluOP9Jdf0)lObDQ5cvUS(aW!4`r z-><i8jXe8LMUQE$Zk}}^aiaF(flXDHx;%aj*I1Vu%W4}|6jwDg9rWJ|6<EzHi={c0 zceABvjkeq^k~UwVwp|VC{l0c-KfOph;y2{M9-lsp)k$JQS|8P1)6SAseZ1qWc9kcj zyJElg<lmBHVOW!s=Oi_IpQN4~mfnFp?Q7a1Pdk=NfBhkSc0E^~S8vuAXEO3~Te-g4 zo08YTxjGPv%bNw?b+9a{>6ryGlzUr;hNonBBBmp){qnZ^h`zgWN8UF^^~1?}89g7= zvE9ez<IzeP537EvTrHnVt94@4LisXhyJqHRNoIVVPW~8_ubqqaTiT;j@d}w?|2`h? zKb<Vc8HrB+XWQ#IW208~^qqIM*ZneUV<=*k1OnEQz*1|ydFGRCNB&|t$6Tz3EQqX# zEQzd%EQ+j(EbD64MHWU@b~Q^QYrC4ok=0$z^2qu~0Z0W%2}lh{5l9tC8Au&SAxI@i zDM&4@rWm9eS5ppB4^j|P5mFLT6H*jX6;c*b7g88f8B!Wj8&VunovSGisSha-sSqg< rsSzm>sS+s@sS_y_sT3&{sTC;}sn*q$i`0u0j8u&OzvLVfbs*;_NO)Hd diff --git a/telegramer/include/pytz/zoneinfo/Indian/Antananarivo b/telegramer/include/pytz/zoneinfo/Indian/Antananarivo deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiUR<NU-)l5P+-((ID$VG{^}c8srQR OO@ULmfUeRt<^llo>q3(N diff --git a/telegramer/include/pytz/zoneinfo/Indian/Chagos b/telegramer/include/pytz/zoneinfo/Indian/Chagos deleted file mode 100644 index 93d6dda50f579093f25617c77081ae99c8631ea5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMXUS@(U8!Lz#?>i_@$&&b5Yz~KA@q|q&afrWt~B!Pj$$2WvQ k+rSiv%@{&RFc@gwe~{rI(?Ax0Xre6PvH@CUr)$Oq0G>%BlK=n! diff --git a/telegramer/include/pytz/zoneinfo/Indian/Christmas b/telegramer/include/pytz/zoneinfo/Indian/Christmas deleted file mode 100644 index d18c3810d97bbd424dc3c8fa98de46bfa08c3fa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iR>+1LQCy6)>>)_=YfO8<;bMkYEzfkpIPE TE1!VOz-=Oz4bWUWU2`q~S7aD5 diff --git a/telegramer/include/pytz/zoneinfo/Indian/Cocos b/telegramer/include/pytz/zoneinfo/Indian/Cocos deleted file mode 100644 index f8116e7025cadc709bbd995905e88c92ed03642a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 zcmWHE%1kq2zzdjwvLMXW03_=F|Nqa($iNVF2gqTF&R}5i@eN_nHZU_bU<e_>ETA#} YYeLnQfK0(>CYKG^Y&%^uD`NvL0F6i*%m4rY diff --git a/telegramer/include/pytz/zoneinfo/Indian/Comoro b/telegramer/include/pytz/zoneinfo/Indian/Comoro deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiUR<NU-)l5P+-((ID$VG{^}c8srQR OO@ULmfUeRt<^llo>q3(N diff --git a/telegramer/include/pytz/zoneinfo/Indian/Kerguelen b/telegramer/include/pytz/zoneinfo/Indian/Kerguelen deleted file mode 100644 index cde4cf7ea7086a3fa3609566ff03e9425b096f36..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMWHD>12|{{R2~jEpe#ZUGD|x&{Ue+6JZ!AtaatG~_?XG>|#C OP2{oxnro+P$^`(9>=%~+ diff --git a/telegramer/include/pytz/zoneinfo/Indian/Mahe b/telegramer/include/pytz/zoneinfo/Indian/Mahe deleted file mode 100644 index 208f9386bdad172305a48f6ab7e70ac3e1ca0e1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMXSS<l{5|NsAgMn(n(<3Auh77h$7KE5Fg+6E>JAtaatG~_?X SG>{p%P2{oxnro+P!UX{Gdl_K> diff --git a/telegramer/include/pytz/zoneinfo/Indian/Maldives b/telegramer/include/pytz/zoneinfo/Indian/Maldives deleted file mode 100644 index 7c839cfa9bd62842cf23f01d5195b239bc5a437c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmWHE%1kq2zzdjxvLMXU03_b(AD&VF|NnnRCME_3mlr@05Xr*8;1<BZ;o}>^;0wgs l2Br)lBp3`d?|;~~`4S)+WC6(3dLWyiC0sT@tL$`5xd3Z|CHMdU diff --git a/telegramer/include/pytz/zoneinfo/Indian/Mauritius b/telegramer/include/pytz/zoneinfo/Indian/Mauritius deleted file mode 100644 index 17f26169904928e4061e4ee58bdf7a6c62001524..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 241 zcmWHE%1kq2zzf)bvdlot(^=0tLxT0KgT(D315f5@4?NHHU#S28|34EW5Hc|^n7#m+ z;}*ck!oXnRz`)_-8^WM%U<$-03?U@g12pnK$S#m+Ap5{H&`uByvKLGP?FP}r+RtSJ KbcLO+2^Rpsttt-y diff --git a/telegramer/include/pytz/zoneinfo/Indian/Mayotte b/telegramer/include/pytz/zoneinfo/Indian/Mayotte deleted file mode 100644 index 9dcfc19c56e62b12b730f4335b34479695f273f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 265 zcmWHE%1kq2zzbM`vLGzd{r}>hjqh$nY&rhm!ojy|BhKVhU14NmWM*PuP-+1gp{&8c z!oZ+qz`(`8ptgpA55o5G4PnqWFfuk^aCHQ;OiUR<NU-)l5P+-((ID$VG{^}c8srQR OO@ULmfUeRt<^llo>q3(N diff --git a/telegramer/include/pytz/zoneinfo/Indian/Reunion b/telegramer/include/pytz/zoneinfo/Indian/Reunion deleted file mode 100644 index dfe08313dffde345044d5053e3359f92163d3e38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMVc@r-3d{r~^}85tQEOu$+!92i)9d_x$t4NMq9NH7U#$bXP& RAVYAQ$YldG*G|`j3jo2r7;yjq diff --git a/telegramer/include/pytz/zoneinfo/Iran b/telegramer/include/pytz/zoneinfo/Iran deleted file mode 100644 index 8cec5ad7de2f2c14dd4b0a6c375d7198ef7429b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2582 zcmchYYfP499L66Mg&7_KDr+uXW>A2@`>OBl2?$bdK?RXRc}!GL6DS2rJfewdKnY<& ziidzg(ZV2aQb-f(1|iY#6i{Yv;wHmQ+)Ul*xi06|`jWw(_s5^>8hp8adm?8ojk6g4 z__fGP9n5D)!Sc2GT&uGlaBROEnCulWC_BJsQ0KUSC!^xE^P~{vV!Nqby`EC;!&0?- z>u&Y(>0SDjnirHuS+n%q6D_>5d+U)Kwkq!=oAg;wEque5X-nWOWwmtaQ6o~+XxHI# zOy5#5=1#P>U2jw3!Zta!evlYfHBgQ}m?tI_1nP;ITKTO`mHr72M8KR<{c4!E3iKbT zgM2Gg@YQ-L&xeUAb<<_Y(H0R}lCG!bJE&<}D&_RF@gi(VhYp{esAhyF=!l88)lAQQ zGIFS!i0a>>qm5ZAy30|<{E;nQKbs}rsC5yuj-1f5izll&x!rPZMw*zH;-=@vA5w3| zl<5T_78NV5$b}wpV$pLhda+Z!T6{lP#&tG{`1VG*q&Y$?t?ALr$~UXG_FCogoGOu! z9;Xvm-c(7k1@i5vVd9-hL*)uvu~_+1giaoONu@Ys$dr3dBDJGRzuW4s-aGB5S5?=l zw6Ya)b>UsHCaYMl-Qc0tC0>^67t9xSz3hA2_v-UlzYW7jU>)YCy<zwY!_hD%huaM> z3=v>B8-~BMd-~z;r%sDBBF)Fc$7=O4KS!I-C_LsB`R^-hc(k$}^9xG@u{Qj7EDpr# zKrD|m*9T&OAXW%si6GVpVv(e|N)XEgu}%;R1+h{PO9io35Q_z|S`f<xv0e}h2C-rg zO9ruK(p)r%RfAYIh;@TlIEa;lSUQNcgIGL>)q_|*i1mXo0E7h~OaNg62qQpP0m2Lr zc7QMhge4$M0bvUWV?bB~!W<CxfG`MzMWksG2%A6{1;Q#2W`VE^gkc~o17R8n+dvox z!a5M<fv^vRfgmg-O%p-b2*OAZR)R1Sgq<J^1z{-&Q$g4Y!dMX2f-o0^y&wz*VKE4k zNz-N!MuV^#gxMhM24Oe|%R!h9!gdhGgRmalFk>F?^q_0JH(Iu{ziGrX?7#0)$9!|& zQ0AMv?=sK0zK;2pi)(p*(9oOrR#ndawu3hIKg_?zJTr3{^Q_f_nP(>yGXH4qZr&e< z59htzubKTE-)Q!8huJg##CaR@JUbinyxY~xcl>#r_oo*EdGD;f#eV+LRQ3x>hBN<c zXDRbtTVj~+Ue(ULaA_OwJ#%7t?+qQu{=SKM?C<vsV*dH_^~{U<KlHz-=K=HLuKVnl zTutZw#aVCmzdTjRe(8}|<_C&8m><mD!MrSE2=hb9^~?{)hcW*uW;*Y$r?l`c7aQ3> z;^D~tH_uiwuW%aAyyD&s=9QgE%)f0<<bCu^H*Zz5kNv80H}=2VbDsIJoY~Ber#mvQ zUYX6jW?>fZ@1tFKpLp#g`zLL|?Ef&LoB651Y0PUK+?m(@b(neGjYGVDY_szIso@Iy zx;l>i`Z8DM4TS~FPiF-)Z(QHRyeX-X_s_8rywA)q*l!NXVE?Se%KY4mRm^{JiD%x@ zr;~ZhotwPRcdX!j;o?yCFV+{ce@V?`e)+&<=D+4=FmKItV&1mq3Eu6A+j;*s*Ps30 y!|MK*x8&n}2S57j|INF&-vqv){k*K>tUl(?=KI;tGsHI5+cL^C#4^Tevftk%?{cC5 diff --git a/telegramer/include/pytz/zoneinfo/Israel b/telegramer/include/pytz/zoneinfo/Israel deleted file mode 100644 index 1ebd0664aa29c0abd722661f761031ec0304631c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2388 zcmd_qZ%kEn9LMqRO}t*foJ^@^$e%I_#mnWdRFaCKU?8`+F@%6)1ZE)}lgJeEm)c)U zw-&XgqFcm9rNg4O=Au^9Xd}28i&V_hACLmvX$1;P?Yz(0SnE+wdeZmq+}GLVDWA8m za6?6+{Nn<xUwF6%>)}0ASznEwjORPnc1y=ZvwV@dUv>5msW0ETB3++HNcY~I;%{k^ zuXfeR$rpRo*X6CMr!rW23q7j$T!DNW_nA5sUM8n!UX;^AemT>YBz+&I$ywti`L3-? zooo778TB>teSV$%&|aZ_OevM08Z%Y@b<1R++LZGlC)Hr|VKq3oRa~|8hU;>xKXB|9 zEvPxsKkMf|wAoQb+MJ0K+Kqdvw6OL|#@r`r{o$QgwFvEXZ{&`nT2$8i{+mh~jhiEj zz4Hn^+AXrg8y!4iM1M2jzjdhFxb2fE-k6?YZGKCHf5EYGBlZP{f8oAWhP$-I8@DaS z@MIqM$7jB3BrLA-CdR*~CC$0wPY!9&l7DUXE(&m|McocPb*xm~-ZyUC;UCfOJW`}B ze&djycIaL0uAMvdCA9~QrJGaqW!@$&J$tV#&&<_V#BP#|@G*1c^gLNPTxPBsPL|B` zqk2~7^|HEszj^oD<C48Q(9CHFRcoHxr{`8*RNj(w^B&(Rb#G;so|kk?`LY-2_su?_ z@?)>n^MBv23PNMdf}VA1?P#!B*tT31^^BPJAJC+@t=lZAOPA7Dj+$EWJXu%Qr9Y77 zl(OPh{lTz7DNlP@uW$ya^>eHB_5B0tq09ODhT|RT;j^jwBX7Q?Do^;#jnC{>Rm~~p zqvcgffA$7*)0%pDtbD@U?A|EV8GYvCGjgTIJ+9YWh?gxh&gfgZX33KmKGL@y9+hof z2ld+5LR8(6r}d|5epB1`l<5xVHB%-(0h6zjZ&RI9ozq;?T=?I7M53J5|GGq8k^qtU z9*0XLEK;V6q%L*L{QEDHf6dPE$!hD#T46nTXuXIy91gzzdJ*yci^W~FF8_m1Cy?bK z>$9~Mh^!D<BC<wgk;p2MWg_cD7K*GCSt_ztTWhh%YLVq4>$SBOjI0=0GO}i5(a5Ti zWh3iG7LKeOSvs<IWbw%Ak>%T3>*pwdtyKY}1V{~#A|O>j%7D}XDFjjpq!dUkkYXU! zK+1vC11Shn5u_xxR!xwiAXP!ig46{m3{n}SG)Qfb;vm&Q%7fGgDG*X2q(rt>jgTVQ zT2(^IgwzQs6jCXqR7kCmVj<N+%7xSmDHun^kdkrK3@MteRW+n+9Cbqq$5A<?bR4xq ziicDWDIZclq<}~TkrE;`M2d)1(bg&>Qb${>kVqwwQX;iPipfz;q?{b}L<-7LQKY0C zHARZbQB|a@NL_8M!XlMLN{iGMDK1i7q`XLdkpd$XMoP?4W2DF&RYuCpQD<AL&>WSv ywMxxVYoyp5)keyV)Eg-{{y!@oWEFm4f|dH%oJ4n$J1H(9B{nDC<4$&ag8l@yuiuRT diff --git a/telegramer/include/pytz/zoneinfo/Jamaica b/telegramer/include/pytz/zoneinfo/Jamaica deleted file mode 100644 index 2a9b7fd52d37a1ffe9fc589daa04d88c6c71a6e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482 zcmbu*F;Buk9ES0uk_c^)3WDIMQyobh+%-Uyi7q;I5!0=!3o!vT4otAP8W#r=1_s#J z{0waO1x)S}c>W(|V`9AK?l*LL-sn2%HPo-CDu1(bgL`?##rfCvsGjD7w>UqY7}q?; zo_<LE^{XzdZquFRP#50^CV1)T-RB!qx@+lj(lmQl$GXxEP4(2*`=MuQhhbdLeVPNu zi!;vF51+LQN2$%5wRSmEIcq;w8UL~qsSCO1UAbqGivCbw<s?r>eWXN!6g5cEMyW!| okUFFgsYFVVTBI1MM#_<TWC3IaWC>&qWD#T){QokpOmyOY16WpdI{*Lx diff --git a/telegramer/include/pytz/zoneinfo/Japan b/telegramer/include/pytz/zoneinfo/Japan deleted file mode 100644 index 26f4d34d67b46513491f26c2e661c6e653cc130d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 309 zcmWHE%1kq2zyK^j5fBCeP9O%cc^ZJkbvvel>u)1J-1zaU;O1HD54YJFKHOd_`{B;B zM<4F?{Qtnr$OM5549(0y^$a}=7=fDWCNOY7NFU!21}_&N4h{iHGlFmk36A&=1gVFX r6o6=uW56`fK_D9BC=d;D7>EWr4om|b2%<rb1kq$Wlndx;T}v(iZ^UHp diff --git a/telegramer/include/pytz/zoneinfo/Kwajalein b/telegramer/include/pytz/zoneinfo/Kwajalein deleted file mode 100644 index 1a7975fad7f7e96f7101eb3c64c9b420eeebb621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmWHE%1kq2zzf)cvTQ&s(Eub4zBRpY@)cLYdAG(7|J$SkbjtV~>i_@$&&b5g$im9X zz%b_lP!+?h4Gb&{3_S}NI2ah}CNS{){~zwaAi%(|;sAq)k8cQrwxJ;q8!%`aSTg7u z8Ufix3?U>q1!&{{Ix`6c5Djt?hz2<eM1!3MVgQ{7atp|bAi5qXL<MJZ*#I4D2Xr(S E0Dm)39RL6T diff --git a/telegramer/include/pytz/zoneinfo/Libya b/telegramer/include/pytz/zoneinfo/Libya deleted file mode 100644 index 07b393bb7db14cef1e906ebe63cfbbe8cddc79d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 625 zcmb`@&nrYh0KoCtT?f{Wo{Q(l;<399B=szg#O_6Gtap+!4%F^xj&jiEPvGPrIXXyc z%Rvq<PFu=NqS>-3N{@?jVZM*EQs%vx@B49?`FM-@HCOyP9P_uv%;tl)i^Sd8iFF@8 zl9lyqtMa(2t3%_edRW%a*-O7=ds5o9@5=rd(5+AXe%tM`Y%d@C9p?`+R@(48_if#^ zQ?I(WUZkU@RnN+%?#*4PcsimJsj#0+j>*1>Q{T<L%Hi~=N{tls+}^I3_il>0e5n`? z1y|&!-*2qu3%3vrOPnO;gv@@MEK$d^Xq=h##8hU1#S<Aby+0iK(+mzyIXne`f)La| zP%wfT5DE$#0)~PU)BsVCC{PqE3K#{A0!P8408$VskQ7V`C<T=QOTnc8Q;;dp6l@0I N^k31VMt;zZegQ-UsTu$P diff --git a/telegramer/include/pytz/zoneinfo/MET b/telegramer/include/pytz/zoneinfo/MET deleted file mode 100644 index 4a826bb185531c34eb37959037c68fbf08c23f71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2094 zcmdVaUrd#C9LMp8h?UIfiw1`T#3DlL$bU!;Qac`)$)S{!kxFPFY7xbW7?j2;=3FuN zsj-|x(u%QaMqOa7p|$>w7LwIi!*Xr8vVU`K=IqZ{{oYSqcGF!wXM0{}zpLls_jyOx zZEG&_{_(l+@6%mQ{?F*28Q9oAdHl@2sbLKa9?~;!Nc(&CXlzEC#!s!Xqb~+D@m$D0 zeyYTdJ(^>m#8U0knq|6Vuu(}*%&|**!m{=<B{$`2Ry0ksBP;B(;@^~#{i9N6f2lzH zQ3cMvskD=?=<?4FXwJJ&D*f;g%Q!e}nfvzJ++JDM_D5`9OU$m=&}dgyM=iTNY*z)# zEGHw^uAZ4@xd|cVo-5J(Z*sI?GF8{Sf03>o`9;CO@3pY+xbk{FQhxhk6*Rr9!ulyI zij7!t#i*66dD==d2du29)5@m1ZBcrk7M<8+i+}IXk|QCz{);-54>hP_bct@*xm-*8 zGqkKZTg!VE+l`e83N@wMikwplho-GE;X_qre`QstzqOU|Z`;a=F}vyH^R{aEC958P zOOf5ZR&(%K-Q3!0xAZ=y)weyaH7%P}dtHm7)m6GRP@}cM1zLN)Ky@jVR`>ZV-F|Mq zt$Xcft)EP^4F{5~e&i>+qx-Bi^i9~thDqDB;|JZj^mS{jA6HY>UfWzTtmcakX-lTG z<-0xFIvvxtu`b<pBC5MzSg)23+U%ZP6>1%-wR^W_>Asz%*0#FbV$A`&zi_U#hkvyP zl0EClPf^F2udOrbygHA)XI-bK^x&&Q_Rz$rw(osMJBA1JaMz%^cXun!yCD9*@bkPR z^AbESV_uxQwtu}jiG*vTUL+iKhjDSye{u2ua1WQ?*9`y}0Wt(+49FmmQ6R%W#(@k3 z83{5JWGu*F+y{D$1{n@A9%Mksh>#&6V?qXnj0zbRGA>^?Fl1!N(2%hqgF{A#3=bI} zGC*X6$Pke+B7;Omi3}4NCo)i9H&SG%$XJoVBBMoyi;Nc;Ffw9f$jF$HK_jC^hK-CH z8Mv<-IWlx#H+E$3$mo&bBjZN`fJ6WZ0TKfw2uKu=Fd%V20)a#V35BnV1riKj7Y!sF zNIZ~$AQ3@Ag2V&~3KA70EJ$3Cz#x%9LgVXVg9OLdMF$BF5+5W$NQ96OAu&RNghUAm z6A~vRP)MYZP$98Gg5~R?g@g-<7ZNZeVo1o4m?1$!qK1SGi5n6)ByvdTkk}!?^L5cf u!sqMahXfFbAQD0(hDZ>RDB}MvjESz03kzvwFjAaXnpY5v6c%89apIpo*$oZ= diff --git a/telegramer/include/pytz/zoneinfo/MST b/telegramer/include/pytz/zoneinfo/MST deleted file mode 100644 index c93a58eee8b32f672fd3a96ca3e6ada5b0a0e168..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 lcmWHE%1kq2AP5+NDp>yipF4qp!8bUBA%rYlTtKbnTmW*V4CVj; diff --git a/telegramer/include/pytz/zoneinfo/MST7MDT b/telegramer/include/pytz/zoneinfo/MST7MDT deleted file mode 100644 index 4506a6e150dfd73884811c8c0f5a0e21dc76a756..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2310 zcmdtiYfRO39LMn=s39jVzb+U8nrTT8AfP9K%*Y@@Ize~@G{PwG>}UmEI%y&P42to< z`CS>aOoDFKnt5Hbv7xTv+REl|Yg!AtUo0D4wpd(FzxU6FJ?dHOd-ngo{yWb<Z~w~R z>Rk1Yn_~ar;hwh--@5ya0(rSF%Je*wBE2vEXg=(Wk-<5CG!ND%s=r1a)1yDssIjxv zdi;X|HE}dwhu=(5YFDz3=$KHEkB{p~>ypgm`sp&IV$57tI4V)uLndmwSEBWC6TR=C z_6)ybF8`ueV$K~?vB%pa?!8_W|5lIIFSMyEI-k;4Zf;ap1sn9$D@s&C>79C7eww-_ z?M9tAyVy*R_UNR@RFm}ETuJ^a+N2Cll^G{~GBXcekZXHSnOUJ3`nqlV&Ftl2J!j2F z>U!^&`i7<NsJRL6>(q2nsT1cV&9h#m4V{qm3x0J||ErR5rrBh6KP_2@E6u#tfMh?n zz~ltlb?$~~W`4<Py`XZ$cvJ7vH|Kt87DlD(yyQPs-uE#&fApLxIQfgd<+DDu=zvFV z?c1Xk@Az46+r3p4HhnC&x9>Jf$`8mL4O>jnyl17jXq74ceL$CFwy2Ubdv$4Sohm)t zsh3_XRAn!{E_Z)YYkVyoa?iVqO?holD!OKwdl!{SWhl}tOUV@f@^6fPEMBU-Cr#D2 z8M<cX*Q#b9POq34P%B>>*7ptVRkiJd`u_e-Ro8Gtuj*c_9`K!(`i>6sVCErtC=fIa zu{)))q|7v43`rn0(*#b{NH8ki1osuln(ux!YrCrT!>8laBcXi#=>B2VRF$locMPg^ z-f_LY>4@5pa9(dL?@}8lMkSPY$b^PImraQ~&8GfivUwzAp6KqCC(qWHmew}ea<ss- z)-}r3T`49)MX1P0_Vr)-g~NM9O%8|O?uk(DJ=MKqF8%K-@+;pm`&8Sf!p}q^F1`H! zVoYIs3<?<)GAv|V$iR@1IqlGpu_1#)Mu!X!-T3%mfSes6Fhq_qB7;Omi3}4NCo)i{ z9Vs$YryVOYSY))wa5=_{447lY$dEb4j0~D%)X1<o#*GY|W8}!tk+CC#ciPb-!$-!C z1OSNu5&|R!NDz=HAYtH$0}=?1NFbqb+E^gLaN1}f;oyh|5)dRJNJx;FAVEQ*f`kQ$ z3lbP4GDv8U*dW1i+UOwR;fN0sAdUzjA>xP;5+sf&Az?z|gaitS6cQ?@jTI6sr;Qd8 zE~kwb5-^U4At6Izh6D|X8WJ`nZb;ye$RVL~+Snn%bK2-3;d9#fApzuwAQD237$QOB zh$0e3jyNKLL?VfV5{V@eOs9<|5>BU$ClXMnjVKaQB&JAEIiiY$l_Rc5U^ya-gq9<= zNN}Atx=47PHoi!Joi@Tqh>;j0K}MpCgc*r5{_g_qvE5#1`<>yNpOu@Ho#D&L$;!#f G_WTVQwMumW diff --git a/telegramer/include/pytz/zoneinfo/Mexico/BajaNorte b/telegramer/include/pytz/zoneinfo/Mexico/BajaNorte deleted file mode 100644 index ada6bf78b2815d3d99c97d521ab9a6b35c8af8c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2342 zcmdtiUrg0y9LMn=gpw%wq@u(hds2#laOA%z_Rpvz$O)7qi5Z#kXHW)-p%f81w$_^C ziw?_G^yKV<wIXJb{bS^oTWhSsR+x=3Q(zcHtQD2x^t^vvcGX?$clJE5-_G6d;`8?J zsIE+N{_)JU|8RIZ?BPA~wccM_x*7}Xx~H3_dMnH8-i=ny>9Fak&n6DH46gd6Zt(c~ zb>BpnIz#PmPhD)@EZ^sCl}lyGaycPGM!orJZ1EN~9-pMfr_<F$=t4Cy7@@9=PN^Sy zep8cY2i1@5=hgg?ZnNP0fC}$#Hw)kER*Smc)arP<y6#!giyQ0JlIp#BY3Vi<k>}UT z)~!{`6S8#V%3`^GUZjo+&XlO>3=@5Exx@@EGqE54E-QLw%nh$z5Z$m^-+1sNSy>XU zSJiy0;xd2IH|2k*ZjSg;$0v5G_}NL55Z0m+hCern6T8*wz8;fwu33^hj~dUZU9zV6 zag%a%qoh_H(P{N@lJ4E7Gm7U*W_*dxN*kB8q1ie+W{%1pi_+`<98>GhUe!4lK2;mu ziZr);@VdIS?GJO?i-*<iwcnXLTDxRpVV}9P{5i>8W6WK-d*tp#hm1F_P`op*=)90r z$s0PT^Dixt%`crY1z*>Quc^b_(_0{gJNKKSV;<SEq10?`P*NO|WBl8u#eX%{lw^J- zC70Lh?JIs(+dqlXrL*VMj+3+czTtP&&ejoqf8X<}to)3AptDi!@(r5@pXrd@$^GV` zs{K+Pe!^6EOQmA6)l|jjNYy~4sSb^m>Nhr-n$dtfe5^u0@<oi=)8N&QcF(HXk_27X zHliNOny>fPo>BD?lX_p_NwqI9&opHBOT+LLb0G4B9OxS`jWezCL}#~oa;Q?8n%m7& zr#DG+S-pAsg+vJo4hp^|IAo5!{yV=w;7Ebv1OhLM6A}otwK&)E9<;!{m3uEO@cA8I zvEM1;<l1wuJw<*7<2XTo-~N9wu7G_Q7&0<sXvo-*!6BnVhKG#L)eaCDAu>c{jL0C7 zQ6j@c#)%9R8L6usDl%4AJ6L42$Z(PIA_L~j88I?sWX#B*kx?VVM#hZ{92q$>bY$$v z;E~ZI!$-!C1i;ls00{vS10)DY6p%0=aX<orL;?u~5(^|4NHmaexY~Fi0dchvK|+GW z1PKZf6(lT3T#&#ZkwHR(#0Cit5*;KwNPLh0x!MRJAwpt=1PO@}5+)>0NT85NA)!KI zg#-(U77{KbUP!=PZN!j}x!RZ^K|`X3gbj%s5;!DsNa&E*A;CkUhlJ17#t#XgtBoKM zLRT9@B#1~9kuV~0L;{IK5(y;|OC*>`G?8#3@k9dZY9oq-)YZlm3974&DiT&Cu1H{! z$ReRdVv7V9i7paeB)&+1U2TMs5WCtKBSChxQAWay#2E=R5@{sVNUZUHAM7w&^K4u5 UBwxBG&6ASkOHK8pdQ!sv0^LWd&Hw-a diff --git a/telegramer/include/pytz/zoneinfo/Mexico/BajaSur b/telegramer/include/pytz/zoneinfo/Mexico/BajaSur deleted file mode 100644 index e4a785743d75f939c3e4798ebe7c79d38e4cfd08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1526 zcmd6mduUH#9EZPSF0IvGatV=GiPmn+W@hYSGxpoYew!J#-)4R@cC}d>w!LL3a(Smj z%}^pNZHZYf<uAE}keW#?$(EEgEko-#&+)(Vhw^!Q&vQEG-{*1XmUzSEkK1bh9dF+$ z7rO>3JBG^py1dz*TL~{c-Tq5G-J=HwpM0wF^t9X<6sgo<#my?bdYWFG>{UxPjOd63 zYt+*4UL84Mk&2r2O``h!)v}*&BzkzVS>E+RVmkfIiaTu*d;GIm+1ezlDxR6R=2jh_ z-f0qw8gydF39~veUnd3BnB>4XojlT_Qhev@l%8s}_QN>6t~*PmKAk77(~&Ceny;kS z%}^OfKFa!n2(zK|p=8EQH}2G{k~R0c$qqUq8|AIZnR;30^xZR?h7ar9`{&H&*QI*P zmAh)|%`}~N=&Z^=8LB-cS`{?9q%fsWZOaRhqJ@cSd+a3fPWG7M*<Yo2DBkQCeJv#~ z1I^BNw`AA#@uu{_D_zzyV9GDu)D=~aP36H;x+=F{?e^B|>gY$RCTX+YGqYXQhSW*z zkE5zCAXn;oD^&fL7}<M!z1sJDj_kisW*Ry~8V{tKrep8LXN+^k`uP5XV_9cnL@cY# zh_7X}pO=Z2bzF--?>_4H_f(f#T$yf3uy2NaU5RdS|AjP`J;Z^?0}%)!5kw@1woDMA z;BTprvr_X!E{I?d$snRZWP=C?kq#oBLt8$GfD8#CA~Ixz2+5EVA|^vlh@cQjA)-QL zg$N6g79y@gTV9C34sD4cB12?`2+fchA~r*Ah~Ny#A)+&6hX~J*9wI(Nen<cg?Fb+t zKw^Ld0f_<<1|$whAdpBPp)g{B1jC315)LCCNI(wlh#(;`VuA$4hzb%GBrZr`kjNmR zL1Kdh2Z;_69wa_UfDY{lAt5@nV}u0Bh!PSeBTh)5j7Z^s8>*jOZlqmokZVbBSa4{N MD=aiPA~@9V7s`x}c>n+a diff --git a/telegramer/include/pytz/zoneinfo/Mexico/General b/telegramer/include/pytz/zoneinfo/Mexico/General deleted file mode 100644 index e7fb6f2953d123efbc2550f450decd794b017d4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1584 zcmd6m*-Mo{07pkNDie&#9$G*m5W&p7va(E5OmA-2B`dv}Ub9R`EwQp3Pz0e&AEHHs zDB38p9x}}yBv3FC!-&dAE)*g4&=g40_BH4G1A?H3e#6Z9;CnsCSy17Q5Pw{W`8(U( z#aDW#WY3o>Pp>=Ibw6R;)#n%E>igNMhbHg1hR;T2jO2XwjC^>LG3Gt&8M~jGF>yY? z^SZwIX?S_Ph^U(@Ba`z*RMrO>9TY2KBA&>#bC!#B%ck_Y_rJvY?~irtw2#`*`$%u> z`JpxqwClLjlWKF9)LW_tReZ}Sxi$TgN+_+CiECQaw!}=C<nLC=t0HCcOtVPw36Lo- z%f$A#Uu9}vvPc_Uq#fsi#g1FkI=x}O$T;~zXBG#ktjd0!9Y0Sw)4Fs{;764ka$M($ zQI#KXLFRjU)XwQfSuohCc0DhYyKmeUd+u$Kg>4<8=*&v#s;CvkC*pKTic{<@4AiB; zF`_KaTe}x#sqz)?borMkweRP!t{7jU_P@EV58RzmmBT~wVCRIYx_V7k*F03RwN2I( zyb`tU1F|l5KpaYP$iqv!Mg5uzUH`2~H2A0LhKXWv<kM<>^g)6+Hs-GzFBhrfw?FF> z&GD+K{h9Xi=FBq3$A0<xx5u(B`w3xL-5tWmvTj}!b1kd?j+dYPE=Y4Yg(KT361a)x z<~fD)FFdl$a0%iQ#3_hZ5Vvd^zaWl5JcGDq)A$B)&ZhAW;vU35?8ZTchY%MTK0=&i zcnNWn;ipaGD8o~Ts}Nry&O*F}xC`+Y;xNQxh|3V4Ax=ZQhPVy!+oo}x;kixYI>UE} z^9=7H?lb&{G{EQp(gLIhNE47QAZ<YUfHVT>#HMKl(hH;+NH>sn82vyRVsr#)iP00J zDMnY2witau8iRCZ)3gTZ4bmK>J4kzw{vZuPI)t<c=@HT-qf1Dej6NZaGCH+sT4nTV t(=^NI7Sb-GUr57{jv+0>|Fvgd6L*Y>JH!zc8Xg)J;s_55jSda-{SB~`;=}*| diff --git a/telegramer/include/pytz/zoneinfo/NZ b/telegramer/include/pytz/zoneinfo/NZ deleted file mode 100644 index 6575fdce31183d8238b18f2f30ab5b9227c7071c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2437 zcmd_rdrXye9LMqJpwhVKWemws!@O`gTvUW2LIninl6fRN8GVz42MwVlp$lXgnPXVf zmaRD|$pO<6kpwLTF@#zdvNfreebbp|D>PSA%fg=bv;M3<`l~;Bp6&Pg?QG|tvwhyj z<t{CbH~#jy;jd43T5=!1(K^~X+CBT1ZZ2rAK4%_ScuqPB`ptL3`{iI_tJyiKRk}jz z&8{E&^w8BZ^YBN_`tDGk`QBNN9(nt+=jibdwR`K3r>AMJ9$R<V)4OS_9?#wBdB3Pa z`<%6&6WI;YA1I!IsB#&&Qe&R{>eW=AkU!LC?{4+IxLf=he^>vZV;WF<S_8Y9G^nsg zZ+xXr$ENJkan&U{KI~bYP>`h)Mv65!F;auiZ<3IynG({!T0$KW656~-Zu-t&CT*T2 zH}_nSuo6F+{Q96w$rzTY8{6fUSRV~9{Y0k){h+rx9Wvc(fzAjxD>Kea)0xA4GOO2H zBR+1H$UWf__0|@Nt{)@LhP4v2=Dfs~<w)G(4<tUnRA)!*kvR!VbgplMBm_rm!q2Tb z?{bhPeqOIhUtH4L4wUKb-GP$4Jx}jw{7F*Q$LXE5Cna@duFNm&kOj%HvM}XkNt-lI z(!;7H{n{^*G2)g*SL!tLbh2cfD$&J9!(>TkmS#5^lCvXHb8F5>UR8kJRXn0gB|`4b zP+gYlFRtmmx_t5l$@hL!^RExeJ>Ng41>d&IiW8=~yI+!f_tnYDmNvcbg%T-zew*H3 zo+V;d>H~R^vMMuQi(&%g!I_D=I`E<tk6kXsSN*i)(mXLw4QuIV6J<^3r@FT7nmn{) zzm~oDjjXHMuH_ZoQXy^f@QSbXk<@LnKIw>7PN|efC%&c|jC|Sf>(g4LiBkP#kv`TF zDvuvZ*C$#N^vPYTq-INq);^OaPno}|m&0q+KfT_g$NZ<SVKfa{b1?P|I7S=B8(oHv zVYKvcKRD<J<Zf7Z&g>kQk?UT@Vc*hRe9v$=4A;Nd-gWDl-<jQlY!KNYvPEQ%$R?3p zBHKjviEPx?+9|SCWUt6(k=-KOMfQtq7}+thWn|CDrjcDE+eY?{Y#iCSt+jPz@BB1u z-qzYZvVCO#NCS`#AT2<8fHVQ=0@4Pg4@e_ytxh1Vu(f)DG{e^F2GR~&s~<>1kd7cN zL3)BT1?dXX7NjpoW01}utwDN&G{@HJ4$>Z^KS+a+4k0Z<dW19y=@QZ=q)$krkWL}3 zLVATX%hu`^(k`T5NW+khAuU6ChBOW78qzkTZ%E^i&LOQsdWSU6*6JS8KBRw01Cb6Q zEkt^VG!f|{(nh3@NF$L>BCSMvi8K@GCelt@tDi_ik&YrQMS6-f73nI{R-~^;W0B4x ztwnl^G#BYE(q5#$wpN3Y4kIl_dW<v~=`zx0q|ZpBkxnD6MtY4j8|gOEZlvEx!)>jO zBP~aIjx-(VI?{He??~g3&g1`~^}hTCM90PFy3<@yIZ4jB&e*7&InFp|Y|L!mKLIzD B3|asH diff --git a/telegramer/include/pytz/zoneinfo/NZ-CHAT b/telegramer/include/pytz/zoneinfo/NZ-CHAT deleted file mode 100644 index c00410988272dec2ae70ede88720b4a46146a4d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2068 zcmdVaYfRO39LMqhA$8-f<`|Nprg`CTJBN#4L}-AZAYJgJMD$7X9MDKh61s?HQaR^p z+O$X1EN2c|q;1qzfo|1$P?@dSW!X2|@<Bs$wQd@x-}_$=d(xxUIot2`Kj+EW*?ymQ zb4y1w?ET|w{NLYhck-OE%^jz{*!N~)<{y@L<e(*8IH=_Q%a-!laZBwzV`;CyVCiim zHu2d$n^bwgCileb%IqCBWo5lh8H-v*S<o`hJ*Lc=b;^8it+E0+$~v?}SACPBX-~}8 z)g$MX9ZS~q=TB-z?WksMJF0666Kz(<hc-L?d%HG2pg9SPZEotfntOJ(%^Q73^G{5$ zoDU8uI511OFYi>|uJOv>y<P?D&Z)4oQK6;pD;(*tqMQL0moBrCq}?jb$g|QP58HyD z)2-~2T~_|-MO%2R)2<s%Q^npUyT1PiRc;E|4ZWvS)w*1Z+FsM*ibCC3xlh&8CaWg9 zM>UszQteoami*FZb!RG6e>!GM-_F*up?YiR_tdyQXv?>pRa1AW-4q?Oo0X$mYRzt~ zN>THi6SiXdc||6?Xp#6y-S*w%w({$vTJ>(bwG2O_+h6HZ>q~=n$5S!2J-Ns3+)yvI zciCM{L9MQf*qVY=-94|&)}~!hbkYh%e@(X7#RY0VJ!%~vPu04ik8J(mCEc_CRqK5E zE8W|@*EVb%)<zBLzExk^{Z)Iksr)VLn$e{Praotzy@)pd_NaARnR-57V-JpG>7h4j z?BT<u_Q-+N+Ojj#dbd|=Yy0oEH6D)##wA?wpS}}3Z{V`KjNXxIZ=B~HZw(}Q-mwO6 z;$LS&W3v|wg+nFoEGp&<yVLUm0k8RATzB_R<BNu>cOciEALQ!$y6cZj0GR<Y1!NA$ zB#>Dk(?I5dOaz$;G8JDp7i2QNZZ^nteBFGI3HiDiAyY!;giH#V6*4VkUdY6dnITg{ z=7vlTnH@4cUpGHwg2)V!DI#-3CW*`vnI<w%WTMDSk*OkcMJ9{P7MZTEn=dk9WX8yp zkvSuiMrMsn8<{sUab)Jm)RDO(lSgKcOyAedA4vd`0VD-T4v-`uSwPZ&<N-+pk_jXg zNG_0MAlX3Df#d^8h_A~Ck`g2*NK%ljAZbDJf+PmX43ZioH%M}j>>%ku@`EG@$&jy0 z5t1V$Nl2EEG$DCH5`|<6NfnYSBw0wdkaQvWLK22#3`v=<%Nde1Bx^|8kh~#@Lo$b? z4#^#oJYUy<|8e$`D*Ogk=7quwi%Z-Gm&MB5#`64<{K5jh%55yp54q<e_nh=6i{2Jl diff --git a/telegramer/include/pytz/zoneinfo/Navajo b/telegramer/include/pytz/zoneinfo/Navajo deleted file mode 100644 index 5fbe26b1d93d1acb2561c390c1e097d07f1a262e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2444 zcmdtjeN5F=9LMnkqQH%ZQ;8v<nU)CgtR#>b6-0<P2(NH8!YHnHS1a(Ln-=0RD8?U+ zvtrCL36!+fOng|gv7xTv+REl|Yg!BKxhxw!Y?8peo%dPwPk;4STVM9OuOIjS&-=Po z`_|@&f812_4G-6C9^R)b{@GWcUmFNlJ<liU-dDa?dprTXw{@E6E54}vI`*j#+N1RF zyx$s!>*B?gOursm&?$b8b?d7UesOi|Njd(VTTGm*mXq%nh`_OY8GIv2h@FWtq%9yq zpPH0YHYBL9x|w=v#e|wxIIhF9MpXC<xjIswP>}}?Nyq3Ob<M?I9d-V=h(6JxW8Uo* zv2XTB`ErZ6w*6Uo-Bypd-d8WDuPPC7rT5Ai`6=Rtlm#+=Zn2sf>5vJb$tvNO`8x57 zNR>1kp=X`^LCrpNN#EFeTFvp#k~i%*sOGK=%6aQP6gTI7E^k@(wwNFHo=i^FA~|qD zr#Lo>l#!D<^^!~6I=EM-oo!U<-OuTaBb6$%*{ic&TBNeQtuklR47IRitz1+&rgD?- zlegu3q85jz%DluYBJbNMnLmDB6rB1=-u~%;Skmv%cMR+nOFMqlckbFQ3L8GsceU<P zcbE6;d+N8TqRba{anTx8{Ogb`NpBJ*XZOp}=vq;Fq+Kq%Tqw$3eO)jAxJEgf+VuVJ zELG(-K3&l@M?J8lOjr6t)rzEa?OOSja!thQs@zkm>gzP=p8ch855>q;fg!QFZ&W@w zvR~A+4$FrI+eK~tQMsmjy?EGpM%T5qsYlWe>qoslRUh4{JtbwzbJ?%G$?3{_+O2)z zvC4O#K(G7eXSKeoT0V9rMm+A%mrooV6%AF1vaw@WY{;FI8yk*_O>r0G=JGDFIWVsM zd54vM<TJe`zEf=(Jg&En`PI|iz51DRZq?M>qPHC@P|dX-y?tkr3Jv-5Z%WwTuYY~@ z-y00>?i3;ze5)rU%)Dz6Vc(<dr(EuI31^XcR+y*SJQXgpA|XQThwERgFKDhdEUF(_ zA+khdjmRRARU*qo)@d~hMOKO|)oRv?EEZWUvRq`nR<mGa#mJJ8HKScLFRYp~%LdlX zv2bMN$kLIuBa25?Z#BzD)^9ZhKq`Qg0I2~-5s)fylmV#&M<I|(aFhb61xGQEYH*YT zsRvRJq#{;R5~L<bQIM)2WkKqK6b7jbQW~T-9K}JZ!%-fjK2}p8q(W9xBBVwfMMA2C zlnJR5QYfTSNU4xoA;m(fg_H}a7g8{!VpdZ!q-GpNL#oD6Hl%JGg+nUGQ97h{Nb!*B zA>~8rXEg;xDrhw&L~3X?MMSE|QAVVWNFk9*BBexXi4+s5CQ?qMo>o&(q@q?+QlzF< zQ&gm?9A!o7%28OPvK*yFYRgevq`F9Xk@_M9Mk;JIB}Qs&HAP0MY&B&@>WmZ`sWeBa zky>*U8>u!&xsiHv6db9z)s!5mxz!XMsk+sa9jQA~c%<@3>5<wa#mE15^&RHNV6pj8 VNOLaC$jQh`b7p5}WM^bK{s0D?lEVN1 diff --git a/telegramer/include/pytz/zoneinfo/PRC b/telegramer/include/pytz/zoneinfo/PRC deleted file mode 100644 index 91f6f8bc2e234bafd484146986bdb289082c3588..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 561 zcmbu*zb^w}9LMo*sb7uZVlR)P+QDKF36WCLNNV90hMH8mol%_2lQ6N|X0Wt&|A0ig zYPz+k{ufWWnRwpMAJ9ZRm*-x?E%$l*C$;LT3_WT4b=2NzZC~|=C*8PAnz!SMZcTj$ zt?sL|$L>r!EJ$Z#%XIIr^!v`U`Pj?IXSr#5n@#x|uLk|}K=-#pGy8I==4y|zif-%k z``2;iyrCBIr}1LxMlH>RUUvCRWd{{6H&If#PQe>V40%K@diI|riW5nZK+9zLz?b5# zy+^+If0*`TPqbFdBNLIC$W&ym(@sWaJMDC2K9T^*fTTcjAW4ucNE)Zj!x|DHnUGXS cE+iR}4M~UOLlQb|MkJ;4e>qdO>B@TQ7g9&Lh5!Hn diff --git a/telegramer/include/pytz/zoneinfo/PST8PDT b/telegramer/include/pytz/zoneinfo/PST8PDT deleted file mode 100644 index 99d246baa35cb9c6f56d50adbec163452e2a47fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2310 zcmdtieN5F=9LMnsio{c$9#A5%lbUo9u7H9l_MqY}m<Y!8CT1qa!ypY6LMS6!tg_}f z<WPIMeRZzonxSJhn?>YSt2KIn)+`%iZKbPNWo?nty`A^*&;IJa*17wg*KfbS?mwS* zU|mC9p64GY#(v@9^w@`Y6?(NqUOAO)<jG7q_`)CN<K3w;H1qeZL)Ej@#qlTfrSo-a z;!Lgn>%$TiKIzwy*D{sofLBL7ov5PQ67-~Px0uP*84|NP-b`5%C$Tv{nb<wcBu;%| z;*P$n<9|735=MI^@vA|VG|(+qzSXO)I`W2A&v&b<cN=|8d$YQ>VY|MrYK=-R4d|&0 z7pQ680-Z9W(p(>xqEn;uP3ndDa>KXNOxn;?Nq_H8GyV9u+<5S;(XDgzO^?50W(1S; z%*L<Pto)z#%`4tkvy(s98JQuK5grq7{4V7k`BXB0*rew4AD67*4wD_)E55#sW^Ttr zlCyWI$*q4*=QYnV^ZYG(e&rOCpApmrdFRc7*g{>H7NZK!rRs&3&Z$KoOz5JsAF0L1 zQsmZCudCa-F3RmM?ooF%eIrXckC>(9{j#j)SyMb$WO?xxv;4PV?azKj`G-&F(xlC* zv~Ry&F}6zG`BI;({Ib!M?d*|NZ>}`u>vu@SKA%~=xJvG7Ri-knNCImvn7~B31oOWz z!S5F7wbOr9Yu``PRpDW^?r>P&Gjc+$?;O?l_U~64YEJ9>LhWi}*@#p>)nhhg56J!X zJ4{W|A*uCOncA^Vsm~}f^@DZN5Swlqj+RK{_Yw2JzFNKcvoy7()vq7y4XdW0S8wea zRoe;@^!BFHsyTT~x0D}NEs;2BEgUedBR@)8$|2L%KPc_vo#x?CuRL<5&g|^ymPb#P zn2rt2^4Nh)6Xl8WL{GBUf9V&AbnWs?jznIzf6v`lxI6Lk_ln|xr=rq6ciX2Tz(k@h zUx9xyrieWZ3K<nLEM#2Bz>twS?a+|1A%jCkhYSzh_;_J}96Lf_h{za`K_a6>hKY<5 z8K~2a6d9`1juja!GFoJ~$as+fBO^wJjEor>G%{*r*vPn%fg>YFhK`IK8NAbu9vMC| zek1@$1dtFQF+hTVL;(o{5(gv@NF<O@IBhJDU^s0wkZ>UJKmvk91PKWe6C@}|RFJSB zaX|uuL<R{B5*s8qP8%I0JV<<y03i`VLWIN!2@(<|Buq%0kU$}kLPF)Vu|k67w9!Js z<+Sla0)|8k2^kVIBxp#~kgy?fLjs3H4hfyp#tsRd(?$;opVP(<2_O<dB!ox|ksu;b zM8b%~5eXy`NhFj=ERkS3Z8VW^I&D0WfI4kNk&q%WMS_Y%6$vX6S0u1VWRcJ!u|<OG zw9!Sv>$LGj0_?OAMna6l7zr{GWhBf<obi7b=y==hLfh}GvU$EdUrtt8ZmuuamlOXt D9#L8< diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Apia b/telegramer/include/pytz/zoneinfo/Pacific/Apia deleted file mode 100644 index 999c367c3f201c957a8ab746ad89669c4c9ed5ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 612 zcmcK0u`feW6vy%N^u0<N3<d*(jYQML^9%;WBqVJWPf@fd5>*QWiAic95`z$lk-=c| zVz5d?B(}~LCkCUx0LS@tl}HTU&As2@4fotnSXe9f(XU^J{cW_}zH7&^_d(Cg?m+sy zJIb6TCA;&=+0Vm^iS1^|mA_<?dw6P8WO}5D!>)Uo>3rvW{8I8-<Jq@;nR~h6`NxVB z?oN5(dXE>cB3U|G<>JAKls3khD_c@kRj0v?xv{wSb9qFOO`=+|aa4<A<f?A6lTcmB zQ47&+jarEw#A!=sLQ465&}%2QPf0(p-A~~ko^8|U`p}czrHbdHAh7ER3@9j2aMWug zC|FR?px{A4gn|hL6$&mCWc3;w3OW>gC<sw7qM$^<iGmabD+*c^ylB<l@Xy4FLBk$S H`9b0vW(%dG diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Auckland b/telegramer/include/pytz/zoneinfo/Pacific/Auckland deleted file mode 100644 index 6575fdce31183d8238b18f2f30ab5b9227c7071c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2437 zcmd_rdrXye9LMqJpwhVKWemws!@O`gTvUW2LIninl6fRN8GVz42MwVlp$lXgnPXVf zmaRD|$pO<6kpwLTF@#zdvNfreebbp|D>PSA%fg=bv;M3<`l~;Bp6&Pg?QG|tvwhyj z<t{CbH~#jy;jd43T5=!1(K^~X+CBT1ZZ2rAK4%_ScuqPB`ptL3`{iI_tJyiKRk}jz z&8{E&^w8BZ^YBN_`tDGk`QBNN9(nt+=jibdwR`K3r>AMJ9$R<V)4OS_9?#wBdB3Pa z`<%6&6WI;YA1I!IsB#&&Qe&R{>eW=AkU!LC?{4+IxLf=he^>vZV;WF<S_8Y9G^nsg zZ+xXr$ENJkan&U{KI~bYP>`h)Mv65!F;auiZ<3IynG({!T0$KW656~-Zu-t&CT*T2 zH}_nSuo6F+{Q96w$rzTY8{6fUSRV~9{Y0k){h+rx9Wvc(fzAjxD>Kea)0xA4GOO2H zBR+1H$UWf__0|@Nt{)@LhP4v2=Dfs~<w)G(4<tUnRA)!*kvR!VbgplMBm_rm!q2Tb z?{bhPeqOIhUtH4L4wUKb-GP$4Jx}jw{7F*Q$LXE5Cna@duFNm&kOj%HvM}XkNt-lI z(!;7H{n{^*G2)g*SL!tLbh2cfD$&J9!(>TkmS#5^lCvXHb8F5>UR8kJRXn0gB|`4b zP+gYlFRtmmx_t5l$@hL!^RExeJ>Ng41>d&IiW8=~yI+!f_tnYDmNvcbg%T-zew*H3 zo+V;d>H~R^vMMuQi(&%g!I_D=I`E<tk6kXsSN*i)(mXLw4QuIV6J<^3r@FT7nmn{) zzm~oDjjXHMuH_ZoQXy^f@QSbXk<@LnKIw>7PN|efC%&c|jC|Sf>(g4LiBkP#kv`TF zDvuvZ*C$#N^vPYTq-INq);^OaPno}|m&0q+KfT_g$NZ<SVKfa{b1?P|I7S=B8(oHv zVYKvcKRD<J<Zf7Z&g>kQk?UT@Vc*hRe9v$=4A;Nd-gWDl-<jQlY!KNYvPEQ%$R?3p zBHKjviEPx?+9|SCWUt6(k=-KOMfQtq7}+thWn|CDrjcDE+eY?{Y#iCSt+jPz@BB1u z-qzYZvVCO#NCS`#AT2<8fHVQ=0@4Pg4@e_ytxh1Vu(f)DG{e^F2GR~&s~<>1kd7cN zL3)BT1?dXX7NjpoW01}utwDN&G{@HJ4$>Z^KS+a+4k0Z<dW19y=@QZ=q)$krkWL}3 zLVATX%hu`^(k`T5NW+khAuU6ChBOW78qzkTZ%E^i&LOQsdWSU6*6JS8KBRw01Cb6Q zEkt^VG!f|{(nh3@NF$L>BCSMvi8K@GCelt@tDi_ik&YrQMS6-f73nI{R-~^;W0B4x ztwnl^G#BYE(q5#$wpN3Y4kIl_dW<v~=`zx0q|ZpBkxnD6MtY4j8|gOEZlvEx!)>jO zBP~aIjx-(VI?{He??~g3&g1`~^}hTCM90PFy3<@yIZ4jB&e*7&InFp|Y|L!mKLIzD B3|asH diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Bougainville b/telegramer/include/pytz/zoneinfo/Pacific/Bougainville deleted file mode 100644 index 2892d268094ea785b045e53cb441a551672aabd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 268 zcmWHE%1kq2zzbM`vMfL>&;TUPIGZJ0($4u1GVgjr{r~^}8JQTFnHd-+-T=xm^eg}= ztee2V!N4$U0|SqbZwQ07p#g)ofhCYOWC$T_85__TkcFZ^v;T)}3(^45MQ@i(0MQ`J zK{Ut#Ap1a$0MYe8F-9h4CKjOW5SMg*0J#L{9!{`(c){)w1i2@`7tK*zHbAG@=^7ex F0RVS6KXU*8 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Chatham b/telegramer/include/pytz/zoneinfo/Pacific/Chatham deleted file mode 100644 index c00410988272dec2ae70ede88720b4a46146a4d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2068 zcmdVaYfRO39LMqhA$8-f<`|Nprg`CTJBN#4L}-AZAYJgJMD$7X9MDKh61s?HQaR^p z+O$X1EN2c|q;1qzfo|1$P?@dSW!X2|@<Bs$wQd@x-}_$=d(xxUIot2`Kj+EW*?ymQ zb4y1w?ET|w{NLYhck-OE%^jz{*!N~)<{y@L<e(*8IH=_Q%a-!laZBwzV`;CyVCiim zHu2d$n^bwgCileb%IqCBWo5lh8H-v*S<o`hJ*Lc=b;^8it+E0+$~v?}SACPBX-~}8 z)g$MX9ZS~q=TB-z?WksMJF0666Kz(<hc-L?d%HG2pg9SPZEotfntOJ(%^Q73^G{5$ zoDU8uI511OFYi>|uJOv>y<P?D&Z)4oQK6;pD;(*tqMQL0moBrCq}?jb$g|QP58HyD z)2-~2T~_|-MO%2R)2<s%Q^npUyT1PiRc;E|4ZWvS)w*1Z+FsM*ibCC3xlh&8CaWg9 zM>UszQteoami*FZb!RG6e>!GM-_F*up?YiR_tdyQXv?>pRa1AW-4q?Oo0X$mYRzt~ zN>THi6SiXdc||6?Xp#6y-S*w%w({$vTJ>(bwG2O_+h6HZ>q~=n$5S!2J-Ns3+)yvI zciCM{L9MQf*qVY=-94|&)}~!hbkYh%e@(X7#RY0VJ!%~vPu04ik8J(mCEc_CRqK5E zE8W|@*EVb%)<zBLzExk^{Z)Iksr)VLn$e{Praotzy@)pd_NaARnR-57V-JpG>7h4j z?BT<u_Q-+N+Ojj#dbd|=Yy0oEH6D)##wA?wpS}}3Z{V`KjNXxIZ=B~HZw(}Q-mwO6 z;$LS&W3v|wg+nFoEGp&<yVLUm0k8RATzB_R<BNu>cOciEALQ!$y6cZj0GR<Y1!NA$ zB#>Dk(?I5dOaz$;G8JDp7i2QNZZ^nteBFGI3HiDiAyY!;giH#V6*4VkUdY6dnITg{ z=7vlTnH@4cUpGHwg2)V!DI#-3CW*`vnI<w%WTMDSk*OkcMJ9{P7MZTEn=dk9WX8yp zkvSuiMrMsn8<{sUab)Jm)RDO(lSgKcOyAedA4vd`0VD-T4v-`uSwPZ&<N-+pk_jXg zNG_0MAlX3Df#d^8h_A~Ck`g2*NK%ljAZbDJf+PmX43ZioH%M}j>>%ku@`EG@$&jy0 z5t1V$Nl2EEG$DCH5`|<6NfnYSBw0wdkaQvWLK22#3`v=<%Nde1Bx^|8kh~#@Lo$b? z4#^#oJYUy<|8e$`D*Ogk=7quwi%Z-Gm&MB5#`64<{K5jh%55yp54q<e_nh=6i{2Jl diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Chuuk b/telegramer/include/pytz/zoneinfo/Pacific/Chuuk deleted file mode 100644 index 07c84b7110ad9589810b916390aedc7ef498f423..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmWHE%1kq2zzf)bvMfN%(*Pu92rhoG!1CvZldrfEE(!8|sQ>@}KO++mGXDQ>a|Wn@ zp=SX|b=?F84hY-FH-tgk(11bPz>*<^ux;!>BUpi$2Waqrk%#+DKy;m%+7=KEvK>T& qTmYg$t^m;>mw@c92eKKNn1PT9=A1qskOV$QaoGT!XQyjuzy$!|p-bif diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Easter b/telegramer/include/pytz/zoneinfo/Pacific/Easter deleted file mode 100644 index cae3744096402e8a452336544edf96ca9ae5ad8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2233 zcmdtiT};(=9LMqh;W^+W2$|3mS{MbwkLQQ*_=_YVJxF=dBs5cpfF(pgrWD3jVzpF8 zYi_!%R{7BM3tcFi%-?#l2P@BoBU`MSbgMNPJy}}*`@R3wRqLXgF8ZJI`@jA>7w6*a zeBPmkmZn1IZ&$4Sgv0f$Jv^swwzrYvy8pLurM@(9LEIA`8>iz7@paXk2wf|YcNdtb zjBJSxEYdNKUt$xE>ew$QB<@m*zU)|7;>Ul~3470}#L+SB??0(7-#wzIG!Lt!r%svV znn5+S>99%3>Q<?@?=)8=56HAxo6NMyPMIFF+)NKIk+idOP5MxoT=i+AzIsQxTyrR( zuWkQTuG^NOGkPP{jJ60pv;3mEzV0i1L)y5?EOSieFUQoZ?|wEno_<MXoqxyN^wy}{ zJocK&e)&boIoxk%_dOxGFSMGxRjWm9-lFrXs-<9Mi!Piqri%0eU7RpamH3b7(wI|H z=1kFLAH}Kiud_|X{%_PRANWn>(<juNy%Q$TdQi>n4;#JsL%Fs2O;c6)hTK;3yqTBs zoK)uz>+0{@Wq$IYo<9+xY9_mN?a?-MNBADS;D{p&hbnaNy;xOO-)9!>Iw<v3r_G%` z+vTq8pY-C!4hbcErk9qURZ9<jYnEO4zFM~J6Vq^hzq+?gOyj<_vb?j$tk_yB_k~uN zl`YwFe~~t;YW=c0b*5R9H6d$$h%!x66IIjr483;poN6A8)GgtYs&&^Hy>4h&J<xMp zKe%I1t#90?+ct`{S3aX3Y8a4?%-7As6`j%<z14K3FOjY@>rD5BGI`|PpxN+wx;*-7 zp4s?zsoL~pvgvsxO+B_gS3ll&QT5g(>0Z}$eNhpS|M-fI`7d8FuDf%C<9PQd*FCVu z7w5XWw>yb{-4E<>>?b4QOIjEVIo0;eRwee7+EZ-*_dXx*KM4Jc&Dfv8ZP`*~zuSJh z-43!J^ftr;JL0li0``P#3fUF1Eo5KF#*m$P+N~jbLpF!(4%r^EKV*Z*4v{S)dqg&g z>=M}~vQK2A$WA@&R*}7W+RY-nMYfCV7uhhfV`R(7o{>!>yGFK+>>JrQvU5+nb!6|z z=8@ea+eh|~Gyv%U(gLIhNE47QAZ<YUfHVT>1kwtm7f3UZZg|>uApJlZf^-CF3DOg! zDM(k4wjg~$8iRBOX${gFPum=%JD#>ZNPmz9Ass?mg!Bk$64E84O-P@RMj@R-T7~oq zX_lw$7Sb+H+b^VHNXL+tAw5HyhI9>S8`3wVaY*No)_L0AA<gr&-9y@k^bctu(m|w! zNDq-FB3(q<i1ZO@B+^Nwl}Im<W_sFgBJD)_i8K`HDAH1-r$|$gt|Dzk`s!)Z@qcY> ee5LJgpv2yb13AI+-2B{<yn=$9V9}pX@xKE%a7T*( diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Efate b/telegramer/include/pytz/zoneinfo/Pacific/Efate deleted file mode 100644 index d8d4093bc804be0730b5f5f530db42d55991afbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 538 zcmb`Dy-LGS7(j1qq*8qVwQ99${Rs|fM@2qx5!%8PGU;L$L3{#HEcys;{?wt9qe!Rf z5IT1VPU`F;&MLG+{7xsKli&?E=ObLWIVafO-^-i6HK<?MtWn*}Bj=y3M)L4NlAX4U zJ$*;xt=BMp{}N5qAH&J3FFSR16K0N{?exKe%<MF6wpf$d)oVNFRb@VXVspb4$#r+_ z!pDXzz8yzPr(3f8z8QIyf>h%;cAbHQ+yA)6)Y|;WZRqHHd1r=x&!uu;JU_47H^y=F z9IxOHUV3$m8+YK>qdG!7A+8W#h%>|+;tuhLG=Ow~w1D)0G=X%1wCPd%KpH_hL0Un2 SL7Kt;?v`59j_WJFpZWoSR&+lA diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Enderbury b/telegramer/include/pytz/zoneinfo/Pacific/Enderbury deleted file mode 100644 index 39b786e9601e1a0bc98aca6d8a55a97127775046..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234 zcmWHE%1kq2zzbM_vLGyQSm$<wP#DVreOAj2_5c6>XJle#hUonNKiq+V<^TWG00s^Q zhHV!ZcytX67<3JdfY^{h+t8RHgamtlM*j!d1u`3CABYCo38G20m&*odyPd9~F&6+! CDl4l1 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Fakaofo b/telegramer/include/pytz/zoneinfo/Pacific/Fakaofo deleted file mode 100644 index e40307f6aab2a169bb957979f5580affb379131e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200 zcmWHE%1kq2zzdjxvLMXU03`hW&fHM{|NnnRCZ_-Y<0}{#{{K%6U|?Zj*mi+|!^by- pLD$fbLEF%nA%p~jf#&_MGY{<mnh&-BL=$BRmkrP=J6%I#E&xI<DYF0o diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Fiji b/telegramer/include/pytz/zoneinfo/Pacific/Fiji deleted file mode 100644 index af07ac8faecad5ceddcff91412539c8c3648fa0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1049 zcmcK2&r1|>7{~E(3knOi=n#cMhi>ZH?uxh=OPzN&#RhY?Uk`}M?pDesu5CXSJarE# zJjD`L7a{sdr(p>r>0lHTQSn?Nu!Go9s_A)u|9~K(8HU$kU>Lsdk6$RBNqT?0()M<f zJ=^DZ-4A;EcWdvv4e8t8BYjUkN@i!LW)`E8ZLUhLa#)WvUdh1T=Q=pMD1#ddI#gcP zp%1kc-~TH9vyu+?yp-Yjtd6vQ(UDqA^FLo`p|C@bf4Zk9Qs4FD(vqCo^HxuP-XaoN zm(hs@9c@06v4Lwk*0>?#-6b8Ly(q<KR*U61Ev?10oT=*B_Zd0Yk=OH2yJTWpzfRta z%H+3ptyI^gvK;Evu{SdHuuY~9t?BgjUox}rwa#2nsd*2ycC;y%bWSfPZ_AaSs#n_r zt>4OP{YOLVVHieQ{^5>z-kpH+eZ11L+4JrO-qvi+_d4Q9dlH`i7w2!k64HRL(ZOjg zkRDEJf^>0O8>A1?2<e2hLV7u^8Pd&Z?T~&>Ylw7oT1%v-)0!e(k+w)*q%qPNX^r$o znj_tv)*k8av<)CTaM~7-JveO>$S#m=Ap1Zzg6ss@3bGfcZ3fwm)3$@`$7vfvcI31z zA$xM#rjT7ZZCl8`kc}ZbL$-$O&1su+{^#Ab9<=qPyW$BmneNQR<FQ2SfN3PVQ>oV9 DXILWs diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Funafuti b/telegramer/include/pytz/zoneinfo/Pacific/Funafuti deleted file mode 100644 index ea728637ac1fa43f2d73469151c688468b34c3e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iT3I1;}Aoae#rv$2WvQ+t7$1gang-hWxKH U<M{(J1GkA>Hb8UjbPbKT0Kp&`eE<Le diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Galapagos b/telegramer/include/pytz/zoneinfo/Pacific/Galapagos deleted file mode 100644 index 31f0921ea04cd201675b613082fb4b0b0b91941c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmWHE%1kq2zzbM`vMfN%vu%k_gPg>X0Bv!u2ik>S0_y+&|If(G#LWEv{~DnD|NlET zFtC6~Mj*L*0RxARZwP~~fhiE1F@%s{5zyTKAj^P;GJq@u(I88~G|*xYO_JqYHb5uX HnQ;LCaeg|i diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Gambier b/telegramer/include/pytz/zoneinfo/Pacific/Gambier deleted file mode 100644 index e1fc3daa55eb2bc8c5d6a78bd77a01d193a821a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmWHE%1kq2zzdjwvLMVcCBTEF{{R2~jEw*PH`XvP0LcOd79Zab23-S7h7b}=0vhrk SWE#j`+$M6_0L`_t<N^T6-XRVE diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Guadalcanal b/telegramer/include/pytz/zoneinfo/Pacific/Guadalcanal deleted file mode 100644 index 7e9d10a100f9cbe796d99945dd60e0430dced523..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmWHE%1kq2zzdjwvLMVc#oxH6{{R2~jEoEnQ@?=p%-X=f;^P~_plxW#5JG}UKtuk6 TOaqyL+e9uKpt*LshK5`KtEU_P diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Guam b/telegramer/include/pytz/zoneinfo/Pacific/Guam deleted file mode 100644 index 66490d25dff9bcc8f710b0141f1a02e64aeb32f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 494 zcmWHE%1kq2zzalxvaCQX+5jX@n@@dkL9g?}8}oe(@6CRE_&u9};h(*F&woZahX1h< z9~h?VFfh867BH3bF)*uz7qAyhXJ9{b@dFRXL<XLkb{*dRQyTbE7Z+GL$0aZ_F*2h; zmjD0FOn}xf^ekXtVPL46z`()4Flz%N4_HLN$2Ww*Jvf9x+rW~+-6e#<IRnTEA?yWF zpowfiEczb^L>?YJ1ETB9bbUZH$V(s^<Sh^l@*0Q+c@IQ`ya=Mf-UKl~UIhV=cR>K? zWe@;)8$^S=4gw(WgJ@6?fN5YbfM`%qfN5ZGfM`&VfM`&#fM`h2Ff*~BK~`AUv~K|^ PBO!#iK;fioXut&kj?IX8 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Honolulu b/telegramer/include/pytz/zoneinfo/Pacific/Honolulu deleted file mode 100644 index c7cd060159bd22fc5e6f10ac5a2089afb2c19c6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmWHE%1kq2zyNGO5fBCeb|40^MH+y_ZdPZH-HL?~r#o#=TvGm0a4FH#;%aZP2O|?B zGYcc@|Nl8m3=BXrf`R4#|Edf|4lv0BCI$ZgFHT@!@$n5|@CXKC7a$G?;(!pK!3+$H uP%?xBC;bP4k_QF*Ks3l{U>fK=5Dju7hz2<mOaq+?qN(g$E}&lw4Y&a7X=UL6 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Johnston b/telegramer/include/pytz/zoneinfo/Pacific/Johnston deleted file mode 100644 index c7cd060159bd22fc5e6f10ac5a2089afb2c19c6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmWHE%1kq2zyNGO5fBCeb|40^MH+y_ZdPZH-HL?~r#o#=TvGm0a4FH#;%aZP2O|?B zGYcc@|Nl8m3=BXrf`R4#|Edf|4lv0BCI$ZgFHT@!@$n5|@CXKC7a$G?;(!pK!3+$H uP%?xBC;bP4k_QF*Ks3l{U>fK=5Dju7hz2<mOaq+?qN(g$E}&lw4Y&a7X=UL6 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Kanton b/telegramer/include/pytz/zoneinfo/Pacific/Kanton deleted file mode 100644 index 39b786e9601e1a0bc98aca6d8a55a97127775046..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234 zcmWHE%1kq2zzbM_vLGyQSm$<wP#DVreOAj2_5c6>XJle#hUonNKiq+V<^TWG00s^Q zhHV!ZcytX67<3JdfY^{h+t8RHgamtlM*j!d1u`3CABYCo38G20m&*odyPd9~F&6+! CDl4l1 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Kiritimati b/telegramer/include/pytz/zoneinfo/Pacific/Kiritimati deleted file mode 100644 index 7cae0cb7562e5c0f9fa46913b71a5c3628c01bbf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 238 zcmWHE%1kq2zzbM_vLGzf03t#^G3c{uFR1_j|34!WGxPudIY8n6|FeK<{{JsdVBlh4 zIP!pj&&M}}LD$g0!~jGxXd9X^gpgnp(D46t<{lsm!8U?ukgXt^bep+sfcD$z8k%qc E08<Arwg3PC diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Kosrae b/telegramer/include/pytz/zoneinfo/Pacific/Kosrae deleted file mode 100644 index a584aae5eb8187f88600e97b6d6c764245a37e65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmWHE%1kq2zzaBmvTQ&s(f}l82u{7Q!1CvZgKteQoP5QVa7mEw!~Ztv0JH9Nhx-5j z|1&Z%GBYu<Ff#uC@9qOs&oFBP0}BH~-2?^>28Nym3_J`BD-JLSK*W7~Lm0FT4H>iz zEP=EE5F0Us5Oxq3&{%dL76ID#U*zGIEg-tiOvVI6gB%E=L5>8`AculzkYhnK*ufwM s(9t0GfgBE^>w!XyOw3Fy%uK9IOfc8aI0KR+)el@YKwsJE8X9r|0FqT_fB*mh diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Kwajalein b/telegramer/include/pytz/zoneinfo/Pacific/Kwajalein deleted file mode 100644 index 1a7975fad7f7e96f7101eb3c64c9b420eeebb621..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 316 zcmWHE%1kq2zzf)cvTQ&s(Eub4zBRpY@)cLYdAG(7|J$SkbjtV~>i_@$&&b5g$im9X zz%b_lP!+?h4Gb&{3_S}NI2ah}CNS{){~zwaAi%(|;sAq)k8cQrwxJ;q8!%`aSTg7u z8Ufix3?U>q1!&{{Ix`6c5Djt?hz2<eM1!3MVgQ{7atp|bAi5qXL<MJZ*#I4D2Xr(S E0Dm)39RL6T diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Majuro b/telegramer/include/pytz/zoneinfo/Pacific/Majuro deleted file mode 100644 index 9ef8374de4fa73cd2a3359d3c3886b11643a7146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 310 zcmWHE%1kq2zzaBlvaCQX(f}l82u{7Q!1CvZgKteQoP5QVaNgFv;D4KRK>h#!{~4JW znVA?_SQr=<fRr-K+Q0y0*G*smn$WX=fro)%#Q_EZAKwrLZ9_u_Z39anZ2-hZ3?U@A z0%+0yIx~?55Djt-hz7X`M1x!fqCqYL(O}ns7(f?-TvQKalJ80`8=zb5bPbKT0DetZ Aa{vGU diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Marquesas b/telegramer/include/pytz/zoneinfo/Pacific/Marquesas deleted file mode 100644 index 74d6792bf6fcb791bfc0af1f827737f612abef67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmWHE%1kq2zzdjwvLMVgCBVm{{{R2~jEw*P*IF<z{QqB-!NB6<8^WM#U}<c?5JG}k YKx6)c%mdks&rB{Gu-SH&R>lTg0Q#CDx&QzG diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Midway b/telegramer/include/pytz/zoneinfo/Pacific/Midway deleted file mode 100644 index cb56709a77dedb471150f4907771bf38f1879ba4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmWHE%1kq2zzdjwvdlot(EubSvi{~^1d42|U|{(FKmG@ZObuXQ@$n5|2o4Tm2qD3| V|3Hvudx8T*6Ec?zXt<#v7XWnZBuoGR diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Nauru b/telegramer/include/pytz/zoneinfo/Pacific/Nauru deleted file mode 100644 index acec0429f147f40279107a48cb85c3b0e9f56c94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 252 zcmWHE%1kq2zzbM`vMfL>wD`F;%b5u;Ph4_V{~)Ni;zj-c|Nj}87@3(F80OS~j99n> zq_A!R0~Z6siUSOMKE5Fg+J=V41`OH;mOz#fLkJ1h0Zso8vJymttOd~^t3ft_tOwEc SKrs@Xz-0q;h@GyX5f=dOmpQEf diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Niue b/telegramer/include/pytz/zoneinfo/Pacific/Niue deleted file mode 100644 index 89117b377325529e65994174eb5a2fe90e1f39ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmWHE%1kq2zzdjxvLMWNe_@u-*JV2r>i_@$&&b5||9`?31_mIxfPv-z|I`2mE+5|z p23<o#BLfi05JG~{KokFij0e#mD?kpa2eOH<hRX(MnVq2_7XTJlG)@2j diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Norfolk b/telegramer/include/pytz/zoneinfo/Pacific/Norfolk deleted file mode 100644 index 53c1aad4e0a541ee3c2c4c6de33a79ca93a7d244..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 880 zcmc)IJ4{ny7>Dui$i=h^ut;%W6~e8j7=*--5Tk*FE?5bYD0evZ#>>5e&=V#$#mSgp z(20d0BqWkJ=mr=EM+l1xgs{A9=u*DtL^cMPJWameNr#iu|I<W!vOi|dy{`WIw#>?X zv2TyPh7&)Ox9x@254_gAeecn`6@5Ins2!<!?eu1~t8GNP8+)~<_JQ`6+|$0uE$uJp zkpU+m1IKs8`&}c+UpM5*_lP`Q4au{`zw-Rek-YHtWKe#JBu{kc;h_#U?dnL~4;?N2 zs$)gVIv)C<<9}Z3#G$XLogsa>)vc2o_jPJ5Ew4U_Ouy}tnb{Ue58sh=CX;clWM${% zUgVQ&%tFfkGPB^DY-2utF=1m?Hl6&l`}d#CHDi{(IoF%x2~*~}Zj7@!YR_13y4>*y z$6kp`ENWjgGgDJIhb+r8SU1ovjI4|-jjW9<j;xL>kF1XrKq??5kQzvlKwAYVgVaF^ zA(fC)NG+roQVl7G)I$m)6_JuiO{8d`t%{UI>LP`a%1CLXHc}j^j+76yTmHZL;oJ7F ZQD5P@)>wUc+>KVcmC^F}O{l64p90#q=NSM1 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Noumea b/telegramer/include/pytz/zoneinfo/Pacific/Noumea deleted file mode 100644 index 931a1a306f70eb0c7578a65425086270c6ea2b88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmWHE%1kq2zzSHQq8vaP#O9gw^+*Zdvt0rFVY?3q9Gep$X#U>8Xo2?!V+ogn`v3p` zGcf`oGYbm?!yF!v-W3NJf$Uiu7=R`q*gn1?4BCc9Kx_z7#K;&zf)jwY{0BJ$q!Z*6 l5Djt;hz2<cM1!0KqCrjr(IDr6XbPOjWdn4rovxuF7XYYcI)eZJ diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Pago_Pago b/telegramer/include/pytz/zoneinfo/Pacific/Pago_Pago deleted file mode 100644 index cb56709a77dedb471150f4907771bf38f1879ba4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmWHE%1kq2zzdjwvdlot(EubSvi{~^1d42|U|{(FKmG@ZObuXQ@$n5|2o4Tm2qD3| V|3Hvudx8T*6Ec?zXt<#v7XWnZBuoGR diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Palau b/telegramer/include/pytz/zoneinfo/Pacific/Palau deleted file mode 100644 index 146b35152aaeffb5940d30910ba37703f4096285..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa(`2WAo6d;G8ZUO^~k8cQrwt*!>2w{_$fm%SA p18B&9k%#AVKy;m%*&C2q^*}Zw6BFEM5s)CV1za{jYwUC_xd1P4BeDPh diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Pitcairn b/telegramer/include/pytz/zoneinfo/Pacific/Pitcairn deleted file mode 100644 index ef91b061bb145b2658d49fd5065ed74b1a6cf6f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmWHE%1kq2zzdjxvLMXY03=LZoH*+L|Nqa(#Pt7v8xI4+|Nk8o3@rcu_ct(b`S^w~ n=o(lU8-PfL5E6_An)tuYT<;6e2Cx+%nn-K7Y=D;8S#SXWD_|-E diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Pohnpei b/telegramer/include/pytz/zoneinfo/Pacific/Pohnpei deleted file mode 100644 index c298ddd4debb649220e5dfde60948591bc6a3501..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 303 zcmWHE%1kq2zzf)cvaCQX&;TT62u{7Q!1CvZgKteQoP5QVa7mEwL;e5%{~4JWnVA?F z|NnQo0#wB?YXbud14G>e1`Y;>o&^j%5D_2W5C&~SLk4XFOCW8)5JK2;pne86AQk}H z@n7WOP8|?kXQr?QM1vdyqCpM<(I7{GXpqA|G{|uv*VF^qj7-c-EX+(yFc(e<0m%^O OPA(gutL=0R4Y>eZAz3j1 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Ponape b/telegramer/include/pytz/zoneinfo/Pacific/Ponape deleted file mode 100644 index c298ddd4debb649220e5dfde60948591bc6a3501..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 303 zcmWHE%1kq2zzf)cvaCQX&;TT62u{7Q!1CvZgKteQoP5QVa7mEwL;e5%{~4JWnVA?F z|NnQo0#wB?YXbud14G>e1`Y;>o&^j%5D_2W5C&~SLk4XFOCW8)5JK2;pne86AQk}H z@n7WOP8|?kXQr?QM1vdyqCpM<(I7{GXpqA|G{|uv*VF^qj7-c-EX+(yFc(e<0m%^O OPA(gutL=0R4Y>eZAz3j1 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Port_Moresby b/telegramer/include/pytz/zoneinfo/Pacific/Port_Moresby deleted file mode 100644 index 920ad27e629e350c1baac8537bb639a59fd19039..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iUF~1IS_MS-`;J;~T=DZD_y{Lf9l`pcWA3 z1sd`{Y+Douh%S1&WCF;ndLWyT31|_-=*|xeEMN;bK^6q~LT%x)0orA!YiPg)0AN8Q AvH$=8 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Rarotonga b/telegramer/include/pytz/zoneinfo/Pacific/Rarotonga deleted file mode 100644 index eea37aba3724b3517c275dffd57e49bed205c5e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 603 zcmb`@KTE?v9LDkM9}zN0)liPMv1c_@ghHZ=MbSY-kh*2CP8C6FadVK0P`mjCN^iVw z4qXMoty9p&7a%B3b@LVY^Y`4YAefN*1Tuu@8y%jWtcYJPkH1dv<6iUeI%ve1VSZ_P z>rT$JyK1)Bmu~%7xzv`~>W<2eds>!`jEpaIF1c-Tw{@L=_f6quU3;TRGuQNVaqwZ( zF6sI1lPQG-xzHM`#mtxV_a2r1`6kQDLsfnr$qo@ElIhe>cS?wddm)HgYdFsiM4ITl zAsnL1W@4(gABjpBtOhtmC8+XS^oP0pce<**#1ON2y})#c`49;pGC-uTat??jR?Y&E z#>#mh5?MJDL@J0}5Xm62L8ODo2aymWBScDwoDfN^oE0LimGi<5Z~N!O&Iae+3<KvI D<Hm?a diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Saipan b/telegramer/include/pytz/zoneinfo/Pacific/Saipan deleted file mode 100644 index 66490d25dff9bcc8f710b0141f1a02e64aeb32f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 494 zcmWHE%1kq2zzalxvaCQX+5jX@n@@dkL9g?}8}oe(@6CRE_&u9};h(*F&woZahX1h< z9~h?VFfh867BH3bF)*uz7qAyhXJ9{b@dFRXL<XLkb{*dRQyTbE7Z+GL$0aZ_F*2h; zmjD0FOn}xf^ekXtVPL46z`()4Flz%N4_HLN$2Ww*Jvf9x+rW~+-6e#<IRnTEA?yWF zpowfiEczb^L>?YJ1ETB9bbUZH$V(s^<Sh^l@*0Q+c@IQ`ya=Mf-UKl~UIhV=cR>K? zWe@;)8$^S=4gw(WgJ@6?fN5YbfM`%qfN5ZGfM`&VfM`&#fM`h2Ff*~BK~`AUv~K|^ PBO!#iK;fioXut&kj?IX8 diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Samoa b/telegramer/include/pytz/zoneinfo/Pacific/Samoa deleted file mode 100644 index cb56709a77dedb471150f4907771bf38f1879ba4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmWHE%1kq2zzdjwvdlot(EubSvi{~^1d42|U|{(FKmG@ZObuXQ@$n5|2o4Tm2qD3| V|3Hvudx8T*6Ec?zXt<#v7XWnZBuoGR diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Tahiti b/telegramer/include/pytz/zoneinfo/Pacific/Tahiti deleted file mode 100644 index 442b8eb5a438985092d8657ebcabe8859037482a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWHE%1kq2zzdjwvLMVcB_MQ1{r~^}85#foFFwJ*03;I_SbTg#7<3H{7(z%e324ZF TkZB-$ahu3x12osp(0~g7aQq_R diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Tarawa b/telegramer/include/pytz/zoneinfo/Pacific/Tarawa deleted file mode 100644 index 3db6c750333fa6dc1efc84b1abc24528fbc00b0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iT431ju1nae#rv$2WvQ+t7$1gang-hWxKH U6FLJj1GkA>Hb8UjbPbKT0MM`*rvLx| diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Tongatapu b/telegramer/include/pytz/zoneinfo/Pacific/Tongatapu deleted file mode 100644 index c2e5999bb2cbfb7a8059004f8a593d454b49d3be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 372 zcmWHE%1kq2zzW!)qTE0l#1^{bI>+I?-~$2k-|iPIF4tbLV&Mp|D%#;-{rFXYP0%cd z2-)iwBB~TF)c^ngpOJ}~g_Q{enHU(>I)IE?Tfo4=z_9HC0~Z6skq3-?$ZQ|q5C&~S zBO?PKX$-_BAPtO+3?U>q4`}OukP|^P$eAD`Ku!hGAm@T;kdwhQ(Agjw<a7`Xaz2O# bc>zoVy#b=@fo4(3D_k}}ui5Du8gl^v2r^J@ diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Truk b/telegramer/include/pytz/zoneinfo/Pacific/Truk deleted file mode 100644 index 07c84b7110ad9589810b916390aedc7ef498f423..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmWHE%1kq2zzf)bvMfN%(*Pu92rhoG!1CvZldrfEE(!8|sQ>@}KO++mGXDQ>a|Wn@ zp=SX|b=?F84hY-FH-tgk(11bPz>*<^ux;!>BUpi$2Waqrk%#+DKy;m%+7=KEvK>T& qTmYg$t^m;>mw@c92eKKNn1PT9=A1qskOV$QaoGT!XQyjuzy$!|p-bif diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Wake b/telegramer/include/pytz/zoneinfo/Pacific/Wake deleted file mode 100644 index c9e310670f07e9e577791bfa19fefde61351fcdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iOhi1ju1nae#rv$2WvQ+t7$1gang-hWxKH UlQ;u11GkA>Hb8UjbPbKT0M2k3p#T5? diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Wallis b/telegramer/include/pytz/zoneinfo/Pacific/Wallis deleted file mode 100644 index b35344b312c6ca690c0f79a858c3995a05c71ff3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 166 zcmWHE%1kq2zzdjwvLMXS03_=F|Nqa($iT2B0?1)lae#rv$2WvQ+t7$1gang-hWxKH U<5&SQ1GkA>Hb8UjbPbKT0O8CT)c^nh diff --git a/telegramer/include/pytz/zoneinfo/Pacific/Yap b/telegramer/include/pytz/zoneinfo/Pacific/Yap deleted file mode 100644 index 07c84b7110ad9589810b916390aedc7ef498f423..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmWHE%1kq2zzf)bvMfN%(*Pu92rhoG!1CvZldrfEE(!8|sQ>@}KO++mGXDQ>a|Wn@ zp=SX|b=?F84hY-FH-tgk(11bPz>*<^ux;!>BUpi$2Waqrk%#+DKy;m%+7=KEvK>T& qTmYg$t^m;>mw@c92eKKNn1PT9=A1qskOV$QaoGT!XQyjuzy$!|p-bif diff --git a/telegramer/include/pytz/zoneinfo/Poland b/telegramer/include/pytz/zoneinfo/Poland deleted file mode 100644 index e33cf67171da78aa9e6eb02e50f9b9603da4c3f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2654 zcmeIzYfP499LMnskW^Coqaw;dEFzImK@b%oGeT#8sXQv;2@Oz-3=a{S#64xMnREXf z;t(k<WagCELR+G9cA70CJdk0dR<1mZOhdBPm|fr7wboi&z35%nv*-TYJmc;4{dsbh z7mPN4Id|J%_;U8zFYm#QeN^p>ZmI4Qlv~|;;rgz&dabEFq_4cA`fB+O-M#a$*^__F z)RnH!Jz4yvoVt`QpS%&I&99}(r`r;wrTmCFy?tBJnaxePXP4YAI+q@ytqTvzXTuxi zd`z%>-n&xTyiaJ`l@ht^Ib$y0XmqZ8z1O*Vy3*+wP-!}jyk)-MUu&-I+-`n2mt(H4 zd(PZwOg2B}%r%`AL(I*j38t$w$@wWb%=E3D<@mqU$J|ps){!+)PC(vZr=Q2q>7SWs z2E=zbff4P_!2Zonkk4@^sI$Qtba9U}`0O?(_`Quz$k8`V=z-lPY}d<X$d(NzeEn)O zv@p-yJAanBFT*1d$!T(bOrk`F4wDD^43c5KsXDAHL8HEn)ZwiIHTr{|`e5VF8dG~k zM^s$Uk>wxjs5M75Ht(RuEohU`s~ROfrAZQIR?3*L8c7^oB8lB)GA^V-$F(n(@xK;p z(y>&T@I|&J*DcbNrX+o6(<Gf(9jXr(MChdQ@$$%IUro&mkw+shX<BNxO!oaqr$l@u zQ?7j{Q+-az)aH}&*u~c+y?(!BoI0+VTerxx1AFxG6)R-=mW?`N&Ssrin6Fu53)Pb` zMP~&~)7de@b@r`T%?_L_+2{M|+^#6eIeb&+wff2Y-2t+ou|pP?b;_cOX31UDDvL|6 z>XM0X%M%MuX<m4h<fqhYL9au)G)#5r<sG`LdzCIfxmKTS_vlmm=4s)FE9L1IQ*=dL zmOQg8T#Gi1k(D!&WmQ3ttd1KZYtnv^X9J8Bj|$Y{>)%L;|1B+Pd0*CEYtwZNb@F_3 zldi9NS4-<_^o6yxTDG-Jy?nfVdieI}byrUxZ(sXz=TF}L;itFXfB!M2e}la{JbM@u zI@GI|G5%uu{`oyR)+>Nt%)mdMzyD`OrpL^&-_*1$9v+j%OPYP*c-dng?)#m;J^$ib z-?nG=;g;#h^+v9^tG(vPRY$Hna^;b0k6eA^`XdQIGJvE2$pMlCBnwCykUSuXKr(@( z0?7rE3?v&!I*@$0+Jqn(akVKya)KlU$%?B@3z8QkF-T^R)F8P*l7nOiNe_}ABtaZ9 zgrtZ=j*uiFSwhl;<cUL~kW3+|LUQG5lZ9jpNf(kYBw-vf#vx@Ka>gNP9J0nCZ5;B3 zBo4_Ok~$=JNb+25_K@@;`9l(jWDrRql0zhkNEVSaB6&m-iDVK<C6Y@dnXWdQNIG3@ zK9PhX8AVcx<P=FNl2s(FNM4b|BAG=}i{utbE|Og&y{<ODNP=B$hLIE_IYyF<WEn{_ zl4m5*NT!igBe_PBjbs~1x2w%Jl5kg>aU|tP&XJ@eSx3^2<Q+*ol6fTcNbZs3BiTpN zkK`Yj09QK$$P^%RfJ_213&=De^MFhQG84#DAaj9C1~MDSbRhGAOo*$U5oAhS?VKQ! tg3JmsEy%ne6NAhQ{y#N;J2ifGjz+{WOfi}9Bgc%4jmeCQ#ZmEozX3dW2`vBs diff --git a/telegramer/include/pytz/zoneinfo/Portugal b/telegramer/include/pytz/zoneinfo/Portugal deleted file mode 100644 index 55f01930ba92ff6852ae4745e78adb5f96c5b057..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3497 zcmeI!cXW++9LMpSM$F)cN)Y2ARidd3Gb&ZmL{+ThB1XiB2!cn9hSH>!s*;oQqiC&q zM2wS0tXL)XiV>?RF>k3A>roWdH2u9Fe^gKZsGigQ*C!|UbzQ+<KJTx+_s|%B+g~16 z^$FiR4)x7@k0kXn>so5kf{r8f1-~6gTDb6zz9@H!{&ix!zBpr}X++ueB{K^3w6+EM z(vhR|bWc}vS(h|3!{wfy@i5g~mAk^sTH$G~HoE9*K65eGem>k>r`OZh$4}F<yOz*5 z`G@M8D<0H07jMzG+{@6n9o%H*Y+h+@UmT_H_-eYjbG~WrN{BOe$3~iaB08IU+qmfa zTKby%Tg^2OL}cm*3NGonPCk0>eN8`fd7ycCLzsE^_EN`@q>hdw=hMuibDEk*_jK%^ z7oX<H+qXkMHqOmFmN~?6JS^F9eAyEHg#C$mBBg`lq<@U#<fl{gQ@!?@-*wnv<_Db6 zzW3U#ovyxKJ5wfIJM(moR&aBwcDCRH?OblGc797Q?ZUcb<6_!4<I=1c!|_Q^<A?b7 zjLQQ9j4NS{j33*17+3vWjcav_jO!j=+KqBGv>VULX*Uah)e6tv(QX~OsNMedn06;) zr*?P#TJ7HSG|im&g?4{fvi8%U)y9Kx!+01p(|FY66XR#kF~;L6(Z&<!?#7b`A%^QW zjcnx?c-Sh;aJ5xTZjmI$HcYA%T|KF?y_&2N^epMsCcnt4p0}i1mGjcw`KWY%uw7QW zl4DdqWyl(PW=fB3IihA}mZ-IGv8bIQMV(0>3oR~Q)a^S+)C;u>&(^`>HSgx4e$58r zb=PX5flH8V@TjS5c(uOt%6F5E_LY)vWIvJK={Mz@v(L)L$%m!S*lp4`dX@BxED-+j z*`i6{R?#$Uu4q<kk!bFpAet8?ik2R;Wy=c#M8LE0vQ<uyc<Wq*Y`r{625xO7-=5l5 zwpmb9z7yjq+a?Eyb|Ee@DB453TmQNY4k{8MF1fP3=Oxkp?lsZDd6(#rcTl{4WvS?x zl_5fp?2w&OQ$**?C9=!Nk)mtL6xprkbQu;mScbQVllIW|vb%d{*~8mQ_IT_oBdUgo zh|`s2uSX3<?~M;-pZv0-uTe=vX5SY55+8`D*?FRWRK6H6xlj&lvq=n!JR+m(%oBqH zvt&%^4RS~=DTmznQpOg=%b^D+$YB@ka`?(VGH(B9F=ASv9JxGPjEb!z^{LIo=x(h= ze2lvo<5ydZ4Sp)dRkDfkK2_y-^Rh^&@K`1s-zz5EEszt}EEgZ<ZIzSe?Us|X7Riq$ zq|3zAL}@GO^onzdm;7>3KlkGIOa8f1fA=qxE@OR_E&1<%6&F7qq$b8zK{%B_TbVz< zuc<ke|KoF8Wqy}e^|{-NKN;G|ZtEEw&I{k_S3&Ac5TB^WNj=YBtiS8|d;33_!D99F z3v{i8%po#~mTDG}X+-7`nMh<Nk*P%H5}8b7Hj(K><`bDvOEsg&lp=GAOe!*~$h0E! zicBmrv&hsUb8D$47nxmTdXf1>CK#DvWQvhFMkX1VWn`L>c}6B0nQ2Qk)yQ01s>w!X z8<}oPHQ&gDTdEmHrrc7^IWp;%YSxiyw^Z|vOuVI<d1UG>)!ZYKZ>eS<nSNyckpv(a zKvIC@z)~dv$pVrFBo9a;kW3({Kyra31IY%G4kRB)LXeCgDM50AB*ju?1xX8%7bG!A zW{}h%xj~YHWCuwPk{={NNQRIUAvr>lgk;H5rHTJu<cWtw@sKGVQibFSNfweVBwa|p zkc1%_LsDj`a)u-g$r_S2ByULKkjx>eLvn{C56K>qJ|urg0+9?NDMWH;sgj6f5lJJG zM<kI*CXrMkxkQqQWD`jzl20U|NJf#AB005GNky`1snUw%6-g|TStPYcZjt07*+tTe z<QGXWl3^spNRE*tBU!doX-4vFsS=H38c8*hYb4o7wvlur`9>0sWE@F3l5-^KNY*V? z+L63ls>CCiM^caE9!WlueI)%z{*e;^IRlVW067PclK?pjkkbG;4=mM*fSd`)seqgd z$jN}54an($oDaweft(S@DS@05$Vq{m7079UoEMhr#6ZprOLb}>=LT|eAZG`1dLZWq na)RLhXNYxf{8wj4S#`1aa=LhTYSOq_V_zRX?@oTcyes=VR=Xml diff --git a/telegramer/include/pytz/zoneinfo/ROC b/telegramer/include/pytz/zoneinfo/ROC deleted file mode 100644 index 24c43444b6751343d2915843d03e55753d4d7359..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 761 zcmcK1KPZH89LMqR`FoCmn=A^0GKfqDQI3Jm<e?~=l<jvYiS%0xSWNzmibWa8A_Gc^ z!QVR^$KlTZUknWD{NC(h@OyfmS3S@4ygyD`S7*8Wc#7@URC{M@`&etP*Sk)u-Wwax z`!x&Zpsr93{j27va#ez9ohp>~qC>H1bDSuWlg({)8fn$xc`;|*J#xO-p(2CBaxpP! zF58!Nv~NpYRXxacS-!d{2uRF3r{YPoI{q3ox6z>79b~EdjZFQpRBayLKICa?-8_d% zwdsFX=^riPejHU9;SufGSur^$GurEfl`F}W{HseO5V!v$N=u5!(73pHy0X6C_!}MZ zH968?Z(pq=&L1)*?CISxxkxS~8Ilc2hvY*NA{mjCNX{>9QY0&q7RiewMlvI*k=#gf zBs-EG$&XBc%z#XR%<-k21epby2AKz$2$>0)3YiO;jM~h`VLD_!WI|*{{KqLhcGr>$ F&nGsEUL61c diff --git a/telegramer/include/pytz/zoneinfo/ROK b/telegramer/include/pytz/zoneinfo/ROK deleted file mode 100644 index 96199e73e73aafacd89e48cb2855a96d7a134e1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 617 zcmcK0&npB`0KoA#c14?7Qk#qN;~<AkiGwRH?4p>H)21*|8mE0H7m3)~$w^8n2PbF# z11Bj5<7Z79v(sWd<uJMMzLSHKa`0ZS@0<57^FHSE!d$QTb+qc+I=vdVTqU>tRCqDe zrQF$~?N;}4SK)_!eU|oa+)Z_Rl=SZWlDaRvd8LVN^{{vxJ~p>l{!C1ko3cUBW6PlY zZdH<#vT}N9J-znH=fe~0<vb;;>${ffUzTkLIj4PT-sxB!bUMdJ9HS;4`<_Paj~Sf_ zk+0|1^BW>A#EK*IS7G01i1zwqZHGN4*)daOrc5!aS7z80<{zH@sRvJ|JfA{9VTh_J zC>#_L3JZmX!b2gVFj1%|Tof`28-*^a=A#fs)r=HM3MYk>!b+h<3SJ5^g_-^%bwazR G2NGX5oeD|- diff --git a/telegramer/include/pytz/zoneinfo/Singapore b/telegramer/include/pytz/zoneinfo/Singapore deleted file mode 100644 index 2364b2178b03853b1771cb3f34b964fdb82e2e91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmWHE%1kq2zzaBmvK&CH&;TUbnp+p|O81*^fa^)Zsm)IlPEU;Ixa5)hL2Tkzj{5)q z|1&Z%v#_$Uva>TVB*p^OgGd$zhNJ?J5$OtyJRp(*NM=?r2r)3!O<<5<U?^x{kn!;i zVF(6dZ3A-<HZlN_#vsxXh%FdGNN_079sldhf)|5mkb^-q$k8Ag<ZuuTay*Czc>qL% aJOOeq$Ri-S9w<gL&v4lQy=13r!36*{S756E diff --git a/telegramer/include/pytz/zoneinfo/Turkey b/telegramer/include/pytz/zoneinfo/Turkey deleted file mode 100644 index 508446bb6aee2841ab88e82607a6ad6e748e7db7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1947 zcmd_qZ%9>V0LSs?`nP+{bz_9Pc+<9~_m<WCx4Je{%S>!HcdDyFf5t&8ib5V%gGPe4 zDT7wf8GBJA#8%G0%Gwr#Q7fV`boS>^w&_xK##j)+d<I!_QucgLuUc=`>z&KFpX24? zi_7!<#nx`!TqvHq()1_XTs!s6b0DtYtbL=0+9zk2?YA!2M>jP($98{cbkxSpFUrr_ zojD=rc&Nqh3Wv?E-yYc~#`ZcVFBcd+d&-?&&&x*d@=^20YjLBmuiLz_&1>8|T4MHB zCK$J(>CR8HcH2MajW`3|4fbHdL38kKx&6z%ubtaB)*D0pwmJM&neo^CZ_WD+BgW{K z6!SrL#7=4YRHl|3mZ{_K3vZxVdhI^p8+%u#efc;(d9XsJchtpS>O3W<v@M8F`FpAn z*n36>hR!&dcWf2>VMI-DFOXULcc|><jcUgBI`!)MW$Lx)0yVQVtX`iJP&q-L%1s+r zq2!PZjlLqoe|Y7r{(t1`@9)XHvqN%D$5lCZU#ptec3Qr%^D~*h@e^6F;k+t*@1QDL zbVL=e`ar!oJ+9^#u2b_LHK<7DE*TkKs!GP!%7x$Ms<&>J%F@nCx#(P$Ec+-&mbVL8 zu{l*PZVRd<Rd;1&<72fnr%zT@I%-*RuT?$sSAX@Ncl<Spm;5!?di=|OKjweCyUQQF za?x6GVA!fX`MLE@Q<LA=-)gN~^RcyR`zC93<bppIt+whiYOVURBUb&xd~411c}B~` z#Dpg?;YD6a$w`SRssH^o$@|<ZDM^UTus%j2bBoZ=v-3TZgb2<M={$?6LPRP&)BgSW zv)_rXh>03~S5>X%Ehh5vi}YR0f7O>qUr(RhKSb;W4!HhJChEWG89)v?a@bw<fkzHK za`2JEj|6~3fP{d=;HrZ_qCmnx;y?m%)sY~fAh969AkiS<An_mpArT=VAu+k?ppdAL zu#mWrz>vt0(2&@W;P_pM4hhdy$Hzs0t~x?4LPTOjf<&T3!bIY9)qx_By6RAoSdn0n zXpwM{c#(jSh>?(yn3157sFARdxLtMNNaRT9NbE@PNc2efNc_kEAR~Yb0Wt>2ARwcF z3<EL_u6iJlk#N;Rfs6$*7|3WK!-0$kG9bu^AVY$T2{I_is360Fj0-X_$jG?rp+UyR zRSym_I>_)K<AV$kGD64@A!CFL5;982Fd^fF3=}d_u6n4DvBDO82LBg><tx>LRyMcD F_XJx;ZRG#} diff --git a/telegramer/include/pytz/zoneinfo/UCT b/telegramer/include/pytz/zoneinfo/UCT deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/US/Alaska b/telegramer/include/pytz/zoneinfo/US/Alaska deleted file mode 100644 index 9bbb2fd3b361ea8aa4c126d14df5fa370343a63f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2371 zcmciCZA{fw0LSqQ0v8C15>k=qB=Y>=0R^EbF9-r60dl(ukxF8BSP2AU_(W1Vaw{?0 z9L`3^IkwuQo#|G(7TvVgCgupY9>$_{YbHzAdYVOYJKvM57d7AI|G)G99PW7g`??!i zp3HIl>j^WzaCr8a!#!oE`Hb$#^NlC`+&11+EPo#_Q!^(vxcqOdkdA>;SHO!YGO#<@ zHLJZu2Q@AC1=l9&kfKDNGdol}UtZ@6i<;75!xOIXAI|FAz8UpJe0f<$`i6bCpB$BU zym`hIb#PeTx#y_st}Xp?cFSH@bbY&wsc3WET~H_Iq^@?&UC^rMg)MQ#2G;7>^ysMA zAB*+;i-{_3e4)PQlvBkY3(@x;zN|!7fxNGGR4wq#mkFD`6AN>%%fyvuL{iMxGCA$2 zNS>M2so{G?>f~2CZK_SAkG!ul&cCEG2M_D4<D1o@o)@%ywMJ!omCWhLQH#r-mrLrR zRc>;#%***zEp@Jt`Ej#F{-qRIF#U_T|Ko7^z{KaGP$%gJ-#sZF+83&q9Xcdjty8*a z*E_1X`mA2wd{C7vdP|p<Y*VE_U65s&1ETEwX;~4uRa6`wk}Iz?iptkM(5pV{R#n@N z=!f5KP}PmQb<Kf7Ra@xQtGnV=U0j8BdmPIBN4oapUR0iM%jKGQzgY88nyjC>AR2}u z<YSYkMdPlk^6`-&v9@_kt{dzV>#M%kO?^ky6Pf4q2Jddw9I5rjGOyZrWxw_&S19i% zow~)Du3CmYdefyy_0)k5`Se(tc&6(SxmibuR?kw|)_+yB=gpJPwvLI8m}%KreN1%v z=jg8dbE<3dH{Cr~tL~8rz2(||wRP}4z3q!mwY}$cz2k&O^{nmH&kf|OfWTP+LBThB zLqeUm@O3yoyykHD{T=HaL4JR4TR^D&M%Z7X>^+9BBi8Tl-x&~Z?+L4_+>W9;a~?IP z#+-8gC@*n4>bX>!OHrk{nJ0h`&tDh!e{U_^`~!#Q6?3?!_|3EI)b&qsM_*AnvOQ#f zR<l85hiJFRg+20^O#-__wu$T$*(kD8WUI(tt!A^xZmnj!$bOLxBRfX6jO-cNG_q@C z+sM9=jUzipwvOx_**vm)Wc$eet)>B1(*dLfNDq)EAYDM(fb;=r1kwql6-Y0TW+2@_ z+F>>QKpJ8-9YI=x^aN=N(iNmFNMDe~Ae}*4gY*Vz4$>W@JxG6$23bvqkQO05LYjnh z32773C!|qGr;t`5y+WFWbPH*h)$|K#nALO)X_?jZ3~3tDHKc7w-;l;3okLoO^bTnr z(mkYoR?|PEfmYK&q=i<~L!^mF7m+q1eMB0GbP{PL(o3Y7NH>voBK<@fYBe22T52^t zMVe|gT}9f8^c86=(pjXnNN<tmBHcyWi}V+1u+?-JX|dJx7-_QAbQx(g(r2X6NT-oj zBfZ8O%?=6-4!POu3=6%5@88kx{$JDmPrGm2!ijnTdC#a?oRyO$Gpe$)v$C^f_@5Pu BZASnA diff --git a/telegramer/include/pytz/zoneinfo/US/Aleutian b/telegramer/include/pytz/zoneinfo/US/Aleutian deleted file mode 100644 index 43236498f681cc06f64ca2afa613880331fe6fbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2356 zcmciCZ%ma{0LSsm-|Iy&Dj-Bm!Hl7RhrdBt9tc86<UwwrWC#iolmVg)3ox8hdC|nN zoE0&9(I3~SV{FZ&u`@U43+KAv*1#M!H|MM|UA1J6yq)jK){C0&@;rN<&)MC5`}=yU zn_f<L{p)zlFT9+7^Ky@W%Y4rF75FBW|JFKD=g8X=FQ_}G+8qC<Ug<hk;RGDYmVupF zPEgxM9b8xL3n|akp?MiTcUrV|zrDlfiI~-%;p<M=%}aXzk5j${Q@3Qe9`!B!dP+WU zV$z9tcT_&uciMSq&j<41ra>oi^IjQM+~Y*&*2zbbYMq#bZoSBp@5Baf)v>D*mc{<! z=*3quRNO?mUUDW%J^E#&Ui#rJwXCB^#`jLCgvunjy!m(WSoVCmqGVD$9yKEqSDqG$ zeveKH8x%>?KkJo0^@vqt7j*K)_f*Qz7dmyMORerXqQyXsN^AUFrngI#QPeLpD-u*z z;!c^J5v-nYdu2{syvVthEpz9B#FOV@<Wt{Y6>C(cetPtrc&0yEuYLc7kS()1Z~s}9 zUv^19TmOkFSpAJIEa+2(zuu5VDIbfXi{r95{E#Rf8IdJ3&EomNZ}s}`4ye+ulX}Bf zuc)#u1KK%SqRQ9o)*CyLRYhEt_Es)b-nm>|nRQcDUagdymWGQ>XLID{J2yo2N3rt7 z>2a}T|D1ejY(&)5Ps^=C?}*yc+q&-HNwqEIvfkb}pz6cNbVJc@)i85hHzro8#tZv& zlRH;64cF`DYm3#ZM|<UKz8tZmW4nA^#fp~7LfLwFPPAnw%AGCKqCMIpca>?e%fCW* z<Xl!AKe%;g%$VvNyRP@l9#?M+o!4(p?o(Yo!@B!az3QnstoI&!P6Y%81q6rO>j|Cb zzK@T~_1P7d%kOV+T)}>Sdu_lx`(0pviLm!bzOER*zqd7DiM=mcU+Q&js4#Dpc^$7S z-`w*Hyso@;=CaOQ%n9Jb`Rn5S?~#R>Kk#ynn3sFJ-<-8){usyZgVi<2=#b%A&G?W3 zq8%X@hR88v1O|zW5*a2kPGq3SNRgph%~+AaTFq#Y;UeQj28@gt88R|vWYEZ{kzpg_ zMh1?I92q(?c4Y9#=#k-D&G@Y*07wLo5Fjx?f`CK;2?G)bBoIg>kWe78K!Slp!)n5T z#KUR=f<y!f2@(?|C`eS0upn_k0)s>b2@Mh(BsfTPknkY!v6=uO5kf+Q#0Uuz5+x)| zNSu&BA(28tg~SR877{J12^SJCs|gqqF{=p~5;G)dNYs$9A#p<jheQqu9TGbvcu4fD zCVWWztR{d+1g$27NDPr6B2h%bh{O>IBoav^lt?U*U?R~(!imJwY66Nx)M`SC#MEkn zibNF&D-u^Eut;Q)&?2!#f{R2K2`>^~s|hd?VXFx-5@V|gG7@DZ%t)M(KqHYxLXCH0 z9UK@EdauXrnRg$bziVB+?f+@^KheH>3o}7a6Q=0Nr5UN|sUo>FEiE-IRfPQs4Q6_I diff --git a/telegramer/include/pytz/zoneinfo/US/Arizona b/telegramer/include/pytz/zoneinfo/US/Arizona deleted file mode 100644 index ac6bb0c78291f1e341f42c34639458fb385bf8ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328 zcmWHE%1kq2zzev6vMfL>&;TUnEwh?1e>Z!>f;O263unD-INixJ;k@{Lne+Wm*Ia0n zlKJ(cRN(iE2nHrbAY=wYMyCJ&r@dof`2T<P0!Ehq|L0C%-~h9Ee0)O~d|iMz7>L6| z7(z&J%6}k;W8v8VqCrjq(I97mXpqxDG{|`%8stO}4RR)!209f)gPaSZK~4tIWICG* I=zeo90HHg7VgLXD diff --git a/telegramer/include/pytz/zoneinfo/US/Central b/telegramer/include/pytz/zoneinfo/US/Central deleted file mode 100644 index a5b1617c7f70bfc77b7d504aaa3f23603082c3cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3576 zcmeI!TTs<i7>4l=1wj+F14U?9S&?zpP%=nM8G=GAw+LbysUe+MCSs)FY9gtova(V; zpca}P2#i$7LQ*ouYDzJJtQ}CHS>!6rNNMlZ^KY7Irkk2>x@b9@A2N93#ru4&8F@F3 zlD|BE`x8FA@9c-~gSGuqwlPAled8CkZuua+{;31%x%Ud>`Fnmg<w^VWhB>Wf<J4Ap zA!wD_G<v&i@>H9bPJLEhaz9~S?p`LZ)Gam@O*!&vS(d4+o+wqtmzvGb%+{~vW~%C? zm+RM)$EhtdN9e6#!_>9}KV8$$qiTm9)U};$YP+AWY~Q_8z4=wAyjAHobq$TOV@18G zpV2IDS0$*OB@fE3^b*rB_cnPa`bM)m?E(Gn;44jI<Sn|fXP(*<I9cy$NmlRO=h6E{ z998>r`kSUj-Likex8~z%A4~JuADB<#wn>Xrn%1B-(%SZ@`P8#TAE;kwK69_qpTGEs za@Q5<FYdoxwUuS-_B@yBC{EO0ri@Wv%^I%1o}OSjlN03N*idsQEL6TZL(F0OzjpXo zhxxX%L%wTnFkQPF<og}%>PTgqHfwjOA6D$tKQ7y#y7SBR(b=Wyr}X9e*!Vp4bM$=O zbK$+_m%*v}ctEZ>-jgdQ4yBmhmK6E5G2D1+!o|BO(8%gQ@hLrG`Yb*oeHRQ=zBwmp zzbW6VeiOR1f6Pb9|DiD5f5>a9f5r1Mz&x%_YFnuXwpN+I`bBzB?PF%}i;u~WH3jD6 z`wQfhq6~9tUWS~O6>ox4;^p*9Ld+Q>LnQdzvFgl#UJ2=QrV9BnSPyMKp@!`}uFrb= za}~PzGd+C$4s~|nU^(aR_3GSdKgfui-ZJOKHOcv@Yt02gTO{nFyG@v9uO2yIjv48$ z))yU4GU0Vk=!m8pRAkv=9aTL^MHgr3n3Wf(*xW)HwJ<=9PR^8zuRW~d!p6y%QSYm< z{=+1G=phr|>5)rL>@nkZx5=dkUNH%ky*hFG!{)LTZaw~KWhUg;>&r_XQdguurzg(M zSCgVkbkd}2R8sdgNsheLBsZ;*l)!Y8QoTe{yJF2%&#cl{H&0e+ON;d6tuZQnX11R4 z<SFW!ghYMqqN8f+u;JP@ty#HxeRM`#jmr2sR5C;No6L7avOHVOjPef2cCR)wOB&?5 zx;xFRxf^A6*-UeN+D@HQTBL4>EZ1{#v(?<d<$7LnqMFw=U+0DmSGgag>O6lRl~)m= zZ|eL~-TY*V-14E<+*%kew^g>A{ER?RD|VR$aYy9#{0(Md&|WD>FEs_8E?pR3t_s~B z>N|p$t2^p8>!P0d>dvy2dPz&FT3WnF-&GT#if2vN%T^CkeSH4LpT2+k9bdmc{pIic z<Nwa@c)b<-MZDhHDj#33_vLjG!1prH`N<IH>uJCL{OUB9Oq^stQ(cl|KNF|h&lH#4 zHv4@3!1WJy(QDtVzMgf+J|Y{5>?E?4$X+6wiR>n_oydM78;b0xquo+uPaW;1BD;!g zE3&W1#v(h5Y%Q|4$mSxui)=5lzsLq7JB(~Gvd4~glaXC^wA+mAGqTahP9s~5>@~94 z$ZjLsjqEqF;mD37TaN6xquq35*B$M)Bm0hQyrbQDWb2W=M>ZeXePsKQ{YM($Xgh$k z0O<kJ1f&Z{8<0LAjX*kqv;ye`(hQ^<NIQ^zAPqq}g0#fZ_5^7P(iNmFNMDe~Ae}*4 zgY*Vz4$>W@JxG6$1|c0nT7>k-(KZR`64EB5Pv|s?Z|D@ywu(oukY@4d7Sb-HUr57{ zjyc+vAw6@nP2<ruq-{vwkj5dMLt4k9cS!SibPs7CkNzPI<k3N-g*<wQG?7Oa9c>$t zJ|c}oI*GIr=_S%k9^FLR$)lf0LwR%*X(^AMI@+cpU3Ii=Mf!>~7U?X~TBNr~bCK>M z?d8#5q`^EojI@|XkC7(x=(3}2Gmkzajpos5q}52Tk!B;^M%s<^8)-Pwairx)&mC>k zd34>;ww*`c9c|-zbRKCv(tD)&NcWNUBmGBi0OSrpZUN*TaI`l8au+z-+knS?;An3I z9(MwAEAY4%keh+W-GJN<JnjeNhCuEJ<d#703FM|g?g~eHTOjv^qrEYZJHyf58pyqY z+#Eda4&?UWaep8;2#-4ixkY%~Bgjp{<1TTuw+V8eINBQpxl<hNt%BSu$jyS>Ey(SH n+%L!tga6+#|L%?%V9%T}_S}g`8yz(&DkdT=Ha03YDrUfMOLBGg diff --git a/telegramer/include/pytz/zoneinfo/US/East-Indiana b/telegramer/include/pytz/zoneinfo/US/East-Indiana deleted file mode 100644 index 09511ccdcf97a5baa8e1b0eb75e040eee6b6e0c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1666 zcmdVaT};kV0LStFL*AOuCUenZx^UBrvdmhxOs$2dygYfS)X7^*(Gj&G`CoXy!A)V7 zj1gwph`4Am!&tLPDN)B;GiE#Fg4v$G^F7?Tvbk}do&V?GbJNZ5`vh`JHYPfMoH6Db zE@z#&yhpm`(ReP#J$385Y}z-$J$<5IK3qA&eb}2J9~}s~PolrdCq?6QSLLwtH1(tI z&gph~rg!RRNjIEcr$zTg9C!NEQT;sF>h^bR(=P@Z+?N-Q$bt46ckp0^RE>G=tCE0x zT{q8tlQ~DeEtuxM|1w2?F#kQ+7OB1So^l$3+PD9eN{g?O>1hi@`f#((h%HnZU59jL z*nE|FwM;Mk6s;DWJSZ3UqzZp+sm!`QLuBXs<&ydku{0%KE~^|8%Ok^OAm@Py{1}!i zk}irB?<VS1QTNoUyPx&yV6)0S+okgc4ypV-t$Iy+nJQS{pbHzbl<;4ZMf*#|+Sq!z zuGlZuhgHiB8S!Gnr(9V)Gh7sRrpS`f!=mJJl-xAbElTT?b=l+3YI9Yj-qO;g%5#ER z9&S}zla#I~Z&2GJ?&$5=HEMfsP*%;Y7gYndW%bl*QQdw<)_ltqI~w=OoxLfdwys$2 zYKsze1(|a9F-MH>+0V$3-!H%Z{Pi3)V$|q=@bSEsWXJKmn^$}xo_DFq8EfCi+vg;n z&ScNK-{G6O*dK5fq?x<i+?D1o2{`HIJ>7iA@!2N?{$g&PIRztwO~~w!=^^t&CWy?? zYNm+H5t*db%o3R<GEZcp$V`!`B6CG1Yc;b)ri;uMnJ_YAWXi~#kx3)7My8F-8<{vV zb7bmh=gte0=a|_8(?{lyBw#feASqZ)4oDJKlLe9nk_VCqk_nOuk_(ayk`0m$k`I!Q z)ntUEWHmV<Nm)%+NLol<NMcB4NNPxKNODMaNP0+qNP<X)NQzdIBa)=mWQn9{HF+Y5 zBAFtoBDo^TBH1G8BKaZ-BN-zpTTRYL(pHl-lD5_4jU<j_j--y{jwFv{kN<J{q2?DM Y$^0V3_-Dr@#?6ZHCnUrr#LWu*39tolUH||9 diff --git a/telegramer/include/pytz/zoneinfo/US/Eastern b/telegramer/include/pytz/zoneinfo/US/Eastern deleted file mode 100644 index 2f75480e069b60b6c58a9137c7eebd4796f74226..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3536 zcmeI!Sx}W_9LMpa;)WucQm$lLAu8a83sO>PgnGmjT+r~zKnAt=mx@@5l_ud#S(Afp zgQ+NJDQ=jk;TkzM<%0WykET>6`Y0}>c}~ywz3nD0y6a`$^Et!dc=!AM;}TLQ^>F>; zscV13%X8Jfd~fl#{m5MvC`-5fp}tz+l4YO&q?RXNloj)S*LjoI$;$A2wQA%6lOK?+ z3VMEH3Op<In&uyxHRW0Q>nbtdl%(plWh2bG+#$MfQ!leVGemFr@<rL0GFWYz-BUJ4 zcU48>17u536ZLKXyRx;OQN?XeNpZyywcY2o*<QL??YMNpd{=l#m+UJxI~Q%#yYjv; zyVDlyJ@e<7y|L+fU(y8geb^XX>Ygn>_($mdA&IiTdbB#=7bOQy_ESH;Z{$eFTXIC* z*JU#<nWItX^s)F-bG-ddeImTToOCVIrvet5Q+l30?a7xjyOQ<U@@zS``dw9CGDXg3 zCn=rlmJ6xRtBaXo@=Hu7bt$o#Tpk^&E22ZpuYH>8--7(j?+@S9SL)p`SMD6ue^iv2 ztH-zK%F-fpZD*OfUU)>z(js+Z(Pp_hcZsS>%aL0XW~tk;8FFX9ICVEHL8?2=)PMR% z%Do0-^}Xsb=KgQ}^<O6=%!B>yv}bEu<IVSK*AkDZm32Yao~cb8@hBhlK<W<Hs$SH2 zso!mns{cVNY1lMRHC(&c_?iW(k$z7apIWZ{cBM#@;`!Qt^*qz`vq`#HcCvYB)(g6M zYP4xFwzCe12{sS+Ypfp$Ze&_^2v)5cRGQYc8>!YeeWlHXO4au8RcW{TpbFgZvpl+N zgKD4dGLOCUiRuu4(R7?#s2>mCXPy}Rv3@dOl?m!RO$T}QO0aLd4lZ9Qov-xKT}rZ~ zYgwEM$xW5eO}$lE<`C)jNlVo|CB^i3<DTjn9b<ZpIIF^gx|rTQN>rcvex`4m)4FfP zb<^+u4joZ?*z`Y>t0N1q$y3|k)=w`wBm=&fsH4(0$}{uls%K*t%X3LDtASzZGHBp) zYEV^yi4K{dqstbW7{6z9%%-VkaAik5<jZUsdOS+GXHSt~TRN!N@opKO<D*`T43iNv zD%8lf%_J^<zlytGC8NUEs8N^w&6vPaJ!anxGuBg}6Y|Q;xblU1{QM&GQpr@En6$)9 z$Q`DYd$YWpHAPJf$&pu5+$za0Lz1JzRB~m4qy#lnDL+L@YP~9zx;9WIR~%DQaw5#s zgE#c6>21wxg=IP|-eY7@k$yc~n>W&y=xG6a%=Fk<db;Plr1#BH>E*j6qh*H5C|M!1 zsuR?kx$ntaCnMGD%oLfkHBe<H#>m`HU8;7i8vfMrso_7U>3{Iw{k_+_E!XApdVkne z%g5_2Uhit)d~fW0HXZ7Ya}643-;wqmZQtQ>cFSC@TFysY4K~ngpTs)mBV-GaJw!GU z*+pa<k$prq64^;)E0MiKHq+7WCbFH5c0Z8~MRpX~Qe;n&O+|JU*;Zs<k&Q)m7TH>4 zZ;{PKb{E-RN4vks20PjvMz$E)V`P(&T}HMU*=J;<k)1}i8rf@Pvyt6Kw%gI}H?rZ5 zcE^z|NA}#&ZaT8-$hIT<j%+-#^T^gCd+%sBAK86m`;q-e8h~^FX#vs$qzOnDkTxKF zKpKH`0%--(3#1uHHymv{kbWQyK{|r81nCLV6r?LiTadmWjX^qtv<B%7(j25aNP8S@ ze~<<t9YR`!PLKFPlXz^GfHon0LK=m13TYM6E2LSDwp&QM9Bsdlh9Mn8T88utX&TZs zq-{vwkj5dMLt2OQ4rw0JJ*0g||Bwbc+72QuM0$uc5$Ph*Mx>8OBau!btwef>G!yA2 z(oRR)Po$xawxdW(k)9$=MY@W#73nL|SfsN^Ymwd}%|*J4v=`|w(qKp1VWh=KkC7%L zT}IlB^ciV1(rKjCNUxD*Bi%;Y?P&XrG~Cg49BH|u?K#qPr0YoAk-j61M>>zR9_c;O ze5CtG`yFlnksH9#-T}xh;Armw<R(Dw0^~M8?gQjTK<)(ORzU6r<Yqwb2IO`??g!+C zaI|*>a!WYcdjh#B9PM3!+!n}vf!r9#oq^mM$i0Ew9LU{)+#bmNf!rXD_6|XA5l4HE zAUBDly-SeW1i4R;8wI&jkXr@0SMdLv<=@{dzV?&}w<k?kchArsq20Q=yLS)m9@@?K EZ-WKA#Q*>R diff --git a/telegramer/include/pytz/zoneinfo/US/Hawaii b/telegramer/include/pytz/zoneinfo/US/Hawaii deleted file mode 100644 index c7cd060159bd22fc5e6f10ac5a2089afb2c19c6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmWHE%1kq2zyNGO5fBCeb|40^MH+y_ZdPZH-HL?~r#o#=TvGm0a4FH#;%aZP2O|?B zGYcc@|Nl8m3=BXrf`R4#|Edf|4lv0BCI$ZgFHT@!@$n5|@CXKC7a$G?;(!pK!3+$H uP%?xBC;bP4k_QF*Ks3l{U>fK=5Dju7hz2<mOaq+?qN(g$E}&lw4Y&a7X=UL6 diff --git a/telegramer/include/pytz/zoneinfo/US/Indiana-Starke b/telegramer/include/pytz/zoneinfo/US/Indiana-Starke deleted file mode 100644 index fcd408d74df43310a9a85c475f83d545f6d75911..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2428 zcmd_rQB2ik7{~Dkfe-{G6GJjEt(buXHk3Bl+S0K@BA5qIA&k@z%Y0QJQKQ$bL0&XV zn~G})i(IZ5T2rwtG^N&R&d?-CCBP(SA+N>-DV@{%eY?zBUH7p6`TTcudiDF_T~hY^ zO!>=&*l&2aJ@(-}THBBMeTjPSC%>tNnz6cZ&jt1M>pp#U+K@V15^B!potKU&r_Fb% zN2ODmO;=Q%boIPtzV{v07f!4<7rS@qOZ(qc-K|ynhpp>WPko{8E%U0r>I{9^GfVwg z9H*}oq?`WCbops^thpK=D_3t$G}r9^eyx4j{M_FszjU;jfiK$R`te>h*xaMd-c#zv zwv&2jX|1|7Tq?J(ddx_tM}Ge@!T4Gd#Q%PTk=+pzP&;Twy*wy^Yr|Dg$rv4+dtKf2 z#DES-{ziqo5wAldKT@Fw-jy)(wi?s3Lx*=AG!Z8%^w?wD&A9#BC9<yE+`YA2##iN= zd&=@<!s0X&<w=u?kH?sMr^iV2)Y)p%=n;t-HA%(XjMn${-d2;_Z|VC#yQE?dUDR=n z$JLa|aq_^HMm06>hD=-asd+H<oII4Z*E}3`SmGbqV&Z-6dV1J0Gw0DtHFwSeHTTz} zk~w3w$vjslo`@Xd`FN9L4WyW--r1$+b<9`Uo2&HvBgrbKs8Hwb9IqCnXXvLZhSb8z zaoU^Lp}ZpjIzP2V<zI=FMX}$SMW2f-_8l=xn);-$d$%citxcY3-DrxJ?~|qVMdsP; zle(m~N<BBDNiQocRLdi3^oq<3wPIkUE{%^<rKhuWSxA5?JCLYX^<P#m?DWWsXZ&V$ zWrDoa+-uh4M~K>X%B)Qtlyz&~GwY+;r97wBl=}vBWm=P}>^`G6MAxVdt%r2g@Jh9@ zeuv)FnWZ*YSLjz-5><6^fqr%OST!oZ{saa&c)jya@ZWrY=fCZ~4gQBe`&a*(-~ZuP zB7Xm|g8@N){|5~++P#On&qzLH!k^#I%l68XbL_LwJ_Yv4^~zlP&IPzn@cxJO`Rx@4 z`WlcGB1=Tph%6FWC9+JXT_>_oWTnVbk+mX=b=uV;%SG0UEEriavSeh<$fA)|Bg;nC zjVv5lIkI$Q?a1PtcJ;{eop$|50gwtHB|vI`6alFMQU;_BNFk6)Af-TRfvy<5Pz}zO zgQFfuK{zUclmw{>QWT^rPFohFE>2q*j>;gVL282(2dNHH9*+7T1>&d>QX-BTAw}Y- z5>h6PIw6JPsFc%|3aJ%RETmdUxsZAx1>>j~QZkO3Aw}b;8d5fnx;bs(kjf#YLu%)= z#p9@+)0U5;eok9JjtU|rL~4i>5vd|lMx>5NA(2WVr9^7!w8ccK>9pnKsHf8wl%t|Z zNjYkY6qTc@NLe}RiWC;9EK*vewn%Z2>N;(Ck@`AqfsqP3ZHbW@BSq$@GE!!aIwOVV zs5DY)j#?wd=BT#QmK&+J(-s`5xYL##sX0<~r0Pi7k-8&=$NyL5!|X4CS@xGfV)kQ6 RGn0}Nvr|%%Qj(Ix{s3RXO|k$0 diff --git a/telegramer/include/pytz/zoneinfo/US/Michigan b/telegramer/include/pytz/zoneinfo/US/Michigan deleted file mode 100644 index e104faa46545ee873295cde34e1d46bccad8647c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2230 zcmdtie@xV60LSq!L|_Lbm=r2fLCOx{_|+-mRT!}A)DzRg6ipuuyq!=ysokIqYk{WA zS<_*z#xkvuu92E8S1~nbZmmpNML&j5ZL!v(9-}{6S6t8gS^xD{|Fxdm^So~N*ZuQ( zhZ-Xr%AJ3lWb+G`v)f$0XLrAsx9WgzpY!3<T3*ioRbCb`^|`lC4><=_tm4^cV&BhG ze+*UWKQByI$<<e6O6ggVvU`fWF5|FpIftZ6Zx^YmTc&;SvPwT4me-%^QWIZ$N@pC{ zpfYzh>q#B=s2d`FJ$YrJ$_lvkjdRn~P3}~ko#z%)CXDK-iK$}hFD^Oln^BQ-=|?&J z%teuV>|=TJ!DHf<sH1Ova<9m1_*Um{>{qvW&*>Qpo>MckUeyJKn^nR1`_k=dQ10PZ zWZ@5U)U1IWvS=_QihCo{b7HnA>0BsF_hyT-a9Edb`dw7`1N!!*Ukh)+EIqq?K+H)= z*Ok-0RFxw?>$$OaRn_@Rdfr#P%GdvsyyKlG)SY`ik$1hYURAdpm-D-}iM#9f$(p8h zqP8R|>uPI6-B_RY7q3<R!Cg9#K3@e+wCV+;`D)>dJLRI!szq>Xi(LFxo~U0PluLS& z#J#=}x%80{u`DN3h8ix2P;5*t_Z|_;zniF6<epb6&Rx|j$NN;{;X%D>c$Zq;byD9y z(5lun?bmC27b_8bQ?A?5BGwo8$Opnf(UjgUoBbuCd9+c63o=FcBcF^UkBP|ZxpL#k zr=q2&O1ECTsveBy=!g0TRa?WjmU~XBhrLQ~YTK_iXPwns>O0hy@hdV~*(0LEXJmVJ zyJ#OcBs+d<6p!|H%g2U%VryquKK^#D=v)(!+n#qsLgF<^iP!!|KJobR8IBW=AAQM5 zipNjA;Y^6fKRBI`X5S3^PF@rYIW@~dP966?bC;M~8)67f!ryP`UyLSh4#PplgA526 zk<|<d851%nWK>o&EM#2B!1ybS3>li$j13taGCHdn9x^^;fXE1uAtGZ$28oOk874AL zWT41Mk)a}EMFxwE78$P9j29U&GGb)N$e58qBcn!!jf@)^I5Ki%=*ZZS!CTGfk>Oj- z_>ll05kNwK!~h8b5(Oj-NF0zrAdx^qfy4p{1`-V<999z#Bp^sckdPoTL4txr1qllh z7bGx9WRTDxu|a}kHPJ!BV>R(X0%SE2LPCVZ2niArB_vEpoRB~vkwQX+#0m+P)kF&k zm(|1z37FMH3<(($GbCt8)R3?taYF)!L=FiZ5<4V#Nc52KSxx+q09s81kq}x<43Qur zQAEOs#1RQ35=kVKNGy?HBGE*`X*Kag0%|o8MM7#dF-3xkL=_1u5?3U!NM!MU8(NpC Xu-DYLC|Kbs_mma|%gQ`uo>JFeUM!I( diff --git a/telegramer/include/pytz/zoneinfo/US/Mountain b/telegramer/include/pytz/zoneinfo/US/Mountain deleted file mode 100644 index 5fbe26b1d93d1acb2561c390c1e097d07f1a262e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2444 zcmdtjeN5F=9LMnkqQH%ZQ;8v<nU)CgtR#>b6-0<P2(NH8!YHnHS1a(Ln-=0RD8?U+ zvtrCL36!+fOng|gv7xTv+REl|Yg!BKxhxw!Y?8peo%dPwPk;4STVM9OuOIjS&-=Po z`_|@&f812_4G-6C9^R)b{@GWcUmFNlJ<liU-dDa?dprTXw{@E6E54}vI`*j#+N1RF zyx$s!>*B?gOursm&?$b8b?d7UesOi|Njd(VTTGm*mXq%nh`_OY8GIv2h@FWtq%9yq zpPH0YHYBL9x|w=v#e|wxIIhF9MpXC<xjIswP>}}?Nyq3Ob<M?I9d-V=h(6JxW8Uo* zv2XTB`ErZ6w*6Uo-Bypd-d8WDuPPC7rT5Ai`6=Rtlm#+=Zn2sf>5vJb$tvNO`8x57 zNR>1kp=X`^LCrpNN#EFeTFvp#k~i%*sOGK=%6aQP6gTI7E^k@(wwNFHo=i^FA~|qD zr#Lo>l#!D<^^!~6I=EM-oo!U<-OuTaBb6$%*{ic&TBNeQtuklR47IRitz1+&rgD?- zlegu3q85jz%DluYBJbNMnLmDB6rB1=-u~%;Skmv%cMR+nOFMqlckbFQ3L8GsceU<P zcbE6;d+N8TqRba{anTx8{Ogb`NpBJ*XZOp}=vq;Fq+Kq%Tqw$3eO)jAxJEgf+VuVJ zELG(-K3&l@M?J8lOjr6t)rzEa?OOSja!thQs@zkm>gzP=p8ch855>q;fg!QFZ&W@w zvR~A+4$FrI+eK~tQMsmjy?EGpM%T5qsYlWe>qoslRUh4{JtbwzbJ?%G$?3{_+O2)z zvC4O#K(G7eXSKeoT0V9rMm+A%mrooV6%AF1vaw@WY{;FI8yk*_O>r0G=JGDFIWVsM zd54vM<TJe`zEf=(Jg&En`PI|iz51DRZq?M>qPHC@P|dX-y?tkr3Jv-5Z%WwTuYY~@ z-y00>?i3;ze5)rU%)Dz6Vc(<dr(EuI31^XcR+y*SJQXgpA|XQThwERgFKDhdEUF(_ zA+khdjmRRARU*qo)@d~hMOKO|)oRv?EEZWUvRq`nR<mGa#mJJ8HKScLFRYp~%LdlX zv2bMN$kLIuBa25?Z#BzD)^9ZhKq`Qg0I2~-5s)fylmV#&M<I|(aFhb61xGQEYH*YT zsRvRJq#{;R5~L<bQIM)2WkKqK6b7jbQW~T-9K}JZ!%-fjK2}p8q(W9xBBVwfMMA2C zlnJR5QYfTSNU4xoA;m(fg_H}a7g8{!VpdZ!q-GpNL#oD6Hl%JGg+nUGQ97h{Nb!*B zA>~8rXEg;xDrhw&L~3X?MMSE|QAVVWNFk9*BBexXi4+s5CQ?qMo>o&(q@q?+QlzF< zQ&gm?9A!o7%28OPvK*yFYRgevq`F9Xk@_M9Mk;JIB}Qs&HAP0MY&B&@>WmZ`sWeBa zky>*U8>u!&xsiHv6db9z)s!5mxz!XMsk+sa9jQA~c%<@3>5<wa#mE15^&RHNV6pj8 VNOLaC$jQh`b7p5}WM^bK{s0D?lEVN1 diff --git a/telegramer/include/pytz/zoneinfo/US/Pacific b/telegramer/include/pytz/zoneinfo/US/Pacific deleted file mode 100644 index 9dad4f4c75b373635ccbe634798f8d9e587e36c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2836 zcmd_rX;76_9LMpCq6mtfOq2-iq$YxZfTFmRxecHqDoA36O9F#ws1Rxy(nOgx#-Gfk zjgDqbP8phGV_AeYIW>)C&^T@pSt6t2f|j^+Z|8g7_Ntdn&ok%woVoAs_m?@lATPo5 zkEetEg~RiiJ=}Yg*-zDbDdz3{A!447GFxB2F5j&SGj;v0Ev=hBKppiK&pB4MQ%-ol zl9RQfPBpwMKkxWZ8fw<cFY8{G#;OAOwP2~7E}bmDrOuGwb7JI7<WOl!o}|uppRSrC zqE&P25Opq~t2$Q~qRuy6Ru^_(S1pI?)Wyo<>QePZxx8$@x>9jOTGt$qtA!uSwYl%e zAL*~kpJSer>w`<AZQwR_quVUG*{NLJY<pJUYR*%)kLBvWzDZHueaYJQew6ZTiPU~C zbW!bAcGm5e4HW<R5vIfRAn7<Z&;-O?kbw2$O`!T-0(X9?gD&rq&W+Wk%kjf1xVF-C z{j^$j+wqZBuT`o$)`{-Esz}{guw3`Zo~c4oGj-1q!&R@yVLG&LhTIhxs>9kPN?7Yq zbNA_95?<HS^geJy`s{8q_iQ~Wx@3^P_n9xGZ&tAGx9EiGpLj{%H|cXVAmm3K5mluk zye%d&s7ysR{9vNaEl`7McAMz>Qi-YBU}E>olfk7=n79q&BtHKYolw+Yh9np3p&1<| zF(OM3OK6ti0ZBS3yn{+Q8>UCxI;%z=x~)f@{8o+L6>9F^|ABg-;-(q%#(MQ&;VCn= ze20unuQB5nz9bU{8#8gj5}A0lUMI)AsFLgV>eS%HDs|6hJ*j1?n*8P-Gv(+aNn5?q zO#Nhvq|aGlrfrIq>7%pFj1nao;iF9E%vQ;~-P>d({v=svM(SC8uBcgGhwE%_y_&t< zs~>LItLBt9>PKoetDJ=g_1vmeYF=7{nZI_UEQqN!kLItCg~8iQZgRHdwv?Ovh*6S% zIL{OW^p=91DP~cVPafNps}~;$S4&Eg_2boERhSj2msT{YWy3n_<%I`TQAmp}PT#JI zeSxMVsa8rF&YP8?+hk?UVY8~OT%N3|HcuVPlhvh_=IMPYQkqj_)@+HAc7FD4@9*IH z-+6t$$^jma&-a%2`TKkoWu8v%-o<^@l(bCGv<dcP*z=G*(=zQp+T-zapUi(z0-t?y z{KIOIA|O>j%7D}XDFjjpr!56i3#1rGHIQ;3^*{=OR0JsrQWK;oNL7%sAay|sgH#47 z4N@DYEe=v0r!5asAEZD?g^&^<HA0GnR0$~)QYWNPNTrZcA+<t^g;WbEm($h@DHu{Q zq-5x7#)YEs*s1|#L+XYU4yhbcI;3_;@tn4LNco($en<h43L+&$YKRmOsUlKFq>e}- zkxC+^L~4l?6R9RrPNbelL7lduNJ){JB1J{2ij)<pD^ggbvPfx>+9Jh8s*9A@Y3qv= z*l8<_lo+WoQe>pcNSTp3BZWpPjg%UxHBxM(+DN&PdLspQ+KMA3M{14~9jQ7}cBJk| z;gQNCrAKOy6d$QRQhukcKe7N$y8_4(IPDrBi-4>GvJA*NAPa%41hN#!S|E#otOl|i zPP-n+f;jDpAWP!3Yl18avMR{3AnSrG46-uF(jaStEDo|d$nqfTgDjBKt`M?BPP<0P zB023UA<KlU6S7dqN+C;ytQE3Y$Z8?Wg{&8{U{1SY$dWnjnjwqkw5x_J8?tW5!XYb% jEFH3T`2StJAUlLfb`Yb}hQubs#zm*a$H&IU#s&Qiukn{d diff --git a/telegramer/include/pytz/zoneinfo/US/Samoa b/telegramer/include/pytz/zoneinfo/US/Samoa deleted file mode 100644 index cb56709a77dedb471150f4907771bf38f1879ba4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmWHE%1kq2zzdjwvdlot(EubSvi{~^1d42|U|{(FKmG@ZObuXQ@$n5|2o4Tm2qD3| V|3Hvudx8T*6Ec?zXt<#v7XWnZBuoGR diff --git a/telegramer/include/pytz/zoneinfo/UTC b/telegramer/include/pytz/zoneinfo/UTC deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/Universal b/telegramer/include/pytz/zoneinfo/Universal deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/W-SU b/telegramer/include/pytz/zoneinfo/W-SU deleted file mode 100644 index ddb3f4e99a1030f33b56fad986c8d9c16e59eb32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1535 zcmd_pOGs2v0Eh8&eK(rb!qglm&2)U$sA*0)IyEzjsUbNPSW%Qng3+OZ6j}@+wy8i0 zTv(<w>Y|s6YLnFvfkYNAS_B#d(ZT{b1Q9Ad&UZz$TD9&D_x_Go1;Ov{Z)$BR5`SH5 z^c!xj-TLO770{2~!?v;O6<<2~a%X1yzByZObnc(+f7>=aAe@1L@*#I{^@&i>RWve~ zaC~IY6&@P4`5GPslaD0WhbPu1O}P_eCL0pxR)vy2#ZM$pdfe;AuS}$j_ABe{Zk2lN zys}+9t=6AwR%vZ}Rr<jywV`gS$|%oP8}pM@rq!adV&|1T(k|^^lVtYC#6V8_(?HIf zIhp(Xv&_3cCG&%?WWm)Za#QC$x%o`LbToI%!b78~=v0p?cJ-+(dpcA}YCx419Z;p; zkE*hic3Jk$tDN&qa@*r9wSBT&mJfNP>yb@XbY;rQULoBr(Q-$pRqgamOV6<%%A5I8 z`aJJdRpcF6o$*Xn&%97I;XzgN`j*=Dp-a`?y`<{KZ_4`1CzZc0^@tH379J565g8R7 z6CJf8Dth5#iCy-ITe<9u<=^=89B&aK!>RufJR^iCykNxW^I6W7Jw}`mWo|?Nw{jgK zVewqmU?dA+O%tiVzt43T>5K2n+)F>t@7C4(MLl<;zP&sez51>dd5#j{^ZE6yUz(S} z(^$BcUYI8#{QuC`Pkrrs7#c%5Ls~<6Gu6!@-68EE{h8_pkq%9Di%5^Ax=Ex<q)q-* z`a~K<IyKd;BE2HbBHbeGBK;x_BON0xBRwNcBV8kHBYh){Bb_6yo9f<?=8^7Ab^A#F z$Oe!dAX`B8fNTQU1+oofAIL_KogiC5_F}3xgY3psZwJ{AvLR$g$d-^jA)7*Wg=`Di s7qT&AXUNu&y&;=Jc4w-$hwRT(ZxGobvPEQ%$R_cB-=#%wxuDqc3lb5KyZ`_I diff --git a/telegramer/include/pytz/zoneinfo/WET b/telegramer/include/pytz/zoneinfo/WET deleted file mode 100644 index c27390b5b638399057d5f5c6d09ef8c81d5f01c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1905 zcmdVaT}+h)9LMp)h-J*^4~n3?Wkf(7;qa;uM>9My3RK`Iq#`NEiy{RP1HxE~F;~p} z+?b6O%NAqJDSCl*f!5S?wv4qk)~uXcuJ`8J)cMlbdcKd|b=6h#VTaGNb8)Wz-(PT3 zYg4x8U(W;1H+*>doDc60Jv`o^h_{#6BZC21SH2<}Qxz4A)q;vOwlL?8qLcs7q6I%F zCiE-CT>Dh9SB`4&HwU%k%znk4IBD^RCoSQ<_bqW)mel`-J=)f3OSd%GW930hE(+M= z-h4}mPqioJ$69Jwu~Kj3D($BfrOz(XlV=~)Q&YF(9sf<sM!r+V;FrqmIw4=<5oOg~ zwCv6)%kfWJZq)(HOBl2K>>kUX8?Y5|BU*83n-%=st-_PV_Vo9)Dw?R5f4WdBhgNIV zyYYIaDOsxr3+&mFFcmk(*_xDJ6eymvlCUpSnta(xul{Uhp{J~D=Da<3<wJXZ@{pBZ z_)Hc1hHdTP545hm-PRAksmhwSRMoaa)yvxyEHBlDn6=vIP1nYoKGjB-Snan_df`Tz zZTk4SHqVCJmV=R2H}$7&9k^!oBQy45{j6=<^SidM`otRQE~qhS)OPqM)im$8niHhv zU*A#7T&G&k_iE>*pmu$<S#9S!Z1-Nj+9#^*rIsYUJd|f0l||Os6l1SsC0bYDw!IqZ zvF^-hb^rCF^+eoM&)1(@@70UyJ2qjj%}lF*^tAR&j_LK@aSiMnP>ARM`s3U@VIEI} zg*Y#F|MN{vpgQQO2?RYM_nzQ?I9q;`(?!k~Ibr0CkyA#_898a>tdY}3&Ko&#SLe); zQ%BAnIeFylk<&-cA4veo07(JK0Z9VM0!ahO14#tQ#MMa!$puLU$p%RW$p=XY$p}da z$q7jc$qGpe$qPvg$;{PB4av>bNe;;lNe{^nNf5~pNfF5rNfOBtNfXHvNfgNxNfpV} z)kzk~*40TD$rni&$rwo)$r(u+$r?!;$s0)=$s9=?$=%gS9?9O-Ngv4{nE+%4kSRds z0GR}27LaK`<^h=qWG0ZQK;{CO3}iN3o#{a41DOzHMvy5%<^-7(WLA)ALFNUS7-VLU zsX^ujnH*$xT%GAb=Ev2UAY_J+DMIE5nIvSEkZHpEWS+vEt@1Hi-in-zybNDvmbW6y H$6NS60s5Ht diff --git a/telegramer/include/pytz/zoneinfo/Zulu b/telegramer/include/pytz/zoneinfo/Zulu deleted file mode 100644 index 91558be0c2bf903b2364215ba26d5227d6126508..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 hcmWHE%1kq2AP5+NDp(+@LPMMxLdep^1=MQ51psH$25|rY diff --git a/telegramer/include/pytz/zoneinfo/iso3166.tab b/telegramer/include/pytz/zoneinfo/iso3166.tab deleted file mode 100644 index a4ff61a..0000000 --- a/telegramer/include/pytz/zoneinfo/iso3166.tab +++ /dev/null @@ -1,274 +0,0 @@ -# ISO 3166 alpha-2 country codes -# -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. -# -# From Paul Eggert (2015-05-02): -# This file contains a table of two-letter country codes. Columns are -# separated by a single tab. Lines beginning with '#' are comments. -# All text uses UTF-8 encoding. The columns of the table are as follows: -# -# 1. ISO 3166-1 alpha-2 country code, current as of -# ISO 3166-1 N976 (2018-11-06). See: Updates on ISO 3166-1 -# https://isotc.iso.org/livelink/livelink/Open/16944257 -# 2. The usual English name for the coded region, -# chosen so that alphabetic sorting of subsets produces helpful lists. -# This is not the same as the English name in the ISO 3166 tables. -# -# The table is sorted by country code. -# -# This table is intended as an aid for users, to help them select time -# zone data appropriate for their practical needs. It is not intended -# to take or endorse any position on legal or territorial claims. -# -#country- -#code name of country, territory, area, or subdivision -AD Andorra -AE United Arab Emirates -AF Afghanistan -AG Antigua & Barbuda -AI Anguilla -AL Albania -AM Armenia -AO Angola -AQ Antarctica -AR Argentina -AS Samoa (American) -AT Austria -AU Australia -AW Aruba -AX Åland Islands -AZ Azerbaijan -BA Bosnia & Herzegovina -BB Barbados -BD Bangladesh -BE Belgium -BF Burkina Faso -BG Bulgaria -BH Bahrain -BI Burundi -BJ Benin -BL St Barthelemy -BM Bermuda -BN Brunei -BO Bolivia -BQ Caribbean NL -BR Brazil -BS Bahamas -BT Bhutan -BV Bouvet Island -BW Botswana -BY Belarus -BZ Belize -CA Canada -CC Cocos (Keeling) Islands -CD Congo (Dem. Rep.) -CF Central African Rep. -CG Congo (Rep.) -CH Switzerland -CI Côte d'Ivoire -CK Cook Islands -CL Chile -CM Cameroon -CN China -CO Colombia -CR Costa Rica -CU Cuba -CV Cape Verde -CW Curaçao -CX Christmas Island -CY Cyprus -CZ Czech Republic -DE Germany -DJ Djibouti -DK Denmark -DM Dominica -DO Dominican Republic -DZ Algeria -EC Ecuador -EE Estonia -EG Egypt -EH Western Sahara -ER Eritrea -ES Spain -ET Ethiopia -FI Finland -FJ Fiji -FK Falkland Islands -FM Micronesia -FO Faroe Islands -FR France -GA Gabon -GB Britain (UK) -GD Grenada -GE Georgia -GF French Guiana -GG Guernsey -GH Ghana -GI Gibraltar -GL Greenland -GM Gambia -GN Guinea -GP Guadeloupe -GQ Equatorial Guinea -GR Greece -GS South Georgia & the South Sandwich Islands -GT Guatemala -GU Guam -GW Guinea-Bissau -GY Guyana -HK Hong Kong -HM Heard Island & McDonald Islands -HN Honduras -HR Croatia -HT Haiti -HU Hungary -ID Indonesia -IE Ireland -IL Israel -IM Isle of Man -IN India -IO British Indian Ocean Territory -IQ Iraq -IR Iran -IS Iceland -IT Italy -JE Jersey -JM Jamaica -JO Jordan -JP Japan -KE Kenya -KG Kyrgyzstan -KH Cambodia -KI Kiribati -KM Comoros -KN St Kitts & Nevis -KP Korea (North) -KR Korea (South) -KW Kuwait -KY Cayman Islands -KZ Kazakhstan -LA Laos -LB Lebanon -LC St Lucia -LI Liechtenstein -LK Sri Lanka -LR Liberia -LS Lesotho -LT Lithuania -LU Luxembourg -LV Latvia -LY Libya -MA Morocco -MC Monaco -MD Moldova -ME Montenegro -MF St Martin (French) -MG Madagascar -MH Marshall Islands -MK North Macedonia -ML Mali -MM Myanmar (Burma) -MN Mongolia -MO Macau -MP Northern Mariana Islands -MQ Martinique -MR Mauritania -MS Montserrat -MT Malta -MU Mauritius -MV Maldives -MW Malawi -MX Mexico -MY Malaysia -MZ Mozambique -NA Namibia -NC New Caledonia -NE Niger -NF Norfolk Island -NG Nigeria -NI Nicaragua -NL Netherlands -NO Norway -NP Nepal -NR Nauru -NU Niue -NZ New Zealand -OM Oman -PA Panama -PE Peru -PF French Polynesia -PG Papua New Guinea -PH Philippines -PK Pakistan -PL Poland -PM St Pierre & Miquelon -PN Pitcairn -PR Puerto Rico -PS Palestine -PT Portugal -PW Palau -PY Paraguay -QA Qatar -RE Réunion -RO Romania -RS Serbia -RU Russia -RW Rwanda -SA Saudi Arabia -SB Solomon Islands -SC Seychelles -SD Sudan -SE Sweden -SG Singapore -SH St Helena -SI Slovenia -SJ Svalbard & Jan Mayen -SK Slovakia -SL Sierra Leone -SM San Marino -SN Senegal -SO Somalia -SR Suriname -SS South Sudan -ST Sao Tome & Principe -SV El Salvador -SX St Maarten (Dutch) -SY Syria -SZ Eswatini (Swaziland) -TC Turks & Caicos Is -TD Chad -TF French Southern & Antarctic Lands -TG Togo -TH Thailand -TJ Tajikistan -TK Tokelau -TL East Timor -TM Turkmenistan -TN Tunisia -TO Tonga -TR Turkey -TT Trinidad & Tobago -TV Tuvalu -TW Taiwan -TZ Tanzania -UA Ukraine -UG Uganda -UM US minor outlying islands -US United States -UY Uruguay -UZ Uzbekistan -VA Vatican City -VC St Vincent -VE Venezuela -VG Virgin Islands (UK) -VI Virgin Islands (US) -VN Vietnam -VU Vanuatu -WF Wallis & Futuna -WS Samoa (western) -YE Yemen -YT Mayotte -ZA South Africa -ZM Zambia -ZW Zimbabwe diff --git a/telegramer/include/pytz/zoneinfo/leapseconds b/telegramer/include/pytz/zoneinfo/leapseconds deleted file mode 100644 index ffa5eb8..0000000 --- a/telegramer/include/pytz/zoneinfo/leapseconds +++ /dev/null @@ -1,82 +0,0 @@ -# Allowance for leap seconds added to each time zone file. - -# This file is in the public domain. - -# This file is generated automatically from the data in the public-domain -# NIST format leap-seconds.list file, which can be copied from -# <ftp://ftp.nist.gov/pub/time/leap-seconds.list> -# or <ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list>. -# The NIST file is used instead of its IERS upstream counterpart -# <https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list> -# because under US law the NIST file is public domain -# whereas the IERS file's copyright and license status is unclear. -# For more about leap-seconds.list, please see -# The NTP Timescale and Leap Seconds -# <https://www.eecis.udel.edu/~mills/leap.html>. - -# The rules for leap seconds are specified in Annex 1 (Time scales) of: -# Standard-frequency and time-signal emissions. -# International Telecommunication Union - Radiocommunication Sector -# (ITU-R) Recommendation TF.460-6 (02/2002) -# <https://www.itu.int/rec/R-REC-TF.460-6-200202-I/>. -# The International Earth Rotation and Reference Systems Service (IERS) -# periodically uses leap seconds to keep UTC to within 0.9 s of UT1 -# (a proxy for Earth's angle in space as measured by astronomers) -# and publishes leap second data in a copyrighted file -# <https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat>. -# See: Levine J. Coordinated Universal Time and the leap second. -# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995 -# <https://ieeexplore.ieee.org/document/7909995>. - -# There were no leap seconds before 1972, as no official mechanism -# accounted for the discrepancy between atomic time (TAI) and the earth's -# rotation. The first ("1 Jan 1972") data line in leap-seconds.list -# does not denote a leap second; it denotes the start of the current definition -# of UTC. - -# All leap-seconds are Stationary (S) at the given UTC time. -# The correction (+ or -) is made at the given time, so in the unlikely -# event of a negative leap second, a line would look like this: -# Leap YEAR MON DAY 23:59:59 - S -# Typical lines look like this: -# Leap YEAR MON DAY 23:59:60 + S -Leap 1972 Jun 30 23:59:60 + S -Leap 1972 Dec 31 23:59:60 + S -Leap 1973 Dec 31 23:59:60 + S -Leap 1974 Dec 31 23:59:60 + S -Leap 1975 Dec 31 23:59:60 + S -Leap 1976 Dec 31 23:59:60 + S -Leap 1977 Dec 31 23:59:60 + S -Leap 1978 Dec 31 23:59:60 + S -Leap 1979 Dec 31 23:59:60 + S -Leap 1981 Jun 30 23:59:60 + S -Leap 1982 Jun 30 23:59:60 + S -Leap 1983 Jun 30 23:59:60 + S -Leap 1985 Jun 30 23:59:60 + S -Leap 1987 Dec 31 23:59:60 + S -Leap 1989 Dec 31 23:59:60 + S -Leap 1990 Dec 31 23:59:60 + S -Leap 1992 Jun 30 23:59:60 + S -Leap 1993 Jun 30 23:59:60 + S -Leap 1994 Jun 30 23:59:60 + S -Leap 1995 Dec 31 23:59:60 + S -Leap 1997 Jun 30 23:59:60 + S -Leap 1998 Dec 31 23:59:60 + S -Leap 2005 Dec 31 23:59:60 + S -Leap 2008 Dec 31 23:59:60 + S -Leap 2012 Jun 30 23:59:60 + S -Leap 2015 Jun 30 23:59:60 + S -Leap 2016 Dec 31 23:59:60 + S - -# UTC timestamp when this leap second list expires. -# Any additional leap seconds will come after this. -# This Expires line is commented out for now, -# so that pre-2020a zic implementations do not reject this file. -#Expires 2022 Dec 28 00:00:00 - -# POSIX timestamps for the data in this file: -#updated 1467936000 (2016-07-08 00:00:00 UTC) -#expires 1672185600 (2022-12-28 00:00:00 UTC) - -# Updated through IERS Bulletin C63 -# File expires on: 28 December 2022 diff --git a/telegramer/include/pytz/zoneinfo/tzdata.zi b/telegramer/include/pytz/zoneinfo/tzdata.zi deleted file mode 100644 index e21fc92..0000000 --- a/telegramer/include/pytz/zoneinfo/tzdata.zi +++ /dev/null @@ -1,4437 +0,0 @@ -# version unknown-dirty -# This zic input file is in the public domain. -R d 1916 o - Jun 14 23s 1 S -R d 1916 1919 - O Su>=1 23s 0 - -R d 1917 o - Mar 24 23s 1 S -R d 1918 o - Mar 9 23s 1 S -R d 1919 o - Mar 1 23s 1 S -R d 1920 o - F 14 23s 1 S -R d 1920 o - O 23 23s 0 - -R d 1921 o - Mar 14 23s 1 S -R d 1921 o - Jun 21 23s 0 - -R d 1939 o - S 11 23s 1 S -R d 1939 o - N 19 1 0 - -R d 1944 1945 - Ap M>=1 2 1 S -R d 1944 o - O 8 2 0 - -R d 1945 o - S 16 1 0 - -R d 1971 o - Ap 25 23s 1 S -R d 1971 o - S 26 23s 0 - -R d 1977 o - May 6 0 1 S -R d 1977 o - O 21 0 0 - -R d 1978 o - Mar 24 1 1 S -R d 1978 o - S 22 3 0 - -R d 1980 o - Ap 25 0 1 S -R d 1980 o - O 31 2 0 - -Z Africa/Algiers 0:12:12 - LMT 1891 Mar 16 -0:9:21 - PMT 1911 Mar 11 -0 d WE%sT 1940 F 25 2 -1 d CE%sT 1946 O 7 -0 - WET 1956 Ja 29 -1 - CET 1963 Ap 14 -0 d WE%sT 1977 O 21 -1 d CE%sT 1979 O 26 -0 d WE%sT 1981 May -1 - CET -Z Atlantic/Cape_Verde -1:34:4 - LMT 1912 Ja 1 2u --2 - -02 1942 S --2 1 -01 1945 O 15 --2 - -02 1975 N 25 2 --1 - -01 -Z Africa/Ndjamena 1:0:12 - LMT 1912 -1 - WAT 1979 O 14 -1 1 WAST 1980 Mar 8 -1 - WAT -Z Africa/Abidjan -0:16:8 - LMT 1912 -0 - GMT -L Africa/Abidjan Africa/Accra -L Africa/Abidjan Africa/Bamako -L Africa/Abidjan Africa/Banjul -L Africa/Abidjan Africa/Conakry -L Africa/Abidjan Africa/Dakar -L Africa/Abidjan Africa/Freetown -L Africa/Abidjan Africa/Lome -L Africa/Abidjan Africa/Nouakchott -L Africa/Abidjan Africa/Ouagadougou -L Africa/Abidjan Atlantic/St_Helena -R K 1940 o - Jul 15 0 1 S -R K 1940 o - O 1 0 0 - -R K 1941 o - Ap 15 0 1 S -R K 1941 o - S 16 0 0 - -R K 1942 1944 - Ap 1 0 1 S -R K 1942 o - O 27 0 0 - -R K 1943 1945 - N 1 0 0 - -R K 1945 o - Ap 16 0 1 S -R K 1957 o - May 10 0 1 S -R K 1957 1958 - O 1 0 0 - -R K 1958 o - May 1 0 1 S -R K 1959 1981 - May 1 1 1 S -R K 1959 1965 - S 30 3 0 - -R K 1966 1994 - O 1 3 0 - -R K 1982 o - Jul 25 1 1 S -R K 1983 o - Jul 12 1 1 S -R K 1984 1988 - May 1 1 1 S -R K 1989 o - May 6 1 1 S -R K 1990 1994 - May 1 1 1 S -R K 1995 2010 - Ap lastF 0s 1 S -R K 1995 2005 - S lastTh 24 0 - -R K 2006 o - S 21 24 0 - -R K 2007 o - S Th>=1 24 0 - -R K 2008 o - Au lastTh 24 0 - -R K 2009 o - Au 20 24 0 - -R K 2010 o - Au 10 24 0 - -R K 2010 o - S 9 24 1 S -R K 2010 o - S lastTh 24 0 - -R K 2014 o - May 15 24 1 S -R K 2014 o - Jun 26 24 0 - -R K 2014 o - Jul 31 24 1 S -R K 2014 o - S lastTh 24 0 - -Z Africa/Cairo 2:5:9 - LMT 1900 O -2 K EE%sT -Z Africa/Bissau -1:2:20 - LMT 1912 Ja 1 1u --1 - -01 1975 -0 - GMT -Z Africa/Nairobi 2:27:16 - LMT 1908 May -2:30 - +0230 1928 Jun 30 24 -3 - EAT 1930 Ja 4 24 -2:30 - +0230 1936 D 31 24 -2:45 - +0245 1942 Jul 31 24 -3 - EAT -L Africa/Nairobi Africa/Addis_Ababa -L Africa/Nairobi Africa/Asmara -L Africa/Nairobi Africa/Dar_es_Salaam -L Africa/Nairobi Africa/Djibouti -L Africa/Nairobi Africa/Kampala -L Africa/Nairobi Africa/Mogadishu -L Africa/Nairobi Indian/Antananarivo -L Africa/Nairobi Indian/Comoro -L Africa/Nairobi Indian/Mayotte -Z Africa/Monrovia -0:43:8 - LMT 1882 --0:43:8 - MMT 1919 Mar --0:44:30 - MMT 1972 Ja 7 -0 - GMT -R L 1951 o - O 14 2 1 S -R L 1952 o - Ja 1 0 0 - -R L 1953 o - O 9 2 1 S -R L 1954 o - Ja 1 0 0 - -R L 1955 o - S 30 0 1 S -R L 1956 o - Ja 1 0 0 - -R L 1982 1984 - Ap 1 0 1 S -R L 1982 1985 - O 1 0 0 - -R L 1985 o - Ap 6 0 1 S -R L 1986 o - Ap 4 0 1 S -R L 1986 o - O 3 0 0 - -R L 1987 1989 - Ap 1 0 1 S -R L 1987 1989 - O 1 0 0 - -R L 1997 o - Ap 4 0 1 S -R L 1997 o - O 4 0 0 - -R L 2013 o - Mar lastF 1 1 S -R L 2013 o - O lastF 2 0 - -Z Africa/Tripoli 0:52:44 - LMT 1920 -1 L CE%sT 1959 -2 - EET 1982 -1 L CE%sT 1990 May 4 -2 - EET 1996 S 30 -1 L CE%sT 1997 O 4 -2 - EET 2012 N 10 2 -1 L CE%sT 2013 O 25 2 -2 - EET -R MU 1982 o - O 10 0 1 - -R MU 1983 o - Mar 21 0 0 - -R MU 2008 o - O lastSu 2 1 - -R MU 2009 o - Mar lastSu 2 0 - -Z Indian/Mauritius 3:50 - LMT 1907 -4 MU +04/+05 -R M 1939 o - S 12 0 1 - -R M 1939 o - N 19 0 0 - -R M 1940 o - F 25 0 1 - -R M 1945 o - N 18 0 0 - -R M 1950 o - Jun 11 0 1 - -R M 1950 o - O 29 0 0 - -R M 1967 o - Jun 3 12 1 - -R M 1967 o - O 1 0 0 - -R M 1974 o - Jun 24 0 1 - -R M 1974 o - S 1 0 0 - -R M 1976 1977 - May 1 0 1 - -R M 1976 o - Au 1 0 0 - -R M 1977 o - S 28 0 0 - -R M 1978 o - Jun 1 0 1 - -R M 1978 o - Au 4 0 0 - -R M 2008 o - Jun 1 0 1 - -R M 2008 o - S 1 0 0 - -R M 2009 o - Jun 1 0 1 - -R M 2009 o - Au 21 0 0 - -R M 2010 o - May 2 0 1 - -R M 2010 o - Au 8 0 0 - -R M 2011 o - Ap 3 0 1 - -R M 2011 o - Jul 31 0 0 - -R M 2012 2013 - Ap lastSu 2 1 - -R M 2012 o - Jul 20 3 0 - -R M 2012 o - Au 20 2 1 - -R M 2012 o - S 30 3 0 - -R M 2013 o - Jul 7 3 0 - -R M 2013 o - Au 10 2 1 - -R M 2013 2018 - O lastSu 3 0 - -R M 2014 2018 - Mar lastSu 2 1 - -R M 2014 o - Jun 28 3 0 - -R M 2014 o - Au 2 2 1 - -R M 2015 o - Jun 14 3 0 - -R M 2015 o - Jul 19 2 1 - -R M 2016 o - Jun 5 3 0 - -R M 2016 o - Jul 10 2 1 - -R M 2017 o - May 21 3 0 - -R M 2017 o - Jul 2 2 1 - -R M 2018 o - May 13 3 0 - -R M 2018 o - Jun 17 2 1 - -R M 2019 o - May 5 3 -1 - -R M 2019 o - Jun 9 2 0 - -R M 2020 o - Ap 19 3 -1 - -R M 2020 o - May 31 2 0 - -R M 2021 o - Ap 11 3 -1 - -R M 2021 o - May 16 2 0 - -R M 2022 o - Mar 27 3 -1 - -R M 2022 o - May 8 2 0 - -R M 2023 o - Mar 19 3 -1 - -R M 2023 o - Ap 30 2 0 - -R M 2024 o - Mar 10 3 -1 - -R M 2024 o - Ap 14 2 0 - -R M 2025 o - F 23 3 -1 - -R M 2025 o - Ap 6 2 0 - -R M 2026 o - F 15 3 -1 - -R M 2026 o - Mar 22 2 0 - -R M 2027 o - F 7 3 -1 - -R M 2027 o - Mar 14 2 0 - -R M 2028 o - Ja 23 3 -1 - -R M 2028 o - Mar 5 2 0 - -R M 2029 o - Ja 14 3 -1 - -R M 2029 o - F 18 2 0 - -R M 2029 o - D 30 3 -1 - -R M 2030 o - F 10 2 0 - -R M 2030 o - D 22 3 -1 - -R M 2031 o - F 2 2 0 - -R M 2031 o - D 14 3 -1 - -R M 2032 o - Ja 18 2 0 - -R M 2032 o - N 28 3 -1 - -R M 2033 o - Ja 9 2 0 - -R M 2033 o - N 20 3 -1 - -R M 2033 o - D 25 2 0 - -R M 2034 o - N 5 3 -1 - -R M 2034 o - D 17 2 0 - -R M 2035 o - O 28 3 -1 - -R M 2035 o - D 9 2 0 - -R M 2036 o - O 19 3 -1 - -R M 2036 o - N 23 2 0 - -R M 2037 o - O 4 3 -1 - -R M 2037 o - N 15 2 0 - -R M 2038 o - S 26 3 -1 - -R M 2038 o - N 7 2 0 - -R M 2039 o - S 18 3 -1 - -R M 2039 o - O 23 2 0 - -R M 2040 o - S 2 3 -1 - -R M 2040 o - O 14 2 0 - -R M 2041 o - Au 25 3 -1 - -R M 2041 o - S 29 2 0 - -R M 2042 o - Au 10 3 -1 - -R M 2042 o - S 21 2 0 - -R M 2043 o - Au 2 3 -1 - -R M 2043 o - S 13 2 0 - -R M 2044 o - Jul 24 3 -1 - -R M 2044 o - Au 28 2 0 - -R M 2045 o - Jul 9 3 -1 - -R M 2045 o - Au 20 2 0 - -R M 2046 o - Jul 1 3 -1 - -R M 2046 o - Au 12 2 0 - -R M 2047 o - Jun 23 3 -1 - -R M 2047 o - Jul 28 2 0 - -R M 2048 o - Jun 7 3 -1 - -R M 2048 o - Jul 19 2 0 - -R M 2049 o - May 30 3 -1 - -R M 2049 o - Jul 4 2 0 - -R M 2050 o - May 15 3 -1 - -R M 2050 o - Jun 26 2 0 - -R M 2051 o - May 7 3 -1 - -R M 2051 o - Jun 18 2 0 - -R M 2052 o - Ap 28 3 -1 - -R M 2052 o - Jun 2 2 0 - -R M 2053 o - Ap 13 3 -1 - -R M 2053 o - May 25 2 0 - -R M 2054 o - Ap 5 3 -1 - -R M 2054 o - May 17 2 0 - -R M 2055 o - Mar 28 3 -1 - -R M 2055 o - May 2 2 0 - -R M 2056 o - Mar 12 3 -1 - -R M 2056 o - Ap 23 2 0 - -R M 2057 o - Mar 4 3 -1 - -R M 2057 o - Ap 8 2 0 - -R M 2058 o - F 17 3 -1 - -R M 2058 o - Mar 31 2 0 - -R M 2059 o - F 9 3 -1 - -R M 2059 o - Mar 23 2 0 - -R M 2060 o - F 1 3 -1 - -R M 2060 o - Mar 7 2 0 - -R M 2061 o - Ja 16 3 -1 - -R M 2061 o - F 27 2 0 - -R M 2062 o - Ja 8 3 -1 - -R M 2062 o - F 19 2 0 - -R M 2062 o - D 31 3 -1 - -R M 2063 o - F 4 2 0 - -R M 2063 o - D 16 3 -1 - -R M 2064 o - Ja 27 2 0 - -R M 2064 o - D 7 3 -1 - -R M 2065 o - Ja 11 2 0 - -R M 2065 o - N 22 3 -1 - -R M 2066 o - Ja 3 2 0 - -R M 2066 o - N 14 3 -1 - -R M 2066 o - D 26 2 0 - -R M 2067 o - N 6 3 -1 - -R M 2067 o - D 11 2 0 - -R M 2068 o - O 21 3 -1 - -R M 2068 o - D 2 2 0 - -R M 2069 o - O 13 3 -1 - -R M 2069 o - N 24 2 0 - -R M 2070 o - O 5 3 -1 - -R M 2070 o - N 9 2 0 - -R M 2071 o - S 20 3 -1 - -R M 2071 o - N 1 2 0 - -R M 2072 o - S 11 3 -1 - -R M 2072 o - O 16 2 0 - -R M 2073 o - Au 27 3 -1 - -R M 2073 o - O 8 2 0 - -R M 2074 o - Au 19 3 -1 - -R M 2074 o - S 30 2 0 - -R M 2075 o - Au 11 3 -1 - -R M 2075 o - S 15 2 0 - -R M 2076 o - Jul 26 3 -1 - -R M 2076 o - S 6 2 0 - -R M 2077 o - Jul 18 3 -1 - -R M 2077 o - Au 29 2 0 - -R M 2078 o - Jul 10 3 -1 - -R M 2078 o - Au 14 2 0 - -R M 2079 o - Jun 25 3 -1 - -R M 2079 o - Au 6 2 0 - -R M 2080 o - Jun 16 3 -1 - -R M 2080 o - Jul 21 2 0 - -R M 2081 o - Jun 1 3 -1 - -R M 2081 o - Jul 13 2 0 - -R M 2082 o - May 24 3 -1 - -R M 2082 o - Jul 5 2 0 - -R M 2083 o - May 16 3 -1 - -R M 2083 o - Jun 20 2 0 - -R M 2084 o - Ap 30 3 -1 - -R M 2084 o - Jun 11 2 0 - -R M 2085 o - Ap 22 3 -1 - -R M 2085 o - Jun 3 2 0 - -R M 2086 o - Ap 14 3 -1 - -R M 2086 o - May 19 2 0 - -R M 2087 o - Mar 30 3 -1 - -R M 2087 o - May 11 2 0 - -Z Africa/Casablanca -0:30:20 - LMT 1913 O 26 -0 M +00/+01 1984 Mar 16 -1 - +01 1986 -0 M +00/+01 2018 O 28 3 -1 M +01/+00 -Z Africa/El_Aaiun -0:52:48 - LMT 1934 --1 - -01 1976 Ap 14 -0 M +00/+01 2018 O 28 3 -1 M +01/+00 -Z Africa/Maputo 2:10:20 - LMT 1903 Mar -2 - CAT -L Africa/Maputo Africa/Blantyre -L Africa/Maputo Africa/Bujumbura -L Africa/Maputo Africa/Gaborone -L Africa/Maputo Africa/Harare -L Africa/Maputo Africa/Kigali -L Africa/Maputo Africa/Lubumbashi -L Africa/Maputo Africa/Lusaka -R NA 1994 o - Mar 21 0 -1 WAT -R NA 1994 2017 - S Su>=1 2 0 CAT -R NA 1995 2017 - Ap Su>=1 2 -1 WAT -Z Africa/Windhoek 1:8:24 - LMT 1892 F 8 -1:30 - +0130 1903 Mar -2 - SAST 1942 S 20 2 -2 1 SAST 1943 Mar 21 2 -2 - SAST 1990 Mar 21 -2 NA %s -Z Africa/Lagos 0:13:35 - LMT 1905 Jul -0 - GMT 1908 Jul -0:13:35 - LMT 1914 -0:30 - +0030 1919 S -1 - WAT -L Africa/Lagos Africa/Bangui -L Africa/Lagos Africa/Brazzaville -L Africa/Lagos Africa/Douala -L Africa/Lagos Africa/Kinshasa -L Africa/Lagos Africa/Libreville -L Africa/Lagos Africa/Luanda -L Africa/Lagos Africa/Malabo -L Africa/Lagos Africa/Niamey -L Africa/Lagos Africa/Porto-Novo -Z Indian/Reunion 3:41:52 - LMT 1911 Jun -4 - +04 -Z Africa/Sao_Tome 0:26:56 - LMT 1884 --0:36:45 - LMT 1912 Ja 1 0u -0 - GMT 2018 Ja 1 1 -1 - WAT 2019 Ja 1 2 -0 - GMT -Z Indian/Mahe 3:41:48 - LMT 1907 -4 - +04 -R SA 1942 1943 - S Su>=15 2 1 - -R SA 1943 1944 - Mar Su>=15 2 0 - -Z Africa/Johannesburg 1:52 - LMT 1892 F 8 -1:30 - SAST 1903 Mar -2 SA SAST -L Africa/Johannesburg Africa/Maseru -L Africa/Johannesburg Africa/Mbabane -R SD 1970 o - May 1 0 1 S -R SD 1970 1985 - O 15 0 0 - -R SD 1971 o - Ap 30 0 1 S -R SD 1972 1985 - Ap lastSu 0 1 S -Z Africa/Khartoum 2:10:8 - LMT 1931 -2 SD CA%sT 2000 Ja 15 12 -3 - EAT 2017 N -2 - CAT -Z Africa/Juba 2:6:28 - LMT 1931 -2 SD CA%sT 2000 Ja 15 12 -3 - EAT 2021 F -2 - CAT -R n 1939 o - Ap 15 23s 1 S -R n 1939 o - N 18 23s 0 - -R n 1940 o - F 25 23s 1 S -R n 1941 o - O 6 0 0 - -R n 1942 o - Mar 9 0 1 S -R n 1942 o - N 2 3 0 - -R n 1943 o - Mar 29 2 1 S -R n 1943 o - Ap 17 2 0 - -R n 1943 o - Ap 25 2 1 S -R n 1943 o - O 4 2 0 - -R n 1944 1945 - Ap M>=1 2 1 S -R n 1944 o - O 8 0 0 - -R n 1945 o - S 16 0 0 - -R n 1977 o - Ap 30 0s 1 S -R n 1977 o - S 24 0s 0 - -R n 1978 o - May 1 0s 1 S -R n 1978 o - O 1 0s 0 - -R n 1988 o - Jun 1 0s 1 S -R n 1988 1990 - S lastSu 0s 0 - -R n 1989 o - Mar 26 0s 1 S -R n 1990 o - May 1 0s 1 S -R n 2005 o - May 1 0s 1 S -R n 2005 o - S 30 1s 0 - -R n 2006 2008 - Mar lastSu 2s 1 S -R n 2006 2008 - O lastSu 2s 0 - -Z Africa/Tunis 0:40:44 - LMT 1881 May 12 -0:9:21 - PMT 1911 Mar 11 -1 n CE%sT -Z Antarctica/Casey 0 - -00 1969 -8 - +08 2009 O 18 2 -11 - +11 2010 Mar 5 2 -8 - +08 2011 O 28 2 -11 - +11 2012 F 21 17u -8 - +08 2016 O 22 -11 - +11 2018 Mar 11 4 -8 - +08 2018 O 7 4 -11 - +11 2019 Mar 17 3 -8 - +08 2019 O 4 3 -11 - +11 2020 Mar 8 3 -8 - +08 2020 O 4 0:1 -11 - +11 -Z Antarctica/Davis 0 - -00 1957 Ja 13 -7 - +07 1964 N -0 - -00 1969 F -7 - +07 2009 O 18 2 -5 - +05 2010 Mar 10 20u -7 - +07 2011 O 28 2 -5 - +05 2012 F 21 20u -7 - +07 -Z Antarctica/Mawson 0 - -00 1954 F 13 -6 - +06 2009 O 18 2 -5 - +05 -Z Indian/Kerguelen 0 - -00 1950 -5 - +05 -R Tr 2005 ma - Mar lastSu 1u 2 +02 -R Tr 2004 ma - O lastSu 1u 0 +00 -Z Antarctica/Troll 0 - -00 2005 F 12 -0 Tr %s -Z Antarctica/Vostok 0 - -00 1957 D 16 -6 - +06 -Z Antarctica/Rothera 0 - -00 1976 D --3 - -03 -Z Asia/Kabul 4:36:48 - LMT 1890 -4 - +04 1945 -4:30 - +0430 -R AM 2011 o - Mar lastSu 2s 1 - -R AM 2011 o - O lastSu 2s 0 - -Z Asia/Yerevan 2:58 - LMT 1924 May 2 -3 - +03 1957 Mar -4 R +04/+05 1991 Mar 31 2s -3 R +03/+04 1995 S 24 2s -4 - +04 1997 -4 R +04/+05 2011 -4 AM +04/+05 -R AZ 1997 2015 - Mar lastSu 4 1 - -R AZ 1997 2015 - O lastSu 5 0 - -Z Asia/Baku 3:19:24 - LMT 1924 May 2 -3 - +03 1957 Mar -4 R +04/+05 1991 Mar 31 2s -3 R +03/+04 1992 S lastSu 2s -4 - +04 1996 -4 E +04/+05 1997 -4 AZ +04/+05 -R BD 2009 o - Jun 19 23 1 - -R BD 2009 o - D 31 24 0 - -Z Asia/Dhaka 6:1:40 - LMT 1890 -5:53:20 - HMT 1941 O -6:30 - +0630 1942 May 15 -5:30 - +0530 1942 S -6:30 - +0630 1951 S 30 -6 - +06 2009 -6 BD +06/+07 -Z Asia/Thimphu 5:58:36 - LMT 1947 Au 15 -5:30 - +0530 1987 O -6 - +06 -Z Indian/Chagos 4:49:40 - LMT 1907 -5 - +05 1996 -6 - +06 -Z Asia/Brunei 7:39:40 - LMT 1926 Mar -7:30 - +0730 1933 -8 - +08 -Z Asia/Yangon 6:24:47 - LMT 1880 -6:24:47 - RMT 1920 -6:30 - +0630 1942 May -9 - +09 1945 May 3 -6:30 - +0630 -R Sh 1919 o - Ap 12 24 1 D -R Sh 1919 o - S 30 24 0 S -R Sh 1940 o - Jun 1 0 1 D -R Sh 1940 o - O 12 24 0 S -R Sh 1941 o - Mar 15 0 1 D -R Sh 1941 o - N 1 24 0 S -R Sh 1942 o - Ja 31 0 1 D -R Sh 1945 o - S 1 24 0 S -R Sh 1946 o - May 15 0 1 D -R Sh 1946 o - S 30 24 0 S -R Sh 1947 o - Ap 15 0 1 D -R Sh 1947 o - O 31 24 0 S -R Sh 1948 1949 - May 1 0 1 D -R Sh 1948 1949 - S 30 24 0 S -R CN 1986 o - May 4 2 1 D -R CN 1986 1991 - S Su>=11 2 0 S -R CN 1987 1991 - Ap Su>=11 2 1 D -Z Asia/Shanghai 8:5:43 - LMT 1901 -8 Sh C%sT 1949 May 28 -8 CN C%sT -Z Asia/Urumqi 5:50:20 - LMT 1928 -6 - +06 -R HK 1946 o - Ap 21 0 1 S -R HK 1946 o - D 1 3:30s 0 - -R HK 1947 o - Ap 13 3:30s 1 S -R HK 1947 o - N 30 3:30s 0 - -R HK 1948 o - May 2 3:30s 1 S -R HK 1948 1952 - O Su>=28 3:30s 0 - -R HK 1949 1953 - Ap Su>=1 3:30 1 S -R HK 1953 1964 - O Su>=31 3:30 0 - -R HK 1954 1964 - Mar Su>=18 3:30 1 S -R HK 1965 1976 - Ap Su>=16 3:30 1 S -R HK 1965 1976 - O Su>=16 3:30 0 - -R HK 1973 o - D 30 3:30 1 S -R HK 1979 o - May 13 3:30 1 S -R HK 1979 o - O 21 3:30 0 - -Z Asia/Hong_Kong 7:36:42 - LMT 1904 O 30 0:36:42 -8 - HKT 1941 Jun 15 3 -8 1 HKST 1941 O 1 4 -8 0:30 HKWT 1941 D 25 -9 - JST 1945 N 18 2 -8 HK HK%sT -R f 1946 o - May 15 0 1 D -R f 1946 o - O 1 0 0 S -R f 1947 o - Ap 15 0 1 D -R f 1947 o - N 1 0 0 S -R f 1948 1951 - May 1 0 1 D -R f 1948 1951 - O 1 0 0 S -R f 1952 o - Mar 1 0 1 D -R f 1952 1954 - N 1 0 0 S -R f 1953 1959 - Ap 1 0 1 D -R f 1955 1961 - O 1 0 0 S -R f 1960 1961 - Jun 1 0 1 D -R f 1974 1975 - Ap 1 0 1 D -R f 1974 1975 - O 1 0 0 S -R f 1979 o - Jul 1 0 1 D -R f 1979 o - O 1 0 0 S -Z Asia/Taipei 8:6 - LMT 1896 -8 - CST 1937 O -9 - JST 1945 S 21 1 -8 f C%sT -R _ 1942 1943 - Ap 30 23 1 - -R _ 1942 o - N 17 23 0 - -R _ 1943 o - S 30 23 0 S -R _ 1946 o - Ap 30 23s 1 D -R _ 1946 o - S 30 23s 0 S -R _ 1947 o - Ap 19 23s 1 D -R _ 1947 o - N 30 23s 0 S -R _ 1948 o - May 2 23s 1 D -R _ 1948 o - O 31 23s 0 S -R _ 1949 1950 - Ap Sa>=1 23s 1 D -R _ 1949 1950 - O lastSa 23s 0 S -R _ 1951 o - Mar 31 23s 1 D -R _ 1951 o - O 28 23s 0 S -R _ 1952 1953 - Ap Sa>=1 23s 1 D -R _ 1952 o - N 1 23s 0 S -R _ 1953 1954 - O lastSa 23s 0 S -R _ 1954 1956 - Mar Sa>=17 23s 1 D -R _ 1955 o - N 5 23s 0 S -R _ 1956 1964 - N Su>=1 3:30 0 S -R _ 1957 1964 - Mar Su>=18 3:30 1 D -R _ 1965 1973 - Ap Su>=16 3:30 1 D -R _ 1965 1966 - O Su>=16 2:30 0 S -R _ 1967 1976 - O Su>=16 3:30 0 S -R _ 1973 o - D 30 3:30 1 D -R _ 1975 1976 - Ap Su>=16 3:30 1 D -R _ 1979 o - May 13 3:30 1 D -R _ 1979 o - O Su>=16 3:30 0 S -Z Asia/Macau 7:34:10 - LMT 1904 O 30 -8 - CST 1941 D 21 23 -9 _ +09/+10 1945 S 30 24 -8 _ C%sT -R CY 1975 o - Ap 13 0 1 S -R CY 1975 o - O 12 0 0 - -R CY 1976 o - May 15 0 1 S -R CY 1976 o - O 11 0 0 - -R CY 1977 1980 - Ap Su>=1 0 1 S -R CY 1977 o - S 25 0 0 - -R CY 1978 o - O 2 0 0 - -R CY 1979 1997 - S lastSu 0 0 - -R CY 1981 1998 - Mar lastSu 0 1 S -Z Asia/Nicosia 2:13:28 - LMT 1921 N 14 -2 CY EE%sT 1998 S -2 E EE%sT -Z Asia/Famagusta 2:15:48 - LMT 1921 N 14 -2 CY EE%sT 1998 S -2 E EE%sT 2016 S 8 -3 - +03 2017 O 29 1u -2 E EE%sT -L Asia/Nicosia Europe/Nicosia -Z Asia/Tbilisi 2:59:11 - LMT 1880 -2:59:11 - TBMT 1924 May 2 -3 - +03 1957 Mar -4 R +04/+05 1991 Mar 31 2s -3 R +03/+04 1992 -3 e +03/+04 1994 S lastSu -4 e +04/+05 1996 O lastSu -4 1 +05 1997 Mar lastSu -4 e +04/+05 2004 Jun 27 -3 R +03/+04 2005 Mar lastSu 2 -4 - +04 -Z Asia/Dili 8:22:20 - LMT 1912 -8 - +08 1942 F 21 23 -9 - +09 1976 May 3 -8 - +08 2000 S 17 -9 - +09 -Z Asia/Kolkata 5:53:28 - LMT 1854 Jun 28 -5:53:20 - HMT 1870 -5:21:10 - MMT 1906 -5:30 - IST 1941 O -5:30 1 +0630 1942 May 15 -5:30 - IST 1942 S -5:30 1 +0630 1945 O 15 -5:30 - IST -Z Asia/Jakarta 7:7:12 - LMT 1867 Au 10 -7:7:12 - BMT 1923 D 31 23:47:12 -7:20 - +0720 1932 N -7:30 - +0730 1942 Mar 23 -9 - +09 1945 S 23 -7:30 - +0730 1948 May -8 - +08 1950 May -7:30 - +0730 1964 -7 - WIB -Z Asia/Pontianak 7:17:20 - LMT 1908 May -7:17:20 - PMT 1932 N -7:30 - +0730 1942 Ja 29 -9 - +09 1945 S 23 -7:30 - +0730 1948 May -8 - +08 1950 May -7:30 - +0730 1964 -8 - WITA 1988 -7 - WIB -Z Asia/Makassar 7:57:36 - LMT 1920 -7:57:36 - MMT 1932 N -8 - +08 1942 F 9 -9 - +09 1945 S 23 -8 - WITA -Z Asia/Jayapura 9:22:48 - LMT 1932 N -9 - +09 1944 S -9:30 - +0930 1964 -9 - WIT -R i 1978 1980 - Mar 20 24 1 - -R i 1978 o - O 20 24 0 - -R i 1979 o - S 18 24 0 - -R i 1980 o - S 22 24 0 - -R i 1991 o - May 2 24 1 - -R i 1992 1995 - Mar 21 24 1 - -R i 1991 1995 - S 21 24 0 - -R i 1996 o - Mar 20 24 1 - -R i 1996 o - S 20 24 0 - -R i 1997 1999 - Mar 21 24 1 - -R i 1997 1999 - S 21 24 0 - -R i 2000 o - Mar 20 24 1 - -R i 2000 o - S 20 24 0 - -R i 2001 2003 - Mar 21 24 1 - -R i 2001 2003 - S 21 24 0 - -R i 2004 o - Mar 20 24 1 - -R i 2004 o - S 20 24 0 - -R i 2005 o - Mar 21 24 1 - -R i 2005 o - S 21 24 0 - -R i 2008 o - Mar 20 24 1 - -R i 2008 o - S 20 24 0 - -R i 2009 2011 - Mar 21 24 1 - -R i 2009 2011 - S 21 24 0 - -R i 2012 o - Mar 20 24 1 - -R i 2012 o - S 20 24 0 - -R i 2013 2015 - Mar 21 24 1 - -R i 2013 2015 - S 21 24 0 - -R i 2016 o - Mar 20 24 1 - -R i 2016 o - S 20 24 0 - -R i 2017 2019 - Mar 21 24 1 - -R i 2017 2019 - S 21 24 0 - -R i 2020 o - Mar 20 24 1 - -R i 2020 o - S 20 24 0 - -R i 2021 2023 - Mar 21 24 1 - -R i 2021 2023 - S 21 24 0 - -R i 2024 o - Mar 20 24 1 - -R i 2024 o - S 20 24 0 - -R i 2025 2027 - Mar 21 24 1 - -R i 2025 2027 - S 21 24 0 - -R i 2028 2029 - Mar 20 24 1 - -R i 2028 2029 - S 20 24 0 - -R i 2030 2031 - Mar 21 24 1 - -R i 2030 2031 - S 21 24 0 - -R i 2032 2033 - Mar 20 24 1 - -R i 2032 2033 - S 20 24 0 - -R i 2034 2035 - Mar 21 24 1 - -R i 2034 2035 - S 21 24 0 - -R i 2036 2037 - Mar 20 24 1 - -R i 2036 2037 - S 20 24 0 - -R i 2038 2039 - Mar 21 24 1 - -R i 2038 2039 - S 21 24 0 - -R i 2040 2041 - Mar 20 24 1 - -R i 2040 2041 - S 20 24 0 - -R i 2042 2043 - Mar 21 24 1 - -R i 2042 2043 - S 21 24 0 - -R i 2044 2045 - Mar 20 24 1 - -R i 2044 2045 - S 20 24 0 - -R i 2046 2047 - Mar 21 24 1 - -R i 2046 2047 - S 21 24 0 - -R i 2048 2049 - Mar 20 24 1 - -R i 2048 2049 - S 20 24 0 - -R i 2050 2051 - Mar 21 24 1 - -R i 2050 2051 - S 21 24 0 - -R i 2052 2053 - Mar 20 24 1 - -R i 2052 2053 - S 20 24 0 - -R i 2054 2055 - Mar 21 24 1 - -R i 2054 2055 - S 21 24 0 - -R i 2056 2057 - Mar 20 24 1 - -R i 2056 2057 - S 20 24 0 - -R i 2058 2059 - Mar 21 24 1 - -R i 2058 2059 - S 21 24 0 - -R i 2060 2062 - Mar 20 24 1 - -R i 2060 2062 - S 20 24 0 - -R i 2063 o - Mar 21 24 1 - -R i 2063 o - S 21 24 0 - -R i 2064 2066 - Mar 20 24 1 - -R i 2064 2066 - S 20 24 0 - -R i 2067 o - Mar 21 24 1 - -R i 2067 o - S 21 24 0 - -R i 2068 2070 - Mar 20 24 1 - -R i 2068 2070 - S 20 24 0 - -R i 2071 o - Mar 21 24 1 - -R i 2071 o - S 21 24 0 - -R i 2072 2074 - Mar 20 24 1 - -R i 2072 2074 - S 20 24 0 - -R i 2075 o - Mar 21 24 1 - -R i 2075 o - S 21 24 0 - -R i 2076 2078 - Mar 20 24 1 - -R i 2076 2078 - S 20 24 0 - -R i 2079 o - Mar 21 24 1 - -R i 2079 o - S 21 24 0 - -R i 2080 2082 - Mar 20 24 1 - -R i 2080 2082 - S 20 24 0 - -R i 2083 o - Mar 21 24 1 - -R i 2083 o - S 21 24 0 - -R i 2084 2086 - Mar 20 24 1 - -R i 2084 2086 - S 20 24 0 - -R i 2087 o - Mar 21 24 1 - -R i 2087 o - S 21 24 0 - -R i 2088 ma - Mar 20 24 1 - -R i 2088 ma - S 20 24 0 - -Z Asia/Tehran 3:25:44 - LMT 1916 -3:25:44 - TMT 1946 -3:30 - +0330 1977 N -4 i +04/+05 1979 -3:30 i +0330/+0430 -R IQ 1982 o - May 1 0 1 - -R IQ 1982 1984 - O 1 0 0 - -R IQ 1983 o - Mar 31 0 1 - -R IQ 1984 1985 - Ap 1 0 1 - -R IQ 1985 1990 - S lastSu 1s 0 - -R IQ 1986 1990 - Mar lastSu 1s 1 - -R IQ 1991 2007 - Ap 1 3s 1 - -R IQ 1991 2007 - O 1 3s 0 - -Z Asia/Baghdad 2:57:40 - LMT 1890 -2:57:36 - BMT 1918 -3 - +03 1982 May -3 IQ +03/+04 -R Z 1940 o - May 31 24u 1 D -R Z 1940 o - S 30 24u 0 S -R Z 1940 o - N 16 24u 1 D -R Z 1942 1946 - O 31 24u 0 S -R Z 1943 1944 - Mar 31 24u 1 D -R Z 1945 1946 - Ap 15 24u 1 D -R Z 1948 o - May 22 24u 2 DD -R Z 1948 o - Au 31 24u 1 D -R Z 1948 1949 - O 31 24u 0 S -R Z 1949 o - Ap 30 24u 1 D -R Z 1950 o - Ap 15 24u 1 D -R Z 1950 o - S 14 24u 0 S -R Z 1951 o - Mar 31 24u 1 D -R Z 1951 o - N 10 24u 0 S -R Z 1952 o - Ap 19 24u 1 D -R Z 1952 o - O 18 24u 0 S -R Z 1953 o - Ap 11 24u 1 D -R Z 1953 o - S 12 24u 0 S -R Z 1954 o - Jun 12 24u 1 D -R Z 1954 o - S 11 24u 0 S -R Z 1955 o - Jun 11 24u 1 D -R Z 1955 o - S 10 24u 0 S -R Z 1956 o - Jun 2 24u 1 D -R Z 1956 o - S 29 24u 0 S -R Z 1957 o - Ap 27 24u 1 D -R Z 1957 o - S 21 24u 0 S -R Z 1974 o - Jul 6 24 1 D -R Z 1974 o - O 12 24 0 S -R Z 1975 o - Ap 19 24 1 D -R Z 1975 o - Au 30 24 0 S -R Z 1980 o - Au 2 24s 1 D -R Z 1980 o - S 13 24s 0 S -R Z 1984 o - May 5 24s 1 D -R Z 1984 o - Au 25 24s 0 S -R Z 1985 o - Ap 13 24 1 D -R Z 1985 o - Au 31 24 0 S -R Z 1986 o - May 17 24 1 D -R Z 1986 o - S 6 24 0 S -R Z 1987 o - Ap 14 24 1 D -R Z 1987 o - S 12 24 0 S -R Z 1988 o - Ap 9 24 1 D -R Z 1988 o - S 3 24 0 S -R Z 1989 o - Ap 29 24 1 D -R Z 1989 o - S 2 24 0 S -R Z 1990 o - Mar 24 24 1 D -R Z 1990 o - Au 25 24 0 S -R Z 1991 o - Mar 23 24 1 D -R Z 1991 o - Au 31 24 0 S -R Z 1992 o - Mar 28 24 1 D -R Z 1992 o - S 5 24 0 S -R Z 1993 o - Ap 2 0 1 D -R Z 1993 o - S 5 0 0 S -R Z 1994 o - Ap 1 0 1 D -R Z 1994 o - Au 28 0 0 S -R Z 1995 o - Mar 31 0 1 D -R Z 1995 o - S 3 0 0 S -R Z 1996 o - Mar 14 24 1 D -R Z 1996 o - S 15 24 0 S -R Z 1997 o - Mar 20 24 1 D -R Z 1997 o - S 13 24 0 S -R Z 1998 o - Mar 20 0 1 D -R Z 1998 o - S 6 0 0 S -R Z 1999 o - Ap 2 2 1 D -R Z 1999 o - S 3 2 0 S -R Z 2000 o - Ap 14 2 1 D -R Z 2000 o - O 6 1 0 S -R Z 2001 o - Ap 9 1 1 D -R Z 2001 o - S 24 1 0 S -R Z 2002 o - Mar 29 1 1 D -R Z 2002 o - O 7 1 0 S -R Z 2003 o - Mar 28 1 1 D -R Z 2003 o - O 3 1 0 S -R Z 2004 o - Ap 7 1 1 D -R Z 2004 o - S 22 1 0 S -R Z 2005 2012 - Ap F<=1 2 1 D -R Z 2005 o - O 9 2 0 S -R Z 2006 o - O 1 2 0 S -R Z 2007 o - S 16 2 0 S -R Z 2008 o - O 5 2 0 S -R Z 2009 o - S 27 2 0 S -R Z 2010 o - S 12 2 0 S -R Z 2011 o - O 2 2 0 S -R Z 2012 o - S 23 2 0 S -R Z 2013 ma - Mar F>=23 2 1 D -R Z 2013 ma - O lastSu 2 0 S -Z Asia/Jerusalem 2:20:54 - LMT 1880 -2:20:40 - JMT 1918 -2 Z I%sT -R JP 1948 o - May Sa>=1 24 1 D -R JP 1948 1951 - S Sa>=8 25 0 S -R JP 1949 o - Ap Sa>=1 24 1 D -R JP 1950 1951 - May Sa>=1 24 1 D -Z Asia/Tokyo 9:18:59 - LMT 1887 D 31 15u -9 JP J%sT -R J 1973 o - Jun 6 0 1 S -R J 1973 1975 - O 1 0 0 - -R J 1974 1977 - May 1 0 1 S -R J 1976 o - N 1 0 0 - -R J 1977 o - O 1 0 0 - -R J 1978 o - Ap 30 0 1 S -R J 1978 o - S 30 0 0 - -R J 1985 o - Ap 1 0 1 S -R J 1985 o - O 1 0 0 - -R J 1986 1988 - Ap F>=1 0 1 S -R J 1986 1990 - O F>=1 0 0 - -R J 1989 o - May 8 0 1 S -R J 1990 o - Ap 27 0 1 S -R J 1991 o - Ap 17 0 1 S -R J 1991 o - S 27 0 0 - -R J 1992 o - Ap 10 0 1 S -R J 1992 1993 - O F>=1 0 0 - -R J 1993 1998 - Ap F>=1 0 1 S -R J 1994 o - S F>=15 0 0 - -R J 1995 1998 - S F>=15 0s 0 - -R J 1999 o - Jul 1 0s 1 S -R J 1999 2002 - S lastF 0s 0 - -R J 2000 2001 - Mar lastTh 0s 1 S -R J 2002 2012 - Mar lastTh 24 1 S -R J 2003 o - O 24 0s 0 - -R J 2004 o - O 15 0s 0 - -R J 2005 o - S lastF 0s 0 - -R J 2006 2011 - O lastF 0s 0 - -R J 2013 o - D 20 0 0 - -R J 2014 2021 - Mar lastTh 24 1 S -R J 2014 ma - O lastF 0s 0 - -R J 2022 ma - F lastTh 24 1 S -Z Asia/Amman 2:23:44 - LMT 1931 -2 J EE%sT -Z Asia/Almaty 5:7:48 - LMT 1924 May 2 -5 - +05 1930 Jun 21 -6 R +06/+07 1991 Mar 31 2s -5 R +05/+06 1992 Ja 19 2s -6 R +06/+07 2004 O 31 2s -6 - +06 -Z Asia/Qyzylorda 4:21:52 - LMT 1924 May 2 -4 - +04 1930 Jun 21 -5 - +05 1981 Ap -5 1 +06 1981 O -6 - +06 1982 Ap -5 R +05/+06 1991 Mar 31 2s -4 R +04/+05 1991 S 29 2s -5 R +05/+06 1992 Ja 19 2s -6 R +06/+07 1992 Mar 29 2s -5 R +05/+06 2004 O 31 2s -6 - +06 2018 D 21 -5 - +05 -Z Asia/Qostanay 4:14:28 - LMT 1924 May 2 -4 - +04 1930 Jun 21 -5 - +05 1981 Ap -5 1 +06 1981 O -6 - +06 1982 Ap -5 R +05/+06 1991 Mar 31 2s -4 R +04/+05 1992 Ja 19 2s -5 R +05/+06 2004 O 31 2s -6 - +06 -Z Asia/Aqtobe 3:48:40 - LMT 1924 May 2 -4 - +04 1930 Jun 21 -5 - +05 1981 Ap -5 1 +06 1981 O -6 - +06 1982 Ap -5 R +05/+06 1991 Mar 31 2s -4 R +04/+05 1992 Ja 19 2s -5 R +05/+06 2004 O 31 2s -5 - +05 -Z Asia/Aqtau 3:21:4 - LMT 1924 May 2 -4 - +04 1930 Jun 21 -5 - +05 1981 O -6 - +06 1982 Ap -5 R +05/+06 1991 Mar 31 2s -4 R +04/+05 1992 Ja 19 2s -5 R +05/+06 1994 S 25 2s -4 R +04/+05 2004 O 31 2s -5 - +05 -Z Asia/Atyrau 3:27:44 - LMT 1924 May 2 -3 - +03 1930 Jun 21 -5 - +05 1981 O -6 - +06 1982 Ap -5 R +05/+06 1991 Mar 31 2s -4 R +04/+05 1992 Ja 19 2s -5 R +05/+06 1999 Mar 28 2s -4 R +04/+05 2004 O 31 2s -5 - +05 -Z Asia/Oral 3:25:24 - LMT 1924 May 2 -3 - +03 1930 Jun 21 -5 - +05 1981 Ap -5 1 +06 1981 O -6 - +06 1982 Ap -5 R +05/+06 1989 Mar 26 2s -4 R +04/+05 1992 Ja 19 2s -5 R +05/+06 1992 Mar 29 2s -4 R +04/+05 2004 O 31 2s -5 - +05 -R KG 1992 1996 - Ap Su>=7 0s 1 - -R KG 1992 1996 - S lastSu 0 0 - -R KG 1997 2005 - Mar lastSu 2:30 1 - -R KG 1997 2004 - O lastSu 2:30 0 - -Z Asia/Bishkek 4:58:24 - LMT 1924 May 2 -5 - +05 1930 Jun 21 -6 R +06/+07 1991 Mar 31 2s -5 R +05/+06 1991 Au 31 2 -5 KG +05/+06 2005 Au 12 -6 - +06 -R KR 1948 o - Jun 1 0 1 D -R KR 1948 o - S 12 24 0 S -R KR 1949 o - Ap 3 0 1 D -R KR 1949 1951 - S Sa>=7 24 0 S -R KR 1950 o - Ap 1 0 1 D -R KR 1951 o - May 6 0 1 D -R KR 1955 o - May 5 0 1 D -R KR 1955 o - S 8 24 0 S -R KR 1956 o - May 20 0 1 D -R KR 1956 o - S 29 24 0 S -R KR 1957 1960 - May Su>=1 0 1 D -R KR 1957 1960 - S Sa>=17 24 0 S -R KR 1987 1988 - May Su>=8 2 1 D -R KR 1987 1988 - O Su>=8 3 0 S -Z Asia/Seoul 8:27:52 - LMT 1908 Ap -8:30 - KST 1912 -9 - JST 1945 S 8 -9 KR K%sT 1954 Mar 21 -8:30 KR K%sT 1961 Au 10 -9 KR K%sT -Z Asia/Pyongyang 8:23 - LMT 1908 Ap -8:30 - KST 1912 -9 - JST 1945 Au 24 -9 - KST 2015 Au 15 -8:30 - KST 2018 May 4 23:30 -9 - KST -R l 1920 o - Mar 28 0 1 S -R l 1920 o - O 25 0 0 - -R l 1921 o - Ap 3 0 1 S -R l 1921 o - O 3 0 0 - -R l 1922 o - Mar 26 0 1 S -R l 1922 o - O 8 0 0 - -R l 1923 o - Ap 22 0 1 S -R l 1923 o - S 16 0 0 - -R l 1957 1961 - May 1 0 1 S -R l 1957 1961 - O 1 0 0 - -R l 1972 o - Jun 22 0 1 S -R l 1972 1977 - O 1 0 0 - -R l 1973 1977 - May 1 0 1 S -R l 1978 o - Ap 30 0 1 S -R l 1978 o - S 30 0 0 - -R l 1984 1987 - May 1 0 1 S -R l 1984 1991 - O 16 0 0 - -R l 1988 o - Jun 1 0 1 S -R l 1989 o - May 10 0 1 S -R l 1990 1992 - May 1 0 1 S -R l 1992 o - O 4 0 0 - -R l 1993 ma - Mar lastSu 0 1 S -R l 1993 1998 - S lastSu 0 0 - -R l 1999 ma - O lastSu 0 0 - -Z Asia/Beirut 2:22 - LMT 1880 -2 l EE%sT -R NB 1935 1941 - S 14 0 0:20 - -R NB 1935 1941 - D 14 0 0 - -Z Asia/Kuala_Lumpur 6:46:46 - LMT 1901 -6:55:25 - SMT 1905 Jun -7 - +07 1933 -7 0:20 +0720 1936 -7:20 - +0720 1941 S -7:30 - +0730 1942 F 16 -9 - +09 1945 S 12 -7:30 - +0730 1982 -8 - +08 -Z Asia/Kuching 7:21:20 - LMT 1926 Mar -7:30 - +0730 1933 -8 NB +08/+0820 1942 F 16 -9 - +09 1945 S 12 -8 - +08 -Z Indian/Maldives 4:54 - LMT 1880 -4:54 - MMT 1960 -5 - +05 -R X 1983 1984 - Ap 1 0 1 - -R X 1983 o - O 1 0 0 - -R X 1985 1998 - Mar lastSu 0 1 - -R X 1984 1998 - S lastSu 0 0 - -R X 2001 o - Ap lastSa 2 1 - -R X 2001 2006 - S lastSa 2 0 - -R X 2002 2006 - Mar lastSa 2 1 - -R X 2015 2016 - Mar lastSa 2 1 - -R X 2015 2016 - S lastSa 0 0 - -Z Asia/Hovd 6:6:36 - LMT 1905 Au -6 - +06 1978 -7 X +07/+08 -Z Asia/Ulaanbaatar 7:7:32 - LMT 1905 Au -7 - +07 1978 -8 X +08/+09 -Z Asia/Choibalsan 7:38 - LMT 1905 Au -7 - +07 1978 -8 - +08 1983 Ap -9 X +09/+10 2008 Mar 31 -8 X +08/+09 -Z Asia/Kathmandu 5:41:16 - LMT 1920 -5:30 - +0530 1986 -5:45 - +0545 -R PK 2002 o - Ap Su>=2 0 1 S -R PK 2002 o - O Su>=2 0 0 - -R PK 2008 o - Jun 1 0 1 S -R PK 2008 2009 - N 1 0 0 - -R PK 2009 o - Ap 15 0 1 S -Z Asia/Karachi 4:28:12 - LMT 1907 -5:30 - +0530 1942 S -5:30 1 +0630 1945 O 15 -5:30 - +0530 1951 S 30 -5 - +05 1971 Mar 26 -5 PK PK%sT -R P 1999 2005 - Ap F>=15 0 1 S -R P 1999 2003 - O F>=15 0 0 - -R P 2004 o - O 1 1 0 - -R P 2005 o - O 4 2 0 - -R P 2006 2007 - Ap 1 0 1 S -R P 2006 o - S 22 0 0 - -R P 2007 o - S 13 2 0 - -R P 2008 2009 - Mar lastF 0 1 S -R P 2008 o - S 1 0 0 - -R P 2009 o - S 4 1 0 - -R P 2010 o - Mar 26 0 1 S -R P 2010 o - Au 11 0 0 - -R P 2011 o - Ap 1 0:1 1 S -R P 2011 o - Au 1 0 0 - -R P 2011 o - Au 30 0 1 S -R P 2011 o - S 30 0 0 - -R P 2012 2014 - Mar lastTh 24 1 S -R P 2012 o - S 21 1 0 - -R P 2013 o - S 27 0 0 - -R P 2014 o - O 24 0 0 - -R P 2015 o - Mar 28 0 1 S -R P 2015 o - O 23 1 0 - -R P 2016 2018 - Mar Sa>=24 1 1 S -R P 2016 2018 - O Sa>=24 1 0 - -R P 2019 o - Mar 29 0 1 S -R P 2019 o - O Sa>=24 0 0 - -R P 2020 2021 - Mar Sa>=24 0 1 S -R P 2020 o - O 24 1 0 - -R P 2021 ma - O F>=23 1 0 - -R P 2022 ma - Mar Su>=25 0 1 S -Z Asia/Gaza 2:17:52 - LMT 1900 O -2 Z EET/EEST 1948 May 15 -2 K EE%sT 1967 Jun 5 -2 Z I%sT 1996 -2 J EE%sT 1999 -2 P EE%sT 2008 Au 29 -2 - EET 2008 S -2 P EE%sT 2010 -2 - EET 2010 Mar 27 0:1 -2 P EE%sT 2011 Au -2 - EET 2012 -2 P EE%sT -Z Asia/Hebron 2:20:23 - LMT 1900 O -2 Z EET/EEST 1948 May 15 -2 K EE%sT 1967 Jun 5 -2 Z I%sT 1996 -2 J EE%sT 1999 -2 P EE%sT -R PH 1936 o - N 1 0 1 D -R PH 1937 o - F 1 0 0 S -R PH 1954 o - Ap 12 0 1 D -R PH 1954 o - Jul 1 0 0 S -R PH 1978 o - Mar 22 0 1 D -R PH 1978 o - S 21 0 0 S -Z Asia/Manila -15:56 - LMT 1844 D 31 -8:4 - LMT 1899 May 11 -8 PH P%sT 1942 May -9 - JST 1944 N -8 PH P%sT -Z Asia/Qatar 3:26:8 - LMT 1920 -4 - +04 1972 Jun -3 - +03 -L Asia/Qatar Asia/Bahrain -Z Asia/Riyadh 3:6:52 - LMT 1947 Mar 14 -3 - +03 -L Asia/Riyadh Antarctica/Syowa -L Asia/Riyadh Asia/Aden -L Asia/Riyadh Asia/Kuwait -Z Asia/Singapore 6:55:25 - LMT 1901 -6:55:25 - SMT 1905 Jun -7 - +07 1933 -7 0:20 +0720 1936 -7:20 - +0720 1941 S -7:30 - +0730 1942 F 16 -9 - +09 1945 S 12 -7:30 - +0730 1982 -8 - +08 -Z Asia/Colombo 5:19:24 - LMT 1880 -5:19:32 - MMT 1906 -5:30 - +0530 1942 Ja 5 -5:30 0:30 +06 1942 S -5:30 1 +0630 1945 O 16 2 -5:30 - +0530 1996 May 25 -6:30 - +0630 1996 O 26 0:30 -6 - +06 2006 Ap 15 0:30 -5:30 - +0530 -R S 1920 1923 - Ap Su>=15 2 1 S -R S 1920 1923 - O Su>=1 2 0 - -R S 1962 o - Ap 29 2 1 S -R S 1962 o - O 1 2 0 - -R S 1963 1965 - May 1 2 1 S -R S 1963 o - S 30 2 0 - -R S 1964 o - O 1 2 0 - -R S 1965 o - S 30 2 0 - -R S 1966 o - Ap 24 2 1 S -R S 1966 1976 - O 1 2 0 - -R S 1967 1978 - May 1 2 1 S -R S 1977 1978 - S 1 2 0 - -R S 1983 1984 - Ap 9 2 1 S -R S 1983 1984 - O 1 2 0 - -R S 1986 o - F 16 2 1 S -R S 1986 o - O 9 2 0 - -R S 1987 o - Mar 1 2 1 S -R S 1987 1988 - O 31 2 0 - -R S 1988 o - Mar 15 2 1 S -R S 1989 o - Mar 31 2 1 S -R S 1989 o - O 1 2 0 - -R S 1990 o - Ap 1 2 1 S -R S 1990 o - S 30 2 0 - -R S 1991 o - Ap 1 0 1 S -R S 1991 1992 - O 1 0 0 - -R S 1992 o - Ap 8 0 1 S -R S 1993 o - Mar 26 0 1 S -R S 1993 o - S 25 0 0 - -R S 1994 1996 - Ap 1 0 1 S -R S 1994 2005 - O 1 0 0 - -R S 1997 1998 - Mar lastM 0 1 S -R S 1999 2006 - Ap 1 0 1 S -R S 2006 o - S 22 0 0 - -R S 2007 o - Mar lastF 0 1 S -R S 2007 o - N F>=1 0 0 - -R S 2008 o - Ap F>=1 0 1 S -R S 2008 o - N 1 0 0 - -R S 2009 o - Mar lastF 0 1 S -R S 2010 2011 - Ap F>=1 0 1 S -R S 2012 ma - Mar lastF 0 1 S -R S 2009 ma - O lastF 0 0 - -Z Asia/Damascus 2:25:12 - LMT 1920 -2 S EE%sT -Z Asia/Dushanbe 4:35:12 - LMT 1924 May 2 -5 - +05 1930 Jun 21 -6 R +06/+07 1991 Mar 31 2s -5 1 +05/+06 1991 S 9 2s -5 - +05 -Z Asia/Bangkok 6:42:4 - LMT 1880 -6:42:4 - BMT 1920 Ap -7 - +07 -L Asia/Bangkok Asia/Phnom_Penh -L Asia/Bangkok Asia/Vientiane -Z Asia/Ashgabat 3:53:32 - LMT 1924 May 2 -4 - +04 1930 Jun 21 -5 R +05/+06 1991 Mar 31 2 -4 R +04/+05 1992 Ja 19 2 -5 - +05 -Z Asia/Dubai 3:41:12 - LMT 1920 -4 - +04 -L Asia/Dubai Asia/Muscat -Z Asia/Samarkand 4:27:53 - LMT 1924 May 2 -4 - +04 1930 Jun 21 -5 - +05 1981 Ap -5 1 +06 1981 O -6 - +06 1982 Ap -5 R +05/+06 1992 -5 - +05 -Z Asia/Tashkent 4:37:11 - LMT 1924 May 2 -5 - +05 1930 Jun 21 -6 R +06/+07 1991 Mar 31 2 -5 R +05/+06 1992 -5 - +05 -Z Asia/Ho_Chi_Minh 7:6:40 - LMT 1906 Jul -7:6:30 - PLMT 1911 May -7 - +07 1942 D 31 23 -8 - +08 1945 Mar 14 23 -9 - +09 1945 S 2 -7 - +07 1947 Ap -8 - +08 1955 Jul -7 - +07 1959 D 31 23 -8 - +08 1975 Jun 13 -7 - +07 -R AU 1917 o - Ja 1 2s 1 D -R AU 1917 o - Mar lastSu 2s 0 S -R AU 1942 o - Ja 1 2s 1 D -R AU 1942 o - Mar lastSu 2s 0 S -R AU 1942 o - S 27 2s 1 D -R AU 1943 1944 - Mar lastSu 2s 0 S -R AU 1943 o - O 3 2s 1 D -Z Australia/Darwin 8:43:20 - LMT 1895 F -9 - ACST 1899 May -9:30 AU AC%sT -R AW 1974 o - O lastSu 2s 1 D -R AW 1975 o - Mar Su>=1 2s 0 S -R AW 1983 o - O lastSu 2s 1 D -R AW 1984 o - Mar Su>=1 2s 0 S -R AW 1991 o - N 17 2s 1 D -R AW 1992 o - Mar Su>=1 2s 0 S -R AW 2006 o - D 3 2s 1 D -R AW 2007 2009 - Mar lastSu 2s 0 S -R AW 2007 2008 - O lastSu 2s 1 D -Z Australia/Perth 7:43:24 - LMT 1895 D -8 AU AW%sT 1943 Jul -8 AW AW%sT -Z Australia/Eucla 8:35:28 - LMT 1895 D -8:45 AU +0845/+0945 1943 Jul -8:45 AW +0845/+0945 -R AQ 1971 o - O lastSu 2s 1 D -R AQ 1972 o - F lastSu 2s 0 S -R AQ 1989 1991 - O lastSu 2s 1 D -R AQ 1990 1992 - Mar Su>=1 2s 0 S -R Ho 1992 1993 - O lastSu 2s 1 D -R Ho 1993 1994 - Mar Su>=1 2s 0 S -Z Australia/Brisbane 10:12:8 - LMT 1895 -10 AU AE%sT 1971 -10 AQ AE%sT -Z Australia/Lindeman 9:55:56 - LMT 1895 -10 AU AE%sT 1971 -10 AQ AE%sT 1992 Jul -10 Ho AE%sT -R AS 1971 1985 - O lastSu 2s 1 D -R AS 1986 o - O 19 2s 1 D -R AS 1987 2007 - O lastSu 2s 1 D -R AS 1972 o - F 27 2s 0 S -R AS 1973 1985 - Mar Su>=1 2s 0 S -R AS 1986 1990 - Mar Su>=15 2s 0 S -R AS 1991 o - Mar 3 2s 0 S -R AS 1992 o - Mar 22 2s 0 S -R AS 1993 o - Mar 7 2s 0 S -R AS 1994 o - Mar 20 2s 0 S -R AS 1995 2005 - Mar lastSu 2s 0 S -R AS 2006 o - Ap 2 2s 0 S -R AS 2007 o - Mar lastSu 2s 0 S -R AS 2008 ma - Ap Su>=1 2s 0 S -R AS 2008 ma - O Su>=1 2s 1 D -Z Australia/Adelaide 9:14:20 - LMT 1895 F -9 - ACST 1899 May -9:30 AU AC%sT 1971 -9:30 AS AC%sT -R AT 1916 o - O Su>=1 2s 1 D -R AT 1917 o - Mar lastSu 2s 0 S -R AT 1917 1918 - O Su>=22 2s 1 D -R AT 1918 1919 - Mar Su>=1 2s 0 S -R AT 1967 o - O Su>=1 2s 1 D -R AT 1968 o - Mar Su>=29 2s 0 S -R AT 1968 1985 - O lastSu 2s 1 D -R AT 1969 1971 - Mar Su>=8 2s 0 S -R AT 1972 o - F lastSu 2s 0 S -R AT 1973 1981 - Mar Su>=1 2s 0 S -R AT 1982 1983 - Mar lastSu 2s 0 S -R AT 1984 1986 - Mar Su>=1 2s 0 S -R AT 1986 o - O Su>=15 2s 1 D -R AT 1987 1990 - Mar Su>=15 2s 0 S -R AT 1987 o - O Su>=22 2s 1 D -R AT 1988 1990 - O lastSu 2s 1 D -R AT 1991 1999 - O Su>=1 2s 1 D -R AT 1991 2005 - Mar lastSu 2s 0 S -R AT 2000 o - Au lastSu 2s 1 D -R AT 2001 ma - O Su>=1 2s 1 D -R AT 2006 o - Ap Su>=1 2s 0 S -R AT 2007 o - Mar lastSu 2s 0 S -R AT 2008 ma - Ap Su>=1 2s 0 S -Z Australia/Hobart 9:49:16 - LMT 1895 S -10 AT AE%sT 1919 O 24 -10 AU AE%sT 1967 -10 AT AE%sT -R AV 1971 1985 - O lastSu 2s 1 D -R AV 1972 o - F lastSu 2s 0 S -R AV 1973 1985 - Mar Su>=1 2s 0 S -R AV 1986 1990 - Mar Su>=15 2s 0 S -R AV 1986 1987 - O Su>=15 2s 1 D -R AV 1988 1999 - O lastSu 2s 1 D -R AV 1991 1994 - Mar Su>=1 2s 0 S -R AV 1995 2005 - Mar lastSu 2s 0 S -R AV 2000 o - Au lastSu 2s 1 D -R AV 2001 2007 - O lastSu 2s 1 D -R AV 2006 o - Ap Su>=1 2s 0 S -R AV 2007 o - Mar lastSu 2s 0 S -R AV 2008 ma - Ap Su>=1 2s 0 S -R AV 2008 ma - O Su>=1 2s 1 D -Z Australia/Melbourne 9:39:52 - LMT 1895 F -10 AU AE%sT 1971 -10 AV AE%sT -R AN 1971 1985 - O lastSu 2s 1 D -R AN 1972 o - F 27 2s 0 S -R AN 1973 1981 - Mar Su>=1 2s 0 S -R AN 1982 o - Ap Su>=1 2s 0 S -R AN 1983 1985 - Mar Su>=1 2s 0 S -R AN 1986 1989 - Mar Su>=15 2s 0 S -R AN 1986 o - O 19 2s 1 D -R AN 1987 1999 - O lastSu 2s 1 D -R AN 1990 1995 - Mar Su>=1 2s 0 S -R AN 1996 2005 - Mar lastSu 2s 0 S -R AN 2000 o - Au lastSu 2s 1 D -R AN 2001 2007 - O lastSu 2s 1 D -R AN 2006 o - Ap Su>=1 2s 0 S -R AN 2007 o - Mar lastSu 2s 0 S -R AN 2008 ma - Ap Su>=1 2s 0 S -R AN 2008 ma - O Su>=1 2s 1 D -Z Australia/Sydney 10:4:52 - LMT 1895 F -10 AU AE%sT 1971 -10 AN AE%sT -Z Australia/Broken_Hill 9:25:48 - LMT 1895 F -10 - AEST 1896 Au 23 -9 - ACST 1899 May -9:30 AU AC%sT 1971 -9:30 AN AC%sT 2000 -9:30 AS AC%sT -R LH 1981 1984 - O lastSu 2 1 - -R LH 1982 1985 - Mar Su>=1 2 0 - -R LH 1985 o - O lastSu 2 0:30 - -R LH 1986 1989 - Mar Su>=15 2 0 - -R LH 1986 o - O 19 2 0:30 - -R LH 1987 1999 - O lastSu 2 0:30 - -R LH 1990 1995 - Mar Su>=1 2 0 - -R LH 1996 2005 - Mar lastSu 2 0 - -R LH 2000 o - Au lastSu 2 0:30 - -R LH 2001 2007 - O lastSu 2 0:30 - -R LH 2006 o - Ap Su>=1 2 0 - -R LH 2007 o - Mar lastSu 2 0 - -R LH 2008 ma - Ap Su>=1 2 0 - -R LH 2008 ma - O Su>=1 2 0:30 - -Z Australia/Lord_Howe 10:36:20 - LMT 1895 F -10 - AEST 1981 Mar -10:30 LH +1030/+1130 1985 Jul -10:30 LH +1030/+11 -Z Antarctica/Macquarie 0 - -00 1899 N -10 - AEST 1916 O 1 2 -10 1 AEDT 1917 F -10 AU AE%sT 1919 Ap 1 0s -0 - -00 1948 Mar 25 -10 AU AE%sT 1967 -10 AT AE%sT 2010 -10 1 AEDT 2011 -10 AT AE%sT -Z Indian/Christmas 7:2:52 - LMT 1895 F -7 - +07 -Z Indian/Cocos 6:27:40 - LMT 1900 -6:30 - +0630 -R FJ 1998 1999 - N Su>=1 2 1 - -R FJ 1999 2000 - F lastSu 3 0 - -R FJ 2009 o - N 29 2 1 - -R FJ 2010 o - Mar lastSu 3 0 - -R FJ 2010 2013 - O Su>=21 2 1 - -R FJ 2011 o - Mar Su>=1 3 0 - -R FJ 2012 2013 - Ja Su>=18 3 0 - -R FJ 2014 o - Ja Su>=18 2 0 - -R FJ 2014 2018 - N Su>=1 2 1 - -R FJ 2015 2021 - Ja Su>=12 3 0 - -R FJ 2019 o - N Su>=8 2 1 - -R FJ 2020 o - D 20 2 1 - -R FJ 2022 ma - N Su>=8 2 1 - -R FJ 2023 ma - Ja Su>=12 3 0 - -Z Pacific/Fiji 11:55:44 - LMT 1915 O 26 -12 FJ +12/+13 -Z Pacific/Gambier -8:59:48 - LMT 1912 O --9 - -09 -Z Pacific/Marquesas -9:18 - LMT 1912 O --9:30 - -0930 -Z Pacific/Tahiti -9:58:16 - LMT 1912 O --10 - -10 -R Gu 1959 o - Jun 27 2 1 D -R Gu 1961 o - Ja 29 2 0 S -R Gu 1967 o - S 1 2 1 D -R Gu 1969 o - Ja 26 0:1 0 S -R Gu 1969 o - Jun 22 2 1 D -R Gu 1969 o - Au 31 2 0 S -R Gu 1970 1971 - Ap lastSu 2 1 D -R Gu 1970 1971 - S Su>=1 2 0 S -R Gu 1973 o - D 16 2 1 D -R Gu 1974 o - F 24 2 0 S -R Gu 1976 o - May 26 2 1 D -R Gu 1976 o - Au 22 2:1 0 S -R Gu 1977 o - Ap 24 2 1 D -R Gu 1977 o - Au 28 2 0 S -Z Pacific/Guam -14:21 - LMT 1844 D 31 -9:39 - LMT 1901 -10 - GST 1941 D 10 -9 - +09 1944 Jul 31 -10 Gu G%sT 2000 D 23 -10 - ChST -L Pacific/Guam Pacific/Saipan -Z Pacific/Tarawa 11:32:4 - LMT 1901 -12 - +12 -Z Pacific/Kanton 0 - -00 1937 Au 31 --12 - -12 1979 O --11 - -11 1994 D 31 -13 - +13 -Z Pacific/Kiritimati -10:29:20 - LMT 1901 --10:40 - -1040 1979 O --10 - -10 1994 D 31 -14 - +14 -Z Pacific/Majuro 11:24:48 - LMT 1901 -11 - +11 1914 O -9 - +09 1919 F -11 - +11 1937 -10 - +10 1941 Ap -9 - +09 1944 Ja 30 -11 - +11 1969 O -12 - +12 -Z Pacific/Kwajalein 11:9:20 - LMT 1901 -11 - +11 1937 -10 - +10 1941 Ap -9 - +09 1944 F 6 -11 - +11 1969 O --12 - -12 1993 Au 20 24 -12 - +12 -Z Pacific/Chuuk -13:52:52 - LMT 1844 D 31 -10:7:8 - LMT 1901 -10 - +10 1914 O -9 - +09 1919 F -10 - +10 1941 Ap -9 - +09 1945 Au -10 - +10 -Z Pacific/Pohnpei -13:27:8 - LMT 1844 D 31 -10:32:52 - LMT 1901 -11 - +11 1914 O -9 - +09 1919 F -11 - +11 1937 -10 - +10 1941 Ap -9 - +09 1945 Au -11 - +11 -Z Pacific/Kosrae -13:8:4 - LMT 1844 D 31 -10:51:56 - LMT 1901 -11 - +11 1914 O -9 - +09 1919 F -11 - +11 1937 -10 - +10 1941 Ap -9 - +09 1945 Au -11 - +11 1969 O -12 - +12 1999 -11 - +11 -Z Pacific/Nauru 11:7:40 - LMT 1921 Ja 15 -11:30 - +1130 1942 Au 29 -9 - +09 1945 S 8 -11:30 - +1130 1979 F 10 2 -12 - +12 -R NC 1977 1978 - D Su>=1 0 1 - -R NC 1978 1979 - F 27 0 0 - -R NC 1996 o - D 1 2s 1 - -R NC 1997 o - Mar 2 2s 0 - -Z Pacific/Noumea 11:5:48 - LMT 1912 Ja 13 -11 NC +11/+12 -R NZ 1927 o - N 6 2 1 S -R NZ 1928 o - Mar 4 2 0 M -R NZ 1928 1933 - O Su>=8 2 0:30 S -R NZ 1929 1933 - Mar Su>=15 2 0 M -R NZ 1934 1940 - Ap lastSu 2 0 M -R NZ 1934 1940 - S lastSu 2 0:30 S -R NZ 1946 o - Ja 1 0 0 S -R NZ 1974 o - N Su>=1 2s 1 D -R k 1974 o - N Su>=1 2:45s 1 - -R NZ 1975 o - F lastSu 2s 0 S -R k 1975 o - F lastSu 2:45s 0 - -R NZ 1975 1988 - O lastSu 2s 1 D -R k 1975 1988 - O lastSu 2:45s 1 - -R NZ 1976 1989 - Mar Su>=1 2s 0 S -R k 1976 1989 - Mar Su>=1 2:45s 0 - -R NZ 1989 o - O Su>=8 2s 1 D -R k 1989 o - O Su>=8 2:45s 1 - -R NZ 1990 2006 - O Su>=1 2s 1 D -R k 1990 2006 - O Su>=1 2:45s 1 - -R NZ 1990 2007 - Mar Su>=15 2s 0 S -R k 1990 2007 - Mar Su>=15 2:45s 0 - -R NZ 2007 ma - S lastSu 2s 1 D -R k 2007 ma - S lastSu 2:45s 1 - -R NZ 2008 ma - Ap Su>=1 2s 0 S -R k 2008 ma - Ap Su>=1 2:45s 0 - -Z Pacific/Auckland 11:39:4 - LMT 1868 N 2 -11:30 NZ NZ%sT 1946 -12 NZ NZ%sT -Z Pacific/Chatham 12:13:48 - LMT 1868 N 2 -12:15 - +1215 1946 -12:45 k +1245/+1345 -L Pacific/Auckland Antarctica/McMurdo -R CK 1978 o - N 12 0 0:30 - -R CK 1979 1991 - Mar Su>=1 0 0 - -R CK 1979 1990 - O lastSu 0 0:30 - -Z Pacific/Rarotonga 13:20:56 - LMT 1899 D 26 --10:39:4 - LMT 1952 O 16 --10:30 - -1030 1978 N 12 --10 CK -10/-0930 -Z Pacific/Niue -11:19:40 - LMT 1952 O 16 --11:20 - -1120 1964 Jul --11 - -11 -Z Pacific/Norfolk 11:11:52 - LMT 1901 -11:12 - +1112 1951 -11:30 - +1130 1974 O 27 2s -11:30 1 +1230 1975 Mar 2 2s -11:30 - +1130 2015 O 4 2s -11 - +11 2019 Jul -11 AN +11/+12 -Z Pacific/Palau -15:2:4 - LMT 1844 D 31 -8:57:56 - LMT 1901 -9 - +09 -Z Pacific/Port_Moresby 9:48:40 - LMT 1880 -9:48:32 - PMMT 1895 -10 - +10 -L Pacific/Port_Moresby Antarctica/DumontDUrville -Z Pacific/Bougainville 10:22:16 - LMT 1880 -9:48:32 - PMMT 1895 -10 - +10 1942 Jul -9 - +09 1945 Au 21 -10 - +10 2014 D 28 2 -11 - +11 -Z Pacific/Pitcairn -8:40:20 - LMT 1901 --8:30 - -0830 1998 Ap 27 --8 - -08 -Z Pacific/Pago_Pago 12:37:12 - LMT 1892 Jul 5 --11:22:48 - LMT 1911 --11 - SST -L Pacific/Pago_Pago Pacific/Midway -R WS 2010 o - S lastSu 0 1 - -R WS 2011 o - Ap Sa>=1 4 0 - -R WS 2011 o - S lastSa 3 1 - -R WS 2012 2021 - Ap Su>=1 4 0 - -R WS 2012 2020 - S lastSu 3 1 - -Z Pacific/Apia 12:33:4 - LMT 1892 Jul 5 --11:26:56 - LMT 1911 --11:30 - -1130 1950 --11 WS -11/-10 2011 D 29 24 -13 WS +13/+14 -Z Pacific/Guadalcanal 10:39:48 - LMT 1912 O -11 - +11 -Z Pacific/Fakaofo -11:24:56 - LMT 1901 --11 - -11 2011 D 30 -13 - +13 -R TO 1999 o - O 7 2s 1 - -R TO 2000 o - Mar 19 2s 0 - -R TO 2000 2001 - N Su>=1 2 1 - -R TO 2001 2002 - Ja lastSu 2 0 - -R TO 2016 o - N Su>=1 2 1 - -R TO 2017 o - Ja Su>=15 3 0 - -Z Pacific/Tongatapu 12:19:12 - LMT 1945 S 10 -12:20 - +1220 1961 -13 - +13 1999 -13 TO +13/+14 -Z Pacific/Funafuti 11:56:52 - LMT 1901 -12 - +12 -Z Pacific/Wake 11:6:28 - LMT 1901 -12 - +12 -R VU 1973 o - D 22 12u 1 - -R VU 1974 o - Mar 30 12u 0 - -R VU 1983 1991 - S Sa>=22 24 1 - -R VU 1984 1991 - Mar Sa>=22 24 0 - -R VU 1992 1993 - Ja Sa>=22 24 0 - -R VU 1992 o - O Sa>=22 24 1 - -Z Pacific/Efate 11:13:16 - LMT 1912 Ja 13 -11 VU +11/+12 -Z Pacific/Wallis 12:15:20 - LMT 1901 -12 - +12 -R G 1916 o - May 21 2s 1 BST -R G 1916 o - O 1 2s 0 GMT -R G 1917 o - Ap 8 2s 1 BST -R G 1917 o - S 17 2s 0 GMT -R G 1918 o - Mar 24 2s 1 BST -R G 1918 o - S 30 2s 0 GMT -R G 1919 o - Mar 30 2s 1 BST -R G 1919 o - S 29 2s 0 GMT -R G 1920 o - Mar 28 2s 1 BST -R G 1920 o - O 25 2s 0 GMT -R G 1921 o - Ap 3 2s 1 BST -R G 1921 o - O 3 2s 0 GMT -R G 1922 o - Mar 26 2s 1 BST -R G 1922 o - O 8 2s 0 GMT -R G 1923 o - Ap Su>=16 2s 1 BST -R G 1923 1924 - S Su>=16 2s 0 GMT -R G 1924 o - Ap Su>=9 2s 1 BST -R G 1925 1926 - Ap Su>=16 2s 1 BST -R G 1925 1938 - O Su>=2 2s 0 GMT -R G 1927 o - Ap Su>=9 2s 1 BST -R G 1928 1929 - Ap Su>=16 2s 1 BST -R G 1930 o - Ap Su>=9 2s 1 BST -R G 1931 1932 - Ap Su>=16 2s 1 BST -R G 1933 o - Ap Su>=9 2s 1 BST -R G 1934 o - Ap Su>=16 2s 1 BST -R G 1935 o - Ap Su>=9 2s 1 BST -R G 1936 1937 - Ap Su>=16 2s 1 BST -R G 1938 o - Ap Su>=9 2s 1 BST -R G 1939 o - Ap Su>=16 2s 1 BST -R G 1939 o - N Su>=16 2s 0 GMT -R G 1940 o - F Su>=23 2s 1 BST -R G 1941 o - May Su>=2 1s 2 BDST -R G 1941 1943 - Au Su>=9 1s 1 BST -R G 1942 1944 - Ap Su>=2 1s 2 BDST -R G 1944 o - S Su>=16 1s 1 BST -R G 1945 o - Ap M>=2 1s 2 BDST -R G 1945 o - Jul Su>=9 1s 1 BST -R G 1945 1946 - O Su>=2 2s 0 GMT -R G 1946 o - Ap Su>=9 2s 1 BST -R G 1947 o - Mar 16 2s 1 BST -R G 1947 o - Ap 13 1s 2 BDST -R G 1947 o - Au 10 1s 1 BST -R G 1947 o - N 2 2s 0 GMT -R G 1948 o - Mar 14 2s 1 BST -R G 1948 o - O 31 2s 0 GMT -R G 1949 o - Ap 3 2s 1 BST -R G 1949 o - O 30 2s 0 GMT -R G 1950 1952 - Ap Su>=14 2s 1 BST -R G 1950 1952 - O Su>=21 2s 0 GMT -R G 1953 o - Ap Su>=16 2s 1 BST -R G 1953 1960 - O Su>=2 2s 0 GMT -R G 1954 o - Ap Su>=9 2s 1 BST -R G 1955 1956 - Ap Su>=16 2s 1 BST -R G 1957 o - Ap Su>=9 2s 1 BST -R G 1958 1959 - Ap Su>=16 2s 1 BST -R G 1960 o - Ap Su>=9 2s 1 BST -R G 1961 1963 - Mar lastSu 2s 1 BST -R G 1961 1968 - O Su>=23 2s 0 GMT -R G 1964 1967 - Mar Su>=19 2s 1 BST -R G 1968 o - F 18 2s 1 BST -R G 1972 1980 - Mar Su>=16 2s 1 BST -R G 1972 1980 - O Su>=23 2s 0 GMT -R G 1981 1995 - Mar lastSu 1u 1 BST -R G 1981 1989 - O Su>=23 1u 0 GMT -R G 1990 1995 - O Su>=22 1u 0 GMT -Z Europe/London -0:1:15 - LMT 1847 D 1 0s -0 G %s 1968 O 27 -1 - BST 1971 O 31 2u -0 G %s 1996 -0 E GMT/BST -L Europe/London Europe/Jersey -L Europe/London Europe/Guernsey -L Europe/London Europe/Isle_of_Man -R IE 1971 o - O 31 2u -1 - -R IE 1972 1980 - Mar Su>=16 2u 0 - -R IE 1972 1980 - O Su>=23 2u -1 - -R IE 1981 ma - Mar lastSu 1u 0 - -R IE 1981 1989 - O Su>=23 1u -1 - -R IE 1990 1995 - O Su>=22 1u -1 - -R IE 1996 ma - O lastSu 1u -1 - -Z Europe/Dublin -0:25 - LMT 1880 Au 2 --0:25:21 - DMT 1916 May 21 2s --0:25:21 1 IST 1916 O 1 2s -0 G %s 1921 D 6 -0 G GMT/IST 1940 F 25 2s -0 1 IST 1946 O 6 2s -0 - GMT 1947 Mar 16 2s -0 1 IST 1947 N 2 2s -0 - GMT 1948 Ap 18 2s -0 G GMT/IST 1968 O 27 -1 IE IST/GMT -R E 1977 1980 - Ap Su>=1 1u 1 S -R E 1977 o - S lastSu 1u 0 - -R E 1978 o - O 1 1u 0 - -R E 1979 1995 - S lastSu 1u 0 - -R E 1981 ma - Mar lastSu 1u 1 S -R E 1996 ma - O lastSu 1u 0 - -R W- 1977 1980 - Ap Su>=1 1s 1 S -R W- 1977 o - S lastSu 1s 0 - -R W- 1978 o - O 1 1s 0 - -R W- 1979 1995 - S lastSu 1s 0 - -R W- 1981 ma - Mar lastSu 1s 1 S -R W- 1996 ma - O lastSu 1s 0 - -R c 1916 o - Ap 30 23 1 S -R c 1916 o - O 1 1 0 - -R c 1917 1918 - Ap M>=15 2s 1 S -R c 1917 1918 - S M>=15 2s 0 - -R c 1940 o - Ap 1 2s 1 S -R c 1942 o - N 2 2s 0 - -R c 1943 o - Mar 29 2s 1 S -R c 1943 o - O 4 2s 0 - -R c 1944 1945 - Ap M>=1 2s 1 S -R c 1944 o - O 2 2s 0 - -R c 1945 o - S 16 2s 0 - -R c 1977 1980 - Ap Su>=1 2s 1 S -R c 1977 o - S lastSu 2s 0 - -R c 1978 o - O 1 2s 0 - -R c 1979 1995 - S lastSu 2s 0 - -R c 1981 ma - Mar lastSu 2s 1 S -R c 1996 ma - O lastSu 2s 0 - -R e 1977 1980 - Ap Su>=1 0 1 S -R e 1977 o - S lastSu 0 0 - -R e 1978 o - O 1 0 0 - -R e 1979 1995 - S lastSu 0 0 - -R e 1981 ma - Mar lastSu 0 1 S -R e 1996 ma - O lastSu 0 0 - -R R 1917 o - Jul 1 23 1 MST -R R 1917 o - D 28 0 0 MMT -R R 1918 o - May 31 22 2 MDST -R R 1918 o - S 16 1 1 MST -R R 1919 o - May 31 23 2 MDST -R R 1919 o - Jul 1 0u 1 MSD -R R 1919 o - Au 16 0 0 MSK -R R 1921 o - F 14 23 1 MSD -R R 1921 o - Mar 20 23 2 +05 -R R 1921 o - S 1 0 1 MSD -R R 1921 o - O 1 0 0 - -R R 1981 1984 - Ap 1 0 1 S -R R 1981 1983 - O 1 0 0 - -R R 1984 1995 - S lastSu 2s 0 - -R R 1985 2010 - Mar lastSu 2s 1 S -R R 1996 2010 - O lastSu 2s 0 - -Z WET 0 E WE%sT -Z CET 1 c CE%sT -Z MET 1 c ME%sT -Z EET 2 E EE%sT -R q 1940 o - Jun 16 0 1 S -R q 1942 o - N 2 3 0 - -R q 1943 o - Mar 29 2 1 S -R q 1943 o - Ap 10 3 0 - -R q 1974 o - May 4 0 1 S -R q 1974 o - O 2 0 0 - -R q 1975 o - May 1 0 1 S -R q 1975 o - O 2 0 0 - -R q 1976 o - May 2 0 1 S -R q 1976 o - O 3 0 0 - -R q 1977 o - May 8 0 1 S -R q 1977 o - O 2 0 0 - -R q 1978 o - May 6 0 1 S -R q 1978 o - O 1 0 0 - -R q 1979 o - May 5 0 1 S -R q 1979 o - S 30 0 0 - -R q 1980 o - May 3 0 1 S -R q 1980 o - O 4 0 0 - -R q 1981 o - Ap 26 0 1 S -R q 1981 o - S 27 0 0 - -R q 1982 o - May 2 0 1 S -R q 1982 o - O 3 0 0 - -R q 1983 o - Ap 18 0 1 S -R q 1983 o - O 1 0 0 - -R q 1984 o - Ap 1 0 1 S -Z Europe/Tirane 1:19:20 - LMT 1914 -1 - CET 1940 Jun 16 -1 q CE%sT 1984 Jul -1 E CE%sT -Z Europe/Andorra 0:6:4 - LMT 1901 -0 - WET 1946 S 30 -1 - CET 1985 Mar 31 2 -1 E CE%sT -R a 1920 o - Ap 5 2s 1 S -R a 1920 o - S 13 2s 0 - -R a 1946 o - Ap 14 2s 1 S -R a 1946 o - O 7 2s 0 - -R a 1947 1948 - O Su>=1 2s 0 - -R a 1947 o - Ap 6 2s 1 S -R a 1948 o - Ap 18 2s 1 S -R a 1980 o - Ap 6 0 1 S -R a 1980 o - S 28 0 0 - -Z Europe/Vienna 1:5:21 - LMT 1893 Ap -1 c CE%sT 1920 -1 a CE%sT 1940 Ap 1 2s -1 c CE%sT 1945 Ap 2 2s -1 1 CEST 1945 Ap 12 2s -1 - CET 1946 -1 a CE%sT 1981 -1 E CE%sT -Z Europe/Minsk 1:50:16 - LMT 1880 -1:50 - MMT 1924 May 2 -2 - EET 1930 Jun 21 -3 - MSK 1941 Jun 28 -1 c CE%sT 1944 Jul 3 -3 R MSK/MSD 1990 -3 - MSK 1991 Mar 31 2s -2 R EE%sT 2011 Mar 27 2s -3 - +03 -R b 1918 o - Mar 9 0s 1 S -R b 1918 1919 - O Sa>=1 23s 0 - -R b 1919 o - Mar 1 23s 1 S -R b 1920 o - F 14 23s 1 S -R b 1920 o - O 23 23s 0 - -R b 1921 o - Mar 14 23s 1 S -R b 1921 o - O 25 23s 0 - -R b 1922 o - Mar 25 23s 1 S -R b 1922 1927 - O Sa>=1 23s 0 - -R b 1923 o - Ap 21 23s 1 S -R b 1924 o - Mar 29 23s 1 S -R b 1925 o - Ap 4 23s 1 S -R b 1926 o - Ap 17 23s 1 S -R b 1927 o - Ap 9 23s 1 S -R b 1928 o - Ap 14 23s 1 S -R b 1928 1938 - O Su>=2 2s 0 - -R b 1929 o - Ap 21 2s 1 S -R b 1930 o - Ap 13 2s 1 S -R b 1931 o - Ap 19 2s 1 S -R b 1932 o - Ap 3 2s 1 S -R b 1933 o - Mar 26 2s 1 S -R b 1934 o - Ap 8 2s 1 S -R b 1935 o - Mar 31 2s 1 S -R b 1936 o - Ap 19 2s 1 S -R b 1937 o - Ap 4 2s 1 S -R b 1938 o - Mar 27 2s 1 S -R b 1939 o - Ap 16 2s 1 S -R b 1939 o - N 19 2s 0 - -R b 1940 o - F 25 2s 1 S -R b 1944 o - S 17 2s 0 - -R b 1945 o - Ap 2 2s 1 S -R b 1945 o - S 16 2s 0 - -R b 1946 o - May 19 2s 1 S -R b 1946 o - O 7 2s 0 - -Z Europe/Brussels 0:17:30 - LMT 1880 -0:17:30 - BMT 1892 May 1 0:17:30 -0 - WET 1914 N 8 -1 - CET 1916 May -1 c CE%sT 1918 N 11 11u -0 b WE%sT 1940 May 20 2s -1 c CE%sT 1944 S 3 -1 b CE%sT 1977 -1 E CE%sT -R BG 1979 o - Mar 31 23 1 S -R BG 1979 o - O 1 1 0 - -R BG 1980 1982 - Ap Sa>=1 23 1 S -R BG 1980 o - S 29 1 0 - -R BG 1981 o - S 27 2 0 - -Z Europe/Sofia 1:33:16 - LMT 1880 -1:56:56 - IMT 1894 N 30 -2 - EET 1942 N 2 3 -1 c CE%sT 1945 -1 - CET 1945 Ap 2 3 -2 - EET 1979 Mar 31 23 -2 BG EE%sT 1982 S 26 3 -2 c EE%sT 1991 -2 e EE%sT 1997 -2 E EE%sT -R CZ 1945 o - Ap M>=1 2s 1 S -R CZ 1945 o - O 1 2s 0 - -R CZ 1946 o - May 6 2s 1 S -R CZ 1946 1949 - O Su>=1 2s 0 - -R CZ 1947 1948 - Ap Su>=15 2s 1 S -R CZ 1949 o - Ap 9 2s 1 S -Z Europe/Prague 0:57:44 - LMT 1850 -0:57:44 - PMT 1891 O -1 c CE%sT 1945 May 9 -1 CZ CE%sT 1946 D 1 3 -1 -1 GMT 1947 F 23 2 -1 CZ CE%sT 1979 -1 E CE%sT -R D 1916 o - May 14 23 1 S -R D 1916 o - S 30 23 0 - -R D 1940 o - May 15 0 1 S -R D 1945 o - Ap 2 2s 1 S -R D 1945 o - Au 15 2s 0 - -R D 1946 o - May 1 2s 1 S -R D 1946 o - S 1 2s 0 - -R D 1947 o - May 4 2s 1 S -R D 1947 o - Au 10 2s 0 - -R D 1948 o - May 9 2s 1 S -R D 1948 o - Au 8 2s 0 - -Z Europe/Copenhagen 0:50:20 - LMT 1890 -0:50:20 - CMT 1894 -1 D CE%sT 1942 N 2 2s -1 c CE%sT 1945 Ap 2 2 -1 D CE%sT 1980 -1 E CE%sT -Z Atlantic/Faroe -0:27:4 - LMT 1908 Ja 11 -0 - WET 1981 -0 E WE%sT -R Th 1991 1992 - Mar lastSu 2 1 D -R Th 1991 1992 - S lastSu 2 0 S -R Th 1993 2006 - Ap Su>=1 2 1 D -R Th 1993 2006 - O lastSu 2 0 S -R Th 2007 ma - Mar Su>=8 2 1 D -R Th 2007 ma - N Su>=1 2 0 S -Z America/Danmarkshavn -1:14:40 - LMT 1916 Jul 28 --3 - -03 1980 Ap 6 2 --3 E -03/-02 1996 -0 - GMT -Z America/Scoresbysund -1:27:52 - LMT 1916 Jul 28 --2 - -02 1980 Ap 6 2 --2 c -02/-01 1981 Mar 29 --1 E -01/+00 -Z America/Nuuk -3:26:56 - LMT 1916 Jul 28 --3 - -03 1980 Ap 6 2 --3 E -03/-02 -Z America/Thule -4:35:8 - LMT 1916 Jul 28 --4 Th A%sT -Z Europe/Tallinn 1:39 - LMT 1880 -1:39 - TMT 1918 F -1 c CE%sT 1919 Jul -1:39 - TMT 1921 May -2 - EET 1940 Au 6 -3 - MSK 1941 S 15 -1 c CE%sT 1944 S 22 -3 R MSK/MSD 1989 Mar 26 2s -2 1 EEST 1989 S 24 2s -2 c EE%sT 1998 S 22 -2 E EE%sT 1999 O 31 4 -2 - EET 2002 F 21 -2 E EE%sT -R FI 1942 o - Ap 2 24 1 S -R FI 1942 o - O 4 1 0 - -R FI 1981 1982 - Mar lastSu 2 1 S -R FI 1981 1982 - S lastSu 3 0 - -Z Europe/Helsinki 1:39:49 - LMT 1878 May 31 -1:39:49 - HMT 1921 May -2 FI EE%sT 1983 -2 E EE%sT -L Europe/Helsinki Europe/Mariehamn -R F 1916 o - Jun 14 23s 1 S -R F 1916 1919 - O Su>=1 23s 0 - -R F 1917 o - Mar 24 23s 1 S -R F 1918 o - Mar 9 23s 1 S -R F 1919 o - Mar 1 23s 1 S -R F 1920 o - F 14 23s 1 S -R F 1920 o - O 23 23s 0 - -R F 1921 o - Mar 14 23s 1 S -R F 1921 o - O 25 23s 0 - -R F 1922 o - Mar 25 23s 1 S -R F 1922 1938 - O Sa>=1 23s 0 - -R F 1923 o - May 26 23s 1 S -R F 1924 o - Mar 29 23s 1 S -R F 1925 o - Ap 4 23s 1 S -R F 1926 o - Ap 17 23s 1 S -R F 1927 o - Ap 9 23s 1 S -R F 1928 o - Ap 14 23s 1 S -R F 1929 o - Ap 20 23s 1 S -R F 1930 o - Ap 12 23s 1 S -R F 1931 o - Ap 18 23s 1 S -R F 1932 o - Ap 2 23s 1 S -R F 1933 o - Mar 25 23s 1 S -R F 1934 o - Ap 7 23s 1 S -R F 1935 o - Mar 30 23s 1 S -R F 1936 o - Ap 18 23s 1 S -R F 1937 o - Ap 3 23s 1 S -R F 1938 o - Mar 26 23s 1 S -R F 1939 o - Ap 15 23s 1 S -R F 1939 o - N 18 23s 0 - -R F 1940 o - F 25 2 1 S -R F 1941 o - May 5 0 2 M -R F 1941 o - O 6 0 1 S -R F 1942 o - Mar 9 0 2 M -R F 1942 o - N 2 3 1 S -R F 1943 o - Mar 29 2 2 M -R F 1943 o - O 4 3 1 S -R F 1944 o - Ap 3 2 2 M -R F 1944 o - O 8 1 1 S -R F 1945 o - Ap 2 2 2 M -R F 1945 o - S 16 3 0 - -R F 1976 o - Mar 28 1 1 S -R F 1976 o - S 26 1 0 - -Z Europe/Paris 0:9:21 - LMT 1891 Mar 16 -0:9:21 - PMT 1911 Mar 11 -0 F WE%sT 1940 Jun 14 23 -1 c CE%sT 1944 Au 25 -0 F WE%sT 1945 S 16 3 -1 F CE%sT 1977 -1 E CE%sT -R DE 1946 o - Ap 14 2s 1 S -R DE 1946 o - O 7 2s 0 - -R DE 1947 1949 - O Su>=1 2s 0 - -R DE 1947 o - Ap 6 3s 1 S -R DE 1947 o - May 11 2s 2 M -R DE 1947 o - Jun 29 3 1 S -R DE 1948 o - Ap 18 2s 1 S -R DE 1949 o - Ap 10 2s 1 S -R So 1945 o - May 24 2 2 M -R So 1945 o - S 24 3 1 S -R So 1945 o - N 18 2s 0 - -Z Europe/Berlin 0:53:28 - LMT 1893 Ap -1 c CE%sT 1945 May 24 2 -1 So CE%sT 1946 -1 DE CE%sT 1980 -1 E CE%sT -L Europe/Zurich Europe/Busingen -Z Europe/Gibraltar -0:21:24 - LMT 1880 Au 2 0s -0 G %s 1957 Ap 14 2 -1 - CET 1982 -1 E CE%sT -R g 1932 o - Jul 7 0 1 S -R g 1932 o - S 1 0 0 - -R g 1941 o - Ap 7 0 1 S -R g 1942 o - N 2 3 0 - -R g 1943 o - Mar 30 0 1 S -R g 1943 o - O 4 0 0 - -R g 1952 o - Jul 1 0 1 S -R g 1952 o - N 2 0 0 - -R g 1975 o - Ap 12 0s 1 S -R g 1975 o - N 26 0s 0 - -R g 1976 o - Ap 11 2s 1 S -R g 1976 o - O 10 2s 0 - -R g 1977 1978 - Ap Su>=1 2s 1 S -R g 1977 o - S 26 2s 0 - -R g 1978 o - S 24 4 0 - -R g 1979 o - Ap 1 9 1 S -R g 1979 o - S 29 2 0 - -R g 1980 o - Ap 1 0 1 S -R g 1980 o - S 28 0 0 - -Z Europe/Athens 1:34:52 - LMT 1895 S 14 -1:34:52 - AMT 1916 Jul 28 0:1 -2 g EE%sT 1941 Ap 30 -1 g CE%sT 1944 Ap 4 -2 g EE%sT 1981 -2 E EE%sT -R h 1918 1919 - Ap 15 2 1 S -R h 1918 1920 - S M>=15 3 0 - -R h 1920 o - Ap 5 2 1 S -R h 1945 o - May 1 23 1 S -R h 1945 o - N 1 1 0 - -R h 1946 o - Mar 31 2s 1 S -R h 1946 o - O 7 2 0 - -R h 1947 1949 - Ap Su>=4 2s 1 S -R h 1947 1949 - O Su>=1 2s 0 - -R h 1954 o - May 23 0 1 S -R h 1954 o - O 3 0 0 - -R h 1955 o - May 22 2 1 S -R h 1955 o - O 2 3 0 - -R h 1956 1957 - Jun Su>=1 2 1 S -R h 1956 1957 - S lastSu 3 0 - -R h 1980 o - Ap 6 0 1 S -R h 1980 o - S 28 1 0 - -R h 1981 1983 - Mar lastSu 0 1 S -R h 1981 1983 - S lastSu 1 0 - -Z Europe/Budapest 1:16:20 - LMT 1890 N -1 c CE%sT 1918 -1 h CE%sT 1941 Ap 7 23 -1 c CE%sT 1945 -1 h CE%sT 1984 -1 E CE%sT -R w 1917 1919 - F 19 23 1 - -R w 1917 o - O 21 1 0 - -R w 1918 1919 - N 16 1 0 - -R w 1921 o - Mar 19 23 1 - -R w 1921 o - Jun 23 1 0 - -R w 1939 o - Ap 29 23 1 - -R w 1939 o - O 29 2 0 - -R w 1940 o - F 25 2 1 - -R w 1940 1941 - N Su>=2 1s 0 - -R w 1941 1942 - Mar Su>=2 1s 1 - -R w 1943 1946 - Mar Su>=1 1s 1 - -R w 1942 1948 - O Su>=22 1s 0 - -R w 1947 1967 - Ap Su>=1 1s 1 - -R w 1949 o - O 30 1s 0 - -R w 1950 1966 - O Su>=22 1s 0 - -R w 1967 o - O 29 1s 0 - -Z Atlantic/Reykjavik -1:28 - LMT 1908 --1 w -01/+00 1968 Ap 7 1s -0 - GMT -R I 1916 o - Jun 3 24 1 S -R I 1916 1917 - S 30 24 0 - -R I 1917 o - Mar 31 24 1 S -R I 1918 o - Mar 9 24 1 S -R I 1918 o - O 6 24 0 - -R I 1919 o - Mar 1 24 1 S -R I 1919 o - O 4 24 0 - -R I 1920 o - Mar 20 24 1 S -R I 1920 o - S 18 24 0 - -R I 1940 o - Jun 14 24 1 S -R I 1942 o - N 2 2s 0 - -R I 1943 o - Mar 29 2s 1 S -R I 1943 o - O 4 2s 0 - -R I 1944 o - Ap 2 2s 1 S -R I 1944 o - S 17 2s 0 - -R I 1945 o - Ap 2 2 1 S -R I 1945 o - S 15 1 0 - -R I 1946 o - Mar 17 2s 1 S -R I 1946 o - O 6 2s 0 - -R I 1947 o - Mar 16 0s 1 S -R I 1947 o - O 5 0s 0 - -R I 1948 o - F 29 2s 1 S -R I 1948 o - O 3 2s 0 - -R I 1966 1968 - May Su>=22 0s 1 S -R I 1966 o - S 24 24 0 - -R I 1967 1969 - S Su>=22 0s 0 - -R I 1969 o - Jun 1 0s 1 S -R I 1970 o - May 31 0s 1 S -R I 1970 o - S lastSu 0s 0 - -R I 1971 1972 - May Su>=22 0s 1 S -R I 1971 o - S lastSu 0s 0 - -R I 1972 o - O 1 0s 0 - -R I 1973 o - Jun 3 0s 1 S -R I 1973 1974 - S lastSu 0s 0 - -R I 1974 o - May 26 0s 1 S -R I 1975 o - Jun 1 0s 1 S -R I 1975 1977 - S lastSu 0s 0 - -R I 1976 o - May 30 0s 1 S -R I 1977 1979 - May Su>=22 0s 1 S -R I 1978 o - O 1 0s 0 - -R I 1979 o - S 30 0s 0 - -Z Europe/Rome 0:49:56 - LMT 1866 D 12 -0:49:56 - RMT 1893 O 31 23:49:56 -1 I CE%sT 1943 S 10 -1 c CE%sT 1944 Jun 4 -1 I CE%sT 1980 -1 E CE%sT -L Europe/Rome Europe/Vatican -L Europe/Rome Europe/San_Marino -R LV 1989 1996 - Mar lastSu 2s 1 S -R LV 1989 1996 - S lastSu 2s 0 - -Z Europe/Riga 1:36:34 - LMT 1880 -1:36:34 - RMT 1918 Ap 15 2 -1:36:34 1 LST 1918 S 16 3 -1:36:34 - RMT 1919 Ap 1 2 -1:36:34 1 LST 1919 May 22 3 -1:36:34 - RMT 1926 May 11 -2 - EET 1940 Au 5 -3 - MSK 1941 Jul -1 c CE%sT 1944 O 13 -3 R MSK/MSD 1989 Mar lastSu 2s -2 1 EEST 1989 S lastSu 2s -2 LV EE%sT 1997 Ja 21 -2 E EE%sT 2000 F 29 -2 - EET 2001 Ja 2 -2 E EE%sT -L Europe/Zurich Europe/Vaduz -Z Europe/Vilnius 1:41:16 - LMT 1880 -1:24 - WMT 1917 -1:35:36 - KMT 1919 O 10 -1 - CET 1920 Jul 12 -2 - EET 1920 O 9 -1 - CET 1940 Au 3 -3 - MSK 1941 Jun 24 -1 c CE%sT 1944 Au -3 R MSK/MSD 1989 Mar 26 2s -2 R EE%sT 1991 S 29 2s -2 c EE%sT 1998 -2 - EET 1998 Mar 29 1u -1 E CE%sT 1999 O 31 1u -2 - EET 2003 -2 E EE%sT -R LX 1916 o - May 14 23 1 S -R LX 1916 o - O 1 1 0 - -R LX 1917 o - Ap 28 23 1 S -R LX 1917 o - S 17 1 0 - -R LX 1918 o - Ap M>=15 2s 1 S -R LX 1918 o - S M>=15 2s 0 - -R LX 1919 o - Mar 1 23 1 S -R LX 1919 o - O 5 3 0 - -R LX 1920 o - F 14 23 1 S -R LX 1920 o - O 24 2 0 - -R LX 1921 o - Mar 14 23 1 S -R LX 1921 o - O 26 2 0 - -R LX 1922 o - Mar 25 23 1 S -R LX 1922 o - O Su>=2 1 0 - -R LX 1923 o - Ap 21 23 1 S -R LX 1923 o - O Su>=2 2 0 - -R LX 1924 o - Mar 29 23 1 S -R LX 1924 1928 - O Su>=2 1 0 - -R LX 1925 o - Ap 5 23 1 S -R LX 1926 o - Ap 17 23 1 S -R LX 1927 o - Ap 9 23 1 S -R LX 1928 o - Ap 14 23 1 S -R LX 1929 o - Ap 20 23 1 S -Z Europe/Luxembourg 0:24:36 - LMT 1904 Jun -1 LX CE%sT 1918 N 25 -0 LX WE%sT 1929 O 6 2s -0 b WE%sT 1940 May 14 3 -1 c WE%sT 1944 S 18 3 -1 b CE%sT 1977 -1 E CE%sT -R MT 1973 o - Mar 31 0s 1 S -R MT 1973 o - S 29 0s 0 - -R MT 1974 o - Ap 21 0s 1 S -R MT 1974 o - S 16 0s 0 - -R MT 1975 1979 - Ap Su>=15 2 1 S -R MT 1975 1980 - S Su>=15 2 0 - -R MT 1980 o - Mar 31 2 1 S -Z Europe/Malta 0:58:4 - LMT 1893 N 2 0s -1 I CE%sT 1973 Mar 31 -1 MT CE%sT 1981 -1 E CE%sT -R MD 1997 ma - Mar lastSu 2 1 S -R MD 1997 ma - O lastSu 3 0 - -Z Europe/Chisinau 1:55:20 - LMT 1880 -1:55 - CMT 1918 F 15 -1:44:24 - BMT 1931 Jul 24 -2 z EE%sT 1940 Au 15 -2 1 EEST 1941 Jul 17 -1 c CE%sT 1944 Au 24 -3 R MSK/MSD 1990 May 6 2 -2 R EE%sT 1992 -2 e EE%sT 1997 -2 MD EE%sT -Z Europe/Monaco 0:29:32 - LMT 1892 Jun -0:9:21 - PMT 1911 Mar 29 -0 F WE%sT 1945 S 16 3 -1 F CE%sT 1977 -1 E CE%sT -R N 1916 o - May 1 0 1 NST -R N 1916 o - O 1 0 0 AMT -R N 1917 o - Ap 16 2s 1 NST -R N 1917 o - S 17 2s 0 AMT -R N 1918 1921 - Ap M>=1 2s 1 NST -R N 1918 1921 - S lastM 2s 0 AMT -R N 1922 o - Mar lastSu 2s 1 NST -R N 1922 1936 - O Su>=2 2s 0 AMT -R N 1923 o - Jun F>=1 2s 1 NST -R N 1924 o - Mar lastSu 2s 1 NST -R N 1925 o - Jun F>=1 2s 1 NST -R N 1926 1931 - May 15 2s 1 NST -R N 1932 o - May 22 2s 1 NST -R N 1933 1936 - May 15 2s 1 NST -R N 1937 o - May 22 2s 1 NST -R N 1937 o - Jul 1 0 1 S -R N 1937 1939 - O Su>=2 2s 0 - -R N 1938 1939 - May 15 2s 1 S -R N 1945 o - Ap 2 2s 1 S -R N 1945 o - S 16 2s 0 - -Z Europe/Amsterdam 0:19:32 - LMT 1835 -0:19:32 N %s 1937 Jul -0:20 N +0020/+0120 1940 May 16 -1 c CE%sT 1945 Ap 2 2 -1 N CE%sT 1977 -1 E CE%sT -R NO 1916 o - May 22 1 1 S -R NO 1916 o - S 30 0 0 - -R NO 1945 o - Ap 2 2s 1 S -R NO 1945 o - O 1 2s 0 - -R NO 1959 1964 - Mar Su>=15 2s 1 S -R NO 1959 1965 - S Su>=15 2s 0 - -R NO 1965 o - Ap 25 2s 1 S -Z Europe/Oslo 0:43 - LMT 1895 -1 NO CE%sT 1940 Au 10 23 -1 c CE%sT 1945 Ap 2 2 -1 NO CE%sT 1980 -1 E CE%sT -L Europe/Oslo Arctic/Longyearbyen -R O 1918 1919 - S 16 2s 0 - -R O 1919 o - Ap 15 2s 1 S -R O 1944 o - Ap 3 2s 1 S -R O 1944 o - O 4 2 0 - -R O 1945 o - Ap 29 0 1 S -R O 1945 o - N 1 0 0 - -R O 1946 o - Ap 14 0s 1 S -R O 1946 o - O 7 2s 0 - -R O 1947 o - May 4 2s 1 S -R O 1947 1949 - O Su>=1 2s 0 - -R O 1948 o - Ap 18 2s 1 S -R O 1949 o - Ap 10 2s 1 S -R O 1957 o - Jun 2 1s 1 S -R O 1957 1958 - S lastSu 1s 0 - -R O 1958 o - Mar 30 1s 1 S -R O 1959 o - May 31 1s 1 S -R O 1959 1961 - O Su>=1 1s 0 - -R O 1960 o - Ap 3 1s 1 S -R O 1961 1964 - May lastSu 1s 1 S -R O 1962 1964 - S lastSu 1s 0 - -Z Europe/Warsaw 1:24 - LMT 1880 -1:24 - WMT 1915 Au 5 -1 c CE%sT 1918 S 16 3 -2 O EE%sT 1922 Jun -1 O CE%sT 1940 Jun 23 2 -1 c CE%sT 1944 O -1 O CE%sT 1977 -1 W- CE%sT 1988 -1 E CE%sT -R p 1916 o - Jun 17 23 1 S -R p 1916 o - N 1 1 0 - -R p 1917 o - F 28 23s 1 S -R p 1917 1921 - O 14 23s 0 - -R p 1918 o - Mar 1 23s 1 S -R p 1919 o - F 28 23s 1 S -R p 1920 o - F 29 23s 1 S -R p 1921 o - F 28 23s 1 S -R p 1924 o - Ap 16 23s 1 S -R p 1924 o - O 14 23s 0 - -R p 1926 o - Ap 17 23s 1 S -R p 1926 1929 - O Sa>=1 23s 0 - -R p 1927 o - Ap 9 23s 1 S -R p 1928 o - Ap 14 23s 1 S -R p 1929 o - Ap 20 23s 1 S -R p 1931 o - Ap 18 23s 1 S -R p 1931 1932 - O Sa>=1 23s 0 - -R p 1932 o - Ap 2 23s 1 S -R p 1934 o - Ap 7 23s 1 S -R p 1934 1938 - O Sa>=1 23s 0 - -R p 1935 o - Mar 30 23s 1 S -R p 1936 o - Ap 18 23s 1 S -R p 1937 o - Ap 3 23s 1 S -R p 1938 o - Mar 26 23s 1 S -R p 1939 o - Ap 15 23s 1 S -R p 1939 o - N 18 23s 0 - -R p 1940 o - F 24 23s 1 S -R p 1940 1941 - O 5 23s 0 - -R p 1941 o - Ap 5 23s 1 S -R p 1942 1945 - Mar Sa>=8 23s 1 S -R p 1942 o - Ap 25 22s 2 M -R p 1942 o - Au 15 22s 1 S -R p 1942 1945 - O Sa>=24 23s 0 - -R p 1943 o - Ap 17 22s 2 M -R p 1943 1945 - Au Sa>=25 22s 1 S -R p 1944 1945 - Ap Sa>=21 22s 2 M -R p 1946 o - Ap Sa>=1 23s 1 S -R p 1946 o - O Sa>=1 23s 0 - -R p 1947 1965 - Ap Su>=1 2s 1 S -R p 1947 1965 - O Su>=1 2s 0 - -R p 1977 o - Mar 27 0s 1 S -R p 1977 o - S 25 0s 0 - -R p 1978 1979 - Ap Su>=1 0s 1 S -R p 1978 o - O 1 0s 0 - -R p 1979 1982 - S lastSu 1s 0 - -R p 1980 o - Mar lastSu 0s 1 S -R p 1981 1982 - Mar lastSu 1s 1 S -R p 1983 o - Mar lastSu 2s 1 S -Z Europe/Lisbon -0:36:45 - LMT 1884 --0:36:45 - LMT 1912 Ja 1 0u -0 p WE%sT 1966 Ap 3 2 -1 - CET 1976 S 26 1 -0 p WE%sT 1983 S 25 1s -0 W- WE%sT 1992 S 27 1s -1 E CE%sT 1996 Mar 31 1u -0 E WE%sT -Z Atlantic/Azores -1:42:40 - LMT 1884 --1:54:32 - HMT 1912 Ja 1 2u --2 p -02/-01 1942 Ap 25 22s --2 p +00 1942 Au 15 22s --2 p -02/-01 1943 Ap 17 22s --2 p +00 1943 Au 28 22s --2 p -02/-01 1944 Ap 22 22s --2 p +00 1944 Au 26 22s --2 p -02/-01 1945 Ap 21 22s --2 p +00 1945 Au 25 22s --2 p -02/-01 1966 Ap 3 2 --1 p -01/+00 1983 S 25 1s --1 W- -01/+00 1992 S 27 1s -0 E WE%sT 1993 Mar 28 1u --1 E -01/+00 -Z Atlantic/Madeira -1:7:36 - LMT 1884 --1:7:36 - FMT 1912 Ja 1 1u --1 p -01/+00 1942 Ap 25 22s --1 p +01 1942 Au 15 22s --1 p -01/+00 1943 Ap 17 22s --1 p +01 1943 Au 28 22s --1 p -01/+00 1944 Ap 22 22s --1 p +01 1944 Au 26 22s --1 p -01/+00 1945 Ap 21 22s --1 p +01 1945 Au 25 22s --1 p -01/+00 1966 Ap 3 2 -0 p WE%sT 1983 S 25 1s -0 E WE%sT -R z 1932 o - May 21 0s 1 S -R z 1932 1939 - O Su>=1 0s 0 - -R z 1933 1939 - Ap Su>=2 0s 1 S -R z 1979 o - May 27 0 1 S -R z 1979 o - S lastSu 0 0 - -R z 1980 o - Ap 5 23 1 S -R z 1980 o - S lastSu 1 0 - -R z 1991 1993 - Mar lastSu 0s 1 S -R z 1991 1993 - S lastSu 0s 0 - -Z Europe/Bucharest 1:44:24 - LMT 1891 O -1:44:24 - BMT 1931 Jul 24 -2 z EE%sT 1981 Mar 29 2s -2 c EE%sT 1991 -2 z EE%sT 1994 -2 e EE%sT 1997 -2 E EE%sT -Z Europe/Kaliningrad 1:22 - LMT 1893 Ap -1 c CE%sT 1945 Ap 10 -2 O EE%sT 1946 Ap 7 -3 R MSK/MSD 1989 Mar 26 2s -2 R EE%sT 2011 Mar 27 2s -3 - +03 2014 O 26 2s -2 - EET -Z Europe/Moscow 2:30:17 - LMT 1880 -2:30:17 - MMT 1916 Jul 3 -2:31:19 R %s 1919 Jul 1 0u -3 R %s 1921 O -3 R MSK/MSD 1922 O -2 - EET 1930 Jun 21 -3 R MSK/MSD 1991 Mar 31 2s -2 R EE%sT 1992 Ja 19 2s -3 R MSK/MSD 2011 Mar 27 2s -4 - MSK 2014 O 26 2s -3 - MSK -Z Europe/Simferopol 2:16:24 - LMT 1880 -2:16 - SMT 1924 May 2 -2 - EET 1930 Jun 21 -3 - MSK 1941 N -1 c CE%sT 1944 Ap 13 -3 R MSK/MSD 1990 -3 - MSK 1990 Jul 1 2 -2 - EET 1992 Mar 20 -2 c EE%sT 1994 May -3 e MSK/MSD 1996 Mar 31 0s -3 1 MSD 1996 O 27 3s -3 R MSK/MSD 1997 -3 - MSK 1997 Mar lastSu 1u -2 E EE%sT 2014 Mar 30 2 -4 - MSK 2014 O 26 2s -3 - MSK -Z Europe/Astrakhan 3:12:12 - LMT 1924 May -3 - +03 1930 Jun 21 -4 R +04/+05 1989 Mar 26 2s -3 R +03/+04 1991 Mar 31 2s -4 - +04 1992 Mar 29 2s -3 R +03/+04 2011 Mar 27 2s -4 - +04 2014 O 26 2s -3 - +03 2016 Mar 27 2s -4 - +04 -Z Europe/Volgograd 2:57:40 - LMT 1920 Ja 3 -3 - +03 1930 Jun 21 -4 - +04 1961 N 11 -4 R +04/+05 1988 Mar 27 2s -3 R +03/+04 1991 Mar 31 2s -4 - +04 1992 Mar 29 2s -3 R +03/+04 2011 Mar 27 2s -4 - +04 2014 O 26 2s -3 - +03 2018 O 28 2s -4 - +04 2020 D 27 2s -3 - +03 -Z Europe/Saratov 3:4:18 - LMT 1919 Jul 1 0u -3 - +03 1930 Jun 21 -4 R +04/+05 1988 Mar 27 2s -3 R +03/+04 1991 Mar 31 2s -4 - +04 1992 Mar 29 2s -3 R +03/+04 2011 Mar 27 2s -4 - +04 2014 O 26 2s -3 - +03 2016 D 4 2s -4 - +04 -Z Europe/Kirov 3:18:48 - LMT 1919 Jul 1 0u -3 - +03 1930 Jun 21 -4 R +04/+05 1989 Mar 26 2s -3 R +03/+04 1991 Mar 31 2s -4 - +04 1992 Mar 29 2s -3 R +03/+04 2011 Mar 27 2s -4 - +04 2014 O 26 2s -3 - +03 -Z Europe/Samara 3:20:20 - LMT 1919 Jul 1 0u -3 - +03 1930 Jun 21 -4 - +04 1935 Ja 27 -4 R +04/+05 1989 Mar 26 2s -3 R +03/+04 1991 Mar 31 2s -2 R +02/+03 1991 S 29 2s -3 - +03 1991 O 20 3 -4 R +04/+05 2010 Mar 28 2s -3 R +03/+04 2011 Mar 27 2s -4 - +04 -Z Europe/Ulyanovsk 3:13:36 - LMT 1919 Jul 1 0u -3 - +03 1930 Jun 21 -4 R +04/+05 1989 Mar 26 2s -3 R +03/+04 1991 Mar 31 2s -2 R +02/+03 1992 Ja 19 2s -3 R +03/+04 2011 Mar 27 2s -4 - +04 2014 O 26 2s -3 - +03 2016 Mar 27 2s -4 - +04 -Z Asia/Yekaterinburg 4:2:33 - LMT 1916 Jul 3 -3:45:5 - PMT 1919 Jul 15 4 -4 - +04 1930 Jun 21 -5 R +05/+06 1991 Mar 31 2s -4 R +04/+05 1992 Ja 19 2s -5 R +05/+06 2011 Mar 27 2s -6 - +06 2014 O 26 2s -5 - +05 -Z Asia/Omsk 4:53:30 - LMT 1919 N 14 -5 - +05 1930 Jun 21 -6 R +06/+07 1991 Mar 31 2s -5 R +05/+06 1992 Ja 19 2s -6 R +06/+07 2011 Mar 27 2s -7 - +07 2014 O 26 2s -6 - +06 -Z Asia/Barnaul 5:35 - LMT 1919 D 10 -6 - +06 1930 Jun 21 -7 R +07/+08 1991 Mar 31 2s -6 R +06/+07 1992 Ja 19 2s -7 R +07/+08 1995 May 28 -6 R +06/+07 2011 Mar 27 2s -7 - +07 2014 O 26 2s -6 - +06 2016 Mar 27 2s -7 - +07 -Z Asia/Novosibirsk 5:31:40 - LMT 1919 D 14 6 -6 - +06 1930 Jun 21 -7 R +07/+08 1991 Mar 31 2s -6 R +06/+07 1992 Ja 19 2s -7 R +07/+08 1993 May 23 -6 R +06/+07 2011 Mar 27 2s -7 - +07 2014 O 26 2s -6 - +06 2016 Jul 24 2s -7 - +07 -Z Asia/Tomsk 5:39:51 - LMT 1919 D 22 -6 - +06 1930 Jun 21 -7 R +07/+08 1991 Mar 31 2s -6 R +06/+07 1992 Ja 19 2s -7 R +07/+08 2002 May 1 3 -6 R +06/+07 2011 Mar 27 2s -7 - +07 2014 O 26 2s -6 - +06 2016 May 29 2s -7 - +07 -Z Asia/Novokuznetsk 5:48:48 - LMT 1924 May -6 - +06 1930 Jun 21 -7 R +07/+08 1991 Mar 31 2s -6 R +06/+07 1992 Ja 19 2s -7 R +07/+08 2010 Mar 28 2s -6 R +06/+07 2011 Mar 27 2s -7 - +07 -Z Asia/Krasnoyarsk 6:11:26 - LMT 1920 Ja 6 -6 - +06 1930 Jun 21 -7 R +07/+08 1991 Mar 31 2s -6 R +06/+07 1992 Ja 19 2s -7 R +07/+08 2011 Mar 27 2s -8 - +08 2014 O 26 2s -7 - +07 -Z Asia/Irkutsk 6:57:5 - LMT 1880 -6:57:5 - IMT 1920 Ja 25 -7 - +07 1930 Jun 21 -8 R +08/+09 1991 Mar 31 2s -7 R +07/+08 1992 Ja 19 2s -8 R +08/+09 2011 Mar 27 2s -9 - +09 2014 O 26 2s -8 - +08 -Z Asia/Chita 7:33:52 - LMT 1919 D 15 -8 - +08 1930 Jun 21 -9 R +09/+10 1991 Mar 31 2s -8 R +08/+09 1992 Ja 19 2s -9 R +09/+10 2011 Mar 27 2s -10 - +10 2014 O 26 2s -8 - +08 2016 Mar 27 2 -9 - +09 -Z Asia/Yakutsk 8:38:58 - LMT 1919 D 15 -8 - +08 1930 Jun 21 -9 R +09/+10 1991 Mar 31 2s -8 R +08/+09 1992 Ja 19 2s -9 R +09/+10 2011 Mar 27 2s -10 - +10 2014 O 26 2s -9 - +09 -Z Asia/Vladivostok 8:47:31 - LMT 1922 N 15 -9 - +09 1930 Jun 21 -10 R +10/+11 1991 Mar 31 2s -9 R +09/+10 1992 Ja 19 2s -10 R +10/+11 2011 Mar 27 2s -11 - +11 2014 O 26 2s -10 - +10 -Z Asia/Khandyga 9:2:13 - LMT 1919 D 15 -8 - +08 1930 Jun 21 -9 R +09/+10 1991 Mar 31 2s -8 R +08/+09 1992 Ja 19 2s -9 R +09/+10 2004 -10 R +10/+11 2011 Mar 27 2s -11 - +11 2011 S 13 0s -10 - +10 2014 O 26 2s -9 - +09 -Z Asia/Sakhalin 9:30:48 - LMT 1905 Au 23 -9 - +09 1945 Au 25 -11 R +11/+12 1991 Mar 31 2s -10 R +10/+11 1992 Ja 19 2s -11 R +11/+12 1997 Mar lastSu 2s -10 R +10/+11 2011 Mar 27 2s -11 - +11 2014 O 26 2s -10 - +10 2016 Mar 27 2s -11 - +11 -Z Asia/Magadan 10:3:12 - LMT 1924 May 2 -10 - +10 1930 Jun 21 -11 R +11/+12 1991 Mar 31 2s -10 R +10/+11 1992 Ja 19 2s -11 R +11/+12 2011 Mar 27 2s -12 - +12 2014 O 26 2s -10 - +10 2016 Ap 24 2s -11 - +11 -Z Asia/Srednekolymsk 10:14:52 - LMT 1924 May 2 -10 - +10 1930 Jun 21 -11 R +11/+12 1991 Mar 31 2s -10 R +10/+11 1992 Ja 19 2s -11 R +11/+12 2011 Mar 27 2s -12 - +12 2014 O 26 2s -11 - +11 -Z Asia/Ust-Nera 9:32:54 - LMT 1919 D 15 -8 - +08 1930 Jun 21 -9 R +09/+10 1981 Ap -11 R +11/+12 1991 Mar 31 2s -10 R +10/+11 1992 Ja 19 2s -11 R +11/+12 2011 Mar 27 2s -12 - +12 2011 S 13 0s -11 - +11 2014 O 26 2s -10 - +10 -Z Asia/Kamchatka 10:34:36 - LMT 1922 N 10 -11 - +11 1930 Jun 21 -12 R +12/+13 1991 Mar 31 2s -11 R +11/+12 1992 Ja 19 2s -12 R +12/+13 2010 Mar 28 2s -11 R +11/+12 2011 Mar 27 2s -12 - +12 -Z Asia/Anadyr 11:49:56 - LMT 1924 May 2 -12 - +12 1930 Jun 21 -13 R +13/+14 1982 Ap 1 0s -12 R +12/+13 1991 Mar 31 2s -11 R +11/+12 1992 Ja 19 2s -12 R +12/+13 2010 Mar 28 2s -11 R +11/+12 2011 Mar 27 2s -12 - +12 -Z Europe/Belgrade 1:22 - LMT 1884 -1 - CET 1941 Ap 18 23 -1 c CE%sT 1945 -1 - CET 1945 May 8 2s -1 1 CEST 1945 S 16 2s -1 - CET 1982 N 27 -1 E CE%sT -L Europe/Belgrade Europe/Ljubljana -L Europe/Belgrade Europe/Podgorica -L Europe/Belgrade Europe/Sarajevo -L Europe/Belgrade Europe/Skopje -L Europe/Belgrade Europe/Zagreb -L Europe/Prague Europe/Bratislava -R s 1918 o - Ap 15 23 1 S -R s 1918 1919 - O 6 24s 0 - -R s 1919 o - Ap 6 23 1 S -R s 1924 o - Ap 16 23 1 S -R s 1924 o - O 4 24s 0 - -R s 1926 o - Ap 17 23 1 S -R s 1926 1929 - O Sa>=1 24s 0 - -R s 1927 o - Ap 9 23 1 S -R s 1928 o - Ap 15 0 1 S -R s 1929 o - Ap 20 23 1 S -R s 1937 o - Jun 16 23 1 S -R s 1937 o - O 2 24s 0 - -R s 1938 o - Ap 2 23 1 S -R s 1938 o - Ap 30 23 2 M -R s 1938 o - O 2 24 1 S -R s 1939 o - O 7 24s 0 - -R s 1942 o - May 2 23 1 S -R s 1942 o - S 1 1 0 - -R s 1943 1946 - Ap Sa>=13 23 1 S -R s 1943 1944 - O Su>=1 1 0 - -R s 1945 1946 - S lastSu 1 0 - -R s 1949 o - Ap 30 23 1 S -R s 1949 o - O 2 1 0 - -R s 1974 1975 - Ap Sa>=12 23 1 S -R s 1974 1975 - O Su>=1 1 0 - -R s 1976 o - Mar 27 23 1 S -R s 1976 1977 - S lastSu 1 0 - -R s 1977 o - Ap 2 23 1 S -R s 1978 o - Ap 2 2s 1 S -R s 1978 o - O 1 2s 0 - -R Sp 1967 o - Jun 3 12 1 S -R Sp 1967 o - O 1 0 0 - -R Sp 1974 o - Jun 24 0 1 S -R Sp 1974 o - S 1 0 0 - -R Sp 1976 1977 - May 1 0 1 S -R Sp 1976 o - Au 1 0 0 - -R Sp 1977 o - S 28 0 0 - -R Sp 1978 o - Jun 1 0 1 S -R Sp 1978 o - Au 4 0 0 - -Z Europe/Madrid -0:14:44 - LMT 1900 D 31 23:45:16 -0 s WE%sT 1940 Mar 16 23 -1 s CE%sT 1979 -1 E CE%sT -Z Africa/Ceuta -0:21:16 - LMT 1900 D 31 23:38:44 -0 - WET 1918 May 6 23 -0 1 WEST 1918 O 7 23 -0 - WET 1924 -0 s WE%sT 1929 -0 - WET 1967 -0 Sp WE%sT 1984 Mar 16 -1 - CET 1986 -1 E CE%sT -Z Atlantic/Canary -1:1:36 - LMT 1922 Mar --1 - -01 1946 S 30 1 -0 - WET 1980 Ap 6 0s -0 1 WEST 1980 S 28 1u -0 E WE%sT -Z Europe/Stockholm 1:12:12 - LMT 1879 -1:0:14 - SET 1900 -1 - CET 1916 May 14 23 -1 1 CEST 1916 O 1 1 -1 - CET 1980 -1 E CE%sT -R CH 1941 1942 - May M>=1 1 1 S -R CH 1941 1942 - O M>=1 2 0 - -Z Europe/Zurich 0:34:8 - LMT 1853 Jul 16 -0:29:46 - BMT 1894 Jun -1 CH CE%sT 1981 -1 E CE%sT -R T 1916 o - May 1 0 1 S -R T 1916 o - O 1 0 0 - -R T 1920 o - Mar 28 0 1 S -R T 1920 o - O 25 0 0 - -R T 1921 o - Ap 3 0 1 S -R T 1921 o - O 3 0 0 - -R T 1922 o - Mar 26 0 1 S -R T 1922 o - O 8 0 0 - -R T 1924 o - May 13 0 1 S -R T 1924 1925 - O 1 0 0 - -R T 1925 o - May 1 0 1 S -R T 1940 o - Jul 1 0 1 S -R T 1940 o - O 6 0 0 - -R T 1940 o - D 1 0 1 S -R T 1941 o - S 21 0 0 - -R T 1942 o - Ap 1 0 1 S -R T 1945 o - O 8 0 0 - -R T 1946 o - Jun 1 0 1 S -R T 1946 o - O 1 0 0 - -R T 1947 1948 - Ap Su>=16 0 1 S -R T 1947 1951 - O Su>=2 0 0 - -R T 1949 o - Ap 10 0 1 S -R T 1950 o - Ap 16 0 1 S -R T 1951 o - Ap 22 0 1 S -R T 1962 o - Jul 15 0 1 S -R T 1963 o - O 30 0 0 - -R T 1964 o - May 15 0 1 S -R T 1964 o - O 1 0 0 - -R T 1973 o - Jun 3 1 1 S -R T 1973 1976 - O Su>=31 2 0 - -R T 1974 o - Mar 31 2 1 S -R T 1975 o - Mar 22 2 1 S -R T 1976 o - Mar 21 2 1 S -R T 1977 1978 - Ap Su>=1 2 1 S -R T 1977 1978 - O Su>=15 2 0 - -R T 1978 o - Jun 29 0 0 - -R T 1983 o - Jul 31 2 1 S -R T 1983 o - O 2 2 0 - -R T 1985 o - Ap 20 1s 1 S -R T 1985 o - S 28 1s 0 - -R T 1986 1993 - Mar lastSu 1s 1 S -R T 1986 1995 - S lastSu 1s 0 - -R T 1994 o - Mar 20 1s 1 S -R T 1995 2006 - Mar lastSu 1s 1 S -R T 1996 2006 - O lastSu 1s 0 - -Z Europe/Istanbul 1:55:52 - LMT 1880 -1:56:56 - IMT 1910 O -2 T EE%sT 1978 Jun 29 -3 T +03/+04 1984 N 1 2 -2 T EE%sT 2007 -2 E EE%sT 2011 Mar 27 1u -2 - EET 2011 Mar 28 1u -2 E EE%sT 2014 Mar 30 1u -2 - EET 2014 Mar 31 1u -2 E EE%sT 2015 O 25 1u -2 1 EEST 2015 N 8 1u -2 E EE%sT 2016 S 7 -3 - +03 -L Europe/Istanbul Asia/Istanbul -Z Europe/Kiev 2:2:4 - LMT 1880 -2:2:4 - KMT 1924 May 2 -2 - EET 1930 Jun 21 -3 - MSK 1941 S 20 -1 c CE%sT 1943 N 6 -3 R MSK/MSD 1990 Jul 1 2 -2 1 EEST 1991 S 29 3 -2 c EE%sT 1996 May 13 -2 E EE%sT -Z Europe/Uzhgorod 1:29:12 - LMT 1890 O -1 - CET 1940 -1 c CE%sT 1944 O -1 1 CEST 1944 O 26 -1 - CET 1945 Jun 29 -3 R MSK/MSD 1990 -3 - MSK 1990 Jul 1 2 -1 - CET 1991 Mar 31 3 -2 - EET 1992 Mar 20 -2 c EE%sT 1996 May 13 -2 E EE%sT -Z Europe/Zaporozhye 2:20:40 - LMT 1880 -2:20 - +0220 1924 May 2 -2 - EET 1930 Jun 21 -3 - MSK 1941 Au 25 -1 c CE%sT 1943 O 25 -3 R MSK/MSD 1991 Mar 31 2 -2 e EE%sT 1992 Mar 20 -2 c EE%sT 1996 May 13 -2 E EE%sT -R u 1918 1919 - Mar lastSu 2 1 D -R u 1918 1919 - O lastSu 2 0 S -R u 1942 o - F 9 2 1 W -R u 1945 o - Au 14 23u 1 P -R u 1945 o - S 30 2 0 S -R u 1967 2006 - O lastSu 2 0 S -R u 1967 1973 - Ap lastSu 2 1 D -R u 1974 o - Ja 6 2 1 D -R u 1975 o - F lastSu 2 1 D -R u 1976 1986 - Ap lastSu 2 1 D -R u 1987 2006 - Ap Su>=1 2 1 D -R u 2007 ma - Mar Su>=8 2 1 D -R u 2007 ma - N Su>=1 2 0 S -Z EST -5 - EST -Z MST -7 - MST -Z HST -10 - HST -Z EST5EDT -5 u E%sT -Z CST6CDT -6 u C%sT -Z MST7MDT -7 u M%sT -Z PST8PDT -8 u P%sT -R NY 1920 o - Mar lastSu 2 1 D -R NY 1920 o - O lastSu 2 0 S -R NY 1921 1966 - Ap lastSu 2 1 D -R NY 1921 1954 - S lastSu 2 0 S -R NY 1955 1966 - O lastSu 2 0 S -Z America/New_York -4:56:2 - LMT 1883 N 18 12:3:58 --5 u E%sT 1920 --5 NY E%sT 1942 --5 u E%sT 1946 --5 NY E%sT 1967 --5 u E%sT -R Ch 1920 o - Jun 13 2 1 D -R Ch 1920 1921 - O lastSu 2 0 S -R Ch 1921 o - Mar lastSu 2 1 D -R Ch 1922 1966 - Ap lastSu 2 1 D -R Ch 1922 1954 - S lastSu 2 0 S -R Ch 1955 1966 - O lastSu 2 0 S -Z America/Chicago -5:50:36 - LMT 1883 N 18 12:9:24 --6 u C%sT 1920 --6 Ch C%sT 1936 Mar 1 2 --5 - EST 1936 N 15 2 --6 Ch C%sT 1942 --6 u C%sT 1946 --6 Ch C%sT 1967 --6 u C%sT -Z America/North_Dakota/Center -6:45:12 - LMT 1883 N 18 12:14:48 --7 u M%sT 1992 O 25 2 --6 u C%sT -Z America/North_Dakota/New_Salem -6:45:39 - LMT 1883 N 18 12:14:21 --7 u M%sT 2003 O 26 2 --6 u C%sT -Z America/North_Dakota/Beulah -6:47:7 - LMT 1883 N 18 12:12:53 --7 u M%sT 2010 N 7 2 --6 u C%sT -R De 1920 1921 - Mar lastSu 2 1 D -R De 1920 o - O lastSu 2 0 S -R De 1921 o - May 22 2 0 S -R De 1965 1966 - Ap lastSu 2 1 D -R De 1965 1966 - O lastSu 2 0 S -Z America/Denver -6:59:56 - LMT 1883 N 18 12:0:4 --7 u M%sT 1920 --7 De M%sT 1942 --7 u M%sT 1946 --7 De M%sT 1967 --7 u M%sT -R CA 1948 o - Mar 14 2:1 1 D -R CA 1949 o - Ja 1 2 0 S -R CA 1950 1966 - Ap lastSu 1 1 D -R CA 1950 1961 - S lastSu 2 0 S -R CA 1962 1966 - O lastSu 2 0 S -Z America/Los_Angeles -7:52:58 - LMT 1883 N 18 12:7:2 --8 u P%sT 1946 --8 CA P%sT 1967 --8 u P%sT -Z America/Juneau 15:2:19 - LMT 1867 O 19 15:33:32 --8:57:41 - LMT 1900 Au 20 12 --8 - PST 1942 --8 u P%sT 1946 --8 - PST 1969 --8 u P%sT 1980 Ap 27 2 --9 u Y%sT 1980 O 26 2 --8 u P%sT 1983 O 30 2 --9 u Y%sT 1983 N 30 --9 u AK%sT -Z America/Sitka 14:58:47 - LMT 1867 O 19 15:30 --9:1:13 - LMT 1900 Au 20 12 --8 - PST 1942 --8 u P%sT 1946 --8 - PST 1969 --8 u P%sT 1983 O 30 2 --9 u Y%sT 1983 N 30 --9 u AK%sT -Z America/Metlakatla 15:13:42 - LMT 1867 O 19 15:44:55 --8:46:18 - LMT 1900 Au 20 12 --8 - PST 1942 --8 u P%sT 1946 --8 - PST 1969 --8 u P%sT 1983 O 30 2 --8 - PST 2015 N 1 2 --9 u AK%sT 2018 N 4 2 --8 - PST 2019 Ja 20 2 --9 u AK%sT -Z America/Yakutat 14:41:5 - LMT 1867 O 19 15:12:18 --9:18:55 - LMT 1900 Au 20 12 --9 - YST 1942 --9 u Y%sT 1946 --9 - YST 1969 --9 u Y%sT 1983 N 30 --9 u AK%sT -Z America/Anchorage 14:0:24 - LMT 1867 O 19 14:31:37 --9:59:36 - LMT 1900 Au 20 12 --10 - AST 1942 --10 u A%sT 1967 Ap --10 - AHST 1969 --10 u AH%sT 1983 O 30 2 --9 u Y%sT 1983 N 30 --9 u AK%sT -Z America/Nome 12:58:22 - LMT 1867 O 19 13:29:35 --11:1:38 - LMT 1900 Au 20 12 --11 - NST 1942 --11 u N%sT 1946 --11 - NST 1967 Ap --11 - BST 1969 --11 u B%sT 1983 O 30 2 --9 u Y%sT 1983 N 30 --9 u AK%sT -Z America/Adak 12:13:22 - LMT 1867 O 19 12:44:35 --11:46:38 - LMT 1900 Au 20 12 --11 - NST 1942 --11 u N%sT 1946 --11 - NST 1967 Ap --11 - BST 1969 --11 u B%sT 1983 O 30 2 --10 u AH%sT 1983 N 30 --10 u H%sT -Z Pacific/Honolulu -10:31:26 - LMT 1896 Ja 13 12 --10:30 - HST 1933 Ap 30 2 --10:30 1 HDT 1933 May 21 12 --10:30 u H%sT 1947 Jun 8 2 --10 - HST -Z America/Phoenix -7:28:18 - LMT 1883 N 18 11:31:42 --7 u M%sT 1944 Ja 1 0:1 --7 - MST 1944 Ap 1 0:1 --7 u M%sT 1944 O 1 0:1 --7 - MST 1967 --7 u M%sT 1968 Mar 21 --7 - MST -L America/Phoenix America/Creston -Z America/Boise -7:44:49 - LMT 1883 N 18 12:15:11 --8 u P%sT 1923 May 13 2 --7 u M%sT 1974 --7 - MST 1974 F 3 2 --7 u M%sT -R In 1941 o - Jun 22 2 1 D -R In 1941 1954 - S lastSu 2 0 S -R In 1946 1954 - Ap lastSu 2 1 D -Z America/Indiana/Indianapolis -5:44:38 - LMT 1883 N 18 12:15:22 --6 u C%sT 1920 --6 In C%sT 1942 --6 u C%sT 1946 --6 In C%sT 1955 Ap 24 2 --5 - EST 1957 S 29 2 --6 - CST 1958 Ap 27 2 --5 - EST 1969 --5 u E%sT 1971 --5 - EST 2006 --5 u E%sT -R Ma 1951 o - Ap lastSu 2 1 D -R Ma 1951 o - S lastSu 2 0 S -R Ma 1954 1960 - Ap lastSu 2 1 D -R Ma 1954 1960 - S lastSu 2 0 S -Z America/Indiana/Marengo -5:45:23 - LMT 1883 N 18 12:14:37 --6 u C%sT 1951 --6 Ma C%sT 1961 Ap 30 2 --5 - EST 1969 --5 u E%sT 1974 Ja 6 2 --6 1 CDT 1974 O 27 2 --5 u E%sT 1976 --5 - EST 2006 --5 u E%sT -R V 1946 o - Ap lastSu 2 1 D -R V 1946 o - S lastSu 2 0 S -R V 1953 1954 - Ap lastSu 2 1 D -R V 1953 1959 - S lastSu 2 0 S -R V 1955 o - May 1 0 1 D -R V 1956 1963 - Ap lastSu 2 1 D -R V 1960 o - O lastSu 2 0 S -R V 1961 o - S lastSu 2 0 S -R V 1962 1963 - O lastSu 2 0 S -Z America/Indiana/Vincennes -5:50:7 - LMT 1883 N 18 12:9:53 --6 u C%sT 1946 --6 V C%sT 1964 Ap 26 2 --5 - EST 1969 --5 u E%sT 1971 --5 - EST 2006 Ap 2 2 --6 u C%sT 2007 N 4 2 --5 u E%sT -R Pe 1955 o - May 1 0 1 D -R Pe 1955 1960 - S lastSu 2 0 S -R Pe 1956 1963 - Ap lastSu 2 1 D -R Pe 1961 1963 - O lastSu 2 0 S -Z America/Indiana/Tell_City -5:47:3 - LMT 1883 N 18 12:12:57 --6 u C%sT 1946 --6 Pe C%sT 1964 Ap 26 2 --5 - EST 1967 O 29 2 --6 u C%sT 1969 Ap 27 2 --5 u E%sT 1971 --5 - EST 2006 Ap 2 2 --6 u C%sT -R Pi 1955 o - May 1 0 1 D -R Pi 1955 1960 - S lastSu 2 0 S -R Pi 1956 1964 - Ap lastSu 2 1 D -R Pi 1961 1964 - O lastSu 2 0 S -Z America/Indiana/Petersburg -5:49:7 - LMT 1883 N 18 12:10:53 --6 u C%sT 1955 --6 Pi C%sT 1965 Ap 25 2 --5 - EST 1966 O 30 2 --6 u C%sT 1977 O 30 2 --5 - EST 2006 Ap 2 2 --6 u C%sT 2007 N 4 2 --5 u E%sT -R St 1947 1961 - Ap lastSu 2 1 D -R St 1947 1954 - S lastSu 2 0 S -R St 1955 1956 - O lastSu 2 0 S -R St 1957 1958 - S lastSu 2 0 S -R St 1959 1961 - O lastSu 2 0 S -Z America/Indiana/Knox -5:46:30 - LMT 1883 N 18 12:13:30 --6 u C%sT 1947 --6 St C%sT 1962 Ap 29 2 --5 - EST 1963 O 27 2 --6 u C%sT 1991 O 27 2 --5 - EST 2006 Ap 2 2 --6 u C%sT -R Pu 1946 1960 - Ap lastSu 2 1 D -R Pu 1946 1954 - S lastSu 2 0 S -R Pu 1955 1956 - O lastSu 2 0 S -R Pu 1957 1960 - S lastSu 2 0 S -Z America/Indiana/Winamac -5:46:25 - LMT 1883 N 18 12:13:35 --6 u C%sT 1946 --6 Pu C%sT 1961 Ap 30 2 --5 - EST 1969 --5 u E%sT 1971 --5 - EST 2006 Ap 2 2 --6 u C%sT 2007 Mar 11 2 --5 u E%sT -Z America/Indiana/Vevay -5:40:16 - LMT 1883 N 18 12:19:44 --6 u C%sT 1954 Ap 25 2 --5 - EST 1969 --5 u E%sT 1973 --5 - EST 2006 --5 u E%sT -R v 1921 o - May 1 2 1 D -R v 1921 o - S 1 2 0 S -R v 1941 o - Ap lastSu 2 1 D -R v 1941 o - S lastSu 2 0 S -R v 1946 o - Ap lastSu 0:1 1 D -R v 1946 o - Jun 2 2 0 S -R v 1950 1961 - Ap lastSu 2 1 D -R v 1950 1955 - S lastSu 2 0 S -R v 1956 1961 - O lastSu 2 0 S -Z America/Kentucky/Louisville -5:43:2 - LMT 1883 N 18 12:16:58 --6 u C%sT 1921 --6 v C%sT 1942 --6 u C%sT 1946 --6 v C%sT 1961 Jul 23 2 --5 - EST 1968 --5 u E%sT 1974 Ja 6 2 --6 1 CDT 1974 O 27 2 --5 u E%sT -Z America/Kentucky/Monticello -5:39:24 - LMT 1883 N 18 12:20:36 --6 u C%sT 1946 --6 - CST 1968 --6 u C%sT 2000 O 29 2 --5 u E%sT -R Dt 1948 o - Ap lastSu 2 1 D -R Dt 1948 o - S lastSu 2 0 S -Z America/Detroit -5:32:11 - LMT 1905 --6 - CST 1915 May 15 2 --5 - EST 1942 --5 u E%sT 1946 --5 Dt E%sT 1967 Jun 14 0:1 --5 u E%sT 1969 --5 - EST 1973 --5 u E%sT 1975 --5 - EST 1975 Ap 27 2 --5 u E%sT -R Me 1946 o - Ap lastSu 2 1 D -R Me 1946 o - S lastSu 2 0 S -R Me 1966 o - Ap lastSu 2 1 D -R Me 1966 o - O lastSu 2 0 S -Z America/Menominee -5:50:27 - LMT 1885 S 18 12 --6 u C%sT 1946 --6 Me C%sT 1969 Ap 27 2 --5 - EST 1973 Ap 29 2 --6 u C%sT -R C 1918 o - Ap 14 2 1 D -R C 1918 o - O 27 2 0 S -R C 1942 o - F 9 2 1 W -R C 1945 o - Au 14 23u 1 P -R C 1945 o - S 30 2 0 S -R C 1974 1986 - Ap lastSu 2 1 D -R C 1974 2006 - O lastSu 2 0 S -R C 1987 2006 - Ap Su>=1 2 1 D -R C 2007 ma - Mar Su>=8 2 1 D -R C 2007 ma - N Su>=1 2 0 S -R j 1917 o - Ap 8 2 1 D -R j 1917 o - S 17 2 0 S -R j 1919 o - May 5 23 1 D -R j 1919 o - Au 12 23 0 S -R j 1920 1935 - May Su>=1 23 1 D -R j 1920 1935 - O lastSu 23 0 S -R j 1936 1941 - May M>=9 0 1 D -R j 1936 1941 - O M>=2 0 0 S -R j 1946 1950 - May Su>=8 2 1 D -R j 1946 1950 - O Su>=2 2 0 S -R j 1951 1986 - Ap lastSu 2 1 D -R j 1951 1959 - S lastSu 2 0 S -R j 1960 1986 - O lastSu 2 0 S -R j 1987 o - Ap Su>=1 0:1 1 D -R j 1987 2006 - O lastSu 0:1 0 S -R j 1988 o - Ap Su>=1 0:1 2 DD -R j 1989 2006 - Ap Su>=1 0:1 1 D -R j 2007 2011 - Mar Su>=8 0:1 1 D -R j 2007 2010 - N Su>=1 0:1 0 S -Z America/St_Johns -3:30:52 - LMT 1884 --3:30:52 j N%sT 1918 --3:30:52 C N%sT 1919 --3:30:52 j N%sT 1935 Mar 30 --3:30 j N%sT 1942 May 11 --3:30 C N%sT 1946 --3:30 j N%sT 2011 N --3:30 C N%sT -Z America/Goose_Bay -4:1:40 - LMT 1884 --3:30:52 - NST 1918 --3:30:52 C N%sT 1919 --3:30:52 - NST 1935 Mar 30 --3:30 - NST 1936 --3:30 j N%sT 1942 May 11 --3:30 C N%sT 1946 --3:30 j N%sT 1966 Mar 15 2 --4 j A%sT 2011 N --4 C A%sT -R H 1916 o - Ap 1 0 1 D -R H 1916 o - O 1 0 0 S -R H 1920 o - May 9 0 1 D -R H 1920 o - Au 29 0 0 S -R H 1921 o - May 6 0 1 D -R H 1921 1922 - S 5 0 0 S -R H 1922 o - Ap 30 0 1 D -R H 1923 1925 - May Su>=1 0 1 D -R H 1923 o - S 4 0 0 S -R H 1924 o - S 15 0 0 S -R H 1925 o - S 28 0 0 S -R H 1926 o - May 16 0 1 D -R H 1926 o - S 13 0 0 S -R H 1927 o - May 1 0 1 D -R H 1927 o - S 26 0 0 S -R H 1928 1931 - May Su>=8 0 1 D -R H 1928 o - S 9 0 0 S -R H 1929 o - S 3 0 0 S -R H 1930 o - S 15 0 0 S -R H 1931 1932 - S M>=24 0 0 S -R H 1932 o - May 1 0 1 D -R H 1933 o - Ap 30 0 1 D -R H 1933 o - O 2 0 0 S -R H 1934 o - May 20 0 1 D -R H 1934 o - S 16 0 0 S -R H 1935 o - Jun 2 0 1 D -R H 1935 o - S 30 0 0 S -R H 1936 o - Jun 1 0 1 D -R H 1936 o - S 14 0 0 S -R H 1937 1938 - May Su>=1 0 1 D -R H 1937 1941 - S M>=24 0 0 S -R H 1939 o - May 28 0 1 D -R H 1940 1941 - May Su>=1 0 1 D -R H 1946 1949 - Ap lastSu 2 1 D -R H 1946 1949 - S lastSu 2 0 S -R H 1951 1954 - Ap lastSu 2 1 D -R H 1951 1954 - S lastSu 2 0 S -R H 1956 1959 - Ap lastSu 2 1 D -R H 1956 1959 - S lastSu 2 0 S -R H 1962 1973 - Ap lastSu 2 1 D -R H 1962 1973 - O lastSu 2 0 S -Z America/Halifax -4:14:24 - LMT 1902 Jun 15 --4 H A%sT 1918 --4 C A%sT 1919 --4 H A%sT 1942 F 9 2s --4 C A%sT 1946 --4 H A%sT 1974 --4 C A%sT -Z America/Glace_Bay -3:59:48 - LMT 1902 Jun 15 --4 C A%sT 1953 --4 H A%sT 1954 --4 - AST 1972 --4 H A%sT 1974 --4 C A%sT -R o 1933 1935 - Jun Su>=8 1 1 D -R o 1933 1935 - S Su>=8 1 0 S -R o 1936 1938 - Jun Su>=1 1 1 D -R o 1936 1938 - S Su>=1 1 0 S -R o 1939 o - May 27 1 1 D -R o 1939 1941 - S Sa>=21 1 0 S -R o 1940 o - May 19 1 1 D -R o 1941 o - May 4 1 1 D -R o 1946 1972 - Ap lastSu 2 1 D -R o 1946 1956 - S lastSu 2 0 S -R o 1957 1972 - O lastSu 2 0 S -R o 1993 2006 - Ap Su>=1 0:1 1 D -R o 1993 2006 - O lastSu 0:1 0 S -Z America/Moncton -4:19:8 - LMT 1883 D 9 --5 - EST 1902 Jun 15 --4 C A%sT 1933 --4 o A%sT 1942 --4 C A%sT 1946 --4 o A%sT 1973 --4 C A%sT 1993 --4 o A%sT 2007 --4 C A%sT -R t 1919 o - Mar 30 23:30 1 D -R t 1919 o - O 26 0 0 S -R t 1920 o - May 2 2 1 D -R t 1920 o - S 26 0 0 S -R t 1921 o - May 15 2 1 D -R t 1921 o - S 15 2 0 S -R t 1922 1923 - May Su>=8 2 1 D -R t 1922 1926 - S Su>=15 2 0 S -R t 1924 1927 - May Su>=1 2 1 D -R t 1927 1937 - S Su>=25 2 0 S -R t 1928 1937 - Ap Su>=25 2 1 D -R t 1938 1940 - Ap lastSu 2 1 D -R t 1938 1939 - S lastSu 2 0 S -R t 1945 1946 - S lastSu 2 0 S -R t 1946 o - Ap lastSu 2 1 D -R t 1947 1949 - Ap lastSu 0 1 D -R t 1947 1948 - S lastSu 0 0 S -R t 1949 o - N lastSu 0 0 S -R t 1950 1973 - Ap lastSu 2 1 D -R t 1950 o - N lastSu 2 0 S -R t 1951 1956 - S lastSu 2 0 S -R t 1957 1973 - O lastSu 2 0 S -Z America/Toronto -5:17:32 - LMT 1895 --5 C E%sT 1919 --5 t E%sT 1942 F 9 2s --5 C E%sT 1946 --5 t E%sT 1974 --5 C E%sT -L America/Toronto America/Nassau -Z America/Thunder_Bay -5:57 - LMT 1895 --6 - CST 1910 --5 - EST 1942 --5 C E%sT 1970 --5 t E%sT 1973 --5 - EST 1974 --5 C E%sT -Z America/Nipigon -5:53:4 - LMT 1895 --5 C E%sT 1940 S 29 --5 1 EDT 1942 F 9 2s --5 C E%sT -Z America/Rainy_River -6:18:16 - LMT 1895 --6 C C%sT 1940 S 29 --6 1 CDT 1942 F 9 2s --6 C C%sT -R W 1916 o - Ap 23 0 1 D -R W 1916 o - S 17 0 0 S -R W 1918 o - Ap 14 2 1 D -R W 1918 o - O 27 2 0 S -R W 1937 o - May 16 2 1 D -R W 1937 o - S 26 2 0 S -R W 1942 o - F 9 2 1 W -R W 1945 o - Au 14 23u 1 P -R W 1945 o - S lastSu 2 0 S -R W 1946 o - May 12 2 1 D -R W 1946 o - O 13 2 0 S -R W 1947 1949 - Ap lastSu 2 1 D -R W 1947 1949 - S lastSu 2 0 S -R W 1950 o - May 1 2 1 D -R W 1950 o - S 30 2 0 S -R W 1951 1960 - Ap lastSu 2 1 D -R W 1951 1958 - S lastSu 2 0 S -R W 1959 o - O lastSu 2 0 S -R W 1960 o - S lastSu 2 0 S -R W 1963 o - Ap lastSu 2 1 D -R W 1963 o - S 22 2 0 S -R W 1966 1986 - Ap lastSu 2s 1 D -R W 1966 2005 - O lastSu 2s 0 S -R W 1987 2005 - Ap Su>=1 2s 1 D -Z America/Winnipeg -6:28:36 - LMT 1887 Jul 16 --6 W C%sT 2006 --6 C C%sT -R r 1918 o - Ap 14 2 1 D -R r 1918 o - O 27 2 0 S -R r 1930 1934 - May Su>=1 0 1 D -R r 1930 1934 - O Su>=1 0 0 S -R r 1937 1941 - Ap Su>=8 0 1 D -R r 1937 o - O Su>=8 0 0 S -R r 1938 o - O Su>=1 0 0 S -R r 1939 1941 - O Su>=8 0 0 S -R r 1942 o - F 9 2 1 W -R r 1945 o - Au 14 23u 1 P -R r 1945 o - S lastSu 2 0 S -R r 1946 o - Ap Su>=8 2 1 D -R r 1946 o - O Su>=8 2 0 S -R r 1947 1957 - Ap lastSu 2 1 D -R r 1947 1957 - S lastSu 2 0 S -R r 1959 o - Ap lastSu 2 1 D -R r 1959 o - O lastSu 2 0 S -R Sw 1957 o - Ap lastSu 2 1 D -R Sw 1957 o - O lastSu 2 0 S -R Sw 1959 1961 - Ap lastSu 2 1 D -R Sw 1959 o - O lastSu 2 0 S -R Sw 1960 1961 - S lastSu 2 0 S -Z America/Regina -6:58:36 - LMT 1905 S --7 r M%sT 1960 Ap lastSu 2 --6 - CST -Z America/Swift_Current -7:11:20 - LMT 1905 S --7 C M%sT 1946 Ap lastSu 2 --7 r M%sT 1950 --7 Sw M%sT 1972 Ap lastSu 2 --6 - CST -R Ed 1918 1919 - Ap Su>=8 2 1 D -R Ed 1918 o - O 27 2 0 S -R Ed 1919 o - May 27 2 0 S -R Ed 1920 1923 - Ap lastSu 2 1 D -R Ed 1920 o - O lastSu 2 0 S -R Ed 1921 1923 - S lastSu 2 0 S -R Ed 1942 o - F 9 2 1 W -R Ed 1945 o - Au 14 23u 1 P -R Ed 1945 o - S lastSu 2 0 S -R Ed 1947 o - Ap lastSu 2 1 D -R Ed 1947 o - S lastSu 2 0 S -R Ed 1972 1986 - Ap lastSu 2 1 D -R Ed 1972 2006 - O lastSu 2 0 S -Z America/Edmonton -7:33:52 - LMT 1906 S --7 Ed M%sT 1987 --7 C M%sT -R Va 1918 o - Ap 14 2 1 D -R Va 1918 o - O 27 2 0 S -R Va 1942 o - F 9 2 1 W -R Va 1945 o - Au 14 23u 1 P -R Va 1945 o - S 30 2 0 S -R Va 1946 1986 - Ap lastSu 2 1 D -R Va 1946 o - S 29 2 0 S -R Va 1947 1961 - S lastSu 2 0 S -R Va 1962 2006 - O lastSu 2 0 S -Z America/Vancouver -8:12:28 - LMT 1884 --8 Va P%sT 1987 --8 C P%sT -Z America/Dawson_Creek -8:0:56 - LMT 1884 --8 C P%sT 1947 --8 Va P%sT 1972 Au 30 2 --7 - MST -Z America/Fort_Nelson -8:10:47 - LMT 1884 --8 Va P%sT 1946 --8 - PST 1947 --8 Va P%sT 1987 --8 C P%sT 2015 Mar 8 2 --7 - MST -R Y 1918 o - Ap 14 2 1 D -R Y 1918 o - O 27 2 0 S -R Y 1919 o - May 25 2 1 D -R Y 1919 o - N 1 0 0 S -R Y 1942 o - F 9 2 1 W -R Y 1945 o - Au 14 23u 1 P -R Y 1945 o - S 30 2 0 S -R Y 1965 o - Ap lastSu 0 2 DD -R Y 1965 o - O lastSu 2 0 S -R Y 1980 1986 - Ap lastSu 2 1 D -R Y 1980 2006 - O lastSu 2 0 S -R Y 1987 2006 - Ap Su>=1 2 1 D -Z America/Pangnirtung 0 - -00 1921 --4 Y A%sT 1995 Ap Su>=1 2 --5 C E%sT 1999 O 31 2 --6 C C%sT 2000 O 29 2 --5 C E%sT -Z America/Iqaluit 0 - -00 1942 Au --5 Y E%sT 1999 O 31 2 --6 C C%sT 2000 O 29 2 --5 C E%sT -Z America/Resolute 0 - -00 1947 Au 31 --6 Y C%sT 2000 O 29 2 --5 - EST 2001 Ap 1 3 --6 C C%sT 2006 O 29 2 --5 - EST 2007 Mar 11 3 --6 C C%sT -Z America/Rankin_Inlet 0 - -00 1957 --6 Y C%sT 2000 O 29 2 --5 - EST 2001 Ap 1 3 --6 C C%sT -Z America/Cambridge_Bay 0 - -00 1920 --7 Y M%sT 1999 O 31 2 --6 C C%sT 2000 O 29 2 --5 - EST 2000 N 5 --6 - CST 2001 Ap 1 3 --7 C M%sT -Z America/Yellowknife 0 - -00 1935 --7 Y M%sT 1980 --7 C M%sT -Z America/Inuvik 0 - -00 1953 --8 Y P%sT 1979 Ap lastSu 2 --7 Y M%sT 1980 --7 C M%sT -Z America/Whitehorse -9:0:12 - LMT 1900 Au 20 --9 Y Y%sT 1967 May 28 --8 Y P%sT 1980 --8 C P%sT 2020 N --7 - MST -Z America/Dawson -9:17:40 - LMT 1900 Au 20 --9 Y Y%sT 1973 O 28 --8 Y P%sT 1980 --8 C P%sT 2020 N --7 - MST -R m 1939 o - F 5 0 1 D -R m 1939 o - Jun 25 0 0 S -R m 1940 o - D 9 0 1 D -R m 1941 o - Ap 1 0 0 S -R m 1943 o - D 16 0 1 W -R m 1944 o - May 1 0 0 S -R m 1950 o - F 12 0 1 D -R m 1950 o - Jul 30 0 0 S -R m 1996 2000 - Ap Su>=1 2 1 D -R m 1996 2000 - O lastSu 2 0 S -R m 2001 o - May Su>=1 2 1 D -R m 2001 o - S lastSu 2 0 S -R m 2002 ma - Ap Su>=1 2 1 D -R m 2002 ma - O lastSu 2 0 S -Z America/Cancun -5:47:4 - LMT 1922 Ja 1 0:12:56 --6 - CST 1981 D 23 --5 m E%sT 1998 Au 2 2 --6 m C%sT 2015 F 1 2 --5 - EST -Z America/Merida -5:58:28 - LMT 1922 Ja 1 0:1:32 --6 - CST 1981 D 23 --5 - EST 1982 D 2 --6 m C%sT -Z America/Matamoros -6:40 - LMT 1921 D 31 23:20 --6 - CST 1988 --6 u C%sT 1989 --6 m C%sT 2010 --6 u C%sT -Z America/Monterrey -6:41:16 - LMT 1921 D 31 23:18:44 --6 - CST 1988 --6 u C%sT 1989 --6 m C%sT -Z America/Mexico_City -6:36:36 - LMT 1922 Ja 1 0:23:24 --7 - MST 1927 Jun 10 23 --6 - CST 1930 N 15 --7 - MST 1931 May 1 23 --6 - CST 1931 O --7 - MST 1932 Ap --6 m C%sT 2001 S 30 2 --6 - CST 2002 F 20 --6 m C%sT -Z America/Ojinaga -6:57:40 - LMT 1922 Ja 1 0:2:20 --7 - MST 1927 Jun 10 23 --6 - CST 1930 N 15 --7 - MST 1931 May 1 23 --6 - CST 1931 O --7 - MST 1932 Ap --6 - CST 1996 --6 m C%sT 1998 --6 - CST 1998 Ap Su>=1 3 --7 m M%sT 2010 --7 u M%sT -Z America/Chihuahua -7:4:20 - LMT 1921 D 31 23:55:40 --7 - MST 1927 Jun 10 23 --6 - CST 1930 N 15 --7 - MST 1931 May 1 23 --6 - CST 1931 O --7 - MST 1932 Ap --6 - CST 1996 --6 m C%sT 1998 --6 - CST 1998 Ap Su>=1 3 --7 m M%sT -Z America/Hermosillo -7:23:52 - LMT 1921 D 31 23:36:8 --7 - MST 1927 Jun 10 23 --6 - CST 1930 N 15 --7 - MST 1931 May 1 23 --6 - CST 1931 O --7 - MST 1932 Ap --6 - CST 1942 Ap 24 --7 - MST 1949 Ja 14 --8 - PST 1970 --7 m M%sT 1999 --7 - MST -Z America/Mazatlan -7:5:40 - LMT 1921 D 31 23:54:20 --7 - MST 1927 Jun 10 23 --6 - CST 1930 N 15 --7 - MST 1931 May 1 23 --6 - CST 1931 O --7 - MST 1932 Ap --6 - CST 1942 Ap 24 --7 - MST 1949 Ja 14 --8 - PST 1970 --7 m M%sT -Z America/Bahia_Banderas -7:1 - LMT 1921 D 31 23:59 --7 - MST 1927 Jun 10 23 --6 - CST 1930 N 15 --7 - MST 1931 May 1 23 --6 - CST 1931 O --7 - MST 1932 Ap --6 - CST 1942 Ap 24 --7 - MST 1949 Ja 14 --8 - PST 1970 --7 m M%sT 2010 Ap 4 2 --6 m C%sT -Z America/Tijuana -7:48:4 - LMT 1922 Ja 1 0:11:56 --7 - MST 1924 --8 - PST 1927 Jun 10 23 --7 - MST 1930 N 15 --8 - PST 1931 Ap --8 1 PDT 1931 S 30 --8 - PST 1942 Ap 24 --8 1 PWT 1945 Au 14 23u --8 1 PPT 1945 N 12 --8 - PST 1948 Ap 5 --8 1 PDT 1949 Ja 14 --8 - PST 1954 --8 CA P%sT 1961 --8 - PST 1976 --8 u P%sT 1996 --8 m P%sT 2001 --8 u P%sT 2002 F 20 --8 m P%sT 2010 --8 u P%sT -R BB 1942 o - Ap 19 5u 1 D -R BB 1942 o - Au 31 6u 0 S -R BB 1943 o - May 2 5u 1 D -R BB 1943 o - S 5 6u 0 S -R BB 1944 o - Ap 10 5u 0:30 - -R BB 1944 o - S 10 6u 0 S -R BB 1977 o - Jun 12 2 1 D -R BB 1977 1978 - O Su>=1 2 0 S -R BB 1978 1980 - Ap Su>=15 2 1 D -R BB 1979 o - S 30 2 0 S -R BB 1980 o - S 25 2 0 S -Z America/Barbados -3:58:29 - LMT 1911 Au 28 --4 BB A%sT 1944 --4 BB AST/-0330 1945 --4 BB A%sT -R BZ 1918 1941 - O Sa>=1 24 0:30 -0530 -R BZ 1919 1942 - F Sa>=8 24 0 CST -R BZ 1942 o - Jun 27 24 1 CWT -R BZ 1945 o - Au 14 23u 1 CPT -R BZ 1945 o - D 15 24 0 CST -R BZ 1947 1967 - O Sa>=1 24 0:30 -0530 -R BZ 1948 1968 - F Sa>=8 24 0 CST -R BZ 1973 o - D 5 0 1 CDT -R BZ 1974 o - F 9 0 0 CST -R BZ 1982 o - D 18 0 1 CDT -R BZ 1983 o - F 12 0 0 CST -Z America/Belize -5:52:48 - LMT 1912 Ap --6 BZ %s -R Be 1917 o - Ap 5 24 1 - -R Be 1917 o - S 30 24 0 - -R Be 1918 o - Ap 13 24 1 - -R Be 1918 o - S 15 24 0 S -R Be 1942 o - Ja 11 2 1 D -R Be 1942 o - O 18 2 0 S -R Be 1943 o - Mar 21 2 1 D -R Be 1943 o - O 31 2 0 S -R Be 1944 1945 - Mar Su>=8 2 1 D -R Be 1944 1945 - N Su>=1 2 0 S -R Be 1947 o - May Su>=15 2 1 D -R Be 1947 o - S Su>=8 2 0 S -R Be 1948 1952 - May Su>=22 2 1 D -R Be 1948 1952 - S Su>=1 2 0 S -R Be 1956 o - May Su>=22 2 1 D -R Be 1956 o - O lastSu 2 0 S -Z Atlantic/Bermuda -4:19:18 - LMT 1890 --4:19:18 Be BMT/BST 1930 Ja 1 2 --4 Be A%sT 1974 Ap 28 2 --4 C A%sT 1976 --4 u A%sT -R CR 1979 1980 - F lastSu 0 1 D -R CR 1979 1980 - Jun Su>=1 0 0 S -R CR 1991 1992 - Ja Sa>=15 0 1 D -R CR 1991 o - Jul 1 0 0 S -R CR 1992 o - Mar 15 0 0 S -Z America/Costa_Rica -5:36:13 - LMT 1890 --5:36:13 - SJMT 1921 Ja 15 --6 CR C%sT -R Q 1928 o - Jun 10 0 1 D -R Q 1928 o - O 10 0 0 S -R Q 1940 1942 - Jun Su>=1 0 1 D -R Q 1940 1942 - S Su>=1 0 0 S -R Q 1945 1946 - Jun Su>=1 0 1 D -R Q 1945 1946 - S Su>=1 0 0 S -R Q 1965 o - Jun 1 0 1 D -R Q 1965 o - S 30 0 0 S -R Q 1966 o - May 29 0 1 D -R Q 1966 o - O 2 0 0 S -R Q 1967 o - Ap 8 0 1 D -R Q 1967 1968 - S Su>=8 0 0 S -R Q 1968 o - Ap 14 0 1 D -R Q 1969 1977 - Ap lastSu 0 1 D -R Q 1969 1971 - O lastSu 0 0 S -R Q 1972 1974 - O 8 0 0 S -R Q 1975 1977 - O lastSu 0 0 S -R Q 1978 o - May 7 0 1 D -R Q 1978 1990 - O Su>=8 0 0 S -R Q 1979 1980 - Mar Su>=15 0 1 D -R Q 1981 1985 - May Su>=5 0 1 D -R Q 1986 1989 - Mar Su>=14 0 1 D -R Q 1990 1997 - Ap Su>=1 0 1 D -R Q 1991 1995 - O Su>=8 0s 0 S -R Q 1996 o - O 6 0s 0 S -R Q 1997 o - O 12 0s 0 S -R Q 1998 1999 - Mar lastSu 0s 1 D -R Q 1998 2003 - O lastSu 0s 0 S -R Q 2000 2003 - Ap Su>=1 0s 1 D -R Q 2004 o - Mar lastSu 0s 1 D -R Q 2006 2010 - O lastSu 0s 0 S -R Q 2007 o - Mar Su>=8 0s 1 D -R Q 2008 o - Mar Su>=15 0s 1 D -R Q 2009 2010 - Mar Su>=8 0s 1 D -R Q 2011 o - Mar Su>=15 0s 1 D -R Q 2011 o - N 13 0s 0 S -R Q 2012 o - Ap 1 0s 1 D -R Q 2012 ma - N Su>=1 0s 0 S -R Q 2013 ma - Mar Su>=8 0s 1 D -Z America/Havana -5:29:28 - LMT 1890 --5:29:36 - HMT 1925 Jul 19 12 --5 Q C%sT -R DO 1966 o - O 30 0 1 EDT -R DO 1967 o - F 28 0 0 EST -R DO 1969 1973 - O lastSu 0 0:30 -0430 -R DO 1970 o - F 21 0 0 EST -R DO 1971 o - Ja 20 0 0 EST -R DO 1972 1974 - Ja 21 0 0 EST -Z America/Santo_Domingo -4:39:36 - LMT 1890 --4:40 - SDMT 1933 Ap 1 12 --5 DO %s 1974 O 27 --4 - AST 2000 O 29 2 --5 u E%sT 2000 D 3 1 --4 - AST -R SV 1987 1988 - May Su>=1 0 1 D -R SV 1987 1988 - S lastSu 0 0 S -Z America/El_Salvador -5:56:48 - LMT 1921 --6 SV C%sT -R GT 1973 o - N 25 0 1 D -R GT 1974 o - F 24 0 0 S -R GT 1983 o - May 21 0 1 D -R GT 1983 o - S 22 0 0 S -R GT 1991 o - Mar 23 0 1 D -R GT 1991 o - S 7 0 0 S -R GT 2006 o - Ap 30 0 1 D -R GT 2006 o - O 1 0 0 S -Z America/Guatemala -6:2:4 - LMT 1918 O 5 --6 GT C%sT -R HT 1983 o - May 8 0 1 D -R HT 1984 1987 - Ap lastSu 0 1 D -R HT 1983 1987 - O lastSu 0 0 S -R HT 1988 1997 - Ap Su>=1 1s 1 D -R HT 1988 1997 - O lastSu 1s 0 S -R HT 2005 2006 - Ap Su>=1 0 1 D -R HT 2005 2006 - O lastSu 0 0 S -R HT 2012 2015 - Mar Su>=8 2 1 D -R HT 2012 2015 - N Su>=1 2 0 S -R HT 2017 ma - Mar Su>=8 2 1 D -R HT 2017 ma - N Su>=1 2 0 S -Z America/Port-au-Prince -4:49:20 - LMT 1890 --4:49 - PPMT 1917 Ja 24 12 --5 HT E%sT -R HN 1987 1988 - May Su>=1 0 1 D -R HN 1987 1988 - S lastSu 0 0 S -R HN 2006 o - May Su>=1 0 1 D -R HN 2006 o - Au M>=1 0 0 S -Z America/Tegucigalpa -5:48:52 - LMT 1921 Ap --6 HN C%sT -Z America/Jamaica -5:7:10 - LMT 1890 --5:7:10 - KMT 1912 F --5 - EST 1974 --5 u E%sT 1984 --5 - EST -Z America/Martinique -4:4:20 - LMT 1890 --4:4:20 - FFMT 1911 May --4 - AST 1980 Ap 6 --4 1 ADT 1980 S 28 --4 - AST -R NI 1979 1980 - Mar Su>=16 0 1 D -R NI 1979 1980 - Jun M>=23 0 0 S -R NI 2005 o - Ap 10 0 1 D -R NI 2005 o - O Su>=1 0 0 S -R NI 2006 o - Ap 30 2 1 D -R NI 2006 o - O Su>=1 1 0 S -Z America/Managua -5:45:8 - LMT 1890 --5:45:12 - MMT 1934 Jun 23 --6 - CST 1973 May --5 - EST 1975 F 16 --6 NI C%sT 1992 Ja 1 4 --5 - EST 1992 S 24 --6 - CST 1993 --5 - EST 1997 --6 NI C%sT -Z America/Panama -5:18:8 - LMT 1890 --5:19:36 - CMT 1908 Ap 22 --5 - EST -L America/Panama America/Atikokan -L America/Panama America/Cayman -Z America/Puerto_Rico -4:24:25 - LMT 1899 Mar 28 12 --4 - AST 1942 May 3 --4 u A%sT 1946 --4 - AST -L America/Puerto_Rico America/Anguilla -L America/Puerto_Rico America/Antigua -L America/Puerto_Rico America/Aruba -L America/Puerto_Rico America/Curacao -L America/Puerto_Rico America/Blanc-Sablon -L America/Puerto_Rico America/Dominica -L America/Puerto_Rico America/Grenada -L America/Puerto_Rico America/Guadeloupe -L America/Puerto_Rico America/Kralendijk -L America/Puerto_Rico America/Lower_Princes -L America/Puerto_Rico America/Marigot -L America/Puerto_Rico America/Montserrat -L America/Puerto_Rico America/Port_of_Spain -L America/Puerto_Rico America/St_Barthelemy -L America/Puerto_Rico America/St_Kitts -L America/Puerto_Rico America/St_Lucia -L America/Puerto_Rico America/St_Thomas -L America/Puerto_Rico America/St_Vincent -L America/Puerto_Rico America/Tortola -Z America/Miquelon -3:44:40 - LMT 1911 May 15 --4 - AST 1980 May --3 - -03 1987 --3 C -03/-02 -Z America/Grand_Turk -4:44:32 - LMT 1890 --5:7:10 - KMT 1912 F --5 - EST 1979 --5 u E%sT 2015 Mar 8 2 --4 - AST 2018 Mar 11 3 --5 u E%sT -R A 1930 o - D 1 0 1 - -R A 1931 o - Ap 1 0 0 - -R A 1931 o - O 15 0 1 - -R A 1932 1940 - Mar 1 0 0 - -R A 1932 1939 - N 1 0 1 - -R A 1940 o - Jul 1 0 1 - -R A 1941 o - Jun 15 0 0 - -R A 1941 o - O 15 0 1 - -R A 1943 o - Au 1 0 0 - -R A 1943 o - O 15 0 1 - -R A 1946 o - Mar 1 0 0 - -R A 1946 o - O 1 0 1 - -R A 1963 o - O 1 0 0 - -R A 1963 o - D 15 0 1 - -R A 1964 1966 - Mar 1 0 0 - -R A 1964 1966 - O 15 0 1 - -R A 1967 o - Ap 2 0 0 - -R A 1967 1968 - O Su>=1 0 1 - -R A 1968 1969 - Ap Su>=1 0 0 - -R A 1974 o - Ja 23 0 1 - -R A 1974 o - May 1 0 0 - -R A 1988 o - D 1 0 1 - -R A 1989 1993 - Mar Su>=1 0 0 - -R A 1989 1992 - O Su>=15 0 1 - -R A 1999 o - O Su>=1 0 1 - -R A 2000 o - Mar 3 0 0 - -R A 2007 o - D 30 0 1 - -R A 2008 2009 - Mar Su>=15 0 0 - -R A 2008 o - O Su>=15 0 1 - -Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 A -03/-02 -Z America/Argentina/Cordoba -4:16:48 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1991 Mar 3 --4 - -04 1991 O 20 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 A -03/-02 -Z America/Argentina/Salta -4:21:40 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1991 Mar 3 --4 - -04 1991 O 20 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/Argentina/Tucuman -4:20:52 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1991 Mar 3 --4 - -04 1991 O 20 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 Jun --4 - -04 2004 Jun 13 --3 A -03/-02 -Z America/Argentina/La_Rioja -4:27:24 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1991 Mar --4 - -04 1991 May 7 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 Jun --4 - -04 2004 Jun 20 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/Argentina/San_Juan -4:34:4 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1991 Mar --4 - -04 1991 May 7 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 May 31 --4 - -04 2004 Jul 25 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/Argentina/Jujuy -4:21:12 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1990 Mar 4 --4 - -04 1990 O 28 --4 1 -03 1991 Mar 17 --4 - -04 1991 O 6 --3 1 -02 1992 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/Argentina/Catamarca -4:23:8 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1991 Mar 3 --4 - -04 1991 O 20 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 Jun --4 - -04 2004 Jun 20 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/Argentina/Mendoza -4:35:16 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1990 Mar 4 --4 - -04 1990 O 15 --4 1 -03 1991 Mar --4 - -04 1991 O 15 --4 1 -03 1992 Mar --4 - -04 1992 O 18 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 May 23 --4 - -04 2004 S 26 --3 A -03/-02 2008 O 18 --3 - -03 -R Sa 2008 2009 - Mar Su>=8 0 0 - -R Sa 2007 2008 - O Su>=8 0 1 - -Z America/Argentina/San_Luis -4:25:24 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1990 --3 1 -02 1990 Mar 14 --4 - -04 1990 O 15 --4 1 -03 1991 Mar --4 - -04 1991 Jun --3 - -03 1999 O 3 --4 1 -03 2000 Mar 3 --3 - -03 2004 May 31 --4 - -04 2004 Jul 25 --3 A -03/-02 2008 Ja 21 --4 Sa -04/-03 2009 O 11 --3 - -03 -Z America/Argentina/Rio_Gallegos -4:36:52 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 Jun --4 - -04 2004 Jun 20 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/Argentina/Ushuaia -4:33:12 - LMT 1894 O 31 --4:16:48 - CMT 1920 May --4 - -04 1930 D --4 A -04/-03 1969 O 5 --3 A -03/-02 1999 O 3 --4 A -04/-03 2000 Mar 3 --3 - -03 2004 May 30 --4 - -04 2004 Jun 20 --3 A -03/-02 2008 O 18 --3 - -03 -Z America/La_Paz -4:32:36 - LMT 1890 --4:32:36 - CMT 1931 O 15 --4:32:36 1 BST 1932 Mar 21 --4 - -04 -R B 1931 o - O 3 11 1 - -R B 1932 1933 - Ap 1 0 0 - -R B 1932 o - O 3 0 1 - -R B 1949 1952 - D 1 0 1 - -R B 1950 o - Ap 16 1 0 - -R B 1951 1952 - Ap 1 0 0 - -R B 1953 o - Mar 1 0 0 - -R B 1963 o - D 9 0 1 - -R B 1964 o - Mar 1 0 0 - -R B 1965 o - Ja 31 0 1 - -R B 1965 o - Mar 31 0 0 - -R B 1965 o - D 1 0 1 - -R B 1966 1968 - Mar 1 0 0 - -R B 1966 1967 - N 1 0 1 - -R B 1985 o - N 2 0 1 - -R B 1986 o - Mar 15 0 0 - -R B 1986 o - O 25 0 1 - -R B 1987 o - F 14 0 0 - -R B 1987 o - O 25 0 1 - -R B 1988 o - F 7 0 0 - -R B 1988 o - O 16 0 1 - -R B 1989 o - Ja 29 0 0 - -R B 1989 o - O 15 0 1 - -R B 1990 o - F 11 0 0 - -R B 1990 o - O 21 0 1 - -R B 1991 o - F 17 0 0 - -R B 1991 o - O 20 0 1 - -R B 1992 o - F 9 0 0 - -R B 1992 o - O 25 0 1 - -R B 1993 o - Ja 31 0 0 - -R B 1993 1995 - O Su>=11 0 1 - -R B 1994 1995 - F Su>=15 0 0 - -R B 1996 o - F 11 0 0 - -R B 1996 o - O 6 0 1 - -R B 1997 o - F 16 0 0 - -R B 1997 o - O 6 0 1 - -R B 1998 o - Mar 1 0 0 - -R B 1998 o - O 11 0 1 - -R B 1999 o - F 21 0 0 - -R B 1999 o - O 3 0 1 - -R B 2000 o - F 27 0 0 - -R B 2000 2001 - O Su>=8 0 1 - -R B 2001 2006 - F Su>=15 0 0 - -R B 2002 o - N 3 0 1 - -R B 2003 o - O 19 0 1 - -R B 2004 o - N 2 0 1 - -R B 2005 o - O 16 0 1 - -R B 2006 o - N 5 0 1 - -R B 2007 o - F 25 0 0 - -R B 2007 o - O Su>=8 0 1 - -R B 2008 2017 - O Su>=15 0 1 - -R B 2008 2011 - F Su>=15 0 0 - -R B 2012 o - F Su>=22 0 0 - -R B 2013 2014 - F Su>=15 0 0 - -R B 2015 o - F Su>=22 0 0 - -R B 2016 2019 - F Su>=15 0 0 - -R B 2018 o - N Su>=1 0 1 - -Z America/Noronha -2:9:40 - LMT 1914 --2 B -02/-01 1990 S 17 --2 - -02 1999 S 30 --2 B -02/-01 2000 O 15 --2 - -02 2001 S 13 --2 B -02/-01 2002 O --2 - -02 -Z America/Belem -3:13:56 - LMT 1914 --3 B -03/-02 1988 S 12 --3 - -03 -Z America/Santarem -3:38:48 - LMT 1914 --4 B -04/-03 1988 S 12 --4 - -04 2008 Jun 24 --3 - -03 -Z America/Fortaleza -2:34 - LMT 1914 --3 B -03/-02 1990 S 17 --3 - -03 1999 S 30 --3 B -03/-02 2000 O 22 --3 - -03 2001 S 13 --3 B -03/-02 2002 O --3 - -03 -Z America/Recife -2:19:36 - LMT 1914 --3 B -03/-02 1990 S 17 --3 - -03 1999 S 30 --3 B -03/-02 2000 O 15 --3 - -03 2001 S 13 --3 B -03/-02 2002 O --3 - -03 -Z America/Araguaina -3:12:48 - LMT 1914 --3 B -03/-02 1990 S 17 --3 - -03 1995 S 14 --3 B -03/-02 2003 S 24 --3 - -03 2012 O 21 --3 B -03/-02 2013 S --3 - -03 -Z America/Maceio -2:22:52 - LMT 1914 --3 B -03/-02 1990 S 17 --3 - -03 1995 O 13 --3 B -03/-02 1996 S 4 --3 - -03 1999 S 30 --3 B -03/-02 2000 O 22 --3 - -03 2001 S 13 --3 B -03/-02 2002 O --3 - -03 -Z America/Bahia -2:34:4 - LMT 1914 --3 B -03/-02 2003 S 24 --3 - -03 2011 O 16 --3 B -03/-02 2012 O 21 --3 - -03 -Z America/Sao_Paulo -3:6:28 - LMT 1914 --3 B -03/-02 1963 O 23 --3 1 -02 1964 --3 B -03/-02 -Z America/Campo_Grande -3:38:28 - LMT 1914 --4 B -04/-03 -Z America/Cuiaba -3:44:20 - LMT 1914 --4 B -04/-03 2003 S 24 --4 - -04 2004 O --4 B -04/-03 -Z America/Porto_Velho -4:15:36 - LMT 1914 --4 B -04/-03 1988 S 12 --4 - -04 -Z America/Boa_Vista -4:2:40 - LMT 1914 --4 B -04/-03 1988 S 12 --4 - -04 1999 S 30 --4 B -04/-03 2000 O 15 --4 - -04 -Z America/Manaus -4:0:4 - LMT 1914 --4 B -04/-03 1988 S 12 --4 - -04 1993 S 28 --4 B -04/-03 1994 S 22 --4 - -04 -Z America/Eirunepe -4:39:28 - LMT 1914 --5 B -05/-04 1988 S 12 --5 - -05 1993 S 28 --5 B -05/-04 1994 S 22 --5 - -05 2008 Jun 24 --4 - -04 2013 N 10 --5 - -05 -Z America/Rio_Branco -4:31:12 - LMT 1914 --5 B -05/-04 1988 S 12 --5 - -05 2008 Jun 24 --4 - -04 2013 N 10 --5 - -05 -R x 1927 1931 - S 1 0 1 - -R x 1928 1932 - Ap 1 0 0 - -R x 1968 o - N 3 4u 1 - -R x 1969 o - Mar 30 3u 0 - -R x 1969 o - N 23 4u 1 - -R x 1970 o - Mar 29 3u 0 - -R x 1971 o - Mar 14 3u 0 - -R x 1970 1972 - O Su>=9 4u 1 - -R x 1972 1986 - Mar Su>=9 3u 0 - -R x 1973 o - S 30 4u 1 - -R x 1974 1987 - O Su>=9 4u 1 - -R x 1987 o - Ap 12 3u 0 - -R x 1988 1990 - Mar Su>=9 3u 0 - -R x 1988 1989 - O Su>=9 4u 1 - -R x 1990 o - S 16 4u 1 - -R x 1991 1996 - Mar Su>=9 3u 0 - -R x 1991 1997 - O Su>=9 4u 1 - -R x 1997 o - Mar 30 3u 0 - -R x 1998 o - Mar Su>=9 3u 0 - -R x 1998 o - S 27 4u 1 - -R x 1999 o - Ap 4 3u 0 - -R x 1999 2010 - O Su>=9 4u 1 - -R x 2000 2007 - Mar Su>=9 3u 0 - -R x 2008 o - Mar 30 3u 0 - -R x 2009 o - Mar Su>=9 3u 0 - -R x 2010 o - Ap Su>=1 3u 0 - -R x 2011 o - May Su>=2 3u 0 - -R x 2011 o - Au Su>=16 4u 1 - -R x 2012 2014 - Ap Su>=23 3u 0 - -R x 2012 2014 - S Su>=2 4u 1 - -R x 2016 2018 - May Su>=9 3u 0 - -R x 2016 2018 - Au Su>=9 4u 1 - -R x 2019 ma - Ap Su>=2 3u 0 - -R x 2019 ma - S Su>=2 4u 1 - -Z America/Santiago -4:42:45 - LMT 1890 --4:42:45 - SMT 1910 Ja 10 --5 - -05 1916 Jul --4:42:45 - SMT 1918 S 10 --4 - -04 1919 Jul --4:42:45 - SMT 1927 S --5 x -05/-04 1932 S --4 - -04 1942 Jun --5 - -05 1942 Au --4 - -04 1946 Jul 15 --4 1 -03 1946 S --4 - -04 1947 Ap --5 - -05 1947 May 21 23 --4 x -04/-03 -Z America/Punta_Arenas -4:43:40 - LMT 1890 --4:42:45 - SMT 1910 Ja 10 --5 - -05 1916 Jul --4:42:45 - SMT 1918 S 10 --4 - -04 1919 Jul --4:42:45 - SMT 1927 S --5 x -05/-04 1932 S --4 - -04 1942 Jun --5 - -05 1942 Au --4 - -04 1947 Ap --5 - -05 1947 May 21 23 --4 x -04/-03 2016 D 4 --3 - -03 -Z Pacific/Easter -7:17:28 - LMT 1890 --7:17:28 - EMT 1932 S --7 x -07/-06 1982 Mar 14 3u --6 x -06/-05 -Z Antarctica/Palmer 0 - -00 1965 --4 A -04/-03 1969 O 5 --3 A -03/-02 1982 May --4 x -04/-03 2016 D 4 --3 - -03 -R CO 1992 o - May 3 0 1 - -R CO 1993 o - Ap 4 0 0 - -Z America/Bogota -4:56:16 - LMT 1884 Mar 13 --4:56:16 - BMT 1914 N 23 --5 CO -05/-04 -R EC 1992 o - N 28 0 1 - -R EC 1993 o - F 5 0 0 - -Z America/Guayaquil -5:19:20 - LMT 1890 --5:14 - QMT 1931 --5 EC -05/-04 -Z Pacific/Galapagos -5:58:24 - LMT 1931 --5 - -05 1986 --6 EC -06/-05 -R FK 1937 1938 - S lastSu 0 1 - -R FK 1938 1942 - Mar Su>=19 0 0 - -R FK 1939 o - O 1 0 1 - -R FK 1940 1942 - S lastSu 0 1 - -R FK 1943 o - Ja 1 0 0 - -R FK 1983 o - S lastSu 0 1 - -R FK 1984 1985 - Ap lastSu 0 0 - -R FK 1984 o - S 16 0 1 - -R FK 1985 2000 - S Su>=9 0 1 - -R FK 1986 2000 - Ap Su>=16 0 0 - -R FK 2001 2010 - Ap Su>=15 2 0 - -R FK 2001 2010 - S Su>=1 2 1 - -Z Atlantic/Stanley -3:51:24 - LMT 1890 --3:51:24 - SMT 1912 Mar 12 --4 FK -04/-03 1983 May --3 FK -03/-02 1985 S 15 --4 FK -04/-03 2010 S 5 2 --3 - -03 -Z America/Cayenne -3:29:20 - LMT 1911 Jul --4 - -04 1967 O --3 - -03 -Z America/Guyana -3:52:39 - LMT 1911 Au --4 - -04 1915 Mar --3:45 - -0345 1975 Au --3 - -03 1992 Mar 29 1 --4 - -04 -R y 1975 1988 - O 1 0 1 - -R y 1975 1978 - Mar 1 0 0 - -R y 1979 1991 - Ap 1 0 0 - -R y 1989 o - O 22 0 1 - -R y 1990 o - O 1 0 1 - -R y 1991 o - O 6 0 1 - -R y 1992 o - Mar 1 0 0 - -R y 1992 o - O 5 0 1 - -R y 1993 o - Mar 31 0 0 - -R y 1993 1995 - O 1 0 1 - -R y 1994 1995 - F lastSu 0 0 - -R y 1996 o - Mar 1 0 0 - -R y 1996 2001 - O Su>=1 0 1 - -R y 1997 o - F lastSu 0 0 - -R y 1998 2001 - Mar Su>=1 0 0 - -R y 2002 2004 - Ap Su>=1 0 0 - -R y 2002 2003 - S Su>=1 0 1 - -R y 2004 2009 - O Su>=15 0 1 - -R y 2005 2009 - Mar Su>=8 0 0 - -R y 2010 ma - O Su>=1 0 1 - -R y 2010 2012 - Ap Su>=8 0 0 - -R y 2013 ma - Mar Su>=22 0 0 - -Z America/Asuncion -3:50:40 - LMT 1890 --3:50:40 - AMT 1931 O 10 --4 - -04 1972 O --3 - -03 1974 Ap --4 y -04/-03 -R PE 1938 o - Ja 1 0 1 - -R PE 1938 o - Ap 1 0 0 - -R PE 1938 1939 - S lastSu 0 1 - -R PE 1939 1940 - Mar Su>=24 0 0 - -R PE 1986 1987 - Ja 1 0 1 - -R PE 1986 1987 - Ap 1 0 0 - -R PE 1990 o - Ja 1 0 1 - -R PE 1990 o - Ap 1 0 0 - -R PE 1994 o - Ja 1 0 1 - -R PE 1994 o - Ap 1 0 0 - -Z America/Lima -5:8:12 - LMT 1890 --5:8:36 - LMT 1908 Jul 28 --5 PE -05/-04 -Z Atlantic/South_Georgia -2:26:8 - LMT 1890 --2 - -02 -Z America/Paramaribo -3:40:40 - LMT 1911 --3:40:52 - PMT 1935 --3:40:36 - PMT 1945 O --3:30 - -0330 1984 O --3 - -03 -R U 1923 1925 - O 1 0 0:30 - -R U 1924 1926 - Ap 1 0 0 - -R U 1933 1938 - O lastSu 0 0:30 - -R U 1934 1941 - Mar lastSa 24 0 - -R U 1939 o - O 1 0 0:30 - -R U 1940 o - O 27 0 0:30 - -R U 1941 o - Au 1 0 0:30 - -R U 1942 o - D 14 0 0:30 - -R U 1943 o - Mar 14 0 0 - -R U 1959 o - May 24 0 0:30 - -R U 1959 o - N 15 0 0 - -R U 1960 o - Ja 17 0 1 - -R U 1960 o - Mar 6 0 0 - -R U 1965 o - Ap 4 0 1 - -R U 1965 o - S 26 0 0 - -R U 1968 o - May 27 0 0:30 - -R U 1968 o - D 1 0 0 - -R U 1970 o - Ap 25 0 1 - -R U 1970 o - Jun 14 0 0 - -R U 1972 o - Ap 23 0 1 - -R U 1972 o - Jul 16 0 0 - -R U 1974 o - Ja 13 0 1:30 - -R U 1974 o - Mar 10 0 0:30 - -R U 1974 o - S 1 0 0 - -R U 1974 o - D 22 0 1 - -R U 1975 o - Mar 30 0 0 - -R U 1976 o - D 19 0 1 - -R U 1977 o - Mar 6 0 0 - -R U 1977 o - D 4 0 1 - -R U 1978 1979 - Mar Su>=1 0 0 - -R U 1978 o - D 17 0 1 - -R U 1979 o - Ap 29 0 1 - -R U 1980 o - Mar 16 0 0 - -R U 1987 o - D 14 0 1 - -R U 1988 o - F 28 0 0 - -R U 1988 o - D 11 0 1 - -R U 1989 o - Mar 5 0 0 - -R U 1989 o - O 29 0 1 - -R U 1990 o - F 25 0 0 - -R U 1990 1991 - O Su>=21 0 1 - -R U 1991 1992 - Mar Su>=1 0 0 - -R U 1992 o - O 18 0 1 - -R U 1993 o - F 28 0 0 - -R U 2004 o - S 19 0 1 - -R U 2005 o - Mar 27 2 0 - -R U 2005 o - O 9 2 1 - -R U 2006 2015 - Mar Su>=8 2 0 - -R U 2006 2014 - O Su>=1 2 1 - -Z America/Montevideo -3:44:51 - LMT 1908 Jun 10 --3:44:51 - MMT 1920 May --4 - -04 1923 O --3:30 U -0330/-03 1942 D 14 --3 U -03/-0230 1960 --3 U -03/-02 1968 --3 U -03/-0230 1970 --3 U -03/-02 1974 --3 U -03/-0130 1974 Mar 10 --3 U -03/-0230 1974 D 22 --3 U -03/-02 -Z America/Caracas -4:27:44 - LMT 1890 --4:27:40 - CMT 1912 F 12 --4:30 - -0430 1965 --4 - -04 2007 D 9 3 --4:30 - -0430 2016 May 1 2:30 --4 - -04 -Z Etc/GMT 0 - GMT -Z Etc/UTC 0 - UTC -L Etc/GMT GMT -L Etc/UTC Etc/Universal -L Etc/UTC Etc/Zulu -L Etc/GMT Etc/Greenwich -L Etc/GMT Etc/GMT-0 -L Etc/GMT Etc/GMT+0 -L Etc/GMT Etc/GMT0 -Z Etc/GMT-14 14 - +14 -Z Etc/GMT-13 13 - +13 -Z Etc/GMT-12 12 - +12 -Z Etc/GMT-11 11 - +11 -Z Etc/GMT-10 10 - +10 -Z Etc/GMT-9 9 - +09 -Z Etc/GMT-8 8 - +08 -Z Etc/GMT-7 7 - +07 -Z Etc/GMT-6 6 - +06 -Z Etc/GMT-5 5 - +05 -Z Etc/GMT-4 4 - +04 -Z Etc/GMT-3 3 - +03 -Z Etc/GMT-2 2 - +02 -Z Etc/GMT-1 1 - +01 -Z Etc/GMT+1 -1 - -01 -Z Etc/GMT+2 -2 - -02 -Z Etc/GMT+3 -3 - -03 -Z Etc/GMT+4 -4 - -04 -Z Etc/GMT+5 -5 - -05 -Z Etc/GMT+6 -6 - -06 -Z Etc/GMT+7 -7 - -07 -Z Etc/GMT+8 -8 - -08 -Z Etc/GMT+9 -9 - -09 -Z Etc/GMT+10 -10 - -10 -Z Etc/GMT+11 -11 - -11 -Z Etc/GMT+12 -12 - -12 -Z Factory 0 - -00 -L Africa/Nairobi Africa/Asmera -L Africa/Abidjan Africa/Timbuktu -L America/Argentina/Catamarca America/Argentina/ComodRivadavia -L America/Adak America/Atka -L America/Argentina/Buenos_Aires America/Buenos_Aires -L America/Argentina/Catamarca America/Catamarca -L America/Panama America/Coral_Harbour -L America/Argentina/Cordoba America/Cordoba -L America/Tijuana America/Ensenada -L America/Indiana/Indianapolis America/Fort_Wayne -L America/Nuuk America/Godthab -L America/Indiana/Indianapolis America/Indianapolis -L America/Argentina/Jujuy America/Jujuy -L America/Indiana/Knox America/Knox_IN -L America/Kentucky/Louisville America/Louisville -L America/Argentina/Mendoza America/Mendoza -L America/Toronto America/Montreal -L America/Rio_Branco America/Porto_Acre -L America/Argentina/Cordoba America/Rosario -L America/Tijuana America/Santa_Isabel -L America/Denver America/Shiprock -L America/Puerto_Rico America/Virgin -L Pacific/Auckland Antarctica/South_Pole -L Asia/Ashgabat Asia/Ashkhabad -L Asia/Kolkata Asia/Calcutta -L Asia/Shanghai Asia/Chongqing -L Asia/Shanghai Asia/Chungking -L Asia/Dhaka Asia/Dacca -L Asia/Shanghai Asia/Harbin -L Asia/Urumqi Asia/Kashgar -L Asia/Kathmandu Asia/Katmandu -L Asia/Macau Asia/Macao -L Asia/Yangon Asia/Rangoon -L Asia/Ho_Chi_Minh Asia/Saigon -L Asia/Jerusalem Asia/Tel_Aviv -L Asia/Thimphu Asia/Thimbu -L Asia/Makassar Asia/Ujung_Pandang -L Asia/Ulaanbaatar Asia/Ulan_Bator -L Atlantic/Faroe Atlantic/Faeroe -L Europe/Oslo Atlantic/Jan_Mayen -L Australia/Sydney Australia/ACT -L Australia/Sydney Australia/Canberra -L Australia/Hobart Australia/Currie -L Australia/Lord_Howe Australia/LHI -L Australia/Sydney Australia/NSW -L Australia/Darwin Australia/North -L Australia/Brisbane Australia/Queensland -L Australia/Adelaide Australia/South -L Australia/Hobart Australia/Tasmania -L Australia/Melbourne Australia/Victoria -L Australia/Perth Australia/West -L Australia/Broken_Hill Australia/Yancowinna -L America/Rio_Branco Brazil/Acre -L America/Noronha Brazil/DeNoronha -L America/Sao_Paulo Brazil/East -L America/Manaus Brazil/West -L America/Halifax Canada/Atlantic -L America/Winnipeg Canada/Central -L America/Toronto Canada/Eastern -L America/Edmonton Canada/Mountain -L America/St_Johns Canada/Newfoundland -L America/Vancouver Canada/Pacific -L America/Regina Canada/Saskatchewan -L America/Whitehorse Canada/Yukon -L America/Santiago Chile/Continental -L Pacific/Easter Chile/EasterIsland -L America/Havana Cuba -L Africa/Cairo Egypt -L Europe/Dublin Eire -L Etc/UTC Etc/UCT -L Europe/London Europe/Belfast -L Europe/Chisinau Europe/Tiraspol -L Europe/London GB -L Europe/London GB-Eire -L Etc/GMT GMT+0 -L Etc/GMT GMT-0 -L Etc/GMT GMT0 -L Etc/GMT Greenwich -L Asia/Hong_Kong Hongkong -L Atlantic/Reykjavik Iceland -L Asia/Tehran Iran -L Asia/Jerusalem Israel -L America/Jamaica Jamaica -L Asia/Tokyo Japan -L Pacific/Kwajalein Kwajalein -L Africa/Tripoli Libya -L America/Tijuana Mexico/BajaNorte -L America/Mazatlan Mexico/BajaSur -L America/Mexico_City Mexico/General -L Pacific/Auckland NZ -L Pacific/Chatham NZ-CHAT -L America/Denver Navajo -L Asia/Shanghai PRC -L Pacific/Kanton Pacific/Enderbury -L Pacific/Honolulu Pacific/Johnston -L Pacific/Pohnpei Pacific/Ponape -L Pacific/Pago_Pago Pacific/Samoa -L Pacific/Chuuk Pacific/Truk -L Pacific/Chuuk Pacific/Yap -L Europe/Warsaw Poland -L Europe/Lisbon Portugal -L Asia/Taipei ROC -L Asia/Seoul ROK -L Asia/Singapore Singapore -L Europe/Istanbul Turkey -L Etc/UTC UCT -L America/Anchorage US/Alaska -L America/Adak US/Aleutian -L America/Phoenix US/Arizona -L America/Chicago US/Central -L America/Indiana/Indianapolis US/East-Indiana -L America/New_York US/Eastern -L Pacific/Honolulu US/Hawaii -L America/Indiana/Knox US/Indiana-Starke -L America/Detroit US/Michigan -L America/Denver US/Mountain -L America/Los_Angeles US/Pacific -L Pacific/Pago_Pago US/Samoa -L Etc/UTC UTC -L Etc/UTC Universal -L Europe/Moscow W-SU -L Etc/UTC Zulu diff --git a/telegramer/include/pytz/zoneinfo/zone.tab b/telegramer/include/pytz/zoneinfo/zone.tab deleted file mode 100644 index 086458f..0000000 --- a/telegramer/include/pytz/zoneinfo/zone.tab +++ /dev/null @@ -1,454 +0,0 @@ -# tzdb timezone descriptions (deprecated version) -# -# This file is in the public domain, so clarified as of -# 2009-05-17 by Arthur David Olson. -# -# From Paul Eggert (2021-09-20): -# This file is intended as a backward-compatibility aid for older programs. -# New programs should use zone1970.tab. This file is like zone1970.tab (see -# zone1970.tab's comments), but with the following additional restrictions: -# -# 1. This file contains only ASCII characters. -# 2. The first data column contains exactly one country code. -# -# Because of (2), each row stands for an area that is the intersection -# of a region identified by a country code and of a timezone where civil -# clocks have agreed since 1970; this is a narrower definition than -# that of zone1970.tab. -# -# Unlike zone1970.tab, a row's third column can be a Link from -# 'backward' instead of a Zone. -# -# This table is intended as an aid for users, to help them select timezones -# appropriate for their practical needs. It is not intended to take or -# endorse any position on legal or territorial claims. -# -#country- -#code coordinates TZ comments -AD +4230+00131 Europe/Andorra -AE +2518+05518 Asia/Dubai -AF +3431+06912 Asia/Kabul -AG +1703-06148 America/Antigua -AI +1812-06304 America/Anguilla -AL +4120+01950 Europe/Tirane -AM +4011+04430 Asia/Yerevan -AO -0848+01314 Africa/Luanda -AQ -7750+16636 Antarctica/McMurdo New Zealand time - McMurdo, South Pole -AQ -6617+11031 Antarctica/Casey Casey -AQ -6835+07758 Antarctica/Davis Davis -AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville -AQ -6736+06253 Antarctica/Mawson Mawson -AQ -6448-06406 Antarctica/Palmer Palmer -AQ -6734-06808 Antarctica/Rothera Rothera -AQ -690022+0393524 Antarctica/Syowa Syowa -AQ -720041+0023206 Antarctica/Troll Troll -AQ -7824+10654 Antarctica/Vostok Vostok -AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) -AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) -AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) -AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) -AR -2649-06513 America/Argentina/Tucuman Tucuman (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) -AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) -AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) -AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) -AR -3319-06621 America/Argentina/San_Luis San Luis (SL) -AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) -AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) -AS -1416-17042 Pacific/Pago_Pago -AT +4813+01620 Europe/Vienna -AU -3133+15905 Australia/Lord_Howe Lord Howe Island -AU -5430+15857 Antarctica/Macquarie Macquarie Island -AU -4253+14719 Australia/Hobart Tasmania -AU -3749+14458 Australia/Melbourne Victoria -AU -3352+15113 Australia/Sydney New South Wales (most areas) -AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) -AU -2728+15302 Australia/Brisbane Queensland (most areas) -AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) -AU -3455+13835 Australia/Adelaide South Australia -AU -1228+13050 Australia/Darwin Northern Territory -AU -3157+11551 Australia/Perth Western Australia (most areas) -AU -3143+12852 Australia/Eucla Western Australia (Eucla) -AW +1230-06958 America/Aruba -AX +6006+01957 Europe/Mariehamn -AZ +4023+04951 Asia/Baku -BA +4352+01825 Europe/Sarajevo -BB +1306-05937 America/Barbados -BD +2343+09025 Asia/Dhaka -BE +5050+00420 Europe/Brussels -BF +1222-00131 Africa/Ouagadougou -BG +4241+02319 Europe/Sofia -BH +2623+05035 Asia/Bahrain -BI -0323+02922 Africa/Bujumbura -BJ +0629+00237 Africa/Porto-Novo -BL +1753-06251 America/St_Barthelemy -BM +3217-06446 Atlantic/Bermuda -BN +0456+11455 Asia/Brunei -BO -1630-06809 America/La_Paz -BQ +120903-0681636 America/Kralendijk -BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Para (east); Amapa -BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) -BR -0803-03454 America/Recife Pernambuco -BR -0712-04812 America/Araguaina Tocantins -BR -0940-03543 America/Maceio Alagoas, Sergipe -BR -1259-03831 America/Bahia Bahia -BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) -BR -2027-05437 America/Campo_Grande Mato Grosso do Sul -BR -1535-05605 America/Cuiaba Mato Grosso -BR -0226-05452 America/Santarem Para (west) -BR -0846-06354 America/Porto_Velho Rondonia -BR +0249-06040 America/Boa_Vista Roraima -BR -0308-06001 America/Manaus Amazonas (east) -BR -0640-06952 America/Eirunepe Amazonas (west) -BR -0958-06748 America/Rio_Branco Acre -BS +2505-07721 America/Nassau -BT +2728+08939 Asia/Thimphu -BW -2439+02555 Africa/Gaborone -BY +5354+02734 Europe/Minsk -BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) -CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE -CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) -CA +4606-06447 America/Moncton Atlantic - New Brunswick -CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA +5125-05707 America/Blanc-Sablon AST - QC (Lower North Shore) -CA +4339-07923 America/Toronto Eastern - ON, QC (most areas) -CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) -CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) -CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) -CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) -CA +484531-0913718 America/Atikokan EST - ON (Atikokan); NU (Coral H) -CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba -CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) -CA +744144-0944945 America/Resolute Central - NU (Resolute) -CA +624900-0920459 America/Rankin_Inlet Central - NU (central) -CA +5024-10439 America/Regina CST - SK (most areas) -CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) -CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) -CA +6227-11421 America/Yellowknife Mountain - NT (central) -CA +682059-1334300 America/Inuvik Mountain - NT (west) -CA +4906-11631 America/Creston MST - BC (Creston) -CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) -CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) -CA +6043-13503 America/Whitehorse MST - Yukon (east) -CA +6404-13925 America/Dawson MST - Yukon (west) -CA +4916-12307 America/Vancouver Pacific - BC (most areas) -CC -1210+09655 Indian/Cocos -CD -0418+01518 Africa/Kinshasa Dem. Rep. of Congo (west) -CD -1140+02728 Africa/Lubumbashi Dem. Rep. of Congo (east) -CF +0422+01835 Africa/Bangui -CG -0416+01517 Africa/Brazzaville -CH +4723+00832 Europe/Zurich -CI +0519-00402 Africa/Abidjan -CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago Chile (most areas) -CL -5309-07055 America/Punta_Arenas Region of Magallanes -CL -2709-10926 Pacific/Easter Easter Island -CM +0403+00942 Africa/Douala -CN +3114+12128 Asia/Shanghai Beijing Time -CN +4348+08735 Asia/Urumqi Xinjiang Time -CO +0436-07405 America/Bogota -CR +0956-08405 America/Costa_Rica -CU +2308-08222 America/Havana -CV +1455-02331 Atlantic/Cape_Verde -CW +1211-06900 America/Curacao -CX -1025+10543 Indian/Christmas -CY +3510+03322 Asia/Nicosia Cyprus (most areas) -CY +3507+03357 Asia/Famagusta Northern Cyprus -CZ +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin Germany (most areas) -DE +4742+00841 Europe/Busingen Busingen -DJ +1136+04309 Africa/Djibouti -DK +5540+01235 Europe/Copenhagen -DM +1518-06124 America/Dominica -DO +1828-06954 America/Santo_Domingo -DZ +3647+00303 Africa/Algiers -EC -0210-07950 America/Guayaquil Ecuador (mainland) -EC -0054-08936 Pacific/Galapagos Galapagos Islands -EE +5925+02445 Europe/Tallinn -EG +3003+03115 Africa/Cairo -EH +2709-01312 Africa/El_Aaiun -ER +1520+03853 Africa/Asmara -ES +4024-00341 Europe/Madrid Spain (mainland) -ES +3553-00519 Africa/Ceuta Ceuta, Melilla -ES +2806-01524 Atlantic/Canary Canary Islands -ET +0902+03842 Africa/Addis_Ababa -FI +6010+02458 Europe/Helsinki -FJ -1808+17825 Pacific/Fiji -FK -5142-05751 Atlantic/Stanley -FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap -FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape -FM +0519+16259 Pacific/Kosrae Kosrae -FO +6201-00646 Atlantic/Faroe -FR +4852+00220 Europe/Paris -GA +0023+00927 Africa/Libreville -GB +513030-0000731 Europe/London -GD +1203-06145 America/Grenada -GE +4143+04449 Asia/Tbilisi -GF +0456-05220 America/Cayenne -GG +492717-0023210 Europe/Guernsey -GH +0533-00013 Africa/Accra -GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Nuuk Greenland (most areas) -GL +7646-01840 America/Danmarkshavn National Park (east coast) -GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit -GL +7634-06847 America/Thule Thule/Pituffik -GM +1328-01639 Africa/Banjul -GN +0931-01343 Africa/Conakry -GP +1614-06132 America/Guadeloupe -GQ +0345+00847 Africa/Malabo -GR +3758+02343 Europe/Athens -GS -5416-03632 Atlantic/South_Georgia -GT +1438-09031 America/Guatemala -GU +1328+14445 Pacific/Guam -GW +1151-01535 Africa/Bissau -GY +0648-05810 America/Guyana -HK +2217+11409 Asia/Hong_Kong -HN +1406-08713 America/Tegucigalpa -HR +4548+01558 Europe/Zagreb -HT +1832-07220 America/Port-au-Prince -HU +4730+01905 Europe/Budapest -ID -0610+10648 Asia/Jakarta Java, Sumatra -ID -0002+10920 Asia/Pontianak Borneo (west, central) -ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) -ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas -IE +5320-00615 Europe/Dublin -IL +314650+0351326 Asia/Jerusalem -IM +5409-00428 Europe/Isle_of_Man -IN +2232+08822 Asia/Kolkata -IO -0720+07225 Indian/Chagos -IQ +3321+04425 Asia/Baghdad -IR +3540+05126 Asia/Tehran -IS +6409-02151 Atlantic/Reykjavik -IT +4154+01229 Europe/Rome -JE +491101-0020624 Europe/Jersey -JM +175805-0764736 America/Jamaica -JO +3157+03556 Asia/Amman -JP +353916+1394441 Asia/Tokyo -KE -0117+03649 Africa/Nairobi -KG +4254+07436 Asia/Bishkek -KH +1133+10455 Asia/Phnom_Penh -KI +0125+17300 Pacific/Tarawa Gilbert Islands -KI -0247-17143 Pacific/Kanton Phoenix Islands -KI +0152-15720 Pacific/Kiritimati Line Islands -KM -1141+04316 Indian/Comoro -KN +1718-06243 America/St_Kitts -KP +3901+12545 Asia/Pyongyang -KR +3733+12658 Asia/Seoul -KW +2920+04759 Asia/Kuwait -KY +1918-08123 America/Cayman -KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) -KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda -KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay -KZ +5017+05710 Asia/Aqtobe Aqtobe/Aktobe -KZ +4431+05016 Asia/Aqtau Mangghystau/Mankistau -KZ +4707+05156 Asia/Atyrau Atyrau/Atirau/Gur'yev -KZ +5113+05121 Asia/Oral West Kazakhstan -LA +1758+10236 Asia/Vientiane -LB +3353+03530 Asia/Beirut -LC +1401-06100 America/St_Lucia -LI +4709+00931 Europe/Vaduz -LK +0656+07951 Asia/Colombo -LR +0618-01047 Africa/Monrovia -LS -2928+02730 Africa/Maseru -LT +5441+02519 Europe/Vilnius -LU +4936+00609 Europe/Luxembourg -LV +5657+02406 Europe/Riga -LY +3254+01311 Africa/Tripoli -MA +3339-00735 Africa/Casablanca -MC +4342+00723 Europe/Monaco -MD +4700+02850 Europe/Chisinau -ME +4226+01916 Europe/Podgorica -MF +1804-06305 America/Marigot -MG -1855+04731 Indian/Antananarivo -MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) -MH +0905+16720 Pacific/Kwajalein Kwajalein -MK +4159+02126 Europe/Skopje -ML +1239-00800 Africa/Bamako -MM +1647+09610 Asia/Yangon -MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) -MN +4801+09139 Asia/Hovd Bayan-Olgiy, Govi-Altai, Hovd, Uvs, Zavkhan -MN +4804+11430 Asia/Choibalsan Dornod, Sukhbaatar -MO +221150+1133230 Asia/Macau -MP +1512+14545 Pacific/Saipan -MQ +1436-06105 America/Martinique -MR +1806-01557 Africa/Nouakchott -MS +1643-06213 America/Montserrat -MT +3554+01431 Europe/Malta -MU -2010+05730 Indian/Mauritius -MV +0410+07330 Indian/Maldives -MW -1547+03500 Africa/Blantyre -MX +1924-09909 America/Mexico_City Central Time -MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo -MX +2058-08937 America/Merida Central Time - Campeche, Yucatan -MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo Leon, Tamaulipas (most areas) -MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo Leon, Tamaulipas (US border) -MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa -MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) -MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) -MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora -MX +3232-11701 America/Tijuana Pacific Time US - Baja California -MX +2048-10515 America/Bahia_Banderas Central Time - Bahia de Banderas -MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) -MY +0133+11020 Asia/Kuching Sabah, Sarawak -MZ -2558+03235 Africa/Maputo -NA -2234+01706 Africa/Windhoek -NC -2216+16627 Pacific/Noumea -NE +1331+00207 Africa/Niamey -NF -2903+16758 Pacific/Norfolk -NG +0627+00324 Africa/Lagos -NI +1209-08617 America/Managua -NL +5222+00454 Europe/Amsterdam -NO +5955+01045 Europe/Oslo -NP +2743+08519 Asia/Kathmandu -NR -0031+16655 Pacific/Nauru -NU -1901-16955 Pacific/Niue -NZ -3652+17446 Pacific/Auckland New Zealand (most areas) -NZ -4357-17633 Pacific/Chatham Chatham Islands -OM +2336+05835 Asia/Muscat -PA +0858-07932 America/Panama -PE -1203-07703 America/Lima -PF -1732-14934 Pacific/Tahiti Society Islands -PF -0900-13930 Pacific/Marquesas Marquesas Islands -PF -2308-13457 Pacific/Gambier Gambier Islands -PG -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas) -PG -0613+15534 Pacific/Bougainville Bougainville -PH +1435+12100 Asia/Manila -PK +2452+06703 Asia/Karachi -PL +5215+02100 Europe/Warsaw -PM +4703-05620 America/Miquelon -PN -2504-13005 Pacific/Pitcairn -PR +182806-0660622 America/Puerto_Rico -PS +3130+03428 Asia/Gaza Gaza Strip -PS +313200+0350542 Asia/Hebron West Bank -PT +3843-00908 Europe/Lisbon Portugal (mainland) -PT +3238-01654 Atlantic/Madeira Madeira Islands -PT +3744-02540 Atlantic/Azores Azores -PW +0720+13429 Pacific/Palau -PY -2516-05740 America/Asuncion -QA +2517+05132 Asia/Qatar -RE -2052+05528 Indian/Reunion -RO +4426+02606 Europe/Bucharest -RS +4450+02030 Europe/Belgrade -RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad -RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area -# The obsolescent zone.tab format cannot represent Europe/Simferopol well. -# Put it in RU section and list as UA. See "territorial claims" above. -# Programs should use zone1970.tab instead; see above. -UA +4457+03406 Europe/Simferopol Crimea -RU +5836+04939 Europe/Kirov MSK+00 - Kirov -RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd -RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan -RU +5134+04602 Europe/Saratov MSK+01 - Saratov -RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk -RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia -RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals -RU +5500+07324 Asia/Omsk MSK+03 - Omsk -RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk -RU +5322+08345 Asia/Barnaul MSK+04 - Altai -RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk -RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo -RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area -RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia -RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky -RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River -RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky -RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River -RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky -RU +5934+15048 Asia/Magadan MSK+08 - Magadan -RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is -RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka -RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea -RW -0157+03004 Africa/Kigali -SA +2438+04643 Asia/Riyadh -SB -0932+16012 Pacific/Guadalcanal -SC -0440+05528 Indian/Mahe -SD +1536+03232 Africa/Khartoum -SE +5920+01803 Europe/Stockholm -SG +0117+10351 Asia/Singapore -SH -1555-00542 Atlantic/St_Helena -SI +4603+01431 Europe/Ljubljana -SJ +7800+01600 Arctic/Longyearbyen -SK +4809+01707 Europe/Bratislava -SL +0830-01315 Africa/Freetown -SM +4355+01228 Europe/San_Marino -SN +1440-01726 Africa/Dakar -SO +0204+04522 Africa/Mogadishu -SR +0550-05510 America/Paramaribo -SS +0451+03137 Africa/Juba -ST +0020+00644 Africa/Sao_Tome -SV +1342-08912 America/El_Salvador -SX +180305-0630250 America/Lower_Princes -SY +3330+03618 Asia/Damascus -SZ -2618+03106 Africa/Mbabane -TC +2128-07108 America/Grand_Turk -TD +1207+01503 Africa/Ndjamena -TF -492110+0701303 Indian/Kerguelen -TG +0608+00113 Africa/Lome -TH +1345+10031 Asia/Bangkok -TJ +3835+06848 Asia/Dushanbe -TK -0922-17114 Pacific/Fakaofo -TL -0833+12535 Asia/Dili -TM +3757+05823 Asia/Ashgabat -TN +3648+01011 Africa/Tunis -TO -210800-1751200 Pacific/Tongatapu -TR +4101+02858 Europe/Istanbul -TT +1039-06131 America/Port_of_Spain -TV -0831+17913 Pacific/Funafuti -TW +2503+12130 Asia/Taipei -TZ -0648+03917 Africa/Dar_es_Salaam -UA +5026+03031 Europe/Kiev Ukraine (most areas) -UA +4837+02218 Europe/Uzhgorod Transcarpathia -UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk -UG +0019+03225 Africa/Kampala -UM +2813-17722 Pacific/Midway Midway Islands -UM +1917+16637 Pacific/Wake Wake Island -US +404251-0740023 America/New_York Eastern (most areas) -US +421953-0830245 America/Detroit Eastern - MI (most areas) -US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) -US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) -US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) -US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) -US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) -US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) -US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) -US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) -US +415100-0873900 America/Chicago Central (most areas) -US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) -US +411745-0863730 America/Indiana/Knox Central - IN (Starke) -US +450628-0873651 America/Menominee Central - MI (Wisconsin border) -US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) -US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) -US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) -US +394421-1045903 America/Denver Mountain (most areas) -US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US +332654-1120424 America/Phoenix MST - Arizona (except Navajo) -US +340308-1181434 America/Los_Angeles Pacific -US +611305-1495401 America/Anchorage Alaska (most areas) -US +581807-1342511 America/Juneau Alaska - Juneau area -US +571035-1351807 America/Sitka Alaska - Sitka area -US +550737-1313435 America/Metlakatla Alaska - Annette Island -US +593249-1394338 America/Yakutat Alaska - Yakutat -US +643004-1652423 America/Nome Alaska (west) -US +515248-1763929 America/Adak Aleutian Islands -US +211825-1575130 Pacific/Honolulu Hawaii -UY -345433-0561245 America/Montevideo -UZ +3940+06648 Asia/Samarkand Uzbekistan (west) -UZ +4120+06918 Asia/Tashkent Uzbekistan (east) -VA +415408+0122711 Europe/Vatican -VC +1309-06114 America/St_Vincent -VE +1030-06656 America/Caracas -VG +1827-06437 America/Tortola -VI +1821-06456 America/St_Thomas -VN +1045+10640 Asia/Ho_Chi_Minh -VU -1740+16825 Pacific/Efate -WF -1318-17610 Pacific/Wallis -WS -1350-17144 Pacific/Apia -YE +1245+04512 Asia/Aden -YT -1247+04514 Indian/Mayotte -ZA -2615+02800 Africa/Johannesburg -ZM -1525+02817 Africa/Lusaka -ZW -1750+03103 Africa/Harare diff --git a/telegramer/include/pytz/zoneinfo/zone1970.tab b/telegramer/include/pytz/zoneinfo/zone1970.tab deleted file mode 100644 index c614be8..0000000 --- a/telegramer/include/pytz/zoneinfo/zone1970.tab +++ /dev/null @@ -1,374 +0,0 @@ -# tzdb timezone descriptions -# -# This file is in the public domain. -# -# From Paul Eggert (2018-06-27): -# This file contains a table where each row stands for a timezone where -# civil timestamps have agreed since 1970. Columns are separated by -# a single tab. Lines beginning with '#' are comments. All text uses -# UTF-8 encoding. The columns of the table are as follows: -# -# 1. The countries that overlap the timezone, as a comma-separated list -# of ISO 3166 2-character country codes. See the file 'iso3166.tab'. -# 2. Latitude and longitude of the timezone's principal location -# in ISO 6709 sign-degrees-minutes-seconds format, -# either ±DDMM±DDDMM or ±DDMMSS±DDDMMSS, -# first latitude (+ is north), then longitude (+ is east). -# 3. Timezone name used in value of TZ environment variable. -# Please see the theory.html file for how these names are chosen. -# If multiple timezones overlap a country, each has a row in the -# table, with each column 1 containing the country code. -# 4. Comments; present if and only if a country has multiple timezones. -# -# If a timezone covers multiple countries, the most-populous city is used, -# and that country is listed first in column 1; any other countries -# are listed alphabetically by country code. The table is sorted -# first by country code, then (if possible) by an order within the -# country that (1) makes some geographical sense, and (2) puts the -# most populous timezones first, where that does not contradict (1). -# -# This table is intended as an aid for users, to help them select timezones -# appropriate for their practical needs. It is not intended to take or -# endorse any position on legal or territorial claims. -# -#country- -#codes coordinates TZ comments -AD +4230+00131 Europe/Andorra -AE,OM +2518+05518 Asia/Dubai -AF +3431+06912 Asia/Kabul -AL +4120+01950 Europe/Tirane -AM +4011+04430 Asia/Yerevan -AQ -6617+11031 Antarctica/Casey Casey -AQ -6835+07758 Antarctica/Davis Davis -AQ -6736+06253 Antarctica/Mawson Mawson -AQ -6448-06406 Antarctica/Palmer Palmer -AQ -6734-06808 Antarctica/Rothera Rothera -AQ -720041+0023206 Antarctica/Troll Troll -AQ -7824+10654 Antarctica/Vostok Vostok -AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) -AR -3124-06411 America/Argentina/Cordoba Argentina (most areas: CB, CC, CN, ER, FM, MN, SE, SF) -AR -2447-06525 America/Argentina/Salta Salta (SA, LP, NQ, RN) -AR -2411-06518 America/Argentina/Jujuy Jujuy (JY) -AR -2649-06513 America/Argentina/Tucuman Tucumán (TM) -AR -2828-06547 America/Argentina/Catamarca Catamarca (CT); Chubut (CH) -AR -2926-06651 America/Argentina/La_Rioja La Rioja (LR) -AR -3132-06831 America/Argentina/San_Juan San Juan (SJ) -AR -3253-06849 America/Argentina/Mendoza Mendoza (MZ) -AR -3319-06621 America/Argentina/San_Luis San Luis (SL) -AR -5138-06913 America/Argentina/Rio_Gallegos Santa Cruz (SC) -AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) -AS,UM -1416-17042 Pacific/Pago_Pago Samoa, Midway -AT +4813+01620 Europe/Vienna -AU -3133+15905 Australia/Lord_Howe Lord Howe Island -AU -5430+15857 Antarctica/Macquarie Macquarie Island -AU -4253+14719 Australia/Hobart Tasmania -AU -3749+14458 Australia/Melbourne Victoria -AU -3352+15113 Australia/Sydney New South Wales (most areas) -AU -3157+14127 Australia/Broken_Hill New South Wales (Yancowinna) -AU -2728+15302 Australia/Brisbane Queensland (most areas) -AU -2016+14900 Australia/Lindeman Queensland (Whitsunday Islands) -AU -3455+13835 Australia/Adelaide South Australia -AU -1228+13050 Australia/Darwin Northern Territory -AU -3157+11551 Australia/Perth Western Australia (most areas) -AU -3143+12852 Australia/Eucla Western Australia (Eucla) -AZ +4023+04951 Asia/Baku -BB +1306-05937 America/Barbados -BD +2343+09025 Asia/Dhaka -BE +5050+00420 Europe/Brussels -BG +4241+02319 Europe/Sofia -BM +3217-06446 Atlantic/Bermuda -BN +0456+11455 Asia/Brunei -BO -1630-06809 America/La_Paz -BR -0351-03225 America/Noronha Atlantic islands -BR -0127-04829 America/Belem Pará (east); Amapá -BR -0343-03830 America/Fortaleza Brazil (northeast: MA, PI, CE, RN, PB) -BR -0803-03454 America/Recife Pernambuco -BR -0712-04812 America/Araguaina Tocantins -BR -0940-03543 America/Maceio Alagoas, Sergipe -BR -1259-03831 America/Bahia Bahia -BR -2332-04637 America/Sao_Paulo Brazil (southeast: GO, DF, MG, ES, RJ, SP, PR, SC, RS) -BR -2027-05437 America/Campo_Grande Mato Grosso do Sul -BR -1535-05605 America/Cuiaba Mato Grosso -BR -0226-05452 America/Santarem Pará (west) -BR -0846-06354 America/Porto_Velho Rondônia -BR +0249-06040 America/Boa_Vista Roraima -BR -0308-06001 America/Manaus Amazonas (east) -BR -0640-06952 America/Eirunepe Amazonas (west) -BR -0958-06748 America/Rio_Branco Acre -BT +2728+08939 Asia/Thimphu -BY +5354+02734 Europe/Minsk -BZ +1730-08812 America/Belize -CA +4734-05243 America/St_Johns Newfoundland; Labrador (southeast) -CA +4439-06336 America/Halifax Atlantic - NS (most areas); PE -CA +4612-05957 America/Glace_Bay Atlantic - NS (Cape Breton) -CA +4606-06447 America/Moncton Atlantic - New Brunswick -CA +5320-06025 America/Goose_Bay Atlantic - Labrador (most areas) -CA,BS +4339-07923 America/Toronto Eastern - ON, QC (most areas), Bahamas -CA +4901-08816 America/Nipigon Eastern - ON, QC (no DST 1967-73) -CA +4823-08915 America/Thunder_Bay Eastern - ON (Thunder Bay) -CA +6344-06828 America/Iqaluit Eastern - NU (most east areas) -CA +6608-06544 America/Pangnirtung Eastern - NU (Pangnirtung) -CA +4953-09709 America/Winnipeg Central - ON (west); Manitoba -CA +4843-09434 America/Rainy_River Central - ON (Rainy R, Ft Frances) -CA +744144-0944945 America/Resolute Central - NU (Resolute) -CA +624900-0920459 America/Rankin_Inlet Central - NU (central) -CA +5024-10439 America/Regina CST - SK (most areas) -CA +5017-10750 America/Swift_Current CST - SK (midwest) -CA +5333-11328 America/Edmonton Mountain - AB; BC (E); SK (W) -CA +690650-1050310 America/Cambridge_Bay Mountain - NU (west) -CA +6227-11421 America/Yellowknife Mountain - NT (central) -CA +682059-1334300 America/Inuvik Mountain - NT (west) -CA +5946-12014 America/Dawson_Creek MST - BC (Dawson Cr, Ft St John) -CA +5848-12242 America/Fort_Nelson MST - BC (Ft Nelson) -CA +6043-13503 America/Whitehorse MST - Yukon (east) -CA +6404-13925 America/Dawson MST - Yukon (west) -CA +4916-12307 America/Vancouver Pacific - BC (most areas) -CC -1210+09655 Indian/Cocos -CH,DE,LI +4723+00832 Europe/Zurich Swiss time -CI,BF,GH,GM,GN,ML,MR,SH,SL,SN,TG +0519-00402 Africa/Abidjan -CK -2114-15946 Pacific/Rarotonga -CL -3327-07040 America/Santiago Chile (most areas) -CL -5309-07055 America/Punta_Arenas Region of Magallanes -CL -2709-10926 Pacific/Easter Easter Island -CN +3114+12128 Asia/Shanghai Beijing Time -CN +4348+08735 Asia/Urumqi Xinjiang Time -CO +0436-07405 America/Bogota -CR +0956-08405 America/Costa_Rica -CU +2308-08222 America/Havana -CV +1455-02331 Atlantic/Cape_Verde -CX -1025+10543 Indian/Christmas -CY +3510+03322 Asia/Nicosia Cyprus (most areas) -CY +3507+03357 Asia/Famagusta Northern Cyprus -CZ,SK +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin Germany (most areas) -DK +5540+01235 Europe/Copenhagen -DO +1828-06954 America/Santo_Domingo -DZ +3647+00303 Africa/Algiers -EC -0210-07950 America/Guayaquil Ecuador (mainland) -EC -0054-08936 Pacific/Galapagos Galápagos Islands -EE +5925+02445 Europe/Tallinn -EG +3003+03115 Africa/Cairo -EH +2709-01312 Africa/El_Aaiun -ES +4024-00341 Europe/Madrid Spain (mainland) -ES +3553-00519 Africa/Ceuta Ceuta, Melilla -ES +2806-01524 Atlantic/Canary Canary Islands -FI,AX +6010+02458 Europe/Helsinki -FJ -1808+17825 Pacific/Fiji -FK -5142-05751 Atlantic/Stanley -FM +0725+15147 Pacific/Chuuk Chuuk/Truk, Yap -FM +0658+15813 Pacific/Pohnpei Pohnpei/Ponape -FM +0519+16259 Pacific/Kosrae Kosrae -FO +6201-00646 Atlantic/Faroe -FR +4852+00220 Europe/Paris -GB,GG,IM,JE +513030-0000731 Europe/London -GE +4143+04449 Asia/Tbilisi -GF +0456-05220 America/Cayenne -GI +3608-00521 Europe/Gibraltar -GL +6411-05144 America/Nuuk Greenland (most areas) -GL +7646-01840 America/Danmarkshavn National Park (east coast) -GL +7029-02158 America/Scoresbysund Scoresbysund/Ittoqqortoormiit -GL +7634-06847 America/Thule Thule/Pituffik -GR +3758+02343 Europe/Athens -GS -5416-03632 Atlantic/South_Georgia -GT +1438-09031 America/Guatemala -GU,MP +1328+14445 Pacific/Guam -GW +1151-01535 Africa/Bissau -GY +0648-05810 America/Guyana -HK +2217+11409 Asia/Hong_Kong -HN +1406-08713 America/Tegucigalpa -HT +1832-07220 America/Port-au-Prince -HU +4730+01905 Europe/Budapest -ID -0610+10648 Asia/Jakarta Java, Sumatra -ID -0002+10920 Asia/Pontianak Borneo (west, central) -ID -0507+11924 Asia/Makassar Borneo (east, south); Sulawesi/Celebes, Bali, Nusa Tengarra; Timor (west) -ID -0232+14042 Asia/Jayapura New Guinea (West Papua / Irian Jaya); Malukus/Moluccas -IE +5320-00615 Europe/Dublin -IL +314650+0351326 Asia/Jerusalem -IN +2232+08822 Asia/Kolkata -IO -0720+07225 Indian/Chagos -IQ +3321+04425 Asia/Baghdad -IR +3540+05126 Asia/Tehran -IS +6409-02151 Atlantic/Reykjavik -IT,SM,VA +4154+01229 Europe/Rome -JM +175805-0764736 America/Jamaica -JO +3157+03556 Asia/Amman -JP +353916+1394441 Asia/Tokyo -KE,DJ,ER,ET,KM,MG,SO,TZ,UG,YT -0117+03649 Africa/Nairobi -KG +4254+07436 Asia/Bishkek -KI +0125+17300 Pacific/Tarawa Gilbert Islands -KI -0247-17143 Pacific/Kanton Phoenix Islands -KI +0152-15720 Pacific/Kiritimati Line Islands -KP +3901+12545 Asia/Pyongyang -KR +3733+12658 Asia/Seoul -KZ +4315+07657 Asia/Almaty Kazakhstan (most areas) -KZ +4448+06528 Asia/Qyzylorda Qyzylorda/Kyzylorda/Kzyl-Orda -KZ +5312+06337 Asia/Qostanay Qostanay/Kostanay/Kustanay -KZ +5017+05710 Asia/Aqtobe Aqtöbe/Aktobe -KZ +4431+05016 Asia/Aqtau Mangghystaū/Mankistau -KZ +4707+05156 Asia/Atyrau Atyraū/Atirau/Gur'yev -KZ +5113+05121 Asia/Oral West Kazakhstan -LB +3353+03530 Asia/Beirut -LK +0656+07951 Asia/Colombo -LR +0618-01047 Africa/Monrovia -LT +5441+02519 Europe/Vilnius -LU +4936+00609 Europe/Luxembourg -LV +5657+02406 Europe/Riga -LY +3254+01311 Africa/Tripoli -MA +3339-00735 Africa/Casablanca -MC +4342+00723 Europe/Monaco -MD +4700+02850 Europe/Chisinau -MH +0709+17112 Pacific/Majuro Marshall Islands (most areas) -MH +0905+16720 Pacific/Kwajalein Kwajalein -MM +1647+09610 Asia/Yangon -MN +4755+10653 Asia/Ulaanbaatar Mongolia (most areas) -MN +4801+09139 Asia/Hovd Bayan-Ölgii, Govi-Altai, Hovd, Uvs, Zavkhan -MN +4804+11430 Asia/Choibalsan Dornod, Sükhbaatar -MO +221150+1133230 Asia/Macau -MQ +1436-06105 America/Martinique -MT +3554+01431 Europe/Malta -MU -2010+05730 Indian/Mauritius -MV +0410+07330 Indian/Maldives -MX +1924-09909 America/Mexico_City Central Time -MX +2105-08646 America/Cancun Eastern Standard Time - Quintana Roo -MX +2058-08937 America/Merida Central Time - Campeche, Yucatán -MX +2540-10019 America/Monterrey Central Time - Durango; Coahuila, Nuevo León, Tamaulipas (most areas) -MX +2550-09730 America/Matamoros Central Time US - Coahuila, Nuevo León, Tamaulipas (US border) -MX +2313-10625 America/Mazatlan Mountain Time - Baja California Sur, Nayarit, Sinaloa -MX +2838-10605 America/Chihuahua Mountain Time - Chihuahua (most areas) -MX +2934-10425 America/Ojinaga Mountain Time US - Chihuahua (US border) -MX +2904-11058 America/Hermosillo Mountain Standard Time - Sonora -MX +3232-11701 America/Tijuana Pacific Time US - Baja California -MX +2048-10515 America/Bahia_Banderas Central Time - Bahía de Banderas -MY +0310+10142 Asia/Kuala_Lumpur Malaysia (peninsula) -MY +0133+11020 Asia/Kuching Sabah, Sarawak -MZ,BI,BW,CD,MW,RW,ZM,ZW -2558+03235 Africa/Maputo Central Africa Time -NA -2234+01706 Africa/Windhoek -NC -2216+16627 Pacific/Noumea -NF -2903+16758 Pacific/Norfolk -NG,AO,BJ,CD,CF,CG,CM,GA,GQ,NE +0627+00324 Africa/Lagos West Africa Time -NI +1209-08617 America/Managua -NL +5222+00454 Europe/Amsterdam -NO,SJ +5955+01045 Europe/Oslo -NP +2743+08519 Asia/Kathmandu -NR -0031+16655 Pacific/Nauru -NU -1901-16955 Pacific/Niue -NZ,AQ -3652+17446 Pacific/Auckland New Zealand time -NZ -4357-17633 Pacific/Chatham Chatham Islands -PA,CA,KY +0858-07932 America/Panama EST - Panama, Cayman, ON (Atikokan), NU (Coral H) -PE -1203-07703 America/Lima -PF -1732-14934 Pacific/Tahiti Society Islands -PF -0900-13930 Pacific/Marquesas Marquesas Islands -PF -2308-13457 Pacific/Gambier Gambier Islands -PG,AQ -0930+14710 Pacific/Port_Moresby Papua New Guinea (most areas), Dumont d'Urville -PG -0613+15534 Pacific/Bougainville Bougainville -PH +1435+12100 Asia/Manila -PK +2452+06703 Asia/Karachi -PL +5215+02100 Europe/Warsaw -PM +4703-05620 America/Miquelon -PN -2504-13005 Pacific/Pitcairn -PR,AG,CA,AI,AW,BL,BQ,CW,DM,GD,GP,KN,LC,MF,MS,SX,TT,VC,VG,VI +182806-0660622 America/Puerto_Rico AST -PS +3130+03428 Asia/Gaza Gaza Strip -PS +313200+0350542 Asia/Hebron West Bank -PT +3843-00908 Europe/Lisbon Portugal (mainland) -PT +3238-01654 Atlantic/Madeira Madeira Islands -PT +3744-02540 Atlantic/Azores Azores -PW +0720+13429 Pacific/Palau -PY -2516-05740 America/Asuncion -QA,BH +2517+05132 Asia/Qatar -RE,TF -2052+05528 Indian/Reunion Réunion, Crozet, Scattered Islands -RO +4426+02606 Europe/Bucharest -RS,BA,HR,ME,MK,SI +4450+02030 Europe/Belgrade -RU +5443+02030 Europe/Kaliningrad MSK-01 - Kaliningrad -RU +554521+0373704 Europe/Moscow MSK+00 - Moscow area -# Mention RU and UA alphabetically. See "territorial claims" above. -RU,UA +4457+03406 Europe/Simferopol Crimea -RU +5836+04939 Europe/Kirov MSK+00 - Kirov -RU +4844+04425 Europe/Volgograd MSK+00 - Volgograd -RU +4621+04803 Europe/Astrakhan MSK+01 - Astrakhan -RU +5134+04602 Europe/Saratov MSK+01 - Saratov -RU +5420+04824 Europe/Ulyanovsk MSK+01 - Ulyanovsk -RU +5312+05009 Europe/Samara MSK+01 - Samara, Udmurtia -RU +5651+06036 Asia/Yekaterinburg MSK+02 - Urals -RU +5500+07324 Asia/Omsk MSK+03 - Omsk -RU +5502+08255 Asia/Novosibirsk MSK+04 - Novosibirsk -RU +5322+08345 Asia/Barnaul MSK+04 - Altai -RU +5630+08458 Asia/Tomsk MSK+04 - Tomsk -RU +5345+08707 Asia/Novokuznetsk MSK+04 - Kemerovo -RU +5601+09250 Asia/Krasnoyarsk MSK+04 - Krasnoyarsk area -RU +5216+10420 Asia/Irkutsk MSK+05 - Irkutsk, Buryatia -RU +5203+11328 Asia/Chita MSK+06 - Zabaykalsky -RU +6200+12940 Asia/Yakutsk MSK+06 - Lena River -RU +623923+1353314 Asia/Khandyga MSK+06 - Tomponsky, Ust-Maysky -RU +4310+13156 Asia/Vladivostok MSK+07 - Amur River -RU +643337+1431336 Asia/Ust-Nera MSK+07 - Oymyakonsky -RU +5934+15048 Asia/Magadan MSK+08 - Magadan -RU +4658+14242 Asia/Sakhalin MSK+08 - Sakhalin Island -RU +6728+15343 Asia/Srednekolymsk MSK+08 - Sakha (E); North Kuril Is -RU +5301+15839 Asia/Kamchatka MSK+09 - Kamchatka -RU +6445+17729 Asia/Anadyr MSK+09 - Bering Sea -SA,AQ,KW,YE +2438+04643 Asia/Riyadh Arabia, Syowa -SB -0932+16012 Pacific/Guadalcanal -SC -0440+05528 Indian/Mahe -SD +1536+03232 Africa/Khartoum -SE +5920+01803 Europe/Stockholm -SG,MY +0117+10351 Asia/Singapore Singapore, peninsular Malaysia -SR +0550-05510 America/Paramaribo -SS +0451+03137 Africa/Juba -ST +0020+00644 Africa/Sao_Tome -SV +1342-08912 America/El_Salvador -SY +3330+03618 Asia/Damascus -TC +2128-07108 America/Grand_Turk -TD +1207+01503 Africa/Ndjamena -TF -492110+0701303 Indian/Kerguelen Kerguelen, St Paul Island, Amsterdam Island -TH,KH,LA,VN +1345+10031 Asia/Bangkok Indochina (most areas) -TJ +3835+06848 Asia/Dushanbe -TK -0922-17114 Pacific/Fakaofo -TL -0833+12535 Asia/Dili -TM +3757+05823 Asia/Ashgabat -TN +3648+01011 Africa/Tunis -TO -210800-1751200 Pacific/Tongatapu -TR +4101+02858 Europe/Istanbul -TV -0831+17913 Pacific/Funafuti -TW +2503+12130 Asia/Taipei -UA +5026+03031 Europe/Kiev Ukraine (most areas) -UA +4837+02218 Europe/Uzhgorod Transcarpathia -UA +4750+03510 Europe/Zaporozhye Zaporozhye and east Lugansk -UM +1917+16637 Pacific/Wake Wake Island -US +404251-0740023 America/New_York Eastern (most areas) -US +421953-0830245 America/Detroit Eastern - MI (most areas) -US +381515-0854534 America/Kentucky/Louisville Eastern - KY (Louisville area) -US +364947-0845057 America/Kentucky/Monticello Eastern - KY (Wayne) -US +394606-0860929 America/Indiana/Indianapolis Eastern - IN (most areas) -US +384038-0873143 America/Indiana/Vincennes Eastern - IN (Da, Du, K, Mn) -US +410305-0863611 America/Indiana/Winamac Eastern - IN (Pulaski) -US +382232-0862041 America/Indiana/Marengo Eastern - IN (Crawford) -US +382931-0871643 America/Indiana/Petersburg Eastern - IN (Pike) -US +384452-0850402 America/Indiana/Vevay Eastern - IN (Switzerland) -US +415100-0873900 America/Chicago Central (most areas) -US +375711-0864541 America/Indiana/Tell_City Central - IN (Perry) -US +411745-0863730 America/Indiana/Knox Central - IN (Starke) -US +450628-0873651 America/Menominee Central - MI (Wisconsin border) -US +470659-1011757 America/North_Dakota/Center Central - ND (Oliver) -US +465042-1012439 America/North_Dakota/New_Salem Central - ND (Morton rural) -US +471551-1014640 America/North_Dakota/Beulah Central - ND (Mercer) -US +394421-1045903 America/Denver Mountain (most areas) -US +433649-1161209 America/Boise Mountain - ID (south); OR (east) -US,CA +332654-1120424 America/Phoenix MST - Arizona (except Navajo), Creston BC -US +340308-1181434 America/Los_Angeles Pacific -US +611305-1495401 America/Anchorage Alaska (most areas) -US +581807-1342511 America/Juneau Alaska - Juneau area -US +571035-1351807 America/Sitka Alaska - Sitka area -US +550737-1313435 America/Metlakatla Alaska - Annette Island -US +593249-1394338 America/Yakutat Alaska - Yakutat -US +643004-1652423 America/Nome Alaska (west) -US +515248-1763929 America/Adak Aleutian Islands -US,UM +211825-1575130 Pacific/Honolulu Hawaii -UY -345433-0561245 America/Montevideo -UZ +3940+06648 Asia/Samarkand Uzbekistan (west) -UZ +4120+06918 Asia/Tashkent Uzbekistan (east) -VE +1030-06656 America/Caracas -VN +1045+10640 Asia/Ho_Chi_Minh Vietnam (south) -VU -1740+16825 Pacific/Efate -WF -1318-17610 Pacific/Wallis -WS -1350-17144 Pacific/Apia -ZA,LS,SZ -2615+02800 Africa/Johannesburg diff --git a/telegramer/include/telegram/__init__.py b/telegramer/include/telegram/__init__.py deleted file mode 100644 index c0b36de..0000000 --- a/telegramer/include/telegram/__init__.py +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""A library that provides a Python interface to the Telegram Bot API""" - -from .base import TelegramObject -from .botcommand import BotCommand -from .user import User -from .files.chatphoto import ChatPhoto -from .chat import Chat -from .chatlocation import ChatLocation -from .chatinvitelink import ChatInviteLink -from .chatjoinrequest import ChatJoinRequest -from .chatmember import ( - ChatMember, - ChatMemberOwner, - ChatMemberAdministrator, - ChatMemberMember, - ChatMemberRestricted, - ChatMemberLeft, - ChatMemberBanned, -) -from .chatmemberupdated import ChatMemberUpdated -from .chatpermissions import ChatPermissions -from .files.photosize import PhotoSize -from .files.audio import Audio -from .files.voice import Voice -from .files.document import Document -from .files.animation import Animation -from .files.sticker import Sticker, StickerSet, MaskPosition -from .files.video import Video -from .files.contact import Contact -from .files.location import Location -from .files.venue import Venue -from .files.videonote import VideoNote -from .chataction import ChatAction -from .dice import Dice -from .userprofilephotos import UserProfilePhotos -from .keyboardbuttonpolltype import KeyboardButtonPollType -from .keyboardbutton import KeyboardButton -from .replymarkup import ReplyMarkup -from .replykeyboardmarkup import ReplyKeyboardMarkup -from .replykeyboardremove import ReplyKeyboardRemove -from .forcereply import ForceReply -from .error import TelegramError -from .files.inputfile import InputFile -from .files.file import File -from .parsemode import ParseMode -from .messageentity import MessageEntity -from .messageid import MessageId -from .games.game import Game -from .poll import Poll, PollOption, PollAnswer -from .voicechat import ( - VoiceChatStarted, - VoiceChatEnded, - VoiceChatParticipantsInvited, - VoiceChatScheduled, -) -from .loginurl import LoginUrl -from .proximityalerttriggered import ProximityAlertTriggered -from .games.callbackgame import CallbackGame -from .payment.shippingaddress import ShippingAddress -from .payment.orderinfo import OrderInfo -from .payment.successfulpayment import SuccessfulPayment -from .payment.invoice import Invoice -from .passport.credentials import EncryptedCredentials -from .passport.passportfile import PassportFile -from .passport.data import IdDocumentData, PersonalDetails, ResidentialAddress -from .passport.encryptedpassportelement import EncryptedPassportElement -from .passport.passportdata import PassportData -from .inline.inlinekeyboardbutton import InlineKeyboardButton -from .inline.inlinekeyboardmarkup import InlineKeyboardMarkup -from .messageautodeletetimerchanged import MessageAutoDeleteTimerChanged -from .message import Message -from .callbackquery import CallbackQuery -from .choseninlineresult import ChosenInlineResult -from .inline.inputmessagecontent import InputMessageContent -from .inline.inlinequery import InlineQuery -from .inline.inlinequeryresult import InlineQueryResult -from .inline.inlinequeryresultarticle import InlineQueryResultArticle -from .inline.inlinequeryresultaudio import InlineQueryResultAudio -from .inline.inlinequeryresultcachedaudio import InlineQueryResultCachedAudio -from .inline.inlinequeryresultcacheddocument import InlineQueryResultCachedDocument -from .inline.inlinequeryresultcachedgif import InlineQueryResultCachedGif -from .inline.inlinequeryresultcachedmpeg4gif import InlineQueryResultCachedMpeg4Gif -from .inline.inlinequeryresultcachedphoto import InlineQueryResultCachedPhoto -from .inline.inlinequeryresultcachedsticker import InlineQueryResultCachedSticker -from .inline.inlinequeryresultcachedvideo import InlineQueryResultCachedVideo -from .inline.inlinequeryresultcachedvoice import InlineQueryResultCachedVoice -from .inline.inlinequeryresultcontact import InlineQueryResultContact -from .inline.inlinequeryresultdocument import InlineQueryResultDocument -from .inline.inlinequeryresultgif import InlineQueryResultGif -from .inline.inlinequeryresultlocation import InlineQueryResultLocation -from .inline.inlinequeryresultmpeg4gif import InlineQueryResultMpeg4Gif -from .inline.inlinequeryresultphoto import InlineQueryResultPhoto -from .inline.inlinequeryresultvenue import InlineQueryResultVenue -from .inline.inlinequeryresultvideo import InlineQueryResultVideo -from .inline.inlinequeryresultvoice import InlineQueryResultVoice -from .inline.inlinequeryresultgame import InlineQueryResultGame -from .inline.inputtextmessagecontent import InputTextMessageContent -from .inline.inputlocationmessagecontent import InputLocationMessageContent -from .inline.inputvenuemessagecontent import InputVenueMessageContent -from .payment.labeledprice import LabeledPrice -from .inline.inputinvoicemessagecontent import InputInvoiceMessageContent -from .inline.inputcontactmessagecontent import InputContactMessageContent -from .payment.shippingoption import ShippingOption -from .payment.precheckoutquery import PreCheckoutQuery -from .payment.shippingquery import ShippingQuery -from .webhookinfo import WebhookInfo -from .games.gamehighscore import GameHighScore -from .update import Update -from .files.inputmedia import ( - InputMedia, - InputMediaVideo, - InputMediaPhoto, - InputMediaAnimation, - InputMediaAudio, - InputMediaDocument, -) -from .constants import ( - MAX_MESSAGE_LENGTH, - MAX_CAPTION_LENGTH, - SUPPORTED_WEBHOOK_PORTS, - MAX_FILESIZE_DOWNLOAD, - MAX_FILESIZE_UPLOAD, - MAX_MESSAGES_PER_SECOND_PER_CHAT, - MAX_MESSAGES_PER_SECOND, - MAX_MESSAGES_PER_MINUTE_PER_GROUP, -) -from .passport.passportelementerrors import ( - PassportElementError, - PassportElementErrorDataField, - PassportElementErrorFile, - PassportElementErrorFiles, - PassportElementErrorFrontSide, - PassportElementErrorReverseSide, - PassportElementErrorSelfie, - PassportElementErrorTranslationFile, - PassportElementErrorTranslationFiles, - PassportElementErrorUnspecified, -) -from .passport.credentials import ( - Credentials, - DataCredentials, - SecureData, - SecureValue, - FileCredentials, - TelegramDecryptionError, -) -from .botcommandscope import ( - BotCommandScope, - BotCommandScopeDefault, - BotCommandScopeAllPrivateChats, - BotCommandScopeAllGroupChats, - BotCommandScopeAllChatAdministrators, - BotCommandScopeChat, - BotCommandScopeChatAdministrators, - BotCommandScopeChatMember, -) -from .bot import Bot -from .version import __version__, bot_api_version # noqa: F401 - -__author__ = 'devs@python-telegram-bot.org' - -__all__ = ( # Keep this alphabetically ordered - 'Animation', - 'Audio', - 'Bot', - 'BotCommand', - 'BotCommandScope', - 'BotCommandScopeAllChatAdministrators', - 'BotCommandScopeAllGroupChats', - 'BotCommandScopeAllPrivateChats', - 'BotCommandScopeChat', - 'BotCommandScopeChatAdministrators', - 'BotCommandScopeChatMember', - 'BotCommandScopeDefault', - 'CallbackGame', - 'CallbackQuery', - 'Chat', - 'ChatAction', - 'ChatInviteLink', - 'ChatJoinRequest', - 'ChatLocation', - 'ChatMember', - 'ChatMemberOwner', - 'ChatMemberAdministrator', - 'ChatMemberMember', - 'ChatMemberRestricted', - 'ChatMemberLeft', - 'ChatMemberBanned', - 'ChatMemberUpdated', - 'ChatPermissions', - 'ChatPhoto', - 'ChosenInlineResult', - 'Contact', - 'Credentials', - 'DataCredentials', - 'Dice', - 'Document', - 'EncryptedCredentials', - 'EncryptedPassportElement', - 'File', - 'FileCredentials', - 'ForceReply', - 'Game', - 'GameHighScore', - 'IdDocumentData', - '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', - 'InputFile', - 'InputInvoiceMessageContent', - 'InputLocationMessageContent', - 'InputMedia', - 'InputMediaAnimation', - 'InputMediaAudio', - 'InputMediaDocument', - 'InputMediaPhoto', - 'InputMediaVideo', - 'InputMessageContent', - 'InputTextMessageContent', - 'InputVenueMessageContent', - 'Invoice', - 'KeyboardButton', - 'KeyboardButtonPollType', - 'LabeledPrice', - 'Location', - 'LoginUrl', - 'MAX_CAPTION_LENGTH', - 'MAX_FILESIZE_DOWNLOAD', - 'MAX_FILESIZE_UPLOAD', - 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', - 'MAX_MESSAGES_PER_SECOND', - 'MAX_MESSAGES_PER_SECOND_PER_CHAT', - 'MAX_MESSAGE_LENGTH', - 'MaskPosition', - 'Message', - 'MessageAutoDeleteTimerChanged', - 'MessageEntity', - 'MessageId', - 'OrderInfo', - 'ParseMode', - 'PassportData', - 'PassportElementError', - 'PassportElementErrorDataField', - 'PassportElementErrorFile', - 'PassportElementErrorFiles', - 'PassportElementErrorFrontSide', - 'PassportElementErrorReverseSide', - 'PassportElementErrorSelfie', - 'PassportElementErrorTranslationFile', - 'PassportElementErrorTranslationFiles', - 'PassportElementErrorUnspecified', - 'PassportFile', - 'PersonalDetails', - 'PhotoSize', - 'Poll', - 'PollAnswer', - 'PollOption', - 'PreCheckoutQuery', - 'ProximityAlertTriggered', - 'ReplyKeyboardMarkup', - 'ReplyKeyboardRemove', - 'ReplyMarkup', - 'ResidentialAddress', - 'SUPPORTED_WEBHOOK_PORTS', - 'SecureData', - 'SecureValue', - 'ShippingAddress', - 'ShippingOption', - 'ShippingQuery', - 'Sticker', - 'StickerSet', - 'SuccessfulPayment', - 'TelegramDecryptionError', - 'TelegramError', - 'TelegramObject', - 'Update', - 'User', - 'UserProfilePhotos', - 'Venue', - 'Video', - 'VideoNote', - 'Voice', - 'VoiceChatStarted', - 'VoiceChatEnded', - 'VoiceChatScheduled', - 'VoiceChatParticipantsInvited', - 'WebhookInfo', -) diff --git a/telegramer/include/telegram/__main__.py b/telegramer/include/telegram/__main__.py deleted file mode 100644 index 2e7a7de..0000000 --- a/telegramer/include/telegram/__main__.py +++ /dev/null @@ -1,54 +0,0 @@ -# !/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0114 -import subprocess -import sys -from typing import Optional - -import certifi - -from . import __version__ as telegram_ver -from .constants import BOT_API_VERSION - - -def _git_revision() -> Optional[str]: - try: - output = subprocess.check_output( # skipcq: BAN-B607 - ["git", "describe", "--long", "--tags"], stderr=subprocess.STDOUT - ) - except (subprocess.SubprocessError, OSError): - return None - return output.decode().strip() - - -def print_ver_info() -> None: # skipcq: PY-D0003 - git_revision = _git_revision() - print(f'python-telegram-bot {telegram_ver}' + (f' ({git_revision})' if git_revision else '')) - print(f'Bot API {BOT_API_VERSION}') - print(f'certifi {certifi.__version__}') # type: ignore[attr-defined] - sys_version = sys.version.replace('\n', ' ') - print(f'Python {sys_version}') - - -def main() -> None: # skipcq: PY-D0003 - print_ver_info() - - -if __name__ == '__main__': - main() diff --git a/telegramer/include/telegram/base.py b/telegramer/include/telegram/base.py deleted file mode 100644 index f119d96..0000000 --- a/telegramer/include/telegram/base.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""Base class for Telegram Objects.""" -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] - -import warnings -from typing import TYPE_CHECKING, List, Optional, Tuple, Type, TypeVar - -from telegram.utils.types import JSONDict -from telegram.utils.deprecate import set_new_attribute_deprecated - -if TYPE_CHECKING: - from telegram import Bot - -TO = TypeVar('TO', bound='TelegramObject', covariant=True) - - -class TelegramObject: - """Base class for most Telegram objects.""" - - _id_attrs: Tuple[object, ...] = () - - # Adding slots reduces memory usage & allows for faster attribute access. - # Only instance variables should be added to __slots__. - # We add __dict__ here for backward compatibility & also to avoid repetition for subclasses. - __slots__ = ('__dict__',) - - def __str__(self) -> str: - return str(self.to_dict()) - - def __getitem__(self, item: str) -> object: - return getattr(self, item, None) - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - @staticmethod - def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]: - return None if data is None else data.copy() - - @classmethod - def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO]: - """Converts JSON data to a Telegram object. - - Args: - data (Dict[:obj:`str`, ...]): The JSON data. - bot (:class:`telegram.Bot`): The bot associated with this object. - - Returns: - The Telegram object. - - """ - data = cls._parse_data(data) - - if data is None: - return None - - if cls == TelegramObject: - return cls() - return cls(bot=bot, **data) # type: ignore[call-arg] - - @classmethod - def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]: - """Converts JSON data to a list of Telegram objects. - - Args: - data (Dict[:obj:`str`, ...]): The JSON data. - bot (:class:`telegram.Bot`): The bot associated with these objects. - - Returns: - A list of Telegram objects. - - """ - if not data: - return [] - - return [cls.de_json(d, bot) for d in data] - - def to_json(self) -> str: - """Gives a JSON representation of object. - - Returns: - :obj:`str` - """ - return json.dumps(self.to_dict()) - - def to_dict(self) -> JSONDict: - """Gives representation of object as :obj:`dict`. - - Returns: - :obj:`dict` - """ - data = {} - - # We want to get all attributes for the class, using self.__slots__ only includes the - # attributes used by that class itself, and not its superclass(es). Hence we get its MRO - # and then get their attributes. The `[:-2]` slice excludes the `object` class & the - # TelegramObject class itself. - attrs = {attr for cls in self.__class__.__mro__[:-2] for attr in cls.__slots__} - for key in attrs: - if key == 'bot' or key.startswith('_'): - continue - - value = getattr(self, key, None) - if value is not None: - if hasattr(value, 'to_dict'): - data[key] = value.to_dict() - else: - data[key] = value - - if data.get('from_user'): - data['from'] = data.pop('from_user', None) - return data - - def __eq__(self, other: object) -> bool: - if isinstance(other, self.__class__): - if self._id_attrs == (): - warnings.warn( - f"Objects of type {self.__class__.__name__} can not be meaningfully tested for" - " equivalence." - ) - if other._id_attrs == (): - warnings.warn( - f"Objects of type {other.__class__.__name__} can not be meaningfully tested" - " for equivalence." - ) - return self._id_attrs == other._id_attrs - return super().__eq__(other) # pylint: disable=no-member - - def __hash__(self) -> int: - if self._id_attrs: - return hash((self.__class__, self._id_attrs)) # pylint: disable=no-member - return super().__hash__() diff --git a/telegramer/include/telegram/bot.py b/telegramer/include/telegram/bot.py deleted file mode 100644 index cc983d5..0000000 --- a/telegramer/include/telegram/bot.py +++ /dev/null @@ -1,5861 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=E0611,E0213,E1102,E1101,R0913,R0904 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Bot.""" - -import functools -import logging -import warnings -from datetime import datetime - -from typing import ( - TYPE_CHECKING, - Callable, - List, - Optional, - Tuple, - TypeVar, - Union, - no_type_check, - Dict, - cast, - Sequence, -) - -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] # noqa: F723 - -try: - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import serialization - - CRYPTO_INSTALLED = True -except ImportError: - default_backend = None # type: ignore[assignment] - serialization = None # type: ignore[assignment] - CRYPTO_INSTALLED = False - -from telegram import ( - Animation, - Audio, - BotCommand, - BotCommandScope, - Chat, - ChatMember, - ChatPermissions, - ChatPhoto, - Contact, - Document, - File, - GameHighScore, - Location, - MaskPosition, - Message, - MessageId, - PassportElementError, - PhotoSize, - Poll, - ReplyMarkup, - ShippingOption, - Sticker, - StickerSet, - TelegramObject, - Update, - User, - UserProfilePhotos, - Venue, - Video, - VideoNote, - Voice, - WebhookInfo, - InlineKeyboardMarkup, - ChatInviteLink, -) -from telegram.constants import MAX_INLINE_QUERY_RESULTS -from telegram.error import InvalidToken, TelegramError -from telegram.utils.deprecate import TelegramDeprecationWarning -from telegram.utils.helpers import ( - DEFAULT_NONE, - DefaultValue, - to_timestamp, - is_local_file, - parse_file_input, - DEFAULT_20, -) -from telegram.utils.request import Request -from telegram.utils.types import FileInput, JSONDict, ODVInput, DVInput - -if TYPE_CHECKING: - from telegram.ext import Defaults - from telegram import ( - InputMediaAudio, - InputMediaDocument, - InputMediaPhoto, - InputMediaVideo, - InputMedia, - InlineQueryResult, - LabeledPrice, - MessageEntity, - ) - -RT = TypeVar('RT') - - -def log( # skipcq: PY-D0003 - func: Callable[..., RT], *args: object, **kwargs: object # pylint: disable=W0613 -) -> Callable[..., RT]: - logger = logging.getLogger(func.__module__) - - @functools.wraps(func) - def decorator(*args: object, **kwargs: object) -> RT: # pylint: disable=W0613 - logger.debug('Entering: %s', func.__name__) - result = func(*args, **kwargs) - logger.debug(result) - logger.debug('Exiting: %s', func.__name__) - return result - - return decorator - - -class Bot(TelegramObject): - """This object represents a Telegram Bot. - - .. versionadded:: 13.2 - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`bot` is equal. - - Note: - Most bot methods have the argument ``api_kwargs`` which allows to pass arbitrary keywords - to the Telegram API. This can be used to access new features of the API before they were - incorporated into PTB. However, this is not guaranteed to work, i.e. it will fail for - passing files. - - Args: - token (:obj:`str`): Bot's unique authentication. - base_url (:obj:`str`, optional): Telegram Bot API service URL. - base_file_url (:obj:`str`, optional): Telegram Bot API file URL. - request (:obj:`telegram.utils.request.Request`, optional): Pre initialized - :obj:`telegram.utils.request.Request`. - private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data. - private_key_password (:obj:`bytes`, optional): Password for above private key. - defaults (:class:`telegram.ext.Defaults`, optional): An object containing default values to - be used if not set explicitly in the bot methods. - - .. deprecated:: 13.6 - Passing :class:`telegram.ext.Defaults` to :class:`telegram.Bot` is deprecated. If - you want to use :class:`telegram.ext.Defaults`, please use - :class:`telegram.ext.ExtBot` instead. - - """ - - __slots__ = ( - 'token', - 'base_url', - 'base_file_url', - 'private_key', - 'defaults', - '_bot', - '_commands', - '_request', - 'logger', - ) - - def __init__( - self, - token: str, - base_url: str = None, - base_file_url: str = None, - request: 'Request' = None, - private_key: bytes = None, - private_key_password: bytes = None, - defaults: 'Defaults' = None, - ): - self.token = self._validate_token(token) - - # Gather default - self.defaults = defaults - - if self.defaults: - warnings.warn( - 'Passing Defaults to telegram.Bot is deprecated. Use telegram.ext.ExtBot instead.', - TelegramDeprecationWarning, - stacklevel=3, - ) - - if base_url is None: - base_url = 'https://api.telegram.org/bot' - - if base_file_url is None: - base_file_url = 'https://api.telegram.org/file/bot' - - self.base_url = str(base_url) + str(self.token) - self.base_file_url = str(base_file_url) + str(self.token) - self._bot: Optional[User] = None - self._commands: Optional[List[BotCommand]] = None - self._request = request or Request() - self.private_key = None - self.logger = logging.getLogger(__name__) - - if private_key: - if not CRYPTO_INSTALLED: - raise RuntimeError( - 'To use Telegram Passports, PTB must be installed via `pip install ' - 'python-telegram-bot[passport]`.' - ) - self.private_key = serialization.load_pem_private_key( - private_key, password=private_key_password, backend=default_backend() - ) - - # The ext_bot argument is a little hack to get warnings handled correctly. - # It's not very clean, but the warnings will be dropped at some point anyway. - def __setattr__(self, key: str, value: object, ext_bot: bool = False) -> None: - if issubclass(self.__class__, Bot) and self.__class__ is not Bot and not ext_bot: - object.__setattr__(self, key, value) - return - super().__setattr__(key, value) - - def _insert_defaults( - self, data: Dict[str, object], timeout: ODVInput[float] - ) -> Optional[float]: - """ - Inserts the defaults values for optional kwargs for which tg.ext.Defaults provides - convenience functionality, i.e. the kwargs with a tg.utils.helpers.DefaultValue default - - data is edited in-place. As timeout is not passed via the kwargs, it needs to be passed - separately and gets returned. - - This can only work, if all kwargs that may have defaults are passed in data! - """ - effective_timeout = DefaultValue.get_value(timeout) - - # If we have no Defaults, we just need to replace DefaultValue instances - # with the actual value - if not self.defaults: - data.update((key, DefaultValue.get_value(value)) for key, value in data.items()) - return effective_timeout - - # if we have Defaults, we replace all DefaultValue instances with the relevant - # Defaults value. If there is none, we fall back to the default value of the bot method - for key, val in data.items(): - if isinstance(val, DefaultValue): - data[key] = self.defaults.api_defaults.get(key, val.value) - - if isinstance(timeout, DefaultValue): - # If we get here, we use Defaults.timeout, unless that's not set, which is the - # case if isinstance(self.defaults.timeout, DefaultValue) - return ( - self.defaults.timeout - if not isinstance(self.defaults.timeout, DefaultValue) - else effective_timeout - ) - return effective_timeout - - def _post( - self, - endpoint: str, - data: JSONDict = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[bool, JSONDict, None]: - if data is None: - data = {} - - if api_kwargs: - if data: - data.update(api_kwargs) - else: - data = api_kwargs - - # Insert is in-place, so no return value for data - if endpoint != 'getUpdates': - effective_timeout = self._insert_defaults(data, timeout) - else: - effective_timeout = cast(float, timeout) - # Drop any None values because Telegram doesn't handle them well - data = {key: value for key, value in data.items() if value is not None} - - return self.request.post( - f'{self.base_url}/{endpoint}', data=data, timeout=effective_timeout - ) - - def _message( - self, - endpoint: str, - data: JSONDict, - reply_to_message_id: int = None, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> Union[bool, Message]: - if reply_to_message_id is not None: - data['reply_to_message_id'] = reply_to_message_id - - if protect_content: - data['protect_content'] = protect_content - - # We don't check if (DEFAULT_)None here, so that _put is able to insert the defaults - # correctly, if necessary - data['disable_notification'] = disable_notification - data['allow_sending_without_reply'] = allow_sending_without_reply - - if reply_markup is not None: - if isinstance(reply_markup, ReplyMarkup): - # We need to_json() instead of to_dict() here, because reply_markups may be - # attached to media messages, which aren't json dumped by utils.request - data['reply_markup'] = reply_markup.to_json() - else: - data['reply_markup'] = reply_markup - - if data.get('media') and (data['media'].parse_mode == DEFAULT_NONE): - if self.defaults: - data['media'].parse_mode = DefaultValue.get_value(self.defaults.parse_mode) - else: - data['media'].parse_mode = None - - result = self._post(endpoint, data, timeout=timeout, api_kwargs=api_kwargs) - - if result is True: - return result - - return Message.de_json(result, self) # type: ignore[return-value, arg-type] - - @property - def request(self) -> Request: # skip-cq: PY-D0003 - return self._request - - @staticmethod - def _validate_token(token: str) -> str: - """A very basic validation on token.""" - if any(x.isspace() for x in token): - raise InvalidToken() - - left, sep, _right = token.partition(':') - if (not sep) or (not left.isdigit()) or (len(left) < 3): - raise InvalidToken() - - return token - - @property - def bot(self) -> User: - """:class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`.""" - if self._bot is None: - self._bot = self.get_me() - return self._bot - - @property - def id(self) -> int: # pylint: disable=C0103 - """:obj:`int`: Unique identifier for this bot.""" - return self.bot.id - - @property - def first_name(self) -> str: - """:obj:`str`: Bot's first name.""" - return self.bot.first_name - - @property - def last_name(self) -> str: - """:obj:`str`: Optional. Bot's last name.""" - return self.bot.last_name # type: ignore - - @property - def username(self) -> str: - """:obj:`str`: Bot's username.""" - return self.bot.username # type: ignore - - @property - def link(self) -> str: - """:obj:`str`: Convenience property. Returns the t.me link of the bot.""" - return f"https://t.me/{self.username}" - - @property - def can_join_groups(self) -> bool: - """:obj:`bool`: Bot's :attr:`telegram.User.can_join_groups` attribute.""" - return self.bot.can_join_groups # type: ignore - - @property - def can_read_all_group_messages(self) -> bool: - """:obj:`bool`: Bot's :attr:`telegram.User.can_read_all_group_messages` attribute.""" - return self.bot.can_read_all_group_messages # type: ignore - - @property - def supports_inline_queries(self) -> bool: - """:obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute.""" - return self.bot.supports_inline_queries # type: ignore - - @property - def commands(self) -> List[BotCommand]: - """ - List[:class:`BotCommand`]: Bot's commands as available in the default scope. - - .. deprecated:: 13.7 - This property has been deprecated since there can be different commands available for - different scopes. - """ - warnings.warn( - "Bot.commands has been deprecated since there can be different command " - "lists for different scopes.", - TelegramDeprecationWarning, - stacklevel=2, - ) - - if self._commands is None: - self._commands = self.get_my_commands() - return self._commands - - @property - def name(self) -> str: - """:obj:`str`: Bot's @username.""" - return f'@{self.username}' - - @log - def get_me(self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None) -> User: - """A simple method for testing your bot's auth token. Requires no parameters. - - Args: - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.User`: A :class:`telegram.User` instance representing that bot if the - credentials are valid, :obj:`None` otherwise. - - Raises: - :class:`telegram.error.TelegramError` - - """ - result = self._post('getMe', timeout=timeout, api_kwargs=api_kwargs) - - self._bot = User.de_json(result, self) # type: ignore[return-value, arg-type] - - return self._bot # type: ignore[return-value] - - @log - def send_message( - self, - chat_id: Union[int, str], - text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> Message: - """Use this method to send text messages. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - text (:obj:`str`): Text of the message to be sent. Max 4096 characters after entities - parsing. Also found as :attr:`telegram.constants.MAX_MESSAGE_LENGTH`. - parse_mode (:obj:`str`): Send Markdown or HTML, if you want Telegram apps to show bold, - italic, fixed-width text or inline URLs in your bot's message. See the constants in - :class:`telegram.ParseMode` for the available modes. - entities (List[:class:`telegram.MessageEntity`], optional): List of special entities - that appear in message text, which can be specified instead of :attr:`parse_mode`. - disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in - this message. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of sent messages from - forwarding and saving. - - .. versionadded:: 13.10 - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. - A JSON-serialized object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'text': text, - 'parse_mode': parse_mode, - 'disable_web_page_preview': disable_web_page_preview, - } - - if entities: - data['entities'] = [me.to_dict() for me in entities] - - return self._message( # type: ignore[return-value] - 'sendMessage', - data, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def delete_message( - self, - chat_id: Union[str, int], - message_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to delete a message, including service messages, with the following - limitations: - - - A message can only be deleted if it was sent less than 48 hours ago. - - A dice message in a private chat can only be deleted if it was sent more than 24 - hours ago. - - Bots can delete outgoing messages in private chats, groups, and supergroups. - - Bots can delete incoming messages in private chats. - - Bots granted :attr:`telegram.ChatMember.can_post_messages` permissions can delete - outgoing messages in channels. - - If the bot is an administrator of a group, it can delete any message there. - - If the bot has :attr:`telegram.ChatMember.can_delete_messages` permission in a - supergroup or a channel, it can delete any message there. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - message_id (:obj:`int`): Identifier of the message to delete. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'message_id': message_id} - - result = self._post('deleteMessage', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def forward_message( - self, - chat_id: Union[int, str], - from_chat_id: Union[str, int], - message_id: int, - disable_notification: DVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> Message: - """Use this method to forward messages of any kind. Service messages can't be forwarded. - - Note: - Since the release of Bot API 5.5 it can be impossible to forward messages from - some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and - :attr:`telegram.Chat.has_protected_content` to check this. - - As a workaround, it is still possible to use :meth:`copy_message`. However, this - behaviour is undocumented and might be changed by Telegram. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - from_chat_id (:obj:`int` | :obj:`str`): Unique identifier for the chat where the - original message was sent (or channel username in the format ``@channelusername``). - message_id (:obj:`int`): Message identifier in the chat specified in from_chat_id. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {} - - if chat_id: - data['chat_id'] = chat_id - if from_chat_id: - data['from_chat_id'] = from_chat_id - if message_id: - data['message_id'] = message_id - return self._message( # type: ignore[return-value] - 'forwardMessage', - data, - disable_notification=disable_notification, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_photo( - self, - chat_id: Union[int, str], - photo: Union[FileInput, 'PhotoSize'], - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> Message: - """Use this method to send photos. - - Note: - The photo argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - photo (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.PhotoSize`): Photo to send. - Pass a file_id as String to send a photo that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get a photo from the - Internet, or upload a new photo using multipart/form-data. Lastly you can pass - an existing :class:`telegram.PhotoSize` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the photo, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional): Photo caption (may also be used when resending photos - by file_id), 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'photo': parse_file_input(photo, PhotoSize, filename=filename), - 'parse_mode': parse_mode, - } - - if caption: - data['caption'] = caption - - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - - return self._message( # type: ignore[return-value] - 'sendPhoto', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_audio( - self, - chat_id: Union[int, str], - audio: Union[FileInput, 'Audio'], - duration: int = None, - performer: str = None, - title: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send audio files, if you want Telegram clients to display them in the - music player. Your audio must be in the .mp3 or .m4a format. - - Bots can currently send audio files of up to 50 MB in size, this limit may be changed in - the future. - - For sending voice messages, use the :meth:`send_voice` method instead. - - Note: - The audio argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - audio (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Audio`): Audio file to send. - Pass a file_id as String to send an audio file that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get an audio file from - the Internet, or upload a new one using multipart/form-data. Lastly you can pass - an existing :class:`telegram.Audio` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the audio, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional): Audio caption, 0-1024 characters after entities - parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - duration (:obj:`int`, optional): Duration of sent audio in seconds. - performer (:obj:`str`, optional): Performer. - title (:obj:`str`, optional): Track name. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail - of the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'audio': parse_file_input(audio, Audio, filename=filename), - 'parse_mode': parse_mode, - } - - if duration: - data['duration'] = duration - if performer: - data['performer'] = performer - if title: - data['title'] = title - if caption: - data['caption'] = caption - - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) - - return self._message( # type: ignore[return-value] - 'sendAudio', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_document( - self, - chat_id: Union[int, str], - document: Union[FileInput, 'Document'], - filename: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - disable_content_type_detection: bool = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send general files. - - Bots can currently send files of any type of up to 50 MB in size, this limit may be - changed in the future. - - Note: - The document argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - document (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Document`): File to send. - Pass a file_id as String to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get a file from the - Internet, or upload a new one using multipart/form-data. Lastly you can pass - an existing :class:`telegram.Document` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the document, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - caption (:obj:`str`, optional): Document caption (may also be used when resending - documents by file_id), 0-1024 characters after entities parsing. - disable_content_type_detection (:obj:`bool`, optional): Disables automatic server-side - content type detection for files uploaded using multipart/form-data. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail - of the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'document': parse_file_input(document, Document, filename=filename), - 'parse_mode': parse_mode, - } - - if caption: - data['caption'] = caption - - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - if disable_content_type_detection is not None: - data['disable_content_type_detection'] = disable_content_type_detection - if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) - - return self._message( # type: ignore[return-value] - 'sendDocument', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_sticker( - self, - chat_id: Union[int, str], - sticker: Union[FileInput, 'Sticker'], - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send static ``.WEBP``, animated ``.TGS``, or video ``.WEBM`` stickers. - - Note: - The sticker argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Sticker`): Sticker to send. - Pass a file_id as String to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get a .webp file from - the Internet, or upload a new one using multipart/form-data. Lastly you can pass - an existing :class:`telegram.Sticker` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'sticker': parse_file_input(sticker, Sticker)} - - return self._message( # type: ignore[return-value] - 'sendSticker', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_video( - self, - chat_id: Union[int, str], - video: Union[FileInput, 'Video'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - width: int = None, - height: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - supports_streaming: bool = None, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send video files, Telegram clients support mp4 videos - (other formats may be sent as Document). - - Bots can currently send video files of up to 50 MB in size, this limit may be changed in - the future. - - Note: - * The video argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - * ``thumb`` will be ignored for small video files, for which Telegram can easily - generate thumb nails. However, this behaviour is undocumented and might be changed - by Telegram. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - video (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Video`): Video file to send. - Pass a file_id as String to send an video file that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get an video file from - the Internet, or upload a new one using multipart/form-data. Lastly you can pass - an existing :class:`telegram.Video` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the video, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - duration (:obj:`int`, optional): Duration of sent video in seconds. - width (:obj:`int`, optional): Video width. - height (:obj:`int`, optional): Video height. - caption (:obj:`str`, optional): Video caption (may also be used when resending videos - by file_id), 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is - suitable for streaming. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail - of the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'video': parse_file_input(video, Video, filename=filename), - 'parse_mode': parse_mode, - } - - if duration: - data['duration'] = duration - if caption: - data['caption'] = caption - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - if supports_streaming: - data['supports_streaming'] = supports_streaming - if width: - data['width'] = width - if height: - data['height'] = height - if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) - - return self._message( # type: ignore[return-value] - 'sendVideo', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_video_note( - self, - chat_id: Union[int, str], - video_note: Union[FileInput, 'VideoNote'], - duration: int = None, - length: int = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - filename: str = None, - protect_content: bool = None, - ) -> Message: - """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. - Use this method to send video messages. - - Note: - * The video_note argument can be either a file_id or a file from disk - ``open(filename, 'rb')`` - * ``thumb`` will be ignored for small video files, for which Telegram can easily - generate thumb nails. However, this behaviour is undocumented and might be changed - by Telegram. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - video_note (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.VideoNote`): Video note - to send. Pass a file_id as String to send a video note that exists on the Telegram - servers (recommended) or upload a new video using multipart/form-data. Or you can - pass an existing :class:`telegram.VideoNote` object to send. Sending video notes by - a URL is currently unsupported. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the video note, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - duration (:obj:`int`, optional): Duration of sent video in seconds. - length (:obj:`int`, optional): Video width and height, i.e. diameter of the video - message. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail - of the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'video_note': parse_file_input(video_note, VideoNote, filename=filename), - } - - if duration is not None: - data['duration'] = duration - if length is not None: - data['length'] = length - if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) - - return self._message( # type: ignore[return-value] - 'sendVideoNote', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_animation( - self, - chat_id: Union[int, str], - animation: Union[FileInput, 'Animation'], - duration: int = None, - width: int = None, - height: int = None, - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). - Bots can currently send animation files of up to 50 MB in size, this limit may be changed - in the future. - - Note: - ``thumb`` will be ignored for small files, for which Telegram can easily - generate thumb nails. However, this behaviour is undocumented and might be changed - by Telegram. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - animation (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Animation`): Animation to - send. Pass a file_id as String to send an animation that exists on the Telegram - servers (recommended), pass an HTTP URL as a String for Telegram to get an - animation from the Internet, or upload a new animation using multipart/form-data. - Lastly you can pass an existing :class:`telegram.Animation` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the animation, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - duration (:obj:`int`, optional): Duration of sent animation in seconds. - width (:obj:`int`, optional): Animation width. - height (:obj:`int`, optional): Animation height. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail - of the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - caption (:obj:`str`, optional): Animation caption (may also be used when resending - animations by file_id), 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'animation': parse_file_input(animation, Animation, filename=filename), - 'parse_mode': parse_mode, - } - - if duration: - data['duration'] = duration - if width: - data['width'] = width - if height: - data['height'] = height - if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) - if caption: - data['caption'] = caption - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - - return self._message( # type: ignore[return-value] - 'sendAnimation', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_voice( - self, - chat_id: Union[int, str], - voice: Union[FileInput, 'Voice'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send audio files, if you want Telegram clients to display the file - as a playable voice message. For this to work, your audio must be in an .ogg file - encoded with OPUS (other formats may be sent as Audio or Document). Bots can currently - send voice messages of up to 50 MB in size, this limit may be changed in the future. - - Note: - The voice argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - voice (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Voice`): Voice file to send. - Pass a file_id as String to send an voice file that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get an voice file from - the Internet, or upload a new one using multipart/form-data. Lastly you can pass - an existing :class:`telegram.Voice` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the voice, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional): Voice message caption, 0-1024 characters after entities - parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - duration (:obj:`int`, optional): Duration of the voice message in seconds. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'voice': parse_file_input(voice, Voice, filename=filename), - 'parse_mode': parse_mode, - } - - if duration: - data['duration'] = duration - if caption: - data['caption'] = caption - - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - - return self._message( # type: ignore[return-value] - 'sendVoice', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_media_group( - self, - chat_id: Union[int, str], - media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] - ], - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> List[Message]: - """Use this method to send a group of photos or videos as an album. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - media (List[:class:`telegram.InputMediaAudio`, :class:`telegram.InputMediaDocument`, \ - :class:`telegram.InputMediaPhoto`, :class:`telegram.InputMediaVideo`]): An array - describing messages to be sent, must include 2–10 items. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - timeout (:obj:`int` | :obj:`float`, optional): Send file timeout (default: 20 seconds). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - List[:class:`telegram.Message`]: An array of the sent Messages. - - Raises: - :class:`telegram.error.TelegramError` - """ - data: JSONDict = { - 'chat_id': chat_id, - 'media': media, - 'disable_notification': disable_notification, - 'allow_sending_without_reply': allow_sending_without_reply, - } - - for med in data['media']: - if med.parse_mode == DEFAULT_NONE: - if self.defaults: - med.parse_mode = DefaultValue.get_value(self.defaults.parse_mode) - else: - med.parse_mode = None - - if reply_to_message_id: - data['reply_to_message_id'] = reply_to_message_id - - if protect_content: - data['protect_content'] = protect_content - - result = self._post('sendMediaGroup', data, timeout=timeout, api_kwargs=api_kwargs) - - return Message.de_list(result, self) # type: ignore - - @log - def send_location( - self, - chat_id: Union[int, str], - latitude: float = None, - longitude: float = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - location: Location = None, - live_period: int = None, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> Message: - """Use this method to send point on the map. - - Note: - You can either supply a :obj:`latitude` and :obj:`longitude` or a :obj:`location`. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - latitude (:obj:`float`, optional): Latitude of location. - longitude (:obj:`float`, optional): Longitude of location. - location (:class:`telegram.Location`, optional): The location to send. - horizontal_accuracy (:obj:`int`, optional): The radius of uncertainty for the location, - measured in meters; 0-1500. - live_period (:obj:`int`, optional): Period in seconds for which the location will be - updated, should be between 60 and 86400. - heading (:obj:`int`, optional): For live locations, a direction in which the user is - moving, in degrees. Must be between 1 and 360 if specified. - proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance - for proximity alerts about approaching another chat member, in meters. Must be - between 1 and 100000 if specified. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - if not ((latitude is not None and longitude is not None) or location): - raise ValueError( - "Either location or latitude and longitude must be passed as argument." - ) - - if not (latitude is not None or longitude is not None) ^ bool(location): - raise ValueError( - "Either location or latitude and longitude must be passed as argument. Not both." - ) - - if isinstance(location, Location): - latitude = location.latitude - longitude = location.longitude - - data: JSONDict = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} - - if live_period: - data['live_period'] = live_period - if horizontal_accuracy: - data['horizontal_accuracy'] = horizontal_accuracy - if heading: - data['heading'] = heading - if proximity_alert_radius: - data['proximity_alert_radius'] = proximity_alert_radius - - return self._message( # type: ignore[return-value] - 'sendLocation', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def edit_message_live_location( - self, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - latitude: float = None, - longitude: float = None, - location: Location = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - ) -> Union[Message, bool]: - """Use this method to edit live location messages sent by the bot or via the bot - (for inline bots). A location can be edited until its :attr:`telegram.Location.live_period` - expires or editing is explicitly disabled by a call to :meth:`stop_message_live_location`. - - Note: - You can either supply a :obj:`latitude` and :obj:`longitude` or a :obj:`location`. - - Args: - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat or username of the target channel - (in the format ``@channelusername``). - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the message to edit. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - latitude (:obj:`float`, optional): Latitude of location. - longitude (:obj:`float`, optional): Longitude of location. - location (:class:`telegram.Location`, optional): The location to send. - horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the - location, measured in meters; 0-1500. - heading (:obj:`int`, optional): Direction in which the user is moving, in degrees. Must - be between 1 and 360 if specified. - proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts - about approaching another chat member, in meters. Must be between 1 and 100000 if - specified. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for a new inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, if edited message is not an inline message, the - edited message is returned, otherwise :obj:`True` is returned. - """ - if not (all([latitude, longitude]) or location): - raise ValueError( - "Either location or latitude and longitude must be passed as argument." - ) - if not (latitude is not None or longitude is not None) ^ bool(location): - raise ValueError( - "Either location or latitude and longitude must be passed as argument. Not both." - ) - - if isinstance(location, Location): - latitude = location.latitude - longitude = location.longitude - - data: JSONDict = {'latitude': latitude, 'longitude': longitude} - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - if horizontal_accuracy: - data['horizontal_accuracy'] = horizontal_accuracy - if heading: - data['heading'] = heading - if proximity_alert_radius: - data['proximity_alert_radius'] = proximity_alert_radius - - return self._message( - 'editMessageLiveLocation', - data, - timeout=timeout, - reply_markup=reply_markup, - api_kwargs=api_kwargs, - ) - - @log - def stop_message_live_location( - self, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """Use this method to stop updating a live location message sent by the bot or via the bot - (for inline bots) before live_period expires. - - Args: - chat_id (:obj:`int` | :obj:`str`): Required if inline_message_id is not specified. - Unique identifier for the target chat or username of the target channel - (in the format ``@channelusername``). - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the sent message with live location to stop. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for a new inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - sent Message is returned, otherwise :obj:`True` is returned. - """ - data: JSONDict = {} - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - - return self._message( - 'stopMessageLiveLocation', - data, - timeout=timeout, - reply_markup=reply_markup, - api_kwargs=api_kwargs, - ) - - @log - def send_venue( - self, - chat_id: Union[int, str], - latitude: float = None, - longitude: float = None, - title: str = None, - address: str = None, - foursquare_id: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - venue: Venue = None, - foursquare_type: str = None, - api_kwargs: JSONDict = None, - google_place_id: str = None, - google_place_type: str = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> Message: - """Use this method to send information about a venue. - - Note: - * You can either supply :obj:`venue`, or :obj:`latitude`, :obj:`longitude`, - :obj:`title` and :obj:`address` and optionally :obj:`foursquare_id` and - :obj:`foursquare_type` or optionally :obj:`google_place_id` and - :obj:`google_place_type`. - * Foursquare details and Google Pace details are mutually exclusive. However, this - behaviour is undocumented and might be changed by Telegram. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - latitude (:obj:`float`, optional): Latitude of venue. - longitude (:obj:`float`, optional): Longitude of venue. - title (:obj:`str`, optional): Name of the venue. - address (:obj:`str`, optional): Address of the venue. - foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue. - foursquare_type (:obj:`str`, optional): Foursquare type of the venue, if known. - (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or - "food/icecream".) - google_place_id (:obj:`str`, optional): Google Places identifier of the venue. - google_place_type (:obj:`str`, optional): Google Places type of the venue. (See - `supported types \ - <https://developers.google.com/places/web-service/supported_types>`_.) - venue (:class:`telegram.Venue`, optional): The venue to send. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - if not (venue or all([latitude, longitude, address, title])): - raise ValueError( - "Either venue or latitude, longitude, address and title must be" - "passed as arguments." - ) - - if isinstance(venue, Venue): - latitude = venue.location.latitude - longitude = venue.location.longitude - address = venue.address - title = venue.title - foursquare_id = venue.foursquare_id - foursquare_type = venue.foursquare_type - google_place_id = venue.google_place_id - google_place_type = venue.google_place_type - - data: JSONDict = { - 'chat_id': chat_id, - 'latitude': latitude, - 'longitude': longitude, - 'address': address, - 'title': title, - } - - if foursquare_id: - data['foursquare_id'] = foursquare_id - if foursquare_type: - data['foursquare_type'] = foursquare_type - if google_place_id: - data['google_place_id'] = google_place_id - if google_place_type: - data['google_place_type'] = google_place_type - - return self._message( # type: ignore[return-value] - 'sendVenue', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_contact( - self, - chat_id: Union[int, str], - phone_number: str = None, - first_name: str = None, - last_name: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - contact: Contact = None, - vcard: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> Message: - """Use this method to send phone contacts. - - Note: - You can either supply :obj:`contact` or :obj:`phone_number` and :obj:`first_name` - with optionally :obj:`last_name` and optionally :obj:`vcard`. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - phone_number (:obj:`str`, optional): Contact's phone number. - first_name (:obj:`str`, optional): Contact's first name. - last_name (:obj:`str`, optional): Contact's last name. - vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, - 0-2048 bytes. - contact (:class:`telegram.Contact`, optional): The contact to send. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - if (not contact) and (not all([phone_number, first_name])): - raise ValueError( - "Either contact or phone_number and first_name must be passed as arguments." - ) - - if isinstance(contact, Contact): - phone_number = contact.phone_number - first_name = contact.first_name - last_name = contact.last_name - vcard = contact.vcard - - data: JSONDict = { - 'chat_id': chat_id, - 'phone_number': phone_number, - 'first_name': first_name, - } - - if last_name: - data['last_name'] = last_name - if vcard: - data['vcard'] = vcard - - return self._message( # type: ignore[return-value] - 'sendContact', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_game( - self, - chat_id: Union[int, str], - game_short_name: str, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> Message: - """Use this method to send a game. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat. - game_short_name (:obj:`str`): Short name of the game, serves as the unique identifier - for the game. Set up your games via `@BotFather <https://t.me/BotFather>`_. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for a new inline keyboard. If empty, one ‘Play game_title’ button will be - shown. If not empty, the first button must launch the game. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'game_short_name': game_short_name} - - return self._message( # type: ignore[return-value] - 'sendGame', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def send_chat_action( - self, - chat_id: Union[str, int], - action: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method when you need to tell the user that something is happening on the bot's - side. The status is set for 5 seconds or less (when a message arrives from your bot, - Telegram clients clear its typing status). Telegram only recommends using this method when - a response from the bot will take a noticeable amount of time to arrive. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - action(:class:`telegram.ChatAction` | :obj:`str`): Type of action to broadcast. Choose - one, depending on what the user is about to receive. For convenience look at the - constants in :class:`telegram.ChatAction` - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'action': action} - - result = self._post('sendChatAction', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - def _effective_inline_results( # pylint: disable=R0201 - self, - results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] - ], - next_offset: str = None, - current_offset: str = None, - ) -> Tuple[Sequence['InlineQueryResult'], Optional[str]]: - """ - Builds the effective results from the results input. - We make this a stand-alone method so tg.ext.ExtBot can wrap it. - - Returns: - Tuple of 1. the effective results and 2. correct the next_offset - - """ - if current_offset is not None and next_offset is not None: - raise ValueError('`current_offset` and `next_offset` are mutually exclusive!') - - if current_offset is not None: - # Convert the string input to integer - if current_offset == '': - current_offset_int = 0 - else: - current_offset_int = int(current_offset) - - # for now set to empty string, stating that there are no more results - # might change later - next_offset = '' - - if callable(results): - callable_output = results(current_offset_int) - if not callable_output: - effective_results: Sequence['InlineQueryResult'] = [] - else: - effective_results = callable_output - # the callback *might* return more results on the next call, so we increment - # the page count - next_offset = str(current_offset_int + 1) - else: - if len(results) > (current_offset_int + 1) * MAX_INLINE_QUERY_RESULTS: - # we expect more results for the next page - next_offset_int = current_offset_int + 1 - next_offset = str(next_offset_int) - effective_results = results[ - current_offset_int - * MAX_INLINE_QUERY_RESULTS : next_offset_int - * MAX_INLINE_QUERY_RESULTS - ] - else: - effective_results = results[current_offset_int * MAX_INLINE_QUERY_RESULTS :] - else: - effective_results = results # type: ignore[assignment] - - return effective_results, next_offset - - @log - def answer_inline_query( - self, - inline_query_id: str, - results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] - ], - cache_time: int = 300, - is_personal: bool = None, - next_offset: str = None, - switch_pm_text: str = None, - switch_pm_parameter: str = None, - timeout: ODVInput[float] = DEFAULT_NONE, - current_offset: str = None, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to send answers to an inline query. No more than 50 results per query are - allowed. - - Warning: - In most use cases :attr:`current_offset` should not be passed manually. Instead of - calling this method directly, use the shortcut :meth:`telegram.InlineQuery.answer` with - ``auto_pagination=True``, which will take care of passing the correct value. - - Args: - inline_query_id (:obj:`str`): Unique identifier for the answered query. - results (List[:class:`telegram.InlineQueryResult`] | Callable): A list of results for - the inline query. In case :attr:`current_offset` is passed, ``results`` may also be - a callable that accepts the current page index starting from 0. It must return - either a list of :class:`telegram.InlineQueryResult` instances or :obj:`None` if - there are no more results. - cache_time (:obj:`int`, optional): The maximum amount of time in seconds that the - result of the inline query may be cached on the server. Defaults to ``300``. - is_personal (:obj:`bool`, optional): Pass :obj:`True`, if results may be cached on - the server side only for the user that sent the query. By default, - results may be returned to any user who sends the same query. - next_offset (:obj:`str`, optional): Pass the offset that a client should send in the - next query with the same text to receive more results. Pass an empty string if - there are no more results or if you don't support pagination. Offset length can't - exceed 64 bytes. - switch_pm_text (:obj:`str`, optional): If passed, clients will display a button with - specified text that switches the user to a private chat with the bot and sends the - bot a start message with the parameter ``switch_pm_parameter``. - switch_pm_parameter (:obj:`str`, optional): Deep-linking parameter for the /start - message sent to the bot when user presses the switch button. 1-64 characters, - only A-Z, a-z, 0-9, _ and - are allowed. - current_offset (:obj:`str`, optional): The :attr:`telegram.InlineQuery.offset` of - the inline query to answer. If passed, PTB will automatically take care of - the pagination for you, i.e. pass the correct ``next_offset`` and truncate the - results list/get the results from the callable you passed. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Example: - An inline bot that sends YouTube videos can ask the user to connect the bot to their - YouTube account to adapt search results accordingly. To do this, it displays a - 'Connect your YouTube account' button above the results, or even before showing any. - The user presses the button, switches to a private chat with the bot and, in doing so, - passes a start parameter that instructs the bot to return an oauth link. Once done, the - bot can offer a switch_inline button so that the user can easily return to the chat - where they wanted to use the bot's inline capabilities. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - - @no_type_check - def _set_defaults(res): - # pylint: disable=W0212 - if hasattr(res, 'parse_mode') and res.parse_mode == DEFAULT_NONE: - if self.defaults: - res.parse_mode = self.defaults.parse_mode - else: - res.parse_mode = None - if hasattr(res, 'input_message_content') and res.input_message_content: - if ( - hasattr(res.input_message_content, 'parse_mode') - and res.input_message_content.parse_mode == DEFAULT_NONE - ): - if self.defaults: - res.input_message_content.parse_mode = DefaultValue.get_value( - self.defaults.parse_mode - ) - else: - res.input_message_content.parse_mode = None - if ( - hasattr(res.input_message_content, 'disable_web_page_preview') - and res.input_message_content.disable_web_page_preview == DEFAULT_NONE - ): - if self.defaults: - res.input_message_content.disable_web_page_preview = ( - DefaultValue.get_value(self.defaults.disable_web_page_preview) - ) - else: - res.input_message_content.disable_web_page_preview = None - - effective_results, next_offset = self._effective_inline_results( - results=results, next_offset=next_offset, current_offset=current_offset - ) - - # Apply defaults - for result in effective_results: - _set_defaults(result) - - results_dicts = [res.to_dict() for res in effective_results] - - data: JSONDict = {'inline_query_id': inline_query_id, 'results': results_dicts} - - if cache_time or cache_time == 0: - data['cache_time'] = cache_time - if is_personal: - data['is_personal'] = is_personal - if next_offset is not None: - data['next_offset'] = next_offset - if switch_pm_text: - data['switch_pm_text'] = switch_pm_text - if switch_pm_parameter: - data['switch_pm_parameter'] = switch_pm_parameter - - return self._post( # type: ignore[return-value] - 'answerInlineQuery', - data, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - @log - def get_user_profile_photos( - self, - user_id: Union[str, int], - offset: int = None, - limit: int = 100, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Optional[UserProfilePhotos]: - """Use this method to get a list of profile pictures for a user. - - Args: - user_id (:obj:`int`): Unique identifier of the target user. - offset (:obj:`int`, optional): Sequential number of the first photo to be returned. - By default, all photos are returned. - limit (:obj:`int`, optional): Limits the number of photos to be retrieved. Values - between 1-100 are accepted. Defaults to ``100``. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.UserProfilePhotos` - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'user_id': user_id} - - if offset is not None: - data['offset'] = offset - if limit: - data['limit'] = limit - - result = self._post('getUserProfilePhotos', data, timeout=timeout, api_kwargs=api_kwargs) - - return UserProfilePhotos.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def get_file( - self, - file_id: Union[ - str, Animation, Audio, ChatPhoto, Document, PhotoSize, Sticker, Video, VideoNote, Voice - ], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> File: - """ - Use this method to get basic info about a file and prepare it for downloading. For the - moment, bots can download files of up to 20MB in size. The file can then be downloaded - with :meth:`telegram.File.download`. It is guaranteed that the link will be - valid for at least 1 hour. When the link expires, a new one can be requested by - calling get_file again. - - Note: - This function may not preserve the original file name and MIME type. - You should save the file's MIME type and name (if available) when the File object - is received. - - Args: - file_id (:obj:`str` | :class:`telegram.Animation` | :class:`telegram.Audio` | \ - :class:`telegram.ChatPhoto` | :class:`telegram.Document` | \ - :class:`telegram.PhotoSize` | :class:`telegram.Sticker` | \ - :class:`telegram.Video` | :class:`telegram.VideoNote` | \ - :class:`telegram.Voice`): - Either the file identifier or an object that has a file_id attribute - to get file information about. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - try: - file_id = file_id.file_id # type: ignore[union-attr] - except AttributeError: - pass - - data: JSONDict = {'file_id': file_id} - - result = self._post('getFile', data, timeout=timeout, api_kwargs=api_kwargs) - - if result.get('file_path') and not is_local_file( # type: ignore[union-attr] - result['file_path'] # type: ignore[index] - ): - result['file_path'] = '{}/{}'.format( # type: ignore[index] - self.base_file_url, result['file_path'] # type: ignore[index] - ) - - return File.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def kick_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Deprecated, use :func:`~telegram.Bot.ban_chat_member` instead. - - .. deprecated:: 13.7 - - """ - warnings.warn( - '`bot.kick_chat_member` is deprecated. Use `bot.ban_chat_member` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return self.ban_chat_member( - chat_id=chat_id, - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - - @log - def ban_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Use this method to ban a user from a group, supergroup or a channel. In the case of - supergroups and channels, the user will not be able to return to the group on their own - using invite links, etc., unless unbanned first. The bot must be an administrator in the - chat for this to work and must have the appropriate admin rights. - - .. versionadded:: 13.7 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target group or username - of the target supergroup or channel (in the format ``@channelusername``). - user_id (:obj:`int`): Unique identifier of the target user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - until_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the user will - be unbanned, unix time. If user is banned for more than 366 days or less than 30 - seconds from the current time they are considered to be banned forever. Applied - for supergroups and channels only. - For timezone naive :obj:`datetime.datetime` objects, the default timezone of the - bot will be used. - revoke_messages (:obj:`bool`, optional): Pass :obj:`True` to delete all messages from - the chat for the user that is being removed. If :obj:`False`, the user will be able - to see messages in the group that were sent before the user was removed. - Always :obj:`True` for supergroups and channels. - - .. versionadded:: 13.4 - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} - - if until_date is not None: - if isinstance(until_date, datetime): - until_date = to_timestamp( - until_date, tzinfo=self.defaults.tzinfo if self.defaults else None - ) - data['until_date'] = until_date - - if revoke_messages is not None: - data['revoke_messages'] = revoke_messages - - result = self._post('banChatMember', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def ban_chat_sender_chat( - self, - chat_id: Union[str, int], - sender_chat_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to ban a channel chat in a supergroup or a channel. Until the chat is - unbanned, the owner of the banned chat won't be able to send messages on behalf of **any of - their channels**. The bot must be an administrator in the supergroup or channel for this - to work and must have the appropriate administrator rights. - - .. versionadded:: 13.9 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target group or username - of the target supergroup or channel (in the format ``@channelusername``). - sender_chat_id (:obj:`int`): Unique identifier of the target sender chat. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} - - result = self._post('banChatSenderChat', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def unban_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - only_if_banned: bool = None, - ) -> bool: - """Use this method to unban a previously kicked user in a supergroup or channel. - - The user will *not* return to the group or channel automatically, but will be able to join - via link, etc. The bot must be an administrator for this to work. By default, this method - guarantees that after the call the user is not a member of the chat, but will be able to - join it. So if the user is a member of the chat they will also be *removed* from the chat. - If you don't want this, use the parameter :attr:`only_if_banned`. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - user_id (:obj:`int`): Unique identifier of the target user. - only_if_banned (:obj:`bool`, optional): Do nothing if the user is not banned. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} - - if only_if_banned is not None: - data['only_if_banned'] = only_if_banned - - result = self._post('unbanChatMember', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def unban_chat_sender_chat( - self, - chat_id: Union[str, int], - sender_chat_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to unban a previously banned channel in a supergroup or channel. - The bot must be an administrator for this to work and must have the - appropriate administrator rights. - - .. versionadded:: 13.9 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - sender_chat_id (:obj:`int`): Unique identifier of the target sender chat. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'sender_chat_id': sender_chat_id} - - result = self._post('unbanChatSenderChat', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def answer_callback_query( - self, - callback_query_id: str, - text: str = None, - show_alert: bool = False, - url: str = None, - cache_time: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to send answers to callback queries sent from inline keyboards. The answer - will be displayed to the user as a notification at the top of the chat screen or as an - alert. - Alternatively, the user can be redirected to the specified Game URL. For this option to - work, you must first create a game for your bot via `@BotFather <https://t.me/BotFather>`_ - and accept the terms. Otherwise, you may use links like t.me/your_bot?start=XXXX that open - your bot with a parameter. - - Args: - callback_query_id (:obj:`str`): Unique identifier for the query to be answered. - text (:obj:`str`, optional): Text of the notification. If not specified, nothing will - be shown to the user, 0-200 characters. - show_alert (:obj:`bool`, optional): If :obj:`True`, an alert will be shown by the - client instead of a notification at the top of the chat screen. Defaults to - :obj:`False`. - url (:obj:`str`, optional): URL that will be opened by the user's client. If you have - created a Game and accepted the conditions via - `@BotFather <https://t.me/BotFather>`_, specify the URL that - opens your game - note that this will only work if the query comes from a callback - game button. Otherwise, you may use links like t.me/your_bot?start=XXXX that open - your bot with a parameter. - cache_time (:obj:`int`, optional): The maximum amount of time in seconds that the - result of the callback query may be cached client-side. Defaults to 0. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool` On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'callback_query_id': callback_query_id} - - if text: - data['text'] = text - if show_alert: - data['show_alert'] = show_alert - if url: - data['url'] = url - if cache_time is not None: - data['cache_time'] = cache_time - - result = self._post('answerCallbackQuery', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def edit_message_text( - self, - text: str, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union[Message, bool]: - """ - Use this method to edit text and game messages. - - Args: - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat or username of the target channel - (in the format ``@channelusername``) - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the message to edit. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - text (:obj:`str`): New text of the message, 1-4096 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in your bot's message. See the - constants in :class:`telegram.ParseMode` for the available modes. - entities (List[:class:`telegram.MessageEntity`], optional): List of special entities - that appear in message text, which can be specified instead of :attr:`parse_mode`. - disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in - this message. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for an inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, if edited message is not an inline message, the - edited message is returned, otherwise :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'text': text, - 'parse_mode': parse_mode, - 'disable_web_page_preview': disable_web_page_preview, - } - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - if entities: - data['entities'] = [me.to_dict() for me in entities] - - return self._message( - 'editMessageText', - data, - timeout=timeout, - reply_markup=reply_markup, - api_kwargs=api_kwargs, - ) - - @log - def edit_message_caption( - self, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - caption: str = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union[Message, bool]: - """ - Use this method to edit captions of messages. - - Args: - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat or username of the target channel - (in the format ``@channelusername``) - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the message to edit. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - caption (:obj:`str`, optional): New caption of the message, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to - show bold, italic, fixed-width text or inline URLs in the media caption. See the - constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for an inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, if edited message is not an inline message, the - edited message is returned, otherwise :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - if inline_message_id is None and (chat_id is None or message_id is None): - raise ValueError( - 'edit_message_caption: Both chat_id and message_id are required when ' - 'inline_message_id is not specified' - ) - - data: JSONDict = {'parse_mode': parse_mode} - - if caption: - data['caption'] = caption - if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - - return self._message( - 'editMessageCaption', - data, - timeout=timeout, - reply_markup=reply_markup, - api_kwargs=api_kwargs, - ) - - @log - def edit_message_media( - self, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - media: 'InputMedia' = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """ - Use this method to edit animation, audio, document, photo, or video messages. If a message - is part of a message album, then it can be edited only to an audio for audio albums, only - to a document for document albums and to a photo or a video otherwise. When an inline - message is edited, a new file can't be uploaded. Use a previously uploaded file via its - ``file_id`` or specify a URL. - - Args: - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat or username of the target channel - (in the format ``@channelusername``). - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the message to edit. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - media (:class:`telegram.InputMedia`): An object for a new media content - of the message. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for an inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - """ - if inline_message_id is None and (chat_id is None or message_id is None): - raise ValueError( - 'edit_message_media: Both chat_id and message_id are required when ' - 'inline_message_id is not specified' - ) - - data: JSONDict = {'media': media} - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - - return self._message( - 'editMessageMedia', - data, - timeout=timeout, - reply_markup=reply_markup, - api_kwargs=api_kwargs, - ) - - @log - def edit_message_reply_markup( - self, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - reply_markup: Optional['InlineKeyboardMarkup'] = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """ - Use this method to edit only the reply markup of messages sent by the bot or via the bot - (for inline bots). - - Args: - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat or username of the target channel - (in the format ``@channelusername``). - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the message to edit. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for an inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, if edited message is not an inline message, the - edited message is returned, otherwise :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - if inline_message_id is None and (chat_id is None or message_id is None): - raise ValueError( - 'edit_message_reply_markup: Both chat_id and message_id are required when ' - 'inline_message_id is not specified' - ) - - data: JSONDict = {} - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - - return self._message( - 'editMessageReplyMarkup', - data, - timeout=timeout, - reply_markup=reply_markup, - api_kwargs=api_kwargs, - ) - - @log - def get_updates( - self, - offset: int = None, - limit: int = 100, - timeout: float = 0, - read_latency: float = 2.0, - allowed_updates: List[str] = None, - api_kwargs: JSONDict = None, - ) -> List[Update]: - """Use this method to receive incoming updates using long polling. - - Args: - offset (:obj:`int`, optional): Identifier of the first update to be returned. Must be - greater by one than the highest among the identifiers of previously received - updates. By default, updates starting with the earliest unconfirmed update are - returned. An update is considered confirmed as soon as getUpdates is called with an - offset higher than its :attr:`telegram.Update.update_id`. The negative offset can - be specified to retrieve updates starting from -offset update from the end of the - updates queue. All previous updates will forgotten. - limit (:obj:`int`, optional): Limits the number of updates to be retrieved. Values - between 1-100 are accepted. Defaults to ``100``. - timeout (:obj:`int`, optional): Timeout in seconds for long polling. Defaults to ``0``, - i.e. usual short polling. Should be positive, short polling should be used for - testing purposes only. - read_latency (:obj:`float` | :obj:`int`, optional): Grace time in seconds for receiving - the reply from server. Will be added to the ``timeout`` value and used as the read - timeout from server. Defaults to ``2``. - allowed_updates (List[:obj:`str`]), optional): A JSON-serialized list the types of - updates you want your bot to receive. For example, specify ["message", - "edited_channel_post", "callback_query"] to only receive updates of these types. - See :class:`telegram.Update` for a complete list of available update types. - Specify an empty list to receive all updates except - :attr:`telegram.Update.chat_member` (default). If not specified, the previous - setting will be used. Please note that this parameter doesn't affect updates - created before the call to the get_updates, so unwanted updates may be received for - a short period of time. - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Note: - 1. This method will not work if an outgoing webhook is set up. - 2. In order to avoid getting duplicate updates, recalculate offset after each - server response. - 3. To take full advantage of this library take a look at :class:`telegram.ext.Updater` - - Returns: - List[:class:`telegram.Update`] - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'timeout': timeout} - - if offset: - data['offset'] = offset - if limit: - data['limit'] = limit - if allowed_updates is not None: - data['allowed_updates'] = allowed_updates - - # Ideally we'd use an aggressive read timeout for the polling. However, - # * Short polling should return within 2 seconds. - # * Long polling poses a different problem: the connection might have been dropped while - # waiting for the server to return and there's no way of knowing the connection had been - # dropped in real time. - result = cast( - List[JSONDict], - self._post( - 'getUpdates', - data, - timeout=float(read_latency) + float(timeout), - api_kwargs=api_kwargs, - ), - ) - - if result: - self.logger.debug('Getting updates: %s', [u['update_id'] for u in result]) - else: - self.logger.debug('No new updates found.') - - return Update.de_list(result, self) # type: ignore[return-value] - - @log - def set_webhook( - self, - url: str = None, - certificate: FileInput = None, - timeout: ODVInput[float] = DEFAULT_NONE, - max_connections: int = 40, - allowed_updates: List[str] = None, - api_kwargs: JSONDict = None, - ip_address: str = None, - drop_pending_updates: bool = None, - ) -> bool: - """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. - Whenever there is an update for the bot, Telegram will send an HTTPS POST request to the - specified url, containing a JSON-serialized Update. In case of an unsuccessful request, - Telegram will give up after a reasonable amount of attempts. - - If you'd like to make sure that the Webhook request comes from Telegram, Telegram - recommends using a secret path in the URL, e.g. https://www.example.com/<token>. Since - nobody else knows your bot's token, you can be pretty sure it's us. - - Note: - The certificate argument should be a file from disk ``open(filename, 'rb')``. - - Args: - url (:obj:`str`): HTTPS url to send updates to. Use an empty string to remove webhook - integration. - certificate (:obj:`filelike`): Upload your public key certificate so that the root - certificate in use can be checked. See our self-signed guide for details. - (https://goo.gl/rw7w6Y) - ip_address (:obj:`str`, optional): The fixed IP address which will be used to send - webhook requests instead of the IP address resolved through DNS. - max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS - connections to the webhook for update delivery, 1-100. Defaults to ``40``. Use - lower values to limit the load on your bot's server, and higher values to increase - your bot's throughput. - allowed_updates (List[:obj:`str`], optional): A JSON-serialized list the types of - updates you want your bot to receive. For example, specify ["message", - "edited_channel_post", "callback_query"] to only receive updates of these types. - See :class:`telegram.Update` for a complete list of available update types. - Specify an empty list to receive all updates except - :attr:`telegram.Update.chat_member` (default). If not specified, the previous - setting will be used. Please note that this parameter doesn't affect updates - created before the call to the set_webhook, so unwanted updates may be received for - a short period of time. - drop_pending_updates (:obj:`bool`, optional): Pass :obj:`True` to drop all pending - updates. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Note: - 1. You will not be able to receive updates using :meth:`get_updates` for long as an - outgoing webhook is set up. - 2. To use a self-signed certificate, you need to upload your public key certificate - using certificate parameter. Please upload as InputFile, sending a String will not - work. - 3. Ports currently supported for Webhooks: ``443``, ``80``, ``88``, ``8443``. - - If you're having any trouble setting up webhooks, please check out this `guide to - Webhooks`_. - - Returns: - :obj:`bool` On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - .. _`guide to Webhooks`: https://core.telegram.org/bots/webhooks - - """ - data: JSONDict = {} - - if url is not None: - data['url'] = url - if certificate: - data['certificate'] = parse_file_input(certificate) - if max_connections is not None: - data['max_connections'] = max_connections - if allowed_updates is not None: - data['allowed_updates'] = allowed_updates - if ip_address: - data['ip_address'] = ip_address - if drop_pending_updates: - data['drop_pending_updates'] = drop_pending_updates - - result = self._post('setWebhook', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def delete_webhook( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - drop_pending_updates: bool = None, - ) -> bool: - """ - Use this method to remove webhook integration if you decide to switch back to - :meth:`get_updates()`. - - Args: - drop_pending_updates (:obj:`bool`, optional): Pass :obj:`True` to drop all pending - updates. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data = {} - - if drop_pending_updates: - data['drop_pending_updates'] = drop_pending_updates - - result = self._post('deleteWebhook', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def leave_chat( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method for your bot to leave a group, supergroup or channel. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('leaveChat', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def get_chat( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Chat: - """ - Use this method to get up to date information about the chat (current name of the user for - one-on-one conversations, current username of a user, group or channel, etc.). - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Chat` - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('getChat', data, timeout=timeout, api_kwargs=api_kwargs) - - return Chat.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def get_chat_administrators( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> List[ChatMember]: - """ - Use this method to get a list of administrators in a chat. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - List[:class:`telegram.ChatMember`]: On success, returns a list of ``ChatMember`` - objects that contains information about all chat administrators except - other bots. If the chat is a group or a supergroup and no administrators were - appointed, only the creator will be returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('getChatAdministrators', data, timeout=timeout, api_kwargs=api_kwargs) - - return ChatMember.de_list(result, self) # type: ignore - - @log - def get_chat_members_count( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> int: - """ - Deprecated, use :func:`~telegram.Bot.get_chat_member_count` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`bot.get_chat_members_count` is deprecated. ' - 'Use `bot.get_chat_member_count` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return self.get_chat_member_count(chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs) - - @log - def get_chat_member_count( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> int: - """Use this method to get the number of members in a chat. - - .. versionadded:: 13.7 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`int`: Number of members in the chat. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('getChatMemberCount', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def get_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> ChatMember: - """Use this method to get information about a member of a chat. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup or channel (in the format ``@channelusername``). - user_id (:obj:`int`): Unique identifier of the target user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.ChatMember` - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} - - result = self._post('getChatMember', data, timeout=timeout, api_kwargs=api_kwargs) - - return ChatMember.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def set_chat_sticker_set( - self, - chat_id: Union[str, int], - sticker_set_name: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to set a new group sticker set for a supergroup. - The bot must be an administrator in the chat for this to work and must have the appropriate - admin rights. Use the field :attr:`telegram.Chat.can_set_sticker_set` optionally returned - in :meth:`get_chat` requests to check if the bot can use this method. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup (in the format @supergroupusername). - sticker_set_name (:obj:`str`): Name of the sticker set to be set as the group - sticker set. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - """ - data: JSONDict = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name} - - result = self._post('setChatStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def delete_chat_sticker_set( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to delete a group sticker set from a supergroup. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. - Use the field :attr:`telegram.Chat.can_set_sticker_set` optionally returned in - :meth:`get_chat` requests to check if the bot can use this method. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup (in the format @supergroupusername). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('deleteChatStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - def get_webhook_info( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> WebhookInfo: - """Use this method to get current webhook status. Requires no parameters. - - If the bot is using :meth:`get_updates`, will return an object with the - :attr:`telegram.WebhookInfo.url` field empty. - - Args: - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.WebhookInfo` - - """ - result = self._post('getWebhookInfo', None, timeout=timeout, api_kwargs=api_kwargs) - - return WebhookInfo.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def set_game_score( - self, - user_id: Union[int, str], - score: int, - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - force: bool = None, - disable_edit_message: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """ - Use this method to set the score of the specified user in a game. - - Args: - user_id (:obj:`int`): User identifier. - score (:obj:`int`): New score, must be non-negative. - force (:obj:`bool`, optional): Pass :obj:`True`, if the high score is allowed to - decrease. This can be useful when fixing mistakes or banning cheaters. - disable_edit_message (:obj:`bool`, optional): Pass :obj:`True`, if the game message - should not be automatically edited to include the current scoreboard. - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat. - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the sent message. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: The edited message, or if the message wasn't sent by the bot - , :obj:`True`. - - Raises: - :class:`telegram.error.TelegramError`: If the new score is not greater than the user's - current score in the chat and force is :obj:`False`. - - """ - data: JSONDict = {'user_id': user_id, 'score': score} - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - if force is not None: - data['force'] = force - if disable_edit_message is not None: - data['disable_edit_message'] = disable_edit_message - - return self._message( - 'setGameScore', - data, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - @log - def get_game_high_scores( - self, - user_id: Union[int, str], - chat_id: Union[str, int] = None, - message_id: int = None, - inline_message_id: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> List[GameHighScore]: - """ - Use this method to get data for high score tables. Will return the score of the specified - user and several of their neighbors in a game. - - Note: - This method will currently return scores for the target user, plus two of their - closest neighbors on each side. Will also return the top three users if the user and - his neighbors are not among them. Please note that this behavior is subject to change. - - Args: - user_id (:obj:`int`): Target user id. - chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not - specified. Unique identifier for the target chat. - message_id (:obj:`int`, optional): Required if inline_message_id is not specified. - Identifier of the sent message. - inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not - specified. Identifier of the inline message. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - List[:class:`telegram.GameHighScore`] - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'user_id': user_id} - - if chat_id: - data['chat_id'] = chat_id - if message_id: - data['message_id'] = message_id - if inline_message_id: - data['inline_message_id'] = inline_message_id - - result = self._post('getGameHighScores', data, timeout=timeout, api_kwargs=api_kwargs) - - return GameHighScore.de_list(result, self) # type: ignore - - @log - def send_invoice( - self, - chat_id: Union[int, str], - title: str, - description: str, - payload: str, - provider_token: str, - currency: str, - prices: List['LabeledPrice'], - start_parameter: str = None, - photo_url: str = None, - photo_size: int = None, - photo_width: int = None, - photo_height: int = None, - need_name: bool = None, - need_phone_number: bool = None, - need_email: bool = None, - need_shipping_address: bool = None, - is_flexible: bool = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: InlineKeyboardMarkup = None, - provider_data: Union[str, object] = None, - send_phone_number_to_provider: bool = None, - send_email_to_provider: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - max_tip_amount: int = None, - suggested_tip_amounts: List[int] = None, - protect_content: bool = None, - ) -> Message: - """Use this method to send invoices. - - Warning: - As of API 5.2 :attr:`start_parameter` is an optional argument and therefore the order - of the arguments had to be changed. Use keyword arguments to make sure that the - arguments are passed correctly. - - .. versionchanged:: 13.5 - As of Bot API 5.2, the parameter :attr:`start_parameter` is optional. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - title (:obj:`str`): Product name, 1-32 characters. - description (:obj:`str`): Product description, 1-255 characters. - payload (:obj:`str`): Bot-defined invoice payload, 1-128 bytes. This will not be - displayed to the user, use for your internal processes. - provider_token (:obj:`str`): Payments provider token, obtained via - `@BotFather <https://t.me/BotFather>`_. - currency (:obj:`str`): Three-letter ISO 4217 currency code. - prices (List[:class:`telegram.LabeledPrice`)]: Price breakdown, a JSON-serialized list - of components (e.g. product price, tax, discount, delivery cost, delivery tax, - bonus, etc.). - max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the - smallest units of the currency (integer, not float/double). For example, for a - maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the exp parameter in - `currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, it - shows the number of digits past the decimal point for each currency (2 for the - majority of currencies). Defaults to ``0``. - - .. versionadded:: 13.5 - suggested_tip_amounts (List[:obj:`int`], optional): A JSON-serialized array of - suggested amounts of tips in the smallest units of the currency (integer, not - float/double). At most 4 suggested tip amounts can be specified. The suggested tip - amounts must be positive, passed in a strictly increased order and must not exceed - ``max_tip_amount``. - - .. versionadded:: 13.5 - start_parameter (:obj:`str`, optional): Unique deep-linking parameter. If left empty, - *forwarded copies* of the sent message will have a *Pay* button, allowing - multiple users to pay directly from the forwarded message, using the same invoice. - If non-empty, forwarded copies of the sent message will have a *URL* button with a - deep link to the bot (instead of a *Pay* button), with the value used as the - start parameter. - - .. versionchanged:: 13.5 - As of Bot API 5.2, this parameter is optional. - provider_data (:obj:`str` | :obj:`object`, optional): JSON-serialized data about the - invoice, which will be shared with the payment provider. A detailed description of - required fields should be provided by the payment provider. When an object is - passed, it will be encoded as JSON. - photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a - photo of the goods or a marketing image for a service. People like it better when - they see what they are paying for. - photo_size (:obj:`str`, optional): Photo size. - photo_width (:obj:`int`, optional): Photo width. - photo_height (:obj:`int`, optional): Photo height. - need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full - name to complete the order. - need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - phone number to complete the order. - need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email - to complete the order. - need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the - user's shipping address to complete the order. - send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's - phone number should be sent to provider. - send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email - address should be sent to provider. - is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on - the shipping method. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for an inline keyboard. If empty, one 'Pay total price' button will be - shown. If not empty, the first button must be a Pay button. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'title': title, - 'description': description, - 'payload': payload, - 'provider_token': provider_token, - 'currency': currency, - 'prices': [p.to_dict() for p in prices], - } - if max_tip_amount is not None: - data['max_tip_amount'] = max_tip_amount - if suggested_tip_amounts is not None: - data['suggested_tip_amounts'] = suggested_tip_amounts - if start_parameter is not None: - data['start_parameter'] = start_parameter - if provider_data is not None: - if isinstance(provider_data, str): - data['provider_data'] = provider_data - else: - data['provider_data'] = json.dumps(provider_data) - if photo_url is not None: - data['photo_url'] = photo_url - if photo_size is not None: - data['photo_size'] = photo_size - if photo_width is not None: - data['photo_width'] = photo_width - if photo_height is not None: - data['photo_height'] = photo_height - if need_name is not None: - data['need_name'] = need_name - if need_phone_number is not None: - data['need_phone_number'] = need_phone_number - if need_email is not None: - data['need_email'] = need_email - if need_shipping_address is not None: - data['need_shipping_address'] = need_shipping_address - if is_flexible is not None: - data['is_flexible'] = is_flexible - if send_phone_number_to_provider is not None: - data['send_phone_number_to_provider'] = send_phone_number_to_provider - if send_email_to_provider is not None: - data['send_email_to_provider'] = send_email_to_provider - - return self._message( # type: ignore[return-value] - 'sendInvoice', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def answer_shipping_query( # pylint: disable=C0103 - self, - shipping_query_id: str, - ok: bool, - shipping_options: List[ShippingOption] = None, - error_message: str = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - If you sent an invoice requesting a shipping address and the parameter ``is_flexible`` was - specified, the Bot API will send an :class:`telegram.Update` with a - :attr:`Update.shipping_query` field to the bot. Use this method to reply to shipping - queries. - - Args: - shipping_query_id (:obj:`str`): Unique identifier for the query to be answered. - ok (:obj:`bool`): Specify :obj:`True` if delivery to the specified address is possible - and :obj:`False` if there are any problems (for example, if delivery to the - specified address is not possible). - shipping_options (List[:class:`telegram.ShippingOption`]), optional]: Required if ok is - :obj:`True`. A JSON-serialized array of available shipping options. - error_message (:obj:`str`, optional): Required if ok is :obj:`False`. Error message in - human readable form that explains why it is impossible to complete the order (e.g. - "Sorry, delivery to your desired address is unavailable"). Telegram will display - this message to the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - ok = bool(ok) - - if ok and (shipping_options is None or error_message is not None): - raise TelegramError( - 'answerShippingQuery: If ok is True, shipping_options ' - 'should not be empty and there should not be error_message' - ) - - if not ok and (shipping_options is not None or error_message is None): - raise TelegramError( - 'answerShippingQuery: If ok is False, error_message ' - 'should not be empty and there should not be shipping_options' - ) - - data: JSONDict = {'shipping_query_id': shipping_query_id, 'ok': ok} - - if ok: - if not shipping_options: - # not using an assert statement directly here since they are removed in - # the optimized bytecode - raise AssertionError - data['shipping_options'] = [option.to_dict() for option in shipping_options] - if error_message is not None: - data['error_message'] = error_message - - result = self._post('answerShippingQuery', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def answer_pre_checkout_query( # pylint: disable=C0103 - self, - pre_checkout_query_id: str, - ok: bool, - error_message: str = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Once the user has confirmed their payment and shipping details, the Bot API sends the final - confirmation in the form of an :class:`telegram.Update` with the field - :attr:`Update.pre_checkout_query`. Use this method to respond to such pre-checkout queries. - - Note: - The Bot API must receive an answer within 10 seconds after the pre-checkout - query was sent. - - Args: - pre_checkout_query_id (:obj:`str`): Unique identifier for the query to be answered. - ok (:obj:`bool`): Specify :obj:`True` if everything is alright - (goods are available, etc.) and the bot is ready to proceed with the order. Use - :obj:`False` if there are any problems. - error_message (:obj:`str`, optional): Required if ok is :obj:`False`. Error message - in human readable form that explains the reason for failure to proceed with - the checkout (e.g. "Sorry, somebody just bought the last of our amazing black - T-shirts while you were busy filling out your payment details. Please choose a - different color or garment!"). Telegram will display this message to the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - ok = bool(ok) - - if not (ok ^ (error_message is not None)): # pylint: disable=C0325 - raise TelegramError( - 'answerPreCheckoutQuery: If ok is True, there should ' - 'not be error_message; if ok is False, error_message ' - 'should not be empty' - ) - - data: JSONDict = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok} - - if error_message is not None: - data['error_message'] = error_message - - result = self._post('answerPreCheckoutQuery', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def restrict_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - permissions: ChatPermissions, - until_date: Union[int, datetime] = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to restrict a user in a supergroup. The bot must be an administrator in - the supergroup for this to work and must have the appropriate admin rights. Pass - :obj:`True` for all boolean parameters to lift restrictions from a user. - - Note: - Since Bot API 4.4, :meth:`restrict_chat_member` takes the new user permissions in a - single argument of type :class:`telegram.ChatPermissions`. The old way of passing - parameters will not keep working forever. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target supergroup (in the format @supergroupusername). - user_id (:obj:`int`): Unique identifier of the target user. - until_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when restrictions - will be lifted for the user, unix time. If user is restricted for more than 366 - days or less than 30 seconds from the current time, they are considered to be - restricted forever. - For timezone naive :obj:`datetime.datetime` objects, the default timezone of the - bot will be used. - permissions (:class:`telegram.ChatPermissions`): A JSON-serialized object for new user - permissions. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - """ - data: JSONDict = { - 'chat_id': chat_id, - 'user_id': user_id, - 'permissions': permissions.to_dict(), - } - - if until_date is not None: - if isinstance(until_date, datetime): - until_date = to_timestamp( - until_date, tzinfo=self.defaults.tzinfo if self.defaults else None - ) - data['until_date'] = until_date - - result = self._post('restrictChatMember', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def promote_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, - can_manage_voice_chats: bool = None, - ) -> bool: - """ - Use this method to promote or demote a user in a supergroup or a channel. The bot must be - an administrator in the chat for this to work and must have the appropriate admin rights. - Pass :obj:`False` for all boolean parameters to demote a user. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - user_id (:obj:`int`): Unique identifier of the target user. - is_anonymous (:obj:`bool`, optional): Pass :obj:`True`, if the administrator's presence - in the chat is hidden. - can_manage_chat (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - access the chat event log, chat statistics, message statistics in channels, see - channel members, see anonymous administrators in supergroups and ignore slow mode. - Implied by any other administrator privilege. - - .. versionadded:: 13.4 - - can_manage_voice_chats (:obj:`bool`, optional): Pass :obj:`True`, if the administrator - can manage voice chats. - - .. versionadded:: 13.4 - - can_change_info (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - change chat title, photo and other settings. - can_post_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - create channel posts, channels only. - can_edit_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - edit messages of other users and can pin messages, channels only. - can_delete_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - delete messages of other users. - can_invite_users (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - invite new users to the chat. - can_restrict_members (:obj:`bool`, optional): Pass :obj:`True`, if the administrator - can restrict, ban or unban chat members. - can_pin_messages (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - pin messages, supergroups only. - can_promote_members (:obj:`bool`, optional): Pass :obj:`True`, if the administrator can - add new administrators with a subset of his own privileges or demote administrators - that he has promoted, directly or indirectly (promoted by administrators that were - appointed by him). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} - - if is_anonymous is not None: - data['is_anonymous'] = is_anonymous - if can_change_info is not None: - data['can_change_info'] = can_change_info - if can_post_messages is not None: - data['can_post_messages'] = can_post_messages - if can_edit_messages is not None: - data['can_edit_messages'] = can_edit_messages - if can_delete_messages is not None: - data['can_delete_messages'] = can_delete_messages - if can_invite_users is not None: - data['can_invite_users'] = can_invite_users - if can_restrict_members is not None: - data['can_restrict_members'] = can_restrict_members - if can_pin_messages is not None: - data['can_pin_messages'] = can_pin_messages - if can_promote_members is not None: - data['can_promote_members'] = can_promote_members - if can_manage_chat is not None: - data['can_manage_chat'] = can_manage_chat - if can_manage_voice_chats is not None: - data['can_manage_voice_chats'] = can_manage_voice_chats - - result = self._post('promoteChatMember', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_chat_permissions( - self, - chat_id: Union[str, int], - permissions: ChatPermissions, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to set default chat permissions for all members. The bot must be an - administrator in the group or a supergroup for this to work and must have the - :attr:`telegram.ChatMember.can_restrict_members` admin rights. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of - the target supergroup (in the format `@supergroupusername`). - permissions (:class:`telegram.ChatPermissions`): New default chat permissions. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'permissions': permissions.to_dict()} - - result = self._post('setChatPermissions', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_chat_administrator_custom_title( - self, - chat_id: Union[int, str], - user_id: Union[int, str], - custom_title: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to set a custom title for administrators promoted by the bot in a - supergroup. The bot must be an administrator for this to work. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of - the target supergroup (in the format `@supergroupusername`). - user_id (:obj:`int`): Unique identifier of the target administrator. - custom_title (:obj:`str`): New custom title for the administrator; 0-16 characters, - emoji are not allowed. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title} - - result = self._post( - 'setChatAdministratorCustomTitle', data, timeout=timeout, api_kwargs=api_kwargs - ) - - return result # type: ignore[return-value] - - @log - def export_chat_invite_link( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> str: - """ - Use this method to generate a new primary invite link for a chat; any previously generated - link is revoked. The bot must be an administrator in the chat for this to work and must - have the appropriate admin rights. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Note: - Each administrator in a chat generates their own invite links. Bots can't use invite - links generated by other administrators. If you want your bot to work with invite - links, it will need to generate its own link using :meth:`export_chat_invite_link` or - by calling the :meth:`get_chat` method. If your bot needs to generate a new primary - invite link replacing its previous one, use :attr:`export_chat_invite_link` again. - - Returns: - :obj:`str`: New invite link on success. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('exportChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def create_chat_invite_link( - self, - chat_id: Union[str, int], - expire_date: Union[int, datetime] = None, - member_limit: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - name: str = None, - creates_join_request: bool = None, - ) -> ChatInviteLink: - """ - Use this method to create an additional invite link for a chat. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. - The link can be revoked using the method :meth:`revoke_chat_invite_link`. - - .. versionadded:: 13.4 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will - expire. Integer input will be interpreted as Unix timestamp. - For timezone naive :obj:`datetime.datetime` objects, the default timezone of the - bot will be used. - member_limit (:obj:`int`, optional): Maximum number of users that can be members of - the chat simultaneously after joining the chat via this invite link; 1-99999. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - name (:obj:`str`, optional): Invite link name; 0-32 characters. - - .. versionadded:: 13.8 - creates_join_request (:obj:`bool`, optional): :obj:`True`, if users joining the chat - via the link need to be approved by chat administrators. - If :obj:`True`, ``member_limit`` can't be specified. - - .. versionadded:: 13.8 - - Returns: - :class:`telegram.ChatInviteLink` - - Raises: - :class:`telegram.error.TelegramError` - - """ - if creates_join_request and member_limit: - raise ValueError( - "If `creates_join_request` is `True`, `member_limit` can't be specified." - ) - - data: JSONDict = { - 'chat_id': chat_id, - } - - if expire_date is not None: - if isinstance(expire_date, datetime): - expire_date = to_timestamp( - expire_date, tzinfo=self.defaults.tzinfo if self.defaults else None - ) - data['expire_date'] = expire_date - - if member_limit is not None: - data['member_limit'] = member_limit - - if name is not None: - data['name'] = name - - if creates_join_request is not None: - data['creates_join_request'] = creates_join_request - - result = self._post('createChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) - - return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def edit_chat_invite_link( - self, - chat_id: Union[str, int], - invite_link: str, - expire_date: Union[int, datetime] = None, - member_limit: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - name: str = None, - creates_join_request: bool = None, - ) -> ChatInviteLink: - """ - Use this method to edit a non-primary invite link created by the bot. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. - - Note: - Though not stated explicitly in the official docs, Telegram changes not only the - optional parameters that are explicitly passed, but also replaces all other optional - parameters to the default values. However, since not documented, this behaviour may - change unbeknown to PTB. - - .. versionadded:: 13.4 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - invite_link (:obj:`str`): The invite link to edit. - expire_date (:obj:`int` | :obj:`datetime.datetime`, optional): Date when the link will - expire. - For timezone naive :obj:`datetime.datetime` objects, the default timezone of the - bot will be used. - member_limit (:obj:`int`, optional): Maximum number of users that can be members of - the chat simultaneously after joining the chat via this invite link; 1-99999. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - name (:obj:`str`, optional): Invite link name; 0-32 characters. - - .. versionadded:: 13.8 - creates_join_request (:obj:`bool`, optional): :obj:`True`, if users joining the chat - via the link need to be approved by chat administrators. - If :obj:`True`, ``member_limit`` can't be specified. - - .. versionadded:: 13.8 - - Returns: - :class:`telegram.ChatInviteLink` - - Raises: - :class:`telegram.error.TelegramError` - - """ - if creates_join_request and member_limit: - raise ValueError( - "If `creates_join_request` is `True`, `member_limit` can't be specified." - ) - - data: JSONDict = {'chat_id': chat_id, 'invite_link': invite_link} - - if expire_date is not None: - if isinstance(expire_date, datetime): - expire_date = to_timestamp( - expire_date, tzinfo=self.defaults.tzinfo if self.defaults else None - ) - data['expire_date'] = expire_date - - if member_limit is not None: - data['member_limit'] = member_limit - - if name is not None: - data['name'] = name - - if creates_join_request is not None: - data['creates_join_request'] = creates_join_request - - result = self._post('editChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) - - return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def revoke_chat_invite_link( - self, - chat_id: Union[str, int], - invite_link: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> ChatInviteLink: - """ - Use this method to revoke an invite link created by the bot. If the primary link is - revoked, a new link is automatically generated. The bot must be an administrator in the - chat for this to work and must have the appropriate admin rights. - - .. versionadded:: 13.4 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - invite_link (:obj:`str`): The invite link to edit. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.ChatInviteLink` - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'invite_link': invite_link} - - result = self._post('revokeChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) - - return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def approve_chat_join_request( - self, - chat_id: Union[str, int], - user_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to approve a chat join request. - - The bot must be an administrator in the chat for this to work and must have the - :attr:`telegram.ChatPermissions.can_invite_users` administrator right. - - .. versionadded:: 13.8 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - user_id (:obj:`int`): Unique identifier of the target user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} - - result = self._post('approveChatJoinRequest', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def decline_chat_join_request( - self, - chat_id: Union[str, int], - user_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to decline a chat join request. - - The bot must be an administrator in the chat for this to work and must have the - :attr:`telegram.ChatPermissions.can_invite_users` administrator right. - - .. versionadded:: 13.8 - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - user_id (:obj:`int`): Unique identifier of the target user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} - - result = self._post('declineChatJoinRequest', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_chat_photo( - self, - chat_id: Union[str, int], - photo: FileInput, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to set a new profile photo for the chat. - - Photos can't be changed for private chats. The bot must be an administrator in the chat - for this to work and must have the appropriate admin rights. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - photo (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`): New chat photo. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'photo': parse_file_input(photo)} - - result = self._post('setChatPhoto', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def delete_chat_photo( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to delete a chat photo. Photos can't be changed for private chats. The bot - must be an administrator in the chat for this to work and must have the appropriate admin - rights. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - result = self._post('deleteChatPhoto', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_chat_title( - self, - chat_id: Union[str, int], - title: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to change the title of a chat. Titles can't be changed for private chats. - The bot must be an administrator in the chat for this to work and must have the appropriate - admin rights. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - title (:obj:`str`): New chat title, 1-255 characters. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'title': title} - - result = self._post('setChatTitle', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_chat_description( - self, - chat_id: Union[str, int], - description: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to change the description of a group, a supergroup or a channel. The bot - must be an administrator in the chat for this to work and must have the appropriate admin - rights. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - description (:obj:`str`): New chat description, 0-255 characters. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'description': description} - - result = self._post('setChatDescription', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def pin_chat_message( - self, - chat_id: Union[str, int], - message_id: int, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to add a message to the list of pinned messages in a chat. If the - chat is not a private chat, the bot must be an administrator in the chat for this to work - and must have the :attr:`telegram.ChatMember.can_pin_messages` admin right in a supergroup - or :attr:`telegram.ChatMember.can_edit_messages` admin right in a channel. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - message_id (:obj:`int`): Identifier of a message to pin. - disable_notification (:obj:`bool`, optional): Pass :obj:`True`, if it is not necessary - to send a notification to all chat members about the new pinned message. - Notifications are always disabled in channels and private chats. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'message_id': message_id, - 'disable_notification': disable_notification, - } - - return self._post( # type: ignore[return-value] - 'pinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs - ) - - @log - def unpin_chat_message( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - message_id: int = None, - ) -> bool: - """ - Use this method to remove a message from the list of pinned messages in a chat. If the - chat is not a private chat, the bot must be an administrator in the chat for this to work - and must have the :attr:`telegram.ChatMember.can_pin_messages` admin right in a - supergroup or :attr:`telegram.ChatMember.can_edit_messages` admin right in a channel. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - message_id (:obj:`int`, optional): Identifier of a message to unpin. If not specified, - the most recent pinned message (by sending date) will be unpinned. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - if message_id is not None: - data['message_id'] = message_id - - return self._post( # type: ignore[return-value] - 'unpinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs - ) - - @log - def unpin_all_chat_messages( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Use this method to clear the list of pinned messages in a chat. If the - chat is not a private chat, the bot must be an administrator in the chat for this - to work and must have the :attr:`telegram.ChatMember.can_pin_messages` admin right in a - supergroup or :attr:`telegram.ChatMember.can_edit_messages` admin right in a channel. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - return self._post( # type: ignore[return-value] - 'unpinAllChatMessages', data, timeout=timeout, api_kwargs=api_kwargs - ) - - @log - def get_sticker_set( - self, - name: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> StickerSet: - """Use this method to get a sticker set. - - Args: - name (:obj:`str`): Name of the sticker set. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.StickerSet` - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'name': name} - - result = self._post('getStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) - - return StickerSet.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def upload_sticker_file( - self, - user_id: Union[str, int], - png_sticker: FileInput, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - ) -> File: - """ - Use this method to upload a ``.PNG`` file with a sticker for later use in - :meth:`create_new_sticker_set` and :meth:`add_sticker_to_set` methods (can be used multiple - times). - - Note: - The png_sticker argument can be either a file_id, an URL or a file from disk - ``open(filename, 'rb')`` - - Args: - user_id (:obj:`int`): User identifier of sticker file owner. - png_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`): - **PNG** image with the sticker, must be up to 512 kilobytes in size, - dimensions must not exceed 512px, and either width or height must be exactly 512px. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.File`: On success, the uploaded File is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'user_id': user_id, 'png_sticker': parse_file_input(png_sticker)} - - result = self._post('uploadStickerFile', data, timeout=timeout, api_kwargs=api_kwargs) - - return File.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def create_new_sticker_set( - self, - user_id: Union[str, int], - name: str, - title: str, - emojis: str, - png_sticker: FileInput = None, - contains_masks: bool = None, - mask_position: MaskPosition = None, - timeout: DVInput[float] = DEFAULT_20, - tgs_sticker: FileInput = None, - api_kwargs: JSONDict = None, - webm_sticker: FileInput = None, - ) -> bool: - """ - Use this method to create new sticker set owned by a user. - The bot will be able to edit the created sticker set. - You must use exactly one of the fields ``png_sticker``, ``tgs_sticker``, or - ``webm_sticker``. - - Warning: - As of API 4.7 ``png_sticker`` is an optional argument and therefore the order of the - arguments had to be changed. Use keyword arguments to make sure that the arguments are - passed correctly. - - Note: - The png_sticker and tgs_sticker argument can be either a file_id, an URL or a file from - disk ``open(filename, 'rb')`` - - Args: - user_id (:obj:`int`): User identifier of created sticker set owner. - name (:obj:`str`): Short name of sticker set, to be used in t.me/addstickers/ URLs - (e.g., animals). Can contain only english letters, digits and underscores. - Must begin with a letter, can't contain consecutive underscores and - must end in "_by_<bot username>". <bot_username> is case insensitive. - 1-64 characters. - title (:obj:`str`): Sticker set title, 1-64 characters. - png_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \ - optional): **PNG** image with the sticker, - must be up to 512 kilobytes in size, dimensions must not exceed 512px, - and either width or height must be exactly 512px. Pass a file_id as a String to - send a file that already exists on the Telegram servers, pass an HTTP URL as a - String for Telegram to get a file from the Internet, or upload a new one - using multipart/form-data. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - tgs_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \ - optional): **TGS** animation with the sticker, uploaded using multipart/form-data. - See https://core.telegram.org/stickers#animated-sticker-requirements for technical - requirements. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - webm_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`,\ - optional): **WEBM** video with the sticker, uploaded using multipart/form-data. - See https://core.telegram.org/stickers#video-sticker-requirements for - technical requirements. - - .. versionadded:: 13.11 - - emojis (:obj:`str`): One or more emoji corresponding to the sticker. - contains_masks (:obj:`bool`, optional): Pass :obj:`True`, if a set of mask stickers - should be created. - mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask - should be placed on faces. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} - - if png_sticker is not None: - data['png_sticker'] = parse_file_input(png_sticker) - if tgs_sticker is not None: - data['tgs_sticker'] = parse_file_input(tgs_sticker) - if webm_sticker is not None: - data['webm_sticker'] = parse_file_input(webm_sticker) - if contains_masks is not None: - data['contains_masks'] = contains_masks - if mask_position is not None: - # We need to_json() instead of to_dict() here, because we're sending a media - # message here, which isn't json dumped by utils.request - data['mask_position'] = mask_position.to_json() - - result = self._post('createNewStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def add_sticker_to_set( - self, - user_id: Union[str, int], - name: str, - emojis: str, - png_sticker: FileInput = None, - mask_position: MaskPosition = None, - timeout: DVInput[float] = DEFAULT_20, - tgs_sticker: FileInput = None, - api_kwargs: JSONDict = None, - webm_sticker: FileInput = None, - ) -> bool: - """ - Use this method to add a new sticker to a set created by the bot. - You **must** use exactly one of the fields ``png_sticker``, ``tgs_sticker`` or - ``webm_sticker``. Animated stickers can be added to animated sticker sets and only to them. - Animated sticker sets can have up to 50 stickers. Static sticker sets can have up to 120 - stickers. - - Warning: - As of API 4.7 ``png_sticker`` is an optional argument and therefore the order of the - arguments had to be changed. Use keyword arguments to make sure that the arguments are - passed correctly. - - Note: - The png_sticker and tgs_sticker argument can be either a file_id, an URL or a file from - disk ``open(filename, 'rb')`` - - Args: - user_id (:obj:`int`): User identifier of created sticker set owner. - - name (:obj:`str`): Sticker set name. - png_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \ - optional): **PNG** image with the sticker, - must be up to 512 kilobytes in size, dimensions must not exceed 512px, - and either width or height must be exactly 512px. Pass a file_id as a String to - send a file that already exists on the Telegram servers, pass an HTTP URL as a - String for Telegram to get a file from the Internet, or upload a new one - using multipart/form-data. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - tgs_sticker (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \ - optional): **TGS** animation with the sticker, uploaded using multipart/form-data. - See https://core.telegram.org/stickers#animated-sticker-requirements for technical - requirements. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - webm_sticker (:obj:`str` | :term:`file object` | :obj:`bytes` | :class:`pathlib.Path`,\ - optional): **WEBM** video with the sticker, uploaded using multipart/form-data. - See https://core.telegram.org/stickers#video-sticker-requirements for - technical requirements. - - .. versionadded:: 13.11 - emojis (:obj:`str`): One or more emoji corresponding to the sticker. - mask_position (:class:`telegram.MaskPosition`, optional): Position where the mask - should be placed on faces. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'user_id': user_id, 'name': name, 'emojis': emojis} - - if png_sticker is not None: - data['png_sticker'] = parse_file_input(png_sticker) - if tgs_sticker is not None: - data['tgs_sticker'] = parse_file_input(tgs_sticker) - if webm_sticker is not None: - data['webm_sticker'] = parse_file_input(webm_sticker) - if mask_position is not None: - # We need to_json() instead of to_dict() here, because we're sending a media - # message here, which isn't json dumped by utils.request - data['mask_position'] = mask_position.to_json() - - result = self._post('addStickerToSet', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_sticker_position_in_set( - self, - sticker: str, - position: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to move a sticker in a set created by the bot to a specific position. - - Args: - sticker (:obj:`str`): File identifier of the sticker. - position (:obj:`int`): New sticker position in the set, zero-based. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'sticker': sticker, 'position': position} - - result = self._post( - 'setStickerPositionInSet', data, timeout=timeout, api_kwargs=api_kwargs - ) - - return result # type: ignore[return-value] - - @log - def delete_sticker_from_set( - self, - sticker: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to delete a sticker from a set created by the bot. - - Args: - sticker (:obj:`str`): File identifier of the sticker. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'sticker': sticker} - - result = self._post('deleteStickerFromSet', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_sticker_set_thumb( - self, - name: str, - user_id: Union[str, int], - thumb: FileInput = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set - for animated sticker sets only. Video thumbnails can be set only for video sticker sets - only. - - Note: - The thumb can be either a file_id, an URL or a file from disk ``open(filename, 'rb')`` - - Args: - name (:obj:`str`): Sticker set name - user_id (:obj:`int`): User identifier of created sticker set owner. - thumb (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path`, \ - optional): A **PNG** image with the thumbnail, must - be up to 128 kilobytes in size and have width and height exactly 100px, or a - **TGS** animation with the thumbnail up to 32 kilobytes in size; see - https://core.telegram.org/stickers#animated-sticker-requirements for animated - sticker technical requirements, or a **WEBM** video with the thumbnail up to 32 - kilobytes in size; see - https://core.telegram.org/stickers#video-sticker-requirements for video sticker - technical requirements. Pass a file_id as a String to send a file that - already exists on the Telegram servers, pass an HTTP URL as a String for Telegram - to get a file from the Internet, or upload a new one using multipart/form-data. - Animated sticker set thumbnails can't be uploaded via HTTP URL. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'name': name, 'user_id': user_id} - - if thumb is not None: - data['thumb'] = parse_file_input(thumb) - - result = self._post('setStickerSetThumb', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def set_passport_data_errors( - self, - user_id: Union[str, int], - errors: List[PassportElementError], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """ - Informs a user that some of the Telegram Passport elements they provided contains errors. - The user will not be able to re-submit their Passport to you until the errors are fixed - (the contents of the field for which you returned the error must change). - - Use this if the data submitted by the user doesn't satisfy the standards your service - requires for any reason. For example, if a birthday date seems invalid, a submitted - document is blurry, a scan shows evidence of tampering, etc. Supply some details in the - error message to make sure the user knows how to correct the issues. - - Args: - user_id (:obj:`int`): User identifier - errors (List[:class:`PassportElementError`]): A JSON-serialized array describing the - errors. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during - creation of the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'user_id': user_id, 'errors': [error.to_dict() for error in errors]} - - result = self._post('setPassportDataErrors', data, timeout=timeout, api_kwargs=api_kwargs) - - return result # type: ignore[return-value] - - @log - def send_poll( - self, - chat_id: Union[int, str], - question: str, - options: List[str], - is_anonymous: bool = True, - type: str = Poll.REGULAR, # pylint: disable=W0622 - allows_multiple_answers: bool = False, - correct_option_id: int = None, - is_closed: bool = None, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - explanation: str = None, - explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, - open_period: int = None, - close_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send a native poll. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - question (:obj:`str`): Poll question, 1-300 characters. - options (List[:obj:`str`]): List of answer options, 2-10 strings 1-100 characters each. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the poll needs to be anonymous, - defaults to :obj:`True`. - type (:obj:`str`, optional): Poll type, :attr:`telegram.Poll.QUIZ` or - :attr:`telegram.Poll.REGULAR`, defaults to :attr:`telegram.Poll.REGULAR`. - allows_multiple_answers (:obj:`bool`, optional): :obj:`True`, if the poll allows - multiple answers, ignored for polls in quiz mode, defaults to :obj:`False`. - correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer - option, required for polls in quiz mode. - explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect - answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most - 2 line feeds after entities parsing. - explanation_parse_mode (:obj:`str`, optional): Mode for parsing entities in the - explanation. See the constants in :class:`telegram.ParseMode` for the available - modes. - explanation_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in message text, which can be specified instead of - :attr:`parse_mode`. - open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active - after creation, 5-600. Can't be used together with :attr:`close_date`. - close_date (:obj:`int` | :obj:`datetime.datetime`, optional): Point in time (Unix - timestamp) when the poll will be automatically closed. Must be at least 5 and no - more than 600 seconds in the future. Can't be used together with - :attr:`open_period`. - For timezone naive :obj:`datetime.datetime` objects, the default timezone of the - bot will be used. - is_closed (:obj:`bool`, optional): Pass :obj:`True`, if the poll needs to be - immediately closed. This can be useful for poll preview. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = { - 'chat_id': chat_id, - 'question': question, - 'options': options, - 'explanation_parse_mode': explanation_parse_mode, - } - - if not is_anonymous: - data['is_anonymous'] = is_anonymous - if type: - data['type'] = type - if allows_multiple_answers: - data['allows_multiple_answers'] = allows_multiple_answers - if correct_option_id is not None: - data['correct_option_id'] = correct_option_id - if is_closed: - data['is_closed'] = is_closed - if explanation: - data['explanation'] = explanation - if explanation_entities: - data['explanation_entities'] = [me.to_dict() for me in explanation_entities] - if open_period: - data['open_period'] = open_period - if close_date: - if isinstance(close_date, datetime): - close_date = to_timestamp( - close_date, tzinfo=self.defaults.tzinfo if self.defaults else None - ) - data['close_date'] = close_date - - return self._message( # type: ignore[return-value] - 'sendPoll', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def stop_poll( - self, - chat_id: Union[int, str], - message_id: int, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Poll: - """ - Use this method to stop a poll which was sent by the bot. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - message_id (:obj:`int`): Identifier of the original message with the poll. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized - object for a new message inline keyboard. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Poll`: On success, the stopped Poll with the final results is - returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id, 'message_id': message_id} - - if reply_markup: - if isinstance(reply_markup, ReplyMarkup): - # We need to_json() instead of to_dict() here, because reply_markups may be - # attached to media messages, which aren't json dumped by utils.request - data['reply_markup'] = reply_markup.to_json() - else: - data['reply_markup'] = reply_markup - - result = self._post('stopPoll', data, timeout=timeout, api_kwargs=api_kwargs) - - return Poll.de_json(result, self) # type: ignore[return-value, arg-type] - - @log - def send_dice( - self, - chat_id: Union[int, str], - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - emoji: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> Message: - """ - Use this method to send an animated emoji that will display a random value. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - emoji (:obj:`str`, optional): Emoji on which the dice throw animation is based. - Currently, must be one of “🎲”, “🎯”, “🏀”, “⚽”, "🎳", or “🎰”. Dice can have - values 1-6 for “🎲”, “🎯” and "🎳", values 1-5 for “🏀” and “⚽”, and values 1-64 - for “🎰”. Defaults to “🎲”. - - .. versionchanged:: 13.4 - Added the "🎳" emoji. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. A - JSON-serialized object for an inline keyboard, custom reply keyboard, instructions - to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.Message`: On success, the sent Message is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {'chat_id': chat_id} - - if emoji: - data['emoji'] = emoji - - return self._message( # type: ignore[return-value] - 'sendDice', - data, - timeout=timeout, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - allow_sending_without_reply=allow_sending_without_reply, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - @log - def get_my_commands( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - scope: BotCommandScope = None, - language_code: str = None, - ) -> List[BotCommand]: - """ - Use this method to get the current list of the bot's commands for the given scope and user - language. - - Args: - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - scope (:class:`telegram.BotCommandScope`, optional): A JSON-serialized object, - describing scope of users. Defaults to :class:`telegram.BotCommandScopeDefault`. - - .. versionadded:: 13.7 - - language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code or an empty - string. - - .. versionadded:: 13.7 - - Returns: - List[:class:`telegram.BotCommand`]: On success, the commands set for the bot. An empty - list is returned if commands are not set. - - Raises: - :class:`telegram.error.TelegramError` - - """ - data: JSONDict = {} - - if scope: - data['scope'] = scope.to_dict() - - if language_code: - data['language_code'] = language_code - - result = self._post('getMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = BotCommand.de_list(result, self) # type: ignore[assignment,arg-type] - return self._commands # type: ignore[return-value] - - return BotCommand.de_list(result, self) # type: ignore[return-value,arg-type] - - @log - def set_my_commands( - self, - commands: List[Union[BotCommand, Tuple[str, str]]], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - scope: BotCommandScope = None, - language_code: str = None, - ) -> bool: - """ - Use this method to change the list of the bot's commands. See the - `Telegram docs <https://core.telegram.org/bots#commands>`_ for more details about bot - commands. - - Args: - commands (List[:class:`BotCommand` | (:obj:`str`, :obj:`str`)]): A JSON-serialized list - of bot commands to be set as the list of the bot's commands. At most 100 commands - can be specified. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - scope (:class:`telegram.BotCommandScope`, optional): A JSON-serialized object, - describing scope of users for which the commands are relevant. Defaults to - :class:`telegram.BotCommandScopeDefault`. - - .. versionadded:: 13.7 - - language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, for whose language - there are no dedicated commands. - - .. versionadded:: 13.7 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - - """ - cmds = [c if isinstance(c, BotCommand) else BotCommand(c[0], c[1]) for c in commands] - - data: JSONDict = {'commands': [c.to_dict() for c in cmds]} - - if scope: - data['scope'] = scope.to_dict() - - if language_code: - data['language_code'] = language_code - - result = self._post('setMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - - # Set commands only for default scope. No need to check for outcome. - # If request failed, we won't come this far - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = cmds - - return result # type: ignore[return-value] - - @log - def delete_my_commands( - self, - scope: BotCommandScope = None, - language_code: str = None, - api_kwargs: JSONDict = None, - timeout: ODVInput[float] = DEFAULT_NONE, - ) -> bool: - """ - Use this method to delete the list of the bot's commands for the given scope and user - language. After deletion, - `higher level commands <https://core.telegram.org/bots/api#determining-list-of-commands>`_ - will be shown to affected users. - - .. versionadded:: 13.7 - - Args: - scope (:class:`telegram.BotCommandScope`, optional): A JSON-serialized object, - describing scope of users for which the commands are relevant. Defaults to - :class:`telegram.BotCommandScopeDefault`. - language_code (:obj:`str`, optional): A two-letter ISO 639-1 language code. If empty, - commands will be applied to all users from the given scope, for whose language - there are no dedicated commands. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - Raises: - :class:`telegram.error.TelegramError` - """ - data: JSONDict = {} - - if scope: - data['scope'] = scope.to_dict() - - if language_code: - data['language_code'] = language_code - - result = self._post('deleteMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = [] - - return result # type: ignore[return-value] - - @log - def log_out(self, timeout: ODVInput[float] = DEFAULT_NONE) -> bool: - """ - Use this method to log out from the cloud Bot API server before launching the bot locally. - You *must* log out the bot before running it locally, otherwise there is no guarantee that - the bot will receive updates. After a successful call, you can immediately log in on a - local server, but will not be able to log in back to the cloud Bot API server for 10 - minutes. - - Args: - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - - Returns: - :obj:`True`: On success - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self._post('logOut', timeout=timeout) # type: ignore[return-value] - - @log - def close(self, timeout: ODVInput[float] = DEFAULT_NONE) -> bool: - """ - Use this method to close the bot instance before moving it from one local server to - another. You need to delete the webhook before calling this method to ensure that the bot - isn't launched again after server restart. The method will return error 429 in the first - 10 minutes after the bot is launched. - - Args: - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - - Returns: - :obj:`True`: On success - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self._post('close', timeout=timeout) # type: ignore[return-value] - - @log - def copy_message( - self, - chat_id: Union[int, str], - from_chat_id: Union[str, int], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> MessageId: - """ - Use this method to copy messages of any kind. Service messages and invoice messages can't - be copied. The method is analogous to the method :meth:`forward_message`, but the copied - message doesn't have a link to the original message. - - Args: - chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username - of the target channel (in the format ``@channelusername``). - from_chat_id (:obj:`int` | :obj:`str`): Unique identifier for the chat where the - original message was sent (or channel username in the format ``@channelusername``). - message_id (:obj:`int`): Message identifier in the chat specified in from_chat_id. - caption (:obj:`str`, optional): New caption for media, 0-1024 characters after - entities parsing. If not specified, the original caption is kept. - parse_mode (:obj:`str`, optional): Mode for parsing entities in the new caption. See - the constants in :class:`telegram.ParseMode` for the available modes. - caption_entities (:class:`telegram.utils.types.SLT[MessageEntity]`): List of special - entities that appear in the new caption, which can be specified instead of - parse_mode - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - protect_content (:obj:`bool`, optional): Protects the contents of the sent message from - forwarding and saving. - - .. versionadded:: 13.10 - - reply_to_message_id (:obj:`int`, optional): If the message is a reply, ID of the - original message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Additional interface options. - A JSON-serialized object for an inline keyboard, custom reply keyboard, - instructions to remove reply keyboard or to force a reply from the user. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - api_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to be passed to the - Telegram API. - - Returns: - :class:`telegram.MessageId`: On success - - Raises: - :class:`telegram.error.TelegramError` - """ - data: JSONDict = { - 'chat_id': chat_id, - 'from_chat_id': from_chat_id, - 'message_id': message_id, - 'parse_mode': parse_mode, - 'disable_notification': disable_notification, - 'allow_sending_without_reply': allow_sending_without_reply, - } - if caption is not None: - data['caption'] = caption - if caption_entities: - data['caption_entities'] = caption_entities - if reply_to_message_id: - data['reply_to_message_id'] = reply_to_message_id - if protect_content: - data['protect_content'] = protect_content - if reply_markup: - if isinstance(reply_markup, ReplyMarkup): - # We need to_json() instead of to_dict() here, because reply_markups may be - # attached to media messages, which aren't json dumped by utils.request - data['reply_markup'] = reply_markup.to_json() - else: - data['reply_markup'] = reply_markup - - result = self._post('copyMessage', data, timeout=timeout, api_kwargs=api_kwargs) - return MessageId.de_json(result, self) # type: ignore[return-value, arg-type] - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name} - - if self.last_name: - data['last_name'] = self.last_name - - return data - - def __eq__(self, other: object) -> bool: - return self.bot == other - - def __hash__(self) -> int: - return hash(self.bot) - - # camelCase aliases - getMe = get_me - """Alias for :meth:`get_me`""" - sendMessage = send_message - """Alias for :meth:`send_message`""" - deleteMessage = delete_message - """Alias for :meth:`delete_message`""" - forwardMessage = forward_message - """Alias for :meth:`forward_message`""" - sendPhoto = send_photo - """Alias for :meth:`send_photo`""" - sendAudio = send_audio - """Alias for :meth:`send_audio`""" - sendDocument = send_document - """Alias for :meth:`send_document`""" - sendSticker = send_sticker - """Alias for :meth:`send_sticker`""" - sendVideo = send_video - """Alias for :meth:`send_video`""" - sendAnimation = send_animation - """Alias for :meth:`send_animation`""" - sendVoice = send_voice - """Alias for :meth:`send_voice`""" - sendVideoNote = send_video_note - """Alias for :meth:`send_video_note`""" - sendMediaGroup = send_media_group - """Alias for :meth:`send_media_group`""" - sendLocation = send_location - """Alias for :meth:`send_location`""" - editMessageLiveLocation = edit_message_live_location - """Alias for :meth:`edit_message_live_location`""" - stopMessageLiveLocation = stop_message_live_location - """Alias for :meth:`stop_message_live_location`""" - sendVenue = send_venue - """Alias for :meth:`send_venue`""" - sendContact = send_contact - """Alias for :meth:`send_contact`""" - sendGame = send_game - """Alias for :meth:`send_game`""" - sendChatAction = send_chat_action - """Alias for :meth:`send_chat_action`""" - answerInlineQuery = answer_inline_query - """Alias for :meth:`answer_inline_query`""" - getUserProfilePhotos = get_user_profile_photos - """Alias for :meth:`get_user_profile_photos`""" - getFile = get_file - """Alias for :meth:`get_file`""" - banChatMember = ban_chat_member - """Alias for :meth:`ban_chat_member`""" - banChatSenderChat = ban_chat_sender_chat - """Alias for :meth:`ban_chat_sender_chat`""" - kickChatMember = kick_chat_member - """Alias for :meth:`kick_chat_member`""" - unbanChatMember = unban_chat_member - """Alias for :meth:`unban_chat_member`""" - unbanChatSenderChat = unban_chat_sender_chat - """Alias for :meth:`unban_chat_sender_chat`""" - answerCallbackQuery = answer_callback_query - """Alias for :meth:`answer_callback_query`""" - editMessageText = edit_message_text - """Alias for :meth:`edit_message_text`""" - editMessageCaption = edit_message_caption - """Alias for :meth:`edit_message_caption`""" - editMessageMedia = edit_message_media - """Alias for :meth:`edit_message_media`""" - editMessageReplyMarkup = edit_message_reply_markup - """Alias for :meth:`edit_message_reply_markup`""" - getUpdates = get_updates - """Alias for :meth:`get_updates`""" - setWebhook = set_webhook - """Alias for :meth:`set_webhook`""" - deleteWebhook = delete_webhook - """Alias for :meth:`delete_webhook`""" - leaveChat = leave_chat - """Alias for :meth:`leave_chat`""" - getChat = get_chat - """Alias for :meth:`get_chat`""" - getChatAdministrators = get_chat_administrators - """Alias for :meth:`get_chat_administrators`""" - getChatMember = get_chat_member - """Alias for :meth:`get_chat_member`""" - setChatStickerSet = set_chat_sticker_set - """Alias for :meth:`set_chat_sticker_set`""" - deleteChatStickerSet = delete_chat_sticker_set - """Alias for :meth:`delete_chat_sticker_set`""" - getChatMemberCount = get_chat_member_count - """Alias for :meth:`get_chat_member_count`""" - getChatMembersCount = get_chat_members_count - """Alias for :meth:`get_chat_members_count`""" - getWebhookInfo = get_webhook_info - """Alias for :meth:`get_webhook_info`""" - setGameScore = set_game_score - """Alias for :meth:`set_game_score`""" - getGameHighScores = get_game_high_scores - """Alias for :meth:`get_game_high_scores`""" - sendInvoice = send_invoice - """Alias for :meth:`send_invoice`""" - answerShippingQuery = answer_shipping_query - """Alias for :meth:`answer_shipping_query`""" - answerPreCheckoutQuery = answer_pre_checkout_query - """Alias for :meth:`answer_pre_checkout_query`""" - restrictChatMember = restrict_chat_member - """Alias for :meth:`restrict_chat_member`""" - promoteChatMember = promote_chat_member - """Alias for :meth:`promote_chat_member`""" - setChatPermissions = set_chat_permissions - """Alias for :meth:`set_chat_permissions`""" - setChatAdministratorCustomTitle = set_chat_administrator_custom_title - """Alias for :meth:`set_chat_administrator_custom_title`""" - exportChatInviteLink = export_chat_invite_link - """Alias for :meth:`export_chat_invite_link`""" - createChatInviteLink = create_chat_invite_link - """Alias for :meth:`create_chat_invite_link`""" - editChatInviteLink = edit_chat_invite_link - """Alias for :meth:`edit_chat_invite_link`""" - revokeChatInviteLink = revoke_chat_invite_link - """Alias for :meth:`revoke_chat_invite_link`""" - approveChatJoinRequest = approve_chat_join_request - """Alias for :meth:`approve_chat_join_request`""" - declineChatJoinRequest = decline_chat_join_request - """Alias for :meth:`decline_chat_join_request`""" - setChatPhoto = set_chat_photo - """Alias for :meth:`set_chat_photo`""" - deleteChatPhoto = delete_chat_photo - """Alias for :meth:`delete_chat_photo`""" - setChatTitle = set_chat_title - """Alias for :meth:`set_chat_title`""" - setChatDescription = set_chat_description - """Alias for :meth:`set_chat_description`""" - pinChatMessage = pin_chat_message - """Alias for :meth:`pin_chat_message`""" - unpinChatMessage = unpin_chat_message - """Alias for :meth:`unpin_chat_message`""" - unpinAllChatMessages = unpin_all_chat_messages - """Alias for :meth:`unpin_all_chat_messages`""" - getStickerSet = get_sticker_set - """Alias for :meth:`get_sticker_set`""" - uploadStickerFile = upload_sticker_file - """Alias for :meth:`upload_sticker_file`""" - createNewStickerSet = create_new_sticker_set - """Alias for :meth:`create_new_sticker_set`""" - addStickerToSet = add_sticker_to_set - """Alias for :meth:`add_sticker_to_set`""" - setStickerPositionInSet = set_sticker_position_in_set - """Alias for :meth:`set_sticker_position_in_set`""" - deleteStickerFromSet = delete_sticker_from_set - """Alias for :meth:`delete_sticker_from_set`""" - setStickerSetThumb = set_sticker_set_thumb - """Alias for :meth:`set_sticker_set_thumb`""" - setPassportDataErrors = set_passport_data_errors - """Alias for :meth:`set_passport_data_errors`""" - sendPoll = send_poll - """Alias for :meth:`send_poll`""" - stopPoll = stop_poll - """Alias for :meth:`stop_poll`""" - sendDice = send_dice - """Alias for :meth:`send_dice`""" - getMyCommands = get_my_commands - """Alias for :meth:`get_my_commands`""" - setMyCommands = set_my_commands - """Alias for :meth:`set_my_commands`""" - deleteMyCommands = delete_my_commands - """Alias for :meth:`delete_my_commands`""" - logOut = log_out - """Alias for :meth:`log_out`""" - copyMessage = copy_message - """Alias for :meth:`copy_message`""" diff --git a/telegramer/include/telegram/botcommand.py b/telegramer/include/telegram/botcommand.py deleted file mode 100644 index 7d0f2da..0000000 --- a/telegramer/include/telegram/botcommand.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Bot Command.""" -from typing import Any - -from telegram import TelegramObject - - -class BotCommand(TelegramObject): - """ - This object represents a bot command. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`command` and :attr:`description` are equal. - - Args: - command (:obj:`str`): Text of the command, 1-32 characters. Can contain only lowercase - English letters, digits and underscores. - description (:obj:`str`): Description of the command, 1-256 characters. - - Attributes: - command (:obj:`str`): Text of the command. - description (:obj:`str`): Description of the command. - - """ - - __slots__ = ('description', '_id_attrs', 'command') - - def __init__(self, command: str, description: str, **_kwargs: Any): - self.command = command - self.description = description - - self._id_attrs = (self.command, self.description) diff --git a/telegramer/include/telegram/botcommandscope.py b/telegramer/include/telegram/botcommandscope.py deleted file mode 100644 index 283f768..0000000 --- a/telegramer/include/telegram/botcommandscope.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains objects representing Telegram bot command scopes.""" -from typing import Any, Union, Optional, TYPE_CHECKING, Dict, Type - -from telegram import TelegramObject, constants -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class BotCommandScope(TelegramObject): - """Base class for objects that represent the scope to which bot commands are applied. - Currently, the following 7 scopes are supported: - - * :class:`telegram.BotCommandScopeDefault` - * :class:`telegram.BotCommandScopeAllPrivateChats` - * :class:`telegram.BotCommandScopeAllGroupChats` - * :class:`telegram.BotCommandScopeAllChatAdministrators` - * :class:`telegram.BotCommandScopeChat` - * :class:`telegram.BotCommandScopeChatAdministrators` - * :class:`telegram.BotCommandScopeChatMember` - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type` is equal. For subclasses with additional attributes, - the notion of equality is overridden. - - Note: - Please see the `official docs`_ on how Telegram determines which commands to display. - - .. _`official docs`: https://core.telegram.org/bots/api#determining-list-of-commands - - .. versionadded:: 13.7 - - Args: - type (:obj:`str`): Scope type. - - Attributes: - type (:obj:`str`): Scope type. - """ - - __slots__ = ('type', '_id_attrs') - - DEFAULT = constants.BOT_COMMAND_SCOPE_DEFAULT - """:const:`telegram.constants.BOT_COMMAND_SCOPE_DEFAULT`""" - ALL_PRIVATE_CHATS = constants.BOT_COMMAND_SCOPE_ALL_PRIVATE_CHATS - """:const:`telegram.constants.BOT_COMMAND_SCOPE_ALL_PRIVATE_CHATS`""" - ALL_GROUP_CHATS = constants.BOT_COMMAND_SCOPE_ALL_GROUP_CHATS - """:const:`telegram.constants.BOT_COMMAND_SCOPE_ALL_GROUP_CHATS`""" - ALL_CHAT_ADMINISTRATORS = constants.BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS - """:const:`telegram.constants.BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS`""" - CHAT = constants.BOT_COMMAND_SCOPE_CHAT - """:const:`telegram.constants.BOT_COMMAND_SCOPE_CHAT`""" - CHAT_ADMINISTRATORS = constants.BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS - """:const:`telegram.constants.BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS`""" - CHAT_MEMBER = constants.BOT_COMMAND_SCOPE_CHAT_MEMBER - """:const:`telegram.constants.BOT_COMMAND_SCOPE_CHAT_MEMBER`""" - - def __init__(self, type: str, **_kwargs: Any): - self.type = type - self._id_attrs = (self.type,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['BotCommandScope']: - """Converts JSON data to the appropriate :class:`BotCommandScope` object, i.e. takes - care of selecting the correct subclass. - - Args: - data (Dict[:obj:`str`, ...]): The JSON data. - bot (:class:`telegram.Bot`): The bot associated with this object. - - Returns: - The Telegram object. - - """ - data = cls._parse_data(data) - - if not data: - return None - - _class_mapping: Dict[str, Type['BotCommandScope']] = { - cls.DEFAULT: BotCommandScopeDefault, - cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats, - cls.ALL_GROUP_CHATS: BotCommandScopeAllGroupChats, - cls.ALL_CHAT_ADMINISTRATORS: BotCommandScopeAllChatAdministrators, - cls.CHAT: BotCommandScopeChat, - cls.CHAT_ADMINISTRATORS: BotCommandScopeChatAdministrators, - cls.CHAT_MEMBER: BotCommandScopeChatMember, - } - - if cls is BotCommandScope: - return _class_mapping.get(data['type'], cls)(**data, bot=bot) - return cls(**data) - - -class BotCommandScopeDefault(BotCommandScope): - """Represents the default scope of bot commands. Default commands are used if no commands with - a `narrower scope`_ are specified for the user. - - .. _`narrower scope`: https://core.telegram.org/bots/api#determining-list-of-commands - - .. versionadded:: 13.7 - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.DEFAULT`. - """ - - __slots__ = () - - def __init__(self, **_kwargs: Any): - super().__init__(type=BotCommandScope.DEFAULT) - - -class BotCommandScopeAllPrivateChats(BotCommandScope): - """Represents the scope of bot commands, covering all private chats. - - .. versionadded:: 13.7 - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.ALL_PRIVATE_CHATS`. - """ - - __slots__ = () - - def __init__(self, **_kwargs: Any): - super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS) - - -class BotCommandScopeAllGroupChats(BotCommandScope): - """Represents the scope of bot commands, covering all group and supergroup chats. - - .. versionadded:: 13.7 - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.ALL_GROUP_CHATS`. - """ - - __slots__ = () - - def __init__(self, **_kwargs: Any): - super().__init__(type=BotCommandScope.ALL_GROUP_CHATS) - - -class BotCommandScopeAllChatAdministrators(BotCommandScope): - """Represents the scope of bot commands, covering all group and supergroup chat administrators. - - .. versionadded:: 13.7 - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.ALL_CHAT_ADMINISTRATORS`. - """ - - __slots__ = () - - def __init__(self, **_kwargs: Any): - super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS) - - -class BotCommandScopeChat(BotCommandScope): - """Represents the scope of bot commands, covering a specific chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type` and :attr:`chat_id` are equal. - - .. versionadded:: 13.7 - - Args: - chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the - target supergroup (in the format ``@supergroupusername``) - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.CHAT`. - chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the - target supergroup (in the format ``@supergroupusername``) - """ - - __slots__ = ('chat_id',) - - def __init__(self, chat_id: Union[str, int], **_kwargs: Any): - super().__init__(type=BotCommandScope.CHAT) - self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith('@') else int(chat_id) - ) - self._id_attrs = (self.type, self.chat_id) - - -class BotCommandScopeChatAdministrators(BotCommandScope): - """Represents the scope of bot commands, covering all administrators of a specific group or - supergroup chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type` and :attr:`chat_id` are equal. - - .. versionadded:: 13.7 - - Args: - chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the - target supergroup (in the format ``@supergroupusername``) - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.CHAT_ADMINISTRATORS`. - chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the - target supergroup (in the format ``@supergroupusername``) - """ - - __slots__ = ('chat_id',) - - def __init__(self, chat_id: Union[str, int], **_kwargs: Any): - super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS) - self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith('@') else int(chat_id) - ) - self._id_attrs = (self.type, self.chat_id) - - -class BotCommandScopeChatMember(BotCommandScope): - """Represents the scope of bot commands, covering a specific member of a group or supergroup - chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type`, :attr:`chat_id` and :attr:`user_id` are equal. - - .. versionadded:: 13.7 - - Args: - chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the - target supergroup (in the format ``@supergroupusername``) - user_id (:obj:`int`): Unique identifier of the target user. - - Attributes: - type (:obj:`str`): Scope type :attr:`telegram.BotCommandScope.CHAT_MEMBER`. - chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the - target supergroup (in the format ``@supergroupusername``) - user_id (:obj:`int`): Unique identifier of the target user. - """ - - __slots__ = ('chat_id', 'user_id') - - def __init__(self, chat_id: Union[str, int], user_id: int, **_kwargs: Any): - super().__init__(type=BotCommandScope.CHAT_MEMBER) - self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith('@') else int(chat_id) - ) - self.user_id = int(user_id) - self._id_attrs = (self.type, self.chat_id, self.user_id) diff --git a/telegramer/include/telegram/callbackquery.py b/telegramer/include/telegram/callbackquery.py deleted file mode 100644 index b783636..0000000 --- a/telegramer/include/telegram/callbackquery.py +++ /dev/null @@ -1,660 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains an object that represents a Telegram CallbackQuery""" -from typing import TYPE_CHECKING, Any, List, Optional, Union, Tuple, ClassVar - -from telegram import Message, TelegramObject, User, Location, ReplyMarkup, constants -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput, DVInput - -if TYPE_CHECKING: - from telegram import ( - Bot, - GameHighScore, - InlineKeyboardMarkup, - MessageId, - InputMedia, - MessageEntity, - ) - - -class CallbackQuery(TelegramObject): - """ - This object represents an incoming callback query from a callback button in an inline keyboard. - - If the button that originated the query was attached to a message sent by the bot, the field - :attr:`message` will be present. If the button was attached to a message sent via the bot (in - inline mode), the field :attr:`inline_message_id` will be present. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Note: - * In Python ``from`` is a reserved word, use ``from_user`` instead. - * Exactly one of the fields :attr:`data` or :attr:`game_short_name` will be present. - * After the user presses an inline button, Telegram clients will display a progress bar - until you call :attr:`answer`. It is, therefore, necessary to react - by calling :attr:`telegram.Bot.answer_callback_query` even if no notification to the user - is needed (e.g., without specifying any of the optional parameters). - * If you're using :attr:`Bot.arbitrary_callback_data`, :attr:`data` may be an instance - of :class:`telegram.ext.InvalidCallbackData`. This will be the case, if the data - associated with the button triggering the :class:`telegram.CallbackQuery` was already - deleted or if :attr:`data` was manipulated by a malicious client. - - .. versionadded:: 13.6 - - - Args: - id (:obj:`str`): Unique identifier for this query. - from_user (:class:`telegram.User`): Sender. - chat_instance (:obj:`str`): Global identifier, uniquely corresponding to the chat to which - the message with the callback button was sent. Useful for high scores in games. - message (:class:`telegram.Message`, optional): Message with the callback button that - originated the query. Note that message content and message date will not be available - if the message is too old. - data (:obj:`str`, optional): Data associated with the callback button. Be aware that a bad - client can send arbitrary data in this field. - inline_message_id (:obj:`str`, optional): Identifier of the message sent via the bot in - inline mode, that originated the query. - game_short_name (:obj:`str`, optional): Short name of a Game to be returned, serves as - the unique identifier for the game - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - - Attributes: - id (:obj:`str`): Unique identifier for this query. - from_user (:class:`telegram.User`): Sender. - chat_instance (:obj:`str`): Global identifier, uniquely corresponding to the chat to which - the message with the callback button was sent. - message (:class:`telegram.Message`): Optional. Message with the callback button that - originated the query. - data (:obj:`str` | :obj:`object`): Optional. Data associated with the callback button. - inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in - inline mode, that originated the query. - game_short_name (:obj:`str`): Optional. Short name of a Game to be returned. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'game_short_name', - 'message', - 'chat_instance', - 'id', - 'from_user', - 'inline_message_id', - 'data', - '_id_attrs', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - from_user: User, - chat_instance: str, - message: Message = None, - data: str = None, - inline_message_id: str = None, - game_short_name: str = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.id = id # pylint: disable=C0103 - self.from_user = from_user - self.chat_instance = chat_instance - # Optionals - self.message = message - self.data = data - self.inline_message_id = inline_message_id - self.game_short_name = game_short_name - - self.bot = bot - - self._id_attrs = (self.id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['CallbackQuery']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['from_user'] = User.de_json(data.get('from'), bot) - data['message'] = Message.de_json(data.get('message'), bot) - - return cls(bot=bot, **data) - - def answer( - self, - text: str = None, - show_alert: bool = False, - url: str = None, - cache_time: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.answer_callback_query(update.callback_query.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.answer_callback_query`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.answer_callback_query( - callback_query_id=self.id, - text=text, - show_alert=show_alert, - url=url, - cache_time=cache_time, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def edit_message_text( - self, - text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.edit_text(text, *args, **kwargs) - - or:: - - bot.edit_message_text(text, inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_text` and :meth:`telegram.Message.edit_text`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.edit_message_text( - inline_message_id=self.inline_message_id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - entities=entities, - chat_id=None, - message_id=None, - ) - return self.message.edit_text( - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - entities=entities, - ) - - def edit_message_caption( - self, - caption: str = None, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.edit_caption(caption, *args, **kwargs) - - or:: - - bot.edit_message_caption(caption=caption - inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_caption` and :meth:`telegram.Message.edit_caption`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.edit_message_caption( - caption=caption, - inline_message_id=self.inline_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - caption_entities=caption_entities, - chat_id=None, - message_id=None, - ) - return self.message.edit_caption( - caption=caption, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - caption_entities=caption_entities, - ) - - def edit_message_reply_markup( - self, - reply_markup: Optional['InlineKeyboardMarkup'] = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.edit_reply_markup( - reply_markup=reply_markup, - *args, - **kwargs - ) - - or:: - - bot.edit_message_reply_markup - inline_message_id=update.callback_query.inline_message_id, - reply_markup=reply_markup, - *args, - **kwargs - ) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_reply_markup` and - :meth:`telegram.Message.edit_reply_markup`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.edit_message_reply_markup( - reply_markup=reply_markup, - inline_message_id=self.inline_message_id, - timeout=timeout, - api_kwargs=api_kwargs, - chat_id=None, - message_id=None, - ) - return self.message.edit_reply_markup( - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def edit_message_media( - self, - media: 'InputMedia' = None, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.edit_media(*args, **kwargs) - - or:: - - bot.edit_message_media(inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_media` and :meth:`telegram.Message.edit_media`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.edit_message_media( - inline_message_id=self.inline_message_id, - media=media, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - chat_id=None, - message_id=None, - ) - return self.message.edit_media( - media=media, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def edit_message_live_location( - self, - latitude: float = None, - longitude: float = None, - location: Location = None, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.edit_live_location(*args, **kwargs) - - or:: - - bot.edit_message_live_location( - inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs - ) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_live_location` and - :meth:`telegram.Message.edit_live_location`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.edit_message_live_location( - inline_message_id=self.inline_message_id, - latitude=latitude, - longitude=longitude, - location=location, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - horizontal_accuracy=horizontal_accuracy, - heading=heading, - proximity_alert_radius=proximity_alert_radius, - chat_id=None, - message_id=None, - ) - return self.message.edit_live_location( - latitude=latitude, - longitude=longitude, - location=location, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - horizontal_accuracy=horizontal_accuracy, - heading=heading, - proximity_alert_radius=proximity_alert_radius, - ) - - def stop_message_live_location( - self, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.stop_live_location(*args, **kwargs) - - or:: - - bot.stop_message_live_location( - inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs - ) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.stop_message_live_location` and - :meth:`telegram.Message.stop_live_location`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.stop_message_live_location( - inline_message_id=self.inline_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - chat_id=None, - message_id=None, - ) - return self.message.stop_live_location( - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def set_game_score( - self, - user_id: Union[int, str], - score: int, - force: bool = None, - disable_edit_message: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union[Message, bool]: - """Shortcut for either:: - - update.callback_query.message.set_game_score(*args, **kwargs) - - or:: - - bot.set_game_score(inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.set_game_score` and :meth:`telegram.Message.set_game_score`. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - - """ - if self.inline_message_id: - return self.bot.set_game_score( - inline_message_id=self.inline_message_id, - user_id=user_id, - score=score, - force=force, - disable_edit_message=disable_edit_message, - timeout=timeout, - api_kwargs=api_kwargs, - chat_id=None, - message_id=None, - ) - return self.message.set_game_score( - user_id=user_id, - score=score, - force=force, - disable_edit_message=disable_edit_message, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def get_game_high_scores( - self, - user_id: Union[int, str], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> List['GameHighScore']: - """Shortcut for either:: - - update.callback_query.message.get_game_high_score(*args, **kwargs) - - or:: - - bot.get_game_high_scores(inline_message_id=update.callback_query.inline_message_id, - *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.get_game_high_scores` and :meth:`telegram.Message.get_game_high_score`. - - Returns: - List[:class:`telegram.GameHighScore`] - - """ - if self.inline_message_id: - return self.bot.get_game_high_scores( - inline_message_id=self.inline_message_id, - user_id=user_id, - timeout=timeout, - api_kwargs=api_kwargs, - chat_id=None, - message_id=None, - ) - return self.message.get_game_high_scores( - user_id=user_id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def delete_message( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - update.callback_query.message.delete(*args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Message.delete`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.message.delete( - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def pin_message( - self, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - update.callback_query.message.pin(*args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Message.pin`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.message.pin( - disable_notification=disable_notification, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def unpin_message( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - update.callback_query.message.unpin(*args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Message.unpin`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.message.unpin( - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def copy_message( - self, - chat_id: Union[int, str], - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - update.callback_query.message.copy( - chat_id, - from_chat_id=update.message.chat_id, - message_id=update.message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Message.copy`. - - Returns: - :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. - - """ - return self.message.copy( - chat_id=chat_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - MAX_ANSWER_TEXT_LENGTH: ClassVar[int] = constants.MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH - """ - :const:`telegram.constants.MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH` - - .. versionadded:: 13.2 - """ diff --git a/telegramer/include/telegram/chat.py b/telegramer/include/telegram/chat.py deleted file mode 100644 index b5dbfe4..0000000 --- a/telegramer/include/telegram/chat.py +++ /dev/null @@ -1,1815 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=W0622 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Chat.""" -import warnings -from datetime import datetime -from typing import TYPE_CHECKING, List, Optional, ClassVar, Union, Tuple, Any - -from telegram import ChatPhoto, TelegramObject, constants -from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput -from telegram.utils.deprecate import TelegramDeprecationWarning - -from .chatpermissions import ChatPermissions -from .chatlocation import ChatLocation -from .utils.helpers import DEFAULT_NONE, DEFAULT_20 - -if TYPE_CHECKING: - from telegram import ( - Bot, - ChatMember, - ChatInviteLink, - Message, - MessageId, - ReplyMarkup, - Contact, - InlineKeyboardMarkup, - Location, - Venue, - MessageEntity, - InputMediaAudio, - InputMediaDocument, - InputMediaPhoto, - InputMediaVideo, - PhotoSize, - Audio, - Document, - Animation, - LabeledPrice, - Sticker, - Video, - VideoNote, - Voice, - ) - - -class Chat(TelegramObject): - """This object represents a chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Args: - id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits - and some programming languages may have difficulty/silent defects in interpreting it. - But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float - type are safe for storing this identifier. - type (:obj:`str`): Type of chat, can be either 'private', 'group', 'supergroup' or - 'channel'. - title (:obj:`str`, optional): Title, for supergroups, channels and group chats. - username(:obj:`str`, optional): Username, for private chats, supergroups and channels if - available. - first_name(:obj:`str`, optional): First name of the other party in a private chat. - last_name(:obj:`str`, optional): Last name of the other party in a private chat. - photo (:class:`telegram.ChatPhoto`, optional): Chat photo. - Returned only in :meth:`telegram.Bot.get_chat`. - bio (:obj:`str`, optional): Bio of the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_chat`. - has_private_forwards (:obj:`bool`, optional): :obj:`True`, if privacy settings of the other - party in the private chat allows to use ``tg://user?id=<user_id>`` links only in chats - with the user. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - description (:obj:`str`, optional): Description, for groups, supergroups and channel chats. - Returned only in :meth:`telegram.Bot.get_chat`. - invite_link (:obj:`str`, optional): Primary invite link, for groups, supergroups and - channel. Returned only in :meth:`telegram.Bot.get_chat`. - pinned_message (:class:`telegram.Message`, optional): The most recent pinned message - (by sending date). Returned only in :meth:`telegram.Bot.get_chat`. - permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, - for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`. - slow_mode_delay (:obj:`int`, optional): For supergroups, the minimum allowed delay between - consecutive messages sent by each unprivileged user. - Returned only in :meth:`telegram.Bot.get_chat`. - message_auto_delete_time (:obj:`int`, optional): The time after which all messages sent to - the chat will be automatically deleted; in seconds. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.4 - has_protected_content (:obj:`bool`, optional): :obj:`True`, if messages from the chat can't - be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.9 - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set. - Returned only in :meth:`telegram.Bot.get_chat`. - can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the - sticker set. Returned only in :meth:`telegram.Bot.get_chat`. - linked_chat_id (:obj:`int`, optional): Unique identifier for the linked chat, i.e. the - discussion group identifier for a channel and vice versa; for supergroups and channel - chats. Returned only in :meth:`telegram.Bot.get_chat`. - location (:class:`telegram.ChatLocation`, optional): For supergroups, the location to which - the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - id (:obj:`int`): Unique identifier for this chat. - type (:obj:`str`): Type of chat. - title (:obj:`str`): Optional. Title, for supergroups, channels and group chats. - username (:obj:`str`): Optional. Username. - first_name (:obj:`str`): Optional. First name of the other party in a private chat. - last_name (:obj:`str`): Optional. Last name of the other party in a private chat. - photo (:class:`telegram.ChatPhoto`): Optional. Chat photo. - bio (:obj:`str`): Optional. Bio of the other party in a private chat. Returned only in - :meth:`telegram.Bot.get_chat`. - has_private_forwards (:obj:`bool`): Optional. :obj:`True`, if privacy settings of the other - party in the private chat allows to use ``tg://user?id=<user_id>`` links only in chats - with the user. - - .. versionadded:: 13.9 - description (:obj:`str`): Optional. Description, for groups, supergroups and channel chats. - invite_link (:obj:`str`): Optional. Primary invite link, for groups, supergroups and - channel. Returned only in :meth:`telegram.Bot.get_chat`. - pinned_message (:class:`telegram.Message`): Optional. The most recent pinned message - (by sending date). Returned only in :meth:`telegram.Bot.get_chat`. - permissions (:class:`telegram.ChatPermissions`): Optional. Default chat member permissions, - for groups and supergroups. Returned only in :meth:`telegram.Bot.get_chat`. - slow_mode_delay (:obj:`int`): Optional. For supergroups, the minimum allowed delay between - consecutive messages sent by each unprivileged user. Returned only in - :meth:`telegram.Bot.get_chat`. - message_auto_delete_time (:obj:`int`): Optional. The time after which all messages sent to - the chat will be automatically deleted; in seconds. Returned only in - :meth:`telegram.Bot.get_chat`. - - .. versionadded:: 13.4 - has_protected_content (:obj:`bool`): Optional. :obj:`True`, if messages from the chat can't - be forwarded to other chats. - - .. versionadded:: 13.9 - sticker_set_name (:obj:`str`): Optional. For supergroups, name of Group sticker set. - can_set_sticker_set (:obj:`bool`): Optional. :obj:`True`, if the bot can change group the - sticker set. - linked_chat_id (:obj:`int`): Optional. Unique identifier for the linked chat, i.e. the - discussion group identifier for a channel and vice versa; for supergroups and channel - chats. Returned only in :meth:`telegram.Bot.get_chat`. - location (:class:`telegram.ChatLocation`): Optional. For supergroups, the location to which - the supergroup is connected. Returned only in :meth:`telegram.Bot.get_chat`. - - """ - - __slots__ = ( - 'bio', - 'id', - 'type', - 'last_name', - 'bot', - 'sticker_set_name', - 'slow_mode_delay', - 'location', - 'first_name', - 'permissions', - 'invite_link', - 'pinned_message', - 'description', - 'can_set_sticker_set', - 'username', - 'title', - 'photo', - 'linked_chat_id', - 'all_members_are_administrators', - 'message_auto_delete_time', - 'has_protected_content', - 'has_private_forwards', - '_id_attrs', - ) - - SENDER: ClassVar[str] = constants.CHAT_SENDER - """:const:`telegram.constants.CHAT_SENDER` - - .. versionadded:: 13.5 - """ - PRIVATE: ClassVar[str] = constants.CHAT_PRIVATE - """:const:`telegram.constants.CHAT_PRIVATE`""" - GROUP: ClassVar[str] = constants.CHAT_GROUP - """:const:`telegram.constants.CHAT_GROUP`""" - SUPERGROUP: ClassVar[str] = constants.CHAT_SUPERGROUP - """:const:`telegram.constants.CHAT_SUPERGROUP`""" - CHANNEL: ClassVar[str] = constants.CHAT_CHANNEL - """:const:`telegram.constants.CHAT_CHANNEL`""" - - def __init__( - self, - id: int, - type: str, - title: str = None, - username: str = None, - first_name: str = None, - last_name: str = None, - bot: 'Bot' = None, - photo: ChatPhoto = None, - description: str = None, - invite_link: str = None, - pinned_message: 'Message' = None, - permissions: ChatPermissions = None, - sticker_set_name: str = None, - can_set_sticker_set: bool = None, - slow_mode_delay: int = None, - bio: str = None, - linked_chat_id: int = None, - location: ChatLocation = None, - message_auto_delete_time: int = None, - has_private_forwards: bool = None, - has_protected_content: bool = None, - **_kwargs: Any, - ): - # Required - self.id = int(id) # pylint: disable=C0103 - self.type = type - # Optionals - self.title = title - self.username = username - self.first_name = first_name - self.last_name = last_name - # TODO: Remove (also from tests), when Telegram drops this completely - self.all_members_are_administrators = _kwargs.get('all_members_are_administrators') - self.photo = photo - self.bio = bio - self.has_private_forwards = has_private_forwards - self.description = description - self.invite_link = invite_link - self.pinned_message = pinned_message - self.permissions = permissions - self.slow_mode_delay = slow_mode_delay - self.message_auto_delete_time = ( - int(message_auto_delete_time) if message_auto_delete_time is not None else None - ) - self.has_protected_content = has_protected_content - self.sticker_set_name = sticker_set_name - self.can_set_sticker_set = can_set_sticker_set - self.linked_chat_id = linked_chat_id - self.location = location - - self.bot = bot - self._id_attrs = (self.id,) - - @property - def full_name(self) -> Optional[str]: - """ - :obj:`str`: Convenience property. If :attr:`first_name` is not :obj:`None` gives, - :attr:`first_name` followed by (if available) :attr:`last_name`. - - Note: - :attr:`full_name` will always be :obj:`None`, if the chat is a (super)group or - channel. - - .. versionadded:: 13.2 - """ - if not self.first_name: - return None - if self.last_name: - return f'{self.first_name} {self.last_name}' - return self.first_name - - @property - def link(self) -> Optional[str]: - """:obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me - link of the chat. - """ - if self.username: - return f"https://t.me/{self.username}" - return None - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Chat']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['photo'] = ChatPhoto.de_json(data.get('photo'), bot) - from telegram import Message # pylint: disable=C0415 - - data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) - data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot) - data['location'] = ChatLocation.de_json(data.get('location'), bot) - - return cls(bot=bot, **data) - - def leave(self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None) -> bool: - """Shortcut for:: - - bot.leave_chat(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.leave_chat`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.leave_chat( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def get_administrators( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> List['ChatMember']: - """Shortcut for:: - - bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.get_chat_administrators`. - - Returns: - List[:class:`telegram.ChatMember`]: A list of administrators in a chat. An Array of - :class:`telegram.ChatMember` objects that contains information about all - chat administrators except other bots. If the chat is a group or a supergroup - and no administrators were appointed, only the creator will be returned. - - """ - return self.bot.get_chat_administrators( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def get_members_count( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> int: - """ - Deprecated, use :func:`~telegram.Chat.get_member_count` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`Chat.get_members_count` is deprecated. Use `Chat.get_member_count` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - - return self.get_member_count( - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def get_member_count( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> int: - """Shortcut for:: - - bot.get_chat_member_count(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.get_chat_member_count`. - - Returns: - :obj:`int` - """ - return self.bot.get_chat_member_count( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def get_member( - self, - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> 'ChatMember': - """Shortcut for:: - - bot.get_chat_member(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_chat_member`. - - Returns: - :class:`telegram.ChatMember` - - """ - return self.bot.get_chat_member( - chat_id=self.id, - user_id=user_id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def kick_member( - self, - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Deprecated, use :func:`~telegram.Chat.ban_member` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`Chat.kick_member` is deprecated. Use `Chat.ban_member` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - - return self.ban_member( - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - - def ban_member( - self, - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """Shortcut for:: - - bot.ban_chat_member(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.ban_chat_member`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - """ - return self.bot.ban_chat_member( - chat_id=self.id, - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - - def ban_sender_chat( - self, - sender_chat_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.ban_chat_sender_chat(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.ban_chat_sender_chat`. - - .. versionadded:: 13.9 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.ban_chat_sender_chat( - chat_id=self.id, sender_chat_id=sender_chat_id, timeout=timeout, api_kwargs=api_kwargs - ) - - def ban_chat( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.ban_chat_sender_chat(sender_chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.ban_chat_sender_chat`. - - .. versionadded:: 13.9 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.ban_chat_sender_chat( - chat_id=chat_id, sender_chat_id=self.id, timeout=timeout, api_kwargs=api_kwargs - ) - - def unban_sender_chat( - self, - sender_chat_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.unban_chat_sender_chat(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.unban_chat_sender_chat`. - - .. versionadded:: 13.9 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unban_chat_sender_chat( - chat_id=self.id, sender_chat_id=sender_chat_id, timeout=timeout, api_kwargs=api_kwargs - ) - - def unban_chat( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.unban_chat_sender_chat(sender_chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.unban_chat_sender_chat`. - - .. versionadded:: 13.9 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unban_chat_sender_chat( - chat_id=chat_id, sender_chat_id=self.id, timeout=timeout, api_kwargs=api_kwargs - ) - - def unban_member( - self, - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - only_if_banned: bool = None, - ) -> bool: - """Shortcut for:: - - bot.unban_chat_member(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.unban_chat_member`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unban_chat_member( - chat_id=self.id, - user_id=user_id, - timeout=timeout, - api_kwargs=api_kwargs, - only_if_banned=only_if_banned, - ) - - def promote_member( - self, - user_id: Union[str, int], - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, - can_manage_voice_chats: bool = None, - ) -> bool: - """Shortcut for:: - - bot.promote_chat_member(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.promote_chat_member`. - - .. versionadded:: 13.2 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.promote_chat_member( - chat_id=self.id, - user_id=user_id, - can_change_info=can_change_info, - can_post_messages=can_post_messages, - can_edit_messages=can_edit_messages, - can_delete_messages=can_delete_messages, - can_invite_users=can_invite_users, - can_restrict_members=can_restrict_members, - can_pin_messages=can_pin_messages, - can_promote_members=can_promote_members, - timeout=timeout, - api_kwargs=api_kwargs, - is_anonymous=is_anonymous, - can_manage_chat=can_manage_chat, - can_manage_voice_chats=can_manage_voice_chats, - ) - - def restrict_member( - self, - user_id: Union[str, int], - permissions: ChatPermissions, - until_date: Union[int, datetime] = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.restrict_chat_member(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.restrict_chat_member`. - - .. versionadded:: 13.2 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.restrict_chat_member( - chat_id=self.id, - user_id=user_id, - permissions=permissions, - until_date=until_date, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def set_permissions( - self, - permissions: ChatPermissions, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.set_chat_permissions(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.set_chat_permissions`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.set_chat_permissions( - chat_id=self.id, - permissions=permissions, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def set_administrator_custom_title( - self, - user_id: Union[int, str], - custom_title: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.set_chat_administrator_custom_title(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.set_chat_administrator_custom_title`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.set_chat_administrator_custom_title( - chat_id=self.id, - user_id=user_id, - custom_title=custom_title, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def pin_message( - self, - message_id: int, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.pin_chat_message(chat_id=update.effective_chat.id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.pin_chat_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.pin_chat_message( - chat_id=self.id, - message_id=message_id, - disable_notification=disable_notification, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def unpin_message( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - message_id: int = None, - ) -> bool: - """Shortcut for:: - - bot.unpin_chat_message(chat_id=update.effective_chat.id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.unpin_chat_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unpin_chat_message( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - message_id=message_id, - ) - - def unpin_all_messages( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.unpin_all_chat_messages(chat_id=update.effective_chat.id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.unpin_all_chat_messages`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unpin_all_chat_messages( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def send_message( - self, - text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_message(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_message( - chat_id=self.id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - entities=entities, - protect_content=protect_content, - ) - - def send_media_group( - self, - media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] - ], - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> List['Message']: - """Shortcut for:: - - bot.send_media_group(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. - - Returns: - List[:class:`telegram.Message`]: On success, instance representing the message posted. - - """ - return self.bot.send_media_group( - chat_id=self.id, - media=media, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_chat_action( - self, - action: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.send_chat_action(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.send_chat_action( - chat_id=self.id, - action=action, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - send_action = send_chat_action - """Alias for :attr:`send_chat_action`""" - - def send_photo( - self, - photo: Union[FileInput, 'PhotoSize'], - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_photo(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_photo( - chat_id=self.id, - photo=photo, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_contact( - self, - phone_number: str = None, - first_name: str = None, - last_name: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - contact: 'Contact' = None, - vcard: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_contact(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_contact( - chat_id=self.id, - phone_number=phone_number, - first_name=first_name, - last_name=last_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - contact=contact, - vcard=vcard, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_audio( - self, - audio: Union[FileInput, 'Audio'], - duration: int = None, - performer: str = None, - title: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_audio(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_audio( - chat_id=self.id, - audio=audio, - duration=duration, - performer=performer, - title=title, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_document( - self, - document: Union[FileInput, 'Document'], - filename: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - disable_content_type_detection: bool = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_document(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_document( - chat_id=self.id, - document=document, - filename=filename, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - thumb=thumb, - api_kwargs=api_kwargs, - disable_content_type_detection=disable_content_type_detection, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - protect_content=protect_content, - ) - - def send_dice( - self, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - emoji: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_dice(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_dice( - chat_id=self.id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - emoji=emoji, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_game( - self, - game_short_name: str, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_game(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_game( - chat_id=self.id, - game_short_name=game_short_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_invoice( - self, - title: str, - description: str, - payload: str, - provider_token: str, - currency: str, - prices: List['LabeledPrice'], - start_parameter: str = None, - photo_url: str = None, - photo_size: int = None, - photo_width: int = None, - photo_height: int = None, - need_name: bool = None, - need_phone_number: bool = None, - need_email: bool = None, - need_shipping_address: bool = None, - is_flexible: bool = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, - provider_data: Union[str, object] = None, - send_phone_number_to_provider: bool = None, - send_email_to_provider: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - max_tip_amount: int = None, - suggested_tip_amounts: List[int] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_invoice(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_invoice`. - - Warning: - As of API 5.2 :attr:`start_parameter` is an optional argument and therefore the order - of the arguments had to be changed. Use keyword arguments to make sure that the - arguments are passed correctly. - - .. versionchanged:: 13.5 - As of Bot API 5.2, the parameter :attr:`start_parameter` is optional. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_invoice( - chat_id=self.id, - title=title, - description=description, - payload=payload, - provider_token=provider_token, - currency=currency, - prices=prices, - start_parameter=start_parameter, - photo_url=photo_url, - photo_size=photo_size, - photo_width=photo_width, - photo_height=photo_height, - need_name=need_name, - need_phone_number=need_phone_number, - need_email=need_email, - need_shipping_address=need_shipping_address, - is_flexible=is_flexible, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - provider_data=provider_data, - send_phone_number_to_provider=send_phone_number_to_provider, - send_email_to_provider=send_email_to_provider, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - max_tip_amount=max_tip_amount, - suggested_tip_amounts=suggested_tip_amounts, - protect_content=protect_content, - ) - - def send_location( - self, - latitude: float = None, - longitude: float = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - location: 'Location' = None, - live_period: int = None, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_location(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_location( - chat_id=self.id, - latitude=latitude, - longitude=longitude, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - location=location, - live_period=live_period, - api_kwargs=api_kwargs, - horizontal_accuracy=horizontal_accuracy, - heading=heading, - proximity_alert_radius=proximity_alert_radius, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_animation( - self, - animation: Union[FileInput, 'Animation'], - duration: int = None, - width: int = None, - height: int = None, - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_animation(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_animation( - chat_id=self.id, - animation=animation, - duration=duration, - width=width, - height=height, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_sticker( - self, - sticker: Union[FileInput, 'Sticker'], - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_sticker(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_sticker( - chat_id=self.id, - sticker=sticker, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_venue( - self, - latitude: float = None, - longitude: float = None, - title: str = None, - address: str = None, - foursquare_id: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - venue: 'Venue' = None, - foursquare_type: str = None, - api_kwargs: JSONDict = None, - google_place_id: str = None, - google_place_type: str = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_venue(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_venue( - chat_id=self.id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - venue=venue, - foursquare_type=foursquare_type, - api_kwargs=api_kwargs, - google_place_id=google_place_id, - google_place_type=google_place_type, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_video( - self, - video: Union[FileInput, 'Video'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - width: int = None, - height: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - supports_streaming: bool = None, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_video(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_video( - chat_id=self.id, - video=video, - duration=duration, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - width=width, - height=height, - parse_mode=parse_mode, - supports_streaming=supports_streaming, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_video_note( - self, - video_note: Union[FileInput, 'VideoNote'], - duration: int = None, - length: int = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_video_note(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_video_note( - chat_id=self.id, - video_note=video_note, - duration=duration, - length=length, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - filename=filename, - protect_content=protect_content, - ) - - def send_voice( - self, - voice: Union[FileInput, 'Voice'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_voice(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_voice( - chat_id=self.id, - voice=voice, - duration=duration, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_poll( - self, - question: str, - options: List[str], - is_anonymous: bool = True, - # We use constant.POLL_REGULAR instead of Poll.REGULAR here to avoid circular imports - type: str = constants.POLL_REGULAR, # pylint: disable=W0622 - allows_multiple_answers: bool = False, - correct_option_id: int = None, - is_closed: bool = None, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - explanation: str = None, - explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, - open_period: int = None, - close_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_poll(update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_poll( - chat_id=self.id, - question=question, - options=options, - is_anonymous=is_anonymous, - type=type, # pylint=pylint, - allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, - is_closed=is_closed, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - explanation=explanation, - explanation_parse_mode=explanation_parse_mode, - open_period=open_period, - close_date=close_date, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - explanation_entities=explanation_entities, - protect_content=protect_content, - ) - - def send_copy( - self, - from_chat_id: Union[str, int], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - bot.copy_message(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.copy_message( - chat_id=self.id, - from_chat_id=from_chat_id, - message_id=message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def copy_message( - self, - chat_id: Union[int, str], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - bot.copy_message(from_chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.copy_message( - from_chat_id=self.id, - chat_id=chat_id, - message_id=message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def export_invite_link( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> str: - """Shortcut for:: - - bot.export_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.export_chat_invite_link`. - - .. versionadded:: 13.4 - - Returns: - :obj:`str`: New invite link on success. - - """ - return self.bot.export_chat_invite_link( - chat_id=self.id, timeout=timeout, api_kwargs=api_kwargs - ) - - def create_invite_link( - self, - expire_date: Union[int, datetime] = None, - member_limit: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - name: str = None, - creates_join_request: bool = None, - ) -> 'ChatInviteLink': - """Shortcut for:: - - bot.create_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.create_chat_invite_link`. - - .. versionadded:: 13.4 - - .. versionchanged:: 13.8 - Edited signature according to the changes of - :meth:`telegram.Bot.create_chat_invite_link`. - - Returns: - :class:`telegram.ChatInviteLink` - - """ - return self.bot.create_chat_invite_link( - chat_id=self.id, - expire_date=expire_date, - member_limit=member_limit, - timeout=timeout, - api_kwargs=api_kwargs, - name=name, - creates_join_request=creates_join_request, - ) - - def edit_invite_link( - self, - invite_link: str, - expire_date: Union[int, datetime] = None, - member_limit: int = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - name: str = None, - creates_join_request: bool = None, - ) -> 'ChatInviteLink': - """Shortcut for:: - - bot.edit_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_chat_invite_link`. - - .. versionadded:: 13.4 - - .. versionchanged:: 13.8 - Edited signature according to the changes of :meth:`telegram.Bot.edit_chat_invite_link`. - - Returns: - :class:`telegram.ChatInviteLink` - - """ - return self.bot.edit_chat_invite_link( - chat_id=self.id, - invite_link=invite_link, - expire_date=expire_date, - member_limit=member_limit, - timeout=timeout, - api_kwargs=api_kwargs, - name=name, - creates_join_request=creates_join_request, - ) - - def revoke_invite_link( - self, - invite_link: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> 'ChatInviteLink': - """Shortcut for:: - - bot.revoke_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.revoke_chat_invite_link`. - - .. versionadded:: 13.4 - - Returns: - :class:`telegram.ChatInviteLink` - - """ - return self.bot.revoke_chat_invite_link( - chat_id=self.id, invite_link=invite_link, timeout=timeout, api_kwargs=api_kwargs - ) - - def approve_join_request( - self, - user_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.approve_chat_join_request(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.approve_chat_join_request`. - - .. versionadded:: 13.8 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.approve_chat_join_request( - chat_id=self.id, user_id=user_id, timeout=timeout, api_kwargs=api_kwargs - ) - - def decline_join_request( - self, - user_id: int, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.decline_chat_join_request(chat_id=update.effective_chat.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.decline_chat_join_request`. - - .. versionadded:: 13.8 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.decline_chat_join_request( - chat_id=self.id, user_id=user_id, timeout=timeout, api_kwargs=api_kwargs - ) diff --git a/telegramer/include/telegram/chataction.py b/telegramer/include/telegram/chataction.py deleted file mode 100644 index 335ab39..0000000 --- a/telegramer/include/telegram/chataction.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChatAction.""" -from typing import ClassVar -from telegram import constants -from telegram.utils.deprecate import set_new_attribute_deprecated - - -class ChatAction: - """Helper class to provide constants for different chat actions.""" - - __slots__ = ('__dict__',) # Adding __dict__ here since it doesn't subclass TGObject - FIND_LOCATION: ClassVar[str] = constants.CHATACTION_FIND_LOCATION - """:const:`telegram.constants.CHATACTION_FIND_LOCATION`""" - RECORD_AUDIO: ClassVar[str] = constants.CHATACTION_RECORD_AUDIO - """:const:`telegram.constants.CHATACTION_RECORD_AUDIO` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :attr:`RECORD_VOICE` instead. - """ - RECORD_VOICE: ClassVar[str] = constants.CHATACTION_RECORD_VOICE - """:const:`telegram.constants.CHATACTION_RECORD_VOICE` - - .. versionadded:: 13.5 - """ - RECORD_VIDEO: ClassVar[str] = constants.CHATACTION_RECORD_VIDEO - """:const:`telegram.constants.CHATACTION_RECORD_VIDEO`""" - RECORD_VIDEO_NOTE: ClassVar[str] = constants.CHATACTION_RECORD_VIDEO_NOTE - """:const:`telegram.constants.CHATACTION_RECORD_VIDEO_NOTE`""" - TYPING: ClassVar[str] = constants.CHATACTION_TYPING - """:const:`telegram.constants.CHATACTION_TYPING`""" - UPLOAD_AUDIO: ClassVar[str] = constants.CHATACTION_UPLOAD_AUDIO - """:const:`telegram.constants.CHATACTION_UPLOAD_AUDIO` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :attr:`UPLOAD_VOICE` instead. - """ - UPLOAD_VOICE: ClassVar[str] = constants.CHATACTION_UPLOAD_VOICE - """:const:`telegram.constants.CHATACTION_UPLOAD_VOICE` - - .. versionadded:: 13.5 - """ - UPLOAD_DOCUMENT: ClassVar[str] = constants.CHATACTION_UPLOAD_DOCUMENT - """:const:`telegram.constants.CHATACTION_UPLOAD_DOCUMENT`""" - CHOOSE_STICKER: ClassVar[str] = constants.CHATACTION_CHOOSE_STICKER - """:const:`telegram.constants.CHOOSE_STICKER` - - .. versionadded:: 13.8""" - UPLOAD_PHOTO: ClassVar[str] = constants.CHATACTION_UPLOAD_PHOTO - """:const:`telegram.constants.CHATACTION_UPLOAD_PHOTO`""" - UPLOAD_VIDEO: ClassVar[str] = constants.CHATACTION_UPLOAD_VIDEO - """:const:`telegram.constants.CHATACTION_UPLOAD_VIDEO`""" - UPLOAD_VIDEO_NOTE: ClassVar[str] = constants.CHATACTION_UPLOAD_VIDEO_NOTE - """:const:`telegram.constants.CHATACTION_UPLOAD_VIDEO_NOTE`""" - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) diff --git a/telegramer/include/telegram/chatinvitelink.py b/telegramer/include/telegram/chatinvitelink.py deleted file mode 100644 index 6852b55..0000000 --- a/telegramer/include/telegram/chatinvitelink.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents an invite link for a chat.""" -import datetime -from typing import TYPE_CHECKING, Any, Optional - -from telegram import TelegramObject, User -from telegram.utils.helpers import from_timestamp, to_timestamp -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class ChatInviteLink(TelegramObject): - """This object represents an invite link for a chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`invite_link`, :attr:`creator`, :attr:`is_primary` and - :attr:`is_revoked` are equal. - - .. versionadded:: 13.4 - - Args: - invite_link (:obj:`str`): The invite link. - creator (:class:`telegram.User`): Creator of the link. - is_primary (:obj:`bool`): :obj:`True`, if the link is primary. - is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked. - expire_date (:class:`datetime.datetime`, optional): Date when the link will expire or - has been expired. - member_limit (:obj:`int`, optional): Maximum number of users that can be members of the - chat simultaneously after joining the chat via this invite link; 1-99999. - name (:obj:`str`, optional): Invite link name. - - .. versionadded:: 13.8 - creates_join_request (:obj:`bool`, optional): :obj:`True`, if users joining the chat via - the link need to be approved by chat administrators. - - .. versionadded:: 13.8 - pending_join_request_count (:obj:`int`, optional): Number of pending join requests - created using this link. - - .. versionadded:: 13.8 - - Attributes: - invite_link (:obj:`str`): The invite link. If the link was created by another chat - administrator, then the second part of the link will be replaced with ``'…'``. - creator (:class:`telegram.User`): Creator of the link. - is_primary (:obj:`bool`): :obj:`True`, if the link is primary. - is_revoked (:obj:`bool`): :obj:`True`, if the link is revoked. - expire_date (:class:`datetime.datetime`): Optional. Date when the link will expire or - has been expired. - member_limit (:obj:`int`): Optional. Maximum number of users that can be members - of the chat simultaneously after joining the chat via this invite link; 1-99999. - name (:obj:`str`): Optional. Invite link name. - - .. versionadded:: 13.8 - creates_join_request (:obj:`bool`): Optional. :obj:`True`, if users joining the chat via - the link need to be approved by chat administrators. - - .. versionadded:: 13.8 - pending_join_request_count (:obj:`int`): Optional. Number of pending join requests - created using this link. - - .. versionadded:: 13.8 - - """ - - __slots__ = ( - 'invite_link', - 'creator', - 'is_primary', - 'is_revoked', - 'expire_date', - 'member_limit', - 'name', - 'creates_join_request', - 'pending_join_request_count', - '_id_attrs', - ) - - def __init__( - self, - invite_link: str, - creator: User, - is_primary: bool, - is_revoked: bool, - expire_date: datetime.datetime = None, - member_limit: int = None, - name: str = None, - creates_join_request: bool = None, - pending_join_request_count: int = None, - **_kwargs: Any, - ): - # Required - self.invite_link = invite_link - self.creator = creator - self.is_primary = is_primary - self.is_revoked = is_revoked - - # Optionals - self.expire_date = expire_date - self.member_limit = int(member_limit) if member_limit is not None else None - self.name = name - self.creates_join_request = creates_join_request - self.pending_join_request_count = ( - int(pending_join_request_count) if pending_join_request_count is not None else None - ) - self._id_attrs = (self.invite_link, self.creator, self.is_primary, self.is_revoked) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatInviteLink']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['creator'] = User.de_json(data.get('creator'), bot) - data['expire_date'] = from_timestamp(data.get('expire_date', None)) - - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['expire_date'] = to_timestamp(self.expire_date) - - return data diff --git a/telegramer/include/telegram/chatjoinrequest.py b/telegramer/include/telegram/chatjoinrequest.py deleted file mode 100644 index ec89c66..0000000 --- a/telegramer/include/telegram/chatjoinrequest.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChatJoinRequest.""" -import datetime -from typing import TYPE_CHECKING, Any, Optional - -from telegram import TelegramObject, User, Chat, ChatInviteLink -from telegram.utils.helpers import from_timestamp, to_timestamp, DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot - - -class ChatJoinRequest(TelegramObject): - """This object represents a join request sent to a chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`chat`, :attr:`from_user` and :attr:`date` are equal. - - Note: - Since Bot API 5.5, bots are allowed to contact users who sent a join request to a chat - where the bot is an administrator with the - :attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right – even if - the user never interacted with the bot before. - - .. versionadded:: 13.8 - - Args: - chat (:class:`telegram.Chat`): Chat to which the request was sent. - from_user (:class:`telegram.User`): User that sent the join request. - date (:class:`datetime.datetime`): Date the request was sent. - bio (:obj:`str`, optional): Bio of the user. - invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link that was used - by the user to send the join request. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - - Attributes: - chat (:class:`telegram.Chat`): Chat to which the request was sent. - from_user (:class:`telegram.User`): User that sent the join request. - date (:class:`datetime.datetime`): Date the request was sent. - bio (:obj:`str`): Optional. Bio of the user. - invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link that was used - by the user to send the join request. - - """ - - __slots__ = ( - 'chat', - 'from_user', - 'date', - 'bio', - 'invite_link', - 'bot', - '_id_attrs', - ) - - def __init__( - self, - chat: Chat, - from_user: User, - date: datetime.datetime, - bio: str = None, - invite_link: ChatInviteLink = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.chat = chat - self.from_user = from_user - self.date = date - - # Optionals - self.bio = bio - self.invite_link = invite_link - - self.bot = bot - self._id_attrs = (self.chat, self.from_user, self.date) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatJoinRequest']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['chat'] = Chat.de_json(data.get('chat'), bot) - data['from_user'] = User.de_json(data.get('from'), bot) - data['date'] = from_timestamp(data.get('date', None)) - data['invite_link'] = ChatInviteLink.de_json(data.get('invite_link'), bot) - - return cls(bot=bot, **data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['date'] = to_timestamp(self.date) - - return data - - def approve( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.approve_chat_join_request(chat_id=update.effective_chat.id, - user_id=update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.approve_chat_join_request`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.approve_chat_join_request( - chat_id=self.chat.id, user_id=self.from_user.id, timeout=timeout, api_kwargs=api_kwargs - ) - - def decline( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.decline_chat_join_request(chat_id=update.effective_chat.id, - user_id=update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.decline_chat_join_request`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.decline_chat_join_request( - chat_id=self.chat.id, user_id=self.from_user.id, timeout=timeout, api_kwargs=api_kwargs - ) diff --git a/telegramer/include/telegram/chatlocation.py b/telegramer/include/telegram/chatlocation.py deleted file mode 100644 index f88c708..0000000 --- a/telegramer/include/telegram/chatlocation.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a location to which a chat is connected.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import TelegramObject -from telegram.utils.types import JSONDict - -from .files.location import Location - -if TYPE_CHECKING: - from telegram import Bot - - -class ChatLocation(TelegramObject): - """This object represents a location to which a chat is connected. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`location` is equal. - - Args: - location (:class:`telegram.Location`): The location to which the supergroup is connected. - Can't be a live location. - address (:obj:`str`): Location address; 1-64 characters, as defined by the chat owner - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - location (:class:`telegram.Location`): The location to which the supergroup is connected. - address (:obj:`str`): Location address, as defined by the chat owner - - """ - - __slots__ = ('location', '_id_attrs', 'address') - - def __init__( - self, - location: Location, - address: str, - **_kwargs: Any, - ): - self.location = location - self.address = address - - self._id_attrs = (self.location,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatLocation']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['location'] = Location.de_json(data.get('location'), bot) - - return cls(bot=bot, **data) diff --git a/telegramer/include/telegram/chatmember.py b/telegramer/include/telegram/chatmember.py deleted file mode 100644 index 06968d4..0000000 --- a/telegramer/include/telegram/chatmember.py +++ /dev/null @@ -1,715 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChatMember.""" -import datetime -from typing import TYPE_CHECKING, Any, Optional, ClassVar, Dict, Type - -from telegram import TelegramObject, User, constants -from telegram.utils.helpers import from_timestamp, to_timestamp -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class ChatMember(TelegramObject): - """Base class for Telegram ChatMember Objects. - Currently, the following 6 types of chat members are supported: - - * :class:`telegram.ChatMemberOwner` - * :class:`telegram.ChatMemberAdministrator` - * :class:`telegram.ChatMemberMember` - * :class:`telegram.ChatMemberRestricted` - * :class:`telegram.ChatMemberLeft` - * :class:`telegram.ChatMemberBanned` - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`user` and :attr:`status` are equal. - - Note: - As of Bot API 5.3, :class:`ChatMember` is nothing but the base class for the subclasses - listed above and is no longer returned directly by :meth:`~telegram.Bot.get_chat`. - Therefore, most of the arguments and attributes were deprecated and you should no longer - use :class:`ChatMember` directly. - - Args: - user (:class:`telegram.User`): Information about the user. - status (:obj:`str`): The member's status in the chat. Can be - :attr:`~telegram.ChatMember.ADMINISTRATOR`, :attr:`~telegram.ChatMember.CREATOR`, - :attr:`~telegram.ChatMember.KICKED`, :attr:`~telegram.ChatMember.LEFT`, - :attr:`~telegram.ChatMember.MEMBER` or :attr:`~telegram.ChatMember.RESTRICTED`. - custom_title (:obj:`str`, optional): Owner and administrators only. - Custom title for this user. - - .. deprecated:: 13.7 - - is_anonymous (:obj:`bool`, optional): Owner and administrators only. :obj:`True`, if the - user's presence in the chat is hidden. - - .. deprecated:: 13.7 - - until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when - restrictions will be lifted for this user. - - .. deprecated:: 13.7 - - can_be_edited (:obj:`bool`, optional): Administrators only. :obj:`True`, if the bot is - allowed to edit administrator privileges of that user. - - .. deprecated:: 13.7 - - can_manage_chat (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can access the chat event log, chat statistics, message statistics in - channels, see channel members, see anonymous administrators in supergroups and ignore - slow mode. Implied by any other administrator privilege. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_manage_voice_chats (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can manage voice chats. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_change_info (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can change the chat title, photo and other settings. - - .. deprecated:: 13.7 - - can_post_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can post in the channel, channels only. - - .. deprecated:: 13.7 - - can_edit_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can edit messages of other users and can pin messages; channels only. - - .. deprecated:: 13.7 - - can_delete_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can delete messages of other users. - - .. deprecated:: 13.7 - - can_invite_users (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can invite new users to the chat. - - .. deprecated:: 13.7 - - can_restrict_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can restrict, ban or unban chat members. - - .. deprecated:: 13.7 - - can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can pin messages, groups and supergroups only. - - .. deprecated:: 13.7 - - can_promote_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can add new administrators with a subset of his own privileges or demote - administrators that he has promoted, directly or indirectly (promoted by administrators - that were appointed by the user). - - .. deprecated:: 13.7 - - is_member (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is a member of - the chat at the moment of the request. - - .. deprecated:: 13.7 - - can_send_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user can - send text messages, contacts, locations and venues. - - .. deprecated:: 13.7 - - can_send_media_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user - can send audios, documents, photos, videos, video notes and voice notes. - - .. deprecated:: 13.7 - - can_send_polls (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is - allowed to send polls. - - .. deprecated:: 13.7 - - can_send_other_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user - can send animations, games, stickers and use inline bots. - - .. deprecated:: 13.7 - - can_add_web_page_previews (:obj:`bool`, optional): Restricted only. :obj:`True`, if user - may add web page previews to his messages. - - .. deprecated:: 13.7 - - Attributes: - user (:class:`telegram.User`): Information about the user. - status (:obj:`str`): The member's status in the chat. - custom_title (:obj:`str`): Optional. Custom title for owner and administrators. - - .. deprecated:: 13.7 - - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's presence in the chat is - hidden. - - .. deprecated:: 13.7 - - until_date (:class:`datetime.datetime`): Optional. Date when restrictions will be lifted - for this user. - - .. deprecated:: 13.7 - - can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator - privileges of that user. - - .. deprecated:: 13.7 - - can_manage_chat (:obj:`bool`): Optional. If the administrator can access the chat event - log, chat statistics, message statistics in channels, see channel members, see - anonymous administrators in supergroups and ignore slow mode. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_manage_voice_chats (:obj:`bool`): Optional. if the administrator can manage - voice chats. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and - other settings. - - .. deprecated:: 13.7 - - can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel. - - .. deprecated:: 13.7 - - can_edit_messages (:obj:`bool`): Optional. If the administrator can edit messages of other - users. - - .. deprecated:: 13.7 - - can_delete_messages (:obj:`bool`): Optional. If the administrator can delete messages of - other users. - - .. deprecated:: 13.7 - - can_invite_users (:obj:`bool`): Optional. If the user can invite new users to the chat. - - .. deprecated:: 13.7 - - can_restrict_members (:obj:`bool`): Optional. If the administrator can restrict, ban or - unban chat members. - - .. deprecated:: 13.7 - - can_pin_messages (:obj:`bool`): Optional. If the user can pin messages. - - .. deprecated:: 13.7 - - can_promote_members (:obj:`bool`): Optional. If the administrator can add new - administrators. - - .. deprecated:: 13.7 - - is_member (:obj:`bool`): Optional. Restricted only. :obj:`True`, if the user is a member of - the chat at the moment of the request. - - .. deprecated:: 13.7 - - can_send_messages (:obj:`bool`): Optional. If the user can send text messages, contacts, - locations and venues. - - .. deprecated:: 13.7 - - can_send_media_messages (:obj:`bool`): Optional. If the user can send media messages, - implies can_send_messages. - - .. deprecated:: 13.7 - - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to - send polls. - - .. deprecated:: 13.7 - - can_send_other_messages (:obj:`bool`): Optional. If the user can send animations, games, - stickers and use inline bots, implies can_send_media_messages. - - .. deprecated:: 13.7 - - can_add_web_page_previews (:obj:`bool`): Optional. If user may add web page previews to his - messages, implies can_send_media_messages - - .. deprecated:: 13.7 - - """ - - __slots__ = ( - 'is_member', - 'can_restrict_members', - 'can_delete_messages', - 'custom_title', - 'can_be_edited', - 'can_post_messages', - 'can_send_messages', - 'can_edit_messages', - 'can_send_media_messages', - 'is_anonymous', - 'can_add_web_page_previews', - 'can_send_other_messages', - 'can_invite_users', - 'can_send_polls', - 'user', - 'can_promote_members', - 'status', - 'can_change_info', - 'can_pin_messages', - 'can_manage_chat', - 'can_manage_voice_chats', - 'until_date', - '_id_attrs', - ) - - ADMINISTRATOR: ClassVar[str] = constants.CHATMEMBER_ADMINISTRATOR - """:const:`telegram.constants.CHATMEMBER_ADMINISTRATOR`""" - CREATOR: ClassVar[str] = constants.CHATMEMBER_CREATOR - """:const:`telegram.constants.CHATMEMBER_CREATOR`""" - KICKED: ClassVar[str] = constants.CHATMEMBER_KICKED - """:const:`telegram.constants.CHATMEMBER_KICKED`""" - LEFT: ClassVar[str] = constants.CHATMEMBER_LEFT - """:const:`telegram.constants.CHATMEMBER_LEFT`""" - MEMBER: ClassVar[str] = constants.CHATMEMBER_MEMBER - """:const:`telegram.constants.CHATMEMBER_MEMBER`""" - RESTRICTED: ClassVar[str] = constants.CHATMEMBER_RESTRICTED - """:const:`telegram.constants.CHATMEMBER_RESTRICTED`""" - - def __init__( - self, - user: User, - status: str, - until_date: datetime.datetime = None, - can_be_edited: bool = None, - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - is_member: bool = None, - custom_title: str = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, - can_manage_voice_chats: bool = None, - **_kwargs: Any, - ): - # Required - self.user = user - self.status = status - - # Optionals - self.custom_title = custom_title - self.is_anonymous = is_anonymous - self.until_date = until_date - self.can_be_edited = can_be_edited - self.can_change_info = can_change_info - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_invite_users = can_invite_users - self.can_restrict_members = can_restrict_members - self.can_pin_messages = can_pin_messages - self.can_promote_members = can_promote_members - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.is_member = is_member - self.can_manage_chat = can_manage_chat - self.can_manage_voice_chats = can_manage_voice_chats - - self._id_attrs = (self.user, self.status) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMember']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['user'] = User.de_json(data.get('user'), bot) - data['until_date'] = from_timestamp(data.get('until_date', None)) - - _class_mapping: Dict[str, Type['ChatMember']] = { - cls.CREATOR: ChatMemberOwner, - cls.ADMINISTRATOR: ChatMemberAdministrator, - cls.MEMBER: ChatMemberMember, - cls.RESTRICTED: ChatMemberRestricted, - cls.LEFT: ChatMemberLeft, - cls.KICKED: ChatMemberBanned, - } - - if cls is ChatMember: - return _class_mapping.get(data['status'], cls)(**data, bot=bot) - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['until_date'] = to_timestamp(self.until_date) - - return data - - -class ChatMemberOwner(ChatMember): - """ - Represents a chat member that owns the chat - and has all administrator privileges. - - .. versionadded:: 13.7 - - Args: - user (:class:`telegram.User`): Information about the user. - custom_title (:obj:`str`, optional): Custom title for this user. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the - user's presence in the chat is hidden. - - Attributes: - status (:obj:`str`): The member's status in the chat, - always :attr:`telegram.ChatMember.CREATOR`. - user (:class:`telegram.User`): Information about the user. - custom_title (:obj:`str`): Optional. Custom title for - this user. - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's - presence in the chat is hidden. - """ - - __slots__ = () - - def __init__( - self, - user: User, - custom_title: str = None, - is_anonymous: bool = None, - **_kwargs: Any, - ): - super().__init__( - status=ChatMember.CREATOR, - user=user, - custom_title=custom_title, - is_anonymous=is_anonymous, - ) - - -class ChatMemberAdministrator(ChatMember): - """ - Represents a chat member that has some additional privileges. - - .. versionadded:: 13.7 - - Args: - user (:class:`telegram.User`): Information about the user. - can_be_edited (:obj:`bool`, optional): :obj:`True`, if the bot - is allowed to edit administrator privileges of that user. - custom_title (:obj:`str`, optional): Custom title for this user. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the user's - presence in the chat is hidden. - can_manage_chat (:obj:`bool`, optional): :obj:`True`, if the administrator - can access the chat event log, chat statistics, message statistics in - channels, see channel members, see anonymous administrators in supergroups - and ignore slow mode. Implied by any other administrator privilege. - can_post_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can post in the channel, channels only. - can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can edit messages of other users and can pin - messages; channels only. - can_delete_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can delete messages of other users. - can_manage_voice_chats (:obj:`bool`, optional): :obj:`True`, if the - administrator can manage voice chats. - can_restrict_members (:obj:`bool`, optional): :obj:`True`, if the - administrator can restrict, ban or unban chat members. - can_promote_members (:obj:`bool`, optional): :obj:`True`, if the administrator - can add new administrators with a subset of his own privileges or demote - administrators that he has promoted, directly or indirectly (promoted by - administrators that were appointed by the user). - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user can change - the chat title, photo and other settings. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user can invite - new users to the chat. - can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed - to pin messages; groups and supergroups only. - - Attributes: - status (:obj:`str`): The member's status in the chat, - always :attr:`telegram.ChatMember.ADMINISTRATOR`. - user (:class:`telegram.User`): Information about the user. - can_be_edited (:obj:`bool`): Optional. :obj:`True`, if the bot - is allowed to edit administrator privileges of that user. - custom_title (:obj:`str`): Optional. Custom title for this user. - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's - presence in the chat is hidden. - can_manage_chat (:obj:`bool`): Optional. :obj:`True`, if the administrator - can access the chat event log, chat statistics, message statistics in - channels, see channel members, see anonymous administrators in supergroups - and ignore slow mode. Implied by any other administrator privilege. - can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can post in the channel, channels only. - can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can edit messages of other users and can pin - messages; channels only. - can_delete_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can delete messages of other users. - can_manage_voice_chats (:obj:`bool`): Optional. :obj:`True`, if the - administrator can manage voice chats. - can_restrict_members (:obj:`bool`): Optional. :obj:`True`, if the - administrator can restrict, ban or unban chat members. - can_promote_members (:obj:`bool`): Optional. :obj:`True`, if the administrator - can add new administrators with a subset of his own privileges or demote - administrators that he has promoted, directly or indirectly (promoted by - administrators that were appointed by the user). - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user can change - the chat title, photo and other settings. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user can invite - new users to the chat. - can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed - to pin messages; groups and supergroups only. - """ - - __slots__ = () - - def __init__( - self, - user: User, - can_be_edited: bool = None, - custom_title: str = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_manage_voice_chats: bool = None, - can_restrict_members: bool = None, - can_promote_members: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, - can_pin_messages: bool = None, - **_kwargs: Any, - ): - super().__init__( - status=ChatMember.ADMINISTRATOR, - user=user, - can_be_edited=can_be_edited, - custom_title=custom_title, - is_anonymous=is_anonymous, - can_manage_chat=can_manage_chat, - can_post_messages=can_post_messages, - can_edit_messages=can_edit_messages, - can_delete_messages=can_delete_messages, - can_manage_voice_chats=can_manage_voice_chats, - can_restrict_members=can_restrict_members, - can_promote_members=can_promote_members, - can_change_info=can_change_info, - can_invite_users=can_invite_users, - can_pin_messages=can_pin_messages, - ) - - -class ChatMemberMember(ChatMember): - """ - Represents a chat member that has no additional - privileges or restrictions. - - .. versionadded:: 13.7 - - Args: - user (:class:`telegram.User`): Information about the user. - - Attributes: - status (:obj:`str`): The member's status in the chat, - always :attr:`telegram.ChatMember.MEMBER`. - user (:class:`telegram.User`): Information about the user. - - """ - - __slots__ = () - - def __init__(self, user: User, **_kwargs: Any): - super().__init__(status=ChatMember.MEMBER, user=user) - - -class ChatMemberRestricted(ChatMember): - """ - Represents a chat member that is under certain restrictions - in the chat. Supergroups only. - - .. versionadded:: 13.7 - - Args: - user (:class:`telegram.User`): Information about the user. - is_member (:obj:`bool`, optional): :obj:`True`, if the user is a - member of the chat at the moment of the request. - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user can change - the chat title, photo and other settings. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user can invite - new users to the chat. - can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed - to pin messages; groups and supergroups only. - can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed - to send text messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed - to send audios, documents, photos, videos, video notes and voice notes. - can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed - to send polls. - can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed - to send animations, games, stickers and use inline bots. - can_add_web_page_previews (:obj:`bool`, optional): :obj:`True`, if the user is - allowed to add web page previews to their messages. - until_date (:class:`datetime.datetime`, optional): Date when restrictions - will be lifted for this user. - - Attributes: - status (:obj:`str`): The member's status in the chat, - always :attr:`telegram.ChatMember.RESTRICTED`. - user (:class:`telegram.User`): Information about the user. - is_member (:obj:`bool`): Optional. :obj:`True`, if the user is a - member of the chat at the moment of the request. - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user can change - the chat title, photo and other settings. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user can invite - new users to the chat. - can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed - to pin messages; groups and supergroups only. - can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed - to send text messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed - to send audios, documents, photos, videos, video notes and voice notes. - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed - to send polls. - can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed - to send animations, games, stickers and use inline bots. - can_add_web_page_previews (:obj:`bool`): Optional. :obj:`True`, if the user is - allowed to add web page previews to their messages. - until_date (:class:`datetime.datetime`): Optional. Date when restrictions - will be lifted for this user. - - """ - - __slots__ = () - - def __init__( - self, - user: User, - is_member: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, - can_pin_messages: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - until_date: datetime.datetime = None, - **_kwargs: Any, - ): - super().__init__( - status=ChatMember.RESTRICTED, - user=user, - is_member=is_member, - can_change_info=can_change_info, - can_invite_users=can_invite_users, - can_pin_messages=can_pin_messages, - can_send_messages=can_send_messages, - can_send_media_messages=can_send_media_messages, - can_send_polls=can_send_polls, - can_send_other_messages=can_send_other_messages, - can_add_web_page_previews=can_add_web_page_previews, - until_date=until_date, - ) - - -class ChatMemberLeft(ChatMember): - """ - Represents a chat member that isn't currently a member of the chat, - but may join it themselves. - - .. versionadded:: 13.7 - - Args: - user (:class:`telegram.User`): Information about the user. - - Attributes: - status (:obj:`str`): The member's status in the chat, - always :attr:`telegram.ChatMember.LEFT`. - user (:class:`telegram.User`): Information about the user. - """ - - __slots__ = () - - def __init__(self, user: User, **_kwargs: Any): - super().__init__(status=ChatMember.LEFT, user=user) - - -class ChatMemberBanned(ChatMember): - """ - Represents a chat member that was banned in the chat and - can't return to the chat or view chat messages. - - .. versionadded:: 13.7 - - Args: - user (:class:`telegram.User`): Information about the user. - until_date (:class:`datetime.datetime`, optional): Date when restrictions - will be lifted for this user. - - Attributes: - status (:obj:`str`): The member's status in the chat, - always :attr:`telegram.ChatMember.KICKED`. - user (:class:`telegram.User`): Information about the user. - until_date (:class:`datetime.datetime`): Optional. Date when restrictions - will be lifted for this user. - - """ - - __slots__ = () - - def __init__( - self, - user: User, - until_date: datetime.datetime = None, - **_kwargs: Any, - ): - super().__init__( - status=ChatMember.KICKED, - user=user, - until_date=until_date, - ) diff --git a/telegramer/include/telegram/chatmemberupdated.py b/telegramer/include/telegram/chatmemberupdated.py deleted file mode 100644 index cd6c76e..0000000 --- a/telegramer/include/telegram/chatmemberupdated.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChatMemberUpdated.""" -import datetime -from typing import TYPE_CHECKING, Any, Optional, Dict, Tuple, Union - -from telegram import TelegramObject, User, Chat, ChatMember, ChatInviteLink -from telegram.utils.helpers import from_timestamp, to_timestamp -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class ChatMemberUpdated(TelegramObject): - """This object represents changes in the status of a chat member. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`chat`, :attr:`from_user`, :attr:`date`, - :attr:`old_chat_member` and :attr:`new_chat_member` are equal. - - .. versionadded:: 13.4 - - Note: - In Python ``from`` is a reserved word, use ``from_user`` instead. - - Args: - chat (:class:`telegram.Chat`): Chat the user belongs to. - from_user (:class:`telegram.User`): Performer of the action, which resulted in the change. - date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to - :class:`datetime.datetime`. - old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member. - new_chat_member (:class:`telegram.ChatMember`): New information about the chat member. - invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link, which was used - by the user to join the chat. For joining by invite link events only. - - Attributes: - chat (:class:`telegram.Chat`): Chat the user belongs to. - from_user (:class:`telegram.User`): Performer of the action, which resulted in the change. - date (:class:`datetime.datetime`): Date the change was done in Unix time. Converted to - :class:`datetime.datetime`. - old_chat_member (:class:`telegram.ChatMember`): Previous information about the chat member. - new_chat_member (:class:`telegram.ChatMember`): New information about the chat member. - invite_link (:class:`telegram.ChatInviteLink`): Optional. Chat invite link, which was used - by the user to join the chat. - - """ - - __slots__ = ( - 'chat', - 'from_user', - 'date', - 'old_chat_member', - 'new_chat_member', - 'invite_link', - '_id_attrs', - ) - - def __init__( - self, - chat: Chat, - from_user: User, - date: datetime.datetime, - old_chat_member: ChatMember, - new_chat_member: ChatMember, - invite_link: ChatInviteLink = None, - **_kwargs: Any, - ): - # Required - self.chat = chat - self.from_user = from_user - self.date = date - self.old_chat_member = old_chat_member - self.new_chat_member = new_chat_member - - # Optionals - self.invite_link = invite_link - - self._id_attrs = ( - self.chat, - self.from_user, - self.date, - self.old_chat_member, - self.new_chat_member, - ) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMemberUpdated']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['chat'] = Chat.de_json(data.get('chat'), bot) - data['from_user'] = User.de_json(data.get('from'), bot) - data['date'] = from_timestamp(data.get('date')) - data['old_chat_member'] = ChatMember.de_json(data.get('old_chat_member'), bot) - data['new_chat_member'] = ChatMember.de_json(data.get('new_chat_member'), bot) - data['invite_link'] = ChatInviteLink.de_json(data.get('invite_link'), bot) - - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - # Required - data['date'] = to_timestamp(self.date) - - return data - - def difference( - self, - ) -> Dict[ - str, - Tuple[ - Union[str, bool, datetime.datetime, User], Union[str, bool, datetime.datetime, User] - ], - ]: - """Computes the difference between :attr:`old_chat_member` and :attr:`new_chat_member`. - - Example: - .. code:: python - - >>> chat_member_updated.difference() - {'custom_title': ('old title', 'new title')} - - Note: - To determine, if the :attr:`telegram.ChatMember.user` attribute has changed, *every* - attribute of the user will be checked. - - .. versionadded:: 13.5 - - Returns: - Dict[:obj:`str`, Tuple[:obj:`obj`, :obj:`obj`]]: A dictionary mapping attribute names - to tuples of the form ``(old_value, new_value)`` - """ - # we first get the names of the attributes that have changed - # user.to_dict() is unhashable, so that needs some special casing further down - old_dict = self.old_chat_member.to_dict() - old_user_dict = old_dict.pop('user') - new_dict = self.new_chat_member.to_dict() - new_user_dict = new_dict.pop('user') - - # Generator for speed: we only need to iterate over it once - # we can't directly use the values from old_dict ^ new_dict b/c that set is unordered - attributes = (entry[0] for entry in set(old_dict.items()) ^ set(new_dict.items())) - - result = { - attribute: (self.old_chat_member[attribute], self.new_chat_member[attribute]) - for attribute in attributes - } - if old_user_dict != new_user_dict: - result['user'] = (self.old_chat_member.user, self.new_chat_member.user) - - return result # type: ignore[return-value] diff --git a/telegramer/include/telegram/chatpermissions.py b/telegramer/include/telegram/chatpermissions.py deleted file mode 100644 index 44b989a..0000000 --- a/telegramer/include/telegram/chatpermissions.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChatPermission.""" - -from typing import Any - -from telegram import TelegramObject - - -class ChatPermissions(TelegramObject): - """Describes actions that a non-administrator user is allowed to take in a chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`can_send_messages`, :attr:`can_send_media_messages`, - :attr:`can_send_polls`, :attr:`can_send_other_messages`, :attr:`can_add_web_page_previews`, - :attr:`can_change_info`, :attr:`can_invite_users` and :attr:`can_pin_messages` are equal. - - Note: - Though not stated explicitly in the official docs, Telegram changes not only the - permissions that are set, but also sets all the others to :obj:`False`. However, since not - documented, this behaviour may change unbeknown to PTB. - - Args: - can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text - messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to - send audios, documents, photos, videos, video notes and voice notes, implies - :attr:`can_send_messages`. - can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send polls, - implies :attr:`can_send_messages`. - can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to - send animations, games, stickers and use inline bots, implies - :attr:`can_send_media_messages`. - can_add_web_page_previews (:obj:`bool`, optional): :obj:`True`, if the user is allowed to - add web page previews to their messages, implies :attr:`can_send_media_messages`. - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user is allowed to change the - chat title, photo and other settings. Ignored in public supergroups. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user is allowed to invite new - users to the chat. - can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin - messages. Ignored in public supergroups. - - Attributes: - can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send text - messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to - send audios, documents, photos, videos, video notes and voice notes, implies - :attr:`can_send_messages`. - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to send polls, - implies :attr:`can_send_messages`. - can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to - send animations, games, stickers and use inline bots, implies - :attr:`can_send_media_messages`. - can_add_web_page_previews (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to - add web page previews to their messages, implies :attr:`can_send_media_messages`. - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to change the - chat title, photo and other settings. Ignored in public supergroups. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to invite - new users to the chat. - can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin - messages. Ignored in public supergroups. - - """ - - __slots__ = ( - 'can_send_other_messages', - 'can_invite_users', - 'can_send_polls', - '_id_attrs', - 'can_send_messages', - 'can_send_media_messages', - 'can_change_info', - 'can_pin_messages', - 'can_add_web_page_previews', - ) - - def __init__( - self, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, - can_pin_messages: bool = None, - **_kwargs: Any, - ): - # Required - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.can_change_info = can_change_info - self.can_invite_users = can_invite_users - self.can_pin_messages = can_pin_messages - - self._id_attrs = ( - self.can_send_messages, - self.can_send_media_messages, - self.can_send_polls, - self.can_send_other_messages, - self.can_add_web_page_previews, - self.can_change_info, - self.can_invite_users, - self.can_pin_messages, - ) diff --git a/telegramer/include/telegram/choseninlineresult.py b/telegramer/include/telegram/choseninlineresult.py deleted file mode 100644 index b931553..0000000 --- a/telegramer/include/telegram/choseninlineresult.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0902,R0913 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChosenInlineResult.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import Location, TelegramObject, User -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class ChosenInlineResult(TelegramObject): - """ - Represents a result of an inline query that was chosen by the user and sent to their chat - partner. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`result_id` is equal. - - Note: - * In Python ``from`` is a reserved word, use ``from_user`` instead. - * It is necessary to enable inline feedback via `@Botfather <https://t.me/BotFather>`_ in - order to receive these objects in updates. - - Args: - result_id (:obj:`str`): The unique identifier for the result that was chosen. - from_user (:class:`telegram.User`): The user that chose the result. - location (:class:`telegram.Location`, optional): Sender location, only for bots that - require user location. - inline_message_id (:obj:`str`, optional): Identifier of the sent inline message. Available - only if there is an inline keyboard attached to the message. Will be also received in - callback queries and can be used to edit the message. - query (:obj:`str`): The query that was used to obtain the result. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - result_id (:obj:`str`): The unique identifier for the result that was chosen. - from_user (:class:`telegram.User`): The user that chose the result. - location (:class:`telegram.Location`): Optional. Sender location. - inline_message_id (:obj:`str`): Optional. Identifier of the sent inline message. - query (:obj:`str`): The query that was used to obtain the result. - - """ - - __slots__ = ('location', 'result_id', 'from_user', 'inline_message_id', '_id_attrs', 'query') - - def __init__( - self, - result_id: str, - from_user: User, - query: str, - location: Location = None, - inline_message_id: str = None, - **_kwargs: Any, - ): - # Required - self.result_id = result_id - self.from_user = from_user - self.query = query - # Optionals - self.location = location - self.inline_message_id = inline_message_id - - self._id_attrs = (self.result_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChosenInlineResult']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - # Required - data['from_user'] = User.de_json(data.pop('from'), bot) - # Optionals - data['location'] = Location.de_json(data.get('location'), bot) - - return cls(**data) diff --git a/telegramer/include/telegram/constants.py b/telegramer/include/telegram/constants.py deleted file mode 100644 index b7600a7..0000000 --- a/telegramer/include/telegram/constants.py +++ /dev/null @@ -1,398 +0,0 @@ -# python-telegram-bot - a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# by the python-telegram-bot contributors <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""Constants in the Telegram network. - -The following constants were extracted from the -`Telegram Bots FAQ <https://core.telegram.org/bots/faq>`_ and -`Telegram Bots API <https://core.telegram.org/bots/api>`_. - -Attributes: - BOT_API_VERSION (:obj:`str`): `5.7`. Telegram Bot API version supported by this - version of `python-telegram-bot`. Also available as ``telegram.bot_api_version``. - - .. versionadded:: 13.4 - MAX_MESSAGE_LENGTH (:obj:`int`): 4096 - MAX_CAPTION_LENGTH (:obj:`int`): 1024 - SUPPORTED_WEBHOOK_PORTS (List[:obj:`int`]): [443, 80, 88, 8443] - MAX_FILESIZE_DOWNLOAD (:obj:`int`): In bytes (20MB) - MAX_FILESIZE_UPLOAD (:obj:`int`): In bytes (50MB) - MAX_PHOTOSIZE_UPLOAD (:obj:`int`): In bytes (10MB) - MAX_MESSAGES_PER_SECOND_PER_CHAT (:obj:`int`): `1`. Telegram may allow short bursts that go - over this limit, but eventually you'll begin receiving 429 errors. - MAX_MESSAGES_PER_SECOND (:obj:`int`): 30 - MAX_MESSAGES_PER_MINUTE_PER_GROUP (:obj:`int`): 20 - MAX_INLINE_QUERY_RESULTS (:obj:`int`): 50 - MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH (:obj:`int`): 200 - - .. versionadded:: 13.2 - -The following constant have been found by experimentation: - -Attributes: - MAX_MESSAGE_ENTITIES (:obj:`int`): 100 (Beyond this cap telegram will simply ignore further - formatting styles) - ANONYMOUS_ADMIN_ID (:obj:`int`): ``1087968824`` (User id in groups for anonymous admin) - SERVICE_CHAT_ID (:obj:`int`): ``777000`` (Telegram service chat, that also acts as sender of - channel posts forwarded to discussion groups) - FAKE_CHANNEL_ID (:obj:`int`): ``136817688`` (User id in groups when message is sent on behalf - of a channel). - - .. versionadded:: 13.9 - -The following constants are related to specific classes and are also available -as attributes of those classes: - -:class:`telegram.Chat`: - -Attributes: - CHAT_PRIVATE (:obj:`str`): ``'private'`` - CHAT_GROUP (:obj:`str`): ``'group'`` - CHAT_SUPERGROUP (:obj:`str`): ``'supergroup'`` - CHAT_CHANNEL (:obj:`str`): ``'channel'`` - CHAT_SENDER (:obj:`str`): ``'sender'``. Only relevant for - :attr:`telegram.InlineQuery.chat_type`. - - .. versionadded:: 13.5 - -:class:`telegram.ChatAction`: - -Attributes: - CHATACTION_FIND_LOCATION (:obj:`str`): ``'find_location'`` - CHATACTION_RECORD_AUDIO (:obj:`str`): ``'record_audio'`` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :const:`CHATACTION_RECORD_VOICE` instead. - CHATACTION_RECORD_VOICE (:obj:`str`): ``'record_voice'`` - - .. versionadded:: 13.5 - CHATACTION_RECORD_VIDEO (:obj:`str`): ``'record_video'`` - CHATACTION_RECORD_VIDEO_NOTE (:obj:`str`): ``'record_video_note'`` - CHATACTION_TYPING (:obj:`str`): ``'typing'`` - CHATACTION_UPLOAD_AUDIO (:obj:`str`): ``'upload_audio'`` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :const:`CHATACTION_UPLOAD_VOICE` instead. - CHATACTION_UPLOAD_VOICE (:obj:`str`): ``'upload_voice'`` - - .. versionadded:: 13.5 - CHATACTION_UPLOAD_DOCUMENT (:obj:`str`): ``'upload_document'`` - CHATACTION_CHOOSE_STICKER (:obj:`str`): ``'choose_sticker'`` - - .. versionadded:: 13.8 - CHATACTION_UPLOAD_PHOTO (:obj:`str`): ``'upload_photo'`` - CHATACTION_UPLOAD_VIDEO (:obj:`str`): ``'upload_video'`` - CHATACTION_UPLOAD_VIDEO_NOTE (:obj:`str`): ``'upload_video_note'`` - -:class:`telegram.ChatMember`: - -Attributes: - CHATMEMBER_ADMINISTRATOR (:obj:`str`): ``'administrator'`` - CHATMEMBER_CREATOR (:obj:`str`): ``'creator'`` - CHATMEMBER_KICKED (:obj:`str`): ``'kicked'`` - CHATMEMBER_LEFT (:obj:`str`): ``'left'`` - CHATMEMBER_MEMBER (:obj:`str`): ``'member'`` - CHATMEMBER_RESTRICTED (:obj:`str`): ``'restricted'`` - -:class:`telegram.Dice`: - -Attributes: - DICE_DICE (:obj:`str`): ``'🎲'`` - DICE_DARTS (:obj:`str`): ``'🎯'`` - DICE_BASKETBALL (:obj:`str`): ``'🏀'`` - DICE_FOOTBALL (:obj:`str`): ``'⚽'`` - DICE_SLOT_MACHINE (:obj:`str`): ``'🎰'`` - DICE_BOWLING (:obj:`str`): ``'🎳'`` - - .. versionadded:: 13.4 - DICE_ALL_EMOJI (List[:obj:`str`]): List of all supported base emoji. - - .. versionchanged:: 13.4 - Added :attr:`DICE_BOWLING` - -:class:`telegram.MessageEntity`: - -Attributes: - MESSAGEENTITY_MENTION (:obj:`str`): ``'mention'`` - MESSAGEENTITY_HASHTAG (:obj:`str`): ``'hashtag'`` - MESSAGEENTITY_CASHTAG (:obj:`str`): ``'cashtag'`` - MESSAGEENTITY_PHONE_NUMBER (:obj:`str`): ``'phone_number'`` - MESSAGEENTITY_BOT_COMMAND (:obj:`str`): ``'bot_command'`` - MESSAGEENTITY_URL (:obj:`str`): ``'url'`` - MESSAGEENTITY_EMAIL (:obj:`str`): ``'email'`` - MESSAGEENTITY_BOLD (:obj:`str`): ``'bold'`` - MESSAGEENTITY_ITALIC (:obj:`str`): ``'italic'`` - MESSAGEENTITY_CODE (:obj:`str`): ``'code'`` - MESSAGEENTITY_PRE (:obj:`str`): ``'pre'`` - MESSAGEENTITY_TEXT_LINK (:obj:`str`): ``'text_link'`` - MESSAGEENTITY_TEXT_MENTION (:obj:`str`): ``'text_mention'`` - MESSAGEENTITY_UNDERLINE (:obj:`str`): ``'underline'`` - MESSAGEENTITY_STRIKETHROUGH (:obj:`str`): ``'strikethrough'`` - MESSAGEENTITY_SPOILER (:obj:`str`): ``'spoiler'`` - - .. versionadded:: 13.10 - MESSAGEENTITY_ALL_TYPES (List[:obj:`str`]): List of all the types of message entity. - -:class:`telegram.ParseMode`: - -Attributes: - PARSEMODE_MARKDOWN (:obj:`str`): ``'Markdown'`` - PARSEMODE_MARKDOWN_V2 (:obj:`str`): ``'MarkdownV2'`` - PARSEMODE_HTML (:obj:`str`): ``'HTML'`` - -:class:`telegram.Poll`: - -Attributes: - POLL_REGULAR (:obj:`str`): ``'regular'`` - POLL_QUIZ (:obj:`str`): ``'quiz'`` - MAX_POLL_QUESTION_LENGTH (:obj:`int`): 300 - MAX_POLL_OPTION_LENGTH (:obj:`int`): 100 - -:class:`telegram.MaskPosition`: - -Attributes: - STICKER_FOREHEAD (:obj:`str`): ``'forehead'`` - STICKER_EYES (:obj:`str`): ``'eyes'`` - STICKER_MOUTH (:obj:`str`): ``'mouth'`` - STICKER_CHIN (:obj:`str`): ``'chin'`` - -:class:`telegram.Update`: - -Attributes: - UPDATE_MESSAGE (:obj:`str`): ``'message'`` - - .. versionadded:: 13.5 - UPDATE_EDITED_MESSAGE (:obj:`str`): ``'edited_message'`` - - .. versionadded:: 13.5 - UPDATE_CHANNEL_POST (:obj:`str`): ``'channel_post'`` - - .. versionadded:: 13.5 - UPDATE_EDITED_CHANNEL_POST (:obj:`str`): ``'edited_channel_post'`` - - .. versionadded:: 13.5 - UPDATE_INLINE_QUERY (:obj:`str`): ``'inline_query'`` - - .. versionadded:: 13.5 - UPDATE_CHOSEN_INLINE_RESULT (:obj:`str`): ``'chosen_inline_result'`` - - .. versionadded:: 13.5 - UPDATE_CALLBACK_QUERY (:obj:`str`): ``'callback_query'`` - - .. versionadded:: 13.5 - UPDATE_SHIPPING_QUERY (:obj:`str`): ``'shipping_query'`` - - .. versionadded:: 13.5 - UPDATE_PRE_CHECKOUT_QUERY (:obj:`str`): ``'pre_checkout_query'`` - - .. versionadded:: 13.5 - UPDATE_POLL (:obj:`str`): ``'poll'`` - - .. versionadded:: 13.5 - UPDATE_POLL_ANSWER (:obj:`str`): ``'poll_answer'`` - - .. versionadded:: 13.5 - UPDATE_MY_CHAT_MEMBER (:obj:`str`): ``'my_chat_member'`` - - .. versionadded:: 13.5 - UPDATE_CHAT_MEMBER (:obj:`str`): ``'chat_member'`` - - .. versionadded:: 13.5 - UPDATE_CHAT_JOIN_REQUEST (:obj:`str`): ``'chat_join_request'`` - - .. versionadded:: 13.8 - UPDATE_ALL_TYPES (List[:obj:`str`]): List of all update types. - - .. versionadded:: 13.5 - .. versionchanged:: 13.8 - -:class:`telegram.BotCommandScope`: - -Attributes: - BOT_COMMAND_SCOPE_DEFAULT (:obj:`str`): ``'default'`` - - ..versionadded:: 13.7 - BOT_COMMAND_SCOPE_ALL_PRIVATE_CHATS (:obj:`str`): ``'all_private_chats'`` - - ..versionadded:: 13.7 - BOT_COMMAND_SCOPE_ALL_GROUP_CHATS (:obj:`str`): ``'all_group_chats'`` - - ..versionadded:: 13.7 - BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS (:obj:`str`): ``'all_chat_administrators'`` - - ..versionadded:: 13.7 - BOT_COMMAND_SCOPE_CHAT (:obj:`str`): ``'chat'`` - - ..versionadded:: 13.7 - BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS (:obj:`str`): ``'chat_administrators'`` - - ..versionadded:: 13.7 - BOT_COMMAND_SCOPE_CHAT_MEMBER (:obj:`str`): ``'chat_member'`` - - ..versionadded:: 13.7 - -""" -from typing import List - -BOT_API_VERSION: str = '5.7' -MAX_MESSAGE_LENGTH: int = 4096 -MAX_CAPTION_LENGTH: int = 1024 -ANONYMOUS_ADMIN_ID: int = 1087968824 -SERVICE_CHAT_ID: int = 777000 -FAKE_CHANNEL_ID: int = 136817688 - -# constants above this line are tested - -SUPPORTED_WEBHOOK_PORTS: List[int] = [443, 80, 88, 8443] -MAX_FILESIZE_DOWNLOAD: int = int(20e6) # (20MB) -MAX_FILESIZE_UPLOAD: int = int(50e6) # (50MB) -MAX_PHOTOSIZE_UPLOAD: int = int(10e6) # (10MB) -MAX_MESSAGES_PER_SECOND_PER_CHAT: int = 1 -MAX_MESSAGES_PER_SECOND: int = 30 -MAX_MESSAGES_PER_MINUTE_PER_GROUP: int = 20 -MAX_MESSAGE_ENTITIES: int = 100 -MAX_INLINE_QUERY_RESULTS: int = 50 -MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH: int = 200 - -CHAT_SENDER: str = 'sender' -CHAT_PRIVATE: str = 'private' -CHAT_GROUP: str = 'group' -CHAT_SUPERGROUP: str = 'supergroup' -CHAT_CHANNEL: str = 'channel' - -CHATACTION_FIND_LOCATION: str = 'find_location' -CHATACTION_RECORD_AUDIO: str = 'record_audio' -CHATACTION_RECORD_VOICE: str = 'record_voice' -CHATACTION_RECORD_VIDEO: str = 'record_video' -CHATACTION_RECORD_VIDEO_NOTE: str = 'record_video_note' -CHATACTION_TYPING: str = 'typing' -CHATACTION_UPLOAD_AUDIO: str = 'upload_audio' -CHATACTION_UPLOAD_VOICE: str = 'upload_voice' -CHATACTION_UPLOAD_DOCUMENT: str = 'upload_document' -CHATACTION_CHOOSE_STICKER: str = 'choose_sticker' -CHATACTION_UPLOAD_PHOTO: str = 'upload_photo' -CHATACTION_UPLOAD_VIDEO: str = 'upload_video' -CHATACTION_UPLOAD_VIDEO_NOTE: str = 'upload_video_note' - -CHATMEMBER_ADMINISTRATOR: str = 'administrator' -CHATMEMBER_CREATOR: str = 'creator' -CHATMEMBER_KICKED: str = 'kicked' -CHATMEMBER_LEFT: str = 'left' -CHATMEMBER_MEMBER: str = 'member' -CHATMEMBER_RESTRICTED: str = 'restricted' - -DICE_DICE: str = '🎲' -DICE_DARTS: str = '🎯' -DICE_BASKETBALL: str = '🏀' -DICE_FOOTBALL: str = '⚽' -DICE_SLOT_MACHINE: str = '🎰' -DICE_BOWLING: str = '🎳' -DICE_ALL_EMOJI: List[str] = [ - DICE_DICE, - DICE_DARTS, - DICE_BASKETBALL, - DICE_FOOTBALL, - DICE_SLOT_MACHINE, - DICE_BOWLING, -] - -MESSAGEENTITY_MENTION: str = 'mention' -MESSAGEENTITY_HASHTAG: str = 'hashtag' -MESSAGEENTITY_CASHTAG: str = 'cashtag' -MESSAGEENTITY_PHONE_NUMBER: str = 'phone_number' -MESSAGEENTITY_BOT_COMMAND: str = 'bot_command' -MESSAGEENTITY_URL: str = 'url' -MESSAGEENTITY_EMAIL: str = 'email' -MESSAGEENTITY_BOLD: str = 'bold' -MESSAGEENTITY_ITALIC: str = 'italic' -MESSAGEENTITY_CODE: str = 'code' -MESSAGEENTITY_PRE: str = 'pre' -MESSAGEENTITY_TEXT_LINK: str = 'text_link' -MESSAGEENTITY_TEXT_MENTION: str = 'text_mention' -MESSAGEENTITY_UNDERLINE: str = 'underline' -MESSAGEENTITY_STRIKETHROUGH: str = 'strikethrough' -MESSAGEENTITY_SPOILER: str = 'spoiler' -MESSAGEENTITY_ALL_TYPES: List[str] = [ - MESSAGEENTITY_MENTION, - MESSAGEENTITY_HASHTAG, - MESSAGEENTITY_CASHTAG, - MESSAGEENTITY_PHONE_NUMBER, - MESSAGEENTITY_BOT_COMMAND, - MESSAGEENTITY_URL, - MESSAGEENTITY_EMAIL, - MESSAGEENTITY_BOLD, - MESSAGEENTITY_ITALIC, - MESSAGEENTITY_CODE, - MESSAGEENTITY_PRE, - MESSAGEENTITY_TEXT_LINK, - MESSAGEENTITY_TEXT_MENTION, - MESSAGEENTITY_UNDERLINE, - MESSAGEENTITY_STRIKETHROUGH, - MESSAGEENTITY_SPOILER, -] - -PARSEMODE_MARKDOWN: str = 'Markdown' -PARSEMODE_MARKDOWN_V2: str = 'MarkdownV2' -PARSEMODE_HTML: str = 'HTML' - -POLL_REGULAR: str = 'regular' -POLL_QUIZ: str = 'quiz' -MAX_POLL_QUESTION_LENGTH: int = 300 -MAX_POLL_OPTION_LENGTH: int = 100 - -STICKER_FOREHEAD: str = 'forehead' -STICKER_EYES: str = 'eyes' -STICKER_MOUTH: str = 'mouth' -STICKER_CHIN: str = 'chin' - -UPDATE_MESSAGE = 'message' -UPDATE_EDITED_MESSAGE = 'edited_message' -UPDATE_CHANNEL_POST = 'channel_post' -UPDATE_EDITED_CHANNEL_POST = 'edited_channel_post' -UPDATE_INLINE_QUERY = 'inline_query' -UPDATE_CHOSEN_INLINE_RESULT = 'chosen_inline_result' -UPDATE_CALLBACK_QUERY = 'callback_query' -UPDATE_SHIPPING_QUERY = 'shipping_query' -UPDATE_PRE_CHECKOUT_QUERY = 'pre_checkout_query' -UPDATE_POLL = 'poll' -UPDATE_POLL_ANSWER = 'poll_answer' -UPDATE_MY_CHAT_MEMBER = 'my_chat_member' -UPDATE_CHAT_MEMBER = 'chat_member' -UPDATE_CHAT_JOIN_REQUEST = 'chat_join_request' -UPDATE_ALL_TYPES = [ - UPDATE_MESSAGE, - UPDATE_EDITED_MESSAGE, - UPDATE_CHANNEL_POST, - UPDATE_EDITED_CHANNEL_POST, - UPDATE_INLINE_QUERY, - UPDATE_CHOSEN_INLINE_RESULT, - UPDATE_CALLBACK_QUERY, - UPDATE_SHIPPING_QUERY, - UPDATE_PRE_CHECKOUT_QUERY, - UPDATE_POLL, - UPDATE_POLL_ANSWER, - UPDATE_MY_CHAT_MEMBER, - UPDATE_CHAT_MEMBER, - UPDATE_CHAT_JOIN_REQUEST, -] - -BOT_COMMAND_SCOPE_DEFAULT = 'default' -BOT_COMMAND_SCOPE_ALL_PRIVATE_CHATS = 'all_private_chats' -BOT_COMMAND_SCOPE_ALL_GROUP_CHATS = 'all_group_chats' -BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS = 'all_chat_administrators' -BOT_COMMAND_SCOPE_CHAT = 'chat' -BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS = 'chat_administrators' -BOT_COMMAND_SCOPE_CHAT_MEMBER = 'chat_member' diff --git a/telegramer/include/telegram/dice.py b/telegramer/include/telegram/dice.py deleted file mode 100644 index 8836529..0000000 --- a/telegramer/include/telegram/dice.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Dice.""" -from typing import Any, List, ClassVar - -from telegram import TelegramObject, constants - - -class Dice(TelegramObject): - """ - This object represents an animated emoji with a random value for currently supported base - emoji. (The singular form of "dice" is "die". However, PTB mimics the Telegram API, which uses - the term "dice".) - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`value` and :attr:`emoji` are equal. - - Note: - If :attr:`emoji` is "🎯", a value of 6 currently represents a bullseye, while a value of 1 - indicates that the dartboard was missed. However, this behaviour is undocumented and might - be changed by Telegram. - - If :attr:`emoji` is "🏀", a value of 4 or 5 currently score a basket, while a value of 1 to - 3 indicates that the basket was missed. However, this behaviour is undocumented and might - be changed by Telegram. - - If :attr:`emoji` is "⚽", a value of 4 to 5 currently scores a goal, while a value of 1 to - 3 indicates that the goal was missed. However, this behaviour is undocumented and might - be changed by Telegram. - - If :attr:`emoji` is "🎳", a value of 6 knocks all the pins, while a value of 1 means all - the pins were missed. However, this behaviour is undocumented and might be changed by - Telegram. - - If :attr:`emoji` is "🎰", each value corresponds to a unique combination of symbols, which - can be found at our `wiki <https://git.io/JkeC6>`_. However, this behaviour is undocumented - and might be changed by Telegram. - - Args: - value (:obj:`int`): Value of the dice. 1-6 for dice, darts and bowling balls, 1-5 for - basketball and football/soccer ball, 1-64 for slot machine. - emoji (:obj:`str`): Emoji on which the dice throw animation is based. - - Attributes: - value (:obj:`int`): Value of the dice. - emoji (:obj:`str`): Emoji on which the dice throw animation is based. - - """ - - __slots__ = ('emoji', 'value', '_id_attrs') - - def __init__(self, value: int, emoji: str, **_kwargs: Any): - self.value = value - self.emoji = emoji - - self._id_attrs = (self.value, self.emoji) - - DICE: ClassVar[str] = constants.DICE_DICE # skipcq: PTC-W0052 - """:const:`telegram.constants.DICE_DICE`""" - DARTS: ClassVar[str] = constants.DICE_DARTS - """:const:`telegram.constants.DICE_DARTS`""" - BASKETBALL: ClassVar[str] = constants.DICE_BASKETBALL - """:const:`telegram.constants.DICE_BASKETBALL`""" - FOOTBALL: ClassVar[str] = constants.DICE_FOOTBALL - """:const:`telegram.constants.DICE_FOOTBALL`""" - SLOT_MACHINE: ClassVar[str] = constants.DICE_SLOT_MACHINE - """:const:`telegram.constants.DICE_SLOT_MACHINE`""" - BOWLING: ClassVar[str] = constants.DICE_BOWLING - """ - :const:`telegram.constants.DICE_BOWLING` - - .. versionadded:: 13.4 - """ - ALL_EMOJI: ClassVar[List[str]] = constants.DICE_ALL_EMOJI - """:const:`telegram.constants.DICE_ALL_EMOJI`""" diff --git a/telegramer/include/telegram/error.py b/telegramer/include/telegram/error.py deleted file mode 100644 index 3cf41d5..0000000 --- a/telegramer/include/telegram/error.py +++ /dev/null @@ -1,151 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0115 -"""This module contains an object that represents Telegram errors.""" -from typing import Tuple - - -def _lstrip_str(in_s: str, lstr: str) -> str: - """ - Args: - in_s (:obj:`str`): in string - lstr (:obj:`str`): substr to strip from left side - - Returns: - :obj:`str`: The stripped string. - - """ - if in_s.startswith(lstr): - res = in_s[len(lstr) :] - else: - res = in_s - return res - - -class TelegramError(Exception): - """Base class for Telegram errors.""" - - # Apparently the base class Exception already has __dict__ in it, so its not included here - __slots__ = ('message',) - - def __init__(self, message: str): - super().__init__() - - msg = _lstrip_str(message, 'Error: ') - msg = _lstrip_str(msg, '[Error]: ') - msg = _lstrip_str(msg, 'Bad Request: ') - if msg != message: - # api_error - capitalize the msg... - msg = msg.capitalize() - self.message = msg - - def __str__(self) -> str: - return '%s' % self.message - - def __reduce__(self) -> Tuple[type, Tuple[str]]: - return self.__class__, (self.message,) - - -class Unauthorized(TelegramError): - """Raised when the bot has not enough rights to perform the requested action.""" - - __slots__ = () - - -class InvalidToken(TelegramError): - """Raised when the token is invalid.""" - - __slots__ = () - - def __init__(self) -> None: - super().__init__('Invalid token') - - def __reduce__(self) -> Tuple[type, Tuple]: # type: ignore[override] - return self.__class__, () - - -class NetworkError(TelegramError): - """Base class for exceptions due to networking errors.""" - - __slots__ = () - - -class BadRequest(NetworkError): - """Raised when Telegram could not process the request correctly.""" - - __slots__ = () - - -class TimedOut(NetworkError): - """Raised when a request took too long to finish.""" - - __slots__ = () - - def __init__(self) -> None: - super().__init__('Timed out') - - def __reduce__(self) -> Tuple[type, Tuple]: # type: ignore[override] - return self.__class__, () - - -class ChatMigrated(TelegramError): - """ - Raised when the requested group chat migrated to supergroup and has a new chat id. - - Args: - new_chat_id (:obj:`int`): The new chat id of the group. - - """ - - __slots__ = ('new_chat_id',) - - def __init__(self, new_chat_id: int): - super().__init__(f'Group migrated to supergroup. New chat id: {new_chat_id}') - self.new_chat_id = new_chat_id - - def __reduce__(self) -> Tuple[type, Tuple[int]]: # type: ignore[override] - return self.__class__, (self.new_chat_id,) - - -class RetryAfter(TelegramError): - """ - Raised when flood limits where exceeded. - - Args: - retry_after (:obj:`int`): Time in seconds, after which the bot can retry the request. - - """ - - __slots__ = ('retry_after',) - - def __init__(self, retry_after: int): - super().__init__(f'Flood control exceeded. Retry in {float(retry_after)} seconds') - self.retry_after = float(retry_after) - - def __reduce__(self) -> Tuple[type, Tuple[float]]: # type: ignore[override] - return self.__class__, (self.retry_after,) - - -class Conflict(TelegramError): - """Raised when a long poll or webhook conflicts with another one.""" - - __slots__ = () - - def __reduce__(self) -> Tuple[type, Tuple[str]]: - return self.__class__, (self.message,) diff --git a/telegramer/include/telegram/ext/__init__.py b/telegramer/include/telegram/ext/__init__.py deleted file mode 100644 index 1d3a8fe..0000000 --- a/telegramer/include/telegram/ext/__init__.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0413 -"""Extensions over the Telegram Bot API to facilitate bot making""" - -from .extbot import ExtBot -from .basepersistence import BasePersistence -from .picklepersistence import PicklePersistence -from .dictpersistence import DictPersistence -from .handler import Handler -from .callbackcontext import CallbackContext -from .contexttypes import ContextTypes -from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async - -# https://bugs.python.org/issue41451, fixed on 3.7+, doesn't actually remove slots -# try-except is just here in case the __init__ is called twice (like in the tests) -# this block is also the reason for the pylint-ignore at the top of the file -try: - del Dispatcher.__slots__ -except AttributeError as exc: - if str(exc) == '__slots__': - pass - else: - raise exc - -from .jobqueue import JobQueue, Job -from .updater import Updater -from .callbackqueryhandler import CallbackQueryHandler -from .choseninlineresulthandler import ChosenInlineResultHandler -from .inlinequeryhandler import InlineQueryHandler -from .filters import BaseFilter, MessageFilter, UpdateFilter, Filters -from .messagehandler import MessageHandler -from .commandhandler import CommandHandler, PrefixHandler -from .regexhandler import RegexHandler -from .stringcommandhandler import StringCommandHandler -from .stringregexhandler import StringRegexHandler -from .typehandler import TypeHandler -from .conversationhandler import ConversationHandler -from .precheckoutqueryhandler import PreCheckoutQueryHandler -from .shippingqueryhandler import ShippingQueryHandler -from .messagequeue import MessageQueue -from .messagequeue import DelayQueue -from .pollanswerhandler import PollAnswerHandler -from .pollhandler import PollHandler -from .chatmemberhandler import ChatMemberHandler -from .chatjoinrequesthandler import ChatJoinRequestHandler -from .defaults import Defaults -from .callbackdatacache import CallbackDataCache, InvalidCallbackData - -__all__ = ( - 'BaseFilter', - 'BasePersistence', - 'CallbackContext', - 'CallbackDataCache', - 'CallbackQueryHandler', - 'ChatJoinRequestHandler', - 'ChatMemberHandler', - 'ChosenInlineResultHandler', - 'CommandHandler', - 'ContextTypes', - 'ConversationHandler', - 'Defaults', - 'DelayQueue', - 'DictPersistence', - 'Dispatcher', - 'DispatcherHandlerStop', - 'ExtBot', - 'Filters', - 'Handler', - 'InlineQueryHandler', - 'InvalidCallbackData', - 'Job', - 'JobQueue', - 'MessageFilter', - 'MessageHandler', - 'MessageQueue', - 'PicklePersistence', - 'PollAnswerHandler', - 'PollHandler', - 'PreCheckoutQueryHandler', - 'PrefixHandler', - 'RegexHandler', - 'ShippingQueryHandler', - 'StringCommandHandler', - 'StringRegexHandler', - 'TypeHandler', - 'UpdateFilter', - 'Updater', - 'run_async', -) diff --git a/telegramer/include/telegram/ext/basepersistence.py b/telegramer/include/telegram/ext/basepersistence.py deleted file mode 100644 index 20afa1d..0000000 --- a/telegramer/include/telegram/ext/basepersistence.py +++ /dev/null @@ -1,566 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the BasePersistence class.""" -import warnings -from sys import version_info as py_ver -from abc import ABC, abstractmethod -from copy import copy -from typing import Dict, Optional, Tuple, cast, ClassVar, Generic, DefaultDict - -from telegram.utils.deprecate import set_new_attribute_deprecated - -from telegram import Bot -import telegram.ext.extbot - -from telegram.ext.utils.types import UD, CD, BD, ConversationDict, CDCData - - -class BasePersistence(Generic[UD, CD, BD], ABC): - """Interface class for adding persistence to your bot. - Subclass this object for different implementations of a persistent bot. - - All relevant methods must be overwritten. This includes: - - * :meth:`get_bot_data` - * :meth:`update_bot_data` - * :meth:`refresh_bot_data` - * :meth:`get_chat_data` - * :meth:`update_chat_data` - * :meth:`refresh_chat_data` - * :meth:`get_user_data` - * :meth:`update_user_data` - * :meth:`refresh_user_data` - * :meth:`get_callback_data` - * :meth:`update_callback_data` - * :meth:`get_conversations` - * :meth:`update_conversation` - * :meth:`flush` - - If you don't actually need one of those methods, a simple ``pass`` is enough. For example, if - ``store_bot_data=False``, you don't need :meth:`get_bot_data`, :meth:`update_bot_data` or - :meth:`refresh_bot_data`. - - Warning: - Persistence will try to replace :class:`telegram.Bot` instances by :attr:`REPLACED_BOT` and - insert the bot set with :meth:`set_bot` upon loading of the data. This is to ensure that - changes to the bot apply to the saved objects, too. If you change the bots token, this may - lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see - :meth:`replace_bot` and :meth:`insert_bot`. - - Note: - :meth:`replace_bot` and :meth:`insert_bot` are used *independently* of the implementation - of the :meth:`update/get_*` methods, i.e. you don't need to worry about it while - implementing a custom persistence subclass. - - Args: - store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this - persistence class. Default is :obj:`True`. - store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this - persistence class. Default is :obj:`True` . - store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this - persistence class. Default is :obj:`True`. - store_callback_data (:obj:`bool`, optional): Whether callback_data should be saved by this - persistence class. Default is :obj:`False`. - - .. versionadded:: 13.6 - - Attributes: - store_user_data (:obj:`bool`): Optional, Whether user_data should be saved by this - persistence class. - store_chat_data (:obj:`bool`): Optional. Whether chat_data should be saved by this - persistence class. - store_bot_data (:obj:`bool`): Optional. Whether bot_data should be saved by this - persistence class. - store_callback_data (:obj:`bool`): Optional. Whether callback_data should be saved by this - persistence class. - - .. versionadded:: 13.6 - """ - - # Apparently Py 3.7 and below have '__dict__' in ABC - if py_ver < (3, 7): - __slots__ = ( - 'store_user_data', - 'store_chat_data', - 'store_bot_data', - 'store_callback_data', - 'bot', - ) - else: - __slots__ = ( - 'store_user_data', # type: ignore[assignment] - 'store_chat_data', - 'store_bot_data', - 'store_callback_data', - 'bot', - '__dict__', - ) - - def __new__( - cls, *args: object, **kwargs: object # pylint: disable=W0613 - ) -> 'BasePersistence': - """This overrides the get_* and update_* methods to use insert/replace_bot. - That has the side effect that we always pass deepcopied data to those methods, so in - Pickle/DictPersistence we don't have to worry about copying the data again. - - Note: This doesn't hold for second tuple-entry of callback_data. That's a Dict[str, str], - so no bots to replace anyway. - """ - instance = super().__new__(cls) - get_user_data = instance.get_user_data - get_chat_data = instance.get_chat_data - get_bot_data = instance.get_bot_data - get_callback_data = instance.get_callback_data - update_user_data = instance.update_user_data - update_chat_data = instance.update_chat_data - update_bot_data = instance.update_bot_data - update_callback_data = instance.update_callback_data - - def get_user_data_insert_bot() -> DefaultDict[int, UD]: - return instance.insert_bot(get_user_data()) - - def get_chat_data_insert_bot() -> DefaultDict[int, CD]: - return instance.insert_bot(get_chat_data()) - - def get_bot_data_insert_bot() -> BD: - return instance.insert_bot(get_bot_data()) - - def get_callback_data_insert_bot() -> Optional[CDCData]: - cdc_data = get_callback_data() - if cdc_data is None: - return None - return instance.insert_bot(cdc_data[0]), cdc_data[1] - - def update_user_data_replace_bot(user_id: int, data: UD) -> None: - return update_user_data(user_id, instance.replace_bot(data)) - - def update_chat_data_replace_bot(chat_id: int, data: CD) -> None: - return update_chat_data(chat_id, instance.replace_bot(data)) - - def update_bot_data_replace_bot(data: BD) -> None: - return update_bot_data(instance.replace_bot(data)) - - def update_callback_data_replace_bot(data: CDCData) -> None: - obj_data, queue = data - return update_callback_data((instance.replace_bot(obj_data), queue)) - - # We want to ignore TGDeprecation warnings so we use obj.__setattr__. Adds to __dict__ - object.__setattr__(instance, 'get_user_data', get_user_data_insert_bot) - object.__setattr__(instance, 'get_chat_data', get_chat_data_insert_bot) - object.__setattr__(instance, 'get_bot_data', get_bot_data_insert_bot) - object.__setattr__(instance, 'get_callback_data', get_callback_data_insert_bot) - object.__setattr__(instance, 'update_user_data', update_user_data_replace_bot) - object.__setattr__(instance, 'update_chat_data', update_chat_data_replace_bot) - object.__setattr__(instance, 'update_bot_data', update_bot_data_replace_bot) - object.__setattr__(instance, 'update_callback_data', update_callback_data_replace_bot) - return instance - - def __init__( - self, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - store_callback_data: bool = False, - ): - self.store_user_data = store_user_data - self.store_chat_data = store_chat_data - self.store_bot_data = store_bot_data - self.store_callback_data = store_callback_data - self.bot: Bot = None # type: ignore[assignment] - - def __setattr__(self, key: str, value: object) -> None: - # Allow user defined subclasses to have custom attributes. - if issubclass(self.__class__, BasePersistence) and self.__class__.__name__ not in { - 'DictPersistence', - 'PicklePersistence', - }: - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - - def set_bot(self, bot: Bot) -> None: - """Set the Bot to be used by this persistence instance. - - Args: - bot (:class:`telegram.Bot`): The bot. - """ - if self.store_callback_data and not isinstance(bot, telegram.ext.extbot.ExtBot): - raise TypeError('store_callback_data can only be used with telegram.ext.ExtBot.') - - self.bot = bot - - @classmethod - def replace_bot(cls, obj: object) -> object: - """ - Replaces all instances of :class:`telegram.Bot` that occur within the passed object with - :attr:`REPLACED_BOT`. Currently, this handles objects of type ``list``, ``tuple``, ``set``, - ``frozenset``, ``dict``, ``defaultdict`` and objects that have a ``__dict__`` or - ``__slots__`` attribute, excluding classes and objects that can't be copied with - ``copy.copy``. If the parsing of an object fails, the object will be returned unchanged and - the error will be logged. - - Args: - obj (:obj:`object`): The object - - Returns: - :obj:`obj`: Copy of the object with Bot instances replaced. - """ - return cls._replace_bot(obj, {}) - - @classmethod - def _replace_bot(cls, obj: object, memo: Dict[int, object]) -> object: # pylint: disable=R0911 - obj_id = id(obj) - if obj_id in memo: - return memo[obj_id] - - if isinstance(obj, Bot): - memo[obj_id] = cls.REPLACED_BOT - return cls.REPLACED_BOT - if isinstance(obj, (list, set)): - # We copy the iterable here for thread safety, i.e. make sure the object we iterate - # over doesn't change its length during the iteration - temp_iterable = obj.copy() - new_iterable = obj.__class__(cls._replace_bot(item, memo) for item in temp_iterable) - memo[obj_id] = new_iterable - return new_iterable - if isinstance(obj, (tuple, frozenset)): - # tuples and frozensets are immutable so we don't need to worry about thread safety - new_immutable = obj.__class__(cls._replace_bot(item, memo) for item in obj) - memo[obj_id] = new_immutable - return new_immutable - if isinstance(obj, type): - # classes usually do have a __dict__, but it's not writable - warnings.warn( - 'BasePersistence.replace_bot does not handle classes. See ' - 'the docs of BasePersistence.replace_bot for more information.', - RuntimeWarning, - ) - return obj - - try: - new_obj = copy(obj) - memo[obj_id] = new_obj - except Exception: - warnings.warn( - 'BasePersistence.replace_bot does not handle objects that can not be copied. See ' - 'the docs of BasePersistence.replace_bot for more information.', - RuntimeWarning, - ) - memo[obj_id] = obj - return obj - - if isinstance(obj, dict): - # We handle dicts via copy(obj) so we don't have to make a - # difference between dict and defaultdict - new_obj = cast(dict, new_obj) - # We can't iterate over obj.items() due to thread safety, i.e. the dicts length may - # change during the iteration - temp_dict = new_obj.copy() - new_obj.clear() - for k, val in temp_dict.items(): - new_obj[cls._replace_bot(k, memo)] = cls._replace_bot(val, memo) - memo[obj_id] = new_obj - return new_obj - try: - if hasattr(obj, '__slots__'): - for attr_name in new_obj.__slots__: - setattr( - new_obj, - attr_name, - cls._replace_bot( - cls._replace_bot(getattr(new_obj, attr_name), memo), memo - ), - ) - if '__dict__' in obj.__slots__: - # In this case, we have already covered the case that obj has __dict__ - # Note that obj may have a __dict__ even if it's not in __slots__! - memo[obj_id] = new_obj - return new_obj - if hasattr(obj, '__dict__'): - for attr_name, attr in new_obj.__dict__.items(): - setattr(new_obj, attr_name, cls._replace_bot(attr, memo)) - memo[obj_id] = new_obj - return new_obj - except Exception as exception: - warnings.warn( - f'Parsing of an object failed with the following exception: {exception}. ' - f'See the docs of BasePersistence.replace_bot for more information.', - RuntimeWarning, - ) - - memo[obj_id] = obj - return obj - - def insert_bot(self, obj: object) -> object: - """ - Replaces all instances of :attr:`REPLACED_BOT` that occur within the passed object with - :attr:`bot`. Currently, this handles objects of type ``list``, ``tuple``, ``set``, - ``frozenset``, ``dict``, ``defaultdict`` and objects that have a ``__dict__`` or - ``__slots__`` attribute, excluding classes and objects that can't be copied with - ``copy.copy``. If the parsing of an object fails, the object will be returned unchanged and - the error will be logged. - - Args: - obj (:obj:`object`): The object - - Returns: - :obj:`obj`: Copy of the object with Bot instances inserted. - """ - return self._insert_bot(obj, {}) - - def _insert_bot(self, obj: object, memo: Dict[int, object]) -> object: # pylint: disable=R0911 - obj_id = id(obj) - if obj_id in memo: - return memo[obj_id] - - if isinstance(obj, Bot): - memo[obj_id] = self.bot - return self.bot - if isinstance(obj, str) and obj == self.REPLACED_BOT: - memo[obj_id] = self.bot - return self.bot - if isinstance(obj, (list, set)): - # We copy the iterable here for thread safety, i.e. make sure the object we iterate - # over doesn't change its length during the iteration - temp_iterable = obj.copy() - new_iterable = obj.__class__(self._insert_bot(item, memo) for item in temp_iterable) - memo[obj_id] = new_iterable - return new_iterable - if isinstance(obj, (tuple, frozenset)): - # tuples and frozensets are immutable so we don't need to worry about thread safety - new_immutable = obj.__class__(self._insert_bot(item, memo) for item in obj) - memo[obj_id] = new_immutable - return new_immutable - if isinstance(obj, type): - # classes usually do have a __dict__, but it's not writable - warnings.warn( - 'BasePersistence.insert_bot does not handle classes. See ' - 'the docs of BasePersistence.insert_bot for more information.', - RuntimeWarning, - ) - return obj - - try: - new_obj = copy(obj) - except Exception: - warnings.warn( - 'BasePersistence.insert_bot does not handle objects that can not be copied. See ' - 'the docs of BasePersistence.insert_bot for more information.', - RuntimeWarning, - ) - memo[obj_id] = obj - return obj - - if isinstance(obj, dict): - # We handle dicts via copy(obj) so we don't have to make a - # difference between dict and defaultdict - new_obj = cast(dict, new_obj) - # We can't iterate over obj.items() due to thread safety, i.e. the dicts length may - # change during the iteration - temp_dict = new_obj.copy() - new_obj.clear() - for k, val in temp_dict.items(): - new_obj[self._insert_bot(k, memo)] = self._insert_bot(val, memo) - memo[obj_id] = new_obj - return new_obj - try: - if hasattr(obj, '__slots__'): - for attr_name in obj.__slots__: - setattr( - new_obj, - attr_name, - self._insert_bot( - self._insert_bot(getattr(new_obj, attr_name), memo), memo - ), - ) - if '__dict__' in obj.__slots__: - # In this case, we have already covered the case that obj has __dict__ - # Note that obj may have a __dict__ even if it's not in __slots__! - memo[obj_id] = new_obj - return new_obj - if hasattr(obj, '__dict__'): - for attr_name, attr in new_obj.__dict__.items(): - setattr(new_obj, attr_name, self._insert_bot(attr, memo)) - memo[obj_id] = new_obj - return new_obj - except Exception as exception: - warnings.warn( - f'Parsing of an object failed with the following exception: {exception}. ' - f'See the docs of BasePersistence.insert_bot for more information.', - RuntimeWarning, - ) - - memo[obj_id] = obj - return obj - - @abstractmethod - def get_user_data(self) -> DefaultDict[int, UD]: - """Will be called by :class:`telegram.ext.Dispatcher` upon creation with a - persistence object. It should return the ``user_data`` if stored, or an empty - :obj:`defaultdict(telegram.ext.utils.types.UD)` with integer keys. - - Returns: - DefaultDict[:obj:`int`, :class:`telegram.ext.utils.types.UD`]: The restored user data. - """ - - @abstractmethod - def get_chat_data(self) -> DefaultDict[int, CD]: - """Will be called by :class:`telegram.ext.Dispatcher` upon creation with a - persistence object. It should return the ``chat_data`` if stored, or an empty - :obj:`defaultdict(telegram.ext.utils.types.CD)` with integer keys. - - Returns: - DefaultDict[:obj:`int`, :class:`telegram.ext.utils.types.CD`]: The restored chat data. - """ - - @abstractmethod - def get_bot_data(self) -> BD: - """Will be called by :class:`telegram.ext.Dispatcher` upon creation with a - persistence object. It should return the ``bot_data`` if stored, or an empty - :class:`telegram.ext.utils.types.BD`. - - Returns: - :class:`telegram.ext.utils.types.BD`: The restored bot data. - """ - - def get_callback_data(self) -> Optional[CDCData]: - """Will be called by :class:`telegram.ext.Dispatcher` upon creation with a - persistence object. If callback data was stored, it should be returned. - - .. versionadded:: 13.6 - - Returns: - Optional[:class:`telegram.ext.utils.types.CDCData`]: The restored meta data or - :obj:`None`, if no data was stored. - """ - raise NotImplementedError - - @abstractmethod - def get_conversations(self, name: str) -> ConversationDict: - """Will be called by :class:`telegram.ext.Dispatcher` when a - :class:`telegram.ext.ConversationHandler` is added if - :attr:`telegram.ext.ConversationHandler.persistent` is :obj:`True`. - It should return the conversations for the handler with `name` or an empty :obj:`dict` - - Args: - name (:obj:`str`): The handlers name. - - Returns: - :obj:`dict`: The restored conversations for the handler. - """ - - @abstractmethod - def update_conversation( - self, name: str, key: Tuple[int, ...], new_state: Optional[object] - ) -> None: - """Will be called when a :class:`telegram.ext.ConversationHandler` changes states. - This allows the storage of the new state in the persistence. - - Args: - name (:obj:`str`): The handler's name. - key (:obj:`tuple`): The key the state is changed for. - new_state (:obj:`tuple` | :obj:`any`): The new state for the given key. - """ - - @abstractmethod - def update_user_data(self, user_id: int, data: UD) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` after a handler has - handled an update. - - Args: - user_id (:obj:`int`): The user the data might have been changed for. - data (:class:`telegram.ext.utils.types.UD`): The - :attr:`telegram.ext.Dispatcher.user_data` ``[user_id]``. - """ - - @abstractmethod - def update_chat_data(self, chat_id: int, data: CD) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` after a handler has - handled an update. - - Args: - chat_id (:obj:`int`): The chat the data might have been changed for. - data (:class:`telegram.ext.utils.types.CD`): The - :attr:`telegram.ext.Dispatcher.chat_data` ``[chat_id]``. - """ - - @abstractmethod - def update_bot_data(self, data: BD) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` after a handler has - handled an update. - - Args: - data (:class:`telegram.ext.utils.types.BD`): The - :attr:`telegram.ext.Dispatcher.bot_data`. - """ - - def refresh_user_data(self, user_id: int, user_data: UD) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` before passing the - :attr:`user_data` to a callback. Can be used to update data stored in :attr:`user_data` - from an external source. - - .. versionadded:: 13.6 - - Args: - user_id (:obj:`int`): The user ID this :attr:`user_data` is associated with. - user_data (:class:`telegram.ext.utils.types.UD`): The ``user_data`` of a single user. - """ - - def refresh_chat_data(self, chat_id: int, chat_data: CD) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` before passing the - :attr:`chat_data` to a callback. Can be used to update data stored in :attr:`chat_data` - from an external source. - - .. versionadded:: 13.6 - - Args: - chat_id (:obj:`int`): The chat ID this :attr:`chat_data` is associated with. - chat_data (:class:`telegram.ext.utils.types.CD`): The ``chat_data`` of a single chat. - """ - - def refresh_bot_data(self, bot_data: BD) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` before passing the - :attr:`bot_data` to a callback. Can be used to update data stored in :attr:`bot_data` - from an external source. - - .. versionadded:: 13.6 - - Args: - bot_data (:class:`telegram.ext.utils.types.BD`): The ``bot_data``. - """ - - def update_callback_data(self, data: CDCData) -> None: - """Will be called by the :class:`telegram.ext.Dispatcher` after a handler has - handled an update. - - .. versionadded:: 13.6 - - Args: - data (:class:`telegram.ext.utils.types.CDCData`): The relevant data to restore - :class:`telegram.ext.CallbackDataCache`. - """ - raise NotImplementedError - - def flush(self) -> None: - """Will be called by :class:`telegram.ext.Updater` upon receiving a stop signal. Gives the - persistence a chance to finish up saving or close a database connection gracefully. - """ - - REPLACED_BOT: ClassVar[str] = 'bot_instance_replaced_by_ptb_persistence' - """:obj:`str`: Placeholder for :class:`telegram.Bot` instances replaced in saved data.""" diff --git a/telegramer/include/telegram/ext/callbackcontext.py b/telegramer/include/telegram/ext/callbackcontext.py deleted file mode 100644 index 607ca92..0000000 --- a/telegramer/include/telegram/ext/callbackcontext.py +++ /dev/null @@ -1,361 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=R0201 -"""This module contains the CallbackContext class.""" -from queue import Queue -from typing import ( - TYPE_CHECKING, - Dict, - List, - Match, - NoReturn, - Optional, - Tuple, - Union, - Generic, - Type, - TypeVar, -) - -from telegram import Update, CallbackQuery -from telegram.ext import ExtBot -from telegram.ext.utils.types import UD, CD, BD - -if TYPE_CHECKING: - from telegram import Bot - from telegram.ext import Dispatcher, Job, JobQueue - -CC = TypeVar('CC', bound='CallbackContext') - - -class CallbackContext(Generic[UD, CD, BD]): - """ - This is a context object passed to the callback called by :class:`telegram.ext.Handler` - or by the :class:`telegram.ext.Dispatcher` in an error handler added by - :attr:`telegram.ext.Dispatcher.add_error_handler` or to the callback of a - :class:`telegram.ext.Job`. - - Note: - :class:`telegram.ext.Dispatcher` will create a single context for an entire update. This - means that if you got 2 handlers in different groups and they both get called, they will - get passed the same `CallbackContext` object (of course with proper attributes like - `.matches` differing). This allows you to add custom attributes in a lower handler group - callback, and then subsequently access those attributes in a higher handler group callback. - Note that the attributes on `CallbackContext` might change in the future, so make sure to - use a fairly unique name for the attributes. - - Warning: - Do not combine custom attributes and ``@run_async``/ - :meth:`telegram.ext.Disptacher.run_async`. Due to how ``run_async`` works, it will - almost certainly execute the callbacks for an update out of order, and the attributes - that you think you added will not be present. - - Args: - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this context. - - Attributes: - matches (List[:obj:`re match object`]): Optional. If the associated update originated from - a regex-supported handler or had a :class:`Filters.regex`, this will contain a list of - match objects for every pattern where ``re.search(pattern, string)`` returned a match. - Note that filters short circuit, so combined regex filters will not always - be evaluated. - args (List[:obj:`str`]): Optional. Arguments passed to a command if the associated update - is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler` - or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the - text after the command, using any whitespace string as a delimiter. - error (:obj:`Exception`): Optional. The error that was raised. Only present when passed - to a error handler registered with :attr:`telegram.ext.Dispatcher.add_error_handler`. - async_args (List[:obj:`object`]): Optional. Positional arguments of the function that - raised the error. Only present when the raising function was run asynchronously using - :meth:`telegram.ext.Dispatcher.run_async`. - async_kwargs (Dict[:obj:`str`, :obj:`object`]): Optional. Keyword arguments of the function - that raised the error. Only present when the raising function was run asynchronously - using :meth:`telegram.ext.Dispatcher.run_async`. - job (:class:`telegram.ext.Job`): Optional. The job which originated this callback. - Only present when passed to the callback of :class:`telegram.ext.Job`. - - """ - - __slots__ = ( - '_dispatcher', - '_chat_id_and_data', - '_user_id_and_data', - 'args', - 'matches', - 'error', - 'job', - 'async_args', - 'async_kwargs', - '__dict__', - ) - - def __init__(self, dispatcher: 'Dispatcher'): - """ - Args: - dispatcher (:class:`telegram.ext.Dispatcher`): - """ - if not dispatcher.use_context: - raise ValueError( - 'CallbackContext should not be used with a non context aware ' 'dispatcher!' - ) - self._dispatcher = dispatcher - self._chat_id_and_data: Optional[Tuple[int, CD]] = None - self._user_id_and_data: Optional[Tuple[int, UD]] = None - self.args: Optional[List[str]] = None - self.matches: Optional[List[Match]] = None - self.error: Optional[Exception] = None - self.job: Optional['Job'] = None - self.async_args: Optional[Union[List, Tuple]] = None - self.async_kwargs: Optional[Dict[str, object]] = None - - @property - def dispatcher(self) -> 'Dispatcher': - """:class:`telegram.ext.Dispatcher`: The dispatcher associated with this context.""" - return self._dispatcher - - @property - def bot_data(self) -> BD: - """:obj:`dict`: Optional. A dict that can be used to keep any data in. For each - update it will be the same ``dict``. - """ - return self.dispatcher.bot_data - - @bot_data.setter - def bot_data(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to bot_data, see https://git.io/Jt6ic" - ) - - @property - def chat_data(self) -> Optional[CD]: - """:obj:`dict`: Optional. A dict that can be used to keep any data in. For each - update from the same chat id it will be the same ``dict``. - - Warning: - When a group chat migrates to a supergroup, its chat id will change and the - ``chat_data`` needs to be transferred. For details see our `wiki page - <https://github.com/python-telegram-bot/python-telegram-bot/wiki/ - Storing-bot,-user-and-chat-related-data#chat-migration>`_. - """ - if self._chat_id_and_data: - return self._chat_id_and_data[1] - return None - - @chat_data.setter - def chat_data(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to chat_data, see https://git.io/Jt6ic" - ) - - @property - def user_data(self) -> Optional[UD]: - """:obj:`dict`: Optional. A dict that can be used to keep any data in. For each - update from the same user it will be the same ``dict``. - """ - if self._user_id_and_data: - return self._user_id_and_data[1] - return None - - @user_data.setter - def user_data(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to user_data, see https://git.io/Jt6ic" - ) - - def refresh_data(self) -> None: - """If :attr:`dispatcher` uses persistence, calls - :meth:`telegram.ext.BasePersistence.refresh_bot_data` on :attr:`bot_data`, - :meth:`telegram.ext.BasePersistence.refresh_chat_data` on :attr:`chat_data` and - :meth:`telegram.ext.BasePersistence.refresh_user_data` on :attr:`user_data`, if - appropriate. - - .. versionadded:: 13.6 - """ - if self.dispatcher.persistence: - if self.dispatcher.persistence.store_bot_data: - self.dispatcher.persistence.refresh_bot_data(self.bot_data) - if self.dispatcher.persistence.store_chat_data and self._chat_id_and_data is not None: - self.dispatcher.persistence.refresh_chat_data(*self._chat_id_and_data) - if self.dispatcher.persistence.store_user_data and self._user_id_and_data is not None: - self.dispatcher.persistence.refresh_user_data(*self._user_id_and_data) - - def drop_callback_data(self, callback_query: CallbackQuery) -> None: - """ - Deletes the cached data for the specified callback query. - - .. versionadded:: 13.6 - - Note: - Will *not* raise exceptions in case the data is not found in the cache. - *Will* raise :class:`KeyError` in case the callback query can not be found in the - cache. - - Args: - callback_query (:class:`telegram.CallbackQuery`): The callback query. - - Raises: - KeyError | RuntimeError: :class:`KeyError`, if the callback query can not be found in - the cache and :class:`RuntimeError`, if the bot doesn't allow for arbitrary - callback data. - """ - if isinstance(self.bot, ExtBot): - if not self.bot.arbitrary_callback_data: - raise RuntimeError( - 'This telegram.ext.ExtBot instance does not use arbitrary callback data.' - ) - self.bot.callback_data_cache.drop_data(callback_query) - else: - raise RuntimeError('telegram.Bot does not allow for arbitrary callback data.') - - @classmethod - def from_error( - cls: Type[CC], - update: object, - error: Exception, - dispatcher: 'Dispatcher', - async_args: Union[List, Tuple] = None, - async_kwargs: Dict[str, object] = None, - ) -> CC: - """ - Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the error - handlers. - - .. seealso:: :meth:`telegram.ext.Dispatcher.add_error_handler` - - Args: - update (:obj:`object` | :class:`telegram.Update`): The update associated with the - error. May be :obj:`None`, e.g. for errors in job callbacks. - error (:obj:`Exception`): The error. - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this - context. - async_args (List[:obj:`object`]): Optional. Positional arguments of the function that - raised the error. Pass only when the raising function was run asynchronously using - :meth:`telegram.ext.Dispatcher.run_async`. - async_kwargs (Dict[:obj:`str`, :obj:`object`]): Optional. Keyword arguments of the - function that raised the error. Pass only when the raising function was run - asynchronously using :meth:`telegram.ext.Dispatcher.run_async`. - - Returns: - :class:`telegram.ext.CallbackContext` - """ - self = cls.from_update(update, dispatcher) - self.error = error - self.async_args = async_args - self.async_kwargs = async_kwargs - return self - - @classmethod - def from_update(cls: Type[CC], update: object, dispatcher: 'Dispatcher') -> CC: - """ - Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the - handlers. - - .. seealso:: :meth:`telegram.ext.Dispatcher.add_handler` - - Args: - update (:obj:`object` | :class:`telegram.Update`): The update. - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this - context. - - Returns: - :class:`telegram.ext.CallbackContext` - """ - self = cls(dispatcher) - - if update is not None and isinstance(update, Update): - chat = update.effective_chat - user = update.effective_user - - if chat: - self._chat_id_and_data = ( - chat.id, - dispatcher.chat_data[chat.id], # pylint: disable=W0212 - ) - if user: - self._user_id_and_data = ( - user.id, - dispatcher.user_data[user.id], # pylint: disable=W0212 - ) - return self - - @classmethod - def from_job(cls: Type[CC], job: 'Job', dispatcher: 'Dispatcher') -> CC: - """ - Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to a - job callback. - - .. seealso:: :meth:`telegram.ext.JobQueue` - - Args: - job (:class:`telegram.ext.Job`): The job. - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this - context. - - Returns: - :class:`telegram.ext.CallbackContext` - """ - self = cls(dispatcher) - self.job = job - return self - - def update(self, data: Dict[str, object]) -> None: - """Updates ``self.__slots__`` with the passed data. - - Args: - data (Dict[:obj:`str`, :obj:`object`]): The data. - """ - for key, value in data.items(): - setattr(self, key, value) - - @property - def bot(self) -> 'Bot': - """:class:`telegram.Bot`: The bot associated with this context.""" - return self._dispatcher.bot - - @property - def job_queue(self) -> Optional['JobQueue']: - """ - :class:`telegram.ext.JobQueue`: The ``JobQueue`` used by the - :class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater` - associated with this context. - - """ - return self._dispatcher.job_queue - - @property - def update_queue(self) -> Queue: - """ - :class:`queue.Queue`: The ``Queue`` instance used by the - :class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater` - associated with this context. - - """ - return self._dispatcher.update_queue - - @property - def match(self) -> Optional[Match[str]]: - """ - `Regex match type`: The first match from :attr:`matches`. - Useful if you are only filtering using a single regex filter. - Returns `None` if :attr:`matches` is empty. - """ - try: - return self.matches[0] # type: ignore[index] # pylint: disable=unsubscriptable-object - except (IndexError, TypeError): - return None diff --git a/telegramer/include/telegram/ext/callbackdatacache.py b/telegramer/include/telegram/ext/callbackdatacache.py deleted file mode 100644 index df64005..0000000 --- a/telegramer/include/telegram/ext/callbackdatacache.py +++ /dev/null @@ -1,408 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the CallbackDataCache class.""" -import logging -import time -from datetime import datetime -from threading import Lock -from typing import Dict, Tuple, Union, Optional, MutableMapping, TYPE_CHECKING, cast -from uuid import uuid4 - -from cachetools import LRUCache # pylint: disable=E0401 - -from telegram import ( - InlineKeyboardMarkup, - InlineKeyboardButton, - TelegramError, - CallbackQuery, - Message, - User, -) -from telegram.utils.helpers import to_float_timestamp -from telegram.ext.utils.types import CDCData - -if TYPE_CHECKING: - from telegram.ext import ExtBot - - -class InvalidCallbackData(TelegramError): - """ - Raised when the received callback data has been tempered with or deleted from cache. - - .. versionadded:: 13.6 - - Args: - callback_data (:obj:`int`, optional): The button data of which the callback data could not - be found. - - Attributes: - callback_data (:obj:`int`): Optional. The button data of which the callback data could not - be found. - """ - - __slots__ = ('callback_data',) - - def __init__(self, callback_data: str = None) -> None: - super().__init__( - 'The object belonging to this callback_data was deleted or the callback_data was ' - 'manipulated.' - ) - self.callback_data = callback_data - - def __reduce__(self) -> Tuple[type, Tuple[Optional[str]]]: # type: ignore[override] - return self.__class__, (self.callback_data,) - - -class _KeyboardData: - __slots__ = ('keyboard_uuid', 'button_data', 'access_time') - - def __init__( - self, keyboard_uuid: str, access_time: float = None, button_data: Dict[str, object] = None - ): - self.keyboard_uuid = keyboard_uuid - self.button_data = button_data or {} - self.access_time = access_time or time.time() - - def update_access_time(self) -> None: - """Updates the access time with the current time.""" - self.access_time = time.time() - - def to_tuple(self) -> Tuple[str, float, Dict[str, object]]: - """Gives a tuple representation consisting of the keyboard uuid, the access time and the - button data. - """ - return self.keyboard_uuid, self.access_time, self.button_data - - -class CallbackDataCache: - """A custom cache for storing the callback data of a :class:`telegram.ext.ExtBot`. Internally, - it keeps two mappings with fixed maximum size: - - * One for mapping the data received in callback queries to the cached objects - * One for mapping the IDs of received callback queries to the cached objects - - The second mapping allows to manually drop data that has been cached for keyboards of messages - sent via inline mode. - If necessary, will drop the least recently used items. - - .. versionadded:: 13.6 - - Args: - bot (:class:`telegram.ext.ExtBot`): The bot this cache is for. - maxsize (:obj:`int`, optional): Maximum number of items in each of the internal mappings. - Defaults to 1024. - persistent_data (:obj:`telegram.ext.utils.types.CDCData`, optional): Data to initialize - the cache with, as returned by :meth:`telegram.ext.BasePersistence.get_callback_data`. - - Attributes: - bot (:class:`telegram.ext.ExtBot`): The bot this cache is for. - maxsize (:obj:`int`): maximum size of the cache. - - """ - - __slots__ = ('bot', 'maxsize', '_keyboard_data', '_callback_queries', '__lock', 'logger') - - def __init__( - self, - bot: 'ExtBot', - maxsize: int = 1024, - persistent_data: CDCData = None, - ): - self.logger = logging.getLogger(__name__) - - self.bot = bot - self.maxsize = maxsize - self._keyboard_data: MutableMapping[str, _KeyboardData] = LRUCache(maxsize=maxsize) - self._callback_queries: MutableMapping[str, str] = LRUCache(maxsize=maxsize) - self.__lock = Lock() - - if persistent_data: - keyboard_data, callback_queries = persistent_data - for key, value in callback_queries.items(): - self._callback_queries[key] = value - for uuid, access_time, data in keyboard_data: - self._keyboard_data[uuid] = _KeyboardData( - keyboard_uuid=uuid, access_time=access_time, button_data=data - ) - - @property - def persistence_data(self) -> CDCData: - """:obj:`telegram.ext.utils.types.CDCData`: The data that needs to be persisted to allow - caching callback data across bot reboots. - """ - # While building a list/dict from the LRUCaches has linear runtime (in the number of - # entries), the runtime is bounded by maxsize and it has the big upside of not throwing a - # highly customized data structure at users trying to implement a custom persistence class - with self.__lock: - return [data.to_tuple() for data in self._keyboard_data.values()], dict( - self._callback_queries.items() - ) - - def process_keyboard(self, reply_markup: InlineKeyboardMarkup) -> InlineKeyboardMarkup: - """Registers the reply markup to the cache. If any of the buttons have - :attr:`callback_data`, stores that data and builds a new keyboard with the correspondingly - replaced buttons. Otherwise does nothing and returns the original reply markup. - - Args: - reply_markup (:class:`telegram.InlineKeyboardMarkup`): The keyboard. - - Returns: - :class:`telegram.InlineKeyboardMarkup`: The keyboard to be passed to Telegram. - - """ - with self.__lock: - return self.__process_keyboard(reply_markup) - - def __process_keyboard(self, reply_markup: InlineKeyboardMarkup) -> InlineKeyboardMarkup: - keyboard_uuid = uuid4().hex - keyboard_data = _KeyboardData(keyboard_uuid) - - # Built a new nested list of buttons by replacing the callback data if needed - buttons = [ - [ - # We create a new button instead of replacing callback_data in case the - # same object is used elsewhere - InlineKeyboardButton( - btn.text, - callback_data=self.__put_button(btn.callback_data, keyboard_data), - ) - if btn.callback_data - else btn - for btn in column - ] - for column in reply_markup.inline_keyboard - ] - - if not keyboard_data.button_data: - # If we arrive here, no data had to be replaced and we can return the input - return reply_markup - - self._keyboard_data[keyboard_uuid] = keyboard_data - return InlineKeyboardMarkup(buttons) - - @staticmethod - def __put_button(callback_data: object, keyboard_data: _KeyboardData) -> str: - """Stores the data for a single button in :attr:`keyboard_data`. - Returns the string that should be passed instead of the callback_data, which is - ``keyboard_uuid + button_uuids``. - """ - uuid = uuid4().hex - keyboard_data.button_data[uuid] = callback_data - return f'{keyboard_data.keyboard_uuid}{uuid}' - - def __get_keyboard_uuid_and_button_data( - self, callback_data: str - ) -> Union[Tuple[str, object], Tuple[None, InvalidCallbackData]]: - keyboard, button = self.extract_uuids(callback_data) - try: - # we get the values before calling update() in case KeyErrors are raised - # we don't want to update in that case - keyboard_data = self._keyboard_data[keyboard] - button_data = keyboard_data.button_data[button] - # Update the timestamp for the LRU - keyboard_data.update_access_time() - return keyboard, button_data - except KeyError: - return None, InvalidCallbackData(callback_data) - - @staticmethod - def extract_uuids(callback_data: str) -> Tuple[str, str]: - """Extracts the keyboard uuid and the button uuid from the given ``callback_data``. - - Args: - callback_data (:obj:`str`): The ``callback_data`` as present in the button. - - Returns: - (:obj:`str`, :obj:`str`): Tuple of keyboard and button uuid - - """ - # Extract the uuids as put in __put_button - return callback_data[:32], callback_data[32:] - - def process_message(self, message: Message) -> None: - """Replaces the data in the inline keyboard attached to the message with the cached - objects, if necessary. If the data could not be found, - :class:`telegram.ext.InvalidCallbackData` will be inserted. - - Note: - Checks :attr:`telegram.Message.via_bot` and :attr:`telegram.Message.from_user` to check - if the reply markup (if any) was actually sent by this caches bot. If it was not, the - message will be returned unchanged. - - Note that this will fail for channel posts, as :attr:`telegram.Message.from_user` is - :obj:`None` for those! In the corresponding reply markups the callback data will be - replaced by :class:`telegram.ext.InvalidCallbackData`. - - Warning: - * Does *not* consider :attr:`telegram.Message.reply_to_message` and - :attr:`telegram.Message.pinned_message`. Pass them to these method separately. - * *In place*, i.e. the passed :class:`telegram.Message` will be changed! - - Args: - message (:class:`telegram.Message`): The message. - - """ - with self.__lock: - self.__process_message(message) - - def __process_message(self, message: Message) -> Optional[str]: - """As documented in process_message, but returns the uuid of the attached keyboard, if any, - which is relevant for process_callback_query. - - **IN PLACE** - """ - if not message.reply_markup: - return None - - if message.via_bot: - sender: Optional[User] = message.via_bot - elif message.from_user: - sender = message.from_user - else: - sender = None - - if sender is not None and sender != self.bot.bot: - return None - - keyboard_uuid = None - - for row in message.reply_markup.inline_keyboard: - for button in row: - if button.callback_data: - button_data = cast(str, button.callback_data) - keyboard_id, callback_data = self.__get_keyboard_uuid_and_button_data( - button_data - ) - # update_callback_data makes sure that the _id_attrs are updated - button.update_callback_data(callback_data) - - # This is lazy loaded. The firsts time we find a button - # we load the associated keyboard - afterwards, there is - if not keyboard_uuid and not isinstance(callback_data, InvalidCallbackData): - keyboard_uuid = keyboard_id - - return keyboard_uuid - - def process_callback_query(self, callback_query: CallbackQuery) -> None: - """Replaces the data in the callback query and the attached messages keyboard with the - cached objects, if necessary. If the data could not be found, - :class:`telegram.ext.InvalidCallbackData` will be inserted. - If :attr:`callback_query.data` or :attr:`callback_query.message` is present, this also - saves the callback queries ID in order to be able to resolve it to the stored data. - - Note: - Also considers inserts data into the buttons of - :attr:`telegram.Message.reply_to_message` and :attr:`telegram.Message.pinned_message` - if necessary. - - Warning: - *In place*, i.e. the passed :class:`telegram.CallbackQuery` will be changed! - - Args: - callback_query (:class:`telegram.CallbackQuery`): The callback query. - - """ - with self.__lock: - mapped = False - - if callback_query.data: - data = callback_query.data - - # Get the cached callback data for the CallbackQuery - keyboard_uuid, button_data = self.__get_keyboard_uuid_and_button_data(data) - callback_query.data = button_data # type: ignore[assignment] - - # Map the callback queries ID to the keyboards UUID for later use - if not mapped and not isinstance(button_data, InvalidCallbackData): - self._callback_queries[callback_query.id] = keyboard_uuid # type: ignore - mapped = True - - # Get the cached callback data for the inline keyboard attached to the - # CallbackQuery. - if callback_query.message: - self.__process_message(callback_query.message) - for message in ( - callback_query.message.pinned_message, - callback_query.message.reply_to_message, - ): - if message: - self.__process_message(message) - - def drop_data(self, callback_query: CallbackQuery) -> None: - """Deletes the data for the specified callback query. - - Note: - Will *not* raise exceptions in case the callback data is not found in the cache. - *Will* raise :class:`KeyError` in case the callback query can not be found in the - cache. - - Args: - callback_query (:class:`telegram.CallbackQuery`): The callback query. - - Raises: - KeyError: If the callback query can not be found in the cache - """ - with self.__lock: - try: - keyboard_uuid = self._callback_queries.pop(callback_query.id) - self.__drop_keyboard(keyboard_uuid) - except KeyError as exc: - raise KeyError('CallbackQuery was not found in cache.') from exc - - def __drop_keyboard(self, keyboard_uuid: str) -> None: - try: - self._keyboard_data.pop(keyboard_uuid) - except KeyError: - return - - def clear_callback_data(self, time_cutoff: Union[float, datetime] = None) -> None: - """Clears the stored callback data. - - Args: - time_cutoff (:obj:`float` | :obj:`datetime.datetime`, optional): Pass a UNIX timestamp - or a :obj:`datetime.datetime` to clear only entries which are older. - For timezone naive :obj:`datetime.datetime` objects, the default timezone of the - bot will be used. - - """ - with self.__lock: - self.__clear(self._keyboard_data, time_cutoff=time_cutoff) - - def clear_callback_queries(self) -> None: - """Clears the stored callback query IDs.""" - with self.__lock: - self.__clear(self._callback_queries) - - def __clear(self, mapping: MutableMapping, time_cutoff: Union[float, datetime] = None) -> None: - if not time_cutoff: - mapping.clear() - return - - if isinstance(time_cutoff, datetime): - effective_cutoff = to_float_timestamp( - time_cutoff, tzinfo=self.bot.defaults.tzinfo if self.bot.defaults else None - ) - else: - effective_cutoff = time_cutoff - - # We need a list instead of a generator here, as the list doesn't change it's size - # during the iteration - to_drop = [key for key, data in mapping.items() if data.access_time < effective_cutoff] - for key in to_drop: - mapping.pop(key) diff --git a/telegramer/include/telegram/ext/callbackqueryhandler.py b/telegramer/include/telegram/ext/callbackqueryhandler.py deleted file mode 100644 index bb19fa7..0000000 --- a/telegramer/include/telegram/ext/callbackqueryhandler.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the CallbackQueryHandler class.""" - -import re -from typing import ( - TYPE_CHECKING, - Callable, - Dict, - Match, - Optional, - Pattern, - TypeVar, - Union, - cast, -) - -from telegram import Update -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .handler import Handler -from .utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class CallbackQueryHandler(Handler[Update, CCT]): - """Handler class to handle Telegram callback queries. Optionally based on a regex. - - Read the documentation of the ``re`` module for more information. - - Note: - * :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same - user or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - * If your bot allows arbitrary objects as ``callback_data``, it may happen that the - original ``callback_data`` for the incoming :class:`telegram.CallbackQuery`` can not be - found. This is the case when either a malicious client tempered with the - ``callback_data`` or the data was simply dropped from cache or not persisted. In these - cases, an instance of :class:`telegram.ext.InvalidCallbackData` will be set as - ``callback_data``. - - .. versionadded:: 13.6 - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pattern (:obj:`str` | `Pattern` | :obj:`callable` | :obj:`type`, optional): - Pattern to test :attr:`telegram.CallbackQuery.data` against. If a string or a regex - pattern is passed, :meth:`re.match` is used on :attr:`telegram.CallbackQuery.data` to - determine if an update should be handled by this handler. If your bot allows arbitrary - objects as ``callback_data``, non-strings will be accepted. To filter arbitrary - objects you may pass - - * a callable, accepting exactly one argument, namely the - :attr:`telegram.CallbackQuery.data`. It must return :obj:`True` or - :obj:`False`/:obj:`None` to indicate, whether the update should be handled. - * a :obj:`type`. If :attr:`telegram.CallbackQuery.data` is an instance of that type - (or a subclass), the update will be handled. - - If :attr:`telegram.CallbackQuery.data` is :obj:`None`, the - :class:`telegram.CallbackQuery` update will not be handled. - - .. versionchanged:: 13.6 - Added support for arbitrary callback data. - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pattern (`Pattern` | :obj:`callable` | :obj:`type`): Optional. Regex pattern, callback or - type to test :attr:`telegram.CallbackQuery.data` against. - - .. versionchanged:: 13.6 - Added support for arbitrary callback data. - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('pattern', 'pass_groups', 'pass_groupdict') - - def __init__( - self, - callback: Callable[[Update, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pattern: Union[str, Pattern, type, Callable[[object], Optional[bool]]] = None, - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - - if isinstance(pattern, str): - pattern = re.compile(pattern) - - self.pattern = pattern - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict - - def check_update(self, update: object) -> Optional[Union[bool, object]]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, Update) and update.callback_query: - callback_data = update.callback_query.data - if self.pattern: - if callback_data is None: - return False - if isinstance(self.pattern, type): - return isinstance(callback_data, self.pattern) - if callable(self.pattern): - return self.pattern(callback_data) - match = re.match(self.pattern, callback_data) - if match: - return match - else: - return True - return None - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: Update = None, - check_result: Union[bool, Match] = None, - ) -> Dict[str, object]: - """Pass the results of ``re.match(pattern, data).{groups(), groupdict()}`` to the - callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if - needed. - """ - optional_args = super().collect_optional_args(dispatcher, update, check_result) - if self.pattern and not callable(self.pattern): - check_result = cast(Match, check_result) - if self.pass_groups: - optional_args['groups'] = check_result.groups() - if self.pass_groupdict: - optional_args['groupdict'] = check_result.groupdict() - return optional_args - - def collect_additional_context( - self, - context: CCT, - update: Update, - dispatcher: 'Dispatcher', - check_result: Union[bool, Match], - ) -> None: - """Add the result of ``re.match(pattern, update.callback_query.data)`` to - :attr:`CallbackContext.matches` as list with one element. - """ - if self.pattern: - check_result = cast(Match, check_result) - context.matches = [check_result] diff --git a/telegramer/include/telegram/ext/chatjoinrequesthandler.py b/telegramer/include/telegram/ext/chatjoinrequesthandler.py deleted file mode 100644 index aafed54..0000000 --- a/telegramer/include/telegram/ext/chatjoinrequesthandler.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the ChatJoinRequestHandler class.""" - - -from telegram import Update - -from .handler import Handler -from .utils.types import CCT - - -class ChatJoinRequestHandler(Handler[Update, CCT]): - """Handler class to handle Telegram updates that contain a chat join request. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - .. versionadded:: 13.8 - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = () - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - return isinstance(update, Update) and bool(update.chat_join_request) diff --git a/telegramer/include/telegram/ext/chatmemberhandler.py b/telegramer/include/telegram/ext/chatmemberhandler.py deleted file mode 100644 index eb9d91b..0000000 --- a/telegramer/include/telegram/ext/chatmemberhandler.py +++ /dev/null @@ -1,145 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the ChatMemberHandler classes.""" -from typing import ClassVar, TypeVar, Union, Callable - -from telegram import Update -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE -from .handler import Handler -from .utils.types import CCT - -RT = TypeVar('RT') - - -class ChatMemberHandler(Handler[Update, CCT]): - """Handler class to handle Telegram updates that contain a chat member update. - - .. versionadded:: 13.4 - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - chat_member_types (:obj:`int`, optional): Pass one of :attr:`MY_CHAT_MEMBER`, - :attr:`CHAT_MEMBER` or :attr:`ANY_CHAT_MEMBER` to specify if this handler should handle - only updates with :attr:`telegram.Update.my_chat_member`, - :attr:`telegram.Update.chat_member` or both. Defaults to :attr:`MY_CHAT_MEMBER`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - chat_member_types (:obj:`int`, optional): Specifies if this handler should handle - only updates with :attr:`telegram.Update.my_chat_member`, - :attr:`telegram.Update.chat_member` or both. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('chat_member_types',) - MY_CHAT_MEMBER: ClassVar[int] = -1 - """:obj:`int`: Used as a constant to handle only :attr:`telegram.Update.my_chat_member`.""" - CHAT_MEMBER: ClassVar[int] = 0 - """:obj:`int`: Used as a constant to handle only :attr:`telegram.Update.chat_member`.""" - ANY_CHAT_MEMBER: ClassVar[int] = 1 - """:obj:`int`: Used as a constant to handle bot :attr:`telegram.Update.my_chat_member` - and :attr:`telegram.Update.chat_member`.""" - - def __init__( - self, - callback: Callable[[Update, CCT], RT], - chat_member_types: int = MY_CHAT_MEMBER, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - - self.chat_member_types = chat_member_types - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, Update): - if not (update.my_chat_member or update.chat_member): - return False - if self.chat_member_types == self.ANY_CHAT_MEMBER: - return True - if self.chat_member_types == self.CHAT_MEMBER: - return bool(update.chat_member) - return bool(update.my_chat_member) - return False diff --git a/telegramer/include/telegram/ext/choseninlineresulthandler.py b/telegramer/include/telegram/ext/choseninlineresulthandler.py deleted file mode 100644 index 1d94b79..0000000 --- a/telegramer/include/telegram/ext/choseninlineresulthandler.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the ChosenInlineResultHandler class.""" -import re -from typing import Optional, TypeVar, Union, Callable, TYPE_CHECKING, Pattern, Match, cast - -from telegram import Update - -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE -from .handler import Handler -from .utils.types import CCT - -RT = TypeVar('RT') - -if TYPE_CHECKING: - from telegram.ext import CallbackContext, Dispatcher - - -class ChosenInlineResultHandler(Handler[Update, CCT]): - """Handler class to handle Telegram updates that contain a chosen inline result. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - pattern (:obj:`str` | `Pattern`, optional): Regex pattern. If not :obj:`None`, ``re.match`` - is used on :attr:`telegram.ChosenInlineResult.result_id` to determine if an update - should be handled by this handler. This is accessible in the callback as - :attr:`telegram.ext.CallbackContext.matches`. - - .. versionadded:: 13.6 - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - pattern (`Pattern`): Optional. Regex pattern to test - :attr:`telegram.ChosenInlineResult.result_id` against. - - .. versionadded:: 13.6 - - """ - - __slots__ = ('pattern',) - - def __init__( - self, - callback: Callable[[Update, 'CallbackContext'], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - pattern: Union[str, Pattern] = None, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - - if isinstance(pattern, str): - pattern = re.compile(pattern) - - self.pattern = pattern - - def check_update(self, update: object) -> Optional[Union[bool, object]]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, Update) and update.chosen_inline_result: - if self.pattern: - match = re.match(self.pattern, update.chosen_inline_result.result_id) - if match: - return match - else: - return True - return None - - def collect_additional_context( - self, - context: 'CallbackContext', - update: Update, - dispatcher: 'Dispatcher', - check_result: Union[bool, Match], - ) -> None: - """This function adds the matched regex pattern result to - :attr:`telegram.ext.CallbackContext.matches`. - """ - if self.pattern: - check_result = cast(Match, check_result) - context.matches = [check_result] diff --git a/telegramer/include/telegram/ext/commandhandler.py b/telegramer/include/telegram/ext/commandhandler.py deleted file mode 100644 index 6f53d23..0000000 --- a/telegramer/include/telegram/ext/commandhandler.py +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the CommandHandler and PrefixHandler classes.""" -import re -import warnings -from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple, TypeVar, Union - -from telegram import MessageEntity, Update -from telegram.ext import BaseFilter, Filters -from telegram.utils.deprecate import TelegramDeprecationWarning -from telegram.utils.types import SLT -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .utils.types import CCT -from .handler import Handler - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class CommandHandler(Handler[Update, CCT]): - """Handler class to handle Telegram commands. - - Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the - bot's name and/or some additional text. The handler will add a ``list`` to the - :class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings, - which is the text following the command split on single or consecutive whitespace characters. - - By default the handler listens to messages as well as edited messages. To change this behavior - use ``~Filters.update.edited_message`` in the filter argument. - - Note: - * :class:`CommandHandler` does *not* handle (edited) channel posts. - * :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a :obj:`dict` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same - user or in the same chat, it will be the same :obj:`dict`. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - command (:class:`telegram.utils.types.SLT[str]`): - The command or list of commands this handler should listen for. - Limitations are the same as described here https://core.telegram.org/bots#commands - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - filters (:class:`telegram.ext.BaseFilter`, optional): A filter inheriting from - :class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in - :class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise - operators (& for and, | for or, ~ for not). - allow_edited (:obj:`bool`, optional): Determines whether the handler should also accept - edited messages. Default is :obj:`False`. - DEPRECATED: Edited is allowed by default. To change this behavior use - ``~Filters.update.edited_message``. - pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the - arguments passed to the command as a keyword argument called ``args``. It will contain - a list of strings, which is the text following the command split on single or - consecutive whitespace characters. Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Raises: - ValueError: when command is too long or has illegal chars. - - Attributes: - command (:class:`telegram.utils.types.SLT[str]`): - The command or list of commands this handler should listen for. - Limitations are the same as described here https://core.telegram.org/bots#commands - callback (:obj:`callable`): The callback function for this handler. - filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these - Filters. - allow_edited (:obj:`bool`): Determines whether the handler should also accept - edited messages. - pass_args (:obj:`bool`): Determines whether the handler should be passed - ``args``. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - """ - - __slots__ = ('command', 'filters', 'pass_args') - - def __init__( - self, - command: SLT[str], - callback: Callable[[Update, CCT], RT], - filters: BaseFilter = None, - allow_edited: bool = None, - pass_args: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - - if isinstance(command, str): - self.command = [command.lower()] - else: - self.command = [x.lower() for x in command] - for comm in self.command: - if not re.match(r'^[\da-z_]{1,32}$', comm): - raise ValueError('Command is not a valid bot command') - - if filters: - self.filters = Filters.update.messages & filters - else: - self.filters = Filters.update.messages - - if allow_edited is not None: - warnings.warn( - 'allow_edited is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if not allow_edited: - self.filters &= ~Filters.update.edited_message - self.pass_args = pass_args - - def check_update( - self, update: object - ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`list`: The list of args for the handler. - - """ - if isinstance(update, Update) and update.effective_message: - message = update.effective_message - - if ( - message.entities - and message.entities[0].type == MessageEntity.BOT_COMMAND - and message.entities[0].offset == 0 - and message.text - and message.bot - ): - command = message.text[1 : message.entities[0].length] - args = message.text.split()[1:] - command_parts = command.split('@') - command_parts.append(message.bot.username) - - if not ( - command_parts[0].lower() in self.command - and command_parts[1].lower() == message.bot.username.lower() - ): - return None - - filter_result = self.filters(update) - if filter_result: - return args, filter_result - return False - return None - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: Update = None, - check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]] = None, - ) -> Dict[str, object]: - """Provide text after the command to the callback the ``args`` argument as list, split on - single whitespaces. - """ - optional_args = super().collect_optional_args(dispatcher, update) - if self.pass_args and isinstance(check_result, tuple): - optional_args['args'] = check_result[0] - return optional_args - - def collect_additional_context( - self, - context: CCT, - update: Update, - dispatcher: 'Dispatcher', - check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]], - ) -> None: - """Add text after the command to :attr:`CallbackContext.args` as list, split on single - whitespaces and add output of data filters to :attr:`CallbackContext` as well. - """ - if isinstance(check_result, tuple): - context.args = check_result[0] - if isinstance(check_result[1], dict): - context.update(check_result[1]) - - -class PrefixHandler(CommandHandler): - """Handler class to handle custom prefix commands. - - This is a intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`. - It supports configurable commands with the same options as CommandHandler. It will respond to - every combination of :attr:`prefix` and :attr:`command`. It will add a ``list`` to the - :class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings, - which is the text following the command split on single or consecutive whitespace characters. - - Examples: - - Single prefix and command: - - .. code:: python - - PrefixHandler('!', 'test', callback) # will respond to '!test'. - - Multiple prefixes, single command: - - .. code:: python - - PrefixHandler(['!', '#'], 'test', callback) # will respond to '!test' and '#test'. - - Multiple prefixes and commands: - - .. code:: python - - PrefixHandler(['!', '#'], ['test', 'help'], callback) # will respond to '!test', \ - '#test', '!help' and '#help'. - - - By default the handler listens to messages as well as edited messages. To change this behavior - use ``~Filters.update.edited_message``. - - Note: - * :class:`PrefixHandler` does *not* handle (edited) channel posts. - * :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a :obj:`dict` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same - user or in the same chat, it will be the same :obj:`dict`. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - prefix (:class:`telegram.utils.types.SLT[str]`): - The prefix(es) that will precede :attr:`command`. - command (:class:`telegram.utils.types.SLT[str]`): - The command or list of commands this handler should listen for. - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - filters (:class:`telegram.ext.BaseFilter`, optional): A filter inheriting from - :class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in - :class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise - operators (& for and, | for or, ~ for not). - pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the - arguments passed to the command as a keyword argument called ``args``. It will contain - a list of strings, which is the text following the command split on single or - consecutive whitespace characters. Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these - Filters. - pass_args (:obj:`bool`): Determines whether the handler should be passed - ``args``. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - # 'prefix' is a class property, & 'command' is included in the superclass, so they're left out. - __slots__ = ('_prefix', '_command', '_commands') - - def __init__( - self, - prefix: SLT[str], - command: SLT[str], - callback: Callable[[Update, CCT], RT], - filters: BaseFilter = None, - pass_args: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - - self._prefix: List[str] = [] - self._command: List[str] = [] - self._commands: List[str] = [] - - super().__init__( - 'nocommand', - callback, - filters=filters, - allow_edited=None, - pass_args=pass_args, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - - self.prefix = prefix # type: ignore[assignment] - self.command = command # type: ignore[assignment] - self._build_commands() - - @property - def prefix(self) -> List[str]: - """ - The prefixes that will precede :attr:`command`. - - Returns: - List[:obj:`str`] - """ - return self._prefix - - @prefix.setter - def prefix(self, prefix: Union[str, List[str]]) -> None: - if isinstance(prefix, str): - self._prefix = [prefix.lower()] - else: - self._prefix = prefix - self._build_commands() - - @property # type: ignore[override] - def command(self) -> List[str]: # type: ignore[override] - """ - The list of commands this handler should listen for. - - Returns: - List[:obj:`str`] - """ - return self._command - - @command.setter - def command(self, command: Union[str, List[str]]) -> None: - if isinstance(command, str): - self._command = [command.lower()] - else: - self._command = command - self._build_commands() - - def _build_commands(self) -> None: - self._commands = [x.lower() + y.lower() for x in self.prefix for y in self.command] - - def check_update( - self, update: object - ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`list`: The list of args for the handler. - - """ - if isinstance(update, Update) and update.effective_message: - message = update.effective_message - - if message.text: - text_list = message.text.split() - if text_list[0].lower() not in self._commands: - return None - filter_result = self.filters(update) - if filter_result: - return text_list[1:], filter_result - return False - return None diff --git a/telegramer/include/telegram/ext/contexttypes.py b/telegramer/include/telegram/ext/contexttypes.py deleted file mode 100644 index ee03037..0000000 --- a/telegramer/include/telegram/ext/contexttypes.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=R0201 -"""This module contains the auxiliary class ContextTypes.""" -from typing import Type, Generic, overload, Dict # pylint: disable=W0611 - -from telegram.ext.callbackcontext import CallbackContext -from telegram.ext.utils.types import CCT, UD, CD, BD - - -class ContextTypes(Generic[CCT, UD, CD, BD]): - """ - Convenience class to gather customizable types of the :class:`telegram.ext.CallbackContext` - interface. - - .. versionadded:: 13.6 - - Args: - context (:obj:`type`, optional): Determines the type of the ``context`` argument of all - (error-)handler callbacks and job callbacks. Must be a subclass of - :class:`telegram.ext.CallbackContext`. Defaults to - :class:`telegram.ext.CallbackContext`. - bot_data (:obj:`type`, optional): Determines the type of ``context.bot_data`` of all - (error-)handler callbacks and job callbacks. Defaults to :obj:`dict`. Must support - instantiating without arguments. - chat_data (:obj:`type`, optional): Determines the type of ``context.chat_data`` of all - (error-)handler callbacks and job callbacks. Defaults to :obj:`dict`. Must support - instantiating without arguments. - user_data (:obj:`type`, optional): Determines the type of ``context.user_data`` of all - (error-)handler callbacks and job callbacks. Defaults to :obj:`dict`. Must support - instantiating without arguments. - - """ - - __slots__ = ('_context', '_bot_data', '_chat_data', '_user_data') - - # overload signatures generated with https://git.io/JtJPj - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[Dict, Dict, Dict], Dict, Dict, Dict]', - ): - ... - - @overload - def __init__(self: 'ContextTypes[CCT, Dict, Dict, Dict]', context: Type[CCT]): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[UD, Dict, Dict], UD, Dict, Dict]', user_data: Type[UD] - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[Dict, CD, Dict], Dict, CD, Dict]', chat_data: Type[CD] - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[Dict, Dict, BD], Dict, Dict, BD]', bot_data: Type[BD] - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, UD, Dict, Dict]', context: Type[CCT], user_data: Type[UD] - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, Dict, CD, Dict]', context: Type[CCT], chat_data: Type[CD] - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, Dict, Dict, BD]', context: Type[CCT], bot_data: Type[BD] - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[UD, CD, Dict], UD, CD, Dict]', - user_data: Type[UD], - chat_data: Type[CD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[UD, Dict, BD], UD, Dict, BD]', - user_data: Type[UD], - bot_data: Type[BD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[Dict, CD, BD], Dict, CD, BD]', - chat_data: Type[CD], - bot_data: Type[BD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, UD, CD, Dict]', - context: Type[CCT], - user_data: Type[UD], - chat_data: Type[CD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, UD, Dict, BD]', - context: Type[CCT], - user_data: Type[UD], - bot_data: Type[BD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, Dict, CD, BD]', - context: Type[CCT], - chat_data: Type[CD], - bot_data: Type[BD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CallbackContext[UD, CD, BD], UD, CD, BD]', - user_data: Type[UD], - chat_data: Type[CD], - bot_data: Type[BD], - ): - ... - - @overload - def __init__( - self: 'ContextTypes[CCT, UD, CD, BD]', - context: Type[CCT], - user_data: Type[UD], - chat_data: Type[CD], - bot_data: Type[BD], - ): - ... - - def __init__( # type: ignore[no-untyped-def] - self, - context=CallbackContext, - bot_data=dict, - chat_data=dict, - user_data=dict, - ): - if not issubclass(context, CallbackContext): - raise ValueError('context must be a subclass of CallbackContext.') - - # We make all those only accessible via properties because we don't currently support - # changing this at runtime, so overriding the attributes doesn't make sense - self._context = context - self._bot_data = bot_data - self._chat_data = chat_data - self._user_data = user_data - - @property - def context(self) -> Type[CCT]: - return self._context - - @property - def bot_data(self) -> Type[BD]: - return self._bot_data - - @property - def chat_data(self) -> Type[CD]: - return self._chat_data - - @property - def user_data(self) -> Type[UD]: - return self._user_data diff --git a/telegramer/include/telegram/ext/conversationhandler.py b/telegramer/include/telegram/ext/conversationhandler.py deleted file mode 100644 index 23edf2f..0000000 --- a/telegramer/include/telegram/ext/conversationhandler.py +++ /dev/null @@ -1,725 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=R0201 -"""This module contains the ConversationHandler.""" - -import logging -import warnings -import functools -import datetime -from threading import Lock -from typing import TYPE_CHECKING, Dict, List, NoReturn, Optional, Union, Tuple, cast, ClassVar - -from telegram import Update -from telegram.ext import ( - BasePersistence, - CallbackContext, - CallbackQueryHandler, - ChosenInlineResultHandler, - DispatcherHandlerStop, - Handler, - InlineQueryHandler, -) -from telegram.ext.utils.promise import Promise -from telegram.ext.utils.types import ConversationDict -from telegram.ext.utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher, Job -CheckUpdateType = Optional[Tuple[Tuple[int, ...], Handler, object]] - - -class _ConversationTimeoutContext: - # '__dict__' is not included since this a private class - __slots__ = ('conversation_key', 'update', 'dispatcher', 'callback_context') - - def __init__( - self, - conversation_key: Tuple[int, ...], - update: Update, - dispatcher: 'Dispatcher', - callback_context: Optional[CallbackContext], - ): - self.conversation_key = conversation_key - self.update = update - self.dispatcher = dispatcher - self.callback_context = callback_context - - -class ConversationHandler(Handler[Update, CCT]): - """ - A handler to hold a conversation with a single or multiple users through Telegram updates by - managing four collections of other handlers. - - Note: - ``ConversationHandler`` will only accept updates that are (subclass-)instances of - :class:`telegram.Update`. This is, because depending on the :attr:`per_user` and - :attr:`per_chat` ``ConversationHandler`` relies on - :attr:`telegram.Update.effective_user` and/or :attr:`telegram.Update.effective_chat` in - order to determine which conversation an update should belong to. For ``per_message=True``, - ``ConversationHandler`` uses ``update.callback_query.message.message_id`` when - ``per_chat=True`` and ``update.callback_query.inline_message_id`` when ``per_chat=False``. - For a more detailed explanation, please see our `FAQ`_. - - Finally, ``ConversationHandler``, does *not* handle (edited) channel posts. - - .. _`FAQ`: https://git.io/JtcyU - - The first collection, a ``list`` named :attr:`entry_points`, is used to initiate the - conversation, for example with a :class:`telegram.ext.CommandHandler` or - :class:`telegram.ext.MessageHandler`. - - The second collection, a ``dict`` named :attr:`states`, contains the different conversation - steps and one or more associated handlers that should be used if the user sends a message when - the conversation with them is currently in that state. Here you can also define a state for - :attr:`TIMEOUT` to define the behavior when :attr:`conversation_timeout` is exceeded, and a - state for :attr:`WAITING` to define behavior when a new update is received while the previous - ``@run_async`` decorated handler is not finished. - - The third collection, a ``list`` named :attr:`fallbacks`, is used if the user is currently in a - conversation but the state has either no associated handler or the handler that is associated - to the state is inappropriate for the update, for example if the update contains a command, but - a regular text message is expected. You could use this for a ``/cancel`` command or to let the - user know their message was not recognized. - - To change the state of conversation, the callback function of a handler must return the new - state after responding to the user. If it does not return anything (returning :obj:`None` by - default), the state will not change. If an entry point callback function returns :obj:`None`, - the conversation ends immediately after the execution of this callback function. - To end the conversation, the callback function must return :attr:`END` or ``-1``. To - handle the conversation timeout, use handler :attr:`TIMEOUT` or ``-2``. - Finally, :class:`telegram.ext.DispatcherHandlerStop` can be used in conversations as described - in the corresponding documentation. - - Note: - In each of the described collections of handlers, a handler may in turn be a - :class:`ConversationHandler`. In that case, the nested :class:`ConversationHandler` should - have the attribute :attr:`map_to_parent` which allows to return to the parent conversation - at specified states within the nested conversation. - - Note that the keys in :attr:`map_to_parent` must not appear as keys in :attr:`states` - attribute or else the latter will be ignored. You may map :attr:`END` to one of the parents - states to continue the parent conversation after this has ended or even map a state to - :attr:`END` to end the *parent* conversation from within the nested one. For an example on - nested :class:`ConversationHandler` s, see our `examples`_. - - .. _`examples`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples - - Args: - entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can - trigger the start of the conversation. The first handler which :attr:`check_update` - method returns :obj:`True` will be used. If all return :obj:`False`, the update is not - handled. - states (Dict[:obj:`object`, List[:class:`telegram.ext.Handler`]]): A :obj:`dict` that - defines the different states of conversation a user can be in and one or more - associated ``Handler`` objects that should be used in that state. The first handler - which :attr:`check_update` method returns :obj:`True` will be used. - fallbacks (List[:class:`telegram.ext.Handler`]): A list of handlers that might be used if - the user is in a conversation, but every handler for their current state returned - :obj:`False` on :attr:`check_update`. The first handler which :attr:`check_update` - method returns :obj:`True` will be used. If all return :obj:`False`, the update is not - handled. - allow_reentry (:obj:`bool`, optional): If set to :obj:`True`, a user that is currently in a - conversation can restart the conversation by triggering one of the entry points. - per_chat (:obj:`bool`, optional): If the conversationkey should contain the Chat's ID. - Default is :obj:`True`. - per_user (:obj:`bool`, optional): If the conversationkey should contain the User's ID. - Default is :obj:`True`. - per_message (:obj:`bool`, optional): If the conversationkey should contain the Message's - ID. Default is :obj:`False`. - conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`, optional): When this - handler is inactive more than this timeout (in seconds), it will be automatically - ended. If this value is 0 or :obj:`None` (default), there will be no timeout. The last - received update and the corresponding ``context`` will be handled by ALL the handler's - who's :attr:`check_update` method returns :obj:`True` that are in the state - :attr:`ConversationHandler.TIMEOUT`. - - Note: - Using `conversation_timeout` with nested conversations is currently not - supported. You can still try to use it, but it will likely behave differently - from what you expect. - - - name (:obj:`str`, optional): The name for this conversationhandler. Required for - persistence. - persistent (:obj:`bool`, optional): If the conversations dict for this handler should be - saved. Name is required and persistence has to be set in :class:`telegram.ext.Updater` - map_to_parent (Dict[:obj:`object`, :obj:`object`], optional): A :obj:`dict` that can be - used to instruct a nested conversationhandler to transition into a mapped state on - its parent conversationhandler in place of a specified nested state. - run_async (:obj:`bool`, optional): Pass :obj:`True` to *override* the - :attr:`Handler.run_async` setting of all handlers (in :attr:`entry_points`, - :attr:`states` and :attr:`fallbacks`). - - Note: - If set to :obj:`True`, you should not pass a handler instance, that needs to be - run synchronously in another context. - - .. versionadded:: 13.2 - - Raises: - ValueError - - Attributes: - persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be - saved. Name is required and persistence has to be set in :class:`telegram.ext.Updater` - run_async (:obj:`bool`): If :obj:`True`, will override the - :attr:`Handler.run_async` setting of all internal handlers on initialization. - - .. versionadded:: 13.2 - - """ - - __slots__ = ( - '_entry_points', - '_states', - '_fallbacks', - '_allow_reentry', - '_per_user', - '_per_chat', - '_per_message', - '_conversation_timeout', - '_name', - 'persistent', - '_persistence', - '_map_to_parent', - 'timeout_jobs', - '_timeout_jobs_lock', - '_conversations', - '_conversations_lock', - 'logger', - ) - - END: ClassVar[int] = -1 - """:obj:`int`: Used as a constant to return when a conversation is ended.""" - TIMEOUT: ClassVar[int] = -2 - """:obj:`int`: Used as a constant to handle state when a conversation is timed out.""" - WAITING: ClassVar[int] = -3 - """:obj:`int`: Used as a constant to handle state when a conversation is still waiting on the - previous ``@run_sync`` decorated running handler to finish.""" - # pylint: disable=W0231 - def __init__( - self, - entry_points: List[Handler[Update, CCT]], - states: Dict[object, List[Handler[Update, CCT]]], - fallbacks: List[Handler[Update, CCT]], - allow_reentry: bool = False, - per_chat: bool = True, - per_user: bool = True, - per_message: bool = False, - conversation_timeout: Union[float, datetime.timedelta] = None, - name: str = None, - persistent: bool = False, - map_to_parent: Dict[object, object] = None, - run_async: bool = False, - ): - self.run_async = run_async - - self._entry_points = entry_points - self._states = states - self._fallbacks = fallbacks - - self._allow_reentry = allow_reentry - self._per_user = per_user - self._per_chat = per_chat - self._per_message = per_message - self._conversation_timeout = conversation_timeout - self._name = name - if persistent and not self.name: - raise ValueError("Conversations can't be persistent when handler is unnamed.") - self.persistent: bool = persistent - self._persistence: Optional[BasePersistence] = None - """:obj:`telegram.ext.BasePersistence`: The persistence used to store conversations. - Set by dispatcher""" - self._map_to_parent = map_to_parent - - self.timeout_jobs: Dict[Tuple[int, ...], 'Job'] = {} - self._timeout_jobs_lock = Lock() - self._conversations: ConversationDict = {} - self._conversations_lock = Lock() - - self.logger = logging.getLogger(__name__) - - if not any((self.per_user, self.per_chat, self.per_message)): - raise ValueError("'per_user', 'per_chat' and 'per_message' can't all be 'False'") - - if self.per_message and not self.per_chat: - warnings.warn( - "If 'per_message=True' is used, 'per_chat=True' should also be used, " - "since message IDs are not globally unique." - ) - - all_handlers: List[Handler] = [] - all_handlers.extend(entry_points) - all_handlers.extend(fallbacks) - - for state_handlers in states.values(): - all_handlers.extend(state_handlers) - - if self.per_message: - for handler in all_handlers: - if not isinstance(handler, CallbackQueryHandler): - warnings.warn( - "If 'per_message=True', all entry points and state handlers" - " must be 'CallbackQueryHandler', since no other handlers " - "have a message context." - ) - break - else: - for handler in all_handlers: - if isinstance(handler, CallbackQueryHandler): - warnings.warn( - "If 'per_message=False', 'CallbackQueryHandler' will not be " - "tracked for every message." - ) - break - - if self.per_chat: - for handler in all_handlers: - if isinstance(handler, (InlineQueryHandler, ChosenInlineResultHandler)): - warnings.warn( - "If 'per_chat=True', 'InlineQueryHandler' can not be used, " - "since inline queries have no chat context." - ) - break - - if self.conversation_timeout: - for handler in all_handlers: - if isinstance(handler, self.__class__): - warnings.warn( - "Using `conversation_timeout` with nested conversations is currently not " - "supported. You can still try to use it, but it will likely behave " - "differently from what you expect." - ) - break - - if self.run_async: - for handler in all_handlers: - handler.run_async = True - - @property - def entry_points(self) -> List[Handler]: - """List[:class:`telegram.ext.Handler`]: A list of ``Handler`` objects that can trigger the - start of the conversation. - """ - return self._entry_points - - @entry_points.setter - def entry_points(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to entry_points after initialization.') - - @property - def states(self) -> Dict[object, List[Handler]]: - """Dict[:obj:`object`, List[:class:`telegram.ext.Handler`]]: A :obj:`dict` that - defines the different states of conversation a user can be in and one or more - associated ``Handler`` objects that should be used in that state. - """ - return self._states - - @states.setter - def states(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to states after initialization.') - - @property - def fallbacks(self) -> List[Handler]: - """List[:class:`telegram.ext.Handler`]: A list of handlers that might be used if - the user is in a conversation, but every handler for their current state returned - :obj:`False` on :attr:`check_update`. - """ - return self._fallbacks - - @fallbacks.setter - def fallbacks(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to fallbacks after initialization.') - - @property - def allow_reentry(self) -> bool: - """:obj:`bool`: Determines if a user can restart a conversation with an entry point.""" - return self._allow_reentry - - @allow_reentry.setter - def allow_reentry(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to allow_reentry after initialization.') - - @property - def per_user(self) -> bool: - """:obj:`bool`: If the conversation key should contain the User's ID.""" - return self._per_user - - @per_user.setter - def per_user(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to per_user after initialization.') - - @property - def per_chat(self) -> bool: - """:obj:`bool`: If the conversation key should contain the Chat's ID.""" - return self._per_chat - - @per_chat.setter - def per_chat(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to per_chat after initialization.') - - @property - def per_message(self) -> bool: - """:obj:`bool`: If the conversation key should contain the message's ID.""" - return self._per_message - - @per_message.setter - def per_message(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to per_message after initialization.') - - @property - def conversation_timeout( - self, - ) -> Optional[Union[float, datetime.timedelta]]: - """:obj:`float` | :obj:`datetime.timedelta`: Optional. When this - handler is inactive more than this timeout (in seconds), it will be automatically - ended. - """ - return self._conversation_timeout - - @conversation_timeout.setter - def conversation_timeout(self, value: object) -> NoReturn: - raise ValueError( - 'You can not assign a new value to conversation_timeout after initialization.' - ) - - @property - def name(self) -> Optional[str]: - """:obj:`str`: Optional. The name for this :class:`ConversationHandler`.""" - return self._name - - @name.setter - def name(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to name after initialization.') - - @property - def map_to_parent(self) -> Optional[Dict[object, object]]: - """Dict[:obj:`object`, :obj:`object`]: Optional. A :obj:`dict` that can be - used to instruct a nested :class:`ConversationHandler` to transition into a mapped state on - its parent :class:`ConversationHandler` in place of a specified nested state. - """ - return self._map_to_parent - - @map_to_parent.setter - def map_to_parent(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to map_to_parent after initialization.') - - @property - def persistence(self) -> Optional[BasePersistence]: - """The persistence class as provided by the :class:`Dispatcher`.""" - return self._persistence - - @persistence.setter - def persistence(self, persistence: BasePersistence) -> None: - self._persistence = persistence - # Set persistence for nested conversations - for handlers in self.states.values(): - for handler in handlers: - if isinstance(handler, ConversationHandler): - handler.persistence = self.persistence - - @property - def conversations(self) -> ConversationDict: # skipcq: PY-D0003 - return self._conversations - - @conversations.setter - def conversations(self, value: ConversationDict) -> None: - self._conversations = value - # Set conversations for nested conversations - for handlers in self.states.values(): - for handler in handlers: - if isinstance(handler, ConversationHandler) and self.persistence and handler.name: - handler.conversations = self.persistence.get_conversations(handler.name) - - def _get_key(self, update: Update) -> Tuple[int, ...]: - chat = update.effective_chat - user = update.effective_user - - key = [] - - if self.per_chat: - key.append(chat.id) # type: ignore[union-attr] - - if self.per_user and user is not None: - key.append(user.id) - - if self.per_message: - key.append( - update.callback_query.inline_message_id # type: ignore[union-attr] - or update.callback_query.message.message_id # type: ignore[union-attr] - ) - - return tuple(key) - - def _resolve_promise(self, state: Tuple) -> object: - old_state, new_state = state - try: - res = new_state.result(0) - res = res if res is not None else old_state - except Exception as exc: - self.logger.exception("Promise function raised exception") - self.logger.exception("%s", exc) - res = old_state - finally: - if res is None and old_state is None: - res = self.END - return res - - def _schedule_job( - self, - new_state: object, - dispatcher: 'Dispatcher', - update: Update, - context: Optional[CallbackContext], - conversation_key: Tuple[int, ...], - ) -> None: - if new_state != self.END: - try: - # both job_queue & conversation_timeout are checked before calling _schedule_job - j_queue = dispatcher.job_queue - self.timeout_jobs[conversation_key] = j_queue.run_once( # type: ignore[union-attr] - self._trigger_timeout, - self.conversation_timeout, # type: ignore[arg-type] - context=_ConversationTimeoutContext( - conversation_key, update, dispatcher, context - ), - ) - except Exception as exc: - self.logger.exception( - "Failed to schedule timeout job due to the following exception:" - ) - self.logger.exception("%s", exc) - - def check_update(self, update: object) -> CheckUpdateType: # pylint: disable=R0911 - """ - Determines whether an update should be handled by this conversationhandler, and if so in - which state the conversation currently is. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if not isinstance(update, Update): - return None - # Ignore messages in channels - if update.channel_post or update.edited_channel_post: - return None - if self.per_chat and not update.effective_chat: - return None - if self.per_message and not update.callback_query: - return None - if update.callback_query and self.per_chat and not update.callback_query.message: - return None - - key = self._get_key(update) - with self._conversations_lock: - state = self.conversations.get(key) - - # Resolve promises - if isinstance(state, tuple) and len(state) == 2 and isinstance(state[1], Promise): - self.logger.debug('waiting for promise...') - - # check if promise is finished or not - if state[1].done.wait(0): - res = self._resolve_promise(state) - self._update_state(res, key) - with self._conversations_lock: - state = self.conversations.get(key) - - # if not then handle WAITING state instead - else: - hdlrs = self.states.get(self.WAITING, []) - for hdlr in hdlrs: - check = hdlr.check_update(update) - if check is not None and check is not False: - return key, hdlr, check - return None - - self.logger.debug('selecting conversation %s with state %s', str(key), str(state)) - - handler = None - - # Search entry points for a match - if state is None or self.allow_reentry: - for entry_point in self.entry_points: - check = entry_point.check_update(update) - if check is not None and check is not False: - handler = entry_point - break - - else: - if state is None: - return None - - # Get the handler list for current state, if we didn't find one yet and we're still here - if state is not None and not handler: - handlers = self.states.get(state) - - for candidate in handlers or []: - check = candidate.check_update(update) - if check is not None and check is not False: - handler = candidate - break - - # Find a fallback handler if all other handlers fail - else: - for fallback in self.fallbacks: - check = fallback.check_update(update) - if check is not None and check is not False: - handler = fallback - break - - else: - return None - - return key, handler, check # type: ignore[return-value] - - def handle_update( # type: ignore[override] - self, - update: Update, - dispatcher: 'Dispatcher', - check_result: CheckUpdateType, - context: CallbackContext = None, - ) -> Optional[object]: - """Send the update to the callback for the current state and Handler - - Args: - check_result: The result from check_update. For this handler it's a tuple of key, - handler, and the handler's check result. - update (:class:`telegram.Update`): Incoming telegram update. - dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update. - context (:class:`telegram.ext.CallbackContext`, optional): The context as provided by - the dispatcher. - - """ - update = cast(Update, update) # for mypy - conversation_key, handler, check_result = check_result # type: ignore[assignment,misc] - raise_dp_handler_stop = False - - with self._timeout_jobs_lock: - # Remove the old timeout job (if present) - timeout_job = self.timeout_jobs.pop(conversation_key, None) - - if timeout_job is not None: - timeout_job.schedule_removal() - try: - new_state = handler.handle_update(update, dispatcher, check_result, context) - except DispatcherHandlerStop as exception: - new_state = exception.state - raise_dp_handler_stop = True - with self._timeout_jobs_lock: - if self.conversation_timeout: - if dispatcher.job_queue is not None: - # Add the new timeout job - if isinstance(new_state, Promise): - new_state.add_done_callback( - functools.partial( - self._schedule_job, - dispatcher=dispatcher, - update=update, - context=context, - conversation_key=conversation_key, - ) - ) - elif new_state != self.END: - self._schedule_job( - new_state, dispatcher, update, context, conversation_key - ) - else: - self.logger.warning( - "Ignoring `conversation_timeout` because the Dispatcher has no JobQueue." - ) - - if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent: - self._update_state(self.END, conversation_key) - if raise_dp_handler_stop: - raise DispatcherHandlerStop(self.map_to_parent.get(new_state)) - return self.map_to_parent.get(new_state) - - self._update_state(new_state, conversation_key) - if raise_dp_handler_stop: - # Don't pass the new state here. If we're in a nested conversation, the parent is - # expecting None as return value. - raise DispatcherHandlerStop() - return None - - def _update_state(self, new_state: object, key: Tuple[int, ...]) -> None: - if new_state == self.END: - with self._conversations_lock: - if key in self.conversations: - # If there is no key in conversations, nothing is done. - del self.conversations[key] - if self.persistent and self.persistence and self.name: - self.persistence.update_conversation(self.name, key, None) - - elif isinstance(new_state, Promise): - with self._conversations_lock: - self.conversations[key] = (self.conversations.get(key), new_state) - if self.persistent and self.persistence and self.name: - self.persistence.update_conversation( - self.name, key, (self.conversations.get(key), new_state) - ) - - elif new_state is not None: - if new_state not in self.states: - warnings.warn( - f"Handler returned state {new_state} which is unknown to the " - f"ConversationHandler{' ' + self.name if self.name is not None else ''}." - ) - with self._conversations_lock: - self.conversations[key] = new_state - if self.persistent and self.persistence and self.name: - self.persistence.update_conversation(self.name, key, new_state) - - def _trigger_timeout(self, context: CallbackContext, job: 'Job' = None) -> None: - self.logger.debug('conversation timeout was triggered!') - - # Backward compatibility with bots that do not use CallbackContext - if isinstance(context, CallbackContext): - job = context.job - ctxt = cast(_ConversationTimeoutContext, job.context) # type: ignore[union-attr] - else: - ctxt = cast(_ConversationTimeoutContext, job.context) - - callback_context = ctxt.callback_context - - with self._timeout_jobs_lock: - found_job = self.timeout_jobs[ctxt.conversation_key] - if found_job is not job: - # The timeout has been cancelled in handle_update - return - del self.timeout_jobs[ctxt.conversation_key] - - handlers = self.states.get(self.TIMEOUT, []) - for handler in handlers: - check = handler.check_update(ctxt.update) - if check is not None and check is not False: - try: - handler.handle_update(ctxt.update, ctxt.dispatcher, check, callback_context) - except DispatcherHandlerStop: - self.logger.warning( - 'DispatcherHandlerStop in TIMEOUT state of ' - 'ConversationHandler has no effect. Ignoring.' - ) - - self._update_state(self.END, ctxt.conversation_key) diff --git a/telegramer/include/telegram/ext/defaults.py b/telegramer/include/telegram/ext/defaults.py deleted file mode 100644 index 1e08255..0000000 --- a/telegramer/include/telegram/ext/defaults.py +++ /dev/null @@ -1,267 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=R0201 -"""This module contains the class Defaults, which allows to pass default values to Updater.""" -from typing import NoReturn, Optional, Dict, Any - -import pytz - -from telegram.utils.deprecate import set_new_attribute_deprecated -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - - -class Defaults: - """Convenience Class to gather all parameters with a (user defined) default value - - Parameters: - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or URLs in your bot's message. - disable_notification (:obj:`bool`, optional): Sends the message silently. Users will - receive a notification with no sound. - disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in this - message. - allow_sending_without_reply (:obj:`bool`, optional): Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as the - read timeout from the server (instead of the one specified during creation of the - connection pool). - - Note: - Will *not* be used for :meth:`telegram.Bot.get_updates`! - quote (:obj:`bool`, optional): If set to :obj:`True`, the reply is sent as an actual reply - to the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will - be ignored. Default: :obj:`True` in group chats and :obj:`False` in private chats. - tzinfo (:obj:`tzinfo`, optional): A timezone to be used for all date(time) inputs - appearing throughout PTB, i.e. if a timezone naive date(time) object is passed - somewhere, it will be assumed to be in ``tzinfo``. Must be a timezone provided by the - ``pytz`` module. Defaults to UTC. - run_async (:obj:`bool`, optional): Default setting for the ``run_async`` parameter of - handlers and error handlers registered through :meth:`Dispatcher.add_handler` and - :meth:`Dispatcher.add_error_handler`. Defaults to :obj:`False`. - """ - - __slots__ = ( - '_timeout', - '_tzinfo', - '_disable_web_page_preview', - '_run_async', - '_quote', - '_disable_notification', - '_allow_sending_without_reply', - '_parse_mode', - '_api_defaults', - '__dict__', - ) - - def __init__( - self, - parse_mode: str = None, - disable_notification: bool = None, - disable_web_page_preview: bool = None, - # Timeout needs special treatment, since the bot methods have two different - # default values for timeout (None and 20s) - timeout: ODVInput[float] = DEFAULT_NONE, - quote: bool = None, - tzinfo: pytz.BaseTzInfo = pytz.utc, - run_async: bool = False, - allow_sending_without_reply: bool = None, - ): - self._parse_mode = parse_mode - self._disable_notification = disable_notification - self._disable_web_page_preview = disable_web_page_preview - self._allow_sending_without_reply = allow_sending_without_reply - self._timeout = timeout - self._quote = quote - self._tzinfo = tzinfo - self._run_async = run_async - - # Gather all defaults that actually have a default value - self._api_defaults = {} - for kwarg in ( - 'parse_mode', - 'explanation_parse_mode', - 'disable_notification', - 'disable_web_page_preview', - 'allow_sending_without_reply', - ): - value = getattr(self, kwarg) - if value not in [None, DEFAULT_NONE]: - self._api_defaults[kwarg] = value - # Special casing, as None is a valid default value - if self._timeout != DEFAULT_NONE: - self._api_defaults['timeout'] = self._timeout - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - @property - def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003 - return self._api_defaults - - @property - def parse_mode(self) -> Optional[str]: - """:obj:`str`: Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or URLs in your bot's message. - """ - return self._parse_mode - - @parse_mode.setter - def parse_mode(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def explanation_parse_mode(self) -> Optional[str]: - """:obj:`str`: Optional. Alias for :attr:`parse_mode`, used for - the corresponding parameter of :meth:`telegram.Bot.send_poll`. - """ - return self._parse_mode - - @explanation_parse_mode.setter - def explanation_parse_mode(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def disable_notification(self) -> Optional[bool]: - """:obj:`bool`: Optional. Sends the message silently. Users will - receive a notification with no sound. - """ - return self._disable_notification - - @disable_notification.setter - def disable_notification(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def disable_web_page_preview(self) -> Optional[bool]: - """:obj:`bool`: Optional. Disables link previews for links in this - message. - """ - return self._disable_web_page_preview - - @disable_web_page_preview.setter - def disable_web_page_preview(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def allow_sending_without_reply(self) -> Optional[bool]: - """:obj:`bool`: Optional. Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - """ - return self._allow_sending_without_reply - - @allow_sending_without_reply.setter - def allow_sending_without_reply(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def timeout(self) -> ODVInput[float]: - """:obj:`int` | :obj:`float`: Optional. If this value is specified, use it as the - read timeout from the server (instead of the one specified during creation of the - connection pool). - """ - return self._timeout - - @timeout.setter - def timeout(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def quote(self) -> Optional[bool]: - """:obj:`bool`: Optional. If set to :obj:`True`, the reply is sent as an actual reply - to the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will - be ignored. Default: :obj:`True` in group chats and :obj:`False` in private chats. - """ - return self._quote - - @quote.setter - def quote(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def tzinfo(self) -> pytz.BaseTzInfo: - """:obj:`tzinfo`: A timezone to be used for all date(time) objects appearing - throughout PTB. - """ - return self._tzinfo - - @tzinfo.setter - def tzinfo(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - @property - def run_async(self) -> bool: - """:obj:`bool`: Optional. Default setting for the ``run_async`` parameter of - handlers and error handlers registered through :meth:`Dispatcher.add_handler` and - :meth:`Dispatcher.add_error_handler`. - """ - return self._run_async - - @run_async.setter - def run_async(self, value: object) -> NoReturn: - raise AttributeError( - "You can not assign a new value to defaults after because it would " - "not have any effect." - ) - - def __hash__(self) -> int: - return hash( - ( - self._parse_mode, - self._disable_notification, - self._disable_web_page_preview, - self._allow_sending_without_reply, - self._timeout, - self._quote, - self._tzinfo, - self._run_async, - ) - ) - - def __eq__(self, other: object) -> bool: - if isinstance(other, Defaults): - return all(getattr(self, attr) == getattr(other, attr) for attr in self.__slots__) - return False - - def __ne__(self, other: object) -> bool: - return not self == other diff --git a/telegramer/include/telegram/ext/dictpersistence.py b/telegramer/include/telegram/ext/dictpersistence.py deleted file mode 100644 index e307123..0000000 --- a/telegramer/include/telegram/ext/dictpersistence.py +++ /dev/null @@ -1,404 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the DictPersistence class.""" - -from typing import DefaultDict, Dict, Optional, Tuple, cast -from collections import defaultdict - -from telegram.utils.helpers import ( - decode_conversations_from_json, - decode_user_chat_data_from_json, - encode_conversations_to_json, -) -from telegram.ext import BasePersistence -from telegram.ext.utils.types import ConversationDict, CDCData - -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] - - -class DictPersistence(BasePersistence): - """Using Python's :obj:`dict` and ``json`` for making your bot persistent. - - Note: - This class does *not* implement a :meth:`flush` method, meaning that data managed by - ``DictPersistence`` is in-memory only and will be lost when the bot shuts down. This is, - because ``DictPersistence`` is mainly intended as starting point for custom persistence - classes that need to JSON-serialize the stored data before writing them to file/database. - - Warning: - :class:`DictPersistence` will try to replace :class:`telegram.Bot` instances by - :attr:`REPLACED_BOT` and insert the bot set with - :meth:`telegram.ext.BasePersistence.set_bot` upon loading of the data. This is to ensure - that changes to the bot apply to the saved objects, too. If you change the bots token, this - may lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see - :meth:`telegram.ext.BasePersistence.replace_bot` and - :meth:`telegram.ext.BasePersistence.insert_bot`. - - Args: - store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this - persistence class. Default is :obj:`True`. - store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this - persistence class. Default is :obj:`True`. - store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this - persistence class. Default is :obj:`True`. - store_callback_data (:obj:`bool`, optional): Whether callback_data should be saved by this - persistence class. Default is :obj:`False`. - - .. versionadded:: 13.6 - user_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct - user_data on creating this persistence. Default is ``""``. - chat_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct - chat_data on creating this persistence. Default is ``""``. - bot_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct - bot_data on creating this persistence. Default is ``""``. - callback_data_json (:obj:`str`, optional): Json string that will be used to reconstruct - callback_data on creating this persistence. Default is ``""``. - - .. versionadded:: 13.6 - conversations_json (:obj:`str`, optional): JSON string that will be used to reconstruct - conversation on creating this persistence. Default is ``""``. - - Attributes: - store_user_data (:obj:`bool`): Whether user_data should be saved by this - persistence class. - store_chat_data (:obj:`bool`): Whether chat_data should be saved by this - persistence class. - store_bot_data (:obj:`bool`): Whether bot_data should be saved by this - persistence class. - store_callback_data (:obj:`bool`): Whether callback_data be saved by this - persistence class. - - .. versionadded:: 13.6 - """ - - __slots__ = ( - '_user_data', - '_chat_data', - '_bot_data', - '_callback_data', - '_conversations', - '_user_data_json', - '_chat_data_json', - '_bot_data_json', - '_callback_data_json', - '_conversations_json', - ) - - def __init__( - self, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - user_data_json: str = '', - chat_data_json: str = '', - bot_data_json: str = '', - conversations_json: str = '', - store_callback_data: bool = False, - callback_data_json: str = '', - ): - super().__init__( - store_user_data=store_user_data, - store_chat_data=store_chat_data, - store_bot_data=store_bot_data, - store_callback_data=store_callback_data, - ) - self._user_data = None - self._chat_data = None - self._bot_data = None - self._callback_data = None - self._conversations = None - self._user_data_json = None - self._chat_data_json = None - self._bot_data_json = None - self._callback_data_json = None - self._conversations_json = None - if user_data_json: - try: - self._user_data = decode_user_chat_data_from_json(user_data_json) - self._user_data_json = user_data_json - except (ValueError, AttributeError) as exc: - raise TypeError("Unable to deserialize user_data_json. Not valid JSON") from exc - if chat_data_json: - try: - self._chat_data = decode_user_chat_data_from_json(chat_data_json) - self._chat_data_json = chat_data_json - except (ValueError, AttributeError) as exc: - raise TypeError("Unable to deserialize chat_data_json. Not valid JSON") from exc - if bot_data_json: - try: - self._bot_data = json.loads(bot_data_json) - self._bot_data_json = bot_data_json - except (ValueError, AttributeError) as exc: - raise TypeError("Unable to deserialize bot_data_json. Not valid JSON") from exc - if not isinstance(self._bot_data, dict): - raise TypeError("bot_data_json must be serialized dict") - if callback_data_json: - try: - data = json.loads(callback_data_json) - except (ValueError, AttributeError) as exc: - raise TypeError( - "Unable to deserialize callback_data_json. Not valid JSON" - ) from exc - # We are a bit more thorough with the checking of the format here, because it's - # more complicated than for the other things - try: - if data is None: - self._callback_data = None - else: - self._callback_data = cast( - CDCData, - ([(one, float(two), three) for one, two, three in data[0]], data[1]), - ) - self._callback_data_json = callback_data_json - except (ValueError, IndexError) as exc: - raise TypeError("callback_data_json is not in the required format") from exc - if self._callback_data is not None and ( - not all( - isinstance(entry[2], dict) and isinstance(entry[0], str) - for entry in self._callback_data[0] - ) - or not isinstance(self._callback_data[1], dict) - ): - raise TypeError("callback_data_json is not in the required format") - - if conversations_json: - try: - self._conversations = decode_conversations_from_json(conversations_json) - self._conversations_json = conversations_json - except (ValueError, AttributeError) as exc: - raise TypeError( - "Unable to deserialize conversations_json. Not valid JSON" - ) from exc - - @property - def user_data(self) -> Optional[DefaultDict[int, Dict]]: - """:obj:`dict`: The user_data as a dict.""" - return self._user_data - - @property - def user_data_json(self) -> str: - """:obj:`str`: The user_data serialized as a JSON-string.""" - if self._user_data_json: - return self._user_data_json - return json.dumps(self.user_data) - - @property - def chat_data(self) -> Optional[DefaultDict[int, Dict]]: - """:obj:`dict`: The chat_data as a dict.""" - return self._chat_data - - @property - def chat_data_json(self) -> str: - """:obj:`str`: The chat_data serialized as a JSON-string.""" - if self._chat_data_json: - return self._chat_data_json - return json.dumps(self.chat_data) - - @property - def bot_data(self) -> Optional[Dict]: - """:obj:`dict`: The bot_data as a dict.""" - return self._bot_data - - @property - def bot_data_json(self) -> str: - """:obj:`str`: The bot_data serialized as a JSON-string.""" - if self._bot_data_json: - return self._bot_data_json - return json.dumps(self.bot_data) - - @property - def callback_data(self) -> Optional[CDCData]: - """:class:`telegram.ext.utils.types.CDCData`: The meta data on the stored callback data. - - .. versionadded:: 13.6 - """ - return self._callback_data - - @property - def callback_data_json(self) -> str: - """:obj:`str`: The meta data on the stored callback data as a JSON-string. - - .. versionadded:: 13.6 - """ - if self._callback_data_json: - return self._callback_data_json - return json.dumps(self.callback_data) - - @property - def conversations(self) -> Optional[Dict[str, ConversationDict]]: - """:obj:`dict`: The conversations as a dict.""" - return self._conversations - - @property - def conversations_json(self) -> str: - """:obj:`str`: The conversations serialized as a JSON-string.""" - if self._conversations_json: - return self._conversations_json - return encode_conversations_to_json(self.conversations) # type: ignore[arg-type] - - def get_user_data(self) -> DefaultDict[int, Dict[object, object]]: - """Returns the user_data created from the ``user_data_json`` or an empty - :obj:`defaultdict`. - - Returns: - :obj:`defaultdict`: The restored user data. - """ - if self.user_data is None: - self._user_data = defaultdict(dict) - return self.user_data # type: ignore[return-value] - - def get_chat_data(self) -> DefaultDict[int, Dict[object, object]]: - """Returns the chat_data created from the ``chat_data_json`` or an empty - :obj:`defaultdict`. - - Returns: - :obj:`defaultdict`: The restored chat data. - """ - if self.chat_data is None: - self._chat_data = defaultdict(dict) - return self.chat_data # type: ignore[return-value] - - def get_bot_data(self) -> Dict[object, object]: - """Returns the bot_data created from the ``bot_data_json`` or an empty :obj:`dict`. - - Returns: - :obj:`dict`: The restored bot data. - """ - if self.bot_data is None: - self._bot_data = {} - return self.bot_data # type: ignore[return-value] - - def get_callback_data(self) -> Optional[CDCData]: - """Returns the callback_data created from the ``callback_data_json`` or :obj:`None`. - - .. versionadded:: 13.6 - - Returns: - Optional[:class:`telegram.ext.utils.types.CDCData`]: The restored meta data or - :obj:`None`, if no data was stored. - """ - if self.callback_data is None: - self._callback_data = None - return None - return self.callback_data[0], self.callback_data[1].copy() - - def get_conversations(self, name: str) -> ConversationDict: - """Returns the conversations created from the ``conversations_json`` or an empty - :obj:`dict`. - - Returns: - :obj:`dict`: The restored conversations data. - """ - if self.conversations is None: - self._conversations = {} - return self.conversations.get(name, {}).copy() # type: ignore[union-attr] - - def update_conversation( - self, name: str, key: Tuple[int, ...], new_state: Optional[object] - ) -> None: - """Will update the conversations for the given handler. - - Args: - name (:obj:`str`): The handler's name. - key (:obj:`tuple`): The key the state is changed for. - new_state (:obj:`tuple` | :obj:`any`): The new state for the given key. - """ - if not self._conversations: - self._conversations = {} - if self._conversations.setdefault(name, {}).get(key) == new_state: - return - self._conversations[name][key] = new_state - self._conversations_json = None - - def update_user_data(self, user_id: int, data: Dict) -> None: - """Will update the user_data (if changed). - - Args: - user_id (:obj:`int`): The user the data might have been changed for. - data (:obj:`dict`): The :attr:`telegram.ext.Dispatcher.user_data` ``[user_id]``. - """ - if self._user_data is None: - self._user_data = defaultdict(dict) - if self._user_data.get(user_id) == data: - return - self._user_data[user_id] = data - self._user_data_json = None - - def update_chat_data(self, chat_id: int, data: Dict) -> None: - """Will update the chat_data (if changed). - - Args: - chat_id (:obj:`int`): The chat the data might have been changed for. - data (:obj:`dict`): The :attr:`telegram.ext.Dispatcher.chat_data` ``[chat_id]``. - """ - if self._chat_data is None: - self._chat_data = defaultdict(dict) - if self._chat_data.get(chat_id) == data: - return - self._chat_data[chat_id] = data - self._chat_data_json = None - - def update_bot_data(self, data: Dict) -> None: - """Will update the bot_data (if changed). - - Args: - data (:obj:`dict`): The :attr:`telegram.ext.Dispatcher.bot_data`. - """ - if self._bot_data == data: - return - self._bot_data = data - self._bot_data_json = None - - def update_callback_data(self, data: CDCData) -> None: - """Will update the callback_data (if changed). - - .. versionadded:: 13.6 - - Args: - data (:class:`telegram.ext.utils.types.CDCData`): The relevant data to restore - :class:`telegram.ext.CallbackDataCache`. - """ - if self._callback_data == data: - return - self._callback_data = (data[0], data[1].copy()) - self._callback_data_json = None - - def refresh_user_data(self, user_id: int, user_data: Dict) -> None: - """Does nothing. - - .. versionadded:: 13.6 - .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_user_data` - """ - - def refresh_chat_data(self, chat_id: int, chat_data: Dict) -> None: - """Does nothing. - - .. versionadded:: 13.6 - .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_chat_data` - """ - - def refresh_bot_data(self, bot_data: Dict) -> None: - """Does nothing. - - .. versionadded:: 13.6 - .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_bot_data` - """ diff --git a/telegramer/include/telegram/ext/dispatcher.py b/telegramer/include/telegram/ext/dispatcher.py deleted file mode 100644 index af24188..0000000 --- a/telegramer/include/telegram/ext/dispatcher.py +++ /dev/null @@ -1,820 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the Dispatcher class.""" - -import logging -import warnings -import weakref -from collections import defaultdict -from functools import wraps -from queue import Empty, Queue -from threading import BoundedSemaphore, Event, Lock, Thread, current_thread -from time import sleep -from typing import ( - TYPE_CHECKING, - Callable, - DefaultDict, - Dict, - List, - Optional, - Set, - Union, - Generic, - TypeVar, - overload, - cast, -) -from uuid import uuid4 - -from telegram import TelegramError, Update -from telegram.ext import BasePersistence, ContextTypes -from telegram.ext.callbackcontext import CallbackContext -from telegram.ext.handler import Handler -import telegram.ext.extbot -from telegram.ext.callbackdatacache import CallbackDataCache -from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated -from telegram.ext.utils.promise import Promise -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE -from telegram.ext.utils.types import CCT, UD, CD, BD - -if TYPE_CHECKING: - from telegram import Bot - from telegram.ext import JobQueue - -DEFAULT_GROUP: int = 0 - -UT = TypeVar('UT') - - -def run_async( - func: Callable[[Update, CallbackContext], object] -) -> Callable[[Update, CallbackContext], object]: - """ - Function decorator that will run the function in a new thread. - - Will run :attr:`telegram.ext.Dispatcher.run_async`. - - Using this decorator is only possible when only a single Dispatcher exist in the system. - - Note: - DEPRECATED. Use :attr:`telegram.ext.Dispatcher.run_async` directly instead or the - :attr:`Handler.run_async` parameter. - - Warning: - If you're using ``@run_async`` you cannot rely on adding custom attributes to - :class:`telegram.ext.CallbackContext`. See its docs for more info. - """ - - @wraps(func) - def async_func(*args: object, **kwargs: object) -> object: - warnings.warn( - 'The @run_async decorator is deprecated. Use the `run_async` parameter of ' - 'your Handler or `Dispatcher.run_async` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return Dispatcher.get_instance()._run_async( # pylint: disable=W0212 - func, *args, update=None, error_handling=False, **kwargs - ) - - return async_func - - -class DispatcherHandlerStop(Exception): - """ - Raise this in handler to prevent execution of any other handler (even in different group). - - In order to use this exception in a :class:`telegram.ext.ConversationHandler`, pass the - optional ``state`` parameter instead of returning the next state: - - .. code-block:: python - - def callback(update, context): - ... - raise DispatcherHandlerStop(next_state) - - Attributes: - state (:obj:`object`): Optional. The next state of the conversation. - - Args: - state (:obj:`object`, optional): The next state of the conversation. - """ - - __slots__ = ('state',) - - def __init__(self, state: object = None) -> None: - super().__init__() - self.state = state - - -class Dispatcher(Generic[CCT, UD, CD, BD]): - """This class dispatches all kinds of updates to its registered handlers. - - Args: - bot (:class:`telegram.Bot`): The bot object that should be passed to the handlers. - update_queue (:obj:`Queue`): The synchronized queue that will contain the updates. - job_queue (:class:`telegram.ext.JobQueue`, optional): The :class:`telegram.ext.JobQueue` - instance to pass onto handler callbacks. - workers (:obj:`int`, optional): Number of maximum concurrent worker threads for the - ``@run_async`` decorator and :meth:`run_async`. Defaults to 4. - persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to - store data that should be persistent over restarts. - use_context (:obj:`bool`, optional): If set to :obj:`True` uses the context based callback - API (ignored if `dispatcher` argument is used). Defaults to :obj:`True`. - **New users**: set this to :obj:`True`. - context_types (:class:`telegram.ext.ContextTypes`, optional): Pass an instance - of :class:`telegram.ext.ContextTypes` to customize the types used in the - ``context`` interface. If not passed, the defaults documented in - :class:`telegram.ext.ContextTypes` will be used. - - .. versionadded:: 13.6 - - Attributes: - bot (:class:`telegram.Bot`): The bot object that should be passed to the handlers. - update_queue (:obj:`Queue`): The synchronized queue that will contain the updates. - job_queue (:class:`telegram.ext.JobQueue`): Optional. The :class:`telegram.ext.JobQueue` - instance to pass onto handler callbacks. - workers (:obj:`int`, optional): Number of maximum concurrent worker threads for the - ``@run_async`` decorator and :meth:`run_async`. - user_data (:obj:`defaultdict`): A dictionary handlers can use to store data for the user. - chat_data (:obj:`defaultdict`): A dictionary handlers can use to store data for the chat. - bot_data (:obj:`dict`): A dictionary handlers can use to store data for the bot. - persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to - store data that should be persistent over restarts. - context_types (:class:`telegram.ext.ContextTypes`): Container for the types used - in the ``context`` interface. - - .. versionadded:: 13.6 - - """ - - # Allowing '__weakref__' creation here since we need it for the singleton - __slots__ = ( - 'workers', - 'persistence', - 'use_context', - 'update_queue', - 'job_queue', - 'user_data', - 'chat_data', - 'bot_data', - '_update_persistence_lock', - 'handlers', - 'groups', - 'error_handlers', - 'running', - '__stop_event', - '__exception_event', - '__async_queue', - '__async_threads', - 'bot', - '__dict__', - '__weakref__', - 'context_types', - ) - - __singleton_lock = Lock() - __singleton_semaphore = BoundedSemaphore() - __singleton = None - logger = logging.getLogger(__name__) - - @overload - def __init__( - self: 'Dispatcher[CallbackContext[Dict, Dict, Dict], Dict, Dict, Dict]', - bot: 'Bot', - update_queue: Queue, - workers: int = 4, - exception_event: Event = None, - job_queue: 'JobQueue' = None, - persistence: BasePersistence = None, - use_context: bool = True, - ): - ... - - @overload - def __init__( - self: 'Dispatcher[CCT, UD, CD, BD]', - bot: 'Bot', - update_queue: Queue, - workers: int = 4, - exception_event: Event = None, - job_queue: 'JobQueue' = None, - persistence: BasePersistence = None, - use_context: bool = True, - context_types: ContextTypes[CCT, UD, CD, BD] = None, - ): - ... - - def __init__( - self, - bot: 'Bot', - update_queue: Queue, - workers: int = 4, - exception_event: Event = None, - job_queue: 'JobQueue' = None, - persistence: BasePersistence = None, - use_context: bool = True, - context_types: ContextTypes[CCT, UD, CD, BD] = None, - ): - self.bot = bot - self.update_queue = update_queue - self.job_queue = job_queue - self.workers = workers - self.use_context = use_context - self.context_types = cast(ContextTypes[CCT, UD, CD, BD], context_types or ContextTypes()) - - if not use_context: - warnings.warn( - 'Old Handler API is deprecated - see https://git.io/fxJuV for details', - TelegramDeprecationWarning, - stacklevel=3, - ) - - if self.workers < 1: - warnings.warn( - 'Asynchronous callbacks can not be processed without at least one worker thread.' - ) - - self.user_data: DefaultDict[int, UD] = defaultdict(self.context_types.user_data) - self.chat_data: DefaultDict[int, CD] = defaultdict(self.context_types.chat_data) - self.bot_data = self.context_types.bot_data() - self.persistence: Optional[BasePersistence] = None - self._update_persistence_lock = Lock() - if persistence: - if not isinstance(persistence, BasePersistence): - raise TypeError("persistence must be based on telegram.ext.BasePersistence") - self.persistence = persistence - self.persistence.set_bot(self.bot) - if self.persistence.store_user_data: - self.user_data = self.persistence.get_user_data() - if not isinstance(self.user_data, defaultdict): - raise ValueError("user_data must be of type defaultdict") - if self.persistence.store_chat_data: - self.chat_data = self.persistence.get_chat_data() - if not isinstance(self.chat_data, defaultdict): - raise ValueError("chat_data must be of type defaultdict") - if self.persistence.store_bot_data: - self.bot_data = self.persistence.get_bot_data() - if not isinstance(self.bot_data, self.context_types.bot_data): - raise ValueError( - f"bot_data must be of type {self.context_types.bot_data.__name__}" - ) - if self.persistence.store_callback_data: - self.bot = cast(telegram.ext.extbot.ExtBot, self.bot) - persistent_data = self.persistence.get_callback_data() - if persistent_data is not None: - if not isinstance(persistent_data, tuple) and len(persistent_data) != 2: - raise ValueError('callback_data must be a 2-tuple') - self.bot.callback_data_cache = CallbackDataCache( - self.bot, - self.bot.callback_data_cache.maxsize, - persistent_data=persistent_data, - ) - else: - self.persistence = None - - self.handlers: Dict[int, List[Handler]] = {} - """Dict[:obj:`int`, List[:class:`telegram.ext.Handler`]]: Holds the handlers per group.""" - self.groups: List[int] = [] - """List[:obj:`int`]: A list with all groups.""" - self.error_handlers: Dict[Callable, Union[bool, DefaultValue]] = {} - """Dict[:obj:`callable`, :obj:`bool`]: A dict, where the keys are error handlers and the - values indicate whether they are to be run asynchronously.""" - - self.running = False - """:obj:`bool`: Indicates if this dispatcher is running.""" - self.__stop_event = Event() - self.__exception_event = exception_event or Event() - self.__async_queue: Queue = Queue() - self.__async_threads: Set[Thread] = set() - - # For backward compatibility, we allow a "singleton" mode for the dispatcher. When there's - # only one instance of Dispatcher, it will be possible to use the `run_async` decorator. - with self.__singleton_lock: - if self.__singleton_semaphore.acquire(blocking=False): # pylint: disable=R1732 - self._set_singleton(self) - else: - self._set_singleton(None) - - def __setattr__(self, key: str, value: object) -> None: - # Mangled names don't automatically apply in __setattr__ (see - # https://docs.python.org/3/tutorial/classes.html#private-variables), so we have to make - # it mangled so they don't raise TelegramDeprecationWarning unnecessarily - if key.startswith('__'): - key = f"_{self.__class__.__name__}{key}" - if issubclass(self.__class__, Dispatcher) and self.__class__ is not Dispatcher: - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - - @property - def exception_event(self) -> Event: # skipcq: PY-D0003 - return self.__exception_event - - def _init_async_threads(self, base_name: str, workers: int) -> None: - base_name = f'{base_name}_' if base_name else '' - - for i in range(workers): - thread = Thread(target=self._pooled, name=f'Bot:{self.bot.id}:worker:{base_name}{i}') - self.__async_threads.add(thread) - thread.start() - - @classmethod - def _set_singleton(cls, val: Optional['Dispatcher']) -> None: - cls.logger.debug('Setting singleton dispatcher as %s', val) - cls.__singleton = weakref.ref(val) if val else None - - @classmethod - def get_instance(cls) -> 'Dispatcher': - """Get the singleton instance of this class. - - Returns: - :class:`telegram.ext.Dispatcher` - - Raises: - RuntimeError - - """ - if cls.__singleton is not None: - return cls.__singleton() # type: ignore[return-value] # pylint: disable=not-callable - raise RuntimeError(f'{cls.__name__} not initialized or multiple instances exist') - - def _pooled(self) -> None: - thr_name = current_thread().name - while 1: - promise = self.__async_queue.get() - - # If unpacking fails, the thread pool is being closed from Updater._join_async_threads - if not isinstance(promise, Promise): - self.logger.debug( - "Closing run_async thread %s/%d", thr_name, len(self.__async_threads) - ) - break - - promise.run() - - if not promise.exception: - self.update_persistence(update=promise.update) - continue - - if isinstance(promise.exception, DispatcherHandlerStop): - self.logger.warning( - 'DispatcherHandlerStop is not supported with async functions; func: %s', - promise.pooled_function.__name__, - ) - continue - - # Avoid infinite recursion of error handlers. - if promise.pooled_function in self.error_handlers: - self.logger.error('An uncaught error was raised while handling the error.') - continue - - # Don't perform error handling for a `Promise` with deactivated error handling. This - # should happen only via the deprecated `@run_async` decorator or `Promises` created - # within error handlers - if not promise.error_handling: - self.logger.error('A promise with deactivated error handling raised an error.') - continue - - # If we arrive here, an exception happened in the promise and was neither - # DispatcherHandlerStop nor raised by an error handler. So we can and must handle it - try: - self.dispatch_error(promise.update, promise.exception, promise=promise) - except Exception: - self.logger.exception('An uncaught error was raised while handling the error.') - - def run_async( - self, func: Callable[..., object], *args: object, update: object = None, **kwargs: object - ) -> Promise: - """ - Queue a function (with given args/kwargs) to be run asynchronously. Exceptions raised - by the function will be handled by the error handlers registered with - :meth:`add_error_handler`. - - Warning: - * If you're using ``@run_async``/:meth:`run_async` you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - * Calling a function through :meth:`run_async` from within an error handler can lead to - an infinite error handling loop. - - Args: - func (:obj:`callable`): The function to run in the thread. - *args (:obj:`tuple`, optional): Arguments to ``func``. - update (:class:`telegram.Update` | :obj:`object`, optional): The update associated with - the functions call. If passed, it will be available in the error handlers, in case - an exception is raised by :attr:`func`. - **kwargs (:obj:`dict`, optional): Keyword arguments to ``func``. - - Returns: - Promise - - """ - return self._run_async(func, *args, update=update, error_handling=True, **kwargs) - - def _run_async( - self, - func: Callable[..., object], - *args: object, - update: object = None, - error_handling: bool = True, - **kwargs: object, - ) -> Promise: - # TODO: Remove error_handling parameter once we drop the @run_async decorator - promise = Promise(func, args, kwargs, update=update, error_handling=error_handling) - self.__async_queue.put(promise) - return promise - - def start(self, ready: Event = None) -> None: - """Thread target of thread 'dispatcher'. - - Runs in background and processes the update queue. - - Args: - ready (:obj:`threading.Event`, optional): If specified, the event will be set once the - dispatcher is ready. - - """ - if self.running: - self.logger.warning('already running') - if ready is not None: - ready.set() - return - - if self.__exception_event.is_set(): - msg = 'reusing dispatcher after exception event is forbidden' - self.logger.error(msg) - raise TelegramError(msg) - - self._init_async_threads(str(uuid4()), self.workers) - self.running = True - self.logger.debug('Dispatcher started') - - if ready is not None: - ready.set() - - while 1: - try: - # Pop update from update queue. - update = self.update_queue.get(True, 1) - except Empty: - if self.__stop_event.is_set(): - self.logger.debug('orderly stopping') - break - if self.__exception_event.is_set(): - self.logger.critical('stopping due to exception in another thread') - break - continue - - self.logger.debug('Processing Update: %s', update) - self.process_update(update) - self.update_queue.task_done() - - self.running = False - self.logger.debug('Dispatcher thread stopped') - - def stop(self) -> None: - """Stops the thread.""" - if self.running: - self.__stop_event.set() - while self.running: - sleep(0.1) - self.__stop_event.clear() - - # async threads must be join()ed only after the dispatcher thread was joined, - # otherwise we can still have new async threads dispatched - threads = list(self.__async_threads) - total = len(threads) - - # Stop all threads in the thread pool by put()ting one non-tuple per thread - for i in range(total): - self.__async_queue.put(None) - - for i, thr in enumerate(threads): - self.logger.debug('Waiting for async thread %s/%s to end', i + 1, total) - thr.join() - self.__async_threads.remove(thr) - self.logger.debug('async thread %s/%s has ended', i + 1, total) - - @property - def has_running_threads(self) -> bool: # skipcq: PY-D0003 - return self.running or bool(self.__async_threads) - - def process_update(self, update: object) -> None: - """Processes a single update and updates the persistence. - - Note: - If the update is handled by least one synchronously running handlers (i.e. - ``run_async=False``), :meth:`update_persistence` is called *once* after all handlers - synchronous handlers are done. Each asynchronously running handler will trigger - :meth:`update_persistence` on its own. - - Args: - update (:class:`telegram.Update` | :obj:`object` | \ - :class:`telegram.error.TelegramError`): - The update to process. - - """ - # An error happened while polling - if isinstance(update, TelegramError): - try: - self.dispatch_error(None, update) - except Exception: - self.logger.exception('An uncaught error was raised while handling the error.') - return - - context = None - handled = False - sync_modes = [] - - for group in self.groups: - try: - for handler in self.handlers[group]: - check = handler.check_update(update) - if check is not None and check is not False: - if not context and self.use_context: - context = self.context_types.context.from_update(update, self) - context.refresh_data() - handled = True - sync_modes.append(handler.run_async) - handler.handle_update(update, self, check, context) - break - - # Stop processing with any other handler. - except DispatcherHandlerStop: - self.logger.debug('Stopping further handlers due to DispatcherHandlerStop') - self.update_persistence(update=update) - break - - # Dispatch any error. - except Exception as exc: - try: - self.dispatch_error(update, exc) - except DispatcherHandlerStop: - self.logger.debug('Error handler stopped further handlers') - break - # Errors should not stop the thread. - except Exception: - self.logger.exception('An uncaught error was raised while handling the error.') - - # Update persistence, if handled - handled_only_async = all(sync_modes) - if handled: - # Respect default settings - if all(mode is DEFAULT_FALSE for mode in sync_modes) and self.bot.defaults: - handled_only_async = self.bot.defaults.run_async - # If update was only handled by async handlers, we don't need to update here - if not handled_only_async: - self.update_persistence(update=update) - - def add_handler(self, handler: Handler[UT, CCT], group: int = DEFAULT_GROUP) -> None: - """Register a handler. - - TL;DR: Order and priority counts. 0 or 1 handlers per group will be used. End handling of - update with :class:`telegram.ext.DispatcherHandlerStop`. - - A handler must be an instance of a subclass of :class:`telegram.ext.Handler`. All handlers - are organized in groups with a numeric value. The default group is 0. All groups will be - evaluated for handling an update, but only 0 or 1 handler per group will be used. If - :class:`telegram.ext.DispatcherHandlerStop` is raised from one of the handlers, no further - handlers (regardless of the group) will be called. - - The priority/order of handlers is determined as follows: - - * Priority of the group (lower group number == higher priority) - * The first handler in a group which should handle an update (see - :attr:`telegram.ext.Handler.check_update`) will be used. Other handlers from the - group will not be used. The order in which handlers were added to the group defines the - priority. - - Args: - handler (:class:`telegram.ext.Handler`): A Handler instance. - group (:obj:`int`, optional): The group identifier. Default is 0. - - """ - # Unfortunately due to circular imports this has to be here - from .conversationhandler import ConversationHandler # pylint: disable=C0415 - - if not isinstance(handler, Handler): - raise TypeError(f'handler is not an instance of {Handler.__name__}') - if not isinstance(group, int): - raise TypeError('group is not int') - # For some reason MyPy infers the type of handler is <nothing> here, - # so for now we just ignore all the errors - if ( - isinstance(handler, ConversationHandler) - and handler.persistent # type: ignore[attr-defined] - and handler.name # type: ignore[attr-defined] - ): - if not self.persistence: - raise ValueError( - f"ConversationHandler {handler.name} " # type: ignore[attr-defined] - f"can not be persistent if dispatcher has no persistence" - ) - handler.persistence = self.persistence # type: ignore[attr-defined] - handler.conversations = ( # type: ignore[attr-defined] - self.persistence.get_conversations(handler.name) # type: ignore[attr-defined] - ) - - if group not in self.handlers: - self.handlers[group] = [] - self.groups.append(group) - self.groups = sorted(self.groups) - - self.handlers[group].append(handler) - - def remove_handler(self, handler: Handler, group: int = DEFAULT_GROUP) -> None: - """Remove a handler from the specified group. - - Args: - handler (:class:`telegram.ext.Handler`): A Handler instance. - group (:obj:`object`, optional): The group identifier. Default is 0. - - """ - if handler in self.handlers[group]: - self.handlers[group].remove(handler) - if not self.handlers[group]: - del self.handlers[group] - self.groups.remove(group) - - def update_persistence(self, update: object = None) -> None: - """Update :attr:`user_data`, :attr:`chat_data` and :attr:`bot_data` in :attr:`persistence`. - - Args: - update (:class:`telegram.Update`, optional): The update to process. If passed, only the - corresponding ``user_data`` and ``chat_data`` will be updated. - """ - with self._update_persistence_lock: - self.__update_persistence(update) - - def __update_persistence(self, update: object = None) -> None: - if self.persistence: - # We use list() here in order to decouple chat_ids from self.chat_data, as dict view - # objects will change, when the dict does and we want to loop over chat_ids - chat_ids = list(self.chat_data.keys()) - user_ids = list(self.user_data.keys()) - - if isinstance(update, Update): - if update.effective_chat: - chat_ids = [update.effective_chat.id] - else: - chat_ids = [] - if update.effective_user: - user_ids = [update.effective_user.id] - else: - user_ids = [] - - if self.persistence.store_callback_data: - self.bot = cast(telegram.ext.extbot.ExtBot, self.bot) - try: - self.persistence.update_callback_data( - self.bot.callback_data_cache.persistence_data - ) - except Exception as exc: - try: - self.dispatch_error(update, exc) - except Exception: - message = ( - 'Saving callback data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' - ) - self.logger.exception(message) - if self.persistence.store_bot_data: - try: - self.persistence.update_bot_data(self.bot_data) - except Exception as exc: - try: - self.dispatch_error(update, exc) - except Exception: - message = ( - 'Saving bot data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' - ) - self.logger.exception(message) - if self.persistence.store_chat_data: - for chat_id in chat_ids: - try: - self.persistence.update_chat_data(chat_id, self.chat_data[chat_id]) - except Exception as exc: - try: - self.dispatch_error(update, exc) - except Exception: - message = ( - 'Saving chat data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' - ) - self.logger.exception(message) - if self.persistence.store_user_data: - for user_id in user_ids: - try: - self.persistence.update_user_data(user_id, self.user_data[user_id]) - except Exception as exc: - try: - self.dispatch_error(update, exc) - except Exception: - message = ( - 'Saving user data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' - ) - self.logger.exception(message) - - def add_error_handler( - self, - callback: Callable[[object, CCT], None], - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, # pylint: disable=W0621 - ) -> None: - """Registers an error handler in the Dispatcher. This handler will receive every error - which happens in your bot. - - Note: - Attempts to add the same callback multiple times will be ignored. - - Warning: - The errors handled within these handlers won't show up in the logger, so you - need to make sure that you reraise the error. - - Args: - callback (:obj:`callable`): The callback function for this error handler. Will be - called when an error is raised. Callback signature for context based API: - - ``def callback(update: object, context: CallbackContext)`` - - The error that happened will be present in context.error. - run_async (:obj:`bool`, optional): Whether this handlers callback should be run - asynchronously using :meth:`run_async`. Defaults to :obj:`False`. - - Note: - See https://git.io/fxJuV for more info about switching to context based API. - """ - if callback in self.error_handlers: - self.logger.debug('The callback is already registered as an error handler. Ignoring.') - return - - if run_async is DEFAULT_FALSE and self.bot.defaults and self.bot.defaults.run_async: - run_async = True - - self.error_handlers[callback] = run_async - - def remove_error_handler(self, callback: Callable[[object, CCT], None]) -> None: - """Removes an error handler. - - Args: - callback (:obj:`callable`): The error handler to remove. - - """ - self.error_handlers.pop(callback, None) - - def dispatch_error( - self, update: Optional[object], error: Exception, promise: Promise = None - ) -> None: - """Dispatches an error. - - Args: - update (:obj:`object` | :class:`telegram.Update`): The update that caused the error. - error (:obj:`Exception`): The error that was raised. - promise (:class:`telegram.utils.Promise`, optional): The promise whose pooled function - raised the error. - - """ - async_args = None if not promise else promise.args - async_kwargs = None if not promise else promise.kwargs - - if self.error_handlers: - for callback, run_async in self.error_handlers.items(): # pylint: disable=W0621 - if self.use_context: - context = self.context_types.context.from_error( - update, error, self, async_args=async_args, async_kwargs=async_kwargs - ) - if run_async: - self.run_async(callback, update, context, update=update) - else: - callback(update, context) - else: - if run_async: - self.run_async(callback, self.bot, update, error, update=update) - else: - callback(self.bot, update, error) - - else: - self.logger.exception( - 'No error handlers are registered, logging exception.', exc_info=error - ) diff --git a/telegramer/include/telegram/ext/extbot.py b/telegramer/include/telegram/ext/extbot.py deleted file mode 100644 index f026191..0000000 --- a/telegramer/include/telegram/ext/extbot.py +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=E0611,E0213,E1102,C0103,E1101,R0913,R0904 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Bot with convenience extensions.""" -from copy import copy -from typing import Union, cast, List, Callable, Optional, Tuple, TypeVar, TYPE_CHECKING, Sequence - -import telegram.bot -from telegram import ( - ReplyMarkup, - Message, - InlineKeyboardMarkup, - Poll, - MessageId, - Update, - Chat, - CallbackQuery, -) - -from telegram.ext.callbackdatacache import CallbackDataCache -from telegram.utils.types import JSONDict, ODVInput, DVInput -from ..utils.helpers import DEFAULT_NONE - -if TYPE_CHECKING: - from telegram import InlineQueryResult, MessageEntity - from telegram.utils.request import Request - from .defaults import Defaults - -HandledTypes = TypeVar('HandledTypes', bound=Union[Message, CallbackQuery, Chat]) - - -class ExtBot(telegram.bot.Bot): - """This object represents a Telegram Bot with convenience extensions. - - Warning: - Not to be confused with :class:`telegram.Bot`. - - For the documentation of the arguments, methods and attributes, please see - :class:`telegram.Bot`. - - .. versionadded:: 13.6 - - Args: - defaults (:class:`telegram.ext.Defaults`, optional): An object containing default values to - be used if not set explicitly in the bot methods. - arbitrary_callback_data (:obj:`bool` | :obj:`int`, optional): Whether to - allow arbitrary objects as callback data for :class:`telegram.InlineKeyboardButton`. - Pass an integer to specify the maximum number of objects cached in memory. For more - details, please see our `wiki <https://git.io/JGBDI>`_. Defaults to :obj:`False`. - - Attributes: - arbitrary_callback_data (:obj:`bool` | :obj:`int`): Whether this bot instance - allows to use arbitrary objects as callback data for - :class:`telegram.InlineKeyboardButton`. - callback_data_cache (:class:`telegram.ext.CallbackDataCache`): The cache for objects passed - as callback data for :class:`telegram.InlineKeyboardButton`. - - """ - - __slots__ = ('arbitrary_callback_data', 'callback_data_cache') - - # The ext_bot argument is a little hack to get warnings handled correctly. - # It's not very clean, but the warnings will be dropped at some point anyway. - def __setattr__(self, key: str, value: object, ext_bot: bool = True) -> None: - if issubclass(self.__class__, ExtBot) and self.__class__ is not ExtBot: - object.__setattr__(self, key, value) - return - super().__setattr__(key, value, ext_bot=ext_bot) # type: ignore[call-arg] - - def __init__( - self, - token: str, - base_url: str = None, - base_file_url: str = None, - request: 'Request' = None, - private_key: bytes = None, - private_key_password: bytes = None, - defaults: 'Defaults' = None, - arbitrary_callback_data: Union[bool, int] = False, - ): - super().__init__( - token=token, - base_url=base_url, - base_file_url=base_file_url, - request=request, - private_key=private_key, - private_key_password=private_key_password, - ) - # We don't pass this to super().__init__ to avoid the deprecation warning - self.defaults = defaults - - # set up callback_data - if not isinstance(arbitrary_callback_data, bool): - maxsize = cast(int, arbitrary_callback_data) - self.arbitrary_callback_data = True - else: - maxsize = 1024 - self.arbitrary_callback_data = arbitrary_callback_data - self.callback_data_cache: CallbackDataCache = CallbackDataCache(bot=self, maxsize=maxsize) - - def _replace_keyboard(self, reply_markup: Optional[ReplyMarkup]) -> Optional[ReplyMarkup]: - # If the reply_markup is an inline keyboard and we allow arbitrary callback data, let the - # CallbackDataCache build a new keyboard with the data replaced. Otherwise return the input - if isinstance(reply_markup, InlineKeyboardMarkup) and self.arbitrary_callback_data: - return self.callback_data_cache.process_keyboard(reply_markup) - - return reply_markup - - def insert_callback_data(self, update: Update) -> None: - """If this bot allows for arbitrary callback data, this inserts the cached data into all - corresponding buttons within this update. - - Note: - Checks :attr:`telegram.Message.via_bot` and :attr:`telegram.Message.from_user` to check - if the reply markup (if any) was actually sent by this caches bot. If it was not, the - message will be returned unchanged. - - Note that this will fail for channel posts, as :attr:`telegram.Message.from_user` is - :obj:`None` for those! In the corresponding reply markups the callback data will be - replaced by :class:`telegram.ext.InvalidCallbackData`. - - Warning: - *In place*, i.e. the passed :class:`telegram.Message` will be changed! - - Args: - update (:class`telegram.Update`): The update. - - """ - # The only incoming updates that can directly contain a message sent by the bot itself are: - # * CallbackQueries - # * Messages where the pinned_message is sent by the bot - # * Messages where the reply_to_message is sent by the bot - # * Messages where via_bot is the bot - # Finally there is effective_chat.pinned message, but that's only returned in get_chat - if update.callback_query: - self._insert_callback_data(update.callback_query) - # elif instead of if, as effective_message includes callback_query.message - # and that has already been processed - elif update.effective_message: - self._insert_callback_data(update.effective_message) - - def _insert_callback_data(self, obj: HandledTypes) -> HandledTypes: - if not self.arbitrary_callback_data: - return obj - - if isinstance(obj, CallbackQuery): - self.callback_data_cache.process_callback_query(obj) - return obj # type: ignore[return-value] - - if isinstance(obj, Message): - if obj.reply_to_message: - # reply_to_message can't contain further reply_to_messages, so no need to check - self.callback_data_cache.process_message(obj.reply_to_message) - if obj.reply_to_message.pinned_message: - # pinned messages can't contain reply_to_message, no need to check - self.callback_data_cache.process_message(obj.reply_to_message.pinned_message) - if obj.pinned_message: - # pinned messages can't contain reply_to_message, no need to check - self.callback_data_cache.process_message(obj.pinned_message) - - # Finally, handle the message itself - self.callback_data_cache.process_message(message=obj) - return obj # type: ignore[return-value] - - if isinstance(obj, Chat) and obj.pinned_message: - self.callback_data_cache.process_message(obj.pinned_message) - - return obj - - def _message( - self, - endpoint: str, - data: JSONDict, - reply_to_message_id: int = None, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> Union[bool, Message]: - # We override this method to call self._replace_keyboard and self._insert_callback_data. - # This covers most methods that have a reply_markup - result = super()._message( - endpoint=endpoint, - data=data, - reply_to_message_id=reply_to_message_id, - disable_notification=disable_notification, - reply_markup=self._replace_keyboard(reply_markup), - allow_sending_without_reply=allow_sending_without_reply, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - if isinstance(result, Message): - self._insert_callback_data(result) - return result - - def get_updates( - self, - offset: int = None, - limit: int = 100, - timeout: float = 0, - read_latency: float = 2.0, - allowed_updates: List[str] = None, - api_kwargs: JSONDict = None, - ) -> List[Update]: - updates = super().get_updates( - offset=offset, - limit=limit, - timeout=timeout, - read_latency=read_latency, - allowed_updates=allowed_updates, - api_kwargs=api_kwargs, - ) - - for update in updates: - self.insert_callback_data(update) - - return updates - - def _effective_inline_results( # pylint: disable=R0201 - self, - results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] - ], - next_offset: str = None, - current_offset: str = None, - ) -> Tuple[Sequence['InlineQueryResult'], Optional[str]]: - """ - This method is called by Bot.answer_inline_query to build the actual results list. - Overriding this to call self._replace_keyboard suffices - """ - effective_results, next_offset = super()._effective_inline_results( - results=results, next_offset=next_offset, current_offset=current_offset - ) - - # Process arbitrary callback - if not self.arbitrary_callback_data: - return effective_results, next_offset - results = [] - for result in effective_results: - # All currently existingInlineQueryResults have a reply_markup, but future ones - # might not have. Better be save than sorry - if not hasattr(result, 'reply_markup'): - results.append(result) - else: - # We build a new result in case the user wants to use the same object in - # different places - new_result = copy(result) - markup = self._replace_keyboard(result.reply_markup) # type: ignore[attr-defined] - new_result.reply_markup = markup - results.append(new_result) - - return results, next_offset - - def stop_poll( - self, - chat_id: Union[int, str], - message_id: int, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Poll: - # We override this method to call self._replace_keyboard - return super().stop_poll( - chat_id=chat_id, - message_id=message_id, - reply_markup=self._replace_keyboard(reply_markup), - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def copy_message( - self, - chat_id: Union[int, str], - from_chat_id: Union[str, int], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> MessageId: - # We override this method to call self._replace_keyboard - return super().copy_message( - chat_id=chat_id, - from_chat_id=from_chat_id, - message_id=message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=self._replace_keyboard(reply_markup), - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def get_chat( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Chat: - # We override this method to call self._insert_callback_data - result = super().get_chat(chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs) - return self._insert_callback_data(result) - - # updated camelCase aliases - getChat = get_chat - """Alias for :meth:`get_chat`""" - copyMessage = copy_message - """Alias for :meth:`copy_message`""" - getUpdates = get_updates - """Alias for :meth:`get_updates`""" - stopPoll = stop_poll - """Alias for :meth:`stop_poll`""" diff --git a/telegramer/include/telegram/ext/filters.py b/telegramer/include/telegram/ext/filters.py deleted file mode 100644 index 519c73a..0000000 --- a/telegramer/include/telegram/ext/filters.py +++ /dev/null @@ -1,2342 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0112, C0103, W0221 -"""This module contains the Filters for use with the MessageHandler class.""" - -import re -import warnings - -from abc import ABC, abstractmethod -from sys import version_info as py_ver -from threading import Lock -from typing import ( - Dict, - FrozenSet, - List, - Match, - Optional, - Pattern, - Set, - Tuple, - Union, - cast, - NoReturn, -) - -from telegram import Chat, Message, MessageEntity, Update, User - -__all__ = [ - 'Filters', - 'BaseFilter', - 'MessageFilter', - 'UpdateFilter', - 'InvertedFilter', - 'MergedFilter', - 'XORFilter', -] - -from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated -from telegram.utils.types import SLT - -DataDict = Dict[str, list] - - -class BaseFilter(ABC): - """Base class for all Filters. - - Filters subclassing from this class can combined using bitwise operators: - - And: - - >>> (Filters.text & Filters.entity(MENTION)) - - Or: - - >>> (Filters.audio | Filters.video) - - Exclusive Or: - - >>> (Filters.regex('To Be') ^ Filters.regex('Not 2B')) - - Not: - - >>> ~ Filters.command - - Also works with more than two filters: - - >>> (Filters.text & (Filters.entity(URL) | Filters.entity(TEXT_LINK))) - >>> Filters.text & (~ Filters.forwarded) - - Note: - Filters use the same short circuiting logic as python's `and`, `or` and `not`. - This means that for example: - - >>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)') - - With ``message.text == x``, will only ever return the matches for the first filter, - since the second one is never evaluated. - - - If you want to create your own filters create a class inheriting from either - :class:`MessageFilter` or :class:`UpdateFilter` and implement a :meth:`filter` method that - returns a boolean: :obj:`True` if the message should be - handled, :obj:`False` otherwise. - Note that the filters work only as class instances, not - actual class objects (so remember to - initialize your filter classes). - - By default the filters name (what will get printed when converted to a string for display) - will be the class name. If you want to overwrite this assign a better name to the :attr:`name` - class variable. - - Attributes: - name (:obj:`str`): Name for this filter. Defaults to the type of filter. - data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should - return a dict with lists. The dict will be merged with - :class:`telegram.ext.CallbackContext`'s internal dict in most cases - (depends on the handler). - """ - - if py_ver < (3, 7): - __slots__ = ('_name', '_data_filter') - else: - __slots__ = ('_name', '_data_filter', '__dict__') # type: ignore[assignment] - - def __new__(cls, *args: object, **kwargs: object) -> 'BaseFilter': # pylint: disable=W0613 - instance = super().__new__(cls) - instance._name = None - instance._data_filter = False - - return instance - - @abstractmethod - def __call__(self, update: Update) -> Optional[Union[bool, DataDict]]: - ... - - def __and__(self, other: 'BaseFilter') -> 'BaseFilter': - return MergedFilter(self, and_filter=other) - - def __or__(self, other: 'BaseFilter') -> 'BaseFilter': - return MergedFilter(self, or_filter=other) - - def __xor__(self, other: 'BaseFilter') -> 'BaseFilter': - return XORFilter(self, other) - - def __invert__(self) -> 'BaseFilter': - return InvertedFilter(self) - - def __setattr__(self, key: str, value: object) -> None: - # Allow setting custom attributes w/o warning for user defined custom filters. - # To differentiate between a custom and a PTB filter, we use this hacky but - # simple way of checking the module name where the class is defined from. - if ( - issubclass(self.__class__, (UpdateFilter, MessageFilter)) - and self.__class__.__module__ != __name__ - ): # __name__ is telegram.ext.filters - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - - @property - def data_filter(self) -> bool: - return self._data_filter - - @data_filter.setter - def data_filter(self, value: bool) -> None: - self._data_filter = value - - @property - def name(self) -> Optional[str]: - return self._name - - @name.setter - def name(self, name: Optional[str]) -> None: - self._name = name # pylint: disable=E0237 - - def __repr__(self) -> str: - # We do this here instead of in a __init__ so filter don't have to call __init__ or super() - if self.name is None: - self.name = self.__class__.__name__ - return self.name - - -class MessageFilter(BaseFilter): - """Base class for all Message Filters. In contrast to :class:`UpdateFilter`, the object passed - to :meth:`filter` is ``update.effective_message``. - - Please see :class:`telegram.ext.filters.BaseFilter` for details on how to create custom - filters. - - Attributes: - name (:obj:`str`): Name for this filter. Defaults to the type of filter. - data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should - return a dict with lists. The dict will be merged with - :class:`telegram.ext.CallbackContext`'s internal dict in most cases - (depends on the handler). - - """ - - __slots__ = () - - def __call__(self, update: Update) -> Optional[Union[bool, DataDict]]: - return self.filter(update.effective_message) - - @abstractmethod - def filter(self, message: Message) -> Optional[Union[bool, DataDict]]: - """This method must be overwritten. - - Args: - message (:class:`telegram.Message`): The message that is tested. - - Returns: - :obj:`dict` or :obj:`bool` - - """ - - -class UpdateFilter(BaseFilter): - """Base class for all Update Filters. In contrast to :class:`MessageFilter`, the object - passed to :meth:`filter` is ``update``, which allows to create filters like - :attr:`Filters.update.edited_message`. - - Please see :class:`telegram.ext.filters.BaseFilter` for details on how to create custom - filters. - - Attributes: - name (:obj:`str`): Name for this filter. Defaults to the type of filter. - data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should - return a dict with lists. The dict will be merged with - :class:`telegram.ext.CallbackContext`'s internal dict in most cases - (depends on the handler). - - """ - - __slots__ = () - - def __call__(self, update: Update) -> Optional[Union[bool, DataDict]]: - return self.filter(update) - - @abstractmethod - def filter(self, update: Update) -> Optional[Union[bool, DataDict]]: - """This method must be overwritten. - - Args: - update (:class:`telegram.Update`): The update that is tested. - - Returns: - :obj:`dict` or :obj:`bool`. - - """ - - -class InvertedFilter(UpdateFilter): - """Represents a filter that has been inverted. - - Args: - f: The filter to invert. - - """ - - __slots__ = ('f',) - - def __init__(self, f: BaseFilter): - self.f = f - - def filter(self, update: Update) -> bool: - return not bool(self.f(update)) - - @property - def name(self) -> str: - return f"<inverted {self.f}>" - - @name.setter - def name(self, name: str) -> NoReturn: - raise RuntimeError('Cannot set name for InvertedFilter') - - -class MergedFilter(UpdateFilter): - """Represents a filter consisting of two other filters. - - Args: - base_filter: Filter 1 of the merged filter. - and_filter: Optional filter to "and" with base_filter. Mutually exclusive with or_filter. - or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter. - - """ - - __slots__ = ('base_filter', 'and_filter', 'or_filter') - - def __init__( - self, base_filter: BaseFilter, and_filter: BaseFilter = None, or_filter: BaseFilter = None - ): - self.base_filter = base_filter - if self.base_filter.data_filter: - self.data_filter = True - self.and_filter = and_filter - if ( - self.and_filter - and not isinstance(self.and_filter, bool) - and self.and_filter.data_filter - ): - self.data_filter = True - self.or_filter = or_filter - if self.or_filter and not isinstance(self.and_filter, bool) and self.or_filter.data_filter: - self.data_filter = True - - @staticmethod - def _merge(base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> DataDict: - base = base_output if isinstance(base_output, dict) else {} - comp = comp_output if isinstance(comp_output, dict) else {} - for k in comp.keys(): - # Make sure comp values are lists - comp_value = comp[k] if isinstance(comp[k], list) else [] - try: - # If base is a list then merge - if isinstance(base[k], list): - base[k] += comp_value - else: - base[k] = [base[k]] + comp_value - except KeyError: - base[k] = comp_value - return base - - def filter(self, update: Update) -> Union[bool, DataDict]: # pylint: disable=R0911 - base_output = self.base_filter(update) - # We need to check if the filters are data filters and if so return the merged data. - # If it's not a data filter or an or_filter but no matches return bool - if self.and_filter: - # And filter needs to short circuit if base is falsey - if base_output: - comp_output = self.and_filter(update) - if comp_output: - if self.data_filter: - merged = self._merge(base_output, comp_output) - if merged: - return merged - return True - elif self.or_filter: - # Or filter needs to short circuit if base is truthey - if base_output: - if self.data_filter: - return base_output - return True - - comp_output = self.or_filter(update) - if comp_output: - if self.data_filter: - return comp_output - return True - return False - - @property - def name(self) -> str: - return ( - f"<{self.base_filter} {'and' if self.and_filter else 'or'} " - f"{self.and_filter or self.or_filter}>" - ) - - @name.setter - def name(self, name: str) -> NoReturn: - raise RuntimeError('Cannot set name for MergedFilter') - - -class XORFilter(UpdateFilter): - """Convenience filter acting as wrapper for :class:`MergedFilter` representing the an XOR gate - for two filters. - - Args: - base_filter: Filter 1 of the merged filter. - xor_filter: Filter 2 of the merged filter. - - """ - - __slots__ = ('base_filter', 'xor_filter', 'merged_filter') - - def __init__(self, base_filter: BaseFilter, xor_filter: BaseFilter): - self.base_filter = base_filter - self.xor_filter = xor_filter - self.merged_filter = (base_filter & ~xor_filter) | (~base_filter & xor_filter) - - def filter(self, update: Update) -> Optional[Union[bool, DataDict]]: - return self.merged_filter(update) - - @property - def name(self) -> str: - return f'<{self.base_filter} xor {self.xor_filter}>' - - @name.setter - def name(self, name: str) -> NoReturn: - raise RuntimeError('Cannot set name for XORFilter') - - -class _DiceEmoji(MessageFilter): - __slots__ = ('emoji',) - - def __init__(self, emoji: str = None, name: str = None): - self.name = f'Filters.dice.{name}' if name else 'Filters.dice' - self.emoji = emoji - - class _DiceValues(MessageFilter): - __slots__ = ('values', 'emoji') - - def __init__( - self, - values: SLT[int], - name: str, - emoji: str = None, - ): - self.values = [values] if isinstance(values, int) else values - self.emoji = emoji - self.name = f'{name}({values})' - - def filter(self, message: Message) -> bool: - if message.dice and message.dice.value in self.values: - if self.emoji: - return message.dice.emoji == self.emoji - return True - return False - - def __call__( # type: ignore[override] - self, update: Union[Update, List[int], Tuple[int]] - ) -> Union[bool, '_DiceValues']: - if isinstance(update, Update): - return self.filter(update.effective_message) - return self._DiceValues(update, self.name, emoji=self.emoji) - - def filter(self, message: Message) -> bool: - if bool(message.dice): - if self.emoji: - return message.dice.emoji == self.emoji - return True - return False - - -class Filters: - """Predefined filters for use as the ``filter`` argument of - :class:`telegram.ext.MessageHandler`. - - Examples: - Use ``MessageHandler(Filters.video, callback_method)`` to filter all video - messages. Use ``MessageHandler(Filters.contact, callback_method)`` for all contacts. etc. - - """ - - __slots__ = ('__dict__',) - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - class _All(MessageFilter): - __slots__ = () - name = 'Filters.all' - - def filter(self, message: Message) -> bool: - return True - - all = _All() - """All Messages.""" - - class _Text(MessageFilter): - __slots__ = () - name = 'Filters.text' - - class _TextStrings(MessageFilter): - __slots__ = ('strings',) - - def __init__(self, strings: Union[List[str], Tuple[str]]): - self.strings = strings - self.name = f'Filters.text({strings})' - - def filter(self, message: Message) -> bool: - if message.text: - return message.text in self.strings - return False - - def __call__( # type: ignore[override] - self, update: Union[Update, List[str], Tuple[str]] - ) -> Union[bool, '_TextStrings']: - if isinstance(update, Update): - return self.filter(update.effective_message) - return self._TextStrings(update) - - def filter(self, message: Message) -> bool: - return bool(message.text) - - text = _Text() - """Text Messages. If a list of strings is passed, it filters messages to only allow those - whose text is appearing in the given list. - - Examples: - To allow any text message, simply use - ``MessageHandler(Filters.text, callback_method)``. - - A simple use case for passing a list is to allow only messages that were sent by a - custom :class:`telegram.ReplyKeyboardMarkup`:: - - buttons = ['Start', 'Settings', 'Back'] - markup = ReplyKeyboardMarkup.from_column(buttons) - ... - MessageHandler(Filters.text(buttons), callback_method) - - Note: - * Dice messages don't have text. If you want to filter either text or dice messages, use - ``Filters.text | Filters.dice``. - * Messages containing a command are accepted by this filter. Use - ``Filters.text & (~Filters.command)``, if you want to filter only text messages without - commands. - - Args: - update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which messages to allow. Only - exact matches are allowed. If not specified, will allow any text message. - """ - - class _Caption(MessageFilter): - __slots__ = () - name = 'Filters.caption' - - class _CaptionStrings(MessageFilter): - __slots__ = ('strings',) - - def __init__(self, strings: Union[List[str], Tuple[str]]): - self.strings = strings - self.name = f'Filters.caption({strings})' - - def filter(self, message: Message) -> bool: - if message.caption: - return message.caption in self.strings - return False - - def __call__( # type: ignore[override] - self, update: Union[Update, List[str], Tuple[str]] - ) -> Union[bool, '_CaptionStrings']: - if isinstance(update, Update): - return self.filter(update.effective_message) - return self._CaptionStrings(update) - - def filter(self, message: Message) -> bool: - return bool(message.caption) - - caption = _Caption() - """Messages with a caption. If a list of strings is passed, it filters messages to only - allow those whose caption is appearing in the given list. - - Examples: - ``MessageHandler(Filters.caption, callback_method)`` - - Args: - update (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which captions to allow. Only - exact matches are allowed. If not specified, will allow any message with a caption. - """ - - class _Command(MessageFilter): - __slots__ = () - name = 'Filters.command' - - class _CommandOnlyStart(MessageFilter): - __slots__ = ('only_start',) - - def __init__(self, only_start: bool): - self.only_start = only_start - self.name = f'Filters.command({only_start})' - - def filter(self, message: Message) -> bool: - return bool( - message.entities - and any(e.type == MessageEntity.BOT_COMMAND for e in message.entities) - ) - - def __call__( # type: ignore[override] - self, update: Union[bool, Update] - ) -> Union[bool, '_CommandOnlyStart']: - if isinstance(update, Update): - return self.filter(update.effective_message) - return self._CommandOnlyStart(update) - - def filter(self, message: Message) -> bool: - return bool( - message.entities - and message.entities[0].type == MessageEntity.BOT_COMMAND - and message.entities[0].offset == 0 - ) - - command = _Command() - """ - Messages with a :attr:`telegram.MessageEntity.BOT_COMMAND`. By default only allows - messages `starting` with a bot command. Pass :obj:`False` to also allow messages that contain a - bot command `anywhere` in the text. - - Examples:: - - MessageHandler(Filters.command, command_at_start_callback) - MessageHandler(Filters.command(False), command_anywhere_callback) - - Note: - ``Filters.text`` also accepts messages containing a command. - - Args: - update (:obj:`bool`, optional): Whether to only allow messages that `start` with a bot - command. Defaults to :obj:`True`. - """ - - class regex(MessageFilter): - """ - Filters updates by searching for an occurrence of ``pattern`` in the message text. - The ``re.search()`` function is used to determine whether an update should be filtered. - - Refer to the documentation of the ``re`` module for more information. - - To get the groups and groupdict matched, see :attr:`telegram.ext.CallbackContext.matches`. - - Examples: - Use ``MessageHandler(Filters.regex(r'help'), callback)`` to capture all messages that - contain the word 'help'. You can also use - ``MessageHandler(Filters.regex(re.compile(r'help', re.IGNORECASE)), callback)`` if - you want your pattern to be case insensitive. This approach is recommended - if you need to specify flags on your pattern. - - Note: - Filters use the same short circuiting logic as python's `and`, `or` and `not`. - This means that for example: - - >>> Filters.regex(r'(a?x)') | Filters.regex(r'(b?x)') - - With a message.text of `x`, will only ever return the matches for the first filter, - since the second one is never evaluated. - - Args: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - """ - - __slots__ = ('pattern',) - data_filter = True - - def __init__(self, pattern: Union[str, Pattern]): - if isinstance(pattern, str): - pattern = re.compile(pattern) - pattern = cast(Pattern, pattern) - self.pattern: Pattern = pattern - self.name = f'Filters.regex({self.pattern})' - - def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]: - """""" # remove method from docs - if message.text: - match = self.pattern.search(message.text) - if match: - return {'matches': [match]} - return {} - - class caption_regex(MessageFilter): - """ - Filters updates by searching for an occurrence of ``pattern`` in the message caption. - - This filter works similarly to :class:`Filters.regex`, with the only exception being that - it applies to the message caption instead of the text. - - Examples: - Use ``MessageHandler(Filters.photo & Filters.caption_regex(r'help'), callback)`` - to capture all photos with caption containing the word 'help'. - - Note: - This filter will not work on simple text messages, but only on media with caption. - - Args: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - """ - - __slots__ = ('pattern',) - data_filter = True - - def __init__(self, pattern: Union[str, Pattern]): - if isinstance(pattern, str): - pattern = re.compile(pattern) - pattern = cast(Pattern, pattern) - self.pattern: Pattern = pattern - self.name = f'Filters.caption_regex({self.pattern})' - - def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]: - """""" # remove method from docs - if message.caption: - match = self.pattern.search(message.caption) - if match: - return {'matches': [match]} - return {} - - class _Reply(MessageFilter): - __slots__ = () - name = 'Filters.reply' - - def filter(self, message: Message) -> bool: - return bool(message.reply_to_message) - - reply = _Reply() - """Messages that are a reply to another message.""" - - class _Audio(MessageFilter): - __slots__ = () - name = 'Filters.audio' - - def filter(self, message: Message) -> bool: - return bool(message.audio) - - audio = _Audio() - """Messages that contain :class:`telegram.Audio`.""" - - class _Document(MessageFilter): - __slots__ = () - name = 'Filters.document' - - class category(MessageFilter): - """Filters documents by their category in the mime-type attribute. - - Note: - This Filter only filters by the mime_type of the document, - it doesn't check the validity of the document. - The user can manipulate the mime-type of a message and - send media with wrong types that don't fit to this handler. - - Example: - Filters.document.category('audio/') returns :obj:`True` for all types - of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. - """ - - __slots__ = ('_category',) - - def __init__(self, category: Optional[str]): - """Initialize the category you want to filter - - Args: - category (str, optional): category of the media you want to filter - """ - self._category = category - self.name = f"Filters.document.category('{self._category}')" - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - if message.document: - return message.document.mime_type.startswith(self._category) - return False - - application = category('application/') - audio = category('audio/') - image = category('image/') - video = category('video/') - text = category('text/') - - class mime_type(MessageFilter): - """This Filter filters documents by their mime-type attribute - - Note: - This Filter only filters by the mime_type of the document, - it doesn't check the validity of document. - The user can manipulate the mime-type of a message and - send media with wrong types that don't fit to this handler. - - Example: - ``Filters.document.mime_type('audio/mpeg')`` filters all audio in mp3 format. - """ - - __slots__ = ('mimetype',) - - def __init__(self, mimetype: Optional[str]): - self.mimetype = mimetype - self.name = f"Filters.document.mime_type('{self.mimetype}')" - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - if message.document: - return message.document.mime_type == self.mimetype - return False - - apk = mime_type('application/vnd.android.package-archive') - doc = mime_type('application/msword') - docx = mime_type('application/vnd.openxmlformats-officedocument.wordprocessingml.document') - exe = mime_type('application/x-ms-dos-executable') - gif = mime_type('video/mp4') - jpg = mime_type('image/jpeg') - mp3 = mime_type('audio/mpeg') - pdf = mime_type('application/pdf') - py = mime_type('text/x-python') - svg = mime_type('image/svg+xml') - txt = mime_type('text/plain') - targz = mime_type('application/x-compressed-tar') - wav = mime_type('audio/x-wav') - xml = mime_type('application/xml') - zip = mime_type('application/zip') - - class file_extension(MessageFilter): - """This filter filters documents by their file ending/extension. - - Note: - * This Filter only filters by the file ending/extension of the document, - it doesn't check the validity of document. - * The user can manipulate the file extension of a document and - send media with wrong types that don't fit to this handler. - * Case insensitive by default, - you may change this with the flag ``case_sensitive=True``. - * Extension should be passed without leading dot - unless it's a part of the extension. - * Pass :obj:`None` to filter files with no extension, - i.e. without a dot in the filename. - - Example: - * ``Filters.document.file_extension("jpg")`` - filters files with extension ``".jpg"``. - * ``Filters.document.file_extension(".jpg")`` - filters files with extension ``"..jpg"``. - * ``Filters.document.file_extension("Dockerfile", case_sensitive=True)`` - filters files with extension ``".Dockerfile"`` minding the case. - * ``Filters.document.file_extension(None)`` - filters files without a dot in the filename. - """ - - __slots__ = ('_file_extension', 'is_case_sensitive') - - def __init__(self, file_extension: Optional[str], case_sensitive: bool = False): - """Initialize the extension you want to filter. - - Args: - file_extension (:obj:`str` | :obj:`None`): - media file extension you want to filter. - case_sensitive (:obj:bool, optional): - pass :obj:`True` to make the filter case sensitive. - Default: :obj:`False`. - """ - self.is_case_sensitive = case_sensitive - if file_extension is None: - self._file_extension = None - self.name = "Filters.document.file_extension(None)" - elif self.is_case_sensitive: - self._file_extension = f".{file_extension}" - self.name = ( - f"Filters.document.file_extension({file_extension!r}," - " case_sensitive=True)" - ) - else: - self._file_extension = f".{file_extension}".lower() - self.name = f"Filters.document.file_extension({file_extension.lower()!r})" - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - if message.document is None: - return False - if self._file_extension is None: - return "." not in message.document.file_name - if self.is_case_sensitive: - filename = message.document.file_name - else: - filename = message.document.file_name.lower() - return filename.endswith(self._file_extension) - - def filter(self, message: Message) -> bool: - return bool(message.document) - - document = _Document() - """ - Subset for messages containing a document/file. - - Examples: - Use these filters like: ``Filters.document.mp3``, - ``Filters.document.mime_type("text/plain")`` etc. Or use just - ``Filters.document`` for all document messages. - - Attributes: - category: Filters documents by their category in the mime-type attribute - - Note: - This Filter only filters by the mime_type of the document, - it doesn't check the validity of the document. - The user can manipulate the mime-type of a message and - send media with wrong types that don't fit to this handler. - - Example: - ``Filters.document.category('audio/')`` filters all types - of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. - application: Same as ``Filters.document.category("application")``. - audio: Same as ``Filters.document.category("audio")``. - image: Same as ``Filters.document.category("image")``. - video: Same as ``Filters.document.category("video")``. - text: Same as ``Filters.document.category("text")``. - mime_type: Filters documents by their mime-type attribute - - Note: - This Filter only filters by the mime_type of the document, - it doesn't check the validity of document. - - The user can manipulate the mime-type of a message and - send media with wrong types that don't fit to this handler. - - Example: - ``Filters.document.mime_type('audio/mpeg')`` filters all audio in mp3 format. - apk: Same as ``Filters.document.mime_type("application/vnd.android.package-archive")``. - doc: Same as ``Filters.document.mime_type("application/msword")``. - docx: Same as ``Filters.document.mime_type("application/vnd.openxmlformats-\ -officedocument.wordprocessingml.document")``. - exe: Same as ``Filters.document.mime_type("application/x-ms-dos-executable")``. - gif: Same as ``Filters.document.mime_type("video/mp4")``. - jpg: Same as ``Filters.document.mime_type("image/jpeg")``. - mp3: Same as ``Filters.document.mime_type("audio/mpeg")``. - pdf: Same as ``Filters.document.mime_type("application/pdf")``. - py: Same as ``Filters.document.mime_type("text/x-python")``. - svg: Same as ``Filters.document.mime_type("image/svg+xml")``. - txt: Same as ``Filters.document.mime_type("text/plain")``. - targz: Same as ``Filters.document.mime_type("application/x-compressed-tar")``. - wav: Same as ``Filters.document.mime_type("audio/x-wav")``. - xml: Same as ``Filters.document.mime_type("application/xml")``. - zip: Same as ``Filters.document.mime_type("application/zip")``. - file_extension: This filter filters documents by their file ending/extension. - - Note: - * This Filter only filters by the file ending/extension of the document, - it doesn't check the validity of document. - * The user can manipulate the file extension of a document and - send media with wrong types that don't fit to this handler. - * Case insensitive by default, - you may change this with the flag ``case_sensitive=True``. - * Extension should be passed without leading dot - unless it's a part of the extension. - * Pass :obj:`None` to filter files with no extension, - i.e. without a dot in the filename. - - Example: - * ``Filters.document.file_extension("jpg")`` - filters files with extension ``".jpg"``. - * ``Filters.document.file_extension(".jpg")`` - filters files with extension ``"..jpg"``. - * ``Filters.document.file_extension("Dockerfile", case_sensitive=True)`` - filters files with extension ``".Dockerfile"`` minding the case. - * ``Filters.document.file_extension(None)`` - filters files without a dot in the filename. - """ - - class _Animation(MessageFilter): - __slots__ = () - name = 'Filters.animation' - - def filter(self, message: Message) -> bool: - return bool(message.animation) - - animation = _Animation() - """Messages that contain :class:`telegram.Animation`.""" - - class _Photo(MessageFilter): - __slots__ = () - name = 'Filters.photo' - - def filter(self, message: Message) -> bool: - return bool(message.photo) - - photo = _Photo() - """Messages that contain :class:`telegram.PhotoSize`.""" - - class _Sticker(MessageFilter): - __slots__ = () - name = 'Filters.sticker' - - def filter(self, message: Message) -> bool: - return bool(message.sticker) - - sticker = _Sticker() - """Messages that contain :class:`telegram.Sticker`.""" - - class _Video(MessageFilter): - __slots__ = () - name = 'Filters.video' - - def filter(self, message: Message) -> bool: - return bool(message.video) - - video = _Video() - """Messages that contain :class:`telegram.Video`.""" - - class _Voice(MessageFilter): - __slots__ = () - name = 'Filters.voice' - - def filter(self, message: Message) -> bool: - return bool(message.voice) - - voice = _Voice() - """Messages that contain :class:`telegram.Voice`.""" - - class _VideoNote(MessageFilter): - __slots__ = () - name = 'Filters.video_note' - - def filter(self, message: Message) -> bool: - return bool(message.video_note) - - video_note = _VideoNote() - """Messages that contain :class:`telegram.VideoNote`.""" - - class _Contact(MessageFilter): - __slots__ = () - name = 'Filters.contact' - - def filter(self, message: Message) -> bool: - return bool(message.contact) - - contact = _Contact() - """Messages that contain :class:`telegram.Contact`.""" - - class _Location(MessageFilter): - __slots__ = () - name = 'Filters.location' - - def filter(self, message: Message) -> bool: - return bool(message.location) - - location = _Location() - """Messages that contain :class:`telegram.Location`.""" - - class _Venue(MessageFilter): - __slots__ = () - name = 'Filters.venue' - - def filter(self, message: Message) -> bool: - return bool(message.venue) - - venue = _Venue() - """Messages that contain :class:`telegram.Venue`.""" - - class _StatusUpdate(UpdateFilter): - """Subset for messages containing a status update. - - Examples: - Use these filters like: ``Filters.status_update.new_chat_members`` etc. Or use just - ``Filters.status_update`` for all status update messages. - - """ - - __slots__ = () - - class _NewChatMembers(MessageFilter): - __slots__ = () - name = 'Filters.status_update.new_chat_members' - - def filter(self, message: Message) -> bool: - return bool(message.new_chat_members) - - new_chat_members = _NewChatMembers() - """Messages that contain :attr:`telegram.Message.new_chat_members`.""" - - class _LeftChatMember(MessageFilter): - __slots__ = () - name = 'Filters.status_update.left_chat_member' - - def filter(self, message: Message) -> bool: - return bool(message.left_chat_member) - - left_chat_member = _LeftChatMember() - """Messages that contain :attr:`telegram.Message.left_chat_member`.""" - - class _NewChatTitle(MessageFilter): - __slots__ = () - name = 'Filters.status_update.new_chat_title' - - def filter(self, message: Message) -> bool: - return bool(message.new_chat_title) - - new_chat_title = _NewChatTitle() - """Messages that contain :attr:`telegram.Message.new_chat_title`.""" - - class _NewChatPhoto(MessageFilter): - __slots__ = () - name = 'Filters.status_update.new_chat_photo' - - def filter(self, message: Message) -> bool: - return bool(message.new_chat_photo) - - new_chat_photo = _NewChatPhoto() - """Messages that contain :attr:`telegram.Message.new_chat_photo`.""" - - class _DeleteChatPhoto(MessageFilter): - __slots__ = () - name = 'Filters.status_update.delete_chat_photo' - - def filter(self, message: Message) -> bool: - return bool(message.delete_chat_photo) - - delete_chat_photo = _DeleteChatPhoto() - """Messages that contain :attr:`telegram.Message.delete_chat_photo`.""" - - class _ChatCreated(MessageFilter): - __slots__ = () - name = 'Filters.status_update.chat_created' - - def filter(self, message: Message) -> bool: - return bool( - message.group_chat_created - or message.supergroup_chat_created - or message.channel_chat_created - ) - - chat_created = _ChatCreated() - """Messages that contain :attr:`telegram.Message.group_chat_created`, - :attr: `telegram.Message.supergroup_chat_created` or - :attr: `telegram.Message.channel_chat_created`.""" - - class _MessageAutoDeleteTimerChanged(MessageFilter): - __slots__ = () - name = 'MessageAutoDeleteTimerChanged' - - def filter(self, message: Message) -> bool: - return bool(message.message_auto_delete_timer_changed) - - message_auto_delete_timer_changed = _MessageAutoDeleteTimerChanged() - """Messages that contain :attr:`message_auto_delete_timer_changed`""" - - class _Migrate(MessageFilter): - __slots__ = () - name = 'Filters.status_update.migrate' - - def filter(self, message: Message) -> bool: - return bool(message.migrate_from_chat_id or message.migrate_to_chat_id) - - migrate = _Migrate() - """Messages that contain :attr:`telegram.Message.migrate_from_chat_id` or - :attr:`telegram.Message.migrate_to_chat_id`.""" - - class _PinnedMessage(MessageFilter): - __slots__ = () - name = 'Filters.status_update.pinned_message' - - def filter(self, message: Message) -> bool: - return bool(message.pinned_message) - - pinned_message = _PinnedMessage() - """Messages that contain :attr:`telegram.Message.pinned_message`.""" - - class _ConnectedWebsite(MessageFilter): - __slots__ = () - name = 'Filters.status_update.connected_website' - - def filter(self, message: Message) -> bool: - return bool(message.connected_website) - - connected_website = _ConnectedWebsite() - """Messages that contain :attr:`telegram.Message.connected_website`.""" - - class _ProximityAlertTriggered(MessageFilter): - __slots__ = () - name = 'Filters.status_update.proximity_alert_triggered' - - def filter(self, message: Message) -> bool: - return bool(message.proximity_alert_triggered) - - proximity_alert_triggered = _ProximityAlertTriggered() - """Messages that contain :attr:`telegram.Message.proximity_alert_triggered`.""" - - class _VoiceChatScheduled(MessageFilter): - __slots__ = () - name = 'Filters.status_update.voice_chat_scheduled' - - def filter(self, message: Message) -> bool: - return bool(message.voice_chat_scheduled) - - voice_chat_scheduled = _VoiceChatScheduled() - """Messages that contain :attr:`telegram.Message.voice_chat_scheduled`.""" - - class _VoiceChatStarted(MessageFilter): - __slots__ = () - name = 'Filters.status_update.voice_chat_started' - - def filter(self, message: Message) -> bool: - return bool(message.voice_chat_started) - - voice_chat_started = _VoiceChatStarted() - """Messages that contain :attr:`telegram.Message.voice_chat_started`.""" - - class _VoiceChatEnded(MessageFilter): - __slots__ = () - name = 'Filters.status_update.voice_chat_ended' - - def filter(self, message: Message) -> bool: - return bool(message.voice_chat_ended) - - voice_chat_ended = _VoiceChatEnded() - """Messages that contain :attr:`telegram.Message.voice_chat_ended`.""" - - class _VoiceChatParticipantsInvited(MessageFilter): - __slots__ = () - name = 'Filters.status_update.voice_chat_participants_invited' - - def filter(self, message: Message) -> bool: - return bool(message.voice_chat_participants_invited) - - voice_chat_participants_invited = _VoiceChatParticipantsInvited() - """Messages that contain :attr:`telegram.Message.voice_chat_participants_invited`.""" - - name = 'Filters.status_update' - - def filter(self, message: Update) -> bool: - return bool( - self.new_chat_members(message) - or self.left_chat_member(message) - or self.new_chat_title(message) - or self.new_chat_photo(message) - or self.delete_chat_photo(message) - or self.chat_created(message) - or self.message_auto_delete_timer_changed(message) - or self.migrate(message) - or self.pinned_message(message) - or self.connected_website(message) - or self.proximity_alert_triggered(message) - or self.voice_chat_scheduled(message) - or self.voice_chat_started(message) - or self.voice_chat_ended(message) - or self.voice_chat_participants_invited(message) - ) - - status_update = _StatusUpdate() - """Subset for messages containing a status update. - - Examples: - Use these filters like: ``Filters.status_update.new_chat_members`` etc. Or use just - ``Filters.status_update`` for all status update messages. - - Attributes: - chat_created: Messages that contain - :attr:`telegram.Message.group_chat_created`, - :attr:`telegram.Message.supergroup_chat_created` or - :attr:`telegram.Message.channel_chat_created`. - connected_website: Messages that contain - :attr:`telegram.Message.connected_website`. - delete_chat_photo: Messages that contain - :attr:`telegram.Message.delete_chat_photo`. - left_chat_member: Messages that contain - :attr:`telegram.Message.left_chat_member`. - migrate: Messages that contain - :attr:`telegram.Message.migrate_to_chat_id` or - :attr:`telegram.Message.migrate_from_chat_id`. - new_chat_members: Messages that contain - :attr:`telegram.Message.new_chat_members`. - new_chat_photo: Messages that contain - :attr:`telegram.Message.new_chat_photo`. - new_chat_title: Messages that contain - :attr:`telegram.Message.new_chat_title`. - message_auto_delete_timer_changed: Messages that contain - :attr:`message_auto_delete_timer_changed`. - - .. versionadded:: 13.4 - pinned_message: Messages that contain - :attr:`telegram.Message.pinned_message`. - proximity_alert_triggered: Messages that contain - :attr:`telegram.Message.proximity_alert_triggered`. - voice_chat_scheduled: Messages that contain - :attr:`telegram.Message.voice_chat_scheduled`. - - .. versionadded:: 13.5 - voice_chat_started: Messages that contain - :attr:`telegram.Message.voice_chat_started`. - - .. versionadded:: 13.4 - voice_chat_ended: Messages that contain - :attr:`telegram.Message.voice_chat_ended`. - - .. versionadded:: 13.4 - voice_chat_participants_invited: Messages that contain - :attr:`telegram.Message.voice_chat_participants_invited`. - - .. versionadded:: 13.4 - - """ - - class _Forwarded(MessageFilter): - __slots__ = () - name = 'Filters.forwarded' - - def filter(self, message: Message) -> bool: - return bool(message.forward_date) - - forwarded = _Forwarded() - """Messages that are forwarded.""" - - class _Game(MessageFilter): - __slots__ = () - name = 'Filters.game' - - def filter(self, message: Message) -> bool: - return bool(message.game) - - game = _Game() - """Messages that contain :class:`telegram.Game`.""" - - class entity(MessageFilter): - """ - Filters messages to only allow those which have a :class:`telegram.MessageEntity` - where their `type` matches `entity_type`. - - Examples: - Example ``MessageHandler(Filters.entity("hashtag"), callback_method)`` - - Args: - entity_type: Entity type to check for. All types can be found as constants - in :class:`telegram.MessageEntity`. - - """ - - __slots__ = ('entity_type',) - - def __init__(self, entity_type: str): - self.entity_type = entity_type - self.name = f'Filters.entity({self.entity_type})' - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - return any(entity.type == self.entity_type for entity in message.entities) - - class caption_entity(MessageFilter): - """ - Filters media messages to only allow those which have a :class:`telegram.MessageEntity` - where their `type` matches `entity_type`. - - Examples: - Example ``MessageHandler(Filters.caption_entity("hashtag"), callback_method)`` - - Args: - entity_type: Caption Entity type to check for. All types can be found as constants - in :class:`telegram.MessageEntity`. - - """ - - __slots__ = ('entity_type',) - - def __init__(self, entity_type: str): - self.entity_type = entity_type - self.name = f'Filters.caption_entity({self.entity_type})' - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - return any(entity.type == self.entity_type for entity in message.caption_entities) - - class _Private(MessageFilter): - __slots__ = () - name = 'Filters.private' - - def filter(self, message: Message) -> bool: - warnings.warn( - 'Filters.private is deprecated. Use Filters.chat_type.private instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return message.chat.type == Chat.PRIVATE - - private = _Private() - """ - Messages sent in a private chat. - - Note: - DEPRECATED. Use - :attr:`telegram.ext.Filters.chat_type.private` instead. - """ - - class _Group(MessageFilter): - __slots__ = () - name = 'Filters.group' - - def filter(self, message: Message) -> bool: - warnings.warn( - 'Filters.group is deprecated. Use Filters.chat_type.groups instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] - - group = _Group() - """ - Messages sent in a group or a supergroup chat. - - Note: - DEPRECATED. Use - :attr:`telegram.ext.Filters.chat_type.groups` instead. - """ - - class _ChatType(MessageFilter): - __slots__ = () - name = 'Filters.chat_type' - - class _Channel(MessageFilter): - __slots__ = () - name = 'Filters.chat_type.channel' - - def filter(self, message: Message) -> bool: - return message.chat.type == Chat.CHANNEL - - channel = _Channel() - - class _Group(MessageFilter): - __slots__ = () - name = 'Filters.chat_type.group' - - def filter(self, message: Message) -> bool: - return message.chat.type == Chat.GROUP - - group = _Group() - - class _SuperGroup(MessageFilter): - __slots__ = () - name = 'Filters.chat_type.supergroup' - - def filter(self, message: Message) -> bool: - return message.chat.type == Chat.SUPERGROUP - - supergroup = _SuperGroup() - - class _Groups(MessageFilter): - __slots__ = () - name = 'Filters.chat_type.groups' - - def filter(self, message: Message) -> bool: - return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] - - groups = _Groups() - - class _Private(MessageFilter): - __slots__ = () - name = 'Filters.chat_type.private' - - def filter(self, message: Message) -> bool: - return message.chat.type == Chat.PRIVATE - - private = _Private() - - def filter(self, message: Message) -> bool: - return bool(message.chat.type) - - chat_type = _ChatType() - """Subset for filtering the type of chat. - - Examples: - Use these filters like: ``Filters.chat_type.channel`` or - ``Filters.chat_type.supergroup`` etc. Or use just ``Filters.chat_type`` for all - chat types. - - Attributes: - channel: Updates from channel - group: Updates from group - supergroup: Updates from supergroup - groups: Updates from group *or* supergroup - private: Updates sent in private chat - """ - - class _ChatUserBaseFilter(MessageFilter, ABC): - __slots__ = ( - 'chat_id_name', - 'username_name', - 'allow_empty', - '__lock', - '_chat_ids', - '_usernames', - ) - - def __init__( - self, - chat_id: SLT[int] = None, - username: SLT[str] = None, - allow_empty: bool = False, - ): - self.chat_id_name = 'chat_id' - self.username_name = 'username' - self.allow_empty = allow_empty - self.__lock = Lock() - - self._chat_ids: Set[int] = set() - self._usernames: Set[str] = set() - - self._set_chat_ids(chat_id) - self._set_usernames(username) - - @abstractmethod - def get_chat_or_user(self, message: Message) -> Union[Chat, User, None]: - ... - - @staticmethod - def _parse_chat_id(chat_id: SLT[int]) -> Set[int]: - if chat_id is None: - return set() - if isinstance(chat_id, int): - return {chat_id} - return set(chat_id) - - @staticmethod - def _parse_username(username: SLT[str]) -> Set[str]: - if username is None: - return set() - if isinstance(username, str): - return {username[1:] if username.startswith('@') else username} - return {chat[1:] if chat.startswith('@') else chat for chat in username} - - def _set_chat_ids(self, chat_id: SLT[int]) -> None: - with self.__lock: - if chat_id and self._usernames: - raise RuntimeError( - f"Can't set {self.chat_id_name} in conjunction with (already set) " - f"{self.username_name}s." - ) - self._chat_ids = self._parse_chat_id(chat_id) - - def _set_usernames(self, username: SLT[str]) -> None: - with self.__lock: - if username and self._chat_ids: - raise RuntimeError( - f"Can't set {self.username_name} in conjunction with (already set) " - f"{self.chat_id_name}s." - ) - self._usernames = self._parse_username(username) - - @property - def chat_ids(self) -> FrozenSet[int]: - with self.__lock: - return frozenset(self._chat_ids) - - @chat_ids.setter - def chat_ids(self, chat_id: SLT[int]) -> None: - self._set_chat_ids(chat_id) - - @property - def usernames(self) -> FrozenSet[str]: - with self.__lock: - return frozenset(self._usernames) - - @usernames.setter - def usernames(self, username: SLT[str]) -> None: - self._set_usernames(username) - - def add_usernames(self, username: SLT[str]) -> None: - with self.__lock: - if self._chat_ids: - raise RuntimeError( - f"Can't set {self.username_name} in conjunction with (already set) " - f"{self.chat_id_name}s." - ) - - parsed_username = self._parse_username(username) - self._usernames |= parsed_username - - def add_chat_ids(self, chat_id: SLT[int]) -> None: - with self.__lock: - if self._usernames: - raise RuntimeError( - f"Can't set {self.chat_id_name} in conjunction with (already set) " - f"{self.username_name}s." - ) - - parsed_chat_id = self._parse_chat_id(chat_id) - - self._chat_ids |= parsed_chat_id - - def remove_usernames(self, username: SLT[str]) -> None: - with self.__lock: - if self._chat_ids: - raise RuntimeError( - f"Can't set {self.username_name} in conjunction with (already set) " - f"{self.chat_id_name}s." - ) - - parsed_username = self._parse_username(username) - self._usernames -= parsed_username - - def remove_chat_ids(self, chat_id: SLT[int]) -> None: - with self.__lock: - if self._usernames: - raise RuntimeError( - f"Can't set {self.chat_id_name} in conjunction with (already set) " - f"{self.username_name}s." - ) - parsed_chat_id = self._parse_chat_id(chat_id) - self._chat_ids -= parsed_chat_id - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - chat_or_user = self.get_chat_or_user(message) - if chat_or_user: - if self.chat_ids: - return chat_or_user.id in self.chat_ids - if self.usernames: - return bool(chat_or_user.username and chat_or_user.username in self.usernames) - return self.allow_empty - return False - - @property - def name(self) -> str: - return ( - f'Filters.{self.__class__.__name__}(' - f'{", ".join(str(s) for s in (self.usernames or self.chat_ids))})' - ) - - @name.setter - def name(self, name: str) -> NoReturn: - raise RuntimeError(f'Cannot set name for Filters.{self.__class__.__name__}') - - class user(_ChatUserBaseFilter): - # pylint: disable=W0235 - """Filters messages to allow only those which are from specified user ID(s) or - username(s). - - Examples: - ``MessageHandler(Filters.user(1234), callback_method)`` - - Warning: - :attr:`user_ids` will give a *copy* of the saved user ids as :class:`frozenset`. This - is to ensure thread safety. To add/remove a user, you should use :meth:`add_usernames`, - :meth:`add_user_ids`, :meth:`remove_usernames` and :meth:`remove_user_ids`. Only update - the entire set by ``filter.user_ids/usernames = new_set``, if you are entirely sure - that it is not causing race conditions, as this will complete replace the current set - of allowed users. - - Args: - user_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which user ID(s) to allow through. - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. Leading ``'@'`` s in usernames will be - discarded. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user - is specified in :attr:`user_ids` and :attr:`usernames`. Defaults to :obj:`False` - - Raises: - RuntimeError: If user_id and username are both present. - - Attributes: - user_ids(set(:obj:`int`), optional): Which user ID(s) to allow through. - usernames(set(:obj:`str`), optional): Which username(s) (without leading ``'@'``) to - allow through. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user - is specified in :attr:`user_ids` and :attr:`usernames`. - - """ - - __slots__ = () - - def __init__( - self, - user_id: SLT[int] = None, - username: SLT[str] = None, - allow_empty: bool = False, - ): - super().__init__(chat_id=user_id, username=username, allow_empty=allow_empty) - self.chat_id_name = 'user_id' - - def get_chat_or_user(self, message: Message) -> Optional[User]: - return message.from_user - - @property - def user_ids(self) -> FrozenSet[int]: - return self.chat_ids - - @user_ids.setter - def user_ids(self, user_id: SLT[int]) -> None: - self.chat_ids = user_id # type: ignore[assignment] - - def add_usernames(self, username: SLT[str]) -> None: - """ - Add one or more users to the allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().add_usernames(username) - - def add_user_ids(self, user_id: SLT[int]) -> None: - """ - Add one or more users to the allowed user ids. - - Args: - user_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which user ID(s) to allow through. - """ - return super().add_chat_ids(user_id) - - def remove_usernames(self, username: SLT[str]) -> None: - """ - Remove one or more users from allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to disallow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().remove_usernames(username) - - def remove_user_ids(self, user_id: SLT[int]) -> None: - """ - Remove one or more users from allowed user ids. - - Args: - user_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which user ID(s) to disallow through. - """ - return super().remove_chat_ids(user_id) - - class via_bot(_ChatUserBaseFilter): - # pylint: disable=W0235 - """Filters messages to allow only those which are from specified via_bot ID(s) or - username(s). - - Examples: - ``MessageHandler(Filters.via_bot(1234), callback_method)`` - - Warning: - :attr:`bot_ids` will give a *copy* of the saved bot ids as :class:`frozenset`. This - is to ensure thread safety. To add/remove a bot, you should use :meth:`add_usernames`, - :meth:`add_bot_ids`, :meth:`remove_usernames` and :meth:`remove_bot_ids`. Only update - the entire set by ``filter.bot_ids/usernames = new_set``, if you are entirely sure - that it is not causing race conditions, as this will complete replace the current set - of allowed bots. - - Args: - bot_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which bot ID(s) to allow through. - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. Leading ``'@'`` s in usernames will be - discarded. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user - is specified in :attr:`bot_ids` and :attr:`usernames`. Defaults to :obj:`False` - - Raises: - RuntimeError: If bot_id and username are both present. - - Attributes: - bot_ids(set(:obj:`int`), optional): Which bot ID(s) to allow through. - usernames(set(:obj:`str`), optional): Which username(s) (without leading ``'@'``) to - allow through. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no bot - is specified in :attr:`bot_ids` and :attr:`usernames`. - - """ - - __slots__ = () - - def __init__( - self, - bot_id: SLT[int] = None, - username: SLT[str] = None, - allow_empty: bool = False, - ): - super().__init__(chat_id=bot_id, username=username, allow_empty=allow_empty) - self.chat_id_name = 'bot_id' - - def get_chat_or_user(self, message: Message) -> Optional[User]: - return message.via_bot - - @property - def bot_ids(self) -> FrozenSet[int]: - return self.chat_ids - - @bot_ids.setter - def bot_ids(self, bot_id: SLT[int]) -> None: - self.chat_ids = bot_id # type: ignore[assignment] - - def add_usernames(self, username: SLT[str]) -> None: - """ - Add one or more users to the allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().add_usernames(username) - - def add_bot_ids(self, bot_id: SLT[int]) -> None: - """ - - Add one or more users to the allowed user ids. - - Args: - bot_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which bot ID(s) to allow through. - """ - return super().add_chat_ids(bot_id) - - def remove_usernames(self, username: SLT[str]) -> None: - """ - Remove one or more users from allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to disallow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().remove_usernames(username) - - def remove_bot_ids(self, bot_id: SLT[int]) -> None: - """ - Remove one or more users from allowed user ids. - - Args: - bot_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which bot ID(s) to disallow through. - """ - return super().remove_chat_ids(bot_id) - - class chat(_ChatUserBaseFilter): - # pylint: disable=W0235 - """Filters messages to allow only those which are from a specified chat ID or username. - - Examples: - ``MessageHandler(Filters.chat(-1234), callback_method)`` - - Warning: - :attr:`chat_ids` will give a *copy* of the saved chat ids as :class:`frozenset`. This - is to ensure thread safety. To add/remove a chat, you should use :meth:`add_usernames`, - :meth:`add_chat_ids`, :meth:`remove_usernames` and :meth:`remove_chat_ids`. Only update - the entire set by ``filter.chat_ids/usernames = new_set``, if you are entirely sure - that it is not causing race conditions, as this will complete replace the current set - of allowed chats. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which chat ID(s) to allow through. - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat - is specified in :attr:`chat_ids` and :attr:`usernames`. Defaults to :obj:`False` - - Raises: - RuntimeError: If chat_id and username are both present. - - Attributes: - chat_ids(set(:obj:`int`), optional): Which chat ID(s) to allow through. - usernames(set(:obj:`str`), optional): Which username(s) (without leading ``'@'``) to - allow through. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat - is specified in :attr:`chat_ids` and :attr:`usernames`. - - """ - - __slots__ = () - - def get_chat_or_user(self, message: Message) -> Optional[Chat]: - return message.chat - - def add_usernames(self, username: SLT[str]) -> None: - """ - Add one or more chats to the allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().add_usernames(username) - - def add_chat_ids(self, chat_id: SLT[int]) -> None: - """ - Add one or more chats to the allowed chat ids. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which chat ID(s) to allow through. - """ - return super().add_chat_ids(chat_id) - - def remove_usernames(self, username: SLT[str]) -> None: - """ - Remove one or more chats from allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to disallow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().remove_usernames(username) - - def remove_chat_ids(self, chat_id: SLT[int]) -> None: - """ - Remove one or more chats from allowed chat ids. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which chat ID(s) to disallow through. - """ - return super().remove_chat_ids(chat_id) - - class forwarded_from(_ChatUserBaseFilter): - # pylint: disable=W0235 - """Filters messages to allow only those which are forwarded from the specified chat ID(s) - or username(s) based on :attr:`telegram.Message.forward_from` and - :attr:`telegram.Message.forward_from_chat`. - - .. versionadded:: 13.5 - - Examples: - ``MessageHandler(Filters.forwarded_from(chat_id=1234), callback_method)`` - - Note: - When a user has disallowed adding a link to their account while forwarding their - messages, this filter will *not* work since both - :attr:`telegram.Message.forwarded_from` and - :attr:`telegram.Message.forwarded_from_chat` are :obj:`None`. However, this behaviour - is undocumented and might be changed by Telegram. - - Warning: - :attr:`chat_ids` will give a *copy* of the saved chat ids as :class:`frozenset`. This - is to ensure thread safety. To add/remove a chat, you should use :meth:`add_usernames`, - :meth:`add_chat_ids`, :meth:`remove_usernames` and :meth:`remove_chat_ids`. Only update - the entire set by ``filter.chat_ids/usernames = new_set``, if you are entirely sure - that it is not causing race conditions, as this will complete replace the current set - of allowed chats. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which chat/user ID(s) to allow through. - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. Leading ``'@'`` s in usernames will be - discarded. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat - is specified in :attr:`chat_ids` and :attr:`usernames`. Defaults to :obj:`False`. - - Raises: - RuntimeError: If both chat_id and username are present. - - Attributes: - chat_ids(set(:obj:`int`), optional): Which chat/user ID(s) to allow through. - usernames(set(:obj:`str`), optional): Which username(s) (without leading ``'@'``) to - allow through. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat - is specified in :attr:`chat_ids` and :attr:`usernames`. - """ - - __slots__ = () - - def get_chat_or_user(self, message: Message) -> Union[User, Chat, None]: - return message.forward_from or message.forward_from_chat - - def add_usernames(self, username: SLT[str]) -> None: - """ - Add one or more chats to the allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().add_usernames(username) - - def add_chat_ids(self, chat_id: SLT[int]) -> None: - """ - Add one or more chats to the allowed chat ids. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which chat/user ID(s) to allow through. - """ - return super().add_chat_ids(chat_id) - - def remove_usernames(self, username: SLT[str]) -> None: - """ - Remove one or more chats from allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which username(s) to disallow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().remove_usernames(username) - - def remove_chat_ids(self, chat_id: SLT[int]) -> None: - """ - Remove one or more chats from allowed chat ids. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which chat/user ID(s) to disallow through. - """ - return super().remove_chat_ids(chat_id) - - class sender_chat(_ChatUserBaseFilter): - # pylint: disable=W0235 - """Filters messages to allow only those which are from a specified sender chat's chat ID or - username. - - Examples: - * To filter for messages sent to a group by a channel with ID - ``-1234``, use ``MessageHandler(Filters.sender_chat(-1234), callback_method)``. - * To filter for messages of anonymous admins in a super group with username - ``@anonymous``, use - ``MessageHandler(Filters.sender_chat(username='anonymous'), callback_method)``. - * To filter for messages sent to a group by *any* channel, use - ``MessageHandler(Filters.sender_chat.channel, callback_method)``. - * To filter for messages of anonymous admins in *any* super group, use - ``MessageHandler(Filters.sender_chat.super_group, callback_method)``. - - Note: - Remember, ``sender_chat`` is also set for messages in a channel as the channel itself, - so when your bot is an admin in a channel and the linked discussion group, you would - receive the message twice (once from inside the channel, once inside the discussion - group). Since v13.9, the field :attr:`telegram.Message.is_automatic_forward` will be - :obj:`True` for the discussion group message. - - .. seealso:: :attr:`Filters.is_automatic_forward` - - Warning: - :attr:`chat_ids` will return a *copy* of the saved chat ids as :class:`frozenset`. This - is to ensure thread safety. To add/remove a chat, you should use :meth:`add_usernames`, - :meth:`add_chat_ids`, :meth:`remove_usernames` and :meth:`remove_chat_ids`. Only update - the entire set by ``filter.chat_ids/usernames = new_set``, if you are entirely sure - that it is not causing race conditions, as this will complete replace the current set - of allowed chats. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which sender chat chat ID(s) to allow through. - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which sender chat username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no sender - chat is specified in :attr:`chat_ids` and :attr:`usernames`. Defaults to - :obj:`False` - - Raises: - RuntimeError: If both chat_id and username are present. - - Attributes: - chat_ids(set(:obj:`int`), optional): Which sender chat chat ID(s) to allow through. - usernames(set(:obj:`str`), optional): Which sender chat username(s) (without leading - ``'@'``) to allow through. - allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no sender - chat is specified in :attr:`chat_ids` and :attr:`usernames`. - super_group: Messages whose sender chat is a super group. - - Examples: - ``Filters.sender_chat.supergroup`` - channel: Messages whose sender chat is a channel. - - Examples: - ``Filters.sender_chat.channel`` - - """ - - __slots__ = () - - def get_chat_or_user(self, message: Message) -> Optional[Chat]: - return message.sender_chat - - def add_usernames(self, username: SLT[str]) -> None: - """ - Add one or more sender chats to the allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which sender chat username(s) to allow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().add_usernames(username) - - def add_chat_ids(self, chat_id: SLT[int]) -> None: - """ - Add one or more sender chats to the allowed chat ids. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which sender chat ID(s) to allow through. - """ - return super().add_chat_ids(chat_id) - - def remove_usernames(self, username: SLT[str]) -> None: - """ - Remove one or more sender chats from allowed usernames. - - Args: - username(:class:`telegram.utils.types.SLT[str]`, optional): - Which sender chat username(s) to disallow through. - Leading ``'@'`` s in usernames will be discarded. - """ - return super().remove_usernames(username) - - def remove_chat_ids(self, chat_id: SLT[int]) -> None: - """ - Remove one or more sender chats from allowed chat ids. - - Args: - chat_id(:class:`telegram.utils.types.SLT[int]`, optional): - Which sender chat ID(s) to disallow through. - """ - return super().remove_chat_ids(chat_id) - - class _SuperGroup(MessageFilter): - __slots__ = () - - def filter(self, message: Message) -> bool: - if message.sender_chat: - return message.sender_chat.type == Chat.SUPERGROUP - return False - - class _Channel(MessageFilter): - __slots__ = () - - def filter(self, message: Message) -> bool: - if message.sender_chat: - return message.sender_chat.type == Chat.CHANNEL - return False - - super_group = _SuperGroup() - channel = _Channel() - - class _IsAutomaticForward(MessageFilter): - __slots__ = () - name = 'Filters.is_automatic_forward' - - def filter(self, message: Message) -> bool: - return bool(message.is_automatic_forward) - - is_automatic_forward = _IsAutomaticForward() - """Messages that contain :attr:`telegram.Message.is_automatic_forward`. - - .. versionadded:: 13.9 - """ - - class _HasProtectedContent(MessageFilter): - __slots__ = () - name = 'Filters.has_protected_content' - - def filter(self, message: Message) -> bool: - return bool(message.has_protected_content) - - has_protected_content = _HasProtectedContent() - """Messages that contain :attr:`telegram.Message.has_protected_content`. - - .. versionadded:: 13.9 - """ - - class _Invoice(MessageFilter): - __slots__ = () - name = 'Filters.invoice' - - def filter(self, message: Message) -> bool: - return bool(message.invoice) - - invoice = _Invoice() - """Messages that contain :class:`telegram.Invoice`.""" - - class _SuccessfulPayment(MessageFilter): - __slots__ = () - name = 'Filters.successful_payment' - - def filter(self, message: Message) -> bool: - return bool(message.successful_payment) - - successful_payment = _SuccessfulPayment() - """Messages that confirm a :class:`telegram.SuccessfulPayment`.""" - - class _PassportData(MessageFilter): - __slots__ = () - name = 'Filters.passport_data' - - def filter(self, message: Message) -> bool: - return bool(message.passport_data) - - passport_data = _PassportData() - """Messages that contain a :class:`telegram.PassportData`""" - - class _Poll(MessageFilter): - __slots__ = () - name = 'Filters.poll' - - def filter(self, message: Message) -> bool: - return bool(message.poll) - - poll = _Poll() - """Messages that contain a :class:`telegram.Poll`.""" - - class _Dice(_DiceEmoji): - __slots__ = () - dice = _DiceEmoji('🎲', 'dice') - darts = _DiceEmoji('🎯', 'darts') - basketball = _DiceEmoji('🏀', 'basketball') - football = _DiceEmoji('⚽') - slot_machine = _DiceEmoji('🎰') - bowling = _DiceEmoji('🎳', 'bowling') - - dice = _Dice() - """Dice Messages. If an integer or a list of integers is passed, it filters messages to only - allow those whose dice value is appearing in the given list. - - Examples: - To allow any dice message, simply use - ``MessageHandler(Filters.dice, callback_method)``. - - To allow only dice messages with the emoji 🎲, but any value, use - ``MessageHandler(Filters.dice.dice, callback_method)``. - - To allow only dice messages with the emoji 🎯 and with value 6, use - ``MessageHandler(Filters.dice.darts(6), callback_method)``. - - To allow only dice messages with the emoji ⚽ and with value 5 `or` 6, use - ``MessageHandler(Filters.dice.football([5, 6]), callback_method)``. - - Note: - Dice messages don't have text. If you want to filter either text or dice messages, use - ``Filters.text | Filters.dice``. - - Args: - update (:class:`telegram.utils.types.SLT[int]`, optional): - Which values to allow. If not specified, will allow any dice message. - - Attributes: - dice: Dice messages with the emoji 🎲. Passing a list of integers is supported just as for - :attr:`Filters.dice`. - darts: Dice messages with the emoji 🎯. Passing a list of integers is supported just as for - :attr:`Filters.dice`. - basketball: Dice messages with the emoji 🏀. Passing a list of integers is supported just - as for :attr:`Filters.dice`. - football: Dice messages with the emoji ⚽. Passing a list of integers is supported just - as for :attr:`Filters.dice`. - slot_machine: Dice messages with the emoji 🎰. Passing a list of integers is supported just - as for :attr:`Filters.dice`. - bowling: Dice messages with the emoji 🎳. Passing a list of integers is supported just - as for :attr:`Filters.dice`. - - .. versionadded:: 13.4 - - """ - - class language(MessageFilter): - """Filters messages to only allow those which are from users with a certain language code. - - Note: - According to official Telegram API documentation, not every single user has the - `language_code` attribute. Do not count on this filter working on all users. - - Examples: - ``MessageHandler(Filters.language("en"), callback_method)`` - - Args: - lang (:class:`telegram.utils.types.SLT[str]`): - Which language code(s) to allow through. - This will be matched using ``.startswith`` meaning that - 'en' will match both 'en_US' and 'en_GB'. - - """ - - __slots__ = ('lang',) - - def __init__(self, lang: SLT[str]): - if isinstance(lang, str): - lang = cast(str, lang) - self.lang = [lang] - else: - lang = cast(List[str], lang) - self.lang = lang - self.name = f'Filters.language({self.lang})' - - def filter(self, message: Message) -> bool: - """""" # remove method from docs - return bool( - message.from_user.language_code - and any(message.from_user.language_code.startswith(x) for x in self.lang) - ) - - class _Attachment(MessageFilter): - __slots__ = () - - name = 'Filters.attachment' - - def filter(self, message: Message) -> bool: - return bool(message.effective_attachment) - - attachment = _Attachment() - """Messages that contain :meth:`telegram.Message.effective_attachment`. - - - .. versionadded:: 13.6""" - - class _UpdateType(UpdateFilter): - __slots__ = () - name = 'Filters.update' - - class _Message(UpdateFilter): - __slots__ = () - name = 'Filters.update.message' - - def filter(self, update: Update) -> bool: - return update.message is not None - - message = _Message() - - class _EditedMessage(UpdateFilter): - __slots__ = () - name = 'Filters.update.edited_message' - - def filter(self, update: Update) -> bool: - return update.edited_message is not None - - edited_message = _EditedMessage() - - class _Messages(UpdateFilter): - __slots__ = () - name = 'Filters.update.messages' - - def filter(self, update: Update) -> bool: - return update.message is not None or update.edited_message is not None - - messages = _Messages() - - class _ChannelPost(UpdateFilter): - __slots__ = () - name = 'Filters.update.channel_post' - - def filter(self, update: Update) -> bool: - return update.channel_post is not None - - channel_post = _ChannelPost() - - class _EditedChannelPost(UpdateFilter): - __slots__ = () - name = 'Filters.update.edited_channel_post' - - def filter(self, update: Update) -> bool: - return update.edited_channel_post is not None - - edited_channel_post = _EditedChannelPost() - - class _ChannelPosts(UpdateFilter): - __slots__ = () - name = 'Filters.update.channel_posts' - - def filter(self, update: Update) -> bool: - return update.channel_post is not None or update.edited_channel_post is not None - - channel_posts = _ChannelPosts() - - def filter(self, update: Update) -> bool: - return bool(self.messages(update) or self.channel_posts(update)) - - update = _UpdateType() - """Subset for filtering the type of update. - - Examples: - Use these filters like: ``Filters.update.message`` or - ``Filters.update.channel_posts`` etc. Or use just ``Filters.update`` for all - types. - - Attributes: - message: Updates with :attr:`telegram.Update.message` - edited_message: Updates with :attr:`telegram.Update.edited_message` - messages: Updates with either :attr:`telegram.Update.message` or - :attr:`telegram.Update.edited_message` - channel_post: Updates with :attr:`telegram.Update.channel_post` - edited_channel_post: Updates with - :attr:`telegram.Update.edited_channel_post` - channel_posts: Updates with either :attr:`telegram.Update.channel_post` or - :attr:`telegram.Update.edited_channel_post` - """ diff --git a/telegramer/include/telegram/ext/handler.py b/telegramer/include/telegram/ext/handler.py deleted file mode 100644 index b6e3a63..0000000 --- a/telegramer/include/telegram/ext/handler.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the base class for handlers as used by the Dispatcher.""" -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, Union, Generic -from sys import version_info as py_ver - -from telegram.utils.deprecate import set_new_attribute_deprecated - -from telegram import Update -from telegram.ext.utils.promise import Promise -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE -from telegram.ext.utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') -UT = TypeVar('UT') - - -class Handler(Generic[UT, CCT], ABC): - """The base class for all update handlers. Create custom handlers by inheriting from it. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - # Apparently Py 3.7 and below have '__dict__' in ABC - if py_ver < (3, 7): - __slots__ = ( - 'callback', - 'pass_update_queue', - 'pass_job_queue', - 'pass_user_data', - 'pass_chat_data', - 'run_async', - ) - else: - __slots__ = ( - 'callback', # type: ignore[assignment] - 'pass_update_queue', - 'pass_job_queue', - 'pass_user_data', - 'pass_chat_data', - 'run_async', - '__dict__', - ) - - def __init__( - self, - callback: Callable[[UT, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - self.callback = callback - self.pass_update_queue = pass_update_queue - self.pass_job_queue = pass_job_queue - self.pass_user_data = pass_user_data - self.pass_chat_data = pass_chat_data - self.run_async = run_async - - def __setattr__(self, key: str, value: object) -> None: - # See comment on BaseFilter to know why this was done. - if key.startswith('__'): - key = f"_{self.__class__.__name__}{key}" - if issubclass(self.__class__, Handler) and not self.__class__.__module__.startswith( - 'telegram.ext.' - ): - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - - @abstractmethod - def check_update(self, update: object) -> Optional[Union[bool, object]]: - """ - This method is called to determine if an update should be handled by - this handler instance. It should always be overridden. - - Note: - Custom updates types can be handled by the dispatcher. Therefore, an implementation of - this method should always check the type of :attr:`update`. - - Args: - update (:obj:`str` | :class:`telegram.Update`): The update to be tested. - - Returns: - Either :obj:`None` or :obj:`False` if the update should not be handled. Otherwise an - object that will be passed to :meth:`handle_update` and - :meth:`collect_additional_context` when the update gets handled. - - """ - - def handle_update( - self, - update: UT, - dispatcher: 'Dispatcher', - check_result: object, - context: CCT = None, - ) -> Union[RT, Promise]: - """ - This method is called if it was determined that an update should indeed - be handled by this instance. Calls :attr:`callback` along with its respectful - arguments. To work with the :class:`telegram.ext.ConversationHandler`, this method - returns the value returned from :attr:`callback`. - Note that it can be overridden if needed by the subclassing handler. - - Args: - update (:obj:`str` | :class:`telegram.Update`): The update to be handled. - dispatcher (:class:`telegram.ext.Dispatcher`): The calling dispatcher. - check_result (:obj:`obj`): The result from :attr:`check_update`. - context (:class:`telegram.ext.CallbackContext`, optional): The context as provided by - the dispatcher. - - """ - run_async = self.run_async - if ( - self.run_async is DEFAULT_FALSE - and dispatcher.bot.defaults - and dispatcher.bot.defaults.run_async - ): - run_async = True - - if context: - self.collect_additional_context(context, update, dispatcher, check_result) - if run_async: - return dispatcher.run_async(self.callback, update, context, update=update) - return self.callback(update, context) - - optional_args = self.collect_optional_args(dispatcher, update, check_result) - if run_async: - return dispatcher.run_async( - self.callback, dispatcher.bot, update, update=update, **optional_args - ) - return self.callback(dispatcher.bot, update, **optional_args) # type: ignore - - def collect_additional_context( - self, - context: CCT, - update: UT, - dispatcher: 'Dispatcher', - check_result: Any, - ) -> None: - """Prepares additional arguments for the context. Override if needed. - - Args: - context (:class:`telegram.ext.CallbackContext`): The context object. - update (:class:`telegram.Update`): The update to gather chat/user id from. - dispatcher (:class:`telegram.ext.Dispatcher`): The calling dispatcher. - check_result: The result (return value) from :attr:`check_update`. - - """ - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: UT = None, - check_result: Any = None, # pylint: disable=W0613 - ) -> Dict[str, object]: - """ - Prepares the optional arguments. If the handler has additional optional args, - it should subclass this method, but remember to call this super method. - - DEPRECATED: This method is being replaced by new context based callbacks. Please see - https://git.io/fxJuV for more info. - - Args: - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher. - update (:class:`telegram.Update`): The update to gather chat/user id from. - check_result: The result from check_update - - """ - optional_args: Dict[str, object] = {} - - if self.pass_update_queue: - optional_args['update_queue'] = dispatcher.update_queue - if self.pass_job_queue: - optional_args['job_queue'] = dispatcher.job_queue - if self.pass_user_data and isinstance(update, Update): - user = update.effective_user - optional_args['user_data'] = dispatcher.user_data[ - user.id if user else None # type: ignore[index] - ] - if self.pass_chat_data and isinstance(update, Update): - chat = update.effective_chat - optional_args['chat_data'] = dispatcher.chat_data[ - chat.id if chat else None # type: ignore[index] - ] - - return optional_args diff --git a/telegramer/include/telegram/ext/inlinequeryhandler.py b/telegramer/include/telegram/ext/inlinequeryhandler.py deleted file mode 100644 index de43431..0000000 --- a/telegramer/include/telegram/ext/inlinequeryhandler.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the InlineQueryHandler class.""" -import re -from typing import ( - TYPE_CHECKING, - Callable, - Dict, - Match, - Optional, - Pattern, - TypeVar, - Union, - cast, - List, -) - -from telegram import Update -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .handler import Handler -from .utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class InlineQueryHandler(Handler[Update, CCT]): - """ - Handler class to handle Telegram inline queries. Optionally based on a regex. Read the - documentation of the ``re`` module for more information. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - * When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - * :attr:`telegram.InlineQuery.chat_type` will not be set for inline queries from secret - chats and may not be set for inline queries coming from third-party clients. These - updates won't be handled, if :attr:`chat_types` is passed. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pattern (:obj:`str` | :obj:`Pattern`, optional): Regex pattern. If not :obj:`None`, - ``re.match`` is used on :attr:`telegram.InlineQuery.query` to determine if an update - should be handled by this handler. - chat_types (List[:obj:`str`], optional): List of allowed chat types. If passed, will only - handle inline queries with the appropriate :attr:`telegram.InlineQuery.chat_type`. - - .. versionadded:: 13.5 - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test - :attr:`telegram.InlineQuery.query` against. - chat_types (List[:obj:`str`], optional): List of allowed chat types. - - .. versionadded:: 13.5 - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('pattern', 'chat_types', 'pass_groups', 'pass_groupdict') - - def __init__( - self, - callback: Callable[[Update, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pattern: Union[str, Pattern] = None, - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - chat_types: List[str] = None, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - - if isinstance(pattern, str): - pattern = re.compile(pattern) - - self.pattern = pattern - self.chat_types = chat_types - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict - - def check_update(self, update: object) -> Optional[Union[bool, Match]]: - """ - Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, Update) and update.inline_query: - if (self.chat_types is not None) and ( - update.inline_query.chat_type not in self.chat_types - ): - return False - if self.pattern: - if update.inline_query.query: - match = re.match(self.pattern, update.inline_query.query) - if match: - return match - else: - return True - return None - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: Update = None, - check_result: Optional[Union[bool, Match]] = None, - ) -> Dict[str, object]: - """Pass the results of ``re.match(pattern, query).{groups(), groupdict()}`` to the - callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if - needed. - """ - optional_args = super().collect_optional_args(dispatcher, update, check_result) - if self.pattern: - check_result = cast(Match, check_result) - if self.pass_groups: - optional_args['groups'] = check_result.groups() - if self.pass_groupdict: - optional_args['groupdict'] = check_result.groupdict() - return optional_args - - def collect_additional_context( - self, - context: CCT, - update: Update, - dispatcher: 'Dispatcher', - check_result: Optional[Union[bool, Match]], - ) -> None: - """Add the result of ``re.match(pattern, update.inline_query.query)`` to - :attr:`CallbackContext.matches` as list with one element. - """ - if self.pattern: - check_result = cast(Match, check_result) - context.matches = [check_result] diff --git a/telegramer/include/telegram/ext/jobqueue.py b/telegramer/include/telegram/ext/jobqueue.py deleted file mode 100644 index f0c1fba..0000000 --- a/telegramer/include/telegram/ext/jobqueue.py +++ /dev/null @@ -1,659 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes JobQueue and Job.""" - -import datetime -import logging -from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union, cast, overload - -import pytz -from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_EXECUTED, JobEvent -from apscheduler.schedulers.background import BackgroundScheduler -from apscheduler.triggers.combining import OrTrigger -from apscheduler.triggers.cron import CronTrigger -from apscheduler.job import Job as APSJob - -from telegram.ext.callbackcontext import CallbackContext -from telegram.utils.types import JSONDict -from telegram.utils.deprecate import set_new_attribute_deprecated - -if TYPE_CHECKING: - from telegram import Bot - from telegram.ext import Dispatcher - import apscheduler.job # noqa: F401 - - -class JobQueue: - """This class allows you to periodically perform tasks with the bot. It is a convenience - wrapper for the APScheduler library. - - Attributes: - scheduler (:class:`apscheduler.schedulers.background.BackgroundScheduler`): The APScheduler - bot (:class:`telegram.Bot`): The bot instance that should be passed to the jobs. - DEPRECATED: Use :attr:`set_dispatcher` instead. - - """ - - __slots__ = ('_dispatcher', 'logger', 'scheduler', '__dict__') - - def __init__(self) -> None: - self._dispatcher: 'Dispatcher' = None # type: ignore[assignment] - self.logger = logging.getLogger(self.__class__.__name__) - self.scheduler = BackgroundScheduler(timezone=pytz.utc) - self.scheduler.add_listener( - self._update_persistence, mask=EVENT_JOB_EXECUTED | EVENT_JOB_ERROR - ) - - # Dispatch errors and don't log them in the APS logger - def aps_log_filter(record): # type: ignore - return 'raised an exception' not in record.msg - - logging.getLogger('apscheduler.executors.default').addFilter(aps_log_filter) - self.scheduler.add_listener(self._dispatch_error, EVENT_JOB_ERROR) - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - def _build_args(self, job: 'Job') -> List[Union[CallbackContext, 'Bot', 'Job']]: - if self._dispatcher.use_context: - return [self._dispatcher.context_types.context.from_job(job, self._dispatcher)] - return [self._dispatcher.bot, job] - - def _tz_now(self) -> datetime.datetime: - return datetime.datetime.now(self.scheduler.timezone) - - def _update_persistence(self, _: JobEvent) -> None: - self._dispatcher.update_persistence() - - def _dispatch_error(self, event: JobEvent) -> None: - try: - self._dispatcher.dispatch_error(None, event.exception) - # Errors should not stop the thread. - except Exception: - self.logger.exception( - 'An error was raised while processing the job and an ' - 'uncaught error was raised while handling the error ' - 'with an error_handler.' - ) - - @overload - def _parse_time_input(self, time: None, shift_day: bool = False) -> None: - ... - - @overload - def _parse_time_input( - self, - time: Union[float, int, datetime.timedelta, datetime.datetime, datetime.time], - shift_day: bool = False, - ) -> datetime.datetime: - ... - - def _parse_time_input( - self, - time: Union[float, int, datetime.timedelta, datetime.datetime, datetime.time, None], - shift_day: bool = False, - ) -> Optional[datetime.datetime]: - if time is None: - return None - if isinstance(time, (int, float)): - return self._tz_now() + datetime.timedelta(seconds=time) - if isinstance(time, datetime.timedelta): - return self._tz_now() + time - if isinstance(time, datetime.time): - date_time = datetime.datetime.combine( - datetime.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time - ) - if date_time.tzinfo is None: - date_time = self.scheduler.timezone.localize(date_time) - if shift_day and date_time <= datetime.datetime.now(pytz.utc): - date_time += datetime.timedelta(days=1) - return date_time - # isinstance(time, datetime.datetime): - return time - - def set_dispatcher(self, dispatcher: 'Dispatcher') -> None: - """Set the dispatcher to be used by this JobQueue. Use this instead of passing a - :class:`telegram.Bot` to the JobQueue, which is deprecated. - - Args: - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher. - - """ - self._dispatcher = dispatcher - if dispatcher.bot.defaults: - self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc) - - def run_once( - self, - callback: Callable[['CallbackContext'], None], - when: Union[float, datetime.timedelta, datetime.datetime, datetime.time], - context: object = None, - name: str = None, - job_kwargs: JSONDict = None, - ) -> 'Job': - """Creates a new ``Job`` that runs once and adds it to the queue. - - Args: - callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. - when (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \ - :obj:`datetime.datetime` | :obj:`datetime.time`): - Time in or at which the job should run. This parameter will be interpreted - depending on its type. - - * :obj:`int` or :obj:`float` will be interpreted as "seconds from now" in which the - job should run. - * :obj:`datetime.timedelta` will be interpreted as "time from now" in which the - job should run. - * :obj:`datetime.datetime` will be interpreted as a specific date and time at - which the job should run. If the timezone (``datetime.tzinfo``) is :obj:`None`, - the default timezone of the bot will be used. - * :obj:`datetime.time` will be interpreted as a specific time of day at which the - job should run. This could be either today or, if the time has already passed, - tomorrow. If the timezone (``time.tzinfo``) is :obj:`None`, the - default timezone of the bot will be used. - - context (:obj:`object`, optional): Additional data needed for the callback function. - Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. - name (:obj:`str`, optional): The name of the new job. Defaults to - ``callback.__name__``. - job_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to pass to the - ``scheduler.add_job()``. - - Returns: - :class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job - queue. - - """ - if not job_kwargs: - job_kwargs = {} - - name = name or callback.__name__ - job = Job(callback, context, name, self) - date_time = self._parse_time_input(when, shift_day=True) - - j = self.scheduler.add_job( - callback, - name=name, - trigger='date', - run_date=date_time, - args=self._build_args(job), - timezone=date_time.tzinfo or self.scheduler.timezone, - **job_kwargs, - ) - - job.job = j - return job - - def run_repeating( - self, - callback: Callable[['CallbackContext'], None], - interval: Union[float, datetime.timedelta], - first: Union[float, datetime.timedelta, datetime.datetime, datetime.time] = None, - last: Union[float, datetime.timedelta, datetime.datetime, datetime.time] = None, - context: object = None, - name: str = None, - job_kwargs: JSONDict = None, - ) -> 'Job': - """Creates a new ``Job`` that runs at specified intervals and adds it to the queue. - - Note: - For a note about DST, please see the documentation of `APScheduler`_. - - .. _`APScheduler`: https://apscheduler.readthedocs.io/en/stable/modules/triggers/cron.html - #daylight-saving-time-behavior - - Args: - callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. - interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`): The interval in which - the job will run. If it is an :obj:`int` or a :obj:`float`, it will be interpreted - as seconds. - first (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \ - :obj:`datetime.datetime` | :obj:`datetime.time`, optional): - Time in or at which the job should run. This parameter will be interpreted - depending on its type. - - * :obj:`int` or :obj:`float` will be interpreted as "seconds from now" in which the - job should run. - * :obj:`datetime.timedelta` will be interpreted as "time from now" in which the - job should run. - * :obj:`datetime.datetime` will be interpreted as a specific date and time at - which the job should run. If the timezone (``datetime.tzinfo``) is :obj:`None`, - the default timezone of the bot will be used. - * :obj:`datetime.time` will be interpreted as a specific time of day at which the - job should run. This could be either today or, if the time has already passed, - tomorrow. If the timezone (``time.tzinfo``) is :obj:`None`, the - default timezone of the bot will be used. - - Defaults to ``interval`` - last (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \ - :obj:`datetime.datetime` | :obj:`datetime.time`, optional): - Latest possible time for the job to run. This parameter will be interpreted - depending on its type. See ``first`` for details. - - If ``last`` is :obj:`datetime.datetime` or :obj:`datetime.time` type - and ``last.tzinfo`` is :obj:`None`, the default timezone of the bot will be - assumed. - - Defaults to :obj:`None`. - context (:obj:`object`, optional): Additional data needed for the callback function. - Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. - name (:obj:`str`, optional): The name of the new job. Defaults to - ``callback.__name__``. - job_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to pass to the - ``scheduler.add_job()``. - - Returns: - :class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job - queue. - - """ - if not job_kwargs: - job_kwargs = {} - - name = name or callback.__name__ - job = Job(callback, context, name, self) - - dt_first = self._parse_time_input(first) - dt_last = self._parse_time_input(last) - - if dt_last and dt_first and dt_last < dt_first: - raise ValueError("'last' must not be before 'first'!") - - if isinstance(interval, datetime.timedelta): - interval = interval.total_seconds() - - j = self.scheduler.add_job( - callback, - trigger='interval', - args=self._build_args(job), - start_date=dt_first, - end_date=dt_last, - seconds=interval, - name=name, - **job_kwargs, - ) - - job.job = j - return job - - def run_monthly( - self, - callback: Callable[['CallbackContext'], None], - when: datetime.time, - day: int, - context: object = None, - name: str = None, - day_is_strict: bool = True, - job_kwargs: JSONDict = None, - ) -> 'Job': - """Creates a new ``Job`` that runs on a monthly basis and adds it to the queue. - - Args: - callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. - when (:obj:`datetime.time`): Time of day at which the job should run. If the timezone - (``when.tzinfo``) is :obj:`None`, the default timezone of the bot will be used. - day (:obj:`int`): Defines the day of the month whereby the job would run. It should - be within the range of 1 and 31, inclusive. - context (:obj:`object`, optional): Additional data needed for the callback function. - Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. - name (:obj:`str`, optional): The name of the new job. Defaults to - ``callback.__name__``. - day_is_strict (:obj:`bool`, optional): If :obj:`False` and day > month.days, will pick - the last day in the month. Defaults to :obj:`True`. - job_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to pass to the - ``scheduler.add_job()``. - - Returns: - :class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job - queue. - - """ - if not job_kwargs: - job_kwargs = {} - - name = name or callback.__name__ - job = Job(callback, context, name, self) - - if day_is_strict: - j = self.scheduler.add_job( - callback, - trigger='cron', - args=self._build_args(job), - name=name, - day=day, - hour=when.hour, - minute=when.minute, - second=when.second, - timezone=when.tzinfo or self.scheduler.timezone, - **job_kwargs, - ) - else: - trigger = OrTrigger( - [ - CronTrigger( - day=day, - hour=when.hour, - minute=when.minute, - second=when.second, - timezone=when.tzinfo, - **job_kwargs, - ), - CronTrigger( - day='last', - hour=when.hour, - minute=when.minute, - second=when.second, - timezone=when.tzinfo or self.scheduler.timezone, - **job_kwargs, - ), - ] - ) - j = self.scheduler.add_job( - callback, trigger=trigger, args=self._build_args(job), name=name, **job_kwargs - ) - - job.job = j - return job - - def run_daily( - self, - callback: Callable[['CallbackContext'], None], - time: datetime.time, - days: Tuple[int, ...] = tuple(range(7)), - context: object = None, - name: str = None, - job_kwargs: JSONDict = None, - ) -> 'Job': - """Creates a new ``Job`` that runs on a daily basis and adds it to the queue. - - Note: - For a note about DST, please see the documentation of `APScheduler`_. - - .. _`APScheduler`: https://apscheduler.readthedocs.io/en/stable/modules/triggers/cron.html - #daylight-saving-time-behavior - - Args: - callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. - time (:obj:`datetime.time`): Time of day at which the job should run. If the timezone - (``time.tzinfo``) is :obj:`None`, the default timezone of the bot will be used. - days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should - run (where ``0-6`` correspond to monday - sunday). Defaults to ``EVERY_DAY`` - context (:obj:`object`, optional): Additional data needed for the callback function. - Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. - name (:obj:`str`, optional): The name of the new job. Defaults to - ``callback.__name__``. - job_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to pass to the - ``scheduler.add_job()``. - - Returns: - :class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job - queue. - - """ - if not job_kwargs: - job_kwargs = {} - - name = name or callback.__name__ - job = Job(callback, context, name, self) - - j = self.scheduler.add_job( - callback, - name=name, - args=self._build_args(job), - trigger='cron', - day_of_week=','.join([str(d) for d in days]), - hour=time.hour, - minute=time.minute, - second=time.second, - timezone=time.tzinfo or self.scheduler.timezone, - **job_kwargs, - ) - - job.job = j - return job - - def run_custom( - self, - callback: Callable[['CallbackContext'], None], - job_kwargs: JSONDict, - context: object = None, - name: str = None, - ) -> 'Job': - """Creates a new customly defined ``Job``. - - Args: - callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. - job_kwargs (:obj:`dict`): Arbitrary keyword arguments. Used as arguments for - ``scheduler.add_job``. - context (:obj:`object`, optional): Additional data needed for the callback function. - Can be accessed through ``job.context`` in the callback. Defaults to ``None``. - name (:obj:`str`, optional): The name of the new job. Defaults to - ``callback.__name__``. - - Returns: - :class:`telegram.ext.Job`: The new ``Job`` instance that has been added to the job - queue. - - """ - name = name or callback.__name__ - job = Job(callback, context, name, self) - - j = self.scheduler.add_job(callback, args=self._build_args(job), name=name, **job_kwargs) - - job.job = j - return job - - def start(self) -> None: - """Starts the job_queue thread.""" - if not self.scheduler.running: - self.scheduler.start() - - def stop(self) -> None: - """Stops the thread.""" - if self.scheduler.running: - self.scheduler.shutdown() - - def jobs(self) -> Tuple['Job', ...]: - """Returns a tuple of all *scheduled* jobs that are currently in the ``JobQueue``.""" - return tuple( - Job._from_aps_job(job, self) # pylint: disable=W0212 - for job in self.scheduler.get_jobs() - ) - - def get_jobs_by_name(self, name: str) -> Tuple['Job', ...]: - """Returns a tuple of all *pending/scheduled* jobs with the given name that are currently - in the ``JobQueue``. - """ - return tuple(job for job in self.jobs() if job.name == name) - - -class Job: - """This class is a convenience wrapper for the jobs held in a :class:`telegram.ext.JobQueue`. - With the current backend APScheduler, :attr:`job` holds a :class:`apscheduler.job.Job` - instance. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Note: - * All attributes and instance methods of :attr:`job` are also directly available as - attributes/methods of the corresponding :class:`telegram.ext.Job` object. - * Two instances of :class:`telegram.ext.Job` are considered equal, if their corresponding - ``job`` attributes have the same ``id``. - * If :attr:`job` isn't passed on initialization, it must be set manually afterwards for - this :class:`telegram.ext.Job` to be useful. - - Args: - callback (:obj:`callable`): The callback function that should be executed by the new job. - Callback signature for context based API: - - ``def callback(CallbackContext)`` - - a ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. - context (:obj:`object`, optional): Additional data needed for the callback function. Can be - accessed through ``job.context`` in the callback. Defaults to :obj:`None`. - name (:obj:`str`, optional): The name of the new job. Defaults to ``callback.__name__``. - job_queue (:class:`telegram.ext.JobQueue`, optional): The ``JobQueue`` this job belongs to. - Only optional for backward compatibility with ``JobQueue.put()``. - job (:class:`apscheduler.job.Job`, optional): The APS Job this job is a wrapper for. - - Attributes: - callback (:obj:`callable`): The callback function that should be executed by the new job. - context (:obj:`object`): Optional. Additional data needed for the callback function. - name (:obj:`str`): Optional. The name of the new job. - job_queue (:class:`telegram.ext.JobQueue`): Optional. The ``JobQueue`` this job belongs to. - job (:class:`apscheduler.job.Job`): Optional. The APS Job this job is a wrapper for. - """ - - __slots__ = ( - 'callback', - 'context', - 'name', - 'job_queue', - '_removed', - '_enabled', - 'job', - '__dict__', - ) - - def __init__( - self, - callback: Callable[['CallbackContext'], None], - context: object = None, - name: str = None, - job_queue: JobQueue = None, - job: APSJob = None, - ): - - self.callback = callback - self.context = context - self.name = name or callback.__name__ - self.job_queue = job_queue - - self._removed = False - self._enabled = False - - self.job = cast(APSJob, job) # skipcq: PTC-W0052 - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - def run(self, dispatcher: 'Dispatcher') -> None: - """Executes the callback function independently of the jobs schedule.""" - try: - if dispatcher.use_context: - self.callback(dispatcher.context_types.context.from_job(self, dispatcher)) - else: - self.callback(dispatcher.bot, self) # type: ignore[arg-type,call-arg] - except Exception as exc: - try: - dispatcher.dispatch_error(None, exc) - # Errors should not stop the thread. - except Exception: - dispatcher.logger.exception( - 'An error was raised while processing the job and an ' - 'uncaught error was raised while handling the error ' - 'with an error_handler.' - ) - - def schedule_removal(self) -> None: - """ - Schedules this job for removal from the ``JobQueue``. It will be removed without executing - its callback function again. - """ - self.job.remove() - self._removed = True - - @property - def removed(self) -> bool: - """:obj:`bool`: Whether this job is due to be removed.""" - return self._removed - - @property - def enabled(self) -> bool: - """:obj:`bool`: Whether this job is enabled.""" - return self._enabled - - @enabled.setter - def enabled(self, status: bool) -> None: - if status: - self.job.resume() - else: - self.job.pause() - self._enabled = status - - @property - def next_t(self) -> Optional[datetime.datetime]: - """ - :obj:`datetime.datetime`: Datetime for the next job execution. - Datetime is localized according to :attr:`tzinfo`. - If job is removed or already ran it equals to :obj:`None`. - """ - return self.job.next_run_time - - @classmethod - def _from_aps_job(cls, job: APSJob, job_queue: JobQueue) -> 'Job': - # context based callbacks - if len(job.args) == 1: - context = job.args[0].job.context - else: - context = job.args[1].context - return cls(job.func, context=context, name=job.name, job_queue=job_queue, job=job) - - def __getattr__(self, item: str) -> object: - return getattr(self.job, item) - - def __lt__(self, other: object) -> bool: - return False - - def __eq__(self, other: object) -> bool: - if isinstance(other, self.__class__): - return self.id == other.id - return False diff --git a/telegramer/include/telegram/ext/messagehandler.py b/telegramer/include/telegram/ext/messagehandler.py deleted file mode 100644 index 57faa3f..0000000 --- a/telegramer/include/telegram/ext/messagehandler.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# TODO: Remove allow_edited -"""This module contains the MessageHandler class.""" -import warnings -from typing import TYPE_CHECKING, Callable, Dict, Optional, TypeVar, Union - -from telegram import Update -from telegram.ext import BaseFilter, Filters -from telegram.utils.deprecate import TelegramDeprecationWarning -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .handler import Handler -from .utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class MessageHandler(Handler[Update, CCT]): - """Handler class to handle telegram messages. They might contain text, media or status updates. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - filters (:class:`telegram.ext.BaseFilter`, optional): A filter inheriting from - :class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in - :class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise - operators (& for and, | for or, ~ for not). Default is - :attr:`telegram.ext.filters.Filters.update`. This defaults to all message_type updates - being: ``message``, ``edited_message``, ``channel_post`` and ``edited_channel_post``. - If you don't want or need any of those pass ``~Filters.update.*`` in the filter - argument. - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - message_updates (:obj:`bool`, optional): Should "normal" message updates be handled? - Default is :obj:`None`. - DEPRECATED: Please switch to filters for update filtering. - channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled? - Default is :obj:`None`. - DEPRECATED: Please switch to filters for update filtering. - edited_updates (:obj:`bool`, optional): Should "edited" message updates be handled? Default - is :obj:`None`. - DEPRECATED: Please switch to filters for update filtering. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Raises: - ValueError - - Attributes: - filters (:obj:`Filter`): Only allow updates with these Filters. See - :mod:`telegram.ext.filters` for a full list of all available filters. - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - message_updates (:obj:`bool`): Should "normal" message updates be handled? - Default is :obj:`None`. - channel_post_updates (:obj:`bool`): Should channel posts updates be handled? - Default is :obj:`None`. - edited_updates (:obj:`bool`): Should "edited" message updates be handled? - Default is :obj:`None`. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('filters',) - - def __init__( - self, - filters: BaseFilter, - callback: Callable[[Update, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - message_updates: bool = None, - channel_post_updates: bool = None, - edited_updates: bool = None, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - run_async=run_async, - ) - if message_updates is False and channel_post_updates is False and edited_updates is False: - raise ValueError( - 'message_updates, channel_post_updates and edited_updates are all False' - ) - if filters is not None: - self.filters = Filters.update & filters - else: - self.filters = Filters.update - if message_updates is not None: - warnings.warn( - 'message_updates is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if message_updates is False: - self.filters &= ~Filters.update.message - - if channel_post_updates is not None: - warnings.warn( - 'channel_post_updates is deprecated. See https://git.io/fxJuV ' 'for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if channel_post_updates is False: - self.filters &= ~Filters.update.channel_post - - if edited_updates is not None: - warnings.warn( - 'edited_updates is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if edited_updates is False: - self.filters &= ~( - Filters.update.edited_message | Filters.update.edited_channel_post - ) - - def check_update(self, update: object) -> Optional[Union[bool, Dict[str, list]]]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, Update) and update.effective_message: - return self.filters(update) - return None - - def collect_additional_context( - self, - context: CCT, - update: Update, - dispatcher: 'Dispatcher', - check_result: Optional[Union[bool, Dict[str, object]]], - ) -> None: - """Adds possible output of data filters to the :class:`CallbackContext`.""" - if isinstance(check_result, dict): - context.update(check_result) diff --git a/telegramer/include/telegram/ext/messagequeue.py b/telegramer/include/telegram/ext/messagequeue.py deleted file mode 100644 index da2a734..0000000 --- a/telegramer/include/telegram/ext/messagequeue.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env python -# -# Module author: -# Tymofii A. Khodniev (thodnev) <thodnev@mail.ru> -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/] -"""A throughput-limiting message processor for Telegram bots.""" -import functools -import queue as q -import threading -import time -import warnings -from typing import TYPE_CHECKING, Callable, List, NoReturn - -from telegram.ext.utils.promise import Promise -from telegram.utils.deprecate import TelegramDeprecationWarning - -if TYPE_CHECKING: - from telegram import Bot - -# We need to count < 1s intervals, so the most accurate timer is needed -curtime = time.perf_counter - - -class DelayQueueError(RuntimeError): - """Indicates processing errors.""" - - __slots__ = () - - -class DelayQueue(threading.Thread): - """ - Processes callbacks from queue with specified throughput limits. Creates a separate thread to - process callbacks with delays. - - .. deprecated:: 13.3 - :class:`telegram.ext.DelayQueue` in its current form is deprecated and will be reinvented - in a future release. See `this thread <https://git.io/JtDbF>`_ for a list of known bugs. - - Args: - queue (:obj:`Queue`, optional): Used to pass callbacks to thread. Creates ``Queue`` - implicitly if not provided. - burst_limit (:obj:`int`, optional): Number of maximum callbacks to process per time-window - defined by :attr:`time_limit_ms`. Defaults to 30. - time_limit_ms (:obj:`int`, optional): Defines width of time-window used when each - processing limit is calculated. Defaults to 1000. - exc_route (:obj:`callable`, optional): A callable, accepting 1 positional argument; used to - route exceptions from processor thread to main thread; is called on `Exception` - subclass exceptions. If not provided, exceptions are routed through dummy handler, - which re-raises them. - autostart (:obj:`bool`, optional): If :obj:`True`, processor is started immediately after - object's creation; if :obj:`False`, should be started manually by `start` method. - Defaults to :obj:`True`. - name (:obj:`str`, optional): Thread's name. Defaults to ``'DelayQueue-N'``, where N is - sequential number of object created. - - Attributes: - burst_limit (:obj:`int`): Number of maximum callbacks to process per time-window. - time_limit (:obj:`int`): Defines width of time-window used when each processing limit is - calculated. - exc_route (:obj:`callable`): A callable, accepting 1 positional argument; used to route - exceptions from processor thread to main thread; - name (:obj:`str`): Thread's name. - - """ - - _instcnt = 0 # instance counter - - def __init__( - self, - queue: q.Queue = None, - burst_limit: int = 30, - time_limit_ms: int = 1000, - exc_route: Callable[[Exception], None] = None, - autostart: bool = True, - name: str = None, - ): - warnings.warn( - 'DelayQueue in its current form is deprecated and will be reinvented in a future ' - 'release. See https://git.io/JtDbF for a list of known bugs.', - category=TelegramDeprecationWarning, - ) - - self._queue = queue if queue is not None else q.Queue() - self.burst_limit = burst_limit - self.time_limit = time_limit_ms / 1000 - self.exc_route = exc_route if exc_route is not None else self._default_exception_handler - self.__exit_req = False # flag to gently exit thread - self.__class__._instcnt += 1 - if name is None: - name = f'{self.__class__.__name__}-{self.__class__._instcnt}' - super().__init__(name=name) - self.daemon = False - if autostart: # immediately start processing - super().start() - - def run(self) -> None: - """ - Do not use the method except for unthreaded testing purposes, the method normally is - automatically called by autostart argument. - - """ - times: List[float] = [] # used to store each callable processing time - while True: - item = self._queue.get() - if self.__exit_req: - return # shutdown thread - # delay routine - now = time.perf_counter() - t_delta = now - self.time_limit # calculate early to improve perf. - if times and t_delta > times[-1]: - # if last call was before the limit time-window - # used to impr. perf. in long-interval calls case - times = [now] - else: - # collect last in current limit time-window - times = [t for t in times if t >= t_delta] - times.append(now) - if len(times) >= self.burst_limit: # if throughput limit was hit - time.sleep(times[1] - t_delta) - # finally process one - try: - func, args, kwargs = item - func(*args, **kwargs) - except Exception as exc: # re-route any exceptions - self.exc_route(exc) # to prevent thread exit - - def stop(self, timeout: float = None) -> None: - """Used to gently stop processor and shutdown its thread. - - Args: - timeout (:obj:`float`): Indicates maximum time to wait for processor to stop and its - thread to exit. If timeout exceeds and processor has not stopped, method silently - returns. :attr:`is_alive` could be used afterwards to check the actual status. - ``timeout`` set to :obj:`None`, blocks until processor is shut down. - Defaults to :obj:`None`. - - """ - self.__exit_req = True # gently request - self._queue.put(None) # put something to unfreeze if frozen - super().join(timeout=timeout) - - @staticmethod - def _default_exception_handler(exc: Exception) -> NoReturn: - """ - Dummy exception handler which re-raises exception in thread. Could be possibly overwritten - by subclasses. - - """ - raise exc - - def __call__(self, func: Callable, *args: object, **kwargs: object) -> None: - """Used to process callbacks in throughput-limiting thread through queue. - - Args: - func (:obj:`callable`): The actual function (or any callable) that is processed through - queue. - *args (:obj:`list`): Variable-length `func` arguments. - **kwargs (:obj:`dict`): Arbitrary keyword-arguments to `func`. - - """ - if not self.is_alive() or self.__exit_req: - raise DelayQueueError('Could not process callback in stopped thread') - self._queue.put((func, args, kwargs)) - - -# The most straightforward way to implement this is to use 2 sequential delay -# queues, like on classic delay chain schematics in electronics. -# So, message path is: -# msg --> group delay if group msg, else no delay --> normal msg delay --> out -# This way OS threading scheduler cares of timings accuracy. -# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org) -class MessageQueue: - """ - Implements callback processing with proper delays to avoid hitting Telegram's message limits. - Contains two ``DelayQueue``, for group and for all messages, interconnected in delay chain. - Callables are processed through *group* ``DelayQueue``, then through *all* ``DelayQueue`` for - group-type messages. For non-group messages, only the *all* ``DelayQueue`` is used. - - .. deprecated:: 13.3 - :class:`telegram.ext.MessageQueue` in its current form is deprecated and will be reinvented - in a future release. See `this thread <https://git.io/JtDbF>`_ for a list of known bugs. - - Args: - all_burst_limit (:obj:`int`, optional): Number of maximum *all-type* callbacks to process - per time-window defined by :attr:`all_time_limit_ms`. Defaults to 30. - all_time_limit_ms (:obj:`int`, optional): Defines width of *all-type* time-window used when - each processing limit is calculated. Defaults to 1000 ms. - group_burst_limit (:obj:`int`, optional): Number of maximum *group-type* callbacks to - process per time-window defined by :attr:`group_time_limit_ms`. Defaults to 20. - group_time_limit_ms (:obj:`int`, optional): Defines width of *group-type* time-window used - when each processing limit is calculated. Defaults to 60000 ms. - exc_route (:obj:`callable`, optional): A callable, accepting one positional argument; used - to route exceptions from processor threads to main thread; is called on ``Exception`` - subclass exceptions. If not provided, exceptions are routed through dummy handler, - which re-raises them. - autostart (:obj:`bool`, optional): If :obj:`True`, processors are started immediately after - object's creation; if :obj:`False`, should be started manually by :attr:`start` method. - Defaults to :obj:`True`. - - """ - - def __init__( - self, - all_burst_limit: int = 30, - all_time_limit_ms: int = 1000, - group_burst_limit: int = 20, - group_time_limit_ms: int = 60000, - exc_route: Callable[[Exception], None] = None, - autostart: bool = True, - ): - warnings.warn( - 'MessageQueue in its current form is deprecated and will be reinvented in a future ' - 'release. See https://git.io/JtDbF for a list of known bugs.', - category=TelegramDeprecationWarning, - ) - - # create according delay queues, use composition - self._all_delayq = DelayQueue( - burst_limit=all_burst_limit, - time_limit_ms=all_time_limit_ms, - exc_route=exc_route, - autostart=autostart, - ) - self._group_delayq = DelayQueue( - burst_limit=group_burst_limit, - time_limit_ms=group_time_limit_ms, - exc_route=exc_route, - autostart=autostart, - ) - - def start(self) -> None: - """Method is used to manually start the ``MessageQueue`` processing.""" - self._all_delayq.start() - self._group_delayq.start() - - def stop(self, timeout: float = None) -> None: - """Stops the ``MessageQueue``.""" - self._group_delayq.stop(timeout=timeout) - self._all_delayq.stop(timeout=timeout) - - stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docstring if any - - def __call__(self, promise: Callable, is_group_msg: bool = False) -> Callable: - """ - Processes callables in throughput-limiting queues to avoid hitting limits (specified with - :attr:`burst_limit` and :attr:`time_limit`. - - Args: - promise (:obj:`callable`): Mainly the ``telegram.utils.promise.Promise`` (see Notes for - other callables), that is processed in delay queues. - is_group_msg (:obj:`bool`, optional): Defines whether ``promise`` would be processed in - group*+*all* ``DelayQueue``s (if set to :obj:`True`), or only through *all* - ``DelayQueue`` (if set to :obj:`False`), resulting in needed delays to avoid - hitting specified limits. Defaults to :obj:`False`. - - Note: - Method is designed to accept ``telegram.utils.promise.Promise`` as ``promise`` - argument, but other callables could be used too. For example, lambdas or simple - functions could be used to wrap original func to be called with needed args. In that - case, be sure that either wrapper func does not raise outside exceptions or the proper - :attr:`exc_route` handler is provided. - - Returns: - :obj:`callable`: Used as ``promise`` argument. - - """ - if not is_group_msg: # ignore middle group delay - self._all_delayq(promise) - else: # use middle group delay - self._group_delayq(self._all_delayq, promise) - return promise - - -def queuedmessage(method: Callable) -> Callable: - """A decorator to be used with :attr:`telegram.Bot` send* methods. - - Note: - As it probably wouldn't be a good idea to make this decorator a property, it has been coded - as decorator function, so it implies that first positional argument to wrapped MUST be - self. - - The next object attributes are used by decorator: - - Attributes: - self._is_messages_queued_default (:obj:`bool`): Value to provide class-defaults to - ``queued`` kwarg if not provided during wrapped method call. - self._msg_queue (:class:`telegram.ext.messagequeue.MessageQueue`): The actual - ``MessageQueue`` used to delay outbound messages according to specified time-limits. - - Wrapped method starts accepting the next kwargs: - - Args: - queued (:obj:`bool`, optional): If set to :obj:`True`, the ``MessageQueue`` is used to - process output messages. Defaults to `self._is_queued_out`. - isgroup (:obj:`bool`, optional): If set to :obj:`True`, the message is meant to be - group-type(as there's no obvious way to determine its type in other way at the moment). - Group-type messages could have additional processing delay according to limits set - in `self._out_queue`. Defaults to :obj:`False`. - - Returns: - ``telegram.utils.promise.Promise``: In case call is queued or original method's return - value if it's not. - - """ - - @functools.wraps(method) - def wrapped(self: 'Bot', *args: object, **kwargs: object) -> object: - # pylint: disable=W0212 - queued = kwargs.pop( - 'queued', self._is_messages_queued_default # type: ignore[attr-defined] - ) - isgroup = kwargs.pop('isgroup', False) - if queued: - prom = Promise(method, (self,) + args, kwargs) - return self._msg_queue(prom, isgroup) # type: ignore[attr-defined] - return method(self, *args, **kwargs) - - return wrapped diff --git a/telegramer/include/telegram/ext/picklepersistence.py b/telegramer/include/telegram/ext/picklepersistence.py deleted file mode 100644 index c3e4ba8..0000000 --- a/telegramer/include/telegram/ext/picklepersistence.py +++ /dev/null @@ -1,463 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the PicklePersistence class.""" -import pickle -from collections import defaultdict -from typing import ( - Any, - Dict, - Optional, - Tuple, - overload, - cast, - DefaultDict, -) - -from telegram.ext import BasePersistence -from .utils.types import UD, CD, BD, ConversationDict, CDCData -from .contexttypes import ContextTypes - - -class PicklePersistence(BasePersistence[UD, CD, BD]): - """Using python's builtin pickle for making your bot persistent. - - Warning: - :class:`PicklePersistence` will try to replace :class:`telegram.Bot` instances by - :attr:`REPLACED_BOT` and insert the bot set with - :meth:`telegram.ext.BasePersistence.set_bot` upon loading of the data. This is to ensure - that changes to the bot apply to the saved objects, too. If you change the bots token, this - may lead to e.g. ``Chat not found`` errors. For the limitations on replacing bots see - :meth:`telegram.ext.BasePersistence.replace_bot` and - :meth:`telegram.ext.BasePersistence.insert_bot`. - - Args: - filename (:obj:`str`): The filename for storing the pickle files. When :attr:`single_file` - is :obj:`False` this will be used as a prefix. - store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this - persistence class. Default is :obj:`True`. - store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this - persistence class. Default is :obj:`True`. - store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this - persistence class. Default is :obj:`True`. - store_callback_data (:obj:`bool`, optional): Whether callback_data should be saved by this - persistence class. Default is :obj:`False`. - - .. versionadded:: 13.6 - single_file (:obj:`bool`, optional): When :obj:`False` will store 5 separate files of - `filename_user_data`, `filename_bot_data`, `filename_chat_data`, - `filename_callback_data` and `filename_conversations`. Default is :obj:`True`. - on_flush (:obj:`bool`, optional): When :obj:`True` will only save to file when - :meth:`flush` is called and keep data in memory until that happens. When - :obj:`False` will store data on any transaction *and* on call to :meth:`flush`. - Default is :obj:`False`. - context_types (:class:`telegram.ext.ContextTypes`, optional): Pass an instance - of :class:`telegram.ext.ContextTypes` to customize the types used in the - ``context`` interface. If not passed, the defaults documented in - :class:`telegram.ext.ContextTypes` will be used. - - .. versionadded:: 13.6 - - Attributes: - filename (:obj:`str`): The filename for storing the pickle files. When :attr:`single_file` - is :obj:`False` this will be used as a prefix. - store_user_data (:obj:`bool`): Optional. Whether user_data should be saved by this - persistence class. - store_chat_data (:obj:`bool`): Optional. Whether chat_data should be saved by this - persistence class. - store_bot_data (:obj:`bool`): Optional. Whether bot_data should be saved by this - persistence class. - store_callback_data (:obj:`bool`): Optional. Whether callback_data be saved by this - persistence class. - - .. versionadded:: 13.6 - single_file (:obj:`bool`): Optional. When :obj:`False` will store 5 separate files of - `filename_user_data`, `filename_bot_data`, `filename_chat_data`, - `filename_callback_data` and `filename_conversations`. Default is :obj:`True`. - on_flush (:obj:`bool`, optional): When :obj:`True` will only save to file when - :meth:`flush` is called and keep data in memory until that happens. When - :obj:`False` will store data on any transaction *and* on call to :meth:`flush`. - Default is :obj:`False`. - context_types (:class:`telegram.ext.ContextTypes`): Container for the types used - in the ``context`` interface. - - .. versionadded:: 13.6 - """ - - __slots__ = ( - 'filename', - 'single_file', - 'on_flush', - 'user_data', - 'chat_data', - 'bot_data', - 'callback_data', - 'conversations', - 'context_types', - ) - - @overload - def __init__( - self: 'PicklePersistence[Dict, Dict, Dict]', - filename: str, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - single_file: bool = True, - on_flush: bool = False, - store_callback_data: bool = False, - ): - ... - - @overload - def __init__( - self: 'PicklePersistence[UD, CD, BD]', - filename: str, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - single_file: bool = True, - on_flush: bool = False, - store_callback_data: bool = False, - context_types: ContextTypes[Any, UD, CD, BD] = None, - ): - ... - - def __init__( - self, - filename: str, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - single_file: bool = True, - on_flush: bool = False, - store_callback_data: bool = False, - context_types: ContextTypes[Any, UD, CD, BD] = None, - ): - super().__init__( - store_user_data=store_user_data, - store_chat_data=store_chat_data, - store_bot_data=store_bot_data, - store_callback_data=store_callback_data, - ) - self.filename = filename - self.single_file = single_file - self.on_flush = on_flush - self.user_data: Optional[DefaultDict[int, UD]] = None - self.chat_data: Optional[DefaultDict[int, CD]] = None - self.bot_data: Optional[BD] = None - self.callback_data: Optional[CDCData] = None - self.conversations: Optional[Dict[str, Dict[Tuple, object]]] = None - self.context_types = cast(ContextTypes[Any, UD, CD, BD], context_types or ContextTypes()) - - def _load_singlefile(self) -> None: - try: - filename = self.filename - with open(self.filename, "rb") as file: - data = pickle.load(file) - self.user_data = defaultdict(self.context_types.user_data, data['user_data']) - self.chat_data = defaultdict(self.context_types.chat_data, data['chat_data']) - # For backwards compatibility with files not containing bot data - self.bot_data = data.get('bot_data', self.context_types.bot_data()) - self.callback_data = data.get('callback_data', {}) - self.conversations = data['conversations'] - except OSError: - self.conversations = {} - self.user_data = defaultdict(self.context_types.user_data) - self.chat_data = defaultdict(self.context_types.chat_data) - self.bot_data = self.context_types.bot_data() - self.callback_data = None - except pickle.UnpicklingError as exc: - raise TypeError(f"File {filename} does not contain valid pickle data") from exc - except Exception as exc: - raise TypeError(f"Something went wrong unpickling {filename}") from exc - - @staticmethod - def _load_file(filename: str) -> Any: - try: - with open(filename, "rb") as file: - return pickle.load(file) - except OSError: - return None - except pickle.UnpicklingError as exc: - raise TypeError(f"File {filename} does not contain valid pickle data") from exc - except Exception as exc: - raise TypeError(f"Something went wrong unpickling {filename}") from exc - - def _dump_singlefile(self) -> None: - with open(self.filename, "wb") as file: - data = { - 'conversations': self.conversations, - 'user_data': self.user_data, - 'chat_data': self.chat_data, - 'bot_data': self.bot_data, - 'callback_data': self.callback_data, - } - pickle.dump(data, file) - - @staticmethod - def _dump_file(filename: str, data: object) -> None: - with open(filename, "wb") as file: - pickle.dump(data, file) - - def get_user_data(self) -> DefaultDict[int, UD]: - """Returns the user_data from the pickle file if it exists or an empty :obj:`defaultdict`. - - Returns: - DefaultDict[:obj:`int`, :class:`telegram.ext.utils.types.UD`]: The restored user data. - """ - if self.user_data: - pass - elif not self.single_file: - filename = f"{self.filename}_user_data" - data = self._load_file(filename) - if not data: - data = defaultdict(self.context_types.user_data) - else: - data = defaultdict(self.context_types.user_data, data) - self.user_data = data - else: - self._load_singlefile() - return self.user_data # type: ignore[return-value] - - def get_chat_data(self) -> DefaultDict[int, CD]: - """Returns the chat_data from the pickle file if it exists or an empty :obj:`defaultdict`. - - Returns: - DefaultDict[:obj:`int`, :class:`telegram.ext.utils.types.CD`]: The restored chat data. - """ - if self.chat_data: - pass - elif not self.single_file: - filename = f"{self.filename}_chat_data" - data = self._load_file(filename) - if not data: - data = defaultdict(self.context_types.chat_data) - else: - data = defaultdict(self.context_types.chat_data, data) - self.chat_data = data - else: - self._load_singlefile() - return self.chat_data # type: ignore[return-value] - - def get_bot_data(self) -> BD: - """Returns the bot_data from the pickle file if it exists or an empty object of type - :class:`telegram.ext.utils.types.BD`. - - Returns: - :class:`telegram.ext.utils.types.BD`: The restored bot data. - """ - if self.bot_data: - pass - elif not self.single_file: - filename = f"{self.filename}_bot_data" - data = self._load_file(filename) - if not data: - data = self.context_types.bot_data() - self.bot_data = data - else: - self._load_singlefile() - return self.bot_data # type: ignore[return-value] - - def get_callback_data(self) -> Optional[CDCData]: - """Returns the callback data from the pickle file if it exists or :obj:`None`. - - .. versionadded:: 13.6 - - Returns: - Optional[:class:`telegram.ext.utils.types.CDCData`]: The restored meta data or - :obj:`None`, if no data was stored. - """ - if self.callback_data: - pass - elif not self.single_file: - filename = f"{self.filename}_callback_data" - data = self._load_file(filename) - if not data: - data = None - self.callback_data = data - else: - self._load_singlefile() - if self.callback_data is None: - return None - return self.callback_data[0], self.callback_data[1].copy() - - def get_conversations(self, name: str) -> ConversationDict: - """Returns the conversations from the pickle file if it exists or an empty dict. - - Args: - name (:obj:`str`): The handlers name. - - Returns: - :obj:`dict`: The restored conversations for the handler. - """ - if self.conversations: - pass - elif not self.single_file: - filename = f"{self.filename}_conversations" - data = self._load_file(filename) - if not data: - data = {name: {}} - self.conversations = data - else: - self._load_singlefile() - return self.conversations.get(name, {}).copy() # type: ignore[union-attr] - - def update_conversation( - self, name: str, key: Tuple[int, ...], new_state: Optional[object] - ) -> None: - """Will update the conversations for the given handler and depending on :attr:`on_flush` - save the pickle file. - - Args: - name (:obj:`str`): The handler's name. - key (:obj:`tuple`): The key the state is changed for. - new_state (:obj:`tuple` | :obj:`any`): The new state for the given key. - """ - if not self.conversations: - self.conversations = {} - if self.conversations.setdefault(name, {}).get(key) == new_state: - return - self.conversations[name][key] = new_state - if not self.on_flush: - if not self.single_file: - filename = f"{self.filename}_conversations" - self._dump_file(filename, self.conversations) - else: - self._dump_singlefile() - - def update_user_data(self, user_id: int, data: UD) -> None: - """Will update the user_data and depending on :attr:`on_flush` save the pickle file. - - Args: - user_id (:obj:`int`): The user the data might have been changed for. - data (:class:`telegram.ext.utils.types.UD`): The - :attr:`telegram.ext.Dispatcher.user_data` ``[user_id]``. - """ - if self.user_data is None: - self.user_data = defaultdict(self.context_types.user_data) - if self.user_data.get(user_id) == data: - return - self.user_data[user_id] = data - if not self.on_flush: - if not self.single_file: - filename = f"{self.filename}_user_data" - self._dump_file(filename, self.user_data) - else: - self._dump_singlefile() - - def update_chat_data(self, chat_id: int, data: CD) -> None: - """Will update the chat_data and depending on :attr:`on_flush` save the pickle file. - - Args: - chat_id (:obj:`int`): The chat the data might have been changed for. - data (:class:`telegram.ext.utils.types.CD`): The - :attr:`telegram.ext.Dispatcher.chat_data` ``[chat_id]``. - """ - if self.chat_data is None: - self.chat_data = defaultdict(self.context_types.chat_data) - if self.chat_data.get(chat_id) == data: - return - self.chat_data[chat_id] = data - if not self.on_flush: - if not self.single_file: - filename = f"{self.filename}_chat_data" - self._dump_file(filename, self.chat_data) - else: - self._dump_singlefile() - - def update_bot_data(self, data: BD) -> None: - """Will update the bot_data and depending on :attr:`on_flush` save the pickle file. - - Args: - data (:class:`telegram.ext.utils.types.BD`): The - :attr:`telegram.ext.Dispatcher.bot_data`. - """ - if self.bot_data == data: - return - self.bot_data = data - if not self.on_flush: - if not self.single_file: - filename = f"{self.filename}_bot_data" - self._dump_file(filename, self.bot_data) - else: - self._dump_singlefile() - - def update_callback_data(self, data: CDCData) -> None: - """Will update the callback_data (if changed) and depending on :attr:`on_flush` save the - pickle file. - - .. versionadded:: 13.6 - - Args: - data (:class:`telegram.ext.utils.types.CDCData`): The relevant data to restore - :class:`telegram.ext.CallbackDataCache`. - """ - if self.callback_data == data: - return - self.callback_data = (data[0], data[1].copy()) - if not self.on_flush: - if not self.single_file: - filename = f"{self.filename}_callback_data" - self._dump_file(filename, self.callback_data) - else: - self._dump_singlefile() - - def refresh_user_data(self, user_id: int, user_data: UD) -> None: - """Does nothing. - - .. versionadded:: 13.6 - .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_user_data` - """ - - def refresh_chat_data(self, chat_id: int, chat_data: CD) -> None: - """Does nothing. - - .. versionadded:: 13.6 - .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_chat_data` - """ - - def refresh_bot_data(self, bot_data: BD) -> None: - """Does nothing. - - .. versionadded:: 13.6 - .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_bot_data` - """ - - def flush(self) -> None: - """Will save all data in memory to pickle file(s).""" - if self.single_file: - if ( - self.user_data - or self.chat_data - or self.bot_data - or self.callback_data - or self.conversations - ): - self._dump_singlefile() - else: - if self.user_data: - self._dump_file(f"{self.filename}_user_data", self.user_data) - if self.chat_data: - self._dump_file(f"{self.filename}_chat_data", self.chat_data) - if self.bot_data: - self._dump_file(f"{self.filename}_bot_data", self.bot_data) - if self.callback_data: - self._dump_file(f"{self.filename}_callback_data", self.callback_data) - if self.conversations: - self._dump_file(f"{self.filename}_conversations", self.conversations) diff --git a/telegramer/include/telegram/ext/pollanswerhandler.py b/telegramer/include/telegram/ext/pollanswerhandler.py deleted file mode 100644 index 53172b2..0000000 --- a/telegramer/include/telegram/ext/pollanswerhandler.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the PollAnswerHandler class.""" - - -from telegram import Update - -from .handler import Handler -from .utils.types import CCT - - -class PollAnswerHandler(Handler[Update, CCT]): - """Handler class to handle Telegram updates that contain a poll answer. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = () - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - return isinstance(update, Update) and bool(update.poll_answer) diff --git a/telegramer/include/telegram/ext/pollhandler.py b/telegramer/include/telegram/ext/pollhandler.py deleted file mode 100644 index 0e2dee6..0000000 --- a/telegramer/include/telegram/ext/pollhandler.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the PollHandler classes.""" - - -from telegram import Update - -from .handler import Handler -from .utils.types import CCT - - -class PollHandler(Handler[Update, CCT]): - """Handler class to handle Telegram updates that contain a poll. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = () - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - return isinstance(update, Update) and bool(update.poll) diff --git a/telegramer/include/telegram/ext/precheckoutqueryhandler.py b/telegramer/include/telegram/ext/precheckoutqueryhandler.py deleted file mode 100644 index bbef18e..0000000 --- a/telegramer/include/telegram/ext/precheckoutqueryhandler.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the PreCheckoutQueryHandler class.""" - - -from telegram import Update - -from .handler import Handler -from .utils.types import CCT - - -class PreCheckoutQueryHandler(Handler[Update, CCT]): - """Handler class to handle Telegram PreCheckout callback queries. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - DEPRECATED: Please switch to context based callbacks. - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = () - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - return isinstance(update, Update) and bool(update.pre_checkout_query) diff --git a/telegramer/include/telegram/ext/regexhandler.py b/telegramer/include/telegram/ext/regexhandler.py deleted file mode 100644 index 8211d83..0000000 --- a/telegramer/include/telegram/ext/regexhandler.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# TODO: Remove allow_edited -"""This module contains the RegexHandler class.""" - -import warnings -from typing import TYPE_CHECKING, Callable, Dict, Optional, Pattern, TypeVar, Union, Any - -from telegram import Update -from telegram.ext import Filters, MessageHandler -from telegram.utils.deprecate import TelegramDeprecationWarning -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE -from telegram.ext.utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class RegexHandler(MessageHandler): - """Handler class to handle Telegram updates based on a regex. - - It uses a regular expression to check text messages. Read the documentation of the ``re`` - module for more information. The ``re.match`` function is used to determine if an update should - be handled by this handler. - - Note: - This handler is being deprecated. For the same use case use: - ``MessageHandler(Filters.regex(r'pattern'), callback)`` - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - - Args: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - message_updates (:obj:`bool`, optional): Should "normal" message updates be handled? - Default is :obj:`True`. - channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled? - Default is :obj:`True`. - edited_updates (:obj:`bool`, optional): Should "edited" message updates be handled? Default - is :obj:`False`. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Raises: - ValueError - - Attributes: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - callback (:obj:`callable`): The callback function for this handler. - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('pass_groups', 'pass_groupdict') - - def __init__( - self, - pattern: Union[str, Pattern], - callback: Callable[[Update, CCT], RT], - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - allow_edited: bool = False, # pylint: disable=W0613 - message_updates: bool = True, - channel_post_updates: bool = False, - edited_updates: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - warnings.warn( - 'RegexHandler is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - super().__init__( - Filters.regex(pattern), - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - message_updates=message_updates, - channel_post_updates=channel_post_updates, - edited_updates=edited_updates, - run_async=run_async, - ) - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: Update = None, - check_result: Optional[Union[bool, Dict[str, Any]]] = None, - ) -> Dict[str, object]: - """Pass the results of ``re.match(pattern, text).{groups(), groupdict()}`` to the - callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if - needed. - """ - optional_args = super().collect_optional_args(dispatcher, update, check_result) - if isinstance(check_result, dict): - if self.pass_groups: - optional_args['groups'] = check_result['matches'][0].groups() - if self.pass_groupdict: - optional_args['groupdict'] = check_result['matches'][0].groupdict() - return optional_args diff --git a/telegramer/include/telegram/ext/shippingqueryhandler.py b/telegramer/include/telegram/ext/shippingqueryhandler.py deleted file mode 100644 index d8b0218..0000000 --- a/telegramer/include/telegram/ext/shippingqueryhandler.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the ShippingQueryHandler class.""" - - -from telegram import Update -from .handler import Handler -from .utils.types import CCT - - -class ShippingQueryHandler(Handler[Update, CCT]): - """Handler class to handle Telegram shipping callback queries. - - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = () - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:class:`telegram.Update` | :obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - return isinstance(update, Update) and bool(update.shipping_query) diff --git a/telegramer/include/telegram/ext/stringcommandhandler.py b/telegramer/include/telegram/ext/stringcommandhandler.py deleted file mode 100644 index e3945ae..0000000 --- a/telegramer/include/telegram/ext/stringcommandhandler.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the StringCommandHandler class.""" - -from typing import TYPE_CHECKING, Callable, Dict, List, Optional, TypeVar, Union - -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .handler import Handler -from .utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class StringCommandHandler(Handler[str, CCT]): - """Handler class to handle string commands. Commands are string updates that start with ``/``. - The handler will add a ``list`` to the - :class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings, - which is the text following the command split on single whitespace characters. - - Note: - This handler is not used to handle Telegram :attr:`telegram.Update`, but strings manually - put in the queue. For example to send messages with the bot using command line or API. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - command (:obj:`str`): The command this handler should listen for. - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the - arguments passed to the command as a keyword argument called ``args``. It will contain - a list of strings, which is the text following the command split on single or - consecutive whitespace characters. Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - command (:obj:`str`): The command this handler should listen for. - callback (:obj:`callable`): The callback function for this handler. - pass_args (:obj:`bool`): Determines whether the handler should be passed - ``args``. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('command', 'pass_args') - - def __init__( - self, - command: str, - callback: Callable[[str, CCT], RT], - pass_args: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - run_async=run_async, - ) - self.command = command - self.pass_args = pass_args - - def check_update(self, update: object) -> Optional[List[str]]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:obj:`object`): The incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, str) and update.startswith('/'): - args = update[1:].split(' ') - if args[0] == self.command: - return args[1:] - return None - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: str = None, - check_result: Optional[List[str]] = None, - ) -> Dict[str, object]: - """Provide text after the command to the callback the ``args`` argument as list, split on - single whitespaces. - """ - optional_args = super().collect_optional_args(dispatcher, update, check_result) - if self.pass_args: - optional_args['args'] = check_result - return optional_args - - def collect_additional_context( - self, - context: CCT, - update: str, - dispatcher: 'Dispatcher', - check_result: Optional[List[str]], - ) -> None: - """Add text after the command to :attr:`CallbackContext.args` as list, split on single - whitespaces. - """ - context.args = check_result diff --git a/telegramer/include/telegram/ext/stringregexhandler.py b/telegramer/include/telegram/ext/stringregexhandler.py deleted file mode 100644 index 9be6b36..0000000 --- a/telegramer/include/telegram/ext/stringregexhandler.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the StringRegexHandler class.""" - -import re -from typing import TYPE_CHECKING, Callable, Dict, Match, Optional, Pattern, TypeVar, Union - -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .handler import Handler -from .utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class StringRegexHandler(Handler[str, CCT]): - """Handler class to handle string updates based on a regex which checks the update content. - - Read the documentation of the ``re`` module for more information. The ``re.match`` function is - used to determine if an update should be handled by this handler. - - Note: - This handler is not used to handle Telegram :attr:`telegram.Update`, but strings manually - put in the queue. For example to send messages with the bot using command line or API. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - callback (:obj:`callable`): The callback function for this handler. - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('pass_groups', 'pass_groupdict', 'pattern') - - def __init__( - self, - pattern: Union[str, Pattern], - callback: Callable[[str, CCT], RT], - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - run_async=run_async, - ) - - if isinstance(pattern, str): - pattern = re.compile(pattern) - - self.pattern = pattern - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict - - def check_update(self, update: object) -> Optional[Match]: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:obj:`object`): The incoming update. - - Returns: - :obj:`bool` - - """ - if isinstance(update, str): - match = re.match(self.pattern, update) - if match: - return match - return None - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: str = None, - check_result: Optional[Match] = None, - ) -> Dict[str, object]: - """Pass the results of ``re.match(pattern, update).{groups(), groupdict()}`` to the - callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if - needed. - """ - optional_args = super().collect_optional_args(dispatcher, update, check_result) - if self.pattern: - if self.pass_groups and check_result: - optional_args['groups'] = check_result.groups() - if self.pass_groupdict and check_result: - optional_args['groupdict'] = check_result.groupdict() - return optional_args - - def collect_additional_context( - self, - context: CCT, - update: str, - dispatcher: 'Dispatcher', - check_result: Optional[Match], - ) -> None: - """Add the result of ``re.match(pattern, update)`` to :attr:`CallbackContext.matches` as - list with one element. - """ - if self.pattern and check_result: - context.matches = [check_result] diff --git a/telegramer/include/telegram/ext/typehandler.py b/telegramer/include/telegram/ext/typehandler.py deleted file mode 100644 index 14029ab..0000000 --- a/telegramer/include/telegram/ext/typehandler.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the TypeHandler class.""" - -from typing import Callable, Type, TypeVar, Union -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE - -from .handler import Handler -from .utils.types import CCT - -RT = TypeVar('RT') -UT = TypeVar('UT') - - -class TypeHandler(Handler[UT, CCT]): - """Handler class to handle updates of custom types. - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - Args: - type (:obj:`type`): The ``type`` of updates this handler should process, as - determined by ``isinstance`` - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - strict (:obj:`bool`, optional): Use ``type`` instead of ``isinstance``. - Default is :obj:`False` - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Attributes: - type (:obj:`type`): The ``type`` of updates this handler should process. - callback (:obj:`callable`): The callback function for this handler. - strict (:obj:`bool`): Use ``type`` instead of ``isinstance``. Default is :obj:`False`. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('type', 'strict') - - def __init__( - self, - type: Type[UT], # pylint: disable=W0622 - callback: Callable[[UT, CCT], RT], - strict: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - super().__init__( - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - run_async=run_async, - ) - self.type = type # pylint: disable=E0237 - self.strict = strict # pylint: disable=E0237 - - def check_update(self, update: object) -> bool: - """Determines whether an update should be passed to this handlers :attr:`callback`. - - Args: - update (:obj:`object`): Incoming update. - - Returns: - :obj:`bool` - - """ - if not self.strict: - return isinstance(update, self.type) - return type(update) is self.type # pylint: disable=C0123 diff --git a/telegramer/include/telegram/ext/updater.py b/telegramer/include/telegram/ext/updater.py deleted file mode 100644 index b2c0512..0000000 --- a/telegramer/include/telegram/ext/updater.py +++ /dev/null @@ -1,890 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the class Updater, which tries to make creating Telegram bots intuitive.""" - -import logging -import ssl -import warnings -from queue import Queue -from signal import SIGABRT, SIGINT, SIGTERM, signal -from threading import Event, Lock, Thread, current_thread -from time import sleep -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Dict, - List, - Optional, - Tuple, - Union, - no_type_check, - Generic, - overload, -) - -from telegram import Bot, TelegramError -from telegram.error import InvalidToken, RetryAfter, TimedOut, Unauthorized -from telegram.ext import Dispatcher, JobQueue, ContextTypes, ExtBot -from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated -from telegram.utils.helpers import get_signal_name, DEFAULT_FALSE, DefaultValue -from telegram.utils.request import Request -from telegram.ext.utils.types import CCT, UD, CD, BD -from telegram.ext.utils.webhookhandler import WebhookAppClass, WebhookServer - -if TYPE_CHECKING: - from telegram.ext import BasePersistence, Defaults, CallbackContext - - -class Updater(Generic[CCT, UD, CD, BD]): - """ - This class, which employs the :class:`telegram.ext.Dispatcher`, provides a frontend to - :class:`telegram.Bot` to the programmer, so they can focus on coding the bot. Its purpose is to - receive the updates from Telegram and to deliver them to said dispatcher. It also runs in a - separate thread, so the user can interact with the bot, for example on the command line. The - dispatcher supports handlers for different kinds of data: Updates from Telegram, basic text - commands and even arbitrary types. The updater can be started as a polling service or, for - production, use a webhook to receive updates. This is achieved using the WebhookServer and - WebhookHandler classes. - - Note: - * You must supply either a :attr:`bot` or a :attr:`token` argument. - * If you supply a :attr:`bot`, you will need to pass :attr:`arbitrary_callback_data`, - and :attr:`defaults` to the bot instead of the :class:`telegram.ext.Updater`. In this - case, you'll have to use the class :class:`telegram.ext.ExtBot`. - - .. versionchanged:: 13.6 - - Args: - token (:obj:`str`, optional): The bot's token given by the @BotFather. - base_url (:obj:`str`, optional): Base_url for the bot. - base_file_url (:obj:`str`, optional): Base_file_url for the bot. - workers (:obj:`int`, optional): Amount of threads in the thread pool for functions - decorated with ``@run_async`` (ignored if `dispatcher` argument is used). - bot (:class:`telegram.Bot`, optional): A pre-initialized bot instance (ignored if - `dispatcher` argument is used). If a pre-initialized bot is used, it is the user's - responsibility to create it using a `Request` instance with a large enough connection - pool. - dispatcher (:class:`telegram.ext.Dispatcher`, optional): A pre-initialized dispatcher - instance. If a pre-initialized dispatcher is used, it is the user's responsibility to - create it with proper arguments. - private_key (:obj:`bytes`, optional): Private key for decryption of telegram passport data. - private_key_password (:obj:`bytes`, optional): Password for above private key. - user_sig_handler (:obj:`function`, optional): Takes ``signum, frame`` as positional - arguments. This will be called when a signal is received, defaults are (SIGINT, - SIGTERM, SIGABRT) settable with :attr:`idle`. - request_kwargs (:obj:`dict`, optional): Keyword args to control the creation of a - `telegram.utils.request.Request` object (ignored if `bot` or `dispatcher` argument is - used). The request_kwargs are very useful for the advanced users who would like to - control the default timeouts and/or control the proxy used for http communication. - use_context (:obj:`bool`, optional): If set to :obj:`True` uses the context based callback - API (ignored if `dispatcher` argument is used). Defaults to :obj:`True`. - **New users**: set this to :obj:`True`. - persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to - store data that should be persistent over restarts (ignored if `dispatcher` argument is - used). - defaults (:class:`telegram.ext.Defaults`, optional): An object containing default values to - be used if not set explicitly in the bot methods. - arbitrary_callback_data (:obj:`bool` | :obj:`int` | :obj:`None`, optional): Whether to - allow arbitrary objects as callback data for :class:`telegram.InlineKeyboardButton`. - Pass an integer to specify the maximum number of cached objects. For more details, - please see our wiki. Defaults to :obj:`False`. - - .. versionadded:: 13.6 - context_types (:class:`telegram.ext.ContextTypes`, optional): Pass an instance - of :class:`telegram.ext.ContextTypes` to customize the types used in the - ``context`` interface. If not passed, the defaults documented in - :class:`telegram.ext.ContextTypes` will be used. - - .. versionadded:: 13.6 - - Raises: - ValueError: If both :attr:`token` and :attr:`bot` are passed or none of them. - - - Attributes: - bot (:class:`telegram.Bot`): The bot used with this Updater. - user_sig_handler (:obj:`function`): Optional. Function to be called when a signal is - received. - update_queue (:obj:`Queue`): Queue for the updates. - job_queue (:class:`telegram.ext.JobQueue`): Jobqueue for the updater. - dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that handles the updates and - dispatches them to the handlers. - running (:obj:`bool`): Indicates if the updater is running. - persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to - store data that should be persistent over restarts. - use_context (:obj:`bool`): Optional. :obj:`True` if using context based callbacks. - - """ - - __slots__ = ( - 'persistence', - 'dispatcher', - 'user_sig_handler', - 'bot', - 'logger', - 'update_queue', - 'job_queue', - '__exception_event', - 'last_update_id', - 'running', - '_request', - 'is_idle', - 'httpd', - '__lock', - '__threads', - '__dict__', - ) - - @overload - def __init__( - self: 'Updater[CallbackContext, dict, dict, dict]', - token: str = None, - base_url: str = None, - workers: int = 4, - bot: Bot = None, - private_key: bytes = None, - private_key_password: bytes = None, - user_sig_handler: Callable = None, - request_kwargs: Dict[str, Any] = None, - persistence: 'BasePersistence' = None, # pylint: disable=E0601 - defaults: 'Defaults' = None, - use_context: bool = True, - base_file_url: str = None, - arbitrary_callback_data: Union[DefaultValue, bool, int, None] = DEFAULT_FALSE, - ): - ... - - @overload - def __init__( - self: 'Updater[CCT, UD, CD, BD]', - token: str = None, - base_url: str = None, - workers: int = 4, - bot: Bot = None, - private_key: bytes = None, - private_key_password: bytes = None, - user_sig_handler: Callable = None, - request_kwargs: Dict[str, Any] = None, - persistence: 'BasePersistence' = None, - defaults: 'Defaults' = None, - use_context: bool = True, - base_file_url: str = None, - arbitrary_callback_data: Union[DefaultValue, bool, int, None] = DEFAULT_FALSE, - context_types: ContextTypes[CCT, UD, CD, BD] = None, - ): - ... - - @overload - def __init__( - self: 'Updater[CCT, UD, CD, BD]', - user_sig_handler: Callable = None, - dispatcher: Dispatcher[CCT, UD, CD, BD] = None, - ): - ... - - def __init__( # type: ignore[no-untyped-def,misc] - self, - token: str = None, - base_url: str = None, - workers: int = 4, - bot: Bot = None, - private_key: bytes = None, - private_key_password: bytes = None, - user_sig_handler: Callable = None, - request_kwargs: Dict[str, Any] = None, - persistence: 'BasePersistence' = None, - defaults: 'Defaults' = None, - use_context: bool = True, - dispatcher=None, - base_file_url: str = None, - arbitrary_callback_data: Union[DefaultValue, bool, int, None] = DEFAULT_FALSE, - context_types: ContextTypes[CCT, UD, CD, BD] = None, - ): - - if defaults and bot: - warnings.warn( - 'Passing defaults to an Updater has no effect when a Bot is passed ' - 'as well. Pass them to the Bot instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - if arbitrary_callback_data is not DEFAULT_FALSE and bot: - warnings.warn( - 'Passing arbitrary_callback_data to an Updater has no ' - 'effect when a Bot is passed as well. Pass them to the Bot instead.', - stacklevel=2, - ) - - if dispatcher is None: - if (token is None) and (bot is None): - raise ValueError('`token` or `bot` must be passed') - if (token is not None) and (bot is not None): - raise ValueError('`token` and `bot` are mutually exclusive') - if (private_key is not None) and (bot is not None): - raise ValueError('`bot` and `private_key` are mutually exclusive') - else: - if bot is not None: - raise ValueError('`dispatcher` and `bot` are mutually exclusive') - if persistence is not None: - raise ValueError('`dispatcher` and `persistence` are mutually exclusive') - if use_context != dispatcher.use_context: - raise ValueError('`dispatcher` and `use_context` are mutually exclusive') - if context_types is not None: - raise ValueError('`dispatcher` and `context_types` are mutually exclusive') - if workers is not None: - raise ValueError('`dispatcher` and `workers` are mutually exclusive') - - self.logger = logging.getLogger(__name__) - self._request = None - - if dispatcher is None: - con_pool_size = workers + 4 - - if bot is not None: - self.bot = bot - if bot.request.con_pool_size < con_pool_size: - self.logger.warning( - 'Connection pool of Request object is smaller than optimal value (%s)', - con_pool_size, - ) - else: - # we need a connection pool the size of: - # * for each of the workers - # * 1 for Dispatcher - # * 1 for polling Updater (even if webhook is used, we can spare a connection) - # * 1 for JobQueue - # * 1 for main thread - if request_kwargs is None: - request_kwargs = {} - if 'con_pool_size' not in request_kwargs: - request_kwargs['con_pool_size'] = con_pool_size - self._request = Request(**request_kwargs) - self.bot = ExtBot( - token, # type: ignore[arg-type] - base_url, - base_file_url=base_file_url, - request=self._request, - private_key=private_key, - private_key_password=private_key_password, - defaults=defaults, - arbitrary_callback_data=( - False # type: ignore[arg-type] - if arbitrary_callback_data is DEFAULT_FALSE - else arbitrary_callback_data - ), - ) - self.update_queue: Queue = Queue() - self.job_queue = JobQueue() - self.__exception_event = Event() - self.persistence = persistence - self.dispatcher = Dispatcher( - self.bot, - self.update_queue, - job_queue=self.job_queue, - workers=workers, - exception_event=self.__exception_event, - persistence=persistence, - use_context=use_context, - context_types=context_types, - ) - self.job_queue.set_dispatcher(self.dispatcher) - else: - con_pool_size = dispatcher.workers + 4 - - self.bot = dispatcher.bot - if self.bot.request.con_pool_size < con_pool_size: - self.logger.warning( - 'Connection pool of Request object is smaller than optimal value (%s)', - con_pool_size, - ) - self.update_queue = dispatcher.update_queue - self.__exception_event = dispatcher.exception_event - self.persistence = dispatcher.persistence - self.job_queue = dispatcher.job_queue - self.dispatcher = dispatcher - - self.user_sig_handler = user_sig_handler - self.last_update_id = 0 - self.running = False - self.is_idle = False - self.httpd = None - self.__lock = Lock() - self.__threads: List[Thread] = [] - - def __setattr__(self, key: str, value: object) -> None: - if key.startswith('__'): - key = f"_{self.__class__.__name__}{key}" - if issubclass(self.__class__, Updater) and self.__class__ is not Updater: - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - - def _init_thread(self, target: Callable, name: str, *args: object, **kwargs: object) -> None: - thr = Thread( - target=self._thread_wrapper, - name=f"Bot:{self.bot.id}:{name}", - args=(target,) + args, - kwargs=kwargs, - ) - thr.start() - self.__threads.append(thr) - - def _thread_wrapper(self, target: Callable, *args: object, **kwargs: object) -> None: - thr_name = current_thread().name - self.logger.debug('%s - started', thr_name) - try: - target(*args, **kwargs) - except Exception: - self.__exception_event.set() - self.logger.exception('unhandled exception in %s', thr_name) - raise - self.logger.debug('%s - ended', thr_name) - - def start_polling( - self, - poll_interval: float = 0.0, - timeout: float = 10, - clean: bool = None, - bootstrap_retries: int = -1, - read_latency: float = 2.0, - allowed_updates: List[str] = None, - drop_pending_updates: bool = None, - ) -> Optional[Queue]: - """Starts polling updates from Telegram. - - Args: - poll_interval (:obj:`float`, optional): Time to wait between polling updates from - Telegram in seconds. Default is ``0.0``. - timeout (:obj:`float`, optional): Passed to :meth:`telegram.Bot.get_updates`. - drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on - Telegram servers before actually starting to poll. Default is :obj:`False`. - - .. versionadded :: 13.4 - clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``. - - .. deprecated:: 13.4 - Use ``drop_pending_updates`` instead. - bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the - :class:`telegram.ext.Updater` will retry on failures on the Telegram server. - - * < 0 - retry indefinitely (default) - * 0 - no retries - * > 0 - retry up to X times - - allowed_updates (List[:obj:`str`], optional): Passed to - :meth:`telegram.Bot.get_updates`. - read_latency (:obj:`float` | :obj:`int`, optional): Grace time in seconds for receiving - the reply from server. Will be added to the ``timeout`` value and used as the read - timeout from server (Default: ``2``). - - Returns: - :obj:`Queue`: The update queue that can be filled from the main thread. - - """ - if (clean is not None) and (drop_pending_updates is not None): - raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.') - - if clean is not None: - warnings.warn( - 'The argument `clean` of `start_polling` is deprecated. Please use ' - '`drop_pending_updates` instead.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean - - with self.__lock: - if not self.running: - self.running = True - - # Create & start threads - self.job_queue.start() - dispatcher_ready = Event() - polling_ready = Event() - self._init_thread(self.dispatcher.start, "dispatcher", ready=dispatcher_ready) - self._init_thread( - self._start_polling, - "updater", - poll_interval, - timeout, - read_latency, - bootstrap_retries, - drop_pending_updates, - allowed_updates, - ready=polling_ready, - ) - - self.logger.debug('Waiting for Dispatcher and polling to start') - dispatcher_ready.wait() - polling_ready.wait() - - # Return the update queue so the main thread can insert updates - return self.update_queue - return None - - def start_webhook( - self, - listen: str = '127.0.0.1', - port: int = 80, - url_path: str = '', - cert: str = None, - key: str = None, - clean: bool = None, - bootstrap_retries: int = 0, - webhook_url: str = None, - allowed_updates: List[str] = None, - force_event_loop: bool = None, - drop_pending_updates: bool = None, - ip_address: str = None, - max_connections: int = 40, - ) -> Optional[Queue]: - """ - Starts a small http server to listen for updates via webhook. If :attr:`cert` - and :attr:`key` are not provided, the webhook will be started directly on - http://listen:port/url_path, so SSL can be handled by another - application. Else, the webhook will be started on - https://listen:port/url_path. Also calls :meth:`telegram.Bot.set_webhook` as required. - - .. versionchanged:: 13.4 - :meth:`start_webhook` now *always* calls :meth:`telegram.Bot.set_webhook`, so pass - ``webhook_url`` instead of calling ``updater.bot.set_webhook(webhook_url)`` manually. - - Args: - listen (:obj:`str`, optional): IP-Address to listen on. Default ``127.0.0.1``. - port (:obj:`int`, optional): Port the bot should be listening on. Default ``80``. - url_path (:obj:`str`, optional): Path inside url. - cert (:obj:`str`, optional): Path to the SSL certificate file. - key (:obj:`str`, optional): Path to the SSL key file. - drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on - Telegram servers before actually starting to poll. Default is :obj:`False`. - - .. versionadded :: 13.4 - clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``. - - .. deprecated:: 13.4 - Use ``drop_pending_updates`` instead. - bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the - :class:`telegram.ext.Updater` will retry on failures on the Telegram server. - - * < 0 - retry indefinitely (default) - * 0 - no retries - * > 0 - retry up to X times - - webhook_url (:obj:`str`, optional): Explicitly specify the webhook url. Useful behind - NAT, reverse proxy, etc. Default is derived from ``listen``, ``port`` & - ``url_path``. - ip_address (:obj:`str`, optional): Passed to :meth:`telegram.Bot.set_webhook`. - - .. versionadded :: 13.4 - allowed_updates (List[:obj:`str`], optional): Passed to - :meth:`telegram.Bot.set_webhook`. - force_event_loop (:obj:`bool`, optional): Legacy parameter formerly used for a - workaround on Windows + Python 3.8+. No longer has any effect. - - .. deprecated:: 13.6 - Since version 13.6, ``tornade>=6.1`` is required, which resolves the former - issue. - - max_connections (:obj:`int`, optional): Passed to - :meth:`telegram.Bot.set_webhook`. - - .. versionadded:: 13.6 - - Returns: - :obj:`Queue`: The update queue that can be filled from the main thread. - - """ - if (clean is not None) and (drop_pending_updates is not None): - raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.') - - if clean is not None: - warnings.warn( - 'The argument `clean` of `start_webhook` is deprecated. Please use ' - '`drop_pending_updates` instead.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - if force_event_loop is not None: - warnings.warn( - 'The argument `force_event_loop` of `start_webhook` is deprecated and no longer ' - 'has any effect.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean - - with self.__lock: - if not self.running: - self.running = True - - # Create & start threads - webhook_ready = Event() - dispatcher_ready = Event() - self.job_queue.start() - self._init_thread(self.dispatcher.start, "dispatcher", dispatcher_ready) - self._init_thread( - self._start_webhook, - "updater", - listen, - port, - url_path, - cert, - key, - bootstrap_retries, - drop_pending_updates, - webhook_url, - allowed_updates, - ready=webhook_ready, - ip_address=ip_address, - max_connections=max_connections, - ) - - self.logger.debug('Waiting for Dispatcher and Webhook to start') - webhook_ready.wait() - dispatcher_ready.wait() - - # Return the update queue so the main thread can insert updates - return self.update_queue - return None - - @no_type_check - def _start_polling( - self, - poll_interval, - timeout, - read_latency, - bootstrap_retries, - drop_pending_updates, - allowed_updates, - ready=None, - ): # pragma: no cover - # Thread target of thread 'updater'. Runs in background, pulls - # updates from Telegram and inserts them in the update queue of the - # Dispatcher. - - self.logger.debug('Updater thread started (polling)') - - self._bootstrap( - bootstrap_retries, - drop_pending_updates=drop_pending_updates, - webhook_url='', - allowed_updates=None, - ) - - self.logger.debug('Bootstrap done') - - def polling_action_cb(): - updates = self.bot.get_updates( - self.last_update_id, - timeout=timeout, - read_latency=read_latency, - allowed_updates=allowed_updates, - ) - - if updates: - if not self.running: - self.logger.debug('Updates ignored and will be pulled again on restart') - else: - for update in updates: - self.update_queue.put(update) - self.last_update_id = updates[-1].update_id + 1 - - return True - - def polling_onerr_cb(exc): - # Put the error into the update queue and let the Dispatcher - # broadcast it - self.update_queue.put(exc) - - if ready is not None: - ready.set() - - self._network_loop_retry( - polling_action_cb, polling_onerr_cb, 'getting Updates', poll_interval - ) - - @no_type_check - def _network_loop_retry(self, action_cb, onerr_cb, description, interval): - """Perform a loop calling `action_cb`, retrying after network errors. - - Stop condition for loop: `self.running` evaluates :obj:`False` or return value of - `action_cb` evaluates :obj:`False`. - - Args: - action_cb (:obj:`callable`): Network oriented callback function to call. - onerr_cb (:obj:`callable`): Callback to call when TelegramError is caught. Receives the - exception object as a parameter. - description (:obj:`str`): Description text to use for logs and exception raised. - interval (:obj:`float` | :obj:`int`): Interval to sleep between each call to - `action_cb`. - - """ - self.logger.debug('Start network loop retry %s', description) - cur_interval = interval - while self.running: - try: - if not action_cb(): - break - except RetryAfter as exc: - self.logger.info('%s', exc) - cur_interval = 0.5 + exc.retry_after - except TimedOut as toe: - self.logger.debug('Timed out %s: %s', description, toe) - # If failure is due to timeout, we should retry asap. - cur_interval = 0 - except InvalidToken as pex: - self.logger.error('Invalid token; aborting') - raise pex - except TelegramError as telegram_exc: - self.logger.error('Error while %s: %s', description, telegram_exc) - onerr_cb(telegram_exc) - cur_interval = self._increase_poll_interval(cur_interval) - else: - cur_interval = interval - - if cur_interval: - sleep(cur_interval) - - @staticmethod - def _increase_poll_interval(current_interval: float) -> float: - # increase waiting times on subsequent errors up to 30secs - if current_interval == 0: - current_interval = 1 - elif current_interval < 30: - current_interval *= 1.5 - else: - current_interval = min(30.0, current_interval) - return current_interval - - @no_type_check - def _start_webhook( - self, - listen, - port, - url_path, - cert, - key, - bootstrap_retries, - drop_pending_updates, - webhook_url, - allowed_updates, - ready=None, - ip_address=None, - max_connections: int = 40, - ): - self.logger.debug('Updater thread started (webhook)') - - # Note that we only use the SSL certificate for the WebhookServer, if the key is also - # present. This is because the WebhookServer may not actually be in charge of performing - # the SSL handshake, e.g. in case a reverse proxy is used - use_ssl = cert is not None and key is not None - - if not url_path.startswith('/'): - url_path = f'/{url_path}' - - # Create Tornado app instance - app = WebhookAppClass(url_path, self.bot, self.update_queue) - - # Form SSL Context - # An SSLError is raised if the private key does not match with the certificate - if use_ssl: - try: - ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_ctx.load_cert_chain(cert, key) - except ssl.SSLError as exc: - raise TelegramError('Invalid SSL Certificate') from exc - else: - ssl_ctx = None - - # Create and start server - self.httpd = WebhookServer(listen, port, app, ssl_ctx) - - if not webhook_url: - webhook_url = self._gen_webhook_url(listen, port, url_path) - - # We pass along the cert to the webhook if present. - cert_file = open(cert, 'rb') if cert is not None else None - self._bootstrap( - max_retries=bootstrap_retries, - drop_pending_updates=drop_pending_updates, - webhook_url=webhook_url, - allowed_updates=allowed_updates, - cert=cert_file, - ip_address=ip_address, - max_connections=max_connections, - ) - if cert_file is not None: - cert_file.close() - - self.httpd.serve_forever(ready=ready) - - @staticmethod - def _gen_webhook_url(listen: str, port: int, url_path: str) -> str: - return f'https://{listen}:{port}{url_path}' - - @no_type_check - def _bootstrap( - self, - max_retries, - drop_pending_updates, - webhook_url, - allowed_updates, - cert=None, - bootstrap_interval=5, - ip_address=None, - max_connections: int = 40, - ): - retries = [0] - - def bootstrap_del_webhook(): - self.logger.debug('Deleting webhook') - if drop_pending_updates: - self.logger.debug('Dropping pending updates from Telegram server') - self.bot.delete_webhook(drop_pending_updates=drop_pending_updates) - return False - - def bootstrap_set_webhook(): - self.logger.debug('Setting webhook') - if drop_pending_updates: - self.logger.debug('Dropping pending updates from Telegram server') - self.bot.set_webhook( - url=webhook_url, - certificate=cert, - allowed_updates=allowed_updates, - ip_address=ip_address, - drop_pending_updates=drop_pending_updates, - max_connections=max_connections, - ) - return False - - def bootstrap_onerr_cb(exc): - if not isinstance(exc, Unauthorized) and (max_retries < 0 or retries[0] < max_retries): - retries[0] += 1 - self.logger.warning( - 'Failed bootstrap phase; try=%s max_retries=%s', retries[0], max_retries - ) - else: - self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc) - raise exc - - # Dropping pending updates from TG can be efficiently done with the drop_pending_updates - # parameter of delete/start_webhook, even in the case of polling. Also we want to make - # sure that no webhook is configured in case of polling, so we just always call - # delete_webhook for polling - if drop_pending_updates or not webhook_url: - self._network_loop_retry( - bootstrap_del_webhook, - bootstrap_onerr_cb, - 'bootstrap del webhook', - bootstrap_interval, - ) - retries[0] = 0 - - # Restore/set webhook settings, if needed. Again, we don't know ahead if a webhook is set, - # so we set it anyhow. - if webhook_url: - self._network_loop_retry( - bootstrap_set_webhook, - bootstrap_onerr_cb, - 'bootstrap set webhook', - bootstrap_interval, - ) - - def stop(self) -> None: - """Stops the polling/webhook thread, the dispatcher and the job queue.""" - self.job_queue.stop() - with self.__lock: - if self.running or self.dispatcher.has_running_threads: - self.logger.debug('Stopping Updater and Dispatcher...') - - self.running = False - - self._stop_httpd() - self._stop_dispatcher() - self._join_threads() - - # Stop the Request instance only if it was created by the Updater - if self._request: - self._request.stop() - - @no_type_check - def _stop_httpd(self) -> None: - if self.httpd: - self.logger.debug( - 'Waiting for current webhook connection to be ' - 'closed... Send a Telegram message to the bot to exit ' - 'immediately.' - ) - self.httpd.shutdown() - self.httpd = None - - @no_type_check - def _stop_dispatcher(self) -> None: - self.logger.debug('Requesting Dispatcher to stop...') - self.dispatcher.stop() - - @no_type_check - def _join_threads(self) -> None: - for thr in self.__threads: - self.logger.debug('Waiting for %s thread to end', thr.name) - thr.join() - self.logger.debug('%s thread has ended', thr.name) - self.__threads = [] - - @no_type_check - def _signal_handler(self, signum, frame) -> None: - self.is_idle = False - if self.running: - self.logger.info( - 'Received signal %s (%s), stopping...', signum, get_signal_name(signum) - ) - if self.persistence: - # Update user_data, chat_data and bot_data before flushing - self.dispatcher.update_persistence() - self.persistence.flush() - self.stop() - if self.user_sig_handler: - self.user_sig_handler(signum, frame) - else: - self.logger.warning('Exiting immediately!') - # pylint: disable=C0415,W0212 - import os - - os._exit(1) - - def idle(self, stop_signals: Union[List, Tuple] = (SIGINT, SIGTERM, SIGABRT)) -> None: - """Blocks until one of the signals are received and stops the updater. - - Args: - stop_signals (:obj:`list` | :obj:`tuple`): List containing signals from the signal - module that should be subscribed to. :meth:`Updater.stop()` will be called on - receiving one of those signals. Defaults to (``SIGINT``, ``SIGTERM``, ``SIGABRT``). - - """ - for sig in stop_signals: - signal(sig, self._signal_handler) - - self.is_idle = True - - while self.is_idle: - sleep(1) diff --git a/telegramer/include/telegram/ext/utils/__init__.py b/telegramer/include/telegram/ext/utils/__init__.py deleted file mode 100644 index b624e1e..0000000 --- a/telegramer/include/telegram/ext/utils/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. diff --git a/telegramer/include/telegram/ext/utils/promise.py b/telegramer/include/telegram/ext/utils/promise.py deleted file mode 100644 index 86b3981..0000000 --- a/telegramer/include/telegram/ext/utils/promise.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the Promise class.""" - -import logging -from threading import Event -from typing import Callable, List, Optional, Tuple, TypeVar, Union - -from telegram.utils.deprecate import set_new_attribute_deprecated -from telegram.utils.types import JSONDict - -RT = TypeVar('RT') - - -logger = logging.getLogger(__name__) - - -class Promise: - """A simple Promise implementation for use with the run_async decorator, DelayQueue etc. - - Args: - pooled_function (:obj:`callable`): The callable that will be called concurrently. - args (:obj:`list` | :obj:`tuple`): Positional arguments for :attr:`pooled_function`. - kwargs (:obj:`dict`): Keyword arguments for :attr:`pooled_function`. - update (:class:`telegram.Update` | :obj:`object`, optional): The update this promise is - associated with. - error_handling (:obj:`bool`, optional): Whether exceptions raised by :attr:`func` - may be handled by error handlers. Defaults to :obj:`True`. - - Attributes: - pooled_function (:obj:`callable`): The callable that will be called concurrently. - args (:obj:`list` | :obj:`tuple`): Positional arguments for :attr:`pooled_function`. - kwargs (:obj:`dict`): Keyword arguments for :attr:`pooled_function`. - done (:obj:`threading.Event`): Is set when the result is available. - update (:class:`telegram.Update` | :obj:`object`): Optional. The update this promise is - associated with. - error_handling (:obj:`bool`): Optional. Whether exceptions raised by :attr:`func` - may be handled by error handlers. Defaults to :obj:`True`. - - """ - - __slots__ = ( - 'pooled_function', - 'args', - 'kwargs', - 'update', - 'error_handling', - 'done', - '_done_callback', - '_result', - '_exception', - '__dict__', - ) - - # TODO: Remove error_handling parameter once we drop the @run_async decorator - def __init__( - self, - pooled_function: Callable[..., RT], - args: Union[List, Tuple], - kwargs: JSONDict, - update: object = None, - error_handling: bool = True, - ): - self.pooled_function = pooled_function - self.args = args - self.kwargs = kwargs - self.update = update - self.error_handling = error_handling - self.done = Event() - self._done_callback: Optional[Callable] = None - self._result: Optional[RT] = None - self._exception: Optional[Exception] = None - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - def run(self) -> None: - """Calls the :attr:`pooled_function` callable.""" - try: - self._result = self.pooled_function(*self.args, **self.kwargs) - - except Exception as exc: - self._exception = exc - - finally: - self.done.set() - if self._exception is None and self._done_callback: - try: - self._done_callback(self.result()) - except Exception as exc: - logger.warning( - "`done_callback` of a Promise raised the following exception." - " The exception won't be handled by error handlers." - ) - logger.warning("Full traceback:", exc_info=exc) - - def __call__(self) -> None: - self.run() - - def result(self, timeout: float = None) -> Optional[RT]: - """Return the result of the ``Promise``. - - Args: - timeout (:obj:`float`, optional): Maximum time in seconds to wait for the result to be - calculated. ``None`` means indefinite. Default is ``None``. - - Returns: - Returns the return value of :attr:`pooled_function` or ``None`` if the ``timeout`` - expires. - - Raises: - object exception raised by :attr:`pooled_function`. - """ - self.done.wait(timeout=timeout) - if self._exception is not None: - raise self._exception # pylint: disable=raising-bad-type - return self._result - - def add_done_callback(self, callback: Callable) -> None: - """ - Callback to be run when :class:`telegram.ext.utils.promise.Promise` becomes done. - - Note: - Callback won't be called if :attr:`pooled_function` - raises an exception. - - Args: - callback (:obj:`callable`): The callable that will be called when promise is done. - callback will be called by passing ``Promise.result()`` as only positional argument. - - """ - if self.done.wait(0): - callback(self.result()) - else: - self._done_callback = callback - - @property - def exception(self) -> Optional[Exception]: - """The exception raised by :attr:`pooled_function` or ``None`` if no exception has been - raised (yet). - """ - return self._exception diff --git a/telegramer/include/telegram/ext/utils/types.py b/telegramer/include/telegram/ext/utils/types.py deleted file mode 100644 index a63e528..0000000 --- a/telegramer/include/telegram/ext/utils/types.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains custom typing aliases. - -.. versionadded:: 13.6 -""" -from typing import TypeVar, TYPE_CHECKING, Tuple, List, Dict, Any, Optional - -if TYPE_CHECKING: - from telegram.ext import CallbackContext # noqa: F401 - - -ConversationDict = Dict[Tuple[int, ...], Optional[object]] -"""Dicts as maintained by the :class:`telegram.ext.ConversationHandler`. - - .. versionadded:: 13.6 -""" - -CDCData = Tuple[List[Tuple[str, float, Dict[str, Any]]], Dict[str, str]] -"""Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :obj:`any`]]], \ - Dict[:obj:`str`, :obj:`str`]]: Data returned by - :attr:`telegram.ext.CallbackDataCache.persistence_data`. - - .. versionadded:: 13.6 -""" - -CCT = TypeVar('CCT', bound='CallbackContext') -"""An instance of :class:`telegram.ext.CallbackContext` or a custom subclass. - -.. versionadded:: 13.6 -""" -UD = TypeVar('UD') -"""Type of the user data for a single user. - -.. versionadded:: 13.6 -""" -CD = TypeVar('CD') -"""Type of the chat data for a single user. - -.. versionadded:: 13.6 -""" -BD = TypeVar('BD') -"""Type of the bot data. - -.. versionadded:: 13.6 -""" diff --git a/telegramer/include/telegram/ext/utils/webhookhandler.py b/telegramer/include/telegram/ext/utils/webhookhandler.py deleted file mode 100644 index 64e639b..0000000 --- a/telegramer/include/telegram/ext/utils/webhookhandler.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0114 - -import logging -from queue import Queue -from ssl import SSLContext -from threading import Event, Lock -from typing import TYPE_CHECKING, Any, Optional - -import tornado.web -from tornado import httputil -from tornado.httpserver import HTTPServer -from tornado.ioloop import IOLoop - -from telegram import Update -from telegram.ext import ExtBot -from telegram.utils.deprecate import set_new_attribute_deprecated -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] - - -class WebhookServer: - __slots__ = ( - 'http_server', - 'listen', - 'port', - 'loop', - 'logger', - 'is_running', - 'server_lock', - 'shutdown_lock', - '__dict__', - ) - - def __init__( - self, listen: str, port: int, webhook_app: 'WebhookAppClass', ssl_ctx: SSLContext - ): - self.http_server = HTTPServer(webhook_app, ssl_options=ssl_ctx) - self.listen = listen - self.port = port - self.loop: Optional[IOLoop] = None - self.logger = logging.getLogger(__name__) - self.is_running = False - self.server_lock = Lock() - self.shutdown_lock = Lock() - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - def serve_forever(self, ready: Event = None) -> None: - with self.server_lock: - IOLoop().make_current() - self.is_running = True - self.logger.debug('Webhook Server started.') - self.loop = IOLoop.current() - self.http_server.listen(self.port, address=self.listen) - - if ready is not None: - ready.set() - - self.loop.start() - self.logger.debug('Webhook Server stopped.') - self.is_running = False - - def shutdown(self) -> None: - with self.shutdown_lock: - if not self.is_running: - self.logger.warning('Webhook Server already stopped.') - return - self.loop.add_callback(self.loop.stop) # type: ignore - - def handle_error(self, request: object, client_address: str) -> None: # pylint: disable=W0613 - """Handle an error gracefully.""" - self.logger.debug( - 'Exception happened during processing of request from %s', - client_address, - exc_info=True, - ) - - -class WebhookAppClass(tornado.web.Application): - def __init__(self, webhook_path: str, bot: 'Bot', update_queue: Queue): - self.shared_objects = {"bot": bot, "update_queue": update_queue} - handlers = [(rf"{webhook_path}/?", WebhookHandler, self.shared_objects)] # noqa - tornado.web.Application.__init__(self, handlers) # type: ignore - - def log_request(self, handler: tornado.web.RequestHandler) -> None: # skipcq: PTC-W0049 - pass - - -# WebhookHandler, process webhook calls -# pylint: disable=W0223 -class WebhookHandler(tornado.web.RequestHandler): - SUPPORTED_METHODS = ["POST"] # type: ignore - - def __init__( - self, - application: tornado.web.Application, - request: httputil.HTTPServerRequest, - **kwargs: JSONDict, - ): - super().__init__(application, request, **kwargs) - self.logger = logging.getLogger(__name__) - - def initialize(self, bot: 'Bot', update_queue: Queue) -> None: - # pylint: disable=W0201 - self.bot = bot - self.update_queue = update_queue - - def set_default_headers(self) -> None: - self.set_header("Content-Type", 'application/json; charset="utf-8"') - - def post(self) -> None: - self.logger.debug('Webhook triggered') - self._validate_post() - json_string = self.request.body.decode() - data = json.loads(json_string) - self.set_status(200) - self.logger.debug('Webhook received data: %s', json_string) - update = Update.de_json(data, self.bot) - if update: - self.logger.debug('Received Update with ID %d on Webhook', update.update_id) - # handle arbitrary callback data, if necessary - if isinstance(self.bot, ExtBot): - self.bot.insert_callback_data(update) - self.update_queue.put(update) - - def _validate_post(self) -> None: - ct_header = self.request.headers.get("Content-Type", None) - if ct_header != 'application/json': - raise tornado.web.HTTPError(403) - - def write_error(self, status_code: int, **kwargs: Any) -> None: - """Log an arbitrary message. - - This is used by all other logging functions. - - It overrides ``BaseHTTPRequestHandler.log_message``, which logs to ``sys.stderr``. - - The first argument, FORMAT, is a format string for the message to be logged. If the format - string contains any % escapes requiring parameters, they should be specified as subsequent - arguments (it's just like printf!). - - The client ip is prefixed to every message. - - """ - super().write_error(status_code, **kwargs) - self.logger.debug( - "%s - - %s", - self.request.remote_ip, - "Exception in WebhookHandler", - exc_info=kwargs['exc_info'], - ) diff --git a/telegramer/include/telegram/files/__init__.py b/telegramer/include/telegram/files/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/files/animation.py b/telegramer/include/telegram/files/animation.py deleted file mode 100644 index a9f2ab5..0000000 --- a/telegramer/include/telegram/files/animation.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Animation.""" -from typing import TYPE_CHECKING, Any, Optional - -from telegram import PhotoSize, TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class Animation(TelegramObject): - """This object represents an animation file (GIF or H.264/MPEG-4 AVC video without sound). - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Video width as defined by sender. - height (:obj:`int`): Video height as defined by sender. - duration (:obj:`int`): Duration of the video in seconds as defined by sender. - thumb (:class:`telegram.PhotoSize`, optional): Animation thumbnail as defined by sender. - file_name (:obj:`str`, optional): Original animation filename as defined by sender. - mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): File identifier. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Video width as defined by sender. - height (:obj:`int`): Video height as defined by sender. - duration (:obj:`int`): Duration of the video in seconds as defined by sender. - thumb (:class:`telegram.PhotoSize`): Optional. Animation thumbnail as defined by sender. - file_name (:obj:`str`): Optional. Original animation filename as defined by sender. - mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'width', - 'file_id', - 'file_size', - 'file_name', - 'thumb', - 'duration', - 'mime_type', - 'height', - 'file_unique_id', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - width: int, - height: int, - duration: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.width = int(width) - self.height = int(height) - self.duration = duration - # Optionals - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Animation']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - - return cls(bot=bot, **data) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/audio.py b/telegramer/include/telegram/files/audio.py deleted file mode 100644 index af6683c..0000000 --- a/telegramer/include/telegram/files/audio.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Audio.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import PhotoSize, TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class Audio(TelegramObject): - """This object represents an audio file to be treated as music by the Telegram clients. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be - the same over time and for different bots. Can't be used to download or reuse the file. - duration (:obj:`int`): Duration of the audio in seconds as defined by sender. - performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio - tags. - title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags. - file_name (:obj:`str`, optional): Original filename as defined by sender. - mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. - file_size (:obj:`int`, optional): File size. - thumb (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to - which the music file belongs. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - duration (:obj:`int`): Duration of the audio in seconds. - performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio - tags. - title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags. - file_name (:obj:`str`): Optional. Original filename as defined by sender. - mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender. - file_size (:obj:`int`): Optional. File size. - thumb (:class:`telegram.PhotoSize`): Optional. Thumbnail of the album cover to - which the music file belongs. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'file_id', - 'bot', - 'file_size', - 'file_name', - 'thumb', - 'title', - 'duration', - 'performer', - 'mime_type', - 'file_unique_id', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - duration: int, - performer: str = None, - title: str = None, - mime_type: str = None, - file_size: int = None, - thumb: PhotoSize = None, - bot: 'Bot' = None, - file_name: str = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.duration = int(duration) - # Optionals - self.performer = performer - self.title = title - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.thumb = thumb - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Audio']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - - return cls(bot=bot, **data) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/chatphoto.py b/telegramer/include/telegram/files/chatphoto.py deleted file mode 100644 index 2f55bf9..0000000 --- a/telegramer/include/telegram/files/chatphoto.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ChatPhoto.""" -from typing import TYPE_CHECKING, Any - -from telegram import TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class ChatPhoto(TelegramObject): - """This object represents a chat photo. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`small_file_unique_id` and :attr:`big_file_unique_id` are - equal. - - Args: - small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This - file_id can be used only for photo download and only for as long - as the photo is not changed. - small_file_unique_id (:obj:`str`): Unique file identifier of small (160x160) chat photo, - which is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - big_file_id (:obj:`str`): Unique file identifier of big (640x640) chat photo. This file_id - can be used only for photo download and only for as long as the photo is not changed. - big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo, - which is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - small_file_id (:obj:`str`): File identifier of small (160x160) chat photo. - This file_id can be used only for photo download and only for as long - as the photo is not changed. - small_file_unique_id (:obj:`str`): Unique file identifier of small (160x160) chat photo, - which is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - big_file_id (:obj:`str`): File identifier of big (640x640) chat photo. - This file_id can be used only for photo download and only for as long as - the photo is not changed. - big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo, - which is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - - """ - - __slots__ = ( - 'big_file_unique_id', - 'bot', - 'small_file_id', - 'small_file_unique_id', - 'big_file_id', - '_id_attrs', - ) - - def __init__( - self, - small_file_id: str, - small_file_unique_id: str, - big_file_id: str, - big_file_unique_id: str, - bot: 'Bot' = None, - **_kwargs: Any, - ): - self.small_file_id = small_file_id - self.small_file_unique_id = small_file_unique_id - self.big_file_id = big_file_id - self.big_file_unique_id = big_file_unique_id - - self.bot = bot - - self._id_attrs = ( - self.small_file_unique_id, - self.big_file_unique_id, - ) - - def get_small_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the - small (160x160) chat photo - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file( - file_id=self.small_file_id, timeout=timeout, api_kwargs=api_kwargs - ) - - def get_big_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the - big (640x640) chat photo - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.big_file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/contact.py b/telegramer/include/telegram/files/contact.py deleted file mode 100644 index cee769f..0000000 --- a/telegramer/include/telegram/files/contact.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Contact.""" - -from typing import Any - -from telegram import TelegramObject - - -class Contact(TelegramObject): - """This object represents a phone contact. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`phone_number` is equal. - - Args: - phone_number (:obj:`str`): Contact's phone number. - first_name (:obj:`str`): Contact's first name. - last_name (:obj:`str`, optional): Contact's last name. - user_id (:obj:`int`, optional): Contact's user identifier in Telegram. - vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - phone_number (:obj:`str`): Contact's phone number. - first_name (:obj:`str`): Contact's first name. - last_name (:obj:`str`): Optional. Contact's last name. - user_id (:obj:`int`): Optional. Contact's user identifier in Telegram. - vcard (:obj:`str`): Optional. Additional data about the contact in the form of a vCard. - - """ - - __slots__ = ('vcard', 'user_id', 'first_name', 'last_name', 'phone_number', '_id_attrs') - - def __init__( - self, - phone_number: str, - first_name: str, - last_name: str = None, - user_id: int = None, - vcard: str = None, - **_kwargs: Any, - ): - # Required - self.phone_number = str(phone_number) - self.first_name = first_name - # Optionals - self.last_name = last_name - self.user_id = user_id - self.vcard = vcard - - self._id_attrs = (self.phone_number,) diff --git a/telegramer/include/telegram/files/document.py b/telegramer/include/telegram/files/document.py deleted file mode 100644 index a0ffcef..0000000 --- a/telegramer/include/telegram/files/document.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Document.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import PhotoSize, TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class Document(TelegramObject): - """This object represents a general file - (as opposed to photos, voice messages and audio files). - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which is supposed to be - the same over time and for different bots. Can't be used to download or reuse the file. - thumb (:class:`telegram.PhotoSize`, optional): Document thumbnail as defined by sender. - file_name (:obj:`str`, optional): Original filename as defined by sender. - mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): File identifier. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - thumb (:class:`telegram.PhotoSize`): Optional. Document thumbnail. - file_name (:obj:`str`): Original filename. - mime_type (:obj:`str`): Optional. MIME type of the file. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'file_id', - 'file_size', - 'file_name', - 'thumb', - 'mime_type', - 'file_unique_id', - '_id_attrs', - ) - - _id_keys = ('file_id',) - - def __init__( - self, - file_id: str, - file_unique_id: str, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - # Optionals - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Document']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - - return cls(bot=bot, **data) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/file.py b/telegramer/include/telegram/files/file.py deleted file mode 100644 index ca77f9d..0000000 --- a/telegramer/include/telegram/files/file.py +++ /dev/null @@ -1,213 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram File.""" -import os -import shutil -import urllib.parse as urllib_parse -from base64 import b64decode -from os.path import basename -from typing import IO, TYPE_CHECKING, Any, Optional, Union - -from telegram import TelegramObject -from telegram.passport.credentials import decrypt -from telegram.utils.helpers import is_local_file - -if TYPE_CHECKING: - from telegram import Bot, FileCredentials - - -class File(TelegramObject): - """ - This object represents a file ready to be downloaded. The file can be downloaded with - :attr:`download`. It is guaranteed that the link will be valid for at least 1 hour. When the - link expires, a new one can be requested by calling :meth:`telegram.Bot.get_file`. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Note: - * Maximum file size to download is 20 MB. - * If you obtain an instance of this class from :attr:`telegram.PassportFile.get_file`, - then it will automatically be decrypted as it downloads when you call :attr:`download()`. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - file_size (:obj:`int`, optional): Optional. File size, if known. - file_path (:obj:`str`, optional): File path. Use :attr:`download` to get the file. - bot (:obj:`telegram.Bot`, optional): Bot to use with shortcut method. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - file_size (:obj:`str`): Optional. File size. - file_path (:obj:`str`): Optional. File path. Use :attr:`download` to get the file. - - """ - - __slots__ = ( - 'bot', - 'file_id', - 'file_size', - 'file_unique_id', - 'file_path', - '_credentials', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - bot: 'Bot' = None, - file_size: int = None, - file_path: str = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - # Optionals - self.file_size = file_size - self.file_path = file_path - self.bot = bot - self._credentials: Optional['FileCredentials'] = None - - self._id_attrs = (self.file_unique_id,) - - def download( - self, custom_path: str = None, out: IO = None, timeout: int = None - ) -> Union[str, IO]: - """ - Download this file. By default, the file is saved in the current working directory with its - original filename as reported by Telegram. If the file has no filename, it the file ID will - be used as filename. If a :attr:`custom_path` is supplied, it will be saved to that path - instead. If :attr:`out` is defined, the file contents will be saved to that object using - the ``out.write`` method. - - Note: - * :attr:`custom_path` and :attr:`out` are mutually exclusive. - * If neither :attr:`custom_path` nor :attr:`out` is provided and :attr:`file_path` is - the path of a local file (which is the case when a Bot API Server is running in - local mode), this method will just return the path. - - Args: - custom_path (:obj:`str`, optional): Custom path. - out (:obj:`io.BufferedWriter`, optional): A file-like object. Must be opened for - writing in binary mode, if applicable. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - - Returns: - :obj:`str` | :obj:`io.BufferedWriter`: The same object as :attr:`out` if specified. - Otherwise, returns the filename downloaded to or the file path of the local file. - - Raises: - ValueError: If both :attr:`custom_path` and :attr:`out` are passed. - - """ - if custom_path is not None and out is not None: - raise ValueError('custom_path and out are mutually exclusive') - - local_file = is_local_file(self.file_path) - - if local_file: - url = self.file_path - else: - # Convert any UTF-8 char into a url encoded ASCII string. - url = self._get_encoded_url() - - if out: - if local_file: - with open(url, 'rb') as file: - buf = file.read() - else: - buf = self.bot.request.retrieve(url) - if self._credentials: - buf = decrypt( - b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf - ) - out.write(buf) - return out - - if custom_path and local_file: - shutil.copyfile(self.file_path, custom_path) - return custom_path - - if custom_path: - filename = custom_path - elif local_file: - return self.file_path - elif self.file_path: - filename = basename(self.file_path) - else: - filename = os.path.join(os.getcwd(), self.file_id) - - buf = self.bot.request.retrieve(url, timeout=timeout) - if self._credentials: - buf = decrypt( - b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf - ) - with open(filename, 'wb') as fobj: - fobj.write(buf) - return filename - - def _get_encoded_url(self) -> str: - """Convert any UTF-8 char in :obj:`File.file_path` into a url encoded ASCII string.""" - sres = urllib_parse.urlsplit(self.file_path) - return urllib_parse.urlunsplit( - urllib_parse.SplitResult( - sres.scheme, sres.netloc, urllib_parse.quote(sres.path), sres.query, sres.fragment - ) - ) - - def download_as_bytearray(self, buf: bytearray = None) -> bytes: - """Download this file and return it as a bytearray. - - Args: - buf (:obj:`bytearray`, optional): Extend the given bytearray with the downloaded data. - - Returns: - :obj:`bytearray`: The same object as :attr:`buf` if it was specified. Otherwise a newly - allocated :obj:`bytearray`. - - """ - if buf is None: - buf = bytearray() - if is_local_file(self.file_path): - with open(self.file_path, "rb") as file: - buf.extend(file.read()) - else: - buf.extend(self.bot.request.retrieve(self._get_encoded_url())) - return buf - - def set_credentials(self, credentials: 'FileCredentials') -> None: - """Sets the passport credentials for the file. - - Args: - credentials (:class:`telegram.FileCredentials`): The credentials. - """ - self._credentials = credentials diff --git a/telegramer/include/telegram/files/inputfile.py b/telegramer/include/telegram/files/inputfile.py deleted file mode 100644 index 2c3196f..0000000 --- a/telegramer/include/telegram/files/inputfile.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=W0622,E0611 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram InputFile.""" - -import imghdr -import logging -import mimetypes -import os -from typing import IO, Optional, Tuple, Union -from uuid import uuid4 - -from telegram.utils.deprecate import set_new_attribute_deprecated - -DEFAULT_MIME_TYPE = 'application/octet-stream' -logger = logging.getLogger(__name__) - - -class InputFile: - """This object represents a Telegram InputFile. - - Args: - obj (:obj:`File handler` | :obj:`bytes`): An open file descriptor or the files content as - bytes. - filename (:obj:`str`, optional): Filename for this InputFile. - attach (:obj:`bool`, optional): Whether this should be send as one file or is part of a - collection of files. - - Raises: - TelegramError - - Attributes: - input_file_content (:obj:`bytes`): The binary content of the file to send. - filename (:obj:`str`): Optional. Filename for the file to be sent. - attach (:obj:`str`): Optional. Attach id for sending multiple files. - - """ - - __slots__ = ('filename', 'attach', 'input_file_content', 'mimetype', '__dict__') - - def __init__(self, obj: Union[IO, bytes], filename: str = None, attach: bool = None): - self.filename = None - if isinstance(obj, bytes): - self.input_file_content = obj - else: - self.input_file_content = obj.read() - self.attach = 'attached' + uuid4().hex if attach else None - - if filename: - self.filename = filename - elif hasattr(obj, 'name') and not isinstance(obj.name, int): # type: ignore[union-attr] - self.filename = os.path.basename(obj.name) # type: ignore[union-attr] - - image_mime_type = self.is_image(self.input_file_content) - if image_mime_type: - self.mimetype = image_mime_type - elif self.filename: - self.mimetype = mimetypes.guess_type(self.filename)[0] or DEFAULT_MIME_TYPE - else: - self.mimetype = DEFAULT_MIME_TYPE - - if not self.filename: - self.filename = self.mimetype.replace('/', '.') - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - @property - def field_tuple(self) -> Tuple[str, bytes, str]: # skipcq: PY-D0003 - return self.filename, self.input_file_content, self.mimetype - - @staticmethod - def is_image(stream: bytes) -> Optional[str]: - """Check if the content file is an image by analyzing its headers. - - Args: - stream (:obj:`bytes`): A byte stream representing the content of a file. - - Returns: - :obj:`str` | :obj:`None`: The mime-type of an image, if the input is an image, or - :obj:`None` else. - - """ - try: - image = imghdr.what(None, stream) - if image: - return f'image/{image}' - return None - except Exception: - logger.debug( - "Could not parse file content. Assuming that file is not an image.", exc_info=True - ) - return None - - @staticmethod - def is_file(obj: object) -> bool: # skipcq: PY-D0003 - return hasattr(obj, 'read') - - def to_dict(self) -> Optional[str]: - """See :meth:`telegram.TelegramObject.to_dict`.""" - if self.attach: - return 'attach://' + self.attach - return None diff --git a/telegramer/include/telegram/files/inputmedia.py b/telegramer/include/telegram/files/inputmedia.py deleted file mode 100644 index c4899fb..0000000 --- a/telegramer/include/telegram/files/inputmedia.py +++ /dev/null @@ -1,525 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""Base class for Telegram InputMedia Objects.""" - -from typing import Union, List, Tuple - -from telegram import ( - Animation, - Audio, - Document, - InputFile, - PhotoSize, - TelegramObject, - Video, - MessageEntity, -) -from telegram.utils.helpers import DEFAULT_NONE, parse_file_input -from telegram.utils.types import FileInput, JSONDict, ODVInput - - -class InputMedia(TelegramObject): - """Base class for Telegram InputMedia Objects. - - See :class:`telegram.InputMediaAnimation`, :class:`telegram.InputMediaAudio`, - :class:`telegram.InputMediaDocument`, :class:`telegram.InputMediaPhoto` and - :class:`telegram.InputMediaVideo` for detailed use. - - """ - - __slots__ = () - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...], None] = None - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - if self.caption_entities: - data['caption_entities'] = [ - ce.to_dict() for ce in self.caption_entities # pylint: disable=E1133 - ] - - return data - - -class InputMediaAnimation(InputMedia): - """Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. - - Note: - When using a :class:`telegram.Animation` for the :attr:`media` attribute. It will take the - width, height and duration from that video, unless otherwise specified with the optional - arguments. - - Args: - media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Animation`): File to send. Pass a - file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP - URL for Telegram to get a file from the Internet. Lastly you can pass an existing - :class:`telegram.Animation` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the animation, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of - the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - caption (:obj:`str`, optional): Caption of the animation to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of parse_mode. - width (:obj:`int`, optional): Animation width. - height (:obj:`int`, optional): Animation height. - duration (:obj:`int`, optional): Animation duration. - - Attributes: - type (:obj:`str`): ``animation``. - media (:obj:`str` | :class:`telegram.InputFile`): Animation to send. - caption (:obj:`str`): Optional. Caption of the document to be sent. - parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption. - thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. - width (:obj:`int`): Optional. Animation width. - height (:obj:`int`): Optional. Animation height. - duration (:obj:`int`): Optional. Animation duration. - - """ - - __slots__ = ( - 'caption_entities', - 'width', - 'media', - 'thumb', - 'caption', - 'duration', - 'parse_mode', - 'height', - 'type', - ) - - def __init__( - self, - media: Union[FileInput, Animation], - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - width: int = None, - height: int = None, - duration: int = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, - filename: str = None, - ): - self.type = 'animation' - - if isinstance(media, Animation): - self.media: Union[str, InputFile] = media.file_id - self.width = media.width - self.height = media.height - self.duration = media.duration - else: - self.media = parse_file_input(media, attach=True, filename=filename) - - if thumb: - self.thumb = parse_file_input(thumb, attach=True) - - if caption: - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - if width: - self.width = width - if height: - self.height = height - if duration: - self.duration = duration - - -class InputMediaPhoto(InputMedia): - """Represents a photo to be sent. - - Args: - media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.PhotoSize`): File to send. Pass a - file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP - URL for Telegram to get a file from the Internet. Lastly you can pass an existing - :class:`telegram.PhotoSize` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the photo, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional ): Caption of the photo to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of parse_mode. - - Attributes: - type (:obj:`str`): ``photo``. - media (:obj:`str` | :class:`telegram.InputFile`): Photo to send. - caption (:obj:`str`): Optional. Caption of the document to be sent. - parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption. - - """ - - __slots__ = ('caption_entities', 'media', 'caption', 'parse_mode', 'type') - - def __init__( - self, - media: Union[FileInput, PhotoSize], - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, - filename: str = None, - ): - self.type = 'photo' - self.media = parse_file_input(media, PhotoSize, attach=True, filename=filename) - - if caption: - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - - -class InputMediaVideo(InputMedia): - """Represents a video to be sent. - - Note: - * When using a :class:`telegram.Video` for the :attr:`media` attribute. It will take the - width, height and duration from that video, unless otherwise specified with the optional - arguments. - * ``thumb`` will be ignored for small video files, for which Telegram can easily - generate thumb nails. However, this behaviour is undocumented and might be changed - by Telegram. - - Args: - media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Video`): File to send. Pass a - file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP - URL for Telegram to get a file from the Internet. Lastly you can pass an existing - :class:`telegram.Video` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the video, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional): Caption of the video to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of parse_mode. - width (:obj:`int`, optional): Video width. - height (:obj:`int`, optional): Video height. - duration (:obj:`int`, optional): Video duration. - supports_streaming (:obj:`bool`, optional): Pass :obj:`True`, if the uploaded video is - suitable for streaming. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of - the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - - Attributes: - type (:obj:`str`): ``video``. - media (:obj:`str` | :class:`telegram.InputFile`): Video file to send. - caption (:obj:`str`): Optional. Caption of the document to be sent. - parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption. - width (:obj:`int`): Optional. Video width. - height (:obj:`int`): Optional. Video height. - duration (:obj:`int`): Optional. Video duration. - supports_streaming (:obj:`bool`): Optional. Pass :obj:`True`, if the uploaded video is - suitable for streaming. - thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. - - """ - - __slots__ = ( - 'caption_entities', - 'width', - 'media', - 'thumb', - 'supports_streaming', - 'caption', - 'duration', - 'parse_mode', - 'height', - 'type', - ) - - def __init__( - self, - media: Union[FileInput, Video], - caption: str = None, - width: int = None, - height: int = None, - duration: int = None, - supports_streaming: bool = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, - filename: str = None, - ): - self.type = 'video' - - if isinstance(media, Video): - self.media: Union[str, InputFile] = media.file_id - self.width = media.width - self.height = media.height - self.duration = media.duration - else: - self.media = parse_file_input(media, attach=True, filename=filename) - - if thumb: - self.thumb = parse_file_input(thumb, attach=True) - - if caption: - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - if width: - self.width = width - if height: - self.height = height - if duration: - self.duration = duration - if supports_streaming: - self.supports_streaming = supports_streaming - - -class InputMediaAudio(InputMedia): - """Represents an audio file to be treated as music to be sent. - - Note: - When using a :class:`telegram.Audio` for the :attr:`media` attribute. It will take the - duration, performer and title from that video, unless otherwise specified with the - optional arguments. - - Args: - media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Audio`): - File to send. Pass a - file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP - URL for Telegram to get a file from the Internet. Lastly you can pass an existing - :class:`telegram.Audio` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the audio, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional): Caption of the audio to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of parse_mode. - duration (:obj:`int`): Duration of the audio in seconds as defined by sender. - performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio - tags. - title (:obj:`str`, optional): Title of the audio as defined by sender or by audio tags. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of - the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - - Attributes: - type (:obj:`str`): ``audio``. - media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send. - caption (:obj:`str`): Optional. Caption of the document to be sent. - parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption. - duration (:obj:`int`): Duration of the audio in seconds. - performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio - tags. - title (:obj:`str`): Optional. Title of the audio as defined by sender or by audio tags. - thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. - - """ - - __slots__ = ( - 'caption_entities', - 'media', - 'thumb', - 'caption', - 'title', - 'duration', - 'type', - 'parse_mode', - 'performer', - ) - - def __init__( - self, - media: Union[FileInput, Audio], - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - duration: int = None, - performer: str = None, - title: str = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, - filename: str = None, - ): - self.type = 'audio' - - if isinstance(media, Audio): - self.media: Union[str, InputFile] = media.file_id - self.duration = media.duration - self.performer = media.performer - self.title = media.title - else: - self.media = parse_file_input(media, attach=True, filename=filename) - - if thumb: - self.thumb = parse_file_input(thumb, attach=True) - - if caption: - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - if duration: - self.duration = duration - if performer: - self.performer = performer - if title: - self.title = title - - -class InputMediaDocument(InputMedia): - """Represents a general file to be sent. - - Args: - media (:obj:`str` | `filelike object` | :obj:`bytes` | :class:`pathlib.Path` | \ - :class:`telegram.Document`): File to send. Pass a - file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP - URL for Telegram to get a file from the Internet. Lastly you can pass an existing - :class:`telegram.Document` object to send. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - filename (:obj:`str`, optional): Custom file name for the document, when uploading a - new file. Convenience parameter, useful e.g. when sending files generated by the - :obj:`tempfile` module. - - .. versionadded:: 13.1 - caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of parse_mode. - thumb (`filelike object` | :obj:`bytes` | :class:`pathlib.Path`, optional): Thumbnail of - the file sent; can be ignored if - thumbnail generation for the file is supported server-side. The thumbnail should be - in JPEG format and less than 200 kB in size. A thumbnail's width and height should - not exceed 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file. - - .. versionchanged:: 13.2 - Accept :obj:`bytes` as input. - disable_content_type_detection (:obj:`bool`, optional): Disables automatic server-side - content type detection for files uploaded using multipart/form-data. Always true, if - the document is sent as part of an album. - - Attributes: - type (:obj:`str`): ``document``. - media (:obj:`str` | :class:`telegram.InputFile`): File to send. - caption (:obj:`str`): Optional. Caption of the document to be sent. - parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption. - thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. - disable_content_type_detection (:obj:`bool`): Optional. Disables automatic server-side - content type detection for files uploaded using multipart/form-data. Always true, if - the document is sent as part of an album. - - """ - - __slots__ = ( - 'caption_entities', - 'media', - 'thumb', - 'caption', - 'parse_mode', - 'type', - 'disable_content_type_detection', - ) - - def __init__( - self, - media: Union[FileInput, Document], - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_content_type_detection: bool = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, - filename: str = None, - ): - self.type = 'document' - self.media = parse_file_input(media, Document, attach=True, filename=filename) - - if thumb: - self.thumb = parse_file_input(thumb, attach=True) - - if caption: - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.disable_content_type_detection = disable_content_type_detection diff --git a/telegramer/include/telegram/files/location.py b/telegramer/include/telegram/files/location.py deleted file mode 100644 index a5f5065..0000000 --- a/telegramer/include/telegram/files/location.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Location.""" - -from typing import Any - -from telegram import TelegramObject - - -class Location(TelegramObject): - """This object represents a point on the map. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`longitute` and :attr:`latitude` are equal. - - Args: - longitude (:obj:`float`): Longitude as defined by sender. - latitude (:obj:`float`): Latitude as defined by sender. - horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, - measured in meters; 0-1500. - live_period (:obj:`int`, optional): Time relative to the message sending date, during which - the location can be updated, in seconds. For active live locations only. - heading (:obj:`int`, optional): The direction in which user is moving, in degrees; 1-360. - For active live locations only. - proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts about - approaching another chat member, in meters. For sent live locations only. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - longitude (:obj:`float`): Longitude as defined by sender. - latitude (:obj:`float`): Latitude as defined by sender. - horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, - measured in meters. - live_period (:obj:`int`): Optional. Time relative to the message sending date, during which - the location can be updated, in seconds. For active live locations only. - heading (:obj:`int`): Optional. The direction in which user is moving, in degrees. - For active live locations only. - proximity_alert_radius (:obj:`int`): Optional. Maximum distance for proximity alerts about - approaching another chat member, in meters. For sent live locations only. - - """ - - __slots__ = ( - 'longitude', - 'horizontal_accuracy', - 'proximity_alert_radius', - 'live_period', - 'latitude', - 'heading', - '_id_attrs', - ) - - def __init__( - self, - longitude: float, - latitude: float, - horizontal_accuracy: float = None, - live_period: int = None, - heading: int = None, - proximity_alert_radius: int = None, - **_kwargs: Any, - ): - # Required - self.longitude = float(longitude) - self.latitude = float(latitude) - - # Optionals - self.horizontal_accuracy = float(horizontal_accuracy) if horizontal_accuracy else None - self.live_period = int(live_period) if live_period else None - self.heading = int(heading) if heading else None - self.proximity_alert_radius = ( - int(proximity_alert_radius) if proximity_alert_radius else None - ) - - self._id_attrs = (self.longitude, self.latitude) diff --git a/telegramer/include/telegram/files/photosize.py b/telegramer/include/telegram/files/photosize.py deleted file mode 100644 index 9a7988b..0000000 --- a/telegramer/include/telegram/files/photosize.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram PhotoSize.""" - -from typing import TYPE_CHECKING, Any - -from telegram import TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class PhotoSize(TelegramObject): - """This object represents one size of a photo or a file/sticker thumbnail. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Photo width. - height (:obj:`int`): Photo height. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Photo width. - height (:obj:`int`): Photo height. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ('bot', 'width', 'file_id', 'file_size', 'height', 'file_unique_id', '_id_attrs') - - def __init__( - self, - file_id: str, - file_unique_id: str, - width: int, - height: int, - file_size: int = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.width = int(width) - self.height = int(height) - # Optionals - self.file_size = file_size - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/sticker.py b/telegramer/include/telegram/files/sticker.py deleted file mode 100644 index e3f22a9..0000000 --- a/telegramer/include/telegram/files/sticker.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains objects that represents stickers.""" - -from typing import TYPE_CHECKING, Any, List, Optional, ClassVar - -from telegram import PhotoSize, TelegramObject, constants -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class Sticker(TelegramObject): - """This object represents a sticker. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Note: - As of v13.11 ``is_video`` is a required argument and therefore the order of the - arguments had to be changed. Use keyword arguments to make sure that the arguments are - passed correctly. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Sticker width. - height (:obj:`int`): Sticker height. - is_animated (:obj:`bool`): :obj:`True`, if the sticker is animated. - is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker. - - .. versionadded:: 13.11 - thumb (:class:`telegram.PhotoSize`, optional): Sticker thumbnail in the .WEBP or .JPG - format. - emoji (:obj:`str`, optional): Emoji associated with the sticker - set_name (:obj:`str`, optional): Name of the sticker set to which the sticker - belongs. - mask_position (:class:`telegram.MaskPosition`, optional): For mask stickers, the - position where the mask should be placed. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Sticker width. - height (:obj:`int`): Sticker height. - is_animated (:obj:`bool`): :obj:`True`, if the sticker is animated. - is_video (:obj:`bool`): :obj:`True`, if the sticker is a video sticker. - - .. versionadded:: 13.11 - thumb (:class:`telegram.PhotoSize`): Optional. Sticker thumbnail in the .webp or .jpg - format. - emoji (:obj:`str`): Optional. Emoji associated with the sticker. - set_name (:obj:`str`): Optional. Name of the sticker set to which the sticker belongs. - mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position - where the mask should be placed. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'width', - 'file_id', - 'is_animated', - 'is_video', - 'file_size', - 'thumb', - 'set_name', - 'mask_position', - 'height', - 'file_unique_id', - 'emoji', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - width: int, - height: int, - is_animated: bool, - is_video: bool, - thumb: PhotoSize = None, - emoji: str = None, - file_size: int = None, - set_name: str = None, - mask_position: 'MaskPosition' = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.width = int(width) - self.height = int(height) - self.is_animated = is_animated - self.is_video = is_video - # Optionals - self.thumb = thumb - self.emoji = emoji - self.file_size = file_size - self.set_name = set_name - self.mask_position = mask_position - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Sticker']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - data['mask_position'] = MaskPosition.de_json(data.get('mask_position'), bot) - - return cls(bot=bot, **data) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) - - -class StickerSet(TelegramObject): - """This object represents a sticker set. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`name` is equal. - - Note: - As of v13.11 ``is_video`` is a required argument and therefore the order of the - arguments had to be changed. Use keyword arguments to make sure that the arguments are - passed correctly. - - Args: - name (:obj:`str`): Sticker set name. - title (:obj:`str`): Sticker set title. - is_animated (:obj:`bool`): :obj:`True`, if the sticker set contains animated stickers. - is_video (:obj:`bool`): :obj:`True`, if the sticker set contains video stickers. - - .. versionadded:: 13.11 - contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks. - stickers (List[:class:`telegram.Sticker`]): List of all set stickers. - thumb (:class:`telegram.PhotoSize`, optional): Sticker set thumbnail in the ``.WEBP``, - ``.TGS``, or ``.WEBM`` format. - - Attributes: - name (:obj:`str`): Sticker set name. - title (:obj:`str`): Sticker set title. - is_animated (:obj:`bool`): :obj:`True`, if the sticker set contains animated stickers. - is_video (:obj:`bool`): :obj:`True`, if the sticker set contains video stickers. - - .. versionadded:: 13.11 - contains_masks (:obj:`bool`): :obj:`True`, if the sticker set contains masks. - stickers (List[:class:`telegram.Sticker`]): List of all set stickers. - thumb (:class:`telegram.PhotoSize`): Optional. Sticker set thumbnail in the ``.WEBP``, - ``.TGS`` or ``.WEBM`` format. - - """ - - __slots__ = ( - 'is_animated', - 'is_video', - 'contains_masks', - 'thumb', - 'title', - 'stickers', - 'name', - '_id_attrs', - ) - - def __init__( - self, - name: str, - title: str, - is_animated: bool, - contains_masks: bool, - stickers: List[Sticker], - is_video: bool, - thumb: PhotoSize = None, - **_kwargs: Any, - ): - self.name = name - self.title = title - self.is_animated = is_animated - self.is_video = is_video - self.contains_masks = contains_masks - self.stickers = stickers - # Optionals - self.thumb = thumb - - self._id_attrs = (self.name,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['StickerSet']: - """See :meth:`telegram.TelegramObject.de_json`.""" - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - data['stickers'] = Sticker.de_list(data.get('stickers'), bot) - - return cls(bot=bot, **data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['stickers'] = [s.to_dict() for s in data.get('stickers')] - - return data - - -class MaskPosition(TelegramObject): - """This object describes the position on faces where a mask should be placed by default. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`point`, :attr:`x_shift`, :attr:`y_shift` and, :attr:`scale` - are equal. - - Attributes: - point (:obj:`str`): The part of the face relative to which the mask should be placed. - One of ``'forehead'``, ``'eyes'``, ``'mouth'``, or ``'chin'``. - x_shift (:obj:`float`): Shift by X-axis measured in widths of the mask scaled to the face - size, from left to right. - y_shift (:obj:`float`): Shift by Y-axis measured in heights of the mask scaled to the face - size, from top to bottom. - scale (:obj:`float`): Mask scaling coefficient. For example, 2.0 means double size. - - Note: - :attr:`type` should be one of the following: `forehead`, `eyes`, `mouth` or `chin`. You can - use the class constants for those. - - Args: - point (:obj:`str`): The part of the face relative to which the mask should be placed. - One of ``'forehead'``, ``'eyes'``, ``'mouth'``, or ``'chin'``. - x_shift (:obj:`float`): Shift by X-axis measured in widths of the mask scaled to the face - size, from left to right. For example, choosing -1.0 will place mask just to the left - of the default mask position. - y_shift (:obj:`float`): Shift by Y-axis measured in heights of the mask scaled to the face - size, from top to bottom. For example, 1.0 will place the mask just below the default - mask position. - scale (:obj:`float`): Mask scaling coefficient. For example, 2.0 means double size. - - """ - - __slots__ = ('point', 'scale', 'x_shift', 'y_shift', '_id_attrs') - - FOREHEAD: ClassVar[str] = constants.STICKER_FOREHEAD - """:const:`telegram.constants.STICKER_FOREHEAD`""" - EYES: ClassVar[str] = constants.STICKER_EYES - """:const:`telegram.constants.STICKER_EYES`""" - MOUTH: ClassVar[str] = constants.STICKER_MOUTH - """:const:`telegram.constants.STICKER_MOUTH`""" - CHIN: ClassVar[str] = constants.STICKER_CHIN - """:const:`telegram.constants.STICKER_CHIN`""" - - def __init__(self, point: str, x_shift: float, y_shift: float, scale: float, **_kwargs: Any): - self.point = point - self.x_shift = x_shift - self.y_shift = y_shift - self.scale = scale - - self._id_attrs = (self.point, self.x_shift, self.y_shift, self.scale) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MaskPosition']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if data is None: - return None - - return cls(**data) diff --git a/telegramer/include/telegram/files/venue.py b/telegramer/include/telegram/files/venue.py deleted file mode 100644 index aad46db..0000000 --- a/telegramer/include/telegram/files/venue.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Venue.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import Location, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class Venue(TelegramObject): - """This object represents a venue. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`location` and :attr:`title` are equal. - - Note: - Foursquare details and Google Pace details are mutually exclusive. However, this - behaviour is undocumented and might be changed by Telegram. - - Args: - location (:class:`telegram.Location`): Venue location. - title (:obj:`str`): Name of the venue. - address (:obj:`str`): Address of the venue. - foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue. - foursquare_type (:obj:`str`, optional): Foursquare type of the venue. (For example, - "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".) - google_place_id (:obj:`str`, optional): Google Places identifier of the venue. - google_place_type (:obj:`str`, optional): Google Places type of the venue. (See - `supported types <https://developers.google.com/places/web-service/supported_types>`_.) - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - location (:class:`telegram.Location`): Venue location. - title (:obj:`str`): Name of the venue. - address (:obj:`str`): Address of the venue. - foursquare_id (:obj:`str`): Optional. Foursquare identifier of the venue. - foursquare_type (:obj:`str`): Optional. Foursquare type of the venue. - google_place_id (:obj:`str`): Optional. Google Places identifier of the venue. - google_place_type (:obj:`str`): Optional. Google Places type of the venue. - - """ - - __slots__ = ( - 'google_place_type', - 'location', - 'title', - 'address', - 'foursquare_type', - 'foursquare_id', - 'google_place_id', - '_id_attrs', - ) - - def __init__( - self, - location: Location, - title: str, - address: str, - foursquare_id: str = None, - foursquare_type: str = None, - google_place_id: str = None, - google_place_type: str = None, - **_kwargs: Any, - ): - # Required - self.location = location - self.title = title - self.address = address - # Optionals - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type - self.google_place_id = google_place_id - self.google_place_type = google_place_type - - self._id_attrs = (self.location, self.title) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Venue']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['location'] = Location.de_json(data.get('location'), bot) - - return cls(**data) diff --git a/telegramer/include/telegram/files/video.py b/telegramer/include/telegram/files/video.py deleted file mode 100644 index 92e7c5e..0000000 --- a/telegramer/include/telegram/files/video.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Video.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import PhotoSize, TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class Video(TelegramObject): - """This object represents a video file. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Video width as defined by sender. - height (:obj:`int`): Video height as defined by sender. - duration (:obj:`int`): Duration of the video in seconds as defined by sender. - thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail. - file_name (:obj:`str`, optional): Original filename as defined by sender. - mime_type (:obj:`str`, optional): Mime type of a file as defined by sender. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - width (:obj:`int`): Video width as defined by sender. - height (:obj:`int`): Video height as defined by sender. - duration (:obj:`int`): Duration of the video in seconds as defined by sender. - thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail. - file_name (:obj:`str`): Optional. Original filename as defined by sender. - mime_type (:obj:`str`): Optional. Mime type of a file as defined by sender. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'width', - 'file_id', - 'file_size', - 'file_name', - 'thumb', - 'duration', - 'mime_type', - 'height', - 'file_unique_id', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - width: int, - height: int, - duration: int, - thumb: PhotoSize = None, - mime_type: str = None, - file_size: int = None, - bot: 'Bot' = None, - file_name: str = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.width = int(width) - self.height = int(height) - self.duration = int(duration) - # Optionals - self.thumb = thumb - self.file_name = file_name - self.mime_type = mime_type - self.file_size = file_size - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Video']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - - return cls(bot=bot, **data) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/videonote.py b/telegramer/include/telegram/files/videonote.py deleted file mode 100644 index 17d6207..0000000 --- a/telegramer/include/telegram/files/videonote.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram VideoNote.""" - -from typing import TYPE_CHECKING, Optional, Any - -from telegram import PhotoSize, TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class VideoNote(TelegramObject): - """This object represents a video message (available in Telegram apps as of v.4.0). - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - length (:obj:`int`): Video width and height (diameter of the video message) as defined - by sender. - duration (:obj:`int`): Duration of the video in seconds as defined by sender. - thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - length (:obj:`int`): Video width and height as defined by sender. - duration (:obj:`int`): Duration of the video in seconds as defined by sender. - thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'length', - 'file_id', - 'file_size', - 'thumb', - 'duration', - 'file_unique_id', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - length: int, - duration: int, - thumb: PhotoSize = None, - file_size: int = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.length = int(length) - self.duration = int(duration) - # Optionals - self.thumb = thumb - self.file_size = file_size - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VideoNote']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - - return cls(bot=bot, **data) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/files/voice.py b/telegramer/include/telegram/files/voice.py deleted file mode 100644 index c878282..0000000 --- a/telegramer/include/telegram/files/voice.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Voice.""" - -from typing import TYPE_CHECKING, Any - -from telegram import TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File - - -class Voice(TelegramObject): - """This object represents a voice note. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - duration (:obj:`int`, optional): Duration of the audio in seconds as defined by sender. - mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. - file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - duration (:obj:`int`): Duration of the audio in seconds as defined by sender. - mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender. - file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'file_id', - 'file_size', - 'duration', - 'mime_type', - 'file_unique_id', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - duration: int, - mime_type: str = None, - file_size: int = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.file_id = str(file_id) - self.file_unique_id = str(file_unique_id) - self.duration = int(duration) - # Optionals - self.mime_type = mime_type - self.file_size = file_size - self.bot = bot - - self._id_attrs = (self.file_unique_id,) - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """Convenience wrapper over :attr:`telegram.Bot.get_file` - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - return self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) diff --git a/telegramer/include/telegram/forcereply.py b/telegramer/include/telegram/forcereply.py deleted file mode 100644 index 792b211..0000000 --- a/telegramer/include/telegram/forcereply.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ForceReply.""" - -from typing import Any - -from telegram import ReplyMarkup - - -class ForceReply(ReplyMarkup): - """ - Upon receiving a message with this object, Telegram clients will display a reply interface to - the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be - extremely useful if you want to create user-friendly step-by-step interfaces without having - to sacrifice privacy mode. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`selective` is equal. - - Args: - selective (:obj:`bool`, optional): Use this parameter if you want to force reply from - specific users only. Targets: - - 1) Users that are @mentioned in the :attr:`~telegram.Message.text` of the - :class:`telegram.Message` object. - 2) If the bot's message is a reply (has ``reply_to_message_id``), sender of the - original message. - - input_field_placeholder (:obj:`str`, optional): The placeholder to be shown in the input - field when the reply is active; 1-64 characters. - - .. versionadded:: 13.7 - - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - force_reply (:obj:`True`): Shows reply interface to the user, as if they manually selected - the bots message and tapped 'Reply'. - selective (:obj:`bool`): Optional. Force reply from specific users only. - input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input - field when the reply is active. - - .. versionadded:: 13.7 - - """ - - __slots__ = ('selective', 'force_reply', 'input_field_placeholder', '_id_attrs') - - def __init__( - self, - force_reply: bool = True, - selective: bool = False, - input_field_placeholder: str = None, - **_kwargs: Any, - ): - # Required - self.force_reply = bool(force_reply) - # Optionals - self.selective = bool(selective) - self.input_field_placeholder = input_field_placeholder - - self._id_attrs = (self.selective,) diff --git a/telegramer/include/telegram/games/__init__.py b/telegramer/include/telegram/games/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/games/callbackgame.py b/telegramer/include/telegram/games/callbackgame.py deleted file mode 100644 index 6803a44..0000000 --- a/telegramer/include/telegram/games/callbackgame.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram CallbackGame.""" - -from telegram import TelegramObject - - -class CallbackGame(TelegramObject): - """A placeholder, currently holds no information. Use BotFather to set up your game.""" - - __slots__ = () diff --git a/telegramer/include/telegram/games/game.py b/telegramer/include/telegram/games/game.py deleted file mode 100644 index 86eb2ab..0000000 --- a/telegramer/include/telegram/games/game.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Game.""" - -import sys -from typing import TYPE_CHECKING, Any, Dict, List, Optional - -from telegram import Animation, MessageEntity, PhotoSize, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class Game(TelegramObject): - """ - This object represents a game. Use `BotFather <https://t.me/BotFather>`_ to create and edit - games, their short names will act as unique identifiers. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`title`, :attr:`description` and :attr:`photo` are equal. - - Args: - title (:obj:`str`): Title of the game. - description (:obj:`str`): Description of the game. - photo (List[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message - in chats. - text (:obj:`str`, optional): Brief description of the game or high scores included in the - game message. Can be automatically edited to include current high scores for the game - when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited - using :meth:`telegram.Bot.edit_message_text`. - 0-4096 characters. Also found as ``telegram.constants.MAX_MESSAGE_LENGTH``. - text_entities (List[:class:`telegram.MessageEntity`], optional): Special entities that - appear in text, such as usernames, URLs, bot commands, etc. - animation (:class:`telegram.Animation`, optional): Animation that will be displayed in the - game message in chats. Upload via `BotFather <https://t.me/BotFather>`_. - - Attributes: - title (:obj:`str`): Title of the game. - description (:obj:`str`): Description of the game. - photo (List[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message - in chats. - text (:obj:`str`): Optional. Brief description of the game or high scores included in the - game message. Can be automatically edited to include current high scores for the game - when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited - using :meth:`telegram.Bot.edit_message_text`. - text_entities (List[:class:`telegram.MessageEntity`]): Optional. Special entities that - appear in text, such as usernames, URLs, bot commands, etc. - animation (:class:`telegram.Animation`): Optional. Animation that will be displayed in the - game message in chats. Upload via `BotFather <https://t.me/BotFather>`_. - - """ - - __slots__ = ( - 'title', - 'photo', - 'description', - 'text_entities', - 'text', - 'animation', - '_id_attrs', - ) - - def __init__( - self, - title: str, - description: str, - photo: List[PhotoSize], - text: str = None, - text_entities: List[MessageEntity] = None, - animation: Animation = None, - **_kwargs: Any, - ): - # Required - self.title = title - self.description = description - self.photo = photo - # Optionals - self.text = text - self.text_entities = text_entities or [] - self.animation = animation - - self._id_attrs = (self.title, self.description, self.photo) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Game']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['photo'] = PhotoSize.de_list(data.get('photo'), bot) - data['text_entities'] = MessageEntity.de_list(data.get('text_entities'), bot) - data['animation'] = Animation.de_json(data.get('animation'), bot) - - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['photo'] = [p.to_dict() for p in self.photo] - if self.text_entities: - data['text_entities'] = [x.to_dict() for x in self.text_entities] - - return data - - def parse_text_entity(self, entity: MessageEntity) -> str: - """Returns the text from a given :class:`telegram.MessageEntity`. - - Note: - This method is present because Telegram calculates the offset and length in - UTF-16 codepoint pairs, which some versions of Python don't handle automatically. - (That is, you can't just slice ``Message.text`` with the offset and length.) - - Args: - entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must - be an entity that belongs to this message. - - Returns: - :obj:`str`: The text of the given entity. - - Raises: - RuntimeError: If this game has no text. - - """ - if not self.text: - raise RuntimeError("This Game has no 'text'.") - - # Is it a narrow build, if so we don't need to convert - if sys.maxunicode == 0xFFFF: - return self.text[entity.offset : entity.offset + entity.length] - entity_text = self.text.encode('utf-16-le') - entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - - return entity_text.decode('utf-16-le') - - def parse_text_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: - """ - Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. - It contains entities from this message filtered by their ``type`` attribute as the key, and - the text that each entity belongs to as the value of the :obj:`dict`. - - Note: - This method should always be used instead of the :attr:`text_entities` attribute, since - it calculates the correct substring from the message text based on UTF-16 codepoints. - See :attr:`parse_text_entity` for more info. - - Args: - types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the - ``type`` attribute of an entity is contained in this list, it will be returned. - Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. - - Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to - the text that belongs to them, calculated based on UTF-16 codepoints. - - """ - if types is None: - types = MessageEntity.ALL_TYPES - - return { - entity: self.parse_text_entity(entity) - for entity in (self.text_entities or []) - if entity.type in types - } - - def __hash__(self) -> int: - return hash((self.title, self.description, tuple(p for p in self.photo))) diff --git a/telegramer/include/telegram/games/gamehighscore.py b/telegramer/include/telegram/games/gamehighscore.py deleted file mode 100644 index 5967dda..0000000 --- a/telegramer/include/telegram/games/gamehighscore.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram GameHighScore.""" - -from typing import TYPE_CHECKING, Optional - -from telegram import TelegramObject, User -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class GameHighScore(TelegramObject): - """This object represents one row of the high scores table for a game. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`position`, :attr:`user` and :attr:`score` are equal. - - Args: - position (:obj:`int`): Position in high score table for the game. - user (:class:`telegram.User`): User. - score (:obj:`int`): Score. - - Attributes: - position (:obj:`int`): Position in high score table for the game. - user (:class:`telegram.User`): User. - score (:obj:`int`): Score. - - """ - - __slots__ = ('position', 'user', 'score', '_id_attrs') - - def __init__(self, position: int, user: User, score: int): - self.position = position - self.user = user - self.score = score - - self._id_attrs = (self.position, self.user, self.score) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['GameHighScore']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['user'] = User.de_json(data.get('user'), bot) - - return cls(**data) diff --git a/telegramer/include/telegram/inline/__init__.py b/telegramer/include/telegram/inline/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/inline/inlinekeyboardbutton.py b/telegramer/include/telegram/inline/inlinekeyboardbutton.py deleted file mode 100644 index 49b6e07..0000000 --- a/telegramer/include/telegram/inline/inlinekeyboardbutton.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram InlineKeyboardButton.""" - -from typing import TYPE_CHECKING, Any - -from telegram import TelegramObject - -if TYPE_CHECKING: - from telegram import CallbackGame, LoginUrl - - -class InlineKeyboardButton(TelegramObject): - """This object represents one button of an inline keyboard. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`text`, :attr:`url`, :attr:`login_url`, :attr:`callback_data`, - :attr:`switch_inline_query`, :attr:`switch_inline_query_current_chat`, :attr:`callback_game` - and :attr:`pay` are equal. - - Note: - * You must use exactly one of the optional fields. Mind that :attr:`callback_game` is not - working as expected. Putting a game short name in it might, but is not guaranteed to - work. - * If your bot allows for arbitrary callback data, in keyboards returned in a response - from telegram, :attr:`callback_data` maybe be an instance of - :class:`telegram.ext.InvalidCallbackData`. This will be the case, if the data - associated with the button was already deleted. - - .. versionadded:: 13.6 - - * Since Bot API 5.5, it's now allowed to mention users by their ID in inline keyboards. - This will only work in Telegram versions released after December 7, 2021. - Older clients will display *unsupported message*. - - Warning: - If your bot allows your arbitrary callback data, buttons whose callback data is a - non-hashable object will become unhashable. Trying to evaluate ``hash(button)`` will - result in a :class:`TypeError`. - - .. versionchanged:: 13.6 - - Args: - text (:obj:`str`): Label text on the button. - url (:obj:`str`, optional): HTTP or tg:// url to be opened when the button is pressed. - Links ``tg://user?id=<user_id>`` can be used to mention a user by - their ID without using a username, if this is allowed by their privacy settings. - - .. versionchanged:: 13.9 - You can now mention a user using ``tg://user?id=<user_id>``. - login_url (:class:`telegram.LoginUrl`, optional): An HTTP URL used to automatically - authorize the user. Can be used as a replacement for the Telegram Login Widget. - callback_data (:obj:`str` | :obj:`Any`, optional): Data to be sent in a callback query to - the bot when button is pressed, UTF-8 1-64 bytes. If the bot instance allows arbitrary - callback data, anything can be passed. - switch_inline_query (:obj:`str`, optional): If set, pressing the button will prompt the - user to select one of their chats, open that chat and insert the bot's username and the - specified inline query in the input field. Can be empty, in which case just the bot's - username will be inserted. This offers an easy way for users to start using your bot - in inline mode when they are currently in a private chat with it. Especially useful - when combined with switch_pm* actions - in this case the user will be automatically - returned to the chat they switched from, skipping the chat selection screen. - switch_inline_query_current_chat (:obj:`str`, optional): If set, pressing the button will - insert the bot's username and the specified inline query in the current chat's input - field. Can be empty, in which case only the bot's username will be inserted. This - offers a quick way for the user to open your bot in inline mode in the same chat - good - for selecting something from multiple options. - callback_game (:class:`telegram.CallbackGame`, optional): Description of the game that will - be launched when the user presses the button. This type of button must always be - the ``first`` button in the first row. - pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. This type of button - must always be the `first` button in the first row and can only be used in invoice - messages. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - text (:obj:`str`): Label text on the button. - url (:obj:`str`): Optional. HTTP or tg:// url to be opened when the button is pressed. - Links ``tg://user?id=<user_id>`` can be used to mention a user by - their ID without using a username, if this is allowed by their privacy settings. - - .. versionchanged:: 13.9 - You can now mention a user using ``tg://user?id=<user_id>``. - login_url (:class:`telegram.LoginUrl`): Optional. An HTTP URL used to automatically - authorize the user. Can be used as a replacement for the Telegram Login Widget. - callback_data (:obj:`str` | :obj:`object`): Optional. Data to be sent in a callback query - to the bot when button is pressed, UTF-8 1-64 bytes. - switch_inline_query (:obj:`str`): Optional. Will prompt the user to select one of their - chats, open that chat and insert the bot's username and the specified inline query in - the input field. Can be empty, in which case just the bot’s username will be inserted. - switch_inline_query_current_chat (:obj:`str`): Optional. Will insert the bot's username and - the specified inline query in the current chat's input field. Can be empty, in which - case just the bot’s username will be inserted. - callback_game (:class:`telegram.CallbackGame`): Optional. Description of the game that will - be launched when the user presses the button. - pay (:obj:`bool`): Optional. Specify :obj:`True`, to send a Pay button. - - """ - - __slots__ = ( - 'callback_game', - 'url', - 'switch_inline_query_current_chat', - 'callback_data', - 'pay', - 'switch_inline_query', - 'text', - '_id_attrs', - 'login_url', - ) - - def __init__( - self, - text: str, - url: str = None, - callback_data: object = None, - switch_inline_query: str = None, - switch_inline_query_current_chat: str = None, - callback_game: 'CallbackGame' = None, - pay: bool = None, - login_url: 'LoginUrl' = None, - **_kwargs: Any, - ): - # Required - self.text = text - - # Optionals - self.url = url - self.login_url = login_url - self.callback_data = callback_data - self.switch_inline_query = switch_inline_query - self.switch_inline_query_current_chat = switch_inline_query_current_chat - self.callback_game = callback_game - self.pay = pay - self._id_attrs = () - self._set_id_attrs() - - def _set_id_attrs(self) -> None: - self._id_attrs = ( - self.text, - self.url, - self.login_url, - self.callback_data, - self.switch_inline_query, - self.switch_inline_query_current_chat, - self.callback_game, - self.pay, - ) - - def update_callback_data(self, callback_data: object) -> None: - """ - Sets :attr:`callback_data` to the passed object. Intended to be used by - :class:`telegram.ext.CallbackDataCache`. - - .. versionadded:: 13.6 - - Args: - callback_data (:obj:`obj`): The new callback data. - """ - self.callback_data = callback_data - self._set_id_attrs() diff --git a/telegramer/include/telegram/inline/inlinekeyboardmarkup.py b/telegramer/include/telegram/inline/inlinekeyboardmarkup.py deleted file mode 100644 index 61724f0..0000000 --- a/telegramer/include/telegram/inline/inlinekeyboardmarkup.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram InlineKeyboardMarkup.""" - -from typing import TYPE_CHECKING, Any, List, Optional - -from telegram import InlineKeyboardButton, ReplyMarkup -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class InlineKeyboardMarkup(ReplyMarkup): - """ - This object represents an inline keyboard that appears right next to the message it belongs to. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their the size of :attr:`inline_keyboard` and all the buttons are equal. - - Args: - inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, - each represented by a list of InlineKeyboardButton objects. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, - each represented by a list of InlineKeyboardButton objects. - - """ - - __slots__ = ('inline_keyboard', '_id_attrs') - - def __init__(self, inline_keyboard: List[List[InlineKeyboardButton]], **_kwargs: Any): - # Required - self.inline_keyboard = inline_keyboard - - self._id_attrs = (self.inline_keyboard,) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['inline_keyboard'] = [] - for inline_keyboard in self.inline_keyboard: - data['inline_keyboard'].append([x.to_dict() for x in inline_keyboard]) - - return data - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboardMarkup']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - keyboard = [] - for row in data['inline_keyboard']: - tmp = [] - for col in row: - btn = InlineKeyboardButton.de_json(col, bot) - if btn: - tmp.append(btn) - keyboard.append(tmp) - - return cls(keyboard) - - @classmethod - def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> 'InlineKeyboardMarkup': - """Shortcut for:: - - InlineKeyboardMarkup([[button]], **kwargs) - - Return an InlineKeyboardMarkup from a single InlineKeyboardButton - - Args: - button (:class:`telegram.InlineKeyboardButton`): The button to use in the markup - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - """ - return cls([[button]], **kwargs) - - @classmethod - def from_row( - cls, button_row: List[InlineKeyboardButton], **kwargs: object - ) -> 'InlineKeyboardMarkup': - """Shortcut for:: - - InlineKeyboardMarkup([button_row], **kwargs) - - Return an InlineKeyboardMarkup from a single row of InlineKeyboardButtons - - Args: - button_row (List[:class:`telegram.InlineKeyboardButton`]): The button to use in the - markup - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - """ - return cls([button_row], **kwargs) - - @classmethod - def from_column( - cls, button_column: List[InlineKeyboardButton], **kwargs: object - ) -> 'InlineKeyboardMarkup': - """Shortcut for:: - - InlineKeyboardMarkup([[button] for button in button_column], **kwargs) - - Return an InlineKeyboardMarkup from a single column of InlineKeyboardButtons - - Args: - button_column (List[:class:`telegram.InlineKeyboardButton`]): The button to use in the - markup - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - """ - button_grid = [[button] for button in button_column] - return cls(button_grid, **kwargs) - - def __hash__(self) -> int: - return hash(tuple(tuple(button for button in row) for row in self.inline_keyboard)) diff --git a/telegramer/include/telegram/inline/inlinequery.py b/telegramer/include/telegram/inline/inlinequery.py deleted file mode 100644 index 1230151..0000000 --- a/telegramer/include/telegram/inline/inlinequery.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0902,R0913 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram InlineQuery.""" - -from typing import TYPE_CHECKING, Any, Optional, Union, Callable, ClassVar, Sequence - -from telegram import Location, TelegramObject, User, constants -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, InlineQueryResult - - -class InlineQuery(TelegramObject): - """ - This object represents an incoming inline query. When the user sends an empty query, your bot - could return some default or trending results. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Note: - In Python ``from`` is a reserved word, use ``from_user`` instead. - - Args: - id (:obj:`str`): Unique identifier for this query. - from_user (:class:`telegram.User`): Sender. - query (:obj:`str`): Text of the query (up to 256 characters). - offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot. - chat_type (:obj:`str`, optional): Type of the chat, from which the inline query was sent. - Can be either :attr:`telegram.Chat.SENDER` for a private chat with the inline query - sender, :attr:`telegram.Chat.PRIVATE`, :attr:`telegram.Chat.GROUP`, - :attr:`telegram.Chat.SUPERGROUP` or :attr:`telegram.Chat.CHANNEL`. The chat type should - be always known for requests sent from official clients and most third-party clients, - unless the request was sent from a secret chat. - - .. versionadded:: 13.5 - location (:class:`telegram.Location`, optional): Sender location, only for bots that - request user location. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - id (:obj:`str`): Unique identifier for this query. - from_user (:class:`telegram.User`): Sender. - query (:obj:`str`): Text of the query (up to 256 characters). - offset (:obj:`str`): Offset of the results to be returned, can be controlled by the bot. - location (:class:`telegram.Location`): Optional. Sender location, only for bots that - request user location. - chat_type (:obj:`str`, optional): Type of the chat, from which the inline query was sent. - - .. versionadded:: 13.5 - - """ - - __slots__ = ('bot', 'location', 'chat_type', 'id', 'offset', 'from_user', 'query', '_id_attrs') - - def __init__( - self, - id: str, # pylint: disable=W0622 - from_user: User, - query: str, - offset: str, - location: Location = None, - bot: 'Bot' = None, - chat_type: str = None, - **_kwargs: Any, - ): - # Required - self.id = id # pylint: disable=C0103 - self.from_user = from_user - self.query = query - self.offset = offset - - # Optional - self.location = location - self.chat_type = chat_type - - self.bot = bot - self._id_attrs = (self.id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineQuery']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['from_user'] = User.de_json(data.get('from'), bot) - data['location'] = Location.de_json(data.get('location'), bot) - - return cls(bot=bot, **data) - - def answer( - self, - results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] - ], - cache_time: int = 300, - is_personal: bool = None, - next_offset: str = None, - switch_pm_text: str = None, - switch_pm_parameter: str = None, - timeout: ODVInput[float] = DEFAULT_NONE, - current_offset: str = None, - api_kwargs: JSONDict = None, - auto_pagination: bool = False, - ) -> bool: - """Shortcut for:: - - bot.answer_inline_query(update.inline_query.id, - *args, - current_offset=self.offset if auto_pagination else None, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.answer_inline_query`. - - Args: - auto_pagination (:obj:`bool`, optional): If set to :obj:`True`, :attr:`offset` will be - passed as :attr:`current_offset` to :meth:`telegram.Bot.answer_inline_query`. - Defaults to :obj:`False`. - - Raises: - TypeError: If both :attr:`current_offset` and :attr:`auto_pagination` are supplied. - """ - if current_offset and auto_pagination: - # We raise TypeError instead of ValueError for backwards compatibility with versions - # which didn't check this here but let Python do the checking - raise TypeError('current_offset and auto_pagination are mutually exclusive!') - return self.bot.answer_inline_query( - inline_query_id=self.id, - current_offset=self.offset if auto_pagination else current_offset, - results=results, - cache_time=cache_time, - is_personal=is_personal, - next_offset=next_offset, - switch_pm_text=switch_pm_text, - switch_pm_parameter=switch_pm_parameter, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - MAX_RESULTS: ClassVar[int] = constants.MAX_INLINE_QUERY_RESULTS - """ - :const:`telegram.constants.MAX_INLINE_QUERY_RESULTS` - - .. versionadded:: 13.2 - """ diff --git a/telegramer/include/telegram/inline/inlinequeryresult.py b/telegramer/include/telegram/inline/inlinequeryresult.py deleted file mode 100644 index f3227d4..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresult.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains the classes that represent Telegram InlineQueryResult.""" - -from typing import Any - -from telegram import TelegramObject -from telegram.utils.types import JSONDict - - -class InlineQueryResult(TelegramObject): - """Baseclass for the InlineQueryResult* classes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Note: - All URLs passed in inline query results will be available to end users and therefore must - be assumed to be *public*. - - Args: - type (:obj:`str`): Type of the result. - id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): Type of the result. - id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - - """ - - __slots__ = ('type', 'id', '_id_attrs') - - def __init__(self, type: str, id: str, **_kwargs: Any): - # Required - self.type = str(type) - self.id = str(id) # pylint: disable=C0103 - - self._id_attrs = (self.id,) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - # pylint: disable=E1101 - if ( - hasattr(self, 'caption_entities') - and self.caption_entities # type: ignore[attr-defined] - ): - data['caption_entities'] = [ - ce.to_dict() for ce in self.caption_entities # type: ignore[attr-defined] - ] - - return data diff --git a/telegramer/include/telegram/inline/inlinequeryresultarticle.py b/telegramer/include/telegram/inline/inlinequeryresultarticle.py deleted file mode 100644 index ecc975c..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultarticle.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultArticle.""" - -from typing import TYPE_CHECKING, Any - -from telegram import InlineQueryResult - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultArticle(InlineQueryResult): - """This object represents a Telegram InlineQueryResultArticle. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - title (:obj:`str`): Title of the result. - input_message_content (:class:`telegram.InputMessageContent`): Content of the message to - be sent. - reply_markup (:class:`telegram.ReplyMarkup`, optional): Inline keyboard attached to - the message - url (:obj:`str`, optional): URL of the result. - hide_url (:obj:`bool`, optional): Pass :obj:`True`, if you don't want the URL to be shown - in the message. - description (:obj:`str`, optional): Short description of the result. - thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. - thumb_width (:obj:`int`, optional): Thumbnail width. - thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'article'. - id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - title (:obj:`str`): Title of the result. - input_message_content (:class:`telegram.InputMessageContent`): Content of the message to - be sent. - reply_markup (:class:`telegram.ReplyMarkup`): Optional. Inline keyboard attached to - the message. - url (:obj:`str`): Optional. URL of the result. - hide_url (:obj:`bool`): Optional. Pass :obj:`True`, if you don't want the URL to be shown - in the message. - description (:obj:`str`): Optional. Short description of the result. - thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result. - thumb_width (:obj:`int`): Optional. Thumbnail width. - thumb_height (:obj:`int`): Optional. Thumbnail height. - - """ - - __slots__ = ( - 'reply_markup', - 'thumb_width', - 'thumb_height', - 'hide_url', - 'url', - 'title', - 'description', - 'input_message_content', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - title: str, - input_message_content: 'InputMessageContent', - reply_markup: 'ReplyMarkup' = None, - url: str = None, - hide_url: bool = None, - description: str = None, - thumb_url: str = None, - thumb_width: int = None, - thumb_height: int = None, - **_kwargs: Any, - ): - - # Required - super().__init__('article', id) - self.title = title - self.input_message_content = input_message_content - - # Optional - self.reply_markup = reply_markup - self.url = url - self.hide_url = hide_url - self.description = description - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height diff --git a/telegramer/include/telegram/inline/inlinequeryresultaudio.py b/telegramer/include/telegram/inline/inlinequeryresultaudio.py deleted file mode 100644 index 9f06c62..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultaudio.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultAudio.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultAudio(InlineQueryResult): - """ - Represents a link to an mp3 audio file. By default, this audio file will be sent by the user. - Alternatively, you can use :attr:`input_message_content` to send a message with the specified - content instead of the audio. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - audio_url (:obj:`str`): A valid URL for the audio file. - title (:obj:`str`): Title. - performer (:obj:`str`, optional): Performer. - audio_duration (:obj:`str`, optional): Audio duration in seconds. - caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the audio. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'audio'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - audio_url (:obj:`str`): A valid URL for the audio file. - title (:obj:`str`): Title. - performer (:obj:`str`): Optional. Performer. - audio_duration (:obj:`str`): Optional. Audio duration in seconds. - caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the audio. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'parse_mode', - 'audio_url', - 'performer', - 'input_message_content', - 'audio_duration', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - audio_url: str, - title: str, - performer: str = None, - audio_duration: int = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - - # Required - super().__init__('audio', id) - self.audio_url = audio_url - self.title = title - - # Optionals - self.performer = performer - self.audio_duration = audio_duration - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedaudio.py b/telegramer/include/telegram/inline/inlinequeryresultcachedaudio.py deleted file mode 100644 index f92096a..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedaudio.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedAudio(InlineQueryResult): - """ - Represents a link to an mp3 audio file stored on the Telegram servers. By default, this audio - file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to - send a message with the specified content instead of the audio. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - audio_file_id (:obj:`str`): A valid file identifier for the audio file. - caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the audio. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'audio'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - audio_file_id (:obj:`str`): A valid file identifier for the audio file. - caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the audio. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'parse_mode', - 'audio_file_id', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - audio_file_id: str, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('audio', id) - self.audio_file_id = audio_file_id - - # Optionals - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcacheddocument.py b/telegramer/include/telegram/inline/inlinequeryresultcacheddocument.py deleted file mode 100644 index 4b76b74..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcacheddocument.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedDocument(InlineQueryResult): - """ - Represents a link to a file stored on the Telegram servers. By default, this file will be sent - by the user with an optional caption. Alternatively, you can use :attr:`input_message_content` - to send a message with the specified content instead of the file. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - title (:obj:`str`): Title for the result. - document_file_id (:obj:`str`): A valid file identifier for the file. - description (:obj:`str`, optional): Short description of the result. - caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption.. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the file. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'document'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - title (:obj:`str`): Title for the result. - document_file_id (:obj:`str`): A valid file identifier for the file. - description (:obj:`str`): Optional. Short description of the result. - caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption.. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the file. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'document_file_id', - 'caption', - 'title', - 'description', - 'parse_mode', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - title: str, - document_file_id: str, - description: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('document', id) - self.title = title - self.document_file_id = document_file_id - - # Optionals - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedgif.py b/telegramer/include/telegram/inline/inlinequeryresultcachedgif.py deleted file mode 100644 index 8cd3ad7..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedgif.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultCachedGif.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedGif(InlineQueryResult): - """ - Represents a link to an animated GIF file stored on the Telegram servers. By default, this - animated GIF file will be sent by the user with an optional caption. Alternatively, you can - use :attr:`input_message_content` to send a message with specified content instead of - the animation. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - gif_file_id (:obj:`str`): A valid file identifier for the GIF file. - title (:obj:`str`, optional): Title for the result.caption (:obj:`str`, optional): - caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the gif. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'gif'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - gif_file_id (:obj:`str`): A valid file identifier for the GIF file. - title (:obj:`str`): Optional. Title for the result. - caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the gif. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'input_message_content', - 'parse_mode', - 'gif_file_id', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - gif_file_id: str, - title: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('gif', id) - self.gif_file_id = gif_file_id - - # Optionals - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedmpeg4gif.py b/telegramer/include/telegram/inline/inlinequeryresultcachedmpeg4gif.py deleted file mode 100644 index 8f1d457..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedmpeg4gif.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the - Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an - optional caption. Alternatively, you can use :attr:`input_message_content` to send a message - with the specified content instead of the animation. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file. - title (:obj:`str`, optional): Title for the result. - caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the MPEG-4 file. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'mpeg4_gif'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file. - title (:obj:`str`): Optional. Title for the result. - caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the MPEG-4 file. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'mpeg4_file_id', - 'caption', - 'title', - 'parse_mode', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - mpeg4_file_id: str, - title: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('mpeg4_gif', id) - self.mpeg4_file_id = mpeg4_file_id - - # Optionals - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedphoto.py b/telegramer/include/telegram/inline/inlinequeryresultcachedphoto.py deleted file mode 100644 index 8bba69b..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedphoto.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains the classes that represent Telegram InlineQueryResultPhoto""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedPhoto(InlineQueryResult): - """ - Represents a link to a photo stored on the Telegram servers. By default, this photo will be - sent by the user with an optional caption. Alternatively, you can use - :attr:`input_message_content` to send a message with the specified content instead - of the photo. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - photo_file_id (:obj:`str`): A valid file identifier of the photo. - title (:obj:`str`, optional): Title for the result. - description (:obj:`str`, optional): Short description of the result. - caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the photo. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'photo'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - photo_file_id (:obj:`str`): A valid file identifier of the photo. - title (:obj:`str`): Optional. Title for the result. - description (:obj:`str`): Optional. Short description of the result. - caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the photo. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'description', - 'parse_mode', - 'photo_file_id', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - photo_file_id: str, - title: str = None, - description: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('photo', id) - self.photo_file_id = photo_file_id - - # Optionals - self.title = title - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedsticker.py b/telegramer/include/telegram/inline/inlinequeryresultcachedsticker.py deleted file mode 100644 index 0425fa5..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedsticker.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultCachedSticker.""" - -from typing import TYPE_CHECKING, Any - -from telegram import InlineQueryResult - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedSticker(InlineQueryResult): - """ - Represents a link to a sticker stored on the Telegram servers. By default, this sticker will - be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a - message with the specified content instead of the sticker. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - sticker_file_id (:obj:`str`): A valid file identifier of the sticker. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the sticker. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'sticker`. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - sticker_file_id (:obj:`str`): A valid file identifier of the sticker. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the sticker. - - """ - - __slots__ = ('reply_markup', 'input_message_content', 'sticker_file_id') - - def __init__( - self, - id: str, # pylint: disable=W0622 - sticker_file_id: str, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - **_kwargs: Any, - ): - # Required - super().__init__('sticker', id) - self.sticker_file_id = sticker_file_id - - # Optionals - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedvideo.py b/telegramer/include/telegram/inline/inlinequeryresultcachedvideo.py deleted file mode 100644 index 3283794..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedvideo.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedVideo(InlineQueryResult): - """ - Represents a link to a video file stored on the Telegram servers. By default, this video file - will be sent by the user with an optional caption. Alternatively, you can use - :attr:`input_message_content` to send a message with the specified content instead - of the video. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - video_file_id (:obj:`str`): A valid file identifier for the video file. - title (:obj:`str`): Title for the result. - description (:obj:`str`, optional): Short description of the result. - caption (:obj:`str`, optional): Caption of the video to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the video. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'video'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - video_file_id (:obj:`str`): A valid file identifier for the video file. - title (:obj:`str`): Title for the result. - description (:obj:`str`): Optional. Short description of the result. - caption (:obj:`str`): Optional. Caption of the video to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the video. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'description', - 'parse_mode', - 'input_message_content', - 'video_file_id', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - video_file_id: str, - title: str, - description: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('video', id) - self.video_file_id = video_file_id - self.title = title - - # Optionals - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcachedvoice.py b/telegramer/include/telegram/inline/inlinequeryresultcachedvoice.py deleted file mode 100644 index f5ff9bf..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcachedvoice.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultCachedVoice(InlineQueryResult): - """ - Represents a link to a voice message stored on the Telegram servers. By default, this voice - message will be sent by the user. Alternatively, you can use :attr:`input_message_content` to - send a message with the specified content instead of the voice message. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - voice_file_id (:obj:`str`): A valid file identifier for the voice message. - title (:obj:`str`): Voice message title. - caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the voice message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'voice'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - voice_file_id (:obj:`str`): A valid file identifier for the voice message. - title (:obj:`str`): Voice message title. - caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the voice message. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'parse_mode', - 'voice_file_id', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - voice_file_id: str, - title: str, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('voice', id) - self.voice_file_id = voice_file_id - self.title = title - - # Optionals - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultcontact.py b/telegramer/include/telegram/inline/inlinequeryresultcontact.py deleted file mode 100644 index df4f6c9..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultcontact.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultContact.""" - -from typing import TYPE_CHECKING, Any - -from telegram import InlineQueryResult - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultContact(InlineQueryResult): - """ - Represents a contact with a phone number. By default, this contact will be sent by the user. - Alternatively, you can use :attr:`input_message_content` to send a message with the specified - content instead of the contact. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - phone_number (:obj:`str`): Contact's phone number. - first_name (:obj:`str`): Contact's first name. - last_name (:obj:`str`, optional): Contact's last name. - vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, - 0-2048 bytes. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the contact. - thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. - thumb_width (:obj:`int`, optional): Thumbnail width. - thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'contact'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - phone_number (:obj:`str`): Contact's phone number. - first_name (:obj:`str`): Contact's first name. - last_name (:obj:`str`): Optional. Contact's last name. - vcard (:obj:`str`): Optional. Additional data about the contact in the form of a vCard, - 0-2048 bytes. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the contact. - thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result. - thumb_width (:obj:`int`): Optional. Thumbnail width. - thumb_height (:obj:`int`): Optional. Thumbnail height. - - """ - - __slots__ = ( - 'reply_markup', - 'thumb_width', - 'thumb_height', - 'vcard', - 'first_name', - 'last_name', - 'phone_number', - 'input_message_content', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - phone_number: str, - first_name: str, - last_name: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - thumb_url: str = None, - thumb_width: int = None, - thumb_height: int = None, - vcard: str = None, - **_kwargs: Any, - ): - # Required - super().__init__('contact', id) - self.phone_number = phone_number - self.first_name = first_name - - # Optionals - self.last_name = last_name - self.vcard = vcard - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height diff --git a/telegramer/include/telegram/inline/inlinequeryresultdocument.py b/telegramer/include/telegram/inline/inlinequeryresultdocument.py deleted file mode 100644 index 42555aa..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultdocument.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultDocument""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultDocument(InlineQueryResult): - """ - Represents a link to a file. By default, this file will be sent by the user with an optional - caption. Alternatively, you can use :attr:`input_message_content` to send a message with the - specified content instead of the file. Currently, only .PDF and .ZIP files can be sent - using this method. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - title (:obj:`str`): Title for the result. - caption (:obj:`str`, optional): Caption of the document to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - document_url (:obj:`str`): A valid URL for the file. - mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" - or "application/zip". - description (:obj:`str`, optional): Short description of the result. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the file. - thumb_url (:obj:`str`, optional): URL of the thumbnail (jpeg only) for the file. - thumb_width (:obj:`int`, optional): Thumbnail width. - thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'document'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - title (:obj:`str`): Title for the result. - caption (:obj:`str`): Optional. Caption of the document to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - document_url (:obj:`str`): A valid URL for the file. - mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" - or "application/zip". - description (:obj:`str`): Optional. Short description of the result. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the file. - thumb_url (:obj:`str`): Optional. URL of the thumbnail (jpeg only) for the file. - thumb_width (:obj:`int`): Optional. Thumbnail width. - thumb_height (:obj:`int`): Optional. Thumbnail height. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'document_url', - 'thumb_width', - 'thumb_height', - 'caption', - 'title', - 'description', - 'parse_mode', - 'mime_type', - 'thumb_url', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - document_url: str, - title: str, - mime_type: str, - caption: str = None, - description: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - thumb_url: str = None, - thumb_width: int = None, - thumb_height: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('document', id) - self.document_url = document_url - self.title = title - self.mime_type = mime_type - - # Optionals - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.description = description - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height diff --git a/telegramer/include/telegram/inline/inlinequeryresultgame.py b/telegramer/include/telegram/inline/inlinequeryresultgame.py deleted file mode 100644 index 02e4a7d..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultgame.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultGame.""" - -from typing import TYPE_CHECKING, Any - -from telegram import InlineQueryResult - -if TYPE_CHECKING: - from telegram import ReplyMarkup - - -class InlineQueryResultGame(InlineQueryResult): - """Represents a :class:`telegram.Game`. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - game_short_name (:obj:`str`): Short name of the game. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'game'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - game_short_name (:obj:`str`): Short name of the game. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - - """ - - __slots__ = ('reply_markup', 'game_short_name') - - def __init__( - self, - id: str, # pylint: disable=W0622 - game_short_name: str, - reply_markup: 'ReplyMarkup' = None, - **_kwargs: Any, - ): - # Required - super().__init__('game', id) - self.id = id # pylint: disable=W0622 - self.game_short_name = game_short_name - - self.reply_markup = reply_markup diff --git a/telegramer/include/telegram/inline/inlinequeryresultgif.py b/telegramer/include/telegram/inline/inlinequeryresultgif.py deleted file mode 100644 index e879dc0..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultgif.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains the classes that represent Telegram InlineQueryResultGif.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultGif(InlineQueryResult): - """ - Represents a link to an animated GIF file. By default, this animated GIF file will be sent by - the user with optional caption. Alternatively, you can use :attr:`input_message_content` to - send a message with the specified content instead of the animation. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - gif_url (:obj:`str`): A valid URL for the GIF file. File size must not exceed 1MB. - gif_width (:obj:`int`, optional): Width of the GIF. - gif_height (:obj:`int`, optional): Height of the GIF. - gif_duration (:obj:`int`, optional): Duration of the GIF - thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for - the result. - thumb_mime_type (:obj:`str`, optional): MIME type of the thumbnail, must be one of - ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``. - title (:obj:`str`, optional): Title for the result. - caption (:obj:`str`, optional): Caption of the GIF file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the GIF animation. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'gif'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - gif_url (:obj:`str`): A valid URL for the GIF file. File size must not exceed 1MB. - gif_width (:obj:`int`): Optional. Width of the GIF. - gif_height (:obj:`int`): Optional. Height of the GIF. - gif_duration (:obj:`int`): Optional. Duration of the GIF. - thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for - the result. - thumb_mime_type (:obj:`str`): Optional. MIME type of the thumbnail. - title (:obj:`str`): Optional. Title for the result. - caption (:obj:`str`): Optional. Caption of the GIF file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the GIF animation. - - """ - - __slots__ = ( - 'reply_markup', - 'gif_height', - 'thumb_mime_type', - 'caption_entities', - 'gif_width', - 'title', - 'caption', - 'parse_mode', - 'gif_duration', - 'input_message_content', - 'gif_url', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - gif_url: str, - thumb_url: str, - gif_width: int = None, - gif_height: int = None, - title: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - gif_duration: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb_mime_type: str = None, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - - # Required - super().__init__('gif', id) - self.gif_url = gif_url - self.thumb_url = thumb_url - - # Optionals - self.gif_width = gif_width - self.gif_height = gif_height - self.gif_duration = gif_duration - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_mime_type = thumb_mime_type diff --git a/telegramer/include/telegram/inline/inlinequeryresultlocation.py b/telegramer/include/telegram/inline/inlinequeryresultlocation.py deleted file mode 100644 index e55d90b..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultlocation.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultLocation.""" - -from typing import TYPE_CHECKING, Any - -from telegram import InlineQueryResult - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultLocation(InlineQueryResult): - """ - Represents a location on a map. By default, the location will be sent by the user. - Alternatively, you can use :attr:`input_message_content` to send a message with the specified - content instead of the location. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - latitude (:obj:`float`): Location latitude in degrees. - longitude (:obj:`float`): Location longitude in degrees. - title (:obj:`str`): Location title. - horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, - measured in meters; 0-1500. - live_period (:obj:`int`, optional): Period in seconds for which the location can be - updated, should be between 60 and 86400. - heading (:obj:`int`, optional): For live locations, a direction in which the user is - moving, in degrees. Must be between 1 and 360 if specified. - proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance for - proximity alerts about approaching another chat member, in meters. Must be between 1 - and 100000 if specified. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the location. - thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. - thumb_width (:obj:`int`, optional): Thumbnail width. - thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'location'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - latitude (:obj:`float`): Location latitude in degrees. - longitude (:obj:`float`): Location longitude in degrees. - title (:obj:`str`): Location title. - horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, - measured in meters. - live_period (:obj:`int`): Optional. Period in seconds for which the location can be - updated, should be between 60 and 86400. - heading (:obj:`int`): Optional. For live locations, a direction in which the user is - moving, in degrees. - proximity_alert_radius (:obj:`int`): Optional. For live locations, a maximum distance for - proximity alerts about approaching another chat member, in meters. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the location. - thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result. - thumb_width (:obj:`int`): Optional. Thumbnail width. - thumb_height (:obj:`int`): Optional. Thumbnail height. - - """ - - __slots__ = ( - 'longitude', - 'reply_markup', - 'thumb_width', - 'thumb_height', - 'heading', - 'title', - 'live_period', - 'proximity_alert_radius', - 'input_message_content', - 'latitude', - 'horizontal_accuracy', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - latitude: float, - longitude: float, - title: str, - live_period: int = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - thumb_url: str = None, - thumb_width: int = None, - thumb_height: int = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - **_kwargs: Any, - ): - # Required - super().__init__('location', id) - self.latitude = float(latitude) - self.longitude = float(longitude) - self.title = title - - # Optionals - self.live_period = live_period - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - self.horizontal_accuracy = float(horizontal_accuracy) if horizontal_accuracy else None - self.heading = int(heading) if heading else None - self.proximity_alert_radius = ( - int(proximity_alert_radius) if proximity_alert_radius else None - ) diff --git a/telegramer/include/telegram/inline/inlinequeryresultmpeg4gif.py b/telegramer/include/telegram/inline/inlinequeryresultmpeg4gif.py deleted file mode 100644 index 5f73218..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultmpeg4gif.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultMpeg4Gif(InlineQueryResult): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this - animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can - use :attr:`input_message_content` to send a message with the specified content instead of the - animation. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - mpeg4_url (:obj:`str`): A valid URL for the MP4 file. File size must not exceed 1MB. - mpeg4_width (:obj:`int`, optional): Video width. - mpeg4_height (:obj:`int`, optional): Video height. - mpeg4_duration (:obj:`int`, optional): Video duration. - thumb_url (:obj:`str`): URL of the static thumbnail (jpeg or gif) for the result. - thumb_mime_type (:obj:`str`): Optional. MIME type of the thumbnail, must be one of - ``'image/jpeg'``, ``'image/gif'``, or ``'video/mp4'``. Defaults to ``'image/jpeg'``. - title (:obj:`str`, optional): Title for the result. - caption (:obj:`str`, optional): Caption of the MPEG-4 file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the video animation. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'mpeg4_gif'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - mpeg4_url (:obj:`str`): A valid URL for the MP4 file. File size must not exceed 1MB. - mpeg4_width (:obj:`int`): Optional. Video width. - mpeg4_height (:obj:`int`): Optional. Video height. - mpeg4_duration (:obj:`int`): Optional. Video duration. - thumb_url (:obj:`str`): URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for - the result. - thumb_mime_type (:obj:`str`): Optional. MIME type of the thumbnail. - title (:obj:`str`): Optional. Title for the result. - caption (:obj:`str`): Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters - after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the video animation. - - """ - - __slots__ = ( - 'reply_markup', - 'thumb_mime_type', - 'caption_entities', - 'mpeg4_duration', - 'mpeg4_width', - 'title', - 'caption', - 'parse_mode', - 'input_message_content', - 'mpeg4_url', - 'mpeg4_height', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - mpeg4_url: str, - thumb_url: str, - mpeg4_width: int = None, - mpeg4_height: int = None, - title: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - mpeg4_duration: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb_mime_type: str = None, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - - # Required - super().__init__('mpeg4_gif', id) - self.mpeg4_url = mpeg4_url - self.thumb_url = thumb_url - - # Optional - self.mpeg4_width = mpeg4_width - self.mpeg4_height = mpeg4_height - self.mpeg4_duration = mpeg4_duration - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_mime_type = thumb_mime_type diff --git a/telegramer/include/telegram/inline/inlinequeryresultphoto.py b/telegramer/include/telegram/inline/inlinequeryresultphoto.py deleted file mode 100644 index e6de727..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultphoto.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultPhoto.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultPhoto(InlineQueryResult): - """ - Represents a link to a photo. By default, this photo will be sent by the user with optional - caption. Alternatively, you can use :attr:`input_message_content` to send a message with the - specified content instead of the photo. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - photo_url (:obj:`str`): A valid URL of the photo. Photo must be in jpeg format. Photo size - must not exceed 5MB. - thumb_url (:obj:`str`): URL of the thumbnail for the photo. - photo_width (:obj:`int`, optional): Width of the photo. - photo_height (:obj:`int`, optional): Height of the photo. - title (:obj:`str`, optional): Title for the result. - description (:obj:`str`, optional): Short description of the result. - caption (:obj:`str`, optional): Caption of the photo to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the photo. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'photo'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - photo_url (:obj:`str`): A valid URL of the photo. Photo must be in jpeg format. Photo size - must not exceed 5MB. - thumb_url (:obj:`str`): URL of the thumbnail for the photo. - photo_width (:obj:`int`): Optional. Width of the photo. - photo_height (:obj:`int`): Optional. Height of the photo. - title (:obj:`str`): Optional. Title for the result. - description (:obj:`str`): Optional. Short description of the result. - caption (:obj:`str`): Optional. Caption of the photo to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the photo. - - """ - - __slots__ = ( - 'photo_url', - 'reply_markup', - 'caption_entities', - 'photo_width', - 'caption', - 'title', - 'description', - 'parse_mode', - 'input_message_content', - 'photo_height', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - photo_url: str, - thumb_url: str, - photo_width: int = None, - photo_height: int = None, - title: str = None, - description: str = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - super().__init__('photo', id) - self.photo_url = photo_url - self.thumb_url = thumb_url - - # Optionals - self.photo_width = int(photo_width) if photo_width is not None else None - self.photo_height = int(photo_height) if photo_height is not None else None - self.title = title - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultvenue.py b/telegramer/include/telegram/inline/inlinequeryresultvenue.py deleted file mode 100644 index 4a64a10..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultvenue.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultVenue.""" - -from typing import TYPE_CHECKING, Any - -from telegram import InlineQueryResult - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultVenue(InlineQueryResult): - """ - Represents a venue. By default, the venue will be sent by the user. Alternatively, you can - use :attr:`input_message_content` to send a message with the specified content instead of the - venue. - - Note: - Foursquare details and Google Pace details are mutually exclusive. However, this - behaviour is undocumented and might be changed by Telegram. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - latitude (:obj:`float`): Latitude of the venue location in degrees. - longitude (:obj:`float`): Longitude of the venue location in degrees. - title (:obj:`str`): Title of the venue. - address (:obj:`str`): Address of the venue. - foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue if known. - foursquare_type (:obj:`str`, optional): Foursquare type of the venue, if known. - (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or - "food/icecream".) - google_place_id (:obj:`str`, optional): Google Places identifier of the venue. - google_place_type (:obj:`str`, optional): Google Places type of the venue. (See - `supported types <https://developers.google.com/places/web-service/supported_types>`_.) - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the location. - thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. - thumb_width (:obj:`int`, optional): Thumbnail width. - thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'venue'. - id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - latitude (:obj:`float`): Latitude of the venue location in degrees. - longitude (:obj:`float`): Longitude of the venue location in degrees. - title (:obj:`str`): Title of the venue. - address (:obj:`str`): Address of the venue. - foursquare_id (:obj:`str`): Optional. Foursquare identifier of the venue if known. - foursquare_type (:obj:`str`): Optional. Foursquare type of the venue, if known. - google_place_id (:obj:`str`): Optional. Google Places identifier of the venue. - google_place_type (:obj:`str`): Optional. Google Places type of the venue. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the venue. - thumb_url (:obj:`str`): Optional. Url of the thumbnail for the result. - thumb_width (:obj:`int`): Optional. Thumbnail width. - thumb_height (:obj:`int`): Optional. Thumbnail height. - - """ - - __slots__ = ( - 'longitude', - 'reply_markup', - 'google_place_type', - 'thumb_width', - 'thumb_height', - 'title', - 'address', - 'foursquare_id', - 'foursquare_type', - 'google_place_id', - 'input_message_content', - 'latitude', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - latitude: float, - longitude: float, - title: str, - address: str, - foursquare_id: str = None, - foursquare_type: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - thumb_url: str = None, - thumb_width: int = None, - thumb_height: int = None, - google_place_id: str = None, - google_place_type: str = None, - **_kwargs: Any, - ): - - # Required - super().__init__('venue', id) - self.latitude = latitude - self.longitude = longitude - self.title = title - self.address = address - - # Optional - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type - self.google_place_id = google_place_id - self.google_place_type = google_place_type - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height diff --git a/telegramer/include/telegram/inline/inlinequeryresultvideo.py b/telegramer/include/telegram/inline/inlinequeryresultvideo.py deleted file mode 100644 index 098e3b0..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultvideo.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultVideo.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultVideo(InlineQueryResult): - """ - Represents a link to a page containing an embedded video player or a video file. By default, - this video file will be sent by the user with an optional caption. Alternatively, you can use - :attr:`input_message_content` to send a message with the specified content instead of - the video. - - Note: - If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must - replace its content using :attr:`input_message_content`. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - video_url (:obj:`str`): A valid URL for the embedded video player or video file. - mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4". - thumb_url (:obj:`str`): URL of the thumbnail (jpeg only) for the video. - title (:obj:`str`): Title for the result. - caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - video_width (:obj:`int`, optional): Video width. - video_height (:obj:`int`, optional): Video height. - video_duration (:obj:`int`, optional): Video duration in seconds. - description (:obj:`str`, optional): Short description of the result. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the video. This field is required if - InlineQueryResultVideo is used to send an HTML-page as a result - (e.g., a YouTube video). - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'video'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - video_url (:obj:`str`): A valid URL for the embedded video player or video file. - mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4". - thumb_url (:obj:`str`): URL of the thumbnail (jpeg only) for the video. - title (:obj:`str`): Title for the result. - caption (:obj:`str`): Optional. Caption of the video to be sent, 0-1024 characters after - entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - video_width (:obj:`int`): Optional. Video width. - video_height (:obj:`int`): Optional. Video height. - video_duration (:obj:`int`): Optional. Video duration in seconds. - description (:obj:`str`): Optional. Short description of the result. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the video. This field is required if - InlineQueryResultVideo is used to send an HTML-page as a result - (e.g., a YouTube video). - - """ - - __slots__ = ( - 'video_url', - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'description', - 'video_duration', - 'parse_mode', - 'mime_type', - 'input_message_content', - 'video_height', - 'video_width', - 'thumb_url', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - video_url: str, - mime_type: str, - thumb_url: str, - title: str, - caption: str = None, - video_width: int = None, - video_height: int = None, - video_duration: int = None, - description: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - - # Required - super().__init__('video', id) - self.video_url = video_url - self.mime_type = mime_type - self.thumb_url = thumb_url - self.title = title - - # Optional - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.video_width = video_width - self.video_height = video_height - self.video_duration = video_duration - self.description = description - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inlinequeryresultvoice.py b/telegramer/include/telegram/inline/inlinequeryresultvoice.py deleted file mode 100644 index 960f41b..0000000 --- a/telegramer/include/telegram/inline/inlinequeryresultvoice.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InlineQueryResultVoice.""" - -from typing import TYPE_CHECKING, Any, Union, Tuple, List - -from telegram import InlineQueryResult, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import ODVInput - -if TYPE_CHECKING: - from telegram import InputMessageContent, ReplyMarkup - - -class InlineQueryResultVoice(InlineQueryResult): - """ - Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, - this voice recording will be sent by the user. Alternatively, you can use - :attr:`input_message_content` to send a message with the specified content instead of the - the voice message. - - Args: - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - voice_url (:obj:`str`): A valid URL for the voice recording. - title (:obj:`str`): Recording title. - caption (:obj:`str`, optional): Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - voice_duration (:obj:`int`, optional): Recording duration in seconds. - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the - message to be sent instead of the voice recording. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): 'voice'. - id (:obj:`str`): Unique identifier for this result, 1-64 bytes. - voice_url (:obj:`str`): A valid URL for the voice recording. - title (:obj:`str`): Recording title. - caption (:obj:`str`): Optional. Caption, 0-1024 characters after entities parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in the media caption. See the constants - in :class:`telegram.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - voice_duration (:obj:`int`): Optional. Recording duration in seconds. - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the - message to be sent instead of the voice recording. - - """ - - __slots__ = ( - 'reply_markup', - 'caption_entities', - 'voice_duration', - 'caption', - 'title', - 'voice_url', - 'parse_mode', - 'input_message_content', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - voice_url: str, - title: str, - voice_duration: int = None, - caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - - # Required - super().__init__('voice', id) - self.voice_url = voice_url - self.title = title - - # Optional - self.voice_duration = voice_duration - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = caption_entities - self.reply_markup = reply_markup - self.input_message_content = input_message_content diff --git a/telegramer/include/telegram/inline/inputcontactmessagecontent.py b/telegramer/include/telegram/inline/inputcontactmessagecontent.py deleted file mode 100644 index fe7b9d7..0000000 --- a/telegramer/include/telegram/inline/inputcontactmessagecontent.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InputContactMessageContent.""" - -from typing import Any - -from telegram import InputMessageContent - - -class InputContactMessageContent(InputMessageContent): - """Represents the content of a contact message to be sent as the result of an inline query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`phone_number` is equal. - - Args: - phone_number (:obj:`str`): Contact's phone number. - first_name (:obj:`str`): Contact's first name. - last_name (:obj:`str`, optional): Contact's last name. - vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, - 0-2048 bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - phone_number (:obj:`str`): Contact's phone number. - first_name (:obj:`str`): Contact's first name. - last_name (:obj:`str`): Optional. Contact's last name. - vcard (:obj:`str`): Optional. Additional data about the contact in the form of a vCard, - 0-2048 bytes. - - """ - - __slots__ = ('vcard', 'first_name', 'last_name', 'phone_number', '_id_attrs') - - def __init__( - self, - phone_number: str, - first_name: str, - last_name: str = None, - vcard: str = None, - **_kwargs: Any, - ): - # Required - self.phone_number = phone_number - self.first_name = first_name - # Optionals - self.last_name = last_name - self.vcard = vcard - - self._id_attrs = (self.phone_number,) diff --git a/telegramer/include/telegram/inline/inputinvoicemessagecontent.py b/telegramer/include/telegram/inline/inputinvoicemessagecontent.py deleted file mode 100644 index 622f0ed..0000000 --- a/telegramer/include/telegram/inline/inputinvoicemessagecontent.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains a class that represents a Telegram InputInvoiceMessageContent.""" - -from typing import Any, List, Optional, TYPE_CHECKING - -from telegram import InputMessageContent, LabeledPrice -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class InputInvoiceMessageContent(InputMessageContent): - """ - Represents the content of a invoice message to be sent as the result of an inline query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`title`, :attr:`description`, :attr:`payload`, - :attr:`provider_token`, :attr:`currency` and :attr:`prices` are equal. - - .. versionadded:: 13.5 - - Args: - title (:obj:`str`): Product name, 1-32 characters - description (:obj:`str`): Product description, 1-255 characters - payload (:obj:`str`):Bot-defined invoice payload, 1-128 bytes. This will not be displayed - to the user, use for your internal processes. - provider_token (:obj:`str`): Payment provider token, obtained via - `@Botfather <https://t.me/Botfather>`_. - currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on - `currencies <https://core.telegram.org/bots/payments#supported-currencies>`_ - prices (List[:class:`telegram.LabeledPrice`]): Price breakdown, a JSON-serialized list of - components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, - etc.) - max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the smallest - units of the currency (integer, not float/double). For example, for a maximum tip of - US$ 1.45 pass ``max_tip_amount = 145``. See the ``exp`` parameter in - `currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, it - shows the number of digits past the decimal point for each currency (2 for the majority - of currencies). Defaults to ``0``. - suggested_tip_amounts (List[:obj:`int`], optional): A JSON-serialized array of suggested - amounts of tip in the smallest units of the currency (integer, not float/double). At - most 4 suggested tip amounts can be specified. The suggested tip amounts must be - positive, passed in a strictly increased order and must not exceed - :attr:`max_tip_amount`. - provider_data (:obj:`str`, optional): A JSON-serialized object for data about the invoice, - which will be shared with the payment provider. A detailed description of the required - fields should be provided by the payment provider. - photo_url (:obj:`str`, optional): URL of the product photo for the invoice. Can be a photo - of the goods or a marketing image for a service. People like it better when they see - what they are paying for. - photo_size (:obj:`int`, optional): Photo size. - photo_width (:obj:`int`, optional): Photo width. - photo_height (:obj:`int`, optional): Photo height. - need_name (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's full name to - complete the order. - need_phone_number (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - phone number to complete the order - need_email (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's email - address to complete the order. - need_shipping_address (:obj:`bool`, optional): Pass :obj:`True`, if you require the user's - shipping address to complete the order - send_phone_number_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's phone - number should be sent to provider. - send_email_to_provider (:obj:`bool`, optional): Pass :obj:`True`, if user's email address - should be sent to provider. - is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on the - shipping method. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - title (:obj:`str`): Product name, 1-32 characters - description (:obj:`str`): Product description, 1-255 characters - payload (:obj:`str`):Bot-defined invoice payload, 1-128 bytes. This will not be displayed - to the user, use for your internal processes. - provider_token (:obj:`str`): Payment provider token, obtained via - `@Botfather <https://t.me/Botfather>`_. - currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on - `currencies <https://core.telegram.org/bots/payments#supported-currencies>`_ - prices (List[:class:`telegram.LabeledPrice`]): Price breakdown, a JSON-serialized list of - components. - max_tip_amount (:obj:`int`): Optional. The maximum accepted amount for tips in the smallest - units of the currency (integer, not float/double). - suggested_tip_amounts (List[:obj:`int`]): Optional. A JSON-serialized array of suggested - amounts of tip in the smallest units of the currency (integer, not float/double). - provider_data (:obj:`str`): Optional. A JSON-serialized object for data about the invoice, - which will be shared with the payment provider. - photo_url (:obj:`str`): Optional. URL of the product photo for the invoice. - photo_size (:obj:`int`): Optional. Photo size. - photo_width (:obj:`int`): Optional. Photo width. - photo_height (:obj:`int`): Optional. Photo height. - need_name (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's full name to - complete the order. - need_phone_number (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's - phone number to complete the order - need_email (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's email - address to complete the order. - need_shipping_address (:obj:`bool`): Optional. Pass :obj:`True`, if you require the user's - shipping address to complete the order - send_phone_number_to_provider (:obj:`bool`): Optional. Pass :obj:`True`, if user's phone - number should be sent to provider. - send_email_to_provider (:obj:`bool`): Optional. Pass :obj:`True`, if user's email address - should be sent to provider. - is_flexible (:obj:`bool`): Optional. Pass :obj:`True`, if the final price depends on the - shipping method. - - """ - - __slots__ = ( - 'title', - 'description', - 'payload', - 'provider_token', - 'currency', - 'prices', - 'max_tip_amount', - 'suggested_tip_amounts', - 'provider_data', - 'photo_url', - 'photo_size', - 'photo_width', - 'photo_height', - 'need_name', - 'need_phone_number', - 'need_email', - 'need_shipping_address', - 'send_phone_number_to_provider', - 'send_email_to_provider', - 'is_flexible', - '_id_attrs', - ) - - def __init__( - self, - title: str, - description: str, - payload: str, - provider_token: str, - currency: str, - prices: List[LabeledPrice], - max_tip_amount: int = None, - suggested_tip_amounts: List[int] = None, - provider_data: str = None, - photo_url: str = None, - photo_size: int = None, - photo_width: int = None, - photo_height: int = None, - need_name: bool = None, - need_phone_number: bool = None, - need_email: bool = None, - need_shipping_address: bool = None, - send_phone_number_to_provider: bool = None, - send_email_to_provider: bool = None, - is_flexible: bool = None, - **_kwargs: Any, - ): - # Required - self.title = title - self.description = description - self.payload = payload - self.provider_token = provider_token - self.currency = currency - self.prices = prices - # Optionals - self.max_tip_amount = int(max_tip_amount) if max_tip_amount else None - self.suggested_tip_amounts = ( - [int(sta) for sta in suggested_tip_amounts] if suggested_tip_amounts else None - ) - self.provider_data = provider_data - self.photo_url = photo_url - self.photo_size = int(photo_size) if photo_size else None - self.photo_width = int(photo_width) if photo_width else None - self.photo_height = int(photo_height) if photo_height else None - self.need_name = need_name - self.need_phone_number = need_phone_number - self.need_email = need_email - self.need_shipping_address = need_shipping_address - self.send_phone_number_to_provider = send_phone_number_to_provider - self.send_email_to_provider = send_email_to_provider - self.is_flexible = is_flexible - - self._id_attrs = ( - self.title, - self.description, - self.payload, - self.provider_token, - self.currency, - self.prices, - ) - - def __hash__(self) -> int: - # we override this as self.prices is a list and not hashable - prices = tuple(self.prices) - return hash( - ( - self.title, - self.description, - self.payload, - self.provider_token, - self.currency, - prices, - ) - ) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['prices'] = [price.to_dict() for price in self.prices] - - return data - - @classmethod - def de_json( - cls, data: Optional[JSONDict], bot: 'Bot' - ) -> Optional['InputInvoiceMessageContent']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['prices'] = LabeledPrice.de_list(data.get('prices'), bot) - - return cls(**data, bot=bot) diff --git a/telegramer/include/telegram/inline/inputlocationmessagecontent.py b/telegramer/include/telegram/inline/inputlocationmessagecontent.py deleted file mode 100644 index 22760bf..0000000 --- a/telegramer/include/telegram/inline/inputlocationmessagecontent.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InputLocationMessageContent.""" - -from typing import Any - -from telegram import InputMessageContent - - -class InputLocationMessageContent(InputMessageContent): - # fmt: off - """ - Represents the content of a location message to be sent as the result of an inline query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`latitude` and :attr:`longitude` are equal. - - Args: - latitude (:obj:`float`): Latitude of the location in degrees. - longitude (:obj:`float`): Longitude of the location in degrees. - horizontal_accuracy (:obj:`float`, optional): The radius of uncertainty for the location, - measured in meters; 0-1500. - live_period (:obj:`int`, optional): Period in seconds for which the location can be - updated, should be between 60 and 86400. - heading (:obj:`int`, optional): For live locations, a direction in which the user is - moving, in degrees. Must be between 1 and 360 if specified. - proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance for - proximity alerts about approaching another chat member, in meters. Must be between 1 - and 100000 if specified. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - latitude (:obj:`float`): Latitude of the location in degrees. - longitude (:obj:`float`): Longitude of the location in degrees. - horizontal_accuracy (:obj:`float`): Optional. The radius of uncertainty for the location, - measured in meters. - live_period (:obj:`int`): Optional. Period in seconds for which the location can be - updated. - heading (:obj:`int`): Optional. For live locations, a direction in which the user is - moving, in degrees. - proximity_alert_radius (:obj:`int`): Optional. For live locations, a maximum distance for - proximity alerts about approaching another chat member, in meters. - - """ - - __slots__ = ('longitude', 'horizontal_accuracy', 'proximity_alert_radius', 'live_period', - 'latitude', 'heading', '_id_attrs') - # fmt: on - - def __init__( - self, - latitude: float, - longitude: float, - live_period: int = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - **_kwargs: Any, - ): - # Required - self.latitude = latitude - self.longitude = longitude - - # Optionals - self.live_period = int(live_period) if live_period else None - self.horizontal_accuracy = float(horizontal_accuracy) if horizontal_accuracy else None - self.heading = int(heading) if heading else None - self.proximity_alert_radius = ( - int(proximity_alert_radius) if proximity_alert_radius else None - ) - - self._id_attrs = (self.latitude, self.longitude) diff --git a/telegramer/include/telegram/inline/inputmessagecontent.py b/telegramer/include/telegram/inline/inputmessagecontent.py deleted file mode 100644 index 4362c72..0000000 --- a/telegramer/include/telegram/inline/inputmessagecontent.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InputMessageContent.""" - -from telegram import TelegramObject - - -class InputMessageContent(TelegramObject): - """Base class for Telegram InputMessageContent Objects. - - See: :class:`telegram.InputContactMessageContent`, - :class:`telegram.InputInvoiceMessageContent`, - :class:`telegram.InputLocationMessageContent`, :class:`telegram.InputTextMessageContent` and - :class:`telegram.InputVenueMessageContent` for more details. - - """ - - __slots__ = () diff --git a/telegramer/include/telegram/inline/inputtextmessagecontent.py b/telegramer/include/telegram/inline/inputtextmessagecontent.py deleted file mode 100644 index c068aa7..0000000 --- a/telegramer/include/telegram/inline/inputtextmessagecontent.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InputTextMessageContent.""" - -from typing import Any, Union, Tuple, List - -from telegram import InputMessageContent, MessageEntity -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - - -class InputTextMessageContent(InputMessageContent): - """ - Represents the content of a text message to be sent as the result of an inline query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`message_text` is equal. - - Args: - message_text (:obj:`str`): Text of the message to be sent, 1-4096 characters after entities - parsing. Also found as :attr:`telegram.constants.MAX_MESSAGE_LENGTH`. - parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in your bot's message. See the constants - in :class:`telegram.ParseMode` for the available modes. - entities (List[:class:`telegram.MessageEntity`], optional): List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the - sent message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - message_text (:obj:`str`): Text of the message to be sent, 1-4096 characters after entities - parsing. - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or inline URLs in your bot's message. See the constants - in :class:`telegram.ParseMode` for the available modes. - entities (List[:class:`telegram.MessageEntity`]): Optional. List of special - entities that appear in the caption, which can be specified instead of - :attr:`parse_mode`. - disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in the - sent message. - - """ - - __slots__ = ('disable_web_page_preview', 'parse_mode', 'entities', 'message_text', '_id_attrs') - - def __init__( - self, - message_text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, - ): - # Required - self.message_text = message_text - # Optionals - self.parse_mode = parse_mode - self.entities = entities - self.disable_web_page_preview = disable_web_page_preview - - self._id_attrs = (self.message_text,) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - if self.entities: - data['entities'] = [ce.to_dict() for ce in self.entities] - - return data diff --git a/telegramer/include/telegram/inline/inputvenuemessagecontent.py b/telegramer/include/telegram/inline/inputvenuemessagecontent.py deleted file mode 100644 index c13107d..0000000 --- a/telegramer/include/telegram/inline/inputvenuemessagecontent.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the classes that represent Telegram InputVenueMessageContent.""" - -from typing import Any - -from telegram import InputMessageContent - - -class InputVenueMessageContent(InputMessageContent): - """Represents the content of a venue message to be sent as the result of an inline query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`latitude`, :attr:`longitude` and :attr:`title` - are equal. - - Note: - Foursquare details and Google Pace details are mutually exclusive. However, this - behaviour is undocumented and might be changed by Telegram. - - Args: - latitude (:obj:`float`): Latitude of the location in degrees. - longitude (:obj:`float`): Longitude of the location in degrees. - title (:obj:`str`): Name of the venue. - address (:obj:`str`): Address of the venue. - foursquare_id (:obj:`str`, optional): Foursquare identifier of the venue, if known. - foursquare_type (:obj:`str`, optional): Foursquare type of the venue, if known. - (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or - "food/icecream".) - google_place_id (:obj:`str`, optional): Google Places identifier of the venue. - google_place_type (:obj:`str`, optional): Google Places type of the venue. (See - `supported types <https://developers.google.com/places/web-service/supported_types>`_.) - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - latitude (:obj:`float`): Latitude of the location in degrees. - longitude (:obj:`float`): Longitude of the location in degrees. - title (:obj:`str`): Name of the venue. - address (:obj:`str`): Address of the venue. - foursquare_id (:obj:`str`): Optional. Foursquare identifier of the venue, if known. - foursquare_type (:obj:`str`): Optional. Foursquare type of the venue, if known. - google_place_id (:obj:`str`): Optional. Google Places identifier of the venue. - google_place_type (:obj:`str`): Optional. Google Places type of the venue. - - """ - - __slots__ = ( - 'longitude', - 'google_place_type', - 'title', - 'address', - 'foursquare_id', - 'foursquare_type', - 'google_place_id', - 'latitude', - '_id_attrs', - ) - - def __init__( - self, - latitude: float, - longitude: float, - title: str, - address: str, - foursquare_id: str = None, - foursquare_type: str = None, - google_place_id: str = None, - google_place_type: str = None, - **_kwargs: Any, - ): - # Required - self.latitude = latitude - self.longitude = longitude - self.title = title - self.address = address - # Optionals - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type - self.google_place_id = google_place_id - self.google_place_type = google_place_type - - self._id_attrs = ( - self.latitude, - self.longitude, - self.title, - ) diff --git a/telegramer/include/telegram/keyboardbutton.py b/telegramer/include/telegram/keyboardbutton.py deleted file mode 100644 index fe2bd87..0000000 --- a/telegramer/include/telegram/keyboardbutton.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram KeyboardButton.""" - -from typing import Any - -from telegram import TelegramObject, KeyboardButtonPollType - - -class KeyboardButton(TelegramObject): - """ - This object represents one button of the reply keyboard. For simple text buttons String can be - used instead of this object to specify text of the button. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`text`, :attr:`request_contact`, :attr:`request_location` and - :attr:`request_poll` are equal. - - Note: - * Optional fields are mutually exclusive. - * :attr:`request_contact` and :attr:`request_location` options will only work in Telegram - versions released after 9 April, 2016. Older clients will ignore them. - * :attr:`request_poll` option will only work in Telegram versions released after 23 - January, 2020. Older clients will receive unsupported message. - - Args: - text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be - sent to the bot as a message when the button is pressed. - request_contact (:obj:`bool`, optional): If :obj:`True`, the user's phone number will be - sent as a contact when the button is pressed. Available in private chats only. - request_location (:obj:`bool`, optional): If :obj:`True`, the user's current location will - be sent when the button is pressed. Available in private chats only. - request_poll (:class:`KeyboardButtonPollType`, optional): If specified, the user will be - asked to create a poll and send it to the bot when the button is pressed. Available in - private chats only. - - Attributes: - text (:obj:`str`): Text of the button. - request_contact (:obj:`bool`): Optional. The user's phone number will be sent. - request_location (:obj:`bool`): Optional. The user's current location will be sent. - request_poll (:class:`KeyboardButtonPollType`): Optional. If the user should create a poll. - - """ - - __slots__ = ('request_location', 'request_contact', 'request_poll', 'text', '_id_attrs') - - def __init__( - self, - text: str, - request_contact: bool = None, - request_location: bool = None, - request_poll: KeyboardButtonPollType = None, - **_kwargs: Any, - ): - # Required - self.text = text - # Optionals - self.request_contact = request_contact - self.request_location = request_location - self.request_poll = request_poll - - self._id_attrs = ( - self.text, - self.request_contact, - self.request_location, - self.request_poll, - ) diff --git a/telegramer/include/telegram/keyboardbuttonpolltype.py b/telegramer/include/telegram/keyboardbuttonpolltype.py deleted file mode 100644 index 813ce97..0000000 --- a/telegramer/include/telegram/keyboardbuttonpolltype.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a type of a Telegram Poll.""" -from typing import Any - -from telegram import TelegramObject - - -class KeyboardButtonPollType(TelegramObject): - """This object represents type of a poll, which is allowed to be created - and sent when the corresponding button is pressed. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type` is equal. - - Attributes: - type (:obj:`str`): Optional. If :attr:`telegram.Poll.QUIZ` is passed, the user will be - allowed to create only polls in the quiz mode. If :attr:`telegram.Poll.REGULAR` is - passed, only regular polls will be allowed. Otherwise, the user will be allowed to - create a poll of any type. - """ - - __slots__ = ('type', '_id_attrs') - - def __init__(self, type: str = None, **_kwargs: Any): # pylint: disable=W0622 - self.type = type - - self._id_attrs = (self.type,) diff --git a/telegramer/include/telegram/loginurl.py b/telegramer/include/telegram/loginurl.py deleted file mode 100644 index 7d6dc56..0000000 --- a/telegramer/include/telegram/loginurl.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram LoginUrl.""" -from typing import Any - -from telegram import TelegramObject - - -class LoginUrl(TelegramObject): - """This object represents a parameter of the inline keyboard button used to automatically - authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is - coming from Telegram. All the user needs to do is tap/click a button and confirm that they want - to log in. Telegram apps support these buttons as of version 5.7. - - Sample bot: `@discussbot <https://t.me/discussbot>`_ - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`url` is equal. - - Note: - You must always check the hash of the received data to verify the authentication - and the integrity of the data as described in - `Checking authorization <https://core.telegram.org/widgets/login#checking-authorization>`_ - - Args: - url (:obj:`str`): An HTTP URL to be opened with user authorization data added to the query - string when the button is pressed. If the user refuses to provide authorization data, - the original URL without information about the user will be opened. The data added is - the same as described in - `Receiving authorization data - <https://core.telegram.org/widgets/login#receiving-authorization-data>`_ - forward_text (:obj:`str`, optional): New text of the button in forwarded messages. - bot_username (:obj:`str`, optional): Username of a bot, which will be used for user - authorization. See - `Setting up a bot <https://core.telegram.org/widgets/login#setting-up-a-bot>`_ - for more details. If not specified, the current - bot's username will be assumed. The url's domain must be the same as the domain linked - with the bot. See - `Linking your domain to the bot - <https://core.telegram.org/widgets/login#linking-your-domain-to-the-bot>`_ - for more details. - request_write_access (:obj:`bool`, optional): Pass :obj:`True` to request the permission - for your bot to send messages to the user. - - Attributes: - url (:obj:`str`): An HTTP URL to be opened with user authorization data. - forward_text (:obj:`str`): Optional. New text of the button in forwarded messages. - bot_username (:obj:`str`): Optional. Username of a bot, which will be used for user - authorization. - request_write_access (:obj:`bool`): Optional. Pass :obj:`True` to request the permission - for your bot to send messages to the user. - - """ - - __slots__ = ('bot_username', 'request_write_access', 'url', 'forward_text', '_id_attrs') - - def __init__( - self, - url: str, - forward_text: bool = None, - bot_username: str = None, - request_write_access: bool = None, - **_kwargs: Any, - ): - # Required - self.url = url - # Optional - self.forward_text = forward_text - self.bot_username = bot_username - self.request_write_access = request_write_access - - self._id_attrs = (self.url,) diff --git a/telegramer/include/telegram/message.py b/telegramer/include/telegram/message.py deleted file mode 100644 index 74d750f..0000000 --- a/telegramer/include/telegram/message.py +++ /dev/null @@ -1,3010 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0902,R0913 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Message.""" -import datetime -import sys -from html import escape -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, ClassVar, Tuple - -from telegram import ( - Animation, - Audio, - Chat, - Contact, - Dice, - Document, - Game, - InlineKeyboardMarkup, - Invoice, - Location, - MessageEntity, - ParseMode, - PassportData, - PhotoSize, - Poll, - Sticker, - SuccessfulPayment, - TelegramObject, - User, - Venue, - Video, - VideoNote, - Voice, - VoiceChatStarted, - VoiceChatEnded, - VoiceChatParticipantsInvited, - ProximityAlertTriggered, - ReplyMarkup, - MessageAutoDeleteTimerChanged, - VoiceChatScheduled, -) -from telegram.utils.helpers import ( - escape_markdown, - from_timestamp, - to_timestamp, - DEFAULT_NONE, - DEFAULT_20, -) -from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput - -if TYPE_CHECKING: - from telegram import ( - Bot, - GameHighScore, - InputMedia, - MessageId, - InputMediaAudio, - InputMediaDocument, - InputMediaPhoto, - InputMediaVideo, - LabeledPrice, - ) - - -class Message(TelegramObject): - # fmt: off - """This object represents a message. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`message_id` and :attr:`chat` are equal. - - Note: - In Python ``from`` is a reserved word, use ``from_user`` instead. - - Args: - message_id (:obj:`int`): Unique message identifier inside this chat. - from_user (:class:`telegram.User`, optional): Sender of the message; empty for messages - sent to channels. For backward compatibility, this will contain a fake sender user in - non-channel chats, if the message was sent on behalf of a chat. - sender_chat (:class:`telegram.Chat`, optional): Sender of the message, sent on behalf of a - chat. For example, the channel itself for channel posts, the supergroup itself for - messages from anonymous group administrators, the linked channel for messages - automatically forwarded to the discussion group. For backward compatibility, - :attr:`from_user` contains a fake sender user in non-channel chats, if the message was - sent on behalf of a chat. - date (:class:`datetime.datetime`): Date the message was sent in Unix time. Converted to - :class:`datetime.datetime`. - chat (:class:`telegram.Chat`): Conversation the message belongs to. - forward_from (:class:`telegram.User`, optional): For forwarded messages, sender of - the original message. - forward_from_chat (:class:`telegram.Chat`, optional): For messages forwarded from channels - or from anonymous administrators, information about the original sender chat. - forward_from_message_id (:obj:`int`, optional): For forwarded channel posts, identifier of - the original message in the channel. - forward_sender_name (:obj:`str`, optional): Sender's name for messages forwarded from users - who disallow adding a link to their account in forwarded messages. - forward_date (:class:`datetime.datetime`, optional): For forwarded messages, date the - original message was sent in Unix time. Converted to :class:`datetime.datetime`. - is_automatic_forward (:obj:`bool`, optional): :obj:`True`, if the message is a channel post - that was automatically forwarded to the connected discussion group. - - .. versionadded:: 13.9 - reply_to_message (:class:`telegram.Message`, optional): For replies, the original message. - edit_date (:class:`datetime.datetime`, optional): Date the message was last edited in Unix - time. Converted to :class:`datetime.datetime`. - has_protected_content (:obj:`bool`, optional): :obj:`True`, if the message can't be - forwarded. - - .. versionadded:: 13.9 - media_group_id (:obj:`str`, optional): The unique identifier of a media message group this - message belongs to. - text (str, optional): For text messages, the actual UTF-8 text of the message, 0-4096 - characters. Also found as :attr:`telegram.constants.MAX_MESSAGE_LENGTH`. - entities (List[:class:`telegram.MessageEntity`], optional): For text messages, special - entities like usernames, URLs, bot commands, etc. that appear in the text. See - :attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. For Messages with a - Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the - caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` - methods for how to use properly. - audio (:class:`telegram.Audio`, optional): Message is an audio file, information - about the file. - document (:class:`telegram.Document`, optional): Message is a general file, information - about the file. - animation (:class:`telegram.Animation`, optional): Message is an animation, information - about the animation. For backward compatibility, when this field is set, the document - field will also be set. - game (:class:`telegram.Game`, optional): Message is a game, information about the game. - photo (List[:class:`telegram.PhotoSize`], optional): Message is a photo, available - sizes of the photo. - sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information - about the sticker. - video (:class:`telegram.Video`, optional): Message is a video, information about the video. - voice (:class:`telegram.Voice`, optional): Message is a voice message, information about - the file. - video_note (:class:`telegram.VideoNote`, optional): Message is a video note, information - about the video message. - new_chat_members (List[:class:`telegram.User`], optional): New members that were added to - the group or supergroup and information about them (the bot itself may be one of these - members). - caption (:obj:`str`, optional): Caption for the animation, audio, document, photo, video - or voice, 0-1024 characters. - contact (:class:`telegram.Contact`, optional): Message is a shared contact, information - about the contact. - location (:class:`telegram.Location`, optional): Message is a shared location, information - about the location. - venue (:class:`telegram.Venue`, optional): Message is a venue, information about the venue. - For backward compatibility, when this field is set, the location field will also be - set. - left_chat_member (:class:`telegram.User`, optional): A member was removed from the group, - information about them (this member may be the bot itself). - new_chat_title (:obj:`str`, optional): A chat title was changed to this value. - new_chat_photo (List[:class:`telegram.PhotoSize`], optional): A chat photo was changed to - this value. - delete_chat_photo (:obj:`bool`, optional): Service message: The chat photo was deleted. - group_chat_created (:obj:`bool`, optional): Service message: The group has been created. - supergroup_chat_created (:obj:`bool`, optional): Service message: The supergroup has been - created. This field can't be received in a message coming through updates, because bot - can't be a member of a supergroup when it is created. It can only be found in - :attr:`reply_to_message` if someone replies to a very first message in a directly - created supergroup. - channel_chat_created (:obj:`bool`, optional): Service message: The channel has been - created. This field can't be received in a message coming through updates, because bot - can't be a member of a channel when it is created. It can only be found in - :attr:`reply_to_message` if someone replies to a very first message in a channel. - message_auto_delete_timer_changed (:class:`telegram.MessageAutoDeleteTimerChanged`, \ - optional): Service message: auto-delete timer settings changed in the chat. - - .. versionadded:: 13.4 - migrate_to_chat_id (:obj:`int`, optional): The group has been migrated to a supergroup with - the specified identifier. This number may be greater than 32 bits and some programming - languages may have difficulty/silent defects in interpreting it. But it is smaller than - 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing - this identifier. - migrate_from_chat_id (:obj:`int`, optional): The supergroup has been migrated from a group - with the specified identifier. This number may be greater than 32 bits and some - programming languages may have difficulty/silent defects in interpreting it. But it is - smaller than 52 bits, so a signed 64 bit integer or double-precision float type are - safe for storing this identifier. - pinned_message (:class:`telegram.Message`, optional): Specified message was pinned. Note - that the Message object in this field will not contain further :attr:`reply_to_message` - fields even if it is itself a reply. - invoice (:class:`telegram.Invoice`, optional): Message is an invoice for a payment, - information about the invoice. - successful_payment (:class:`telegram.SuccessfulPayment`, optional): Message is a service - message about a successful payment, information about the payment. - connected_website (:obj:`str`, optional): The domain name of the website on which the user - has logged in. - forward_signature (:obj:`str`, optional): For messages forwarded from channels, signature - of the post author if present. - author_signature (:obj:`str`, optional): Signature of the post author for messages in - channels, or the custom title of an anonymous group administrator. - passport_data (:class:`telegram.PassportData`, optional): Telegram Passport data. - poll (:class:`telegram.Poll`, optional): Message is a native poll, - information about the poll. - dice (:class:`telegram.Dice`, optional): Message is a dice with random value from 1 to 6. - via_bot (:class:`telegram.User`, optional): Message was sent through an inline bot. - proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`, optional): Service - message. A user in the chat triggered another user's proximity alert while sharing - Live Location. - voice_chat_scheduled (:class:`telegram.VoiceChatScheduled`, optional): Service message: - voice chat scheduled. - - .. versionadded:: 13.5 - voice_chat_started (:class:`telegram.VoiceChatStarted`, optional): Service message: voice - chat started. - - .. versionadded:: 13.4 - voice_chat_ended (:class:`telegram.VoiceChatEnded`, optional): Service message: voice chat - ended. - - .. versionadded:: 13.4 - voice_chat_participants_invited (:class:`telegram.VoiceChatParticipantsInvited` optional): - Service message: new participants invited to a voice chat. - - .. versionadded:: 13.4 - reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached - to the message. ``login_url`` buttons are represented as ordinary url buttons. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - - Attributes: - message_id (:obj:`int`): Unique message identifier inside this chat. - from_user (:class:`telegram.User`): Optional. Sender of the message; empty for messages - sent to channels. For backward compatibility, this will contain a fake sender user in - non-channel chats, if the message was sent on behalf of a chat. - sender_chat (:class:`telegram.Chat`): Optional. Sender of the message, sent on behalf of a - chat. For backward compatibility, :attr:`from_user` contains a fake sender user in - non-channel chats, if the message was sent on behalf of a chat. - date (:class:`datetime.datetime`): Date the message was sent. - chat (:class:`telegram.Chat`): Conversation the message belongs to. - forward_from (:class:`telegram.User`): Optional. Sender of the original message. - forward_from_chat (:class:`telegram.Chat`): Optional. For messages forwarded from channels - or from anonymous administrators, information about the original sender chat. - forward_from_message_id (:obj:`int`): Optional. Identifier of the original message in the - channel. - forward_date (:class:`datetime.datetime`): Optional. Date the original message was sent. - is_automatic_forward (:obj:`bool`): Optional. :obj:`True`, if the message is a channel post - that was automatically forwarded to the connected discussion group. - - .. versionadded:: 13.9 - reply_to_message (:class:`telegram.Message`): Optional. For replies, the original message. - Note that the Message object in this field will not contain further - ``reply_to_message`` fields even if it itself is a reply. - edit_date (:class:`datetime.datetime`): Optional. Date the message was last edited. - has_protected_content (:obj:`bool`): Optional. :obj:`True`, if the message can't be - forwarded. - - .. versionadded:: 13.9 - media_group_id (:obj:`str`): Optional. The unique identifier of a media message group this - message belongs to. - text (:obj:`str`): Optional. The actual UTF-8 text of the message. - entities (List[:class:`telegram.MessageEntity`]): Optional. Special entities like - usernames, URLs, bot commands, etc. that appear in the text. See - :attr:`Message.parse_entity` and :attr:`parse_entities` methods for how to use - properly. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. Special entities like - usernames, URLs, bot commands, etc. that appear in the caption. See - :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` methods for how - to use properly. - audio (:class:`telegram.Audio`): Optional. Information about the file. - document (:class:`telegram.Document`): Optional. Information about the file. - animation (:class:`telegram.Animation`) Optional. Information about the file. - For backward compatibility, when this field is set, the document field will also be - set. - game (:class:`telegram.Game`): Optional. Information about the game. - photo (List[:class:`telegram.PhotoSize`]): Optional. Available sizes of the photo. - sticker (:class:`telegram.Sticker`): Optional. Information about the sticker. - video (:class:`telegram.Video`): Optional. Information about the video. - voice (:class:`telegram.Voice`): Optional. Information about the file. - video_note (:class:`telegram.VideoNote`): Optional. Information about the video message. - new_chat_members (List[:class:`telegram.User`]): Optional. Information about new members to - the chat. (the bot itself may be one of these members). - caption (:obj:`str`): Optional. Caption for the document, photo or video, 0-1024 - characters. - contact (:class:`telegram.Contact`): Optional. Information about the contact. - location (:class:`telegram.Location`): Optional. Information about the location. - venue (:class:`telegram.Venue`): Optional. Information about the venue. - left_chat_member (:class:`telegram.User`): Optional. Information about the user that left - the group. (this member may be the bot itself). - new_chat_title (:obj:`str`): Optional. A chat title was changed to this value. - new_chat_photo (List[:class:`telegram.PhotoSize`]): Optional. A chat photo was changed to - this value. - delete_chat_photo (:obj:`bool`): Optional. The chat photo was deleted. - group_chat_created (:obj:`bool`): Optional. The group has been created. - supergroup_chat_created (:obj:`bool`): Optional. The supergroup has been created. - channel_chat_created (:obj:`bool`): Optional. The channel has been created. - message_auto_delete_timer_changed (:class:`telegram.MessageAutoDeleteTimerChanged`): - Optional. Service message: auto-delete timer settings changed in the chat. - - .. versionadded:: 13.4 - migrate_to_chat_id (:obj:`int`): Optional. The group has been migrated to a supergroup with - the specified identifier. - migrate_from_chat_id (:obj:`int`): Optional. The supergroup has been migrated from a group - with the specified identifier. - pinned_message (:class:`telegram.message`): Optional. Specified message was pinned. - invoice (:class:`telegram.Invoice`): Optional. Information about the invoice. - successful_payment (:class:`telegram.SuccessfulPayment`): Optional. Information about the - payment. - connected_website (:obj:`str`): Optional. The domain name of the website on which the user - has logged in. - forward_signature (:obj:`str`): Optional. Signature of the post author for messages - forwarded from channels. - forward_sender_name (:obj:`str`): Optional. Sender's name for messages forwarded from users - who disallow adding a link to their account in forwarded messages. - author_signature (:obj:`str`): Optional. Signature of the post author for messages in - channels, or the custom title of an anonymous group administrator. - passport_data (:class:`telegram.PassportData`): Optional. Telegram Passport data. - poll (:class:`telegram.Poll`): Optional. Message is a native poll, - information about the poll. - dice (:class:`telegram.Dice`): Optional. Message is a dice. - via_bot (:class:`telegram.User`): Optional. Bot through which the message was sent. - proximity_alert_triggered (:class:`telegram.ProximityAlertTriggered`): Optional. Service - message. A user in the chat triggered another user's proximity alert while sharing - Live Location. - voice_chat_scheduled (:class:`telegram.VoiceChatScheduled`): Optional. Service message: - voice chat scheduled. - - .. versionadded:: 13.5 - voice_chat_started (:class:`telegram.VoiceChatStarted`): Optional. Service message: voice - chat started. - - .. versionadded:: 13.4 - voice_chat_ended (:class:`telegram.VoiceChatEnded`): Optional. Service message: voice chat - ended. - - .. versionadded:: 13.4 - voice_chat_participants_invited (:class:`telegram.VoiceChatParticipantsInvited`): Optional. - Service message: new participants invited to a voice chat. - - .. versionadded:: 13.4 - reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached - to the message. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - # fmt: on - __slots__ = ( - 'reply_markup', - 'audio', - 'contact', - 'migrate_to_chat_id', - 'forward_signature', - 'chat', - 'successful_payment', - 'game', - 'text', - 'forward_sender_name', - 'document', - 'new_chat_title', - 'forward_date', - 'group_chat_created', - 'media_group_id', - 'caption', - 'video', - 'bot', - 'entities', - 'via_bot', - 'new_chat_members', - 'connected_website', - 'animation', - 'migrate_from_chat_id', - 'forward_from', - 'sticker', - 'location', - 'venue', - 'edit_date', - 'reply_to_message', - 'passport_data', - 'pinned_message', - 'forward_from_chat', - 'new_chat_photo', - 'message_id', - 'delete_chat_photo', - 'from_user', - 'author_signature', - 'proximity_alert_triggered', - 'sender_chat', - 'dice', - 'forward_from_message_id', - 'caption_entities', - 'voice', - 'date', - 'supergroup_chat_created', - 'poll', - 'left_chat_member', - 'photo', - 'channel_chat_created', - 'invoice', - 'video_note', - '_effective_attachment', - 'message_auto_delete_timer_changed', - 'voice_chat_ended', - 'voice_chat_participants_invited', - 'voice_chat_started', - 'voice_chat_scheduled', - 'is_automatic_forward', - 'has_protected_content', - '_id_attrs', - ) - - ATTACHMENT_TYPES: ClassVar[List[str]] = [ - 'audio', - 'game', - 'animation', - 'document', - 'photo', - 'sticker', - 'video', - 'voice', - 'video_note', - 'contact', - 'location', - 'venue', - 'invoice', - 'successful_payment', - ] - MESSAGE_TYPES: ClassVar[List[str]] = [ - 'text', - 'new_chat_members', - 'left_chat_member', - 'new_chat_title', - 'new_chat_photo', - 'delete_chat_photo', - 'group_chat_created', - 'supergroup_chat_created', - 'channel_chat_created', - 'message_auto_delete_timer_changed', - 'migrate_to_chat_id', - 'migrate_from_chat_id', - 'pinned_message', - 'poll', - 'dice', - 'passport_data', - 'proximity_alert_triggered', - 'voice_chat_scheduled', - 'voice_chat_started', - 'voice_chat_ended', - 'voice_chat_participants_invited', - ] + ATTACHMENT_TYPES - - def __init__( - self, - message_id: int, - date: datetime.datetime, - chat: Chat, - from_user: User = None, - forward_from: User = None, - forward_from_chat: Chat = None, - forward_from_message_id: int = None, - forward_date: datetime.datetime = None, - reply_to_message: 'Message' = None, - edit_date: datetime.datetime = None, - text: str = None, - entities: List['MessageEntity'] = None, - caption_entities: List['MessageEntity'] = None, - audio: Audio = None, - document: Document = None, - game: Game = None, - photo: List[PhotoSize] = None, - sticker: Sticker = None, - video: Video = None, - voice: Voice = None, - video_note: VideoNote = None, - new_chat_members: List[User] = None, - caption: str = None, - contact: Contact = None, - location: Location = None, - venue: Venue = None, - left_chat_member: User = None, - new_chat_title: str = None, - new_chat_photo: List[PhotoSize] = None, - delete_chat_photo: bool = False, - group_chat_created: bool = False, - supergroup_chat_created: bool = False, - channel_chat_created: bool = False, - migrate_to_chat_id: int = None, - migrate_from_chat_id: int = None, - pinned_message: 'Message' = None, - invoice: Invoice = None, - successful_payment: SuccessfulPayment = None, - forward_signature: str = None, - author_signature: str = None, - media_group_id: str = None, - connected_website: str = None, - animation: Animation = None, - passport_data: PassportData = None, - poll: Poll = None, - forward_sender_name: str = None, - reply_markup: InlineKeyboardMarkup = None, - bot: 'Bot' = None, - dice: Dice = None, - via_bot: User = None, - proximity_alert_triggered: ProximityAlertTriggered = None, - sender_chat: Chat = None, - voice_chat_started: VoiceChatStarted = None, - voice_chat_ended: VoiceChatEnded = None, - voice_chat_participants_invited: VoiceChatParticipantsInvited = None, - message_auto_delete_timer_changed: MessageAutoDeleteTimerChanged = None, - voice_chat_scheduled: VoiceChatScheduled = None, - is_automatic_forward: bool = None, - has_protected_content: bool = None, - **_kwargs: Any, - ): - # Required - self.message_id = int(message_id) - # Optionals - self.from_user = from_user - self.sender_chat = sender_chat - self.date = date - self.chat = chat - self.forward_from = forward_from - self.forward_from_chat = forward_from_chat - self.forward_date = forward_date - self.is_automatic_forward = is_automatic_forward - self.reply_to_message = reply_to_message - self.edit_date = edit_date - self.has_protected_content = has_protected_content - self.text = text - self.entities = entities or [] - self.caption_entities = caption_entities or [] - self.audio = audio - self.game = game - self.document = document - self.photo = photo or [] - self.sticker = sticker - self.video = video - self.voice = voice - self.video_note = video_note - self.caption = caption - self.contact = contact - self.location = location - self.venue = venue - self.new_chat_members = new_chat_members or [] - self.left_chat_member = left_chat_member - self.new_chat_title = new_chat_title - self.new_chat_photo = new_chat_photo or [] - self.delete_chat_photo = bool(delete_chat_photo) - self.group_chat_created = bool(group_chat_created) - self.supergroup_chat_created = bool(supergroup_chat_created) - self.migrate_to_chat_id = migrate_to_chat_id - self.migrate_from_chat_id = migrate_from_chat_id - self.channel_chat_created = bool(channel_chat_created) - self.message_auto_delete_timer_changed = message_auto_delete_timer_changed - self.pinned_message = pinned_message - self.forward_from_message_id = forward_from_message_id - self.invoice = invoice - self.successful_payment = successful_payment - self.connected_website = connected_website - self.forward_signature = forward_signature - self.forward_sender_name = forward_sender_name - self.author_signature = author_signature - self.media_group_id = media_group_id - self.animation = animation - self.passport_data = passport_data - self.poll = poll - self.dice = dice - self.via_bot = via_bot - self.proximity_alert_triggered = proximity_alert_triggered - self.voice_chat_scheduled = voice_chat_scheduled - self.voice_chat_started = voice_chat_started - self.voice_chat_ended = voice_chat_ended - self.voice_chat_participants_invited = voice_chat_participants_invited - self.reply_markup = reply_markup - self.bot = bot - - self._effective_attachment = DEFAULT_NONE - - self._id_attrs = (self.message_id, self.chat) - - @property - def chat_id(self) -> int: - """:obj:`int`: Shortcut for :attr:`telegram.Chat.id` for :attr:`chat`.""" - return self.chat.id - - @property - def link(self) -> Optional[str]: - """:obj:`str`: Convenience property. If the chat of the message is not - a private chat or normal group, returns a t.me link of the message. - """ - if self.chat.type not in [Chat.PRIVATE, Chat.GROUP]: - if self.chat.username: - to_link = self.chat.username - else: - # Get rid of leading -100 for supergroups - to_link = f"c/{str(self.chat.id)[4:]}" - return f"https://t.me/{to_link}/{self.message_id}" - return None - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Message']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['from_user'] = User.de_json(data.get('from'), bot) - data['sender_chat'] = Chat.de_json(data.get('sender_chat'), bot) - data['date'] = from_timestamp(data['date']) - data['chat'] = Chat.de_json(data.get('chat'), bot) - data['entities'] = MessageEntity.de_list(data.get('entities'), bot) - data['caption_entities'] = MessageEntity.de_list(data.get('caption_entities'), bot) - data['forward_from'] = User.de_json(data.get('forward_from'), bot) - data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'), bot) - data['forward_date'] = from_timestamp(data.get('forward_date')) - data['reply_to_message'] = Message.de_json(data.get('reply_to_message'), bot) - data['edit_date'] = from_timestamp(data.get('edit_date')) - data['audio'] = Audio.de_json(data.get('audio'), bot) - data['document'] = Document.de_json(data.get('document'), bot) - data['animation'] = Animation.de_json(data.get('animation'), bot) - data['game'] = Game.de_json(data.get('game'), bot) - data['photo'] = PhotoSize.de_list(data.get('photo'), bot) - data['sticker'] = Sticker.de_json(data.get('sticker'), bot) - data['video'] = Video.de_json(data.get('video'), bot) - data['voice'] = Voice.de_json(data.get('voice'), bot) - data['video_note'] = VideoNote.de_json(data.get('video_note'), bot) - data['contact'] = Contact.de_json(data.get('contact'), bot) - data['location'] = Location.de_json(data.get('location'), bot) - data['venue'] = Venue.de_json(data.get('venue'), bot) - data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot) - data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot) - data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot) - data['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json( - data.get('message_auto_delete_timer_changed'), bot - ) - data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) - data['invoice'] = Invoice.de_json(data.get('invoice'), bot) - data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot) - data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot) - data['poll'] = Poll.de_json(data.get('poll'), bot) - data['dice'] = Dice.de_json(data.get('dice'), bot) - data['via_bot'] = User.de_json(data.get('via_bot'), bot) - data['proximity_alert_triggered'] = ProximityAlertTriggered.de_json( - data.get('proximity_alert_triggered'), bot - ) - data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot) - data['voice_chat_scheduled'] = VoiceChatScheduled.de_json( - data.get('voice_chat_scheduled'), bot - ) - data['voice_chat_started'] = VoiceChatStarted.de_json(data.get('voice_chat_started'), bot) - data['voice_chat_ended'] = VoiceChatEnded.de_json(data.get('voice_chat_ended'), bot) - data['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json( - data.get('voice_chat_participants_invited'), bot - ) - return cls(bot=bot, **data) - - @property - def effective_attachment( - self, - ) -> Union[ - Contact, - Document, - Animation, - Game, - Invoice, - Location, - List[PhotoSize], - Sticker, - SuccessfulPayment, - Venue, - Video, - VideoNote, - Voice, - None, - ]: - """ - :class:`telegram.Audio` - or :class:`telegram.Contact` - or :class:`telegram.Document` - or :class:`telegram.Animation` - or :class:`telegram.Game` - or :class:`telegram.Invoice` - or :class:`telegram.Location` - or List[:class:`telegram.PhotoSize`] - or :class:`telegram.Sticker` - or :class:`telegram.SuccessfulPayment` - or :class:`telegram.Venue` - or :class:`telegram.Video` - or :class:`telegram.VideoNote` - or :class:`telegram.Voice`: The attachment that this message was sent with. May be - :obj:`None` if no attachment was sent. - - """ - if self._effective_attachment is not DEFAULT_NONE: - return self._effective_attachment # type: ignore - - for i in Message.ATTACHMENT_TYPES: - if getattr(self, i, None): - self._effective_attachment = getattr(self, i) - break - else: - self._effective_attachment = None - - return self._effective_attachment # type: ignore - - def __getitem__(self, item: str) -> Any: # pylint: disable=R1710 - return self.chat.id if item == 'chat_id' else super().__getitem__(item) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - # Required - data['date'] = to_timestamp(self.date) - # Optionals - if self.forward_date: - data['forward_date'] = to_timestamp(self.forward_date) - if self.edit_date: - data['edit_date'] = to_timestamp(self.edit_date) - if self.photo: - data['photo'] = [p.to_dict() for p in self.photo] - if self.entities: - data['entities'] = [e.to_dict() for e in self.entities] - if self.caption_entities: - data['caption_entities'] = [e.to_dict() for e in self.caption_entities] - if self.new_chat_photo: - data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo] - if self.new_chat_members: - data['new_chat_members'] = [u.to_dict() for u in self.new_chat_members] - - return data - - def _quote(self, quote: Optional[bool], reply_to_message_id: Optional[int]) -> Optional[int]: - """Modify kwargs for replying with or without quoting.""" - if reply_to_message_id is not None: - return reply_to_message_id - - if quote is not None: - if quote: - return self.message_id - - else: - if self.bot.defaults: - default_quote = self.bot.defaults.quote - else: - default_quote = None - if (default_quote is None and self.chat.type != Chat.PRIVATE) or default_quote: - return self.message_id - - return None - - def reply_text( - self, - text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_message(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_message( - chat_id=self.chat_id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - entities=entities, - protect_content=protect_content, - ) - - def reply_markdown( - self, - text: str, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_message( - update.effective_message.chat_id, - parse_mode=ParseMode.MARKDOWN, - *args, - **kwargs, - ) - - Sends a message with Markdown version 1 formatting. - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. - - Note: - :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for - backward compatibility. You should use :meth:`reply_markdown_v2` instead. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_message( - chat_id=self.chat_id, - text=text, - parse_mode=ParseMode.MARKDOWN, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - entities=entities, - protect_content=protect_content, - ) - - def reply_markdown_v2( - self, - text: str, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_message( - update.effective_message.chat_id, - parse_mode=ParseMode.MARKDOWN_V2, - *args, - **kwargs, - ) - - Sends a message with markdown version 2 formatting. - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_message( - chat_id=self.chat_id, - text=text, - parse_mode=ParseMode.MARKDOWN_V2, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - entities=entities, - protect_content=protect_content, - ) - - def reply_html( - self, - text: str, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_message( - update.effective_message.chat_id, - parse_mode=ParseMode.HTML, - *args, - **kwargs, - ) - - Sends a message with HTML formatting. - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the message is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_message( - chat_id=self.chat_id, - text=text, - parse_mode=ParseMode.HTML, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - entities=entities, - protect_content=protect_content, - ) - - def reply_media_group( - self, - media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] - ], - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> List['Message']: - """Shortcut for:: - - bot.send_media_group(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the media group is sent as an - actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - List[:class:`telegram.Message`]: An array of the sent Messages. - - Raises: - :class:`telegram.error.TelegramError` - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_media_group( - chat_id=self.chat_id, - media=media, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_photo( - self, - photo: Union[FileInput, 'PhotoSize'], - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_photo(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the photo is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_photo( - chat_id=self.chat_id, - photo=photo, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def reply_audio( - self, - audio: Union[FileInput, 'Audio'], - duration: int = None, - performer: str = None, - title: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_audio(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the audio is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_audio( - chat_id=self.chat_id, - audio=audio, - duration=duration, - performer=performer, - title=title, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def reply_document( - self, - document: Union[FileInput, 'Document'], - filename: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - disable_content_type_detection: bool = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_document(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the document is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_document( - chat_id=self.chat_id, - document=document, - filename=filename, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - thumb=thumb, - api_kwargs=api_kwargs, - disable_content_type_detection=disable_content_type_detection, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - protect_content=protect_content, - ) - - def reply_animation( - self, - animation: Union[FileInput, 'Animation'], - duration: int = None, - width: int = None, - height: int = None, - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_animation(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the animation is sent as an - actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_animation( - chat_id=self.chat_id, - animation=animation, - duration=duration, - width=width, - height=height, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def reply_sticker( - self, - sticker: Union[FileInput, 'Sticker'], - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_sticker(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the sticker is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_sticker( - chat_id=self.chat_id, - sticker=sticker, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_video( - self, - video: Union[FileInput, 'Video'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - width: int = None, - height: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - supports_streaming: bool = None, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_video(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the video is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_video( - chat_id=self.chat_id, - video=video, - duration=duration, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - width=width, - height=height, - parse_mode=parse_mode, - supports_streaming=supports_streaming, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def reply_video_note( - self, - video_note: Union[FileInput, 'VideoNote'], - duration: int = None, - length: int = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - filename: str = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_video_note(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the video note is sent as an - actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_video_note( - chat_id=self.chat_id, - video_note=video_note, - duration=duration, - length=length, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - filename=filename, - protect_content=protect_content, - ) - - def reply_voice( - self, - voice: Union[FileInput, 'Voice'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_voice(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the voice note is sent as an - actual reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_voice( - chat_id=self.chat_id, - voice=voice, - duration=duration, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def reply_location( - self, - latitude: float = None, - longitude: float = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - location: Location = None, - live_period: int = None, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_location(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the location is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_location( - chat_id=self.chat_id, - latitude=latitude, - longitude=longitude, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - location=location, - live_period=live_period, - api_kwargs=api_kwargs, - horizontal_accuracy=horizontal_accuracy, - heading=heading, - proximity_alert_radius=proximity_alert_radius, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_venue( - self, - latitude: float = None, - longitude: float = None, - title: str = None, - address: str = None, - foursquare_id: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - venue: Venue = None, - foursquare_type: str = None, - api_kwargs: JSONDict = None, - google_place_id: str = None, - google_place_type: str = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_venue(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the venue is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_venue( - chat_id=self.chat_id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - venue=venue, - foursquare_type=foursquare_type, - api_kwargs=api_kwargs, - google_place_id=google_place_id, - google_place_type=google_place_type, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_contact( - self, - phone_number: str = None, - first_name: str = None, - last_name: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - contact: Contact = None, - vcard: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_contact(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the contact is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` in - private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_contact( - chat_id=self.chat_id, - phone_number=phone_number, - first_name=first_name, - last_name=last_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - contact=contact, - vcard=vcard, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_poll( - self, - question: str, - options: List[str], - is_anonymous: bool = True, - type: str = Poll.REGULAR, # pylint: disable=W0622 - allows_multiple_answers: bool = False, - correct_option_id: int = None, - is_closed: bool = None, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - explanation: str = None, - explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, - open_period: int = None, - close_date: Union[int, datetime.datetime] = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_poll(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the poll is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_poll( - chat_id=self.chat_id, - question=question, - options=options, - is_anonymous=is_anonymous, - type=type, - allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, - is_closed=is_closed, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - explanation=explanation, - explanation_parse_mode=explanation_parse_mode, - open_period=open_period, - close_date=close_date, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - explanation_entities=explanation_entities, - protect_content=protect_content, - ) - - def reply_dice( - self, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - emoji: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_dice(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the dice is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` - in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_dice( - chat_id=self.chat_id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - emoji=emoji, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_chat_action( - self, - action: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.send_chat_action(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`. - - .. versionadded:: 13.2 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.send_chat_action( - chat_id=self.chat_id, - action=action, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def reply_game( - self, - game_short_name: str, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_game(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the game is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` - in private chats. - - .. versionadded:: 13.2 - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_game( - chat_id=self.chat_id, - game_short_name=game_short_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def reply_invoice( - self, - title: str, - description: str, - payload: str, - provider_token: str, - currency: str, - prices: List['LabeledPrice'], - start_parameter: str = None, - photo_url: str = None, - photo_size: int = None, - photo_width: int = None, - photo_height: int = None, - need_name: bool = None, - need_phone_number: bool = None, - need_email: bool = None, - need_shipping_address: bool = None, - is_flexible: bool = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, - provider_data: Union[str, object] = None, - send_phone_number_to_provider: bool = None, - send_email_to_provider: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - quote: bool = None, - max_tip_amount: int = None, - suggested_tip_amounts: List[int] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_invoice(update.effective_message.chat_id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_invoice`. - - Warning: - As of API 5.2 :attr:`start_parameter` is an optional argument and therefore the order - of the arguments had to be changed. Use keyword arguments to make sure that the - arguments are passed correctly. - - .. versionadded:: 13.2 - - .. versionchanged:: 13.5 - As of Bot API 5.2, the parameter :attr:`start_parameter` is optional. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the invoice is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, this - parameter will be ignored. Default: :obj:`True` in group chats and :obj:`False` - in private chats. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.send_invoice( - chat_id=self.chat_id, - title=title, - description=description, - payload=payload, - provider_token=provider_token, - currency=currency, - prices=prices, - start_parameter=start_parameter, - photo_url=photo_url, - photo_size=photo_size, - photo_width=photo_width, - photo_height=photo_height, - need_name=need_name, - need_phone_number=need_phone_number, - need_email=need_email, - need_shipping_address=need_shipping_address, - is_flexible=is_flexible, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - provider_data=provider_data, - send_phone_number_to_provider=send_phone_number_to_provider, - send_email_to_provider=send_email_to_provider, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - max_tip_amount=max_tip_amount, - suggested_tip_amounts=suggested_tip_amounts, - protect_content=protect_content, - ) - - def forward( - self, - chat_id: Union[int, str], - disable_notification: DVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.forward_message(chat_id=chat_id, - from_chat_id=update.effective_message.chat_id, - message_id=update.effective_message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.forward_message`. - - Note: - Since the release of Bot API 5.5 it can be impossible to forward messages from - some chats. Use the attributes :attr:`telegram.Message.has_protected_content` and - :attr:`telegram.Chat.has_protected_content` to check this. - - As a workaround, it is still possible to use :meth:`copy`. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, instance representing the message forwarded. - - """ - return self.bot.forward_message( - chat_id=chat_id, - from_chat_id=self.chat_id, - message_id=self.message_id, - disable_notification=disable_notification, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def copy( - self, - chat_id: Union[int, str], - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - bot.copy_message(chat_id=chat_id, - from_chat_id=update.effective_message.chat_id, - message_id=update.effective_message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. - - Returns: - :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. - - """ - return self.bot.copy_message( - chat_id=chat_id, - from_chat_id=self.chat_id, - message_id=self.message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def reply_copy( - self, - from_chat_id: Union[str, int], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: ReplyMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - quote: bool = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - bot.copy_message(chat_id=message.chat.id, - from_chat_id=from_chat_id, - message_id=message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. - - Args: - quote (:obj:`bool`, optional): If set to :obj:`True`, the copy is sent as an actual - reply to this message. If ``reply_to_message_id`` is passed in ``kwargs``, - this parameter will be ignored. Default: :obj:`True` in group chats and - :obj:`False` in private chats. - - .. versionadded:: 13.1 - - Returns: - :class:`telegram.MessageId`: On success, returns the MessageId of the sent message. - - """ - reply_to_message_id = self._quote(quote, reply_to_message_id) - return self.bot.copy_message( - chat_id=self.chat_id, - from_chat_id=from_chat_id, - message_id=message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def edit_text( - self, - text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.edit_message_text(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.edit_message_text`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise ``True`` is returned. - - """ - return self.bot.edit_message_text( - chat_id=self.chat_id, - message_id=self.message_id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - entities=entities, - inline_message_id=None, - ) - - def edit_caption( - self, - caption: str = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.edit_message_caption(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_caption`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise ``True`` is returned. - - """ - return self.bot.edit_message_caption( - chat_id=self.chat_id, - message_id=self.message_id, - caption=caption, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - caption_entities=caption_entities, - inline_message_id=None, - ) - - def edit_media( - self, - media: 'InputMedia' = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.edit_message_media(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_media`. - - Note: - You can only edit messages that the bot sent itself(i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise ``True`` is returned. - - """ - return self.bot.edit_message_media( - chat_id=self.chat_id, - message_id=self.message_id, - media=media, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - inline_message_id=None, - ) - - def edit_reply_markup( - self, - reply_markup: Optional['InlineKeyboardMarkup'] = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.edit_message_reply_markup(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_reply_markup`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise ``True`` is returned. - """ - return self.bot.edit_message_reply_markup( - chat_id=self.chat_id, - message_id=self.message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - inline_message_id=None, - ) - - def edit_live_location( - self, - latitude: float = None, - longitude: float = None, - location: Location = None, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.edit_message_live_location(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.edit_message_live_location`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - """ - return self.bot.edit_message_live_location( - chat_id=self.chat_id, - message_id=self.message_id, - latitude=latitude, - longitude=longitude, - location=location, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - horizontal_accuracy=horizontal_accuracy, - heading=heading, - proximity_alert_radius=proximity_alert_radius, - inline_message_id=None, - ) - - def stop_live_location( - self, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.stop_message_live_location(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.stop_message_live_location`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - """ - return self.bot.stop_message_live_location( - chat_id=self.chat_id, - message_id=self.message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - inline_message_id=None, - ) - - def set_game_score( - self, - user_id: Union[int, str], - score: int, - force: bool = None, - disable_edit_message: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: - """Shortcut for:: - - bot.set_game_score(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.set_game_score`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the - edited Message is returned, otherwise :obj:`True` is returned. - """ - return self.bot.set_game_score( - chat_id=self.chat_id, - message_id=self.message_id, - user_id=user_id, - score=score, - force=force, - disable_edit_message=disable_edit_message, - timeout=timeout, - api_kwargs=api_kwargs, - inline_message_id=None, - ) - - def get_game_high_scores( - self, - user_id: Union[int, str], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> List['GameHighScore']: - """Shortcut for:: - - bot.get_game_high_scores(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.get_game_high_scores`. - - Note: - You can only edit messages that the bot sent itself (i.e. of the ``bot.send_*`` family - of methods) or channel posts, if the bot is an admin in that channel. However, this - behaviour is undocumented and might be changed by Telegram. - - Returns: - List[:class:`telegram.GameHighScore`] - """ - return self.bot.get_game_high_scores( - chat_id=self.chat_id, - message_id=self.message_id, - user_id=user_id, - timeout=timeout, - api_kwargs=api_kwargs, - inline_message_id=None, - ) - - def delete( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.delete_message(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.delete_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.delete_message( - chat_id=self.chat_id, - message_id=self.message_id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def stop_poll( - self, - reply_markup: InlineKeyboardMarkup = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Poll: - """Shortcut for:: - - bot.stop_poll(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.stop_poll`. - - Returns: - :class:`telegram.Poll`: On success, the stopped Poll with the final results is - returned. - - """ - return self.bot.stop_poll( - chat_id=self.chat_id, - message_id=self.message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def pin( - self, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.pin_chat_message(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.pin_chat_message( - chat_id=self.chat_id, - message_id=self.message_id, - disable_notification=disable_notification, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def unpin( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.unpin_chat_message(chat_id=message.chat_id, - message_id=message.message_id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unpin_chat_message( - chat_id=self.chat_id, - message_id=self.message_id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def parse_entity(self, entity: MessageEntity) -> str: - """Returns the text from a given :class:`telegram.MessageEntity`. - - Note: - This method is present because Telegram calculates the offset and length in - UTF-16 codepoint pairs, which some versions of Python don't handle automatically. - (That is, you can't just slice ``Message.text`` with the offset and length.) - - Args: - entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must - be an entity that belongs to this message. - - Returns: - :obj:`str`: The text of the given entity. - - Raises: - RuntimeError: If the message has no text. - - """ - if not self.text: - raise RuntimeError("This Message has no 'text'.") - - # Is it a narrow build, if so we don't need to convert - if sys.maxunicode == 0xFFFF: - return self.text[entity.offset : entity.offset + entity.length] - - entity_text = self.text.encode('utf-16-le') - entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - return entity_text.decode('utf-16-le') - - def parse_caption_entity(self, entity: MessageEntity) -> str: - """Returns the text from a given :class:`telegram.MessageEntity`. - - Note: - This method is present because Telegram calculates the offset and length in - UTF-16 codepoint pairs, which some versions of Python don't handle automatically. - (That is, you can't just slice ``Message.caption`` with the offset and length.) - - Args: - entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must - be an entity that belongs to this message. - - Returns: - :obj:`str`: The text of the given entity. - - Raises: - RuntimeError: If the message has no caption. - - """ - if not self.caption: - raise RuntimeError("This Message has no 'caption'.") - - # Is it a narrow build, if so we don't need to convert - if sys.maxunicode == 0xFFFF: - return self.caption[entity.offset : entity.offset + entity.length] - - entity_text = self.caption.encode('utf-16-le') - entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - return entity_text.decode('utf-16-le') - - def parse_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: - """ - Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. - It contains entities from this message filtered by their - :attr:`telegram.MessageEntity.type` attribute as the key, and the text that each entity - belongs to as the value of the :obj:`dict`. - - Note: - This method should always be used instead of the :attr:`entities` attribute, since it - calculates the correct substring from the message text based on UTF-16 codepoints. - See :attr:`parse_entity` for more info. - - Args: - types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as - strings. If the ``type`` attribute of an entity is contained in this list, it will - be returned. Defaults to a list of all types. All types can be found as constants - in :class:`telegram.MessageEntity`. - - Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to - the text that belongs to them, calculated based on UTF-16 codepoints. - - """ - if types is None: - types = MessageEntity.ALL_TYPES - - return { - entity: self.parse_entity(entity) - for entity in (self.entities or []) - if entity.type in types - } - - def parse_caption_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: - """ - Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. - It contains entities from this message's caption filtered by their - :attr:`telegram.MessageEntity.type` attribute as the key, and the text that each entity - belongs to as the value of the :obj:`dict`. - - Note: - This method should always be used instead of the :attr:`caption_entities` attribute, - since it calculates the correct substring from the message text based on UTF-16 - codepoints. See :attr:`parse_entity` for more info. - - Args: - types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as - strings. If the ``type`` attribute of an entity is contained in this list, it will - be returned. Defaults to a list of all types. All types can be found as constants - in :class:`telegram.MessageEntity`. - - Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to - the text that belongs to them, calculated based on UTF-16 codepoints. - - """ - if types is None: - types = MessageEntity.ALL_TYPES - - return { - entity: self.parse_caption_entity(entity) - for entity in (self.caption_entities or []) - if entity.type in types - } - - @staticmethod - def _parse_html( - message_text: Optional[str], - entities: Dict[MessageEntity, str], - urled: bool = False, - offset: int = 0, - ) -> Optional[str]: - if message_text is None: - return None - - if sys.maxunicode != 0xFFFF: - message_text = message_text.encode('utf-16-le') # type: ignore - - html_text = '' - last_offset = 0 - - sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset)) - parsed_entities = [] - - for (entity, text) in sorted_entities: - if entity not in parsed_entities: - nested_entities = { - e: t - for (e, t) in sorted_entities - if e.offset >= entity.offset - and e.offset + e.length <= entity.offset + entity.length - and e != entity - } - parsed_entities.extend(list(nested_entities.keys())) - - orig_text = text - text = escape(text) - - if nested_entities: - text = Message._parse_html( - orig_text, nested_entities, urled=urled, offset=entity.offset - ) - - if entity.type == MessageEntity.TEXT_LINK: - insert = f'<a href="{entity.url}">{text}</a>' - elif entity.type == MessageEntity.TEXT_MENTION and entity.user: - insert = f'<a href="tg://user?id={entity.user.id}">{text}</a>' - elif entity.type == MessageEntity.URL and urled: - insert = f'<a href="{text}">{text}</a>' - elif entity.type == MessageEntity.BOLD: - insert = '<b>' + text + '</b>' - elif entity.type == MessageEntity.ITALIC: - insert = '<i>' + text + '</i>' - elif entity.type == MessageEntity.CODE: - insert = '<code>' + text + '</code>' - elif entity.type == MessageEntity.PRE: - if entity.language: - insert = f'<pre><code class="{entity.language}">{text}</code></pre>' - else: - insert = '<pre>' + text + '</pre>' - elif entity.type == MessageEntity.UNDERLINE: - insert = '<u>' + text + '</u>' - elif entity.type == MessageEntity.STRIKETHROUGH: - insert = '<s>' + text + '</s>' - elif entity.type == MessageEntity.SPOILER: - insert = f'<span class="tg-spoiler">{text}</span>' - else: - insert = text - - if offset == 0: - if sys.maxunicode == 0xFFFF: - html_text += ( - escape(message_text[last_offset : entity.offset - offset]) + insert - ) - else: - html_text += ( - escape( - message_text[ # type: ignore - last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le') - ) - + insert - ) - else: - if sys.maxunicode == 0xFFFF: - html_text += message_text[last_offset : entity.offset - offset] + insert - else: - html_text += ( - message_text[ # type: ignore - last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le') - + insert - ) - - last_offset = entity.offset - offset + entity.length - - if offset == 0: - if sys.maxunicode == 0xFFFF: - html_text += escape(message_text[last_offset:]) - else: - html_text += escape( - message_text[last_offset * 2 :].decode('utf-16-le') # type: ignore - ) - else: - if sys.maxunicode == 0xFFFF: - html_text += message_text[last_offset:] - else: - html_text += message_text[last_offset * 2 :].decode('utf-16-le') # type: ignore - - return html_text - - @property - def text_html(self) -> str: - """Creates an HTML-formatted string from the markup entities found in the message. - - Use this if you want to retrieve the message text with the entities formatted as HTML in - the same way the original message was formatted. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as HTML. - - Returns: - :obj:`str`: Message text with entities formatted as HTML. - - """ - return self._parse_html(self.text, self.parse_entities(), urled=False) - - @property - def text_html_urled(self) -> str: - """Creates an HTML-formatted string from the markup entities found in the message. - - Use this if you want to retrieve the message text with the entities formatted as HTML. - This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as HTML. - - Returns: - :obj:`str`: Message text with entities formatted as HTML. - - """ - return self._parse_html(self.text, self.parse_entities(), urled=True) - - @property - def caption_html(self) -> str: - """Creates an HTML-formatted string from the markup entities found in the message's - caption. - - Use this if you want to retrieve the message caption with the caption entities formatted as - HTML in the same way the original message was formatted. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as HTML. - - Returns: - :obj:`str`: Message caption with caption entities formatted as HTML. - """ - return self._parse_html(self.caption, self.parse_caption_entities(), urled=False) - - @property - def caption_html_urled(self) -> str: - """Creates an HTML-formatted string from the markup entities found in the message's - caption. - - Use this if you want to retrieve the message caption with the caption entities formatted as - HTML. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as HTML. - - Returns: - :obj:`str`: Message caption with caption entities formatted as HTML. - """ - return self._parse_html(self.caption, self.parse_caption_entities(), urled=True) - - @staticmethod - def _parse_markdown( - message_text: Optional[str], - entities: Dict[MessageEntity, str], - urled: bool = False, - version: int = 1, - offset: int = 0, - ) -> Optional[str]: - version = int(version) - - if message_text is None: - return None - - if sys.maxunicode != 0xFFFF: - message_text = message_text.encode('utf-16-le') # type: ignore - - markdown_text = '' - last_offset = 0 - - sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset)) - parsed_entities = [] - - for (entity, text) in sorted_entities: - if entity not in parsed_entities: - nested_entities = { - e: t - for (e, t) in sorted_entities - if e.offset >= entity.offset - and e.offset + e.length <= entity.offset + entity.length - and e != entity - } - parsed_entities.extend(list(nested_entities.keys())) - - orig_text = text - text = escape_markdown(text, version=version) - - if nested_entities: - if version < 2: - raise ValueError( - 'Nested entities are not supported for Markdown ' 'version 1' - ) - - text = Message._parse_markdown( - orig_text, - nested_entities, - urled=urled, - offset=entity.offset, - version=version, - ) - - if entity.type == MessageEntity.TEXT_LINK: - if version == 1: - url = entity.url - else: - # Links need special escaping. Also can't have entities nested within - url = escape_markdown( - entity.url, version=version, entity_type=MessageEntity.TEXT_LINK - ) - insert = f'[{text}]({url})' - elif entity.type == MessageEntity.TEXT_MENTION and entity.user: - insert = f'[{text}](tg://user?id={entity.user.id})' - elif entity.type == MessageEntity.URL and urled: - if version == 1: - link = orig_text - else: - link = text - insert = f'[{link}]({orig_text})' - elif entity.type == MessageEntity.BOLD: - insert = '*' + text + '*' - elif entity.type == MessageEntity.ITALIC: - insert = '_' + text + '_' - elif entity.type == MessageEntity.CODE: - # Monospace needs special escaping. Also can't have entities nested within - insert = ( - '`' - + escape_markdown( - orig_text, version=version, entity_type=MessageEntity.CODE - ) - + '`' - ) - elif entity.type == MessageEntity.PRE: - # Monospace needs special escaping. Also can't have entities nested within - code = escape_markdown( - orig_text, version=version, entity_type=MessageEntity.PRE - ) - if entity.language: - prefix = '```' + entity.language + '\n' - else: - if code.startswith('\\'): - prefix = '```' - else: - prefix = '```\n' - insert = prefix + code + '```' - elif entity.type == MessageEntity.UNDERLINE: - if version == 1: - raise ValueError( - 'Underline entities are not supported for Markdown ' 'version 1' - ) - insert = '__' + text + '__' - elif entity.type == MessageEntity.STRIKETHROUGH: - if version == 1: - raise ValueError( - 'Strikethrough entities are not supported for Markdown ' 'version 1' - ) - insert = '~' + text + '~' - elif entity.type == MessageEntity.SPOILER: - if version == 1: - raise ValueError( - "Spoiler entities are not supported for Markdown version 1" - ) - insert = f"||{text}||" - else: - insert = text - - if offset == 0: - if sys.maxunicode == 0xFFFF: - markdown_text += ( - escape_markdown( - message_text[last_offset : entity.offset - offset], version=version - ) - + insert - ) - else: - markdown_text += ( - escape_markdown( - message_text[ # type: ignore - last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le'), - version=version, - ) - + insert - ) - else: - if sys.maxunicode == 0xFFFF: - markdown_text += ( - message_text[last_offset : entity.offset - offset] + insert - ) - else: - markdown_text += ( - message_text[ # type: ignore - last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le') - + insert - ) - - last_offset = entity.offset - offset + entity.length - - if offset == 0: - if sys.maxunicode == 0xFFFF: - markdown_text += escape_markdown(message_text[last_offset:], version=version) - else: - markdown_text += escape_markdown( - message_text[last_offset * 2 :].decode('utf-16-le'), # type: ignore - version=version, - ) - else: - if sys.maxunicode == 0xFFFF: - markdown_text += message_text[last_offset:] - else: - markdown_text += message_text[last_offset * 2 :].decode( # type: ignore - 'utf-16-le' - ) - - return markdown_text - - @property - def text_markdown(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message - using :class:`telegram.ParseMode.MARKDOWN`. - - Use this if you want to retrieve the message text with the entities formatted as Markdown - in the same way the original message was formatted. - - Note: - :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for - backward compatibility. You should use :meth:`text_markdown_v2` instead. - - Returns: - :obj:`str`: Message text with entities formatted as Markdown. - - Raises: - :exc:`ValueError`: If the message contains underline, strikethrough, spoiler or nested - entities. - - """ - return self._parse_markdown(self.text, self.parse_entities(), urled=False) - - @property - def text_markdown_v2(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message - using :class:`telegram.ParseMode.MARKDOWN_V2`. - - Use this if you want to retrieve the message text with the entities formatted as Markdown - in the same way the original message was formatted. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as Markdown V2. - - Returns: - :obj:`str`: Message text with entities formatted as Markdown. - """ - return self._parse_markdown(self.text, self.parse_entities(), urled=False, version=2) - - @property - def text_markdown_urled(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message - using :class:`telegram.ParseMode.MARKDOWN`. - - Use this if you want to retrieve the message text with the entities formatted as Markdown. - This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. - - Note: - :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for - backward compatibility. You should use :meth:`text_markdown_v2_urled` instead. - - Returns: - :obj:`str`: Message text with entities formatted as Markdown. - - Raises: - :exc:`ValueError`: If the message contains underline, strikethrough, spoiler or nested - entities. - - """ - return self._parse_markdown(self.text, self.parse_entities(), urled=True) - - @property - def text_markdown_v2_urled(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message - using :class:`telegram.ParseMode.MARKDOWN_V2`. - - Use this if you want to retrieve the message text with the entities formatted as Markdown. - This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as Markdown V2. - - Returns: - :obj:`str`: Message text with entities formatted as Markdown. - """ - return self._parse_markdown(self.text, self.parse_entities(), urled=True, version=2) - - @property - def caption_markdown(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message's - caption using :class:`telegram.ParseMode.MARKDOWN`. - - Use this if you want to retrieve the message caption with the caption entities formatted as - Markdown in the same way the original message was formatted. - - Note: - :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for - backward compatibility. You should use :meth:`caption_markdown_v2` instead. - - Returns: - :obj:`str`: Message caption with caption entities formatted as Markdown. - - Raises: - :exc:`ValueError`: If the message contains underline, strikethrough, spoiler or nested - entities. - - """ - return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=False) - - @property - def caption_markdown_v2(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message's - caption using :class:`telegram.ParseMode.MARKDOWN_V2`. - - Use this if you want to retrieve the message caption with the caption entities formatted as - Markdown in the same way the original message was formatted. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as Markdown V2. - - Returns: - :obj:`str`: Message caption with caption entities formatted as Markdown. - """ - return self._parse_markdown( - self.caption, self.parse_caption_entities(), urled=False, version=2 - ) - - @property - def caption_markdown_urled(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message's - caption using :class:`telegram.ParseMode.MARKDOWN`. - - Use this if you want to retrieve the message caption with the caption entities formatted as - Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. - - Note: - :attr:`telegram.ParseMode.MARKDOWN` is is a legacy mode, retained by Telegram for - backward compatibility. You should use :meth:`caption_markdown_v2_urled` instead. - - Returns: - :obj:`str`: Message caption with caption entities formatted as Markdown. - - Raises: - :exc:`ValueError`: If the message contains underline, strikethrough, spoiler or nested - entities. - - """ - return self._parse_markdown(self.caption, self.parse_caption_entities(), urled=True) - - @property - def caption_markdown_v2_urled(self) -> str: - """Creates an Markdown-formatted string from the markup entities found in the message's - caption using :class:`telegram.ParseMode.MARKDOWN_V2`. - - Use this if you want to retrieve the message caption with the caption entities formatted as - Markdown. This also formats :attr:`telegram.MessageEntity.URL` as a hyperlink. - - .. versionchanged:: 13.10 - Spoiler entities are now formatted as Markdown V2. - - Returns: - :obj:`str`: Message caption with caption entities formatted as Markdown. - """ - return self._parse_markdown( - self.caption, self.parse_caption_entities(), urled=True, version=2 - ) diff --git a/telegramer/include/telegram/messageautodeletetimerchanged.py b/telegramer/include/telegram/messageautodeletetimerchanged.py deleted file mode 100644 index 21af1fb..0000000 --- a/telegramer/include/telegram/messageautodeletetimerchanged.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a change in the Telegram message auto -deletion. -""" - -from typing import Any - -from telegram import TelegramObject - - -class MessageAutoDeleteTimerChanged(TelegramObject): - """This object represents a service message about a change in auto-delete timer settings. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`message_auto_delete_time` is equal. - - .. versionadded:: 13.4 - - Args: - message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the - chat. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the - chat. - - """ - - __slots__ = ('message_auto_delete_time', '_id_attrs') - - def __init__( - self, - message_auto_delete_time: int, - **_kwargs: Any, - ): - self.message_auto_delete_time = int(message_auto_delete_time) - - self._id_attrs = (self.message_auto_delete_time,) diff --git a/telegramer/include/telegram/messageentity.py b/telegramer/include/telegram/messageentity.py deleted file mode 100644 index d4e1620..0000000 --- a/telegramer/include/telegram/messageentity.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram MessageEntity.""" - -from typing import TYPE_CHECKING, Any, List, Optional, ClassVar - -from telegram import TelegramObject, User, constants -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class MessageEntity(TelegramObject): - """ - This object represents one special entity in a text message. For example, hashtags, - usernames, URLs, etc. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type`, :attr:`offset` and :attr:`length` are equal. - - Args: - type (:obj:`str`): Type of the entity. Currently, can be mention (@username), hashtag, - bot_command, url, email, phone_number, bold (bold text), italic (italic text), - strikethrough, spoiler (spoiler message), code (monowidth string), pre - (monowidth block), text_link (for clickable text URLs), text_mention - (for users without usernames). - offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity. - length (:obj:`int`): Length of the entity in UTF-16 code units. - url (:obj:`str`, optional): For :attr:`TEXT_LINK` only, url that will be opened after - user taps on the text. - user (:class:`telegram.User`, optional): For :attr:`TEXT_MENTION` only, the mentioned - user. - language (:obj:`str`, optional): For :attr:`PRE` only, the programming language of - the entity text. - - Attributes: - type (:obj:`str`): Type of the entity. - offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity. - length (:obj:`int`): Length of the entity in UTF-16 code units. - url (:obj:`str`): Optional. Url that will be opened after user taps on the text. - user (:class:`telegram.User`): Optional. The mentioned user. - language (:obj:`str`): Optional. Programming language of the entity text. - - """ - - __slots__ = ('length', 'url', 'user', 'type', 'language', 'offset', '_id_attrs') - - def __init__( - self, - type: str, # pylint: disable=W0622 - offset: int, - length: int, - url: str = None, - user: User = None, - language: str = None, - **_kwargs: Any, - ): - # Required - self.type = type - self.offset = offset - self.length = length - # Optionals - self.url = url - self.user = user - self.language = language - - self._id_attrs = (self.type, self.offset, self.length) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MessageEntity']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['user'] = User.de_json(data.get('user'), bot) - - return cls(**data) - - MENTION: ClassVar[str] = constants.MESSAGEENTITY_MENTION - """:const:`telegram.constants.MESSAGEENTITY_MENTION`""" - HASHTAG: ClassVar[str] = constants.MESSAGEENTITY_HASHTAG - """:const:`telegram.constants.MESSAGEENTITY_HASHTAG`""" - CASHTAG: ClassVar[str] = constants.MESSAGEENTITY_CASHTAG - """:const:`telegram.constants.MESSAGEENTITY_CASHTAG`""" - PHONE_NUMBER: ClassVar[str] = constants.MESSAGEENTITY_PHONE_NUMBER - """:const:`telegram.constants.MESSAGEENTITY_PHONE_NUMBER`""" - BOT_COMMAND: ClassVar[str] = constants.MESSAGEENTITY_BOT_COMMAND - """:const:`telegram.constants.MESSAGEENTITY_BOT_COMMAND`""" - URL: ClassVar[str] = constants.MESSAGEENTITY_URL - """:const:`telegram.constants.MESSAGEENTITY_URL`""" - EMAIL: ClassVar[str] = constants.MESSAGEENTITY_EMAIL - """:const:`telegram.constants.MESSAGEENTITY_EMAIL`""" - BOLD: ClassVar[str] = constants.MESSAGEENTITY_BOLD - """:const:`telegram.constants.MESSAGEENTITY_BOLD`""" - ITALIC: ClassVar[str] = constants.MESSAGEENTITY_ITALIC - """:const:`telegram.constants.MESSAGEENTITY_ITALIC`""" - CODE: ClassVar[str] = constants.MESSAGEENTITY_CODE - """:const:`telegram.constants.MESSAGEENTITY_CODE`""" - PRE: ClassVar[str] = constants.MESSAGEENTITY_PRE - """:const:`telegram.constants.MESSAGEENTITY_PRE`""" - TEXT_LINK: ClassVar[str] = constants.MESSAGEENTITY_TEXT_LINK - """:const:`telegram.constants.MESSAGEENTITY_TEXT_LINK`""" - TEXT_MENTION: ClassVar[str] = constants.MESSAGEENTITY_TEXT_MENTION - """:const:`telegram.constants.MESSAGEENTITY_TEXT_MENTION`""" - UNDERLINE: ClassVar[str] = constants.MESSAGEENTITY_UNDERLINE - """:const:`telegram.constants.MESSAGEENTITY_UNDERLINE`""" - STRIKETHROUGH: ClassVar[str] = constants.MESSAGEENTITY_STRIKETHROUGH - """:const:`telegram.constants.MESSAGEENTITY_STRIKETHROUGH`""" - SPOILER: ClassVar[str] = constants.MESSAGEENTITY_SPOILER - """:const:`telegram.constants.MESSAGEENTITY_SPOILER` - - .. versionadded:: 13.10 - """ - ALL_TYPES: ClassVar[List[str]] = constants.MESSAGEENTITY_ALL_TYPES - """:const:`telegram.constants.MESSAGEENTITY_ALL_TYPES`\n - List of all the types""" diff --git a/telegramer/include/telegram/messageid.py b/telegramer/include/telegram/messageid.py deleted file mode 100644 index df7f47c..0000000 --- a/telegramer/include/telegram/messageid.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents an instance of a Telegram MessageId.""" -from typing import Any - -from telegram import TelegramObject - - -class MessageId(TelegramObject): - """This object represents a unique message identifier. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`message_id` is equal. - - Attributes: - message_id (:obj:`int`): Unique message identifier - """ - - __slots__ = ('message_id', '_id_attrs') - - def __init__(self, message_id: int, **_kwargs: Any): - self.message_id = int(message_id) - - self._id_attrs = (self.message_id,) diff --git a/telegramer/include/telegram/parsemode.py b/telegramer/include/telegram/parsemode.py deleted file mode 100644 index 38cf051..0000000 --- a/telegramer/include/telegram/parsemode.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Message Parse Modes.""" -from typing import ClassVar - -from telegram import constants -from telegram.utils.deprecate import set_new_attribute_deprecated - - -class ParseMode: - """This object represents a Telegram Message Parse Modes.""" - - __slots__ = ('__dict__',) - - MARKDOWN: ClassVar[str] = constants.PARSEMODE_MARKDOWN - """:const:`telegram.constants.PARSEMODE_MARKDOWN`\n - - Note: - :attr:`MARKDOWN` is a legacy mode, retained by Telegram for backward compatibility. - You should use :attr:`MARKDOWN_V2` instead. - """ - MARKDOWN_V2: ClassVar[str] = constants.PARSEMODE_MARKDOWN_V2 - """:const:`telegram.constants.PARSEMODE_MARKDOWN_V2`""" - HTML: ClassVar[str] = constants.PARSEMODE_HTML - """:const:`telegram.constants.PARSEMODE_HTML`""" - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) diff --git a/telegramer/include/telegram/passport/__init__.py b/telegramer/include/telegram/passport/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/passport/credentials.py b/telegramer/include/telegram/passport/credentials.py deleted file mode 100644 index d27bd31..0000000 --- a/telegramer/include/telegram/passport/credentials.py +++ /dev/null @@ -1,497 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0114, W0622 -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] - -from base64 import b64decode -from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union, no_type_check - -try: - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives.asymmetric.padding import MGF1, OAEP - from cryptography.hazmat.primitives.ciphers import Cipher - from cryptography.hazmat.primitives.ciphers.algorithms import AES - from cryptography.hazmat.primitives.ciphers.modes import CBC - from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512, Hash - - CRYPTO_INSTALLED = True -except ImportError: - default_backend = None - MGF1, OAEP, Cipher, AES, CBC = (None, None, None, None, None) # type: ignore[misc] - SHA1, SHA256, SHA512, Hash = (None, None, None, None) # type: ignore[misc] - - CRYPTO_INSTALLED = False - -from telegram import TelegramError, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class TelegramDecryptionError(TelegramError): - """Something went wrong with decryption.""" - - __slots__ = ('_msg',) - - def __init__(self, message: Union[str, Exception]): - super().__init__(f"TelegramDecryptionError: {message}") - self._msg = str(message) - - def __reduce__(self) -> Tuple[type, Tuple[str]]: - return self.__class__, (self._msg,) - - -@no_type_check -def decrypt(secret, hash, data): - """ - Decrypt per telegram docs at https://core.telegram.org/passport. - - Args: - secret (:obj:`str` or :obj:`bytes`): The encryption secret, either as bytes or as a - base64 encoded string. - hash (:obj:`str` or :obj:`bytes`): The hash, either as bytes or as a - base64 encoded string. - data (:obj:`str` or :obj:`bytes`): The data to decrypt, either as bytes or as a - base64 encoded string. - file (:obj:`bool`): Force data to be treated as raw data, instead of trying to - b64decode it. - - Raises: - :class:`TelegramDecryptionError`: Given hash does not match hash of decrypted data. - - Returns: - :obj:`bytes`: The decrypted data as bytes. - - """ - if not CRYPTO_INSTALLED: - raise RuntimeError( - 'To use Telegram Passports, PTB must be installed via `pip install ' - 'python-telegram-bot[passport]`.' - ) - # Make a SHA512 hash of secret + update - digest = Hash(SHA512(), backend=default_backend()) - digest.update(secret + hash) - secret_hash_hash = digest.finalize() - # First 32 chars is our key, next 16 is the initialisation vector - key, init_vector = secret_hash_hash[:32], secret_hash_hash[32 : 32 + 16] - # Init a AES-CBC cipher and decrypt the data - cipher = Cipher(AES(key), CBC(init_vector), backend=default_backend()) - decryptor = cipher.decryptor() - data = decryptor.update(data) + decryptor.finalize() - # Calculate SHA256 hash of the decrypted data - digest = Hash(SHA256(), backend=default_backend()) - digest.update(data) - data_hash = digest.finalize() - # If the newly calculated hash did not match the one telegram gave us - if data_hash != hash: - # Raise a error that is caught inside telegram.PassportData and transformed into a warning - raise TelegramDecryptionError(f"Hashes are not equal! {data_hash} != {hash}") - # Return data without padding - return data[data[0] :] - - -@no_type_check -def decrypt_json(secret, hash, data): - """Decrypts data using secret and hash and then decodes utf-8 string and loads json""" - return json.loads(decrypt(secret, hash, data).decode('utf-8')) - - -class EncryptedCredentials(TelegramObject): - """Contains data required for decrypting and authenticating EncryptedPassportElement. See the - Telegram Passport Documentation for a complete description of the data decryption and - authentication processes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`data`, :attr:`hash` and :attr:`secret` are equal. - - Note: - This object is decrypted only when originating from - :obj:`telegram.PassportData.decrypted_credentials`. - - Args: - data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's - nonce, data hashes and secrets used for EncryptedPassportElement decryption and - authentication or base64 encrypted data. - hash (:obj:`str`): Base64-encoded data hash for data authentication. - secret (:obj:`str`): Decrypted or encrypted secret used for decryption. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's - nonce, data hashes and secrets used for EncryptedPassportElement decryption and - authentication or base64 encrypted data. - hash (:obj:`str`): Base64-encoded data hash for data authentication. - secret (:obj:`str`): Decrypted or encrypted secret used for decryption. - - """ - - __slots__ = ( - 'hash', - 'secret', - 'bot', - 'data', - '_id_attrs', - '_decrypted_secret', - '_decrypted_data', - ) - - def __init__(self, data: str, hash: str, secret: str, bot: 'Bot' = None, **_kwargs: Any): - # Required - self.data = data - self.hash = hash - self.secret = secret - - self._id_attrs = (self.data, self.hash, self.secret) - - self.bot = bot - self._decrypted_secret = None - self._decrypted_data: Optional['Credentials'] = None - - @property - def decrypted_secret(self) -> str: - """ - :obj:`str`: Lazily decrypt and return secret. - - Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad - private/public key but can also suggest malformed/tampered data. - """ - if self._decrypted_secret is None: - if not CRYPTO_INSTALLED: - raise RuntimeError( - 'To use Telegram Passports, PTB must be installed via `pip install ' - 'python-telegram-bot[passport]`.' - ) - # Try decrypting according to step 1 at - # https://core.telegram.org/passport#decrypting-data - # We make sure to base64 decode the secret first. - # Telegram says to use OAEP padding so we do that. The Mask Generation Function - # is the default for OAEP, the algorithm is the default for PHP which is what - # Telegram's backend servers run. - try: - self._decrypted_secret = self.bot.private_key.decrypt( - b64decode(self.secret), - OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None), # skipcq - ) - except ValueError as exception: - # If decryption fails raise exception - raise TelegramDecryptionError(exception) from exception - return self._decrypted_secret - - @property - def decrypted_data(self) -> 'Credentials': - """ - :class:`telegram.Credentials`: Lazily decrypt and return credentials data. This object - also contains the user specified nonce as - `decrypted_data.nonce`. - - Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad - private/public key but can also suggest malformed/tampered data. - """ - if self._decrypted_data is None: - self._decrypted_data = Credentials.de_json( - decrypt_json(self.decrypted_secret, b64decode(self.hash), b64decode(self.data)), - self.bot, - ) - return self._decrypted_data - - -class Credentials(TelegramObject): - """ - Attributes: - secure_data (:class:`telegram.SecureData`): Credentials for encrypted data - nonce (:obj:`str`): Bot-specified nonce - """ - - __slots__ = ('bot', 'nonce', 'secure_data') - - def __init__(self, secure_data: 'SecureData', nonce: str, bot: 'Bot' = None, **_kwargs: Any): - # Required - self.secure_data = secure_data - self.nonce = nonce - - self.bot = bot - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Credentials']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['secure_data'] = SecureData.de_json(data.get('secure_data'), bot=bot) - - return cls(bot=bot, **data) - - -class SecureData(TelegramObject): - """ - This object represents the credentials that were used to decrypt the encrypted data. - All fields are optional and depend on fields that were requested. - - Attributes: - personal_details (:class:`telegram.SecureValue`, optional): Credentials for encrypted - personal details. - passport (:class:`telegram.SecureValue`, optional): Credentials for encrypted passport. - internal_passport (:class:`telegram.SecureValue`, optional): Credentials for encrypted - internal passport. - driver_license (:class:`telegram.SecureValue`, optional): Credentials for encrypted - driver license. - identity_card (:class:`telegram.SecureValue`, optional): Credentials for encrypted ID card - address (:class:`telegram.SecureValue`, optional): Credentials for encrypted - residential address. - utility_bill (:class:`telegram.SecureValue`, optional): Credentials for encrypted - utility bill. - bank_statement (:class:`telegram.SecureValue`, optional): Credentials for encrypted - bank statement. - rental_agreement (:class:`telegram.SecureValue`, optional): Credentials for encrypted - rental agreement. - passport_registration (:class:`telegram.SecureValue`, optional): Credentials for encrypted - registration from internal passport. - temporary_registration (:class:`telegram.SecureValue`, optional): Credentials for encrypted - temporary registration. - """ - - __slots__ = ( - 'bot', - 'utility_bill', - 'personal_details', - 'temporary_registration', - 'address', - 'driver_license', - 'rental_agreement', - 'internal_passport', - 'identity_card', - 'bank_statement', - 'passport', - 'passport_registration', - ) - - def __init__( - self, - personal_details: 'SecureValue' = None, - passport: 'SecureValue' = None, - internal_passport: 'SecureValue' = None, - driver_license: 'SecureValue' = None, - identity_card: 'SecureValue' = None, - address: 'SecureValue' = None, - utility_bill: 'SecureValue' = None, - bank_statement: 'SecureValue' = None, - rental_agreement: 'SecureValue' = None, - passport_registration: 'SecureValue' = None, - temporary_registration: 'SecureValue' = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Optionals - self.temporary_registration = temporary_registration - self.passport_registration = passport_registration - self.rental_agreement = rental_agreement - self.bank_statement = bank_statement - self.utility_bill = utility_bill - self.address = address - self.identity_card = identity_card - self.driver_license = driver_license - self.internal_passport = internal_passport - self.passport = passport - self.personal_details = personal_details - - self.bot = bot - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureData']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['temporary_registration'] = SecureValue.de_json( - data.get('temporary_registration'), bot=bot - ) - data['passport_registration'] = SecureValue.de_json( - data.get('passport_registration'), bot=bot - ) - data['rental_agreement'] = SecureValue.de_json(data.get('rental_agreement'), bot=bot) - data['bank_statement'] = SecureValue.de_json(data.get('bank_statement'), bot=bot) - data['utility_bill'] = SecureValue.de_json(data.get('utility_bill'), bot=bot) - data['address'] = SecureValue.de_json(data.get('address'), bot=bot) - data['identity_card'] = SecureValue.de_json(data.get('identity_card'), bot=bot) - data['driver_license'] = SecureValue.de_json(data.get('driver_license'), bot=bot) - data['internal_passport'] = SecureValue.de_json(data.get('internal_passport'), bot=bot) - data['passport'] = SecureValue.de_json(data.get('passport'), bot=bot) - data['personal_details'] = SecureValue.de_json(data.get('personal_details'), bot=bot) - - return cls(bot=bot, **data) - - -class SecureValue(TelegramObject): - """ - This object represents the credentials that were used to decrypt the encrypted value. - All fields are optional and depend on the type of field. - - Attributes: - data (:class:`telegram.DataCredentials`, optional): Credentials for encrypted Telegram - Passport data. Available for "personal_details", "passport", "driver_license", - "identity_card", "identity_passport" and "address" types. - front_side (:class:`telegram.FileCredentials`, optional): Credentials for encrypted - document's front side. Available for "passport", "driver_license", "identity_card" - and "internal_passport". - reverse_side (:class:`telegram.FileCredentials`, optional): Credentials for encrypted - document's reverse side. Available for "driver_license" and "identity_card". - selfie (:class:`telegram.FileCredentials`, optional): Credentials for encrypted selfie - of the user with a document. Can be available for "passport", "driver_license", - "identity_card" and "internal_passport". - translation (List[:class:`telegram.FileCredentials`], optional): Credentials for an - encrypted translation of the document. Available for "passport", "driver_license", - "identity_card", "internal_passport", "utility_bill", "bank_statement", - "rental_agreement", "passport_registration" and "temporary_registration". - files (List[:class:`telegram.FileCredentials`], optional): Credentials for encrypted - files. Available for "utility_bill", "bank_statement", "rental_agreement", - "passport_registration" and "temporary_registration" types. - - """ - - __slots__ = ('data', 'front_side', 'reverse_side', 'selfie', 'files', 'translation', 'bot') - - def __init__( - self, - data: 'DataCredentials' = None, - front_side: 'FileCredentials' = None, - reverse_side: 'FileCredentials' = None, - selfie: 'FileCredentials' = None, - files: List['FileCredentials'] = None, - translation: List['FileCredentials'] = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - self.data = data - self.front_side = front_side - self.reverse_side = reverse_side - self.selfie = selfie - self.files = files - self.translation = translation - - self.bot = bot - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureValue']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['data'] = DataCredentials.de_json(data.get('data'), bot=bot) - data['front_side'] = FileCredentials.de_json(data.get('front_side'), bot=bot) - data['reverse_side'] = FileCredentials.de_json(data.get('reverse_side'), bot=bot) - data['selfie'] = FileCredentials.de_json(data.get('selfie'), bot=bot) - data['files'] = FileCredentials.de_list(data.get('files'), bot=bot) - data['translation'] = FileCredentials.de_list(data.get('translation'), bot=bot) - - return cls(bot=bot, **data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['files'] = [p.to_dict() for p in self.files] - data['translation'] = [p.to_dict() for p in self.translation] - - return data - - -class _CredentialsBase(TelegramObject): - """Base class for DataCredentials and FileCredentials.""" - - __slots__ = ('hash', 'secret', 'file_hash', 'data_hash', 'bot') - - def __init__(self, hash: str, secret: str, bot: 'Bot' = None, **_kwargs: Any): - self.hash = hash - self.secret = secret - - # Aliases just be be sure - self.file_hash = self.hash - self.data_hash = self.hash - - self.bot = bot - - -class DataCredentials(_CredentialsBase): - """ - These credentials can be used to decrypt encrypted data from the data field in - EncryptedPassportData. - - Args: - data_hash (:obj:`str`): Checksum of encrypted data - secret (:obj:`str`): Secret of encrypted data - - Attributes: - hash (:obj:`str`): Checksum of encrypted data - secret (:obj:`str`): Secret of encrypted data - """ - - __slots__ = () - - def __init__(self, data_hash: str, secret: str, **_kwargs: Any): - super().__init__(data_hash, secret, **_kwargs) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - del data['file_hash'] - del data['hash'] - - return data - - -class FileCredentials(_CredentialsBase): - """ - These credentials can be used to decrypt encrypted files from the front_side, - reverse_side, selfie and files fields in EncryptedPassportData. - - Args: - file_hash (:obj:`str`): Checksum of encrypted file - secret (:obj:`str`): Secret of encrypted file - - Attributes: - hash (:obj:`str`): Checksum of encrypted file - secret (:obj:`str`): Secret of encrypted file - """ - - __slots__ = () - - def __init__(self, file_hash: str, secret: str, **_kwargs: Any): - super().__init__(file_hash, secret, **_kwargs) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - del data['data_hash'] - del data['hash'] - - return data diff --git a/telegramer/include/telegram/passport/data.py b/telegramer/include/telegram/passport/data.py deleted file mode 100644 index e1d38b5..0000000 --- a/telegramer/include/telegram/passport/data.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0114 -from typing import TYPE_CHECKING, Any - -from telegram import TelegramObject - -if TYPE_CHECKING: - from telegram import Bot - - -class PersonalDetails(TelegramObject): - """ - This object represents personal details. - - Attributes: - first_name (:obj:`str`): First Name. - middle_name (:obj:`str`): Optional. First Name. - last_name (:obj:`str`): Last Name. - birth_date (:obj:`str`): Date of birth in DD.MM.YYYY format. - gender (:obj:`str`): Gender, male or female. - country_code (:obj:`str`): Citizenship (ISO 3166-1 alpha-2 country code). - residence_country_code (:obj:`str`): Country of residence (ISO 3166-1 alpha-2 country - code). - first_name_native (:obj:`str`): First Name in the language of the user's country of - residence. - middle_name_native (:obj:`str`): Optional. Middle Name in the language of the user's - country of residence. - last_name_native (:obj:`str`): Last Name in the language of the user's country of - residence. - """ - - __slots__ = ( - 'middle_name', - 'first_name_native', - 'last_name_native', - 'residence_country_code', - 'first_name', - 'last_name', - 'country_code', - 'gender', - 'bot', - 'middle_name_native', - 'birth_date', - ) - - def __init__( - self, - first_name: str, - last_name: str, - birth_date: str, - gender: str, - country_code: str, - residence_country_code: str, - first_name_native: str = None, - last_name_native: str = None, - middle_name: str = None, - middle_name_native: str = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.first_name = first_name - self.last_name = last_name - self.middle_name = middle_name - self.birth_date = birth_date - self.gender = gender - self.country_code = country_code - self.residence_country_code = residence_country_code - self.first_name_native = first_name_native - self.last_name_native = last_name_native - self.middle_name_native = middle_name_native - - self.bot = bot - - -class ResidentialAddress(TelegramObject): - """ - This object represents a residential address. - - Attributes: - street_line1 (:obj:`str`): First line for the address. - street_line2 (:obj:`str`): Optional. Second line for the address. - city (:obj:`str`): City. - state (:obj:`str`): Optional. State. - country_code (:obj:`str`): ISO 3166-1 alpha-2 country code. - post_code (:obj:`str`): Address post code. - """ - - __slots__ = ( - 'post_code', - 'city', - 'country_code', - 'street_line2', - 'street_line1', - 'bot', - 'state', - ) - - def __init__( - self, - street_line1: str, - street_line2: str, - city: str, - state: str, - country_code: str, - post_code: str, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.street_line1 = street_line1 - self.street_line2 = street_line2 - self.city = city - self.state = state - self.country_code = country_code - self.post_code = post_code - - self.bot = bot - - -class IdDocumentData(TelegramObject): - """ - This object represents the data of an identity document. - - Attributes: - document_no (:obj:`str`): Document number. - expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format. - """ - - __slots__ = ('document_no', 'bot', 'expiry_date') - - def __init__(self, document_no: str, expiry_date: str, bot: 'Bot' = None, **_kwargs: Any): - self.document_no = document_no - self.expiry_date = expiry_date - - self.bot = bot diff --git a/telegramer/include/telegram/passport/encryptedpassportelement.py b/telegramer/include/telegram/passport/encryptedpassportelement.py deleted file mode 100644 index 78b6972..0000000 --- a/telegramer/include/telegram/passport/encryptedpassportelement.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env python -# flake8: noqa: E501 -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram EncryptedPassportElement.""" -from base64 import b64decode -from typing import TYPE_CHECKING, Any, List, Optional - -from telegram import ( - IdDocumentData, - PassportFile, - PersonalDetails, - ResidentialAddress, - TelegramObject, -) -from telegram.passport.credentials import decrypt_json -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot, Credentials - - -class EncryptedPassportElement(TelegramObject): - """ - Contains information about documents or other Telegram Passport elements shared with the bot - by the user. The data has been automatically decrypted by python-telegram-bot. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`type`, :attr:`data`, :attr:`phone_number`, :attr:`email`, - :attr:`files`, :attr:`front_side`, :attr:`reverse_side` and :attr:`selfie` are equal. - - Note: - This object is decrypted only when originating from - :obj:`telegram.PassportData.decrypted_data`. - - Args: - type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", - "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", - "rental_agreement", "passport_registration", "temporary_registration", "phone_number", - "email". - data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ - :class:`telegram.ResidentialAddress` | :obj:`str`, optional): - Decrypted or encrypted data, available for "personal_details", "passport", - "driver_license", "identity_card", "identity_passport" and "address" types. - phone_number (:obj:`str`, optional): User's verified phone number, available only for - "phone_number" type. - email (:obj:`str`, optional): User's verified email address, available only for "email" - type. - files (List[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted files - with documents provided by the user, available for "utility_bill", "bank_statement", - "rental_agreement", "passport_registration" and "temporary_registration" types. - front_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the - front side of the document, provided by the user. Available for "passport", - "driver_license", "identity_card" and "internal_passport". - reverse_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the - reverse side of the document, provided by the user. Available for "driver_license" and - "identity_card". - selfie (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the - selfie of the user holding a document, provided by the user; available for "passport", - "driver_license", "identity_card" and "internal_passport". - translation (List[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted - files with translated versions of documents provided by the user. Available if - requested for "passport", "driver_license", "identity_card", "internal_passport", - "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and - "temporary_registration" types. - hash (:obj:`str`): Base64-encoded element hash for using in - :class:`telegram.PassportElementErrorUnspecified`. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", - "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", - "rental_agreement", "passport_registration", "temporary_registration", "phone_number", - "email". - data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ - :class:`telegram.ResidentialAddress` | :obj:`str`): - Optional. Decrypted or encrypted data, available for "personal_details", "passport", - "driver_license", "identity_card", "identity_passport" and "address" types. - phone_number (:obj:`str`): Optional. User's verified phone number, available only for - "phone_number" type. - email (:obj:`str`): Optional. User's verified email address, available only for "email" - type. - files (List[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files - with documents provided by the user, available for "utility_bill", "bank_statement", - "rental_agreement", "passport_registration" and "temporary_registration" types. - front_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the - front side of the document, provided by the user. Available for "passport", - "driver_license", "identity_card" and "internal_passport". - reverse_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the - reverse side of the document, provided by the user. Available for "driver_license" and - "identity_card". - selfie (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the - selfie of the user holding a document, provided by the user; available for "passport", - "driver_license", "identity_card" and "internal_passport". - translation (List[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted - files with translated versions of documents provided by the user. Available if - requested for "passport", "driver_license", "identity_card", "internal_passport", - "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and - "temporary_registration" types. - hash (:obj:`str`): Base64-encoded element hash for using in - :class:`telegram.PassportElementErrorUnspecified`. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'selfie', - 'files', - 'type', - 'translation', - 'email', - 'hash', - 'phone_number', - 'bot', - 'reverse_side', - 'front_side', - 'data', - '_id_attrs', - ) - - def __init__( - self, - type: str, # pylint: disable=W0622 - data: PersonalDetails = None, - phone_number: str = None, - email: str = None, - files: List[PassportFile] = None, - front_side: PassportFile = None, - reverse_side: PassportFile = None, - selfie: PassportFile = None, - translation: List[PassportFile] = None, - hash: str = None, # pylint: disable=W0622 - bot: 'Bot' = None, - credentials: 'Credentials' = None, # pylint: disable=W0613 - **_kwargs: Any, - ): - # Required - self.type = type - # Optionals - self.data = data - self.phone_number = phone_number - self.email = email - self.files = files - self.front_side = front_side - self.reverse_side = reverse_side - self.selfie = selfie - self.translation = translation - self.hash = hash - - self._id_attrs = ( - self.type, - self.data, - self.phone_number, - self.email, - self.files, - self.front_side, - self.reverse_side, - self.selfie, - ) - - self.bot = bot - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['EncryptedPassportElement']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['files'] = PassportFile.de_list(data.get('files'), bot) or None - data['front_side'] = PassportFile.de_json(data.get('front_side'), bot) - data['reverse_side'] = PassportFile.de_json(data.get('reverse_side'), bot) - data['selfie'] = PassportFile.de_json(data.get('selfie'), bot) - data['translation'] = PassportFile.de_list(data.get('translation'), bot) or None - - return cls(bot=bot, **data) - - @classmethod - def de_json_decrypted( - cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'Credentials' - ) -> Optional['EncryptedPassportElement']: - """Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account - passport credentials. - - Args: - data (Dict[:obj:`str`, ...]): The JSON data. - bot (:class:`telegram.Bot`): The bot associated with this object. - credentials (:class:`telegram.FileCredentials`): The credentials - - Returns: - :class:`telegram.EncryptedPassportElement`: - - """ - if not data: - return None - - if data['type'] not in ('phone_number', 'email'): - secure_data = getattr(credentials.secure_data, data['type']) - - if secure_data.data is not None: - # If not already decrypted - if not isinstance(data['data'], dict): - data['data'] = decrypt_json( - b64decode(secure_data.data.secret), - b64decode(secure_data.data.hash), - b64decode(data['data']), - ) - if data['type'] == 'personal_details': - data['data'] = PersonalDetails.de_json(data['data'], bot=bot) - elif data['type'] in ( - 'passport', - 'internal_passport', - 'driver_license', - 'identity_card', - ): - data['data'] = IdDocumentData.de_json(data['data'], bot=bot) - elif data['type'] == 'address': - data['data'] = ResidentialAddress.de_json(data['data'], bot=bot) - - data['files'] = ( - PassportFile.de_list_decrypted(data.get('files'), bot, secure_data.files) or None - ) - data['front_side'] = PassportFile.de_json_decrypted( - data.get('front_side'), bot, secure_data.front_side - ) - data['reverse_side'] = PassportFile.de_json_decrypted( - data.get('reverse_side'), bot, secure_data.reverse_side - ) - data['selfie'] = PassportFile.de_json_decrypted( - data.get('selfie'), bot, secure_data.selfie - ) - data['translation'] = ( - PassportFile.de_list_decrypted( - data.get('translation'), bot, secure_data.translation - ) - or None - ) - - return cls(bot=bot, **data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - if self.files: - data['files'] = [p.to_dict() for p in self.files] - if self.translation: - data['translation'] = [p.to_dict() for p in self.translation] - - return data diff --git a/telegramer/include/telegram/passport/passportdata.py b/telegramer/include/telegram/passport/passportdata.py deleted file mode 100644 index 40f7e72..0000000 --- a/telegramer/include/telegram/passport/passportdata.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""Contains information about Telegram Passport data shared with the bot by the user.""" - -from typing import TYPE_CHECKING, Any, List, Optional - -from telegram import EncryptedCredentials, EncryptedPassportElement, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot, Credentials - - -class PassportData(TelegramObject): - """Contains information about Telegram Passport data shared with the bot by the user. - - Note: - To be able to decrypt this object, you must pass your ``private_key`` to either - :class:`telegram.Updater` or :class:`telegram.Bot`. Decrypted data is then found in - :attr:`decrypted_data` and the payload can be found in :attr:`decrypted_credentials`'s - attribute :attr:`telegram.Credentials.payload`. - - Args: - data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information - about documents and other Telegram Passport elements that was shared with the bot. - credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information - about documents and other Telegram Passport elements that was shared with the bot. - credentials (:class:`telegram.EncryptedCredentials`): Encrypted credentials. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - - """ - - __slots__ = ('bot', 'credentials', 'data', '_decrypted_data', '_id_attrs') - - def __init__( - self, - data: List[EncryptedPassportElement], - credentials: EncryptedCredentials, - bot: 'Bot' = None, - **_kwargs: Any, - ): - self.data = data - self.credentials = credentials - - self.bot = bot - self._decrypted_data: Optional[List[EncryptedPassportElement]] = None - self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PassportData']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['data'] = EncryptedPassportElement.de_list(data.get('data'), bot) - data['credentials'] = EncryptedCredentials.de_json(data.get('credentials'), bot) - - return cls(bot=bot, **data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['data'] = [e.to_dict() for e in self.data] - - return data - - @property - def decrypted_data(self) -> List[EncryptedPassportElement]: - """ - List[:class:`telegram.EncryptedPassportElement`]: Lazily decrypt and return information - about documents and other Telegram Passport elements which were shared with the bot. - - Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad - private/public key but can also suggest malformed/tampered data. - """ - if self._decrypted_data is None: - self._decrypted_data = [ - EncryptedPassportElement.de_json_decrypted( - element.to_dict(), self.bot, self.decrypted_credentials - ) - for element in self.data - ] - return self._decrypted_data - - @property - def decrypted_credentials(self) -> 'Credentials': - """ - :class:`telegram.Credentials`: Lazily decrypt and return credentials that were used - to decrypt the data. This object also contains the user specified payload as - `decrypted_data.payload`. - - Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad - private/public key but can also suggest malformed/tampered data. - """ - return self.credentials.decrypted_data diff --git a/telegramer/include/telegram/passport/passportelementerrors.py b/telegramer/include/telegram/passport/passportelementerrors.py deleted file mode 100644 index fd38ff3..0000000 --- a/telegramer/include/telegram/passport/passportelementerrors.py +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=W0622 -"""This module contains the classes that represent Telegram PassportElementError.""" - -from typing import Any - -from telegram import TelegramObject - - -class PassportElementError(TelegramObject): - """Baseclass for the PassportElementError* classes. - - This object represents an error in the Telegram Passport element which was submitted that - should be resolved by the user. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source` and :attr:`type` are equal. - - Args: - source (:obj:`str`): Error source. - type (:obj:`str`): The section of the user's Telegram Passport which has the error. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - source (:obj:`str`): Error source. - type (:obj:`str`): The section of the user's Telegram Passport which has the error. - message (:obj:`str`): Error message. - - """ - - # All subclasses of this class won't have _id_attrs in slots since it's added here. - __slots__ = ('message', 'source', 'type', '_id_attrs') - - def __init__(self, source: str, type: str, message: str, **_kwargs: Any): - # Required - self.source = str(source) - self.type = str(type) - self.message = str(message) - - self._id_attrs = (self.source, self.type) - - -class PassportElementErrorDataField(PassportElementError): - """ - Represents an issue in one of the data fields that was provided by the user. The error is - considered resolved when the field's value changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`field_name`, :attr:`data_hash` - and :attr:`message` are equal. - - Args: - type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of - ``"personal_details"``, ``"passport"``, ``"driver_license"``, ``"identity_card"``, - ``"internal_passport"``, ``"address"``. - field_name (:obj:`str`): Name of the data field which has the error. - data_hash (:obj:`str`): Base64-encoded data hash. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of - ``"personal_details"``, ``"passport"``, ``"driver_license"``, ``"identity_card"``, - ``"internal_passport"``, ``"address"``. - field_name (:obj:`str`): Name of the data field which has the error. - data_hash (:obj:`str`): Base64-encoded data hash. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('data_hash', 'field_name') - - def __init__(self, type: str, field_name: str, data_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('data', type, message) - self.field_name = field_name - self.data_hash = data_hash - - self._id_attrs = (self.source, self.type, self.field_name, self.data_hash, self.message) - - -class PassportElementErrorFile(PassportElementError): - """ - Represents an issue with a document scan. The error is considered resolved when the file with - the document scan changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hash`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, - ``"passport_registration"``, ``"temporary_registration"``. - file_hash (:obj:`str`): Base64-encoded file hash. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, - ``"passport_registration"``, ``"temporary_registration"``. - file_hash (:obj:`str`): Base64-encoded file hash. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hash',) - - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('file', type, message) - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - -class PassportElementErrorFiles(PassportElementError): - """ - Represents an issue with a list of scans. The error is considered resolved when the list of - files with the document scans changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hashes`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, - ``"passport_registration"``, ``"temporary_registration"``. - file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, - ``"passport_registration"``, ``"temporary_registration"``. - file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hashes',) - - def __init__(self, type: str, file_hashes: str, message: str, **_kwargs: Any): - # Required - super().__init__('files', type, message) - self.file_hashes = file_hashes - - self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) - - -class PassportElementErrorFrontSide(PassportElementError): - """ - Represents an issue with the front side of a document. The error is considered resolved when - the file with the front side of the document changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hash`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. - file_hash (:obj:`str`): Base64-encoded hash of the file with the front side of the - document. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. - file_hash (:obj:`str`): Base64-encoded hash of the file with the front side of the - document. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hash',) - - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('front_side', type, message) - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - -class PassportElementErrorReverseSide(PassportElementError): - """ - Represents an issue with the reverse side of a document. The error is considered resolved when - the file with the reverse side of the document changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hash`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"driver_license"``, ``"identity_card"``. - file_hash (:obj:`str`): Base64-encoded hash of the file with the reverse side of the - document. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"driver_license"``, ``"identity_card"``. - file_hash (:obj:`str`): Base64-encoded hash of the file with the reverse side of the - document. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hash',) - - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('reverse_side', type, message) - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - -class PassportElementErrorSelfie(PassportElementError): - """ - Represents an issue with the selfie with a document. The error is considered resolved when - the file with the selfie changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hash`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. - file_hash (:obj:`str`): Base64-encoded hash of the file with the selfie. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of - ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. - file_hash (:obj:`str`): Base64-encoded hash of the file with the selfie. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hash',) - - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('selfie', type, message) - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - -class PassportElementErrorTranslationFile(PassportElementError): - """ - Represents an issue with one of the files that constitute the translation of a document. - The error is considered resolved when the file changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hash`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, - one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, - ``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``, - ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. - file_hash (:obj:`str`): Base64-encoded hash of the file. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, - one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, - ``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``, - ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. - file_hash (:obj:`str`): Base64-encoded hash of the file. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hash',) - - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('translation_file', type, message) - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - -class PassportElementErrorTranslationFiles(PassportElementError): - """ - Represents an issue with the translated version of a document. The error is considered - resolved when a file with the document translation changes. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`file_hashes`, and - :attr:`message` are equal. - - Args: - type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, - one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, - ``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``, - ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. - file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, - one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, - ``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``, - ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. - file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('file_hashes',) - - def __init__(self, type: str, file_hashes: str, message: str, **_kwargs: Any): - # Required - super().__init__('translation_files', type, message) - self.file_hashes = file_hashes - - self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) - - -class PassportElementErrorUnspecified(PassportElementError): - """ - Represents an issue in an unspecified place. The error is considered resolved when new - data is added. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`source`, :attr:`type`, :attr:`element_hash`, - and :attr:`message` are equal. - - Args: - type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. - element_hash (:obj:`str`): Base64-encoded element hash. - message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. - element_hash (:obj:`str`): Base64-encoded element hash. - message (:obj:`str`): Error message. - - """ - - __slots__ = ('element_hash',) - - def __init__(self, type: str, element_hash: str, message: str, **_kwargs: Any): - # Required - super().__init__('unspecified', type, message) - self.element_hash = element_hash - - self._id_attrs = (self.source, self.type, self.element_hash, self.message) diff --git a/telegramer/include/telegram/passport/passportfile.py b/telegramer/include/telegram/passport/passportfile.py deleted file mode 100644 index 59f60c8..0000000 --- a/telegramer/include/telegram/passport/passportfile.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Encrypted PassportFile.""" - -from typing import TYPE_CHECKING, Any, List, Optional - -from telegram import TelegramObject -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot, File, FileCredentials - - -class PassportFile(TelegramObject): - """ - This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport - files are in JPEG format when decrypted and don't exceed 10MB. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`file_unique_id` is equal. - - Args: - file_id (:obj:`str`): Identifier for this file, which can be used to download - or reuse the file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - file_size (:obj:`int`): File size. - file_date (:obj:`int`): Unix time when the file was uploaded. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - file_id (:obj:`str`): Identifier for this file. - file_unique_id (:obj:`str`): Unique identifier for this file, which - is supposed to be the same over time and for different bots. - Can't be used to download or reuse the file. - file_size (:obj:`int`): File size. - file_date (:obj:`int`): Unix time when the file was uploaded. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'file_date', - 'bot', - 'file_id', - 'file_size', - '_credentials', - 'file_unique_id', - '_id_attrs', - ) - - def __init__( - self, - file_id: str, - file_unique_id: str, - file_date: int, - file_size: int = None, - bot: 'Bot' = None, - credentials: 'FileCredentials' = None, - **_kwargs: Any, - ): - # Required - self.file_id = file_id - self.file_unique_id = file_unique_id - self.file_size = file_size - self.file_date = file_date - # Optionals - self.bot = bot - self._credentials = credentials - - self._id_attrs = (self.file_unique_id,) - - @classmethod - def de_json_decrypted( - cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'FileCredentials' - ) -> Optional['PassportFile']: - """Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account - passport credentials. - - Args: - data (Dict[:obj:`str`, ...]): The JSON data. - bot (:class:`telegram.Bot`): The bot associated with this object. - credentials (:class:`telegram.FileCredentials`): The credentials - - Returns: - :class:`telegram.PassportFile`: - - """ - data = cls._parse_data(data) - - if not data: - return None - - data['credentials'] = credentials - - return cls(bot=bot, **data) - - @classmethod - def de_list_decrypted( - cls, data: Optional[List[JSONDict]], bot: 'Bot', credentials: List['FileCredentials'] - ) -> List[Optional['PassportFile']]: - """Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account - passport credentials. - - Args: - data (Dict[:obj:`str`, ...]): The JSON data. - bot (:class:`telegram.Bot`): The bot associated with these objects. - credentials (:class:`telegram.FileCredentials`): The credentials - - Returns: - List[:class:`telegram.PassportFile`]: - - """ - if not data: - return [] - - return [ - cls.de_json_decrypted(passport_file, bot, credentials[i]) - for i, passport_file in enumerate(data) - ] - - def get_file( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': - """ - Wrapper over :attr:`telegram.Bot.get_file`. Will automatically assign the correct - credentials to the returned :class:`telegram.File` if originating from - :obj:`telegram.PassportData.decrypted_data`. - - For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. - - Returns: - :class:`telegram.File` - - Raises: - :class:`telegram.error.TelegramError` - - """ - file = self.bot.get_file(file_id=self.file_id, timeout=timeout, api_kwargs=api_kwargs) - file.set_credentials(self._credentials) - return file diff --git a/telegramer/include/telegram/payment/__init__.py b/telegramer/include/telegram/payment/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/payment/invoice.py b/telegramer/include/telegram/payment/invoice.py deleted file mode 100644 index 18fda43..0000000 --- a/telegramer/include/telegram/payment/invoice.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Invoice.""" - -from typing import Any - -from telegram import TelegramObject - - -class Invoice(TelegramObject): - """This object contains basic information about an invoice. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`title`, :attr:`description`, :attr:`start_parameter`, - :attr:`currency` and :attr:`total_amount` are equal. - - Args: - title (:obj:`str`): Product name. - description (:obj:`str`): Product description. - start_parameter (:obj:`str`): Unique bot deep-linking parameter that can be used to - generate this invoice. - currency (:obj:`str`): Three-letter ISO 4217 currency code. - total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not - float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. See the - :obj:`exp` parameter in - `currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, - it shows the number of digits past the decimal point for each currency - (2 for the majority of currencies). - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - title (:obj:`str`): Product name. - description (:obj:`str`): Product description. - start_parameter (:obj:`str`): Unique bot deep-linking parameter. - currency (:obj:`str`): Three-letter ISO 4217 currency code. - total_amount (:obj:`int`): Total price in the smallest units of the currency. - - """ - - __slots__ = ( - 'currency', - 'start_parameter', - 'title', - 'description', - 'total_amount', - '_id_attrs', - ) - - def __init__( - self, - title: str, - description: str, - start_parameter: str, - currency: str, - total_amount: int, - **_kwargs: Any, - ): - self.title = title - self.description = description - self.start_parameter = start_parameter - self.currency = currency - self.total_amount = total_amount - - self._id_attrs = ( - self.title, - self.description, - self.start_parameter, - self.currency, - self.total_amount, - ) diff --git a/telegramer/include/telegram/payment/labeledprice.py b/telegramer/include/telegram/payment/labeledprice.py deleted file mode 100644 index 37b58e3..0000000 --- a/telegramer/include/telegram/payment/labeledprice.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram LabeledPrice.""" - -from typing import Any - -from telegram import TelegramObject - - -class LabeledPrice(TelegramObject): - """This object represents a portion of the price for goods or services. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`label` and :attr:`amount` are equal. - - Args: - label (:obj:`str`): Portion label. - amount (:obj:`int`): Price of the product in the smallest units of the currency (integer, - not float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. - See the :obj:`exp` parameter in - `currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, - it shows the number of digits past the decimal point for each currency - (2 for the majority of currencies). - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - label (:obj:`str`): Portion label. - amount (:obj:`int`): Price of the product in the smallest units of the currency. - - """ - - __slots__ = ('label', '_id_attrs', 'amount') - - def __init__(self, label: str, amount: int, **_kwargs: Any): - self.label = label - self.amount = amount - - self._id_attrs = (self.label, self.amount) diff --git a/telegramer/include/telegram/payment/orderinfo.py b/telegramer/include/telegram/payment/orderinfo.py deleted file mode 100644 index 902b324..0000000 --- a/telegramer/include/telegram/payment/orderinfo.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram OrderInfo.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import ShippingAddress, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class OrderInfo(TelegramObject): - """This object represents information about an order. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`name`, :attr:`phone_number`, :attr:`email` and - :attr:`shipping_address` are equal. - - Args: - name (:obj:`str`, optional): User name. - phone_number (:obj:`str`, optional): User's phone number. - email (:obj:`str`, optional): User email. - shipping_address (:class:`telegram.ShippingAddress`, optional): User shipping address. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - name (:obj:`str`): Optional. User name. - phone_number (:obj:`str`): Optional. User's phone number. - email (:obj:`str`): Optional. User email. - shipping_address (:class:`telegram.ShippingAddress`): Optional. User shipping address. - - """ - - __slots__ = ('email', 'shipping_address', 'phone_number', 'name', '_id_attrs') - - def __init__( - self, - name: str = None, - phone_number: str = None, - email: str = None, - shipping_address: str = None, - **_kwargs: Any, - ): - self.name = name - self.phone_number = phone_number - self.email = email - self.shipping_address = shipping_address - - self._id_attrs = (self.name, self.phone_number, self.email, self.shipping_address) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['OrderInfo']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return cls() - - data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot) - - return cls(**data) diff --git a/telegramer/include/telegram/payment/precheckoutquery.py b/telegramer/include/telegram/payment/precheckoutquery.py deleted file mode 100644 index 65b5e30..0000000 --- a/telegramer/include/telegram/payment/precheckoutquery.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram PreCheckoutQuery.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import OrderInfo, TelegramObject, User -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot - - -class PreCheckoutQuery(TelegramObject): - """This object contains information about an incoming pre-checkout query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Note: - In Python ``from`` is a reserved word, use ``from_user`` instead. - - Args: - id (:obj:`str`): Unique query identifier. - from_user (:class:`telegram.User`): User who sent the query. - currency (:obj:`str`): Three-letter ISO 4217 currency code. - total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not - float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. - See the :obj:`exp` parameter in - `currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, - it shows the number of digits past the decimal point for each currency - (2 for the majority of currencies). - invoice_payload (:obj:`str`): Bot specified invoice payload. - shipping_option_id (:obj:`str`, optional): Identifier of the shipping option chosen by the - user. - order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - id (:obj:`str`): Unique query identifier. - from_user (:class:`telegram.User`): User who sent the query. - currency (:obj:`str`): Three-letter ISO 4217 currency code. - total_amount (:obj:`int`): Total price in the smallest units of the currency. - invoice_payload (:obj:`str`): Bot specified invoice payload. - shipping_option_id (:obj:`str`): Optional. Identifier of the shipping option chosen by the - user. - order_info (:class:`telegram.OrderInfo`): Optional. Order info provided by the user. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'bot', - 'invoice_payload', - 'shipping_option_id', - 'currency', - 'order_info', - 'total_amount', - 'id', - 'from_user', - '_id_attrs', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - from_user: User, - currency: str, - total_amount: int, - invoice_payload: str, - shipping_option_id: str = None, - order_info: OrderInfo = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - self.id = id # pylint: disable=C0103 - self.from_user = from_user - self.currency = currency - self.total_amount = total_amount - self.invoice_payload = invoice_payload - self.shipping_option_id = shipping_option_id - self.order_info = order_info - - self.bot = bot - - self._id_attrs = (self.id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PreCheckoutQuery']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['from_user'] = User.de_json(data.pop('from'), bot) - data['order_info'] = OrderInfo.de_json(data.get('order_info'), bot) - - return cls(bot=bot, **data) - - def answer( # pylint: disable=C0103 - self, - ok: bool, - error_message: str = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.answer_pre_checkout_query(update.pre_checkout_query.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.answer_pre_checkout_query`. - - """ - return self.bot.answer_pre_checkout_query( - pre_checkout_query_id=self.id, - ok=ok, - error_message=error_message, - timeout=timeout, - api_kwargs=api_kwargs, - ) diff --git a/telegramer/include/telegram/payment/shippingaddress.py b/telegramer/include/telegram/payment/shippingaddress.py deleted file mode 100644 index ae16d9f..0000000 --- a/telegramer/include/telegram/payment/shippingaddress.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ShippingAddress.""" - -from typing import Any - -from telegram import TelegramObject - - -class ShippingAddress(TelegramObject): - """This object represents a Telegram ShippingAddress. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`country_code`, :attr:`state`, :attr:`city`, - :attr:`street_line1`, :attr:`street_line2` and :attr:`post_cod` are equal. - - Args: - country_code (:obj:`str`): ISO 3166-1 alpha-2 country code. - state (:obj:`str`): State, if applicable. - city (:obj:`str`): City. - street_line1 (:obj:`str`): First line for the address. - street_line2 (:obj:`str`): Second line for the address. - post_code (:obj:`str`): Address post code. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - country_code (:obj:`str`): ISO 3166-1 alpha-2 country code. - state (:obj:`str`): State, if applicable. - city (:obj:`str`): City. - street_line1 (:obj:`str`): First line for the address. - street_line2 (:obj:`str`): Second line for the address. - post_code (:obj:`str`): Address post code. - - """ - - __slots__ = ( - 'post_code', - 'city', - '_id_attrs', - 'country_code', - 'street_line2', - 'street_line1', - 'state', - ) - - def __init__( - self, - country_code: str, - state: str, - city: str, - street_line1: str, - street_line2: str, - post_code: str, - **_kwargs: Any, - ): - self.country_code = country_code - self.state = state - self.city = city - self.street_line1 = street_line1 - self.street_line2 = street_line2 - self.post_code = post_code - - self._id_attrs = ( - self.country_code, - self.state, - self.city, - self.street_line1, - self.street_line2, - self.post_code, - ) diff --git a/telegramer/include/telegram/payment/shippingoption.py b/telegramer/include/telegram/payment/shippingoption.py deleted file mode 100644 index 5bca48a..0000000 --- a/telegramer/include/telegram/payment/shippingoption.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ShippingOption.""" - -from typing import TYPE_CHECKING, Any, List - -from telegram import TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import LabeledPrice # noqa - - -class ShippingOption(TelegramObject): - """This object represents one shipping option. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Args: - id (:obj:`str`): Shipping option identifier. - title (:obj:`str`): Option title. - prices (List[:class:`telegram.LabeledPrice`]): List of price portions. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - id (:obj:`str`): Shipping option identifier. - title (:obj:`str`): Option title. - prices (List[:class:`telegram.LabeledPrice`]): List of price portions. - - """ - - __slots__ = ('prices', 'title', 'id', '_id_attrs') - - def __init__( - self, - id: str, # pylint: disable=W0622 - title: str, - prices: List['LabeledPrice'], - **_kwargs: Any, - ): - self.id = id # pylint: disable=C0103 - self.title = title - self.prices = prices - - self._id_attrs = (self.id,) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['prices'] = [p.to_dict() for p in self.prices] - - return data diff --git a/telegramer/include/telegram/payment/shippingquery.py b/telegramer/include/telegram/payment/shippingquery.py deleted file mode 100644 index 2a0d140..0000000 --- a/telegramer/include/telegram/payment/shippingquery.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ShippingQuery.""" - -from typing import TYPE_CHECKING, Any, Optional, List - -from telegram import ShippingAddress, TelegramObject, User, ShippingOption -from telegram.utils.helpers import DEFAULT_NONE -from telegram.utils.types import JSONDict, ODVInput - -if TYPE_CHECKING: - from telegram import Bot - - -class ShippingQuery(TelegramObject): - """This object contains information about an incoming shipping query. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Note: - In Python ``from`` is a reserved word, use ``from_user`` instead. - - Args: - id (:obj:`str`): Unique query identifier. - from_user (:class:`telegram.User`): User who sent the query. - invoice_payload (:obj:`str`): Bot specified invoice payload. - shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - id (:obj:`str`): Unique query identifier. - from_user (:class:`telegram.User`): User who sent the query. - invoice_payload (:obj:`str`): Bot specified invoice payload. - shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ('bot', 'invoice_payload', 'shipping_address', 'id', 'from_user', '_id_attrs') - - def __init__( - self, - id: str, # pylint: disable=W0622 - from_user: User, - invoice_payload: str, - shipping_address: ShippingAddress, - bot: 'Bot' = None, - **_kwargs: Any, - ): - self.id = id # pylint: disable=C0103 - self.from_user = from_user - self.invoice_payload = invoice_payload - self.shipping_address = shipping_address - - self.bot = bot - - self._id_attrs = (self.id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ShippingQuery']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['from_user'] = User.de_json(data.pop('from'), bot) - data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot) - - return cls(bot=bot, **data) - - def answer( # pylint: disable=C0103 - self, - ok: bool, - shipping_options: List[ShippingOption] = None, - error_message: str = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.answer_shipping_query(update.shipping_query.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.answer_shipping_query`. - - """ - return self.bot.answer_shipping_query( - shipping_query_id=self.id, - ok=ok, - shipping_options=shipping_options, - error_message=error_message, - timeout=timeout, - api_kwargs=api_kwargs, - ) diff --git a/telegramer/include/telegram/payment/successfulpayment.py b/telegramer/include/telegram/payment/successfulpayment.py deleted file mode 100644 index 7240fb8..0000000 --- a/telegramer/include/telegram/payment/successfulpayment.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram SuccessfulPayment.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import OrderInfo, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class SuccessfulPayment(TelegramObject): - """This object contains basic information about a successful payment. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`telegram_payment_charge_id` and - :attr:`provider_payment_charge_id` are equal. - - Args: - currency (:obj:`str`): Three-letter ISO 4217 currency code. - total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not - float/double). For example, for a price of US$ 1.45 pass ``amount = 145``. - See the :obj:`exp` parameter in - `currencies.json <https://core.telegram.org/bots/payments/currencies.json>`_, - it shows the number of digits past the decimal point for each currency - (2 for the majority of currencies). - invoice_payload (:obj:`str`): Bot specified invoice payload. - shipping_option_id (:obj:`str`, optional): Identifier of the shipping option chosen by the - user. - order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user. - telegram_payment_charge_id (:obj:`str`): Telegram payment identifier. - provider_payment_charge_id (:obj:`str`): Provider payment identifier. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - currency (:obj:`str`): Three-letter ISO 4217 currency code. - total_amount (:obj:`int`): Total price in the smallest units of the currency. - invoice_payload (:obj:`str`): Bot specified invoice payload. - shipping_option_id (:obj:`str`): Optional. Identifier of the shipping option chosen by the - user. - order_info (:class:`telegram.OrderInfo`): Optional. Order info provided by the user. - telegram_payment_charge_id (:obj:`str`): Telegram payment identifier. - provider_payment_charge_id (:obj:`str`): Provider payment identifier. - - """ - - __slots__ = ( - 'invoice_payload', - 'shipping_option_id', - 'currency', - 'order_info', - 'telegram_payment_charge_id', - 'provider_payment_charge_id', - 'total_amount', - '_id_attrs', - ) - - def __init__( - self, - currency: str, - total_amount: int, - invoice_payload: str, - telegram_payment_charge_id: str, - provider_payment_charge_id: str, - shipping_option_id: str = None, - order_info: OrderInfo = None, - **_kwargs: Any, - ): - self.currency = currency - self.total_amount = total_amount - self.invoice_payload = invoice_payload - self.shipping_option_id = shipping_option_id - self.order_info = order_info - self.telegram_payment_charge_id = telegram_payment_charge_id - self.provider_payment_charge_id = provider_payment_charge_id - - self._id_attrs = (self.telegram_payment_charge_id, self.provider_payment_charge_id) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SuccessfulPayment']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['order_info'] = OrderInfo.de_json(data.get('order_info'), bot) - - return cls(**data) diff --git a/telegramer/include/telegram/poll.py b/telegramer/include/telegram/poll.py deleted file mode 100644 index a7c51b2..0000000 --- a/telegramer/include/telegram/poll.py +++ /dev/null @@ -1,295 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Poll.""" - -import datetime -import sys -from typing import TYPE_CHECKING, Any, Dict, List, Optional, ClassVar - -from telegram import MessageEntity, TelegramObject, User, constants -from telegram.utils.helpers import from_timestamp, to_timestamp -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class PollOption(TelegramObject): - """ - This object contains information about one answer option in a poll. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`text` and :attr:`voter_count` are equal. - - Args: - text (:obj:`str`): Option text, 1-100 characters. - voter_count (:obj:`int`): Number of users that voted for this option. - - Attributes: - text (:obj:`str`): Option text, 1-100 characters. - voter_count (:obj:`int`): Number of users that voted for this option. - - """ - - __slots__ = ('voter_count', 'text', '_id_attrs') - - def __init__(self, text: str, voter_count: int, **_kwargs: Any): - self.text = text - self.voter_count = voter_count - - self._id_attrs = (self.text, self.voter_count) - - MAX_LENGTH: ClassVar[int] = constants.MAX_POLL_OPTION_LENGTH - """:const:`telegram.constants.MAX_POLL_OPTION_LENGTH`""" - - -class PollAnswer(TelegramObject): - """ - This object represents an answer of a user in a non-anonymous poll. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`options_ids` are equal. - - Attributes: - poll_id (:obj:`str`): Unique poll identifier. - user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (List[:obj:`int`]): Identifiers of answer options, chosen by the user. - - Args: - poll_id (:obj:`str`): Unique poll identifier. - user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (List[:obj:`int`]): 0-based identifiers of answer options, chosen by the user. - May be empty if the user retracted their vote. - - """ - - __slots__ = ('option_ids', 'user', 'poll_id', '_id_attrs') - - def __init__(self, poll_id: str, user: User, option_ids: List[int], **_kwargs: Any): - self.poll_id = poll_id - self.user = user - self.option_ids = option_ids - - self._id_attrs = (self.poll_id, self.user, tuple(self.option_ids)) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PollAnswer']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['user'] = User.de_json(data.get('user'), bot) - - return cls(**data) - - -class Poll(TelegramObject): - """ - This object contains information about a poll. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Attributes: - id (:obj:`str`): Unique poll identifier. - question (:obj:`str`): Poll question, 1-300 characters. - options (List[:class:`PollOption`]): List of poll options. - total_voter_count (:obj:`int`): Total number of users that voted in the poll. - is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. - is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. - type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. - allows_multiple_answers (:obj:`bool`): :obj:`True`, if the poll allows multiple answers. - correct_option_id (:obj:`int`): Optional. Identifier of the correct answer option. - explanation (:obj:`str`): Optional. Text that is shown when a user chooses an incorrect - answer or taps on the lamp icon in a quiz-style poll. - explanation_entities (List[:class:`telegram.MessageEntity`]): Optional. Special entities - like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. - open_period (:obj:`int`): Optional. Amount of time in seconds the poll will be active - after creation. - close_date (:obj:`datetime.datetime`): Optional. Point in time when the poll will be - automatically closed. - - Args: - id (:obj:`str`): Unique poll identifier. - question (:obj:`str`): Poll question, 1-300 characters. - options (List[:class:`PollOption`]): List of poll options. - is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. - is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. - type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. - allows_multiple_answers (:obj:`bool`): :obj:`True`, if the poll allows multiple answers. - correct_option_id (:obj:`int`, optional): 0-based identifier of the correct answer option. - Available only for polls in the quiz mode, which are closed, or was sent (not - forwarded) by the bot or to the private chat with the bot. - explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect - answer or taps on the lamp icon in a quiz-style poll, 0-200 characters. - explanation_entities (List[:class:`telegram.MessageEntity`], optional): Special entities - like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. - open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active - after creation. - close_date (:obj:`datetime.datetime`, optional): Point in time (Unix timestamp) when the - poll will be automatically closed. Converted to :obj:`datetime.datetime`. - - """ - - __slots__ = ( - 'total_voter_count', - 'allows_multiple_answers', - 'open_period', - 'options', - 'type', - 'explanation_entities', - 'is_anonymous', - 'close_date', - 'is_closed', - 'id', - 'explanation', - 'question', - 'correct_option_id', - '_id_attrs', - ) - - def __init__( - self, - id: str, # pylint: disable=W0622 - question: str, - options: List[PollOption], - total_voter_count: int, - is_closed: bool, - is_anonymous: bool, - type: str, # pylint: disable=W0622 - allows_multiple_answers: bool, - correct_option_id: int = None, - explanation: str = None, - explanation_entities: List[MessageEntity] = None, - open_period: int = None, - close_date: datetime.datetime = None, - **_kwargs: Any, - ): - self.id = id # pylint: disable=C0103 - self.question = question - self.options = options - self.total_voter_count = total_voter_count - self.is_closed = is_closed - self.is_anonymous = is_anonymous - self.type = type - self.allows_multiple_answers = allows_multiple_answers - self.correct_option_id = correct_option_id - self.explanation = explanation - self.explanation_entities = explanation_entities - self.open_period = open_period - self.close_date = close_date - - self._id_attrs = (self.id,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Poll']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['options'] = [PollOption.de_json(option, bot) for option in data['options']] - data['explanation_entities'] = MessageEntity.de_list(data.get('explanation_entities'), bot) - data['close_date'] = from_timestamp(data.get('close_date')) - - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['options'] = [x.to_dict() for x in self.options] - if self.explanation_entities: - data['explanation_entities'] = [e.to_dict() for e in self.explanation_entities] - data['close_date'] = to_timestamp(data.get('close_date')) - - return data - - def parse_explanation_entity(self, entity: MessageEntity) -> str: - """Returns the text from a given :class:`telegram.MessageEntity`. - - Note: - This method is present because Telegram calculates the offset and length in - UTF-16 codepoint pairs, which some versions of Python don't handle automatically. - (That is, you can't just slice ``Message.text`` with the offset and length.) - - Args: - entity (:class:`telegram.MessageEntity`): The entity to extract the text from. It must - be an entity that belongs to this message. - - Returns: - :obj:`str`: The text of the given entity. - - Raises: - RuntimeError: If the poll has no explanation. - - """ - if not self.explanation: - raise RuntimeError("This Poll has no 'explanation'.") - - # Is it a narrow build, if so we don't need to convert - if sys.maxunicode == 0xFFFF: - return self.explanation[entity.offset : entity.offset + entity.length] - entity_text = self.explanation.encode('utf-16-le') - entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - - return entity_text.decode('utf-16-le') - - def parse_explanation_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: - """ - Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. - It contains entities from this polls explanation filtered by their ``type`` attribute as - the key, and the text that each entity belongs to as the value of the :obj:`dict`. - - Note: - This method should always be used instead of the :attr:`explanation_entities` - attribute, since it calculates the correct substring from the message text based on - UTF-16 codepoints. See :attr:`parse_explanation_entity` for more info. - - Args: - types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the - ``type`` attribute of an entity is contained in this list, it will be returned. - Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. - - Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to - the text that belongs to them, calculated based on UTF-16 codepoints. - - """ - if types is None: - types = MessageEntity.ALL_TYPES - - return { - entity: self.parse_explanation_entity(entity) - for entity in (self.explanation_entities or []) - if entity.type in types - } - - REGULAR: ClassVar[str] = constants.POLL_REGULAR - """:const:`telegram.constants.POLL_REGULAR`""" - QUIZ: ClassVar[str] = constants.POLL_QUIZ - """:const:`telegram.constants.POLL_QUIZ`""" - MAX_QUESTION_LENGTH: ClassVar[int] = constants.MAX_POLL_QUESTION_LENGTH - """:const:`telegram.constants.MAX_POLL_QUESTION_LENGTH`""" - MAX_OPTION_LENGTH: ClassVar[int] = constants.MAX_POLL_OPTION_LENGTH - """:const:`telegram.constants.MAX_POLL_OPTION_LENGTH`""" diff --git a/telegramer/include/telegram/proximityalerttriggered.py b/telegramer/include/telegram/proximityalerttriggered.py deleted file mode 100644 index df4cb55..0000000 --- a/telegramer/include/telegram/proximityalerttriggered.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Proximity Alert.""" -from typing import Any, Optional, TYPE_CHECKING - -from telegram import TelegramObject, User -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class ProximityAlertTriggered(TelegramObject): - """ - This object represents the content of a service message, sent whenever a user in the chat - triggers a proximity alert set by another user. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`traveler`, :attr:`watcher` and :attr:`distance` are equal. - - Args: - traveler (:class:`telegram.User`): User that triggered the alert - watcher (:class:`telegram.User`): User that set the alert - distance (:obj:`int`): The distance between the users - - Attributes: - traveler (:class:`telegram.User`): User that triggered the alert - watcher (:class:`telegram.User`): User that set the alert - distance (:obj:`int`): The distance between the users - - """ - - __slots__ = ('traveler', 'distance', 'watcher', '_id_attrs') - - def __init__(self, traveler: User, watcher: User, distance: int, **_kwargs: Any): - self.traveler = traveler - self.watcher = watcher - self.distance = distance - - self._id_attrs = (self.traveler, self.watcher, self.distance) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ProximityAlertTriggered']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['traveler'] = User.de_json(data.get('traveler'), bot) - data['watcher'] = User.de_json(data.get('watcher'), bot) - - return cls(bot=bot, **data) diff --git a/telegramer/include/telegram/py.typed b/telegramer/include/telegram/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/replykeyboardmarkup.py b/telegramer/include/telegram/replykeyboardmarkup.py deleted file mode 100644 index cc6f985..0000000 --- a/telegramer/include/telegram/replykeyboardmarkup.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ReplyKeyboardMarkup.""" - -from typing import Any, List, Union, Sequence - -from telegram import KeyboardButton, ReplyMarkup -from telegram.utils.types import JSONDict - - -class ReplyKeyboardMarkup(ReplyMarkup): - """This object represents a custom keyboard with reply options. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their the size of :attr:`keyboard` and all the buttons are equal. - - Example: - A user requests to change the bot's language, bot replies to the request with a keyboard - to select the new language. Other users in the group don't see the keyboard. - - Args: - keyboard (List[List[:obj:`str` | :class:`telegram.KeyboardButton`]]): Array of button rows, - each represented by an Array of :class:`telegram.KeyboardButton` objects. - resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard vertically - for optimal fit (e.g., make the keyboard smaller if there are just two rows of - buttons). Defaults to :obj:`False`, in which case the custom keyboard is always of the - same height as the app's standard keyboard. - one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as soon as - it's been used. The keyboard will still be available, but clients will automatically - display the usual letter-keyboard in the chat - the user can press a special button in - the input field to see the custom keyboard again. Defaults to :obj:`False`. - selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard to - specific users only. Targets: - - 1) Users that are @mentioned in the :attr:`~telegram.Message.text` of the - :class:`telegram.Message` object. - 2) If the bot's message is a reply (has ``reply_to_message_id``), sender of the - original message. - - Defaults to :obj:`False`. - - input_field_placeholder (:obj:`str`, optional): The placeholder to be shown in the input - field when the keyboard is active; 1-64 characters. - - .. versionadded:: 13.7 - - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - keyboard (List[List[:class:`telegram.KeyboardButton` | :obj:`str`]]): Array of button rows. - resize_keyboard (:obj:`bool`): Optional. Requests clients to resize the keyboard. - one_time_keyboard (:obj:`bool`): Optional. Requests clients to hide the keyboard as soon as - it's been used. - selective (:obj:`bool`): Optional. Show the keyboard to specific users only. - input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input - field when the reply is active. - - .. versionadded:: 13.7 - - """ - - __slots__ = ( - 'selective', - 'keyboard', - 'resize_keyboard', - 'one_time_keyboard', - 'input_field_placeholder', - '_id_attrs', - ) - - def __init__( - self, - keyboard: Sequence[Sequence[Union[str, KeyboardButton]]], - resize_keyboard: bool = False, - one_time_keyboard: bool = False, - selective: bool = False, - input_field_placeholder: str = None, - **_kwargs: Any, - ): - # Required - self.keyboard = [] - for row in keyboard: - button_row = [] - for button in row: - if isinstance(button, KeyboardButton): - button_row.append(button) # telegram.KeyboardButton - else: - button_row.append(KeyboardButton(button)) # str - self.keyboard.append(button_row) - - # Optionals - self.resize_keyboard = bool(resize_keyboard) - self.one_time_keyboard = bool(one_time_keyboard) - self.selective = bool(selective) - self.input_field_placeholder = input_field_placeholder - - self._id_attrs = (self.keyboard,) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['keyboard'] = [] - for row in self.keyboard: - data['keyboard'].append([button.to_dict() for button in row]) - return data - - @classmethod - def from_button( - cls, - button: Union[KeyboardButton, str], - resize_keyboard: bool = False, - one_time_keyboard: bool = False, - selective: bool = False, - input_field_placeholder: str = None, - **kwargs: object, - ) -> 'ReplyKeyboardMarkup': - """Shortcut for:: - - ReplyKeyboardMarkup([[button]], **kwargs) - - Return a ReplyKeyboardMarkup from a single KeyboardButton. - - Args: - button (:class:`telegram.KeyboardButton` | :obj:`str`): The button to use in - the markup. - resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard - vertically for optimal fit (e.g., make the keyboard smaller if there are just two - rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is - always of the same height as the app's standard keyboard. - one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as - soon as it's been used. The keyboard will still be available, but clients will - automatically display the usual letter-keyboard in the chat - the user can press - a special button in the input field to see the custom keyboard again. - Defaults to :obj:`False`. - selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard - to specific users only. Targets: - - 1) Users that are @mentioned in the text of the Message object. - 2) If the bot's message is a reply (has ``reply_to_message_id``), sender of the - original message. - - Defaults to :obj:`False`. - - input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input - field when the reply is active. - - .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - """ - return cls( - [[button]], - resize_keyboard=resize_keyboard, - one_time_keyboard=one_time_keyboard, - selective=selective, - input_field_placeholder=input_field_placeholder, - **kwargs, - ) - - @classmethod - def from_row( - cls, - button_row: List[Union[str, KeyboardButton]], - resize_keyboard: bool = False, - one_time_keyboard: bool = False, - selective: bool = False, - input_field_placeholder: str = None, - **kwargs: object, - ) -> 'ReplyKeyboardMarkup': - """Shortcut for:: - - ReplyKeyboardMarkup([button_row], **kwargs) - - Return a ReplyKeyboardMarkup from a single row of KeyboardButtons. - - Args: - button_row (List[:class:`telegram.KeyboardButton` | :obj:`str`]): The button to use in - the markup. - resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard - vertically for optimal fit (e.g., make the keyboard smaller if there are just two - rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is - always of the same height as the app's standard keyboard. - one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as - soon as it's been used. The keyboard will still be available, but clients will - automatically display the usual letter-keyboard in the chat - the user can press - a special button in the input field to see the custom keyboard again. - Defaults to :obj:`False`. - selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard - to specific users only. Targets: - - 1) Users that are @mentioned in the text of the Message object. - 2) If the bot's message is a reply (has ``reply_to_message_id``), sender of the - original message. - - Defaults to :obj:`False`. - - input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input - field when the reply is active. - - .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - """ - return cls( - [button_row], - resize_keyboard=resize_keyboard, - one_time_keyboard=one_time_keyboard, - selective=selective, - input_field_placeholder=input_field_placeholder, - **kwargs, - ) - - @classmethod - def from_column( - cls, - button_column: List[Union[str, KeyboardButton]], - resize_keyboard: bool = False, - one_time_keyboard: bool = False, - selective: bool = False, - input_field_placeholder: str = None, - **kwargs: object, - ) -> 'ReplyKeyboardMarkup': - """Shortcut for:: - - ReplyKeyboardMarkup([[button] for button in button_column], **kwargs) - - Return a ReplyKeyboardMarkup from a single column of KeyboardButtons. - - Args: - button_column (List[:class:`telegram.KeyboardButton` | :obj:`str`]): The button to use - in the markup. - resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard - vertically for optimal fit (e.g., make the keyboard smaller if there are just two - rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is - always of the same height as the app's standard keyboard. - one_time_keyboard (:obj:`bool`, optional): Requests clients to hide the keyboard as - soon as it's been used. The keyboard will still be available, but clients will - automatically display the usual letter-keyboard in the chat - the user can press - a special button in the input field to see the custom keyboard again. - Defaults to :obj:`False`. - selective (:obj:`bool`, optional): Use this parameter if you want to show the keyboard - to specific users only. Targets: - - 1) Users that are @mentioned in the text of the Message object. - 2) If the bot's message is a reply (has ``reply_to_message_id``), sender of the - original message. - - Defaults to :obj:`False`. - - input_field_placeholder (:obj:`str`): Optional. The placeholder shown in the input - field when the reply is active. - - .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - """ - button_grid = [[button] for button in button_column] - return cls( - button_grid, - resize_keyboard=resize_keyboard, - one_time_keyboard=one_time_keyboard, - selective=selective, - input_field_placeholder=input_field_placeholder, - **kwargs, - ) - - def __hash__(self) -> int: - return hash( - ( - tuple(tuple(button for button in row) for row in self.keyboard), - self.resize_keyboard, - self.one_time_keyboard, - self.selective, - ) - ) diff --git a/telegramer/include/telegram/replykeyboardremove.py b/telegramer/include/telegram/replykeyboardremove.py deleted file mode 100644 index 2762141..0000000 --- a/telegramer/include/telegram/replykeyboardremove.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram ReplyKeyboardRemove.""" -from typing import Any - -from telegram import ReplyMarkup - - -class ReplyKeyboardRemove(ReplyMarkup): - """ - Upon receiving a message with this object, Telegram clients will remove the current custom - keyboard and display the default letter-keyboard. By default, custom keyboards are displayed - until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are - hidden immediately after the user presses a button (see :class:`telegram.ReplyKeyboardMarkup`). - - Example: - A user votes in a poll, bot returns confirmation message in reply to the vote and removes - the keyboard for that user, while still showing the keyboard with poll options to users who - haven't voted yet. - - Note: - User will not be able to summon this keyboard; if you want to hide the keyboard from - sight but keep it accessible, use :attr:`telegram.ReplyKeyboardMarkup.one_time_keyboard`. - - Args: - selective (:obj:`bool`, optional): Use this parameter if you want to remove the keyboard - for specific users only. Targets: - - 1) Users that are @mentioned in the text of the :class:`telegram.Message` object. - 2) If the bot's message is a reply (has `reply_to_message_id`), sender of the original - message. - - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - remove_keyboard (:obj:`True`): Requests clients to remove the custom keyboard. - selective (:obj:`bool`): Optional. Use this parameter if you want to remove the keyboard - for specific users only. - - """ - - __slots__ = ('selective', 'remove_keyboard') - - def __init__(self, selective: bool = False, **_kwargs: Any): - # Required - self.remove_keyboard = True - # Optionals - self.selective = bool(selective) diff --git a/telegramer/include/telegram/replymarkup.py b/telegramer/include/telegram/replymarkup.py deleted file mode 100644 index 081a73a..0000000 --- a/telegramer/include/telegram/replymarkup.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""Base class for Telegram ReplyMarkup Objects.""" - -from telegram import TelegramObject - - -class ReplyMarkup(TelegramObject): - """Base class for Telegram ReplyMarkup Objects. - - See :class:`telegram.InlineKeyboardMarkup`, :class:`telegram.ReplyKeyboardMarkup`, - :class:`telegram.ReplyKeyboardRemove` and :class:`telegram.ForceReply` for - detailed use. - - """ - - __slots__ = () diff --git a/telegramer/include/telegram/update.py b/telegramer/include/telegram/update.py deleted file mode 100644 index 7c7b210..0000000 --- a/telegramer/include/telegram/update.py +++ /dev/null @@ -1,416 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram Update.""" - -from typing import TYPE_CHECKING, Any, Optional - -from telegram import ( - CallbackQuery, - ChosenInlineResult, - InlineQuery, - Message, - Poll, - PreCheckoutQuery, - ShippingQuery, - TelegramObject, - ChatMemberUpdated, - constants, - ChatJoinRequest, -) -from telegram.poll import PollAnswer -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot, Chat, User # noqa - - -class Update(TelegramObject): - """This object represents an incoming update. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`update_id` is equal. - - Note: - At most one of the optional parameters can be present in any given update. - - Args: - update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a - certain positive number and increase sequentially. This ID becomes especially handy if - you're using Webhooks, since it allows you to ignore repeated updates or to restore the - correct update sequence, should they get out of order. If there are no new updates for - at least a week, then identifier of the next update will be chosen randomly instead of - sequentially. - message (:class:`telegram.Message`, optional): New incoming message of any kind - text, - photo, sticker, etc. - edited_message (:class:`telegram.Message`, optional): New version of a message that is - known to the bot and was edited. - channel_post (:class:`telegram.Message`, optional): New incoming channel post of any kind - - text, photo, sticker, etc. - edited_channel_post (:class:`telegram.Message`, optional): New version of a channel post - that is known to the bot and was edited. - inline_query (:class:`telegram.InlineQuery`, optional): New incoming inline query. - chosen_inline_result (:class:`telegram.ChosenInlineResult`, optional): The result of an - inline query that was chosen by a user and sent to their chat partner. - callback_query (:class:`telegram.CallbackQuery`, optional): New incoming callback query. - shipping_query (:class:`telegram.ShippingQuery`, optional): New incoming shipping query. - Only for invoices with flexible price. - pre_checkout_query (:class:`telegram.PreCheckoutQuery`, optional): New incoming - pre-checkout query. Contains full information about checkout. - poll (:class:`telegram.Poll`, optional): New poll state. Bots receive only updates about - stopped polls and polls, which are sent by the bot. - poll_answer (:class:`telegram.PollAnswer`, optional): A user changed their answer - in a non-anonymous poll. Bots receive new votes only in polls that were sent - by the bot itself. - my_chat_member (:class:`telegram.ChatMemberUpdated`, optional): The bot's chat member - status was updated in a chat. For private chats, this update is received only when the - bot is blocked or unblocked by the user. - - .. versionadded:: 13.4 - chat_member (:class:`telegram.ChatMemberUpdated`, optional): A chat member's status was - updated in a chat. The bot must be an administrator in the chat and must explicitly - specify ``'chat_member'`` in the list of ``'allowed_updates'`` to receive these - updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, - :meth:`telegram.ext.Updater.start_polling` and - :meth:`telegram.ext.Updater.start_webhook`). - - .. versionadded:: 13.4 - chat_join_request (:class:`telegram.ChatJoinRequest`, optional): A request to join the - chat has been sent. The bot must have the - :attr:`telegram.ChatPermissions.can_invite_users` administrator right in the chat to - receive these updates. - - .. versionadded:: 13.8 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - update_id (:obj:`int`): The update's unique identifier. - message (:class:`telegram.Message`): Optional. New incoming message. - edited_message (:class:`telegram.Message`): Optional. New version of a message. - channel_post (:class:`telegram.Message`): Optional. New incoming channel post. - edited_channel_post (:class:`telegram.Message`): Optional. New version of a channel post. - inline_query (:class:`telegram.InlineQuery`): Optional. New incoming inline query. - chosen_inline_result (:class:`telegram.ChosenInlineResult`): Optional. The result of an - inline query that was chosen by a user. - callback_query (:class:`telegram.CallbackQuery`): Optional. New incoming callback query. - shipping_query (:class:`telegram.ShippingQuery`): Optional. New incoming shipping query. - pre_checkout_query (:class:`telegram.PreCheckoutQuery`): Optional. New incoming - pre-checkout query. - poll (:class:`telegram.Poll`): Optional. New poll state. Bots receive only updates - about stopped polls and polls, which are sent by the bot. - poll_answer (:class:`telegram.PollAnswer`): Optional. A user changed their answer - in a non-anonymous poll. Bots receive new votes only in polls that were sent - by the bot itself. - my_chat_member (:class:`telegram.ChatMemberUpdated`): Optional. The bot's chat member - status was updated in a chat. For private chats, this update is received only when the - bot is blocked or unblocked by the user. - - .. versionadded:: 13.4 - chat_member (:class:`telegram.ChatMemberUpdated`): Optional. A chat member's status was - updated in a chat. The bot must be an administrator in the chat and must explicitly - specify ``'chat_member'`` in the list of ``'allowed_updates'`` to receive these - updates (see :meth:`telegram.Bot.get_updates`, :meth:`telegram.Bot.set_webhook`, - :meth:`telegram.ext.Updater.start_polling` and - :meth:`telegram.ext.Updater.start_webhook`). - - .. versionadded:: 13.4 - chat_join_request (:class:`telegram.ChatJoinRequest`): Optional. A request to join the - chat has been sent. The bot must have the ``'can_invite_users'`` administrator - right in the chat to receive these updates. - - .. versionadded:: 13.8 - - """ - - __slots__ = ( - 'callback_query', - 'chosen_inline_result', - 'pre_checkout_query', - 'inline_query', - 'update_id', - 'message', - 'shipping_query', - 'poll', - 'poll_answer', - 'channel_post', - 'edited_channel_post', - 'edited_message', - '_effective_user', - '_effective_chat', - '_effective_message', - 'my_chat_member', - 'chat_member', - 'chat_join_request', - '_id_attrs', - ) - - MESSAGE = constants.UPDATE_MESSAGE - """:const:`telegram.constants.UPDATE_MESSAGE` - - .. versionadded:: 13.5""" - EDITED_MESSAGE = constants.UPDATE_EDITED_MESSAGE - """:const:`telegram.constants.UPDATE_EDITED_MESSAGE` - - .. versionadded:: 13.5""" - CHANNEL_POST = constants.UPDATE_CHANNEL_POST - """:const:`telegram.constants.UPDATE_CHANNEL_POST` - - .. versionadded:: 13.5""" - EDITED_CHANNEL_POST = constants.UPDATE_EDITED_CHANNEL_POST - """:const:`telegram.constants.UPDATE_EDITED_CHANNEL_POST` - - .. versionadded:: 13.5""" - INLINE_QUERY = constants.UPDATE_INLINE_QUERY - """:const:`telegram.constants.UPDATE_INLINE_QUERY` - - .. versionadded:: 13.5""" - CHOSEN_INLINE_RESULT = constants.UPDATE_CHOSEN_INLINE_RESULT - """:const:`telegram.constants.UPDATE_CHOSEN_INLINE_RESULT` - - .. versionadded:: 13.5""" - CALLBACK_QUERY = constants.UPDATE_CALLBACK_QUERY - """:const:`telegram.constants.UPDATE_CALLBACK_QUERY` - - .. versionadded:: 13.5""" - SHIPPING_QUERY = constants.UPDATE_SHIPPING_QUERY - """:const:`telegram.constants.UPDATE_SHIPPING_QUERY` - - .. versionadded:: 13.5""" - PRE_CHECKOUT_QUERY = constants.UPDATE_PRE_CHECKOUT_QUERY - """:const:`telegram.constants.UPDATE_PRE_CHECKOUT_QUERY` - - .. versionadded:: 13.5""" - POLL = constants.UPDATE_POLL - """:const:`telegram.constants.UPDATE_POLL` - - .. versionadded:: 13.5""" - POLL_ANSWER = constants.UPDATE_POLL_ANSWER - """:const:`telegram.constants.UPDATE_POLL_ANSWER` - - .. versionadded:: 13.5""" - MY_CHAT_MEMBER = constants.UPDATE_MY_CHAT_MEMBER - """:const:`telegram.constants.UPDATE_MY_CHAT_MEMBER` - - .. versionadded:: 13.5""" - CHAT_MEMBER = constants.UPDATE_CHAT_MEMBER - """:const:`telegram.constants.UPDATE_CHAT_MEMBER` - - .. versionadded:: 13.5""" - CHAT_JOIN_REQUEST = constants.UPDATE_CHAT_JOIN_REQUEST - """:const:`telegram.constants.UPDATE_CHAT_JOIN_REQUEST` - - .. versionadded:: 13.8""" - ALL_TYPES = constants.UPDATE_ALL_TYPES - """:const:`telegram.constants.UPDATE_ALL_TYPES` - - .. versionadded:: 13.5""" - - def __init__( - self, - update_id: int, - message: Message = None, - edited_message: Message = None, - channel_post: Message = None, - edited_channel_post: Message = None, - inline_query: InlineQuery = None, - chosen_inline_result: ChosenInlineResult = None, - callback_query: CallbackQuery = None, - shipping_query: ShippingQuery = None, - pre_checkout_query: PreCheckoutQuery = None, - poll: Poll = None, - poll_answer: PollAnswer = None, - my_chat_member: ChatMemberUpdated = None, - chat_member: ChatMemberUpdated = None, - chat_join_request: ChatJoinRequest = None, - **_kwargs: Any, - ): - # Required - self.update_id = int(update_id) - # Optionals - self.message = message - self.edited_message = edited_message - self.inline_query = inline_query - self.chosen_inline_result = chosen_inline_result - self.callback_query = callback_query - self.shipping_query = shipping_query - self.pre_checkout_query = pre_checkout_query - self.channel_post = channel_post - self.edited_channel_post = edited_channel_post - self.poll = poll - self.poll_answer = poll_answer - self.my_chat_member = my_chat_member - self.chat_member = chat_member - self.chat_join_request = chat_join_request - - self._effective_user: Optional['User'] = None - self._effective_chat: Optional['Chat'] = None - self._effective_message: Optional[Message] = None - - self._id_attrs = (self.update_id,) - - @property - def effective_user(self) -> Optional['User']: - """ - :class:`telegram.User`: The user that sent this update, no matter what kind of update this - is. Will be :obj:`None` for :attr:`channel_post` and :attr:`poll`. - - """ - if self._effective_user: - return self._effective_user - - user = None - - if self.message: - user = self.message.from_user - - elif self.edited_message: - user = self.edited_message.from_user - - elif self.inline_query: - user = self.inline_query.from_user - - elif self.chosen_inline_result: - user = self.chosen_inline_result.from_user - - elif self.callback_query: - user = self.callback_query.from_user - - elif self.shipping_query: - user = self.shipping_query.from_user - - elif self.pre_checkout_query: - user = self.pre_checkout_query.from_user - - elif self.poll_answer: - user = self.poll_answer.user - - elif self.my_chat_member: - user = self.my_chat_member.from_user - - elif self.chat_member: - user = self.chat_member.from_user - - elif self.chat_join_request: - user = self.chat_join_request.from_user - - self._effective_user = user - return user - - @property - def effective_chat(self) -> Optional['Chat']: - """ - :class:`telegram.Chat`: The chat that this update was sent in, no matter what kind of - update this is. Will be :obj:`None` for :attr:`inline_query`, - :attr:`chosen_inline_result`, :attr:`callback_query` from inline messages, - :attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll` and - :attr:`poll_answer`. - - """ - if self._effective_chat: - return self._effective_chat - - chat = None - - if self.message: - chat = self.message.chat - - elif self.edited_message: - chat = self.edited_message.chat - - elif self.callback_query and self.callback_query.message: - chat = self.callback_query.message.chat - - elif self.channel_post: - chat = self.channel_post.chat - - elif self.edited_channel_post: - chat = self.edited_channel_post.chat - - elif self.my_chat_member: - chat = self.my_chat_member.chat - - elif self.chat_member: - chat = self.chat_member.chat - - elif self.chat_join_request: - chat = self.chat_join_request.chat - - self._effective_chat = chat - return chat - - @property - def effective_message(self) -> Optional[Message]: - """ - :class:`telegram.Message`: The message included in this update, no matter what kind of - update this is. Will be :obj:`None` for :attr:`inline_query`, - :attr:`chosen_inline_result`, :attr:`callback_query` from inline messages, - :attr:`shipping_query`, :attr:`pre_checkout_query`, :attr:`poll`, - :attr:`poll_answer`, :attr:`my_chat_member`, :attr:`chat_member` as well as - :attr:`chat_join_request` in case the bot is missing the - :attr:`telegram.ChatPermissions.can_invite_users` administrator right in the chat. - - """ - if self._effective_message: - return self._effective_message - - message = None - - if self.message: - message = self.message - - elif self.edited_message: - message = self.edited_message - - elif self.callback_query: - message = self.callback_query.message - - elif self.channel_post: - message = self.channel_post - - elif self.edited_channel_post: - message = self.edited_channel_post - - self._effective_message = message - return message - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Update']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['message'] = Message.de_json(data.get('message'), bot) - data['edited_message'] = Message.de_json(data.get('edited_message'), bot) - data['inline_query'] = InlineQuery.de_json(data.get('inline_query'), bot) - data['chosen_inline_result'] = ChosenInlineResult.de_json( - data.get('chosen_inline_result'), bot - ) - data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'), bot) - data['shipping_query'] = ShippingQuery.de_json(data.get('shipping_query'), bot) - data['pre_checkout_query'] = PreCheckoutQuery.de_json(data.get('pre_checkout_query'), bot) - data['channel_post'] = Message.de_json(data.get('channel_post'), bot) - data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot) - data['poll'] = Poll.de_json(data.get('poll'), bot) - data['poll_answer'] = PollAnswer.de_json(data.get('poll_answer'), bot) - data['my_chat_member'] = ChatMemberUpdated.de_json(data.get('my_chat_member'), bot) - data['chat_member'] = ChatMemberUpdated.de_json(data.get('chat_member'), bot) - data['chat_join_request'] = ChatJoinRequest.de_json(data.get('chat_join_request'), bot) - - return cls(**data) diff --git a/telegramer/include/telegram/user.py b/telegramer/include/telegram/user.py deleted file mode 100644 index 0a50895..0000000 --- a/telegramer/include/telegram/user.py +++ /dev/null @@ -1,1243 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=W0622 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram User.""" -from datetime import datetime -from typing import TYPE_CHECKING, Any, List, Optional, Union, Tuple - -from telegram import TelegramObject, constants -from telegram.inline.inlinekeyboardbutton import InlineKeyboardButton -from telegram.utils.helpers import ( - mention_html as util_mention_html, - DEFAULT_NONE, - DEFAULT_20, -) -from telegram.utils.helpers import mention_markdown as util_mention_markdown -from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput - -if TYPE_CHECKING: - from telegram import ( - Bot, - Message, - UserProfilePhotos, - MessageId, - InputMediaAudio, - InputMediaDocument, - InputMediaPhoto, - InputMediaVideo, - MessageEntity, - ReplyMarkup, - PhotoSize, - Audio, - Contact, - Document, - InlineKeyboardMarkup, - LabeledPrice, - Location, - Animation, - Sticker, - Video, - Venue, - VideoNote, - Voice, - ) - - -class User(TelegramObject): - """This object represents a Telegram user or bot. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`id` is equal. - - Args: - id (:obj:`int`): Unique identifier for this user or bot. - is_bot (:obj:`bool`): :obj:`True`, if this user is a bot. - first_name (:obj:`str`): User's or bots first name. - last_name (:obj:`str`, optional): User's or bots last name. - username (:obj:`str`, optional): User's or bots username. - language_code (:obj:`str`, optional): IETF language tag of the user's language. - can_join_groups (:obj:`str`, optional): :obj:`True`, if the bot can be invited to groups. - Returned only in :attr:`telegram.Bot.get_me` requests. - can_read_all_group_messages (:obj:`str`, optional): :obj:`True`, if privacy mode is - disabled for the bot. Returned only in :attr:`telegram.Bot.get_me` requests. - supports_inline_queries (:obj:`str`, optional): :obj:`True`, if the bot supports inline - queries. Returned only in :attr:`telegram.Bot.get_me` requests. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - - Attributes: - id (:obj:`int`): Unique identifier for this user or bot. - is_bot (:obj:`bool`): :obj:`True`, if this user is a bot. - first_name (:obj:`str`): User's or bot's first name. - last_name (:obj:`str`): Optional. User's or bot's last name. - username (:obj:`str`): Optional. User's or bot's username. - language_code (:obj:`str`): Optional. IETF language tag of the user's language. - can_join_groups (:obj:`str`): Optional. :obj:`True`, if the bot can be invited to groups. - Returned only in :attr:`telegram.Bot.get_me` requests. - can_read_all_group_messages (:obj:`str`): Optional. :obj:`True`, if privacy mode is - disabled for the bot. Returned only in :attr:`telegram.Bot.get_me` requests. - supports_inline_queries (:obj:`str`): Optional. :obj:`True`, if the bot supports inline - queries. Returned only in :attr:`telegram.Bot.get_me` requests. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. - - """ - - __slots__ = ( - 'is_bot', - 'can_read_all_group_messages', - 'username', - 'first_name', - 'last_name', - 'can_join_groups', - 'supports_inline_queries', - 'id', - 'bot', - 'language_code', - '_id_attrs', - ) - - def __init__( - self, - id: int, - first_name: str, - is_bot: bool, - last_name: str = None, - username: str = None, - language_code: str = None, - can_join_groups: bool = None, - can_read_all_group_messages: bool = None, - supports_inline_queries: bool = None, - bot: 'Bot' = None, - **_kwargs: Any, - ): - # Required - self.id = int(id) # pylint: disable=C0103 - self.first_name = first_name - self.is_bot = is_bot - # Optionals - self.last_name = last_name - self.username = username - self.language_code = language_code - self.can_join_groups = can_join_groups - self.can_read_all_group_messages = can_read_all_group_messages - self.supports_inline_queries = supports_inline_queries - self.bot = bot - - self._id_attrs = (self.id,) - - @property - def name(self) -> str: - """:obj:`str`: Convenience property. If available, returns the user's :attr:`username` - prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`. - """ - if self.username: - return f'@{self.username}' - return self.full_name - - @property - def full_name(self) -> str: - """:obj:`str`: Convenience property. The user's :attr:`first_name`, followed by (if - available) :attr:`last_name`. - """ - if self.last_name: - return f'{self.first_name} {self.last_name}' - return self.first_name - - @property - def link(self) -> Optional[str]: - """:obj:`str`: Convenience property. If :attr:`username` is available, returns a t.me link - of the user. - """ - if self.username: - return f"https://t.me/{self.username}" - return None - - def get_profile_photos( - self, - offset: int = None, - limit: int = 100, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> Optional['UserProfilePhotos']: - """ - Shortcut for:: - - bot.get_user_profile_photos(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.get_user_profile_photos`. - - """ - return self.bot.get_user_profile_photos( - user_id=self.id, - offset=offset, - limit=limit, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def mention_markdown(self, name: str = None) -> str: - """ - Note: - :attr:`telegram.ParseMode.MARKDOWN` is a legacy mode, retained by Telegram for - backward compatibility. You should use :meth:`mention_markdown_v2` instead. - - Args: - name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. - - Returns: - :obj:`str`: The inline mention for the user as markdown (version 1). - - """ - if name: - return util_mention_markdown(self.id, name) - return util_mention_markdown(self.id, self.full_name) - - def mention_markdown_v2(self, name: str = None) -> str: - """ - Args: - name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. - - Returns: - :obj:`str`: The inline mention for the user as markdown (version 2). - - """ - if name: - return util_mention_markdown(self.id, name, version=2) - return util_mention_markdown(self.id, self.full_name, version=2) - - def mention_html(self, name: str = None) -> str: - """ - Args: - name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. - - Returns: - :obj:`str`: The inline mention for the user as HTML. - - """ - if name: - return util_mention_html(self.id, name) - return util_mention_html(self.id, self.full_name) - - def mention_button(self, name: str = None) -> InlineKeyboardButton: - """ - Shortcut for:: - - InlineKeyboardButton(text=name, url=f"tg://user?id={update.effective_user.id}") - - .. versionadded:: 13.9 - - Args: - name (:obj:`str`): The name used as a link for the user. Defaults to :attr:`full_name`. - - Returns: - :class:`telegram.InlineKeyboardButton`: InlineButton with url set to the user mention - """ - return InlineKeyboardButton(text=name or self.full_name, url=f"tg://user?id={self.id}") - - def pin_message( - self, - message_id: int, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.pin_chat_message(chat_id=update.effective_user.id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.pin_chat_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.pin_chat_message( - chat_id=self.id, - message_id=message_id, - disable_notification=disable_notification, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def unpin_message( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - message_id: int = None, - ) -> bool: - """Shortcut for:: - - bot.unpin_chat_message(chat_id=update.effective_user.id, - *args, - **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.unpin_chat_message`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unpin_chat_message( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - message_id=message_id, - ) - - def unpin_all_messages( - self, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.unpin_all_chat_messages(chat_id=update.effective_user.id, - *args, - **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.unpin_all_chat_messages`. - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.unpin_all_chat_messages( - chat_id=self.id, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - def send_message( - self, - text: str, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_message(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_message`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_message( - chat_id=self.id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - entities=entities, - protect_content=protect_content, - ) - - def send_photo( - self, - photo: Union[FileInput, 'PhotoSize'], - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_photo(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_photo`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_photo( - chat_id=self.id, - photo=photo, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_media_group( - self, - media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] - ], - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> List['Message']: - """Shortcut for:: - - bot.send_media_group(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. - - Returns: - List[:class:`telegram.Message`:] On success, instance representing the message posted. - - """ - return self.bot.send_media_group( - chat_id=self.id, - media=media, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_audio( - self, - audio: Union[FileInput, 'Audio'], - duration: int = None, - performer: str = None, - title: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_audio(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_audio`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_audio( - chat_id=self.id, - audio=audio, - duration=duration, - performer=performer, - title=title, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_chat_action( - self, - action: str, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.send_chat_action(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_chat_action`. - - Returns: - :obj:`True`: On success. - - """ - return self.bot.send_chat_action( - chat_id=self.id, - action=action, - timeout=timeout, - api_kwargs=api_kwargs, - ) - - send_action = send_chat_action - """Alias for :attr:`send_chat_action`""" - - def send_contact( - self, - phone_number: str = None, - first_name: str = None, - last_name: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - contact: 'Contact' = None, - vcard: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_contact(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_contact`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_contact( - chat_id=self.id, - phone_number=phone_number, - first_name=first_name, - last_name=last_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - contact=contact, - vcard=vcard, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_dice( - self, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - emoji: str = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_dice(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_dice`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_dice( - chat_id=self.id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - emoji=emoji, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_document( - self, - document: Union[FileInput, 'Document'], - filename: str = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - disable_content_type_detection: bool = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_document(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_document`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_document( - chat_id=self.id, - document=document, - filename=filename, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - thumb=thumb, - api_kwargs=api_kwargs, - disable_content_type_detection=disable_content_type_detection, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - protect_content=protect_content, - ) - - def send_game( - self, - game_short_name: str, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_game(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_game`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_game( - chat_id=self.id, - game_short_name=game_short_name, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_invoice( - self, - title: str, - description: str, - payload: str, - provider_token: str, - currency: str, - prices: List['LabeledPrice'], - start_parameter: str = None, - photo_url: str = None, - photo_size: int = None, - photo_width: int = None, - photo_height: int = None, - need_name: bool = None, - need_phone_number: bool = None, - need_email: bool = None, - need_shipping_address: bool = None, - is_flexible: bool = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, - provider_data: Union[str, object] = None, - send_phone_number_to_provider: bool = None, - send_email_to_provider: bool = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - max_tip_amount: int = None, - suggested_tip_amounts: List[int] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_invoice(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_invoice`. - - Warning: - As of API 5.2 :attr:`start_parameter` is an optional argument and therefore the order - of the arguments had to be changed. Use keyword arguments to make sure that the - arguments are passed correctly. - - .. versionchanged:: 13.5 - As of Bot API 5.2, the parameter :attr:`start_parameter` is optional. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_invoice( - chat_id=self.id, - title=title, - description=description, - payload=payload, - provider_token=provider_token, - currency=currency, - prices=prices, - start_parameter=start_parameter, - photo_url=photo_url, - photo_size=photo_size, - photo_width=photo_width, - photo_height=photo_height, - need_name=need_name, - need_phone_number=need_phone_number, - need_email=need_email, - need_shipping_address=need_shipping_address, - is_flexible=is_flexible, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - provider_data=provider_data, - send_phone_number_to_provider=send_phone_number_to_provider, - send_email_to_provider=send_email_to_provider, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - max_tip_amount=max_tip_amount, - suggested_tip_amounts=suggested_tip_amounts, - protect_content=protect_content, - ) - - def send_location( - self, - latitude: float = None, - longitude: float = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - location: 'Location' = None, - live_period: int = None, - api_kwargs: JSONDict = None, - horizontal_accuracy: float = None, - heading: int = None, - proximity_alert_radius: int = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_location(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_location`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_location( - chat_id=self.id, - latitude=latitude, - longitude=longitude, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - location=location, - live_period=live_period, - api_kwargs=api_kwargs, - horizontal_accuracy=horizontal_accuracy, - heading=heading, - proximity_alert_radius=proximity_alert_radius, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_animation( - self, - animation: Union[FileInput, 'Animation'], - duration: int = None, - width: int = None, - height: int = None, - thumb: FileInput = None, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_animation(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_animation`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_animation( - chat_id=self.id, - animation=animation, - duration=duration, - width=width, - height=height, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_sticker( - self, - sticker: Union[FileInput, 'Sticker'], - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_sticker(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_sticker`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_sticker( - chat_id=self.id, - sticker=sticker, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_video( - self, - video: Union[FileInput, 'Video'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - width: int = None, - height: int = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - supports_streaming: bool = None, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_video(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_video`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_video( - chat_id=self.id, - video=video, - duration=duration, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - width=width, - height=height, - parse_mode=parse_mode, - supports_streaming=supports_streaming, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_venue( - self, - latitude: float = None, - longitude: float = None, - title: str = None, - address: str = None, - foursquare_id: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - venue: 'Venue' = None, - foursquare_type: str = None, - api_kwargs: JSONDict = None, - google_place_id: str = None, - google_place_type: str = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_venue(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_venue`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_venue( - chat_id=self.id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - venue=venue, - foursquare_type=foursquare_type, - api_kwargs=api_kwargs, - google_place_id=google_place_id, - google_place_type=google_place_type, - allow_sending_without_reply=allow_sending_without_reply, - protect_content=protect_content, - ) - - def send_video_note( - self, - video_note: Union[FileInput, 'VideoNote'], - duration: int = None, - length: int = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - thumb: FileInput = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_video_note(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_video_note`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_video_note( - chat_id=self.id, - video_note=video_note, - duration=duration, - length=length, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - thumb=thumb, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - filename=filename, - protect_content=protect_content, - ) - - def send_voice( - self, - voice: Union[FileInput, 'Voice'], - duration: int = None, - caption: str = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: DVInput[float] = DEFAULT_20, - parse_mode: ODVInput[str] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - filename: str = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_voice(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_voice`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_voice( - chat_id=self.id, - voice=voice, - duration=duration, - caption=caption, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - parse_mode=parse_mode, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - caption_entities=caption_entities, - filename=filename, - protect_content=protect_content, - ) - - def send_poll( - self, - question: str, - options: List[str], - is_anonymous: bool = True, - # We use constant.POLL_REGULAR instead of Poll.REGULAR here to avoid circular imports - type: str = constants.POLL_REGULAR, # pylint: disable=W0622 - allows_multiple_answers: bool = False, - correct_option_id: int = None, - is_closed: bool = None, - disable_notification: ODVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - explanation: str = None, - explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, - open_period: int = None, - close_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - protect_content: bool = None, - ) -> 'Message': - """Shortcut for:: - - bot.send_poll(update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.send_poll`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.send_poll( - chat_id=self.id, - question=question, - options=options, - is_anonymous=is_anonymous, - type=type, # pylint=pylint, - allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, - is_closed=is_closed, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - reply_markup=reply_markup, - timeout=timeout, - explanation=explanation, - explanation_parse_mode=explanation_parse_mode, - open_period=open_period, - close_date=close_date, - api_kwargs=api_kwargs, - allow_sending_without_reply=allow_sending_without_reply, - explanation_entities=explanation_entities, - protect_content=protect_content, - ) - - def send_copy( - self, - from_chat_id: Union[str, int], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - bot.copy_message(chat_id=update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.copy_message( - chat_id=self.id, - from_chat_id=from_chat_id, - message_id=message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def copy_message( - self, - chat_id: Union[int, str], - message_id: int, - caption: str = None, - parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, - disable_notification: DVInput[bool] = DEFAULT_NONE, - reply_to_message_id: int = None, - allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - protect_content: bool = None, - ) -> 'MessageId': - """Shortcut for:: - - bot.copy_message(from_chat_id=update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see :meth:`telegram.Bot.copy_message`. - - Returns: - :class:`telegram.Message`: On success, instance representing the message posted. - - """ - return self.bot.copy_message( - from_chat_id=self.id, - chat_id=chat_id, - message_id=message_id, - caption=caption, - parse_mode=parse_mode, - caption_entities=caption_entities, - disable_notification=disable_notification, - reply_to_message_id=reply_to_message_id, - allow_sending_without_reply=allow_sending_without_reply, - reply_markup=reply_markup, - timeout=timeout, - api_kwargs=api_kwargs, - protect_content=protect_content, - ) - - def approve_join_request( - self, - chat_id: Union[int, str], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.approve_chat_join_request(user_id=update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.approve_chat_join_request`. - - .. versionadded:: 13.8 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.approve_chat_join_request( - user_id=self.id, chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs - ) - - def decline_join_request( - self, - chat_id: Union[int, str], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> bool: - """Shortcut for:: - - bot.decline_chat_join_request(user_id=update.effective_user.id, *args, **kwargs) - - For the documentation of the arguments, please see - :meth:`telegram.Bot.decline_chat_join_request`. - - .. versionadded:: 13.8 - - Returns: - :obj:`bool`: On success, :obj:`True` is returned. - - """ - return self.bot.decline_chat_join_request( - user_id=self.id, chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs - ) diff --git a/telegramer/include/telegram/userprofilephotos.py b/telegramer/include/telegram/userprofilephotos.py deleted file mode 100644 index aa3e6ec..0000000 --- a/telegramer/include/telegram/userprofilephotos.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram UserProfilePhotos.""" - -from typing import TYPE_CHECKING, Any, List, Optional - -from telegram import PhotoSize, TelegramObject -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class UserProfilePhotos(TelegramObject): - """This object represent a user's profile pictures. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`total_count` and :attr:`photos` are equal. - - Args: - total_count (:obj:`int`): Total number of profile pictures the target user has. - photos (List[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 - sizes each). - - Attributes: - total_count (:obj:`int`): Total number of profile pictures. - photos (List[List[:class:`telegram.PhotoSize`]]): Requested profile pictures. - - """ - - __slots__ = ('photos', 'total_count', '_id_attrs') - - def __init__(self, total_count: int, photos: List[List[PhotoSize]], **_kwargs: Any): - # Required - self.total_count = int(total_count) - self.photos = photos - - self._id_attrs = (self.total_count, self.photos) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['UserProfilePhotos']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['photos'] = [PhotoSize.de_list(photo, bot) for photo in data['photos']] - - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data['photos'] = [] - for photo in self.photos: - data['photos'].append([x.to_dict() for x in photo]) - - return data - - def __hash__(self) -> int: - return hash(tuple(tuple(p for p in photo) for photo in self.photos)) diff --git a/telegramer/include/telegram/utils/__init__.py b/telegramer/include/telegram/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/utils/deprecate.py b/telegramer/include/telegram/utils/deprecate.py deleted file mode 100644 index bc9f519..0000000 --- a/telegramer/include/telegram/utils/deprecate.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module facilitates the deprecation of functions.""" - -import warnings - - -# We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it -# seem like it's the user that issued the warning -# We name it something else so that you don't get confused when you attempt to suppress it -class TelegramDeprecationWarning(Warning): - """Custom warning class for deprecations in this library.""" - - __slots__ = () - - -# Function to warn users that setting custom attributes is deprecated (Use only in __setattr__!) -# Checks if a custom attribute is added by checking length of dictionary before & after -# assigning attribute. This is the fastest way to do it (I hope!). -def set_new_attribute_deprecated(self: object, key: str, value: object) -> None: - """Warns the user if they set custom attributes on PTB objects.""" - org = len(self.__dict__) - object.__setattr__(self, key, value) - new = len(self.__dict__) - if new > org: - warnings.warn( - f"Setting custom attributes such as {key!r} on objects such as " - f"{self.__class__.__name__!r} of the PTB library is deprecated.", - TelegramDeprecationWarning, - stacklevel=3, - ) diff --git a/telegramer/include/telegram/utils/helpers.py b/telegramer/include/telegram/utils/helpers.py deleted file mode 100644 index 194a461..0000000 --- a/telegramer/include/telegram/utils/helpers.py +++ /dev/null @@ -1,596 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains helper functions.""" - -import datetime as dtm # dtm = "DateTime Module" -import re -import signal -import time - -from collections import defaultdict -from html import escape -from pathlib import Path - -from typing import ( - TYPE_CHECKING, - Any, - DefaultDict, - Dict, - Optional, - Tuple, - Union, - Type, - cast, - IO, - TypeVar, - Generic, - overload, -) - -from telegram.utils.types import JSONDict, FileInput - -if TYPE_CHECKING: - from telegram import Message, Update, TelegramObject, InputFile - -# in PTB-Raw we don't have pytz, so we make a little workaround here -DTM_UTC = dtm.timezone.utc -try: - import pytz - - UTC = pytz.utc -except ImportError: - UTC = DTM_UTC # type: ignore[assignment] - -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] - - -# From https://stackoverflow.com/questions/2549939/get-signal-names-from-numbers-in-python -_signames = { - v: k - for k, v in reversed(sorted(vars(signal).items())) - if k.startswith('SIG') and not k.startswith('SIG_') -} - - -def get_signal_name(signum: int) -> str: - """Returns the signal name of the given signal number.""" - return _signames[signum] - - -def is_local_file(obj: Optional[Union[str, Path]]) -> bool: - """ - Checks if a given string is a file on local system. - - Args: - obj (:obj:`str`): The string to check. - """ - if obj is None: - return False - - path = Path(obj) - try: - return path.is_file() - except Exception: - return False - - -def parse_file_input( - file_input: Union[FileInput, 'TelegramObject'], - tg_type: Type['TelegramObject'] = None, - attach: bool = None, - filename: str = None, -) -> Union[str, 'InputFile', Any]: - """ - Parses input for sending files: - - * For string input, if the input is an absolute path of a local file, - adds the ``file://`` prefix. If the input is a relative path of a local file, computes the - absolute path and adds the ``file://`` prefix. Returns the input unchanged, otherwise. - * :class:`pathlib.Path` objects are treated the same way as strings. - * For IO and bytes input, returns an :class:`telegram.InputFile`. - * If :attr:`tg_type` is specified and the input is of that type, returns the ``file_id`` - attribute. - - Args: - file_input (:obj:`str` | :obj:`bytes` | `filelike object` | Telegram media object): The - input to parse. - tg_type (:obj:`type`, optional): The Telegram media type the input can be. E.g. - :class:`telegram.Animation`. - attach (:obj:`bool`, optional): Whether this file should be send as one file or is part of - a collection of files. Only relevant in case an :class:`telegram.InputFile` is - returned. - filename (:obj:`str`, optional): The filename. Only relevant in case an - :class:`telegram.InputFile` is returned. - - Returns: - :obj:`str` | :class:`telegram.InputFile` | :obj:`object`: The parsed input or the untouched - :attr:`file_input`, in case it's no valid file input. - """ - # Importing on file-level yields cyclic Import Errors - from telegram import InputFile # pylint: disable=C0415 - - if isinstance(file_input, str) and file_input.startswith('file://'): - return file_input - if isinstance(file_input, (str, Path)): - if is_local_file(file_input): - out = Path(file_input).absolute().as_uri() - else: - out = file_input # type: ignore[assignment] - return out - if isinstance(file_input, bytes): - return InputFile(file_input, attach=attach, filename=filename) - if InputFile.is_file(file_input): - file_input = cast(IO, file_input) - return InputFile(file_input, attach=attach, filename=filename) - if tg_type and isinstance(file_input, tg_type): - return file_input.file_id # type: ignore[attr-defined] - return file_input - - -def escape_markdown(text: str, version: int = 1, entity_type: str = None) -> str: - """ - Helper function to escape telegram markup symbols. - - Args: - text (:obj:`str`): The text. - version (:obj:`int` | :obj:`str`): Use to specify the version of telegrams Markdown. - Either ``1`` or ``2``. Defaults to ``1``. - entity_type (:obj:`str`, optional): For the entity types ``PRE``, ``CODE`` and the link - part of ``TEXT_LINKS``, only certain characters need to be escaped in ``MarkdownV2``. - See the official API documentation for details. Only valid in combination with - ``version=2``, will be ignored else. - """ - if int(version) == 1: - escape_chars = r'_*`[' - elif int(version) == 2: - if entity_type in ['pre', 'code']: - escape_chars = r'\`' - elif entity_type == 'text_link': - escape_chars = r'\)' - else: - escape_chars = r'_*[]()~`>#+-=|{}.!' - else: - raise ValueError('Markdown version must be either 1 or 2!') - - return re.sub(f'([{re.escape(escape_chars)}])', r'\\\1', text) - - -# -------- date/time related helpers -------- -def _datetime_to_float_timestamp(dt_obj: dtm.datetime) -> float: - """ - Converts a datetime object to a float timestamp (with sub-second precision). - If the datetime object is timezone-naive, it is assumed to be in UTC. - """ - if dt_obj.tzinfo is None: - dt_obj = dt_obj.replace(tzinfo=dtm.timezone.utc) - return dt_obj.timestamp() - - -def _localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime: - """Localize the datetime, where UTC is handled depending on whether pytz is available or not""" - if tzinfo is DTM_UTC: - return datetime.replace(tzinfo=DTM_UTC) - return tzinfo.localize(datetime) # type: ignore[attr-defined] - - -def to_float_timestamp( - time_object: Union[int, float, dtm.timedelta, dtm.datetime, dtm.time], - reference_timestamp: float = None, - tzinfo: dtm.tzinfo = None, -) -> float: - """ - Converts a given time object to a float POSIX timestamp. - Used to convert different time specifications to a common format. The time object - can be relative (i.e. indicate a time increment, or a time of day) or absolute. - object objects from the :class:`datetime` module that are timezone-naive will be assumed - to be in UTC, if ``bot`` is not passed or ``bot.defaults`` is :obj:`None`. - - Args: - time_object (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \ - :obj:`datetime.datetime` | :obj:`datetime.time`): - Time value to convert. The semantics of this parameter will depend on its type: - - * :obj:`int` or :obj:`float` will be interpreted as "seconds from ``reference_t``" - * :obj:`datetime.timedelta` will be interpreted as - "time increment from ``reference_t``" - * :obj:`datetime.datetime` will be interpreted as an absolute date/time value - * :obj:`datetime.time` will be interpreted as a specific time of day - - reference_timestamp (:obj:`float`, optional): POSIX timestamp that indicates the absolute - time from which relative calculations are to be performed (e.g. when ``t`` is given as - an :obj:`int`, indicating "seconds from ``reference_t``"). Defaults to now (the time at - which this function is called). - - If ``t`` is given as an absolute representation of date & time (i.e. a - :obj:`datetime.datetime` object), ``reference_timestamp`` is not relevant and so its - value should be :obj:`None`. If this is not the case, a ``ValueError`` will be raised. - tzinfo (:obj:`pytz.BaseTzInfo`, optional): If ``t`` is a naive object from the - :class:`datetime` module, it will be interpreted as this timezone. Defaults to - ``pytz.utc``. - - Note: - Only to be used by ``telegram.ext``. - - - Returns: - :obj:`float` | :obj:`None`: - The return value depends on the type of argument ``t``. - If ``t`` is given as a time increment (i.e. as a :obj:`int`, :obj:`float` or - :obj:`datetime.timedelta`), then the return value will be ``reference_t`` + ``t``. - - Else if it is given as an absolute date/time value (i.e. a :obj:`datetime.datetime` - object), the equivalent value as a POSIX timestamp will be returned. - - Finally, if it is a time of the day without date (i.e. a :obj:`datetime.time` - object), the return value is the nearest future occurrence of that time of day. - - Raises: - TypeError: If ``t``'s type is not one of those described above. - ValueError: If ``t`` is a :obj:`datetime.datetime` and :obj:`reference_timestamp` is not - :obj:`None`. - """ - if reference_timestamp is None: - reference_timestamp = time.time() - elif isinstance(time_object, dtm.datetime): - raise ValueError('t is an (absolute) datetime while reference_timestamp is not None') - - if isinstance(time_object, dtm.timedelta): - return reference_timestamp + time_object.total_seconds() - if isinstance(time_object, (int, float)): - return reference_timestamp + time_object - - if tzinfo is None: - tzinfo = UTC - - if isinstance(time_object, dtm.time): - reference_dt = dtm.datetime.fromtimestamp( - reference_timestamp, tz=time_object.tzinfo or tzinfo - ) - reference_date = reference_dt.date() - reference_time = reference_dt.timetz() - - aware_datetime = dtm.datetime.combine(reference_date, time_object) - if aware_datetime.tzinfo is None: - aware_datetime = _localize(aware_datetime, tzinfo) - - # if the time of day has passed today, use tomorrow - if reference_time > aware_datetime.timetz(): - aware_datetime += dtm.timedelta(days=1) - return _datetime_to_float_timestamp(aware_datetime) - if isinstance(time_object, dtm.datetime): - if time_object.tzinfo is None: - time_object = _localize(time_object, tzinfo) - return _datetime_to_float_timestamp(time_object) - - raise TypeError(f'Unable to convert {type(time_object).__name__} object to timestamp') - - -def to_timestamp( - dt_obj: Union[int, float, dtm.timedelta, dtm.datetime, dtm.time, None], - reference_timestamp: float = None, - tzinfo: dtm.tzinfo = None, -) -> Optional[int]: - """ - Wrapper over :func:`to_float_timestamp` which returns an integer (the float value truncated - down to the nearest integer). - - See the documentation for :func:`to_float_timestamp` for more details. - """ - return ( - int(to_float_timestamp(dt_obj, reference_timestamp, tzinfo)) - if dt_obj is not None - else None - ) - - -def from_timestamp(unixtime: Optional[int], tzinfo: dtm.tzinfo = UTC) -> Optional[dtm.datetime]: - """ - Converts an (integer) unix timestamp to a timezone aware datetime object. - :obj:`None` s are left alone (i.e. ``from_timestamp(None)`` is :obj:`None`). - - Args: - unixtime (:obj:`int`): Integer POSIX timestamp. - tzinfo (:obj:`datetime.tzinfo`, optional): The timezone to which the timestamp is to be - converted to. Defaults to UTC. - - Returns: - Timezone aware equivalent :obj:`datetime.datetime` value if ``unixtime`` is not - :obj:`None`; else :obj:`None`. - """ - if unixtime is None: - return None - - if tzinfo is not None: - return dtm.datetime.fromtimestamp(unixtime, tz=tzinfo) - return dtm.datetime.utcfromtimestamp(unixtime) - - -# -------- end -------- - - -def mention_html(user_id: Union[int, str], name: str) -> str: - """ - Args: - user_id (:obj:`int`): The user's id which you want to mention. - name (:obj:`str`): The name the mention is showing. - - Returns: - :obj:`str`: The inline mention for the user as HTML. - """ - return f'<a href="tg://user?id={user_id}">{escape(name)}</a>' - - -def mention_markdown(user_id: Union[int, str], name: str, version: int = 1) -> str: - """ - Args: - user_id (:obj:`int`): The user's id which you want to mention. - name (:obj:`str`): The name the mention is showing. - version (:obj:`int` | :obj:`str`): Use to specify the version of Telegram's Markdown. - Either ``1`` or ``2``. Defaults to ``1``. - - Returns: - :obj:`str`: The inline mention for the user as Markdown. - """ - return f'[{escape_markdown(name, version=version)}](tg://user?id={user_id})' - - -def effective_message_type(entity: Union['Message', 'Update']) -> Optional[str]: - """ - Extracts the type of message as a string identifier from a :class:`telegram.Message` or a - :class:`telegram.Update`. - - Args: - entity (:class:`telegram.Update` | :class:`telegram.Message`): The ``update`` or - ``message`` to extract from. - - Returns: - :obj:`str`: One of ``Message.MESSAGE_TYPES`` - - """ - # Importing on file-level yields cyclic Import Errors - from telegram import Message, Update # pylint: disable=C0415 - - if isinstance(entity, Message): - message = entity - elif isinstance(entity, Update): - message = entity.effective_message # type: ignore[assignment] - else: - raise TypeError(f"entity is not Message or Update (got: {type(entity)})") - - for i in Message.MESSAGE_TYPES: - if getattr(message, i, None): - return i - - return None - - -def create_deep_linked_url(bot_username: str, payload: str = None, group: bool = False) -> str: - """ - Creates a deep-linked URL for this ``bot_username`` with the specified ``payload``. - See https://core.telegram.org/bots#deep-linking to learn more. - - The ``payload`` may consist of the following characters: ``A-Z, a-z, 0-9, _, -`` - - Note: - Works well in conjunction with - ``CommandHandler("start", callback, filters = Filters.regex('payload'))`` - - Examples: - ``create_deep_linked_url(bot.get_me().username, "some-params")`` - - Args: - bot_username (:obj:`str`): The username to link to - payload (:obj:`str`, optional): Parameters to encode in the created URL - group (:obj:`bool`, optional): If :obj:`True` the user is prompted to select a group to - add the bot to. If :obj:`False`, opens a one-on-one conversation with the bot. - Defaults to :obj:`False`. - - Returns: - :obj:`str`: An URL to start the bot with specific parameters - """ - if bot_username is None or len(bot_username) <= 3: - raise ValueError("You must provide a valid bot_username.") - - base_url = f'https://t.me/{bot_username}' - if not payload: - return base_url - - if len(payload) > 64: - raise ValueError("The deep-linking payload must not exceed 64 characters.") - - if not re.match(r'^[A-Za-z0-9_-]+$', payload): - raise ValueError( - "Only the following characters are allowed for deep-linked " - "URLs: A-Z, a-z, 0-9, _ and -" - ) - - if group: - key = 'startgroup' - else: - key = 'start' - - return f'{base_url}?{key}={payload}' - - -def encode_conversations_to_json(conversations: Dict[str, Dict[Tuple, object]]) -> str: - """Helper method to encode a conversations dict (that uses tuples as keys) to a - JSON-serializable way. Use :meth:`decode_conversations_from_json` to decode. - - Args: - conversations (:obj:`dict`): The conversations dict to transform to JSON. - - Returns: - :obj:`str`: The JSON-serialized conversations dict - """ - tmp: Dict[str, JSONDict] = {} - for handler, states in conversations.items(): - tmp[handler] = {} - for key, state in states.items(): - tmp[handler][json.dumps(key)] = state - return json.dumps(tmp) - - -def decode_conversations_from_json(json_string: str) -> Dict[str, Dict[Tuple, object]]: - """Helper method to decode a conversations dict (that uses tuples as keys) from a - JSON-string created with :meth:`encode_conversations_to_json`. - - Args: - json_string (:obj:`str`): The conversations dict as JSON string. - - Returns: - :obj:`dict`: The conversations dict after decoding - """ - tmp = json.loads(json_string) - conversations: Dict[str, Dict[Tuple, object]] = {} - for handler, states in tmp.items(): - conversations[handler] = {} - for key, state in states.items(): - conversations[handler][tuple(json.loads(key))] = state - return conversations - - -def decode_user_chat_data_from_json(data: str) -> DefaultDict[int, Dict[object, object]]: - """Helper method to decode chat or user data (that uses ints as keys) from a - JSON-string. - - Args: - data (:obj:`str`): The user/chat_data dict as JSON string. - - Returns: - :obj:`dict`: The user/chat_data defaultdict after decoding - """ - tmp: DefaultDict[int, Dict[object, object]] = defaultdict(dict) - decoded_data = json.loads(data) - for user, user_data in decoded_data.items(): - user = int(user) - tmp[user] = {} - for key, value in user_data.items(): - try: - key = int(key) - except ValueError: - pass - tmp[user][key] = value - return tmp - - -DVType = TypeVar('DVType', bound=object) -OT = TypeVar('OT', bound=object) - - -class DefaultValue(Generic[DVType]): - """Wrapper for immutable default arguments that allows to check, if the default value was set - explicitly. Usage:: - - DefaultOne = DefaultValue(1) - def f(arg=DefaultOne): - if arg is DefaultOne: - print('`arg` is the default') - arg = arg.value - else: - print('`arg` was set explicitly') - print(f'`arg` = {str(arg)}') - - This yields:: - - >>> f() - `arg` is the default - `arg` = 1 - >>> f(1) - `arg` was set explicitly - `arg` = 1 - >>> f(2) - `arg` was set explicitly - `arg` = 2 - - Also allows to evaluate truthiness:: - - default = DefaultValue(value) - if default: - ... - - is equivalent to:: - - default = DefaultValue(value) - if value: - ... - - ``repr(DefaultValue(value))`` returns ``repr(value)`` and ``str(DefaultValue(value))`` returns - ``f'DefaultValue({value})'``. - - Args: - value (:obj:`obj`): The value of the default argument - - Attributes: - value (:obj:`obj`): The value of the default argument - - """ - - __slots__ = ('value', '__dict__') - - def __init__(self, value: DVType = None): - self.value = value - - def __bool__(self) -> bool: - return bool(self.value) - - @overload - @staticmethod - def get_value(obj: 'DefaultValue[OT]') -> OT: - ... - - @overload - @staticmethod - def get_value(obj: OT) -> OT: - ... - - @staticmethod - def get_value(obj: Union[OT, 'DefaultValue[OT]']) -> OT: - """ - Shortcut for:: - - return obj.value if isinstance(obj, DefaultValue) else obj - - Args: - obj (:obj:`object`): The object to process - - Returns: - Same type as input, or the value of the input: The value - """ - return obj.value if isinstance(obj, DefaultValue) else obj # type: ignore[return-value] - - # This is mostly here for readability during debugging - def __str__(self) -> str: - return f'DefaultValue({self.value})' - - # This is here to have the default instances nicely rendered in the docs - def __repr__(self) -> str: - return repr(self.value) - - -DEFAULT_NONE: DefaultValue = DefaultValue(None) -""":class:`DefaultValue`: Default :obj:`None`""" - -DEFAULT_FALSE: DefaultValue = DefaultValue(False) -""":class:`DefaultValue`: Default :obj:`False`""" - -DEFAULT_20: DefaultValue = DefaultValue(20) -""":class:`DefaultValue`: Default :obj:`20`""" diff --git a/telegramer/include/telegram/utils/promise.py b/telegramer/include/telegram/utils/promise.py deleted file mode 100644 index 01bef33..0000000 --- a/telegramer/include/telegram/utils/promise.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the :class:`telegram.ext.utils.promise.Promise` class for backwards -compatibility. -""" -import warnings - -import telegram.ext.utils.promise as promise -from telegram.utils.deprecate import TelegramDeprecationWarning - -warnings.warn( - 'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.', - TelegramDeprecationWarning, -) - -Promise = promise.Promise -""" -:class:`telegram.ext.utils.promise.Promise` - -.. deprecated:: v13.2 - Use :class:`telegram.ext.utils.promise.Promise` instead. -""" diff --git a/telegramer/include/telegram/utils/request.py b/telegramer/include/telegram/utils/request.py deleted file mode 100644 index 4b8de7b..0000000 --- a/telegramer/include/telegram/utils/request.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains methods to make POST and GET requests.""" -import logging -import os -import socket -import sys -import warnings - -try: - import ujson as json -except ImportError: - import json # type: ignore[no-redef] - -from typing import Any, Union - -import certifi - -try: - import telegram.vendor.ptb_urllib3.urllib3 as urllib3 - import telegram.vendor.ptb_urllib3.urllib3.contrib.appengine as appengine - from telegram.vendor.ptb_urllib3.urllib3.connection import HTTPConnection - from telegram.vendor.ptb_urllib3.urllib3.fields import RequestField - from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout -except ImportError: # pragma: no cover - try: - import urllib3 # type: ignore[no-redef] - import urllib3.contrib.appengine as appengine # type: ignore[no-redef] - from urllib3.connection import HTTPConnection # type: ignore[no-redef] - from urllib3.fields import RequestField # type: ignore[no-redef] - from urllib3.util.timeout import Timeout # type: ignore[no-redef] - - warnings.warn( - 'python-telegram-bot is using upstream urllib3. This is allowed but not ' - 'supported by python-telegram-bot maintainers.' - ) - except ImportError: - warnings.warn( - "python-telegram-bot wasn't properly installed. Please refer to README.rst on " - "how to properly install." - ) - raise - -# pylint: disable=C0412 -from telegram import InputFile, TelegramError -from telegram.error import ( - BadRequest, - ChatMigrated, - Conflict, - InvalidToken, - NetworkError, - RetryAfter, - TimedOut, - Unauthorized, -) -from telegram.utils.types import JSONDict -from telegram.utils.deprecate import set_new_attribute_deprecated - - -def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: disable=W0613 - r""" - Monkey patch urllib3.urllib3.fields.RequestField to make it *not* support RFC2231 compliant - Content-Disposition headers since telegram servers don't understand it. Instead just escape - \\ and " and replace any \n and \r with a space. - """ - value = value.replace('\\', '\\\\').replace('"', '\\"') - value = value.replace('\r', ' ').replace('\n', ' ') - return f'{name}="{value}"' - - -RequestField._render_part = _render_part # type: ignore # pylint: disable=W0212 - -logging.getLogger('telegram.vendor.ptb_urllib3.urllib3').setLevel(logging.WARNING) - -USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)' - - -class Request: - """ - Helper class for python-telegram-bot which provides methods to perform POST & GET towards - Telegram servers. - - Args: - con_pool_size (:obj:`int`): Number of connections to keep in the connection pool. - proxy_url (:obj:`str`): The URL to the proxy server. For example: `http://127.0.0.1:3128`. - urllib3_proxy_kwargs (:obj:`dict`): Arbitrary arguments passed as-is to - :obj:`urllib3.ProxyManager`. This value will be ignored if :attr:`proxy_url` is not - set. - connect_timeout (:obj:`int` | :obj:`float`): The maximum amount of time (in seconds) to - wait for a connection attempt to a server to succeed. :obj:`None` will set an - infinite timeout for connection attempts. Defaults to ``5.0``. - read_timeout (:obj:`int` | :obj:`float`): The maximum amount of time (in seconds) to wait - between consecutive read operations for a response from the server. :obj:`None` will - set an infinite timeout. This value is usually overridden by the various - :class:`telegram.Bot` methods. Defaults to ``5.0``. - - """ - - __slots__ = ('_connect_timeout', '_con_pool_size', '_con_pool', '__dict__') - - def __init__( - self, - con_pool_size: int = 1, - proxy_url: str = None, - urllib3_proxy_kwargs: JSONDict = None, - connect_timeout: float = 5.0, - read_timeout: float = 5.0, - ): - if urllib3_proxy_kwargs is None: - urllib3_proxy_kwargs = {} - - self._connect_timeout = connect_timeout - - sockopts = HTTPConnection.default_socket_options + [ - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - ] - - # TODO: Support other platforms like mac and windows. - if 'linux' in sys.platform: - sockopts.append( - (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 120) # pylint: disable=no-member - ) - sockopts.append( - (socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30) # pylint: disable=no-member - ) - sockopts.append( - (socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 8) # pylint: disable=no-member - ) - - self._con_pool_size = con_pool_size - - kwargs = dict( - maxsize=con_pool_size, - cert_reqs='CERT_REQUIRED', - ca_certs=certifi.where(), - socket_options=sockopts, - timeout=urllib3.Timeout(connect=self._connect_timeout, read=read_timeout, total=None), - ) - - # Set a proxy according to the following order: - # * proxy defined in proxy_url (+ urllib3_proxy_kwargs) - # * proxy set in `HTTPS_PROXY` env. var. - # * proxy set in `https_proxy` env. var. - # * None (if no proxy is configured) - - if not proxy_url: - proxy_url = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy') - - self._con_pool: Union[ - urllib3.PoolManager, - appengine.AppEngineManager, - 'SOCKSProxyManager', # noqa: F821 - urllib3.ProxyManager, - ] = None # type: ignore - if not proxy_url: - if appengine.is_appengine_sandbox(): - # Use URLFetch service if running in App Engine - self._con_pool = appengine.AppEngineManager() - else: - self._con_pool = urllib3.PoolManager(**kwargs) - else: - kwargs.update(urllib3_proxy_kwargs) - if proxy_url.startswith('socks'): - try: - # pylint: disable=C0415 - from telegram.vendor.ptb_urllib3.urllib3.contrib.socks import SOCKSProxyManager - except ImportError as exc: - raise RuntimeError('PySocks is missing') from exc - self._con_pool = SOCKSProxyManager(proxy_url, **kwargs) - else: - mgr = urllib3.proxy_from_url(proxy_url, **kwargs) - if mgr.proxy.auth: - # TODO: what about other auth types? - auth_hdrs = urllib3.make_headers(proxy_basic_auth=mgr.proxy.auth) - mgr.proxy_headers.update(auth_hdrs) - - self._con_pool = mgr - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - @property - def con_pool_size(self) -> int: - """The size of the connection pool used.""" - return self._con_pool_size - - def stop(self) -> None: - """Performs cleanup on shutdown.""" - self._con_pool.clear() # type: ignore - - @staticmethod - def _parse(json_data: bytes) -> Union[JSONDict, bool]: - """Try and parse the JSON returned from Telegram. - - Returns: - dict: A JSON parsed as Python dict with results - on error this dict will be empty. - - """ - decoded_s = json_data.decode('utf-8', 'replace') - try: - data = json.loads(decoded_s) - except ValueError as exc: - raise TelegramError('Invalid server response') from exc - - if not data.get('ok'): # pragma: no cover - description = data.get('description') - parameters = data.get('parameters') - if parameters: - migrate_to_chat_id = parameters.get('migrate_to_chat_id') - if migrate_to_chat_id: - raise ChatMigrated(migrate_to_chat_id) - retry_after = parameters.get('retry_after') - if retry_after: - raise RetryAfter(retry_after) - if description: - return description - - return data['result'] - - def _request_wrapper(self, *args: object, **kwargs: Any) -> bytes: - """Wraps urllib3 request for handling known exceptions. - - Args: - args: unnamed arguments, passed to urllib3 request. - kwargs: keyword arguments, passed to urllib3 request. - - Returns: - bytes: A non-parsed JSON text. - - Raises: - TelegramError - - """ - # Make sure to hint Telegram servers that we reuse connections by sending - # "Connection: keep-alive" in the HTTP headers. - if 'headers' not in kwargs: - kwargs['headers'] = {} - kwargs['headers']['connection'] = 'keep-alive' - # Also set our user agent - kwargs['headers']['user-agent'] = USER_AGENT - - try: - resp = self._con_pool.request(*args, **kwargs) - except urllib3.exceptions.TimeoutError as error: - raise TimedOut() from error - except urllib3.exceptions.HTTPError as error: - # HTTPError must come last as its the base urllib3 exception class - # TODO: do something smart here; for now just raise NetworkError - raise NetworkError(f'urllib3 HTTPError {error}') from error - - if 200 <= resp.status <= 299: - # 200-299 range are HTTP success statuses - return resp.data - - try: - message = str(self._parse(resp.data)) - except ValueError: - message = 'Unknown HTTPError' - - if resp.status in (401, 403): - raise Unauthorized(message) - if resp.status == 400: - raise BadRequest(message) - if resp.status == 404: - raise InvalidToken() - if resp.status == 409: - raise Conflict(message) - if resp.status == 413: - raise NetworkError( - 'File too large. Check telegram api limits ' - 'https://core.telegram.org/bots/api#senddocument' - ) - if resp.status == 502: - raise NetworkError('Bad Gateway') - raise NetworkError(f'{message} ({resp.status})') - - def post(self, url: str, data: JSONDict, timeout: float = None) -> Union[JSONDict, bool]: - """Request an URL. - - Args: - url (:obj:`str`): The web location we want to retrieve. - data (Dict[:obj:`str`, :obj:`str` | :obj:`int`], optional): A dict of key/value pairs. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - - Returns: - A JSON object. - - """ - urlopen_kwargs = {} - - if timeout is not None: - urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout) - - if data is None: - data = {} - - # Are we uploading files? - files = False - - # pylint: disable=R1702 - for key, val in data.copy().items(): - if isinstance(val, InputFile): - # Convert the InputFile to urllib3 field format - data[key] = val.field_tuple - files = True - elif isinstance(val, (float, int)): - # Urllib3 doesn't like floats it seems - data[key] = str(val) - elif key == 'media': - files = True - # List of media - if isinstance(val, list): - # Attach and set val to attached name for all - media = [] - for med in val: - media_dict = med.to_dict() - media.append(media_dict) - if isinstance(med.media, InputFile): - data[med.media.attach] = med.media.field_tuple - # if the file has a thumb, we also need to attach it to the data - if "thumb" in media_dict: - data[med.thumb.attach] = med.thumb.field_tuple - data[key] = json.dumps(media) - # Single media - else: - # Attach and set val to attached name - media_dict = val.to_dict() - if isinstance(val.media, InputFile): - data[val.media.attach] = val.media.field_tuple - # if the file has a thumb, we also need to attach it to the data - if "thumb" in media_dict: - data[val.thumb.attach] = val.thumb.field_tuple - data[key] = json.dumps(media_dict) - elif isinstance(val, list): - # In case we're sending files, we need to json-dump lists manually - # As we can't know if that's the case, we just json-dump here - data[key] = json.dumps(val) - - # Use multipart upload if we're uploading files, otherwise use JSON - if files: - result = self._request_wrapper('POST', url, fields=data, **urlopen_kwargs) - else: - result = self._request_wrapper( - 'POST', - url, - body=json.dumps(data).encode('utf-8'), - headers={'Content-Type': 'application/json'}, - **urlopen_kwargs, - ) - - return self._parse(result) - - def retrieve(self, url: str, timeout: float = None) -> bytes: - """Retrieve the contents of a file by its URL. - - Args: - url (:obj:`str`): The web location we want to retrieve. - timeout (:obj:`int` | :obj:`float`): If this value is specified, use it as the read - timeout from the server (instead of the one specified during creation of the - connection pool). - - """ - urlopen_kwargs = {} - if timeout is not None: - urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout) - - return self._request_wrapper('GET', url, **urlopen_kwargs) - - def download(self, url: str, filename: str, timeout: float = None) -> None: - """Download a file by its URL. - - Args: - url (:obj:`str`): The web location we want to retrieve. - timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as - the read timeout from the server (instead of the one specified during creation of - the connection pool). - filename (:obj:`str`): The filename within the path to download the file. - - """ - buf = self.retrieve(url, timeout=timeout) - with open(filename, 'wb') as fobj: - fobj.write(buf) diff --git a/telegramer/include/telegram/utils/types.py b/telegramer/include/telegram/utils/types.py deleted file mode 100644 index a3e1f42..0000000 --- a/telegramer/include/telegram/utils/types.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains custom typing aliases.""" -from pathlib import Path -from typing import ( - IO, - TYPE_CHECKING, - Any, - Dict, - List, - Optional, - Tuple, - TypeVar, - Union, -) - -if TYPE_CHECKING: - from telegram import InputFile # noqa: F401 - from telegram.utils.helpers import DefaultValue # noqa: F401 - -FileLike = Union[IO, 'InputFile'] -"""Either an open file handler or a :class:`telegram.InputFile`.""" - -FileInput = Union[str, bytes, FileLike, Path] -"""Valid input for passing files to Telegram. Either a file id as string, a file like object, -a local file path as string, :class:`pathlib.Path` or the file contents as :obj:`bytes`.""" - -JSONDict = Dict[str, Any] -"""Dictionary containing response from Telegram or data to send to the API.""" - -DVType = TypeVar('DVType') -ODVInput = Optional[Union['DefaultValue[DVType]', DVType]] -"""Generic type for bot method parameters which can have defaults. ``ODVInput[type]`` is the same -as ``Optional[Union[DefaultValue, type]]``.""" -DVInput = Union['DefaultValue[DVType]', DVType] -"""Generic type for bot method parameters which can have defaults. ``DVInput[type]`` is the same -as ``Union[DefaultValue, type]``.""" - -RT = TypeVar("RT") -SLT = Union[RT, List[RT], Tuple[RT, ...]] -"""Single instance or list/tuple of instances.""" diff --git a/telegramer/include/telegram/utils/webhookhandler.py b/telegramer/include/telegram/utils/webhookhandler.py deleted file mode 100644 index 88e3c5a..0000000 --- a/telegramer/include/telegram/utils/webhookhandler.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the :class:`telegram.ext.utils.webhookhandler.WebhookHandler` class for -backwards compatibility. -""" -import warnings - -import telegram.ext.utils.webhookhandler as webhook_handler -from telegram.utils.deprecate import TelegramDeprecationWarning - -warnings.warn( - 'telegram.utils.webhookhandler is deprecated. Please use telegram.ext.utils.webhookhandler ' - 'instead.', - TelegramDeprecationWarning, -) - -WebhookHandler = webhook_handler.WebhookHandler -WebhookServer = webhook_handler.WebhookServer -WebhookAppClass = webhook_handler.WebhookAppClass diff --git a/telegramer/include/telegram/vendor/__init__.py b/telegramer/include/telegram/vendor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/__init__.py b/telegramer/include/telegram/vendor/ptb_urllib3/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/__init__.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/__init__.py deleted file mode 100644 index 0cd5e34..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/__init__.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -urllib3 - Thread-safe connection pooling and re-using. -""" -from __future__ import absolute_import -import warnings - -from .connectionpool import ( - HTTPConnectionPool, - HTTPSConnectionPool, - connection_from_url -) - -from . import exceptions -from .filepost import encode_multipart_formdata -from .poolmanager import PoolManager, ProxyManager, proxy_from_url -from .response import HTTPResponse -from .util.request import make_headers -from .util.url import get_host -from .util.timeout import Timeout -from .util.retry import Retry - - -# Set default logging handler to avoid "No handler found" warnings. -import logging -try: # Python 2.7+ - from logging import NullHandler -except ImportError: - class NullHandler(logging.Handler): - def emit(self, record): - pass - -__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)' -__license__ = 'MIT' -__version__ = 'dev' - -__all__ = ( - 'HTTPConnectionPool', - 'HTTPSConnectionPool', - 'PoolManager', - 'ProxyManager', - 'HTTPResponse', - 'Retry', - 'Timeout', - 'add_stderr_logger', - 'connection_from_url', - 'disable_warnings', - 'encode_multipart_formdata', - 'get_host', - 'make_headers', - 'proxy_from_url', -) - -logging.getLogger(__name__).addHandler(NullHandler()) - - -def add_stderr_logger(level=logging.DEBUG): - """ - Helper for quickly adding a StreamHandler to the logger. Useful for - debugging. - - Returns the handler after adding it. - """ - # This method needs to be in this __init__.py to get the __name__ correct - # even if urllib3 is vendored within another package. - logger = logging.getLogger(__name__) - handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s')) - logger.addHandler(handler) - logger.setLevel(level) - logger.debug('Added a stderr logging handler to logger: %s', __name__) - return handler - - -# ... Clean up. -del NullHandler - - -# All warning filters *must* be appended unless you're really certain that they -# shouldn't be: otherwise, it's very hard for users to use most Python -# mechanisms to silence them. -# SecurityWarning's always go off by default. -warnings.simplefilter('always', exceptions.SecurityWarning, append=True) -# SubjectAltNameWarning's should go off once per host -warnings.simplefilter('default', exceptions.SubjectAltNameWarning, append=True) -# InsecurePlatformWarning's don't vary between requests, so we keep it default. -warnings.simplefilter('default', exceptions.InsecurePlatformWarning, - append=True) -# SNIMissingWarnings should go off only once. -warnings.simplefilter('default', exceptions.SNIMissingWarning, append=True) - - -def disable_warnings(category=exceptions.HTTPWarning): - """ - Helper for quickly disabling all urllib3 warnings. - """ - warnings.simplefilter('ignore', category) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/_collections.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/_collections.py deleted file mode 100644 index 4fcd2ab..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/_collections.py +++ /dev/null @@ -1,327 +0,0 @@ -from __future__ import absolute_import -try: - from collections.abc import Mapping, MutableMapping -except ImportError: - from collections import Mapping, MutableMapping -try: - from threading import RLock -except ImportError: # Platform-specific: No threads available - class RLock: - def __enter__(self): - pass - - def __exit__(self, exc_type, exc_value, traceback): - pass - - -try: # Python 2.7+ - from collections import OrderedDict -except ImportError: - from .packages.ordered_dict import OrderedDict -from .packages.six import iterkeys, itervalues, PY3 - - -__all__ = ['RecentlyUsedContainer', 'HTTPHeaderDict'] - - -_Null = object() - - -class RecentlyUsedContainer(MutableMapping): - """ - Provides a thread-safe dict-like container which maintains up to - ``maxsize`` keys while throwing away the least-recently-used keys beyond - ``maxsize``. - - :param maxsize: - Maximum number of recent elements to retain. - - :param dispose_func: - Every time an item is evicted from the container, - ``dispose_func(value)`` is called. Callback which will get called - """ - - ContainerCls = OrderedDict - - def __init__(self, maxsize=10, dispose_func=None): - self._maxsize = maxsize - self.dispose_func = dispose_func - - self._container = self.ContainerCls() - self.lock = RLock() - - def __getitem__(self, key): - # Re-insert the item, moving it to the end of the eviction line. - with self.lock: - item = self._container.pop(key) - self._container[key] = item - return item - - def __setitem__(self, key, value): - evicted_value = _Null - with self.lock: - # Possibly evict the existing value of 'key' - evicted_value = self._container.get(key, _Null) - self._container[key] = value - - # If we didn't evict an existing value, we might have to evict the - # least recently used item from the beginning of the container. - if len(self._container) > self._maxsize: - _key, evicted_value = self._container.popitem(last=False) - - if self.dispose_func and evicted_value is not _Null: - self.dispose_func(evicted_value) - - def __delitem__(self, key): - with self.lock: - value = self._container.pop(key) - - if self.dispose_func: - self.dispose_func(value) - - def __len__(self): - with self.lock: - return len(self._container) - - def __iter__(self): - raise NotImplementedError('Iteration over this class is unlikely to be threadsafe.') - - def clear(self): - with self.lock: - # Copy pointers to all values, then wipe the mapping - values = list(itervalues(self._container)) - self._container.clear() - - if self.dispose_func: - for value in values: - self.dispose_func(value) - - def keys(self): - with self.lock: - return list(iterkeys(self._container)) - - -class HTTPHeaderDict(MutableMapping): - """ - :param headers: - An iterable of field-value pairs. Must not contain multiple field names - when compared case-insensitively. - - :param kwargs: - Additional field-value pairs to pass in to ``dict.update``. - - A ``dict`` like container for storing HTTP Headers. - - Field names are stored and compared case-insensitively in compliance with - RFC 7230. Iteration provides the first case-sensitive key seen for each - case-insensitive pair. - - Using ``__setitem__`` syntax overwrites fields that compare equal - case-insensitively in order to maintain ``dict``'s api. For fields that - compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` - in a loop. - - If multiple fields that are equal case-insensitively are passed to the - constructor or ``.update``, the behavior is undefined and some will be - lost. - - >>> headers = HTTPHeaderDict() - >>> headers.add('Set-Cookie', 'foo=bar') - >>> headers.add('set-cookie', 'baz=quxx') - >>> headers['content-length'] = '7' - >>> headers['SET-cookie'] - 'foo=bar, baz=quxx' - >>> headers['Content-Length'] - '7' - """ - - def __init__(self, headers=None, **kwargs): - super(HTTPHeaderDict, self).__init__() - self._container = OrderedDict() - if headers is not None: - if isinstance(headers, HTTPHeaderDict): - self._copy_from(headers) - else: - self.extend(headers) - if kwargs: - self.extend(kwargs) - - def __setitem__(self, key, val): - self._container[key.lower()] = (key, val) - return self._container[key.lower()] - - def __getitem__(self, key): - val = self._container[key.lower()] - return ', '.join(val[1:]) - - def __delitem__(self, key): - del self._container[key.lower()] - - def __contains__(self, key): - return key.lower() in self._container - - def __eq__(self, other): - if not isinstance(other, Mapping) and not hasattr(other, 'keys'): - return False - if not isinstance(other, type(self)): - other = type(self)(other) - return (dict((k.lower(), v) for k, v in self.itermerged()) == - dict((k.lower(), v) for k, v in other.itermerged())) - - def __ne__(self, other): - return not self.__eq__(other) - - if not PY3: # Python 2 - iterkeys = MutableMapping.iterkeys - itervalues = MutableMapping.itervalues - - __marker = object() - - def __len__(self): - return len(self._container) - - def __iter__(self): - # Only provide the originally cased names - for vals in self._container.values(): - yield vals[0] - - def pop(self, key, default=__marker): - '''D.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - ''' - # Using the MutableMapping function directly fails due to the private marker. - # Using ordinary dict.pop would expose the internal structures. - # So let's reinvent the wheel. - try: - value = self[key] - except KeyError: - if default is self.__marker: - raise - return default - else: - del self[key] - return value - - def discard(self, key): - try: - del self[key] - except KeyError: - pass - - def add(self, key, val): - """Adds a (name, value) pair, doesn't overwrite the value if it already - exists. - - >>> headers = HTTPHeaderDict(foo='bar') - >>> headers.add('Foo', 'baz') - >>> headers['foo'] - 'bar, baz' - """ - key_lower = key.lower() - new_vals = key, val - # Keep the common case aka no item present as fast as possible - vals = self._container.setdefault(key_lower, new_vals) - if new_vals is not vals: - # new_vals was not inserted, as there was a previous one - if isinstance(vals, list): - # If already several items got inserted, we have a list - vals.append(val) - else: - # vals should be a tuple then, i.e. only one item so far - # Need to convert the tuple to list for further extension - self._container[key_lower] = [vals[0], vals[1], val] - - def extend(self, *args, **kwargs): - """Generic import function for any type of header-like object. - Adapted version of MutableMapping.update in order to insert items - with self.add instead of self.__setitem__ - """ - if len(args) > 1: - raise TypeError("extend() takes at most 1 positional " - "arguments ({0} given)".format(len(args))) - other = args[0] if len(args) >= 1 else () - - if isinstance(other, HTTPHeaderDict): - for key, val in other.iteritems(): - self.add(key, val) - elif isinstance(other, Mapping): - for key in other: - self.add(key, other[key]) - elif hasattr(other, "keys"): - for key in other.keys(): - self.add(key, other[key]) - else: - for key, value in other: - self.add(key, value) - - for key, value in kwargs.items(): - self.add(key, value) - - def getlist(self, key): - """Returns a list of all the values for the named field. Returns an - empty list if the key doesn't exist.""" - try: - vals = self._container[key.lower()] - except KeyError: - return [] - else: - if isinstance(vals, tuple): - return [vals[1]] - else: - return vals[1:] - - # Backwards compatibility for httplib - getheaders = getlist - getallmatchingheaders = getlist - iget = getlist - - def __repr__(self): - return "%s(%s)" % (type(self).__name__, dict(self.itermerged())) - - def _copy_from(self, other): - for key in other: - val = other.getlist(key) - if isinstance(val, list): - # Don't need to convert tuples - val = list(val) - self._container[key.lower()] = [key] + val - - def copy(self): - clone = type(self)() - clone._copy_from(self) - return clone - - def iteritems(self): - """Iterate over all header lines, including duplicate ones.""" - for key in self: - vals = self._container[key.lower()] - for val in vals[1:]: - yield vals[0], val - - def itermerged(self): - """Iterate over all headers, merging duplicate ones together.""" - for key in self: - val = self._container[key.lower()] - yield val[0], ', '.join(val[1:]) - - def items(self): - return list(self.iteritems()) - - @classmethod - def from_httplib(cls, message): # Python 2 - """Read headers from a Python 2 httplib message object.""" - # python2.7 does not expose a proper API for exporting multiheaders - # efficiently. This function re-reads raw lines from the message - # object and extracts the multiheaders properly. - headers = [] - - for line in message.headers: - if line.startswith((' ', '\t')): - key, value = headers[-1] - headers[-1] = (key, value + '\r\n' + line.rstrip()) - continue - - key, value = line.split(':', 1) - headers.append((key, value.strip())) - - return cls(headers) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connection.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connection.py deleted file mode 100644 index 9f06c39..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connection.py +++ /dev/null @@ -1,369 +0,0 @@ -from __future__ import absolute_import -import datetime -import logging -import os -import sys -import socket -from socket import error as SocketError, timeout as SocketTimeout -import warnings -from .packages import six -from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection -from .packages.six.moves.http_client import HTTPException # noqa: F401 - -try: # Compiled with SSL? - import ssl - BaseSSLError = ssl.SSLError -except (ImportError, AttributeError): # Platform-specific: No SSL. - ssl = None - - class BaseSSLError(BaseException): - pass - - -try: # Python 3: - # Not a no-op, we're adding this to the namespace so it can be imported. - ConnectionError = ConnectionError -except NameError: # Python 2: - class ConnectionError(Exception): - pass - - -from .exceptions import ( - NewConnectionError, - ConnectTimeoutError, - SubjectAltNameWarning, - SystemTimeWarning, -) -from .packages.ssl_match_hostname import match_hostname, CertificateError - -from .util.ssl_ import ( - resolve_cert_reqs, - resolve_ssl_version, - assert_fingerprint, - create_urllib3_context, - ssl_wrap_socket -) - - -from .util import connection - -from ._collections import HTTPHeaderDict - -log = logging.getLogger(__name__) - -port_by_scheme = { - 'http': 80, - 'https': 443, -} - -# When updating RECENT_DATE, move it to -# within two years of the current date, and no -# earlier than 6 months ago. -RECENT_DATE = datetime.date(2016, 1, 1) - - -class DummyConnection(object): - """Used to detect a failed ConnectionCls import.""" - pass - - -class HTTPConnection(_HTTPConnection, object): - """ - Based on httplib.HTTPConnection but provides an extra constructor - backwards-compatibility layer between older and newer Pythons. - - Additional keyword parameters are used to configure attributes of the connection. - Accepted parameters include: - - - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool` - - ``source_address``: Set the source address for the current connection. - - .. note:: This is ignored for Python 2.6. It is only applied for 2.7 and 3.x - - - ``socket_options``: Set specific options on the underlying socket. If not specified, then - defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling - Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. - - For example, if you wish to enable TCP Keep Alive in addition to the defaults, - you might pass:: - - HTTPConnection.default_socket_options + [ - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), - ] - - Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). - """ - - default_port = port_by_scheme['http'] - - #: Disable Nagle's algorithm by default. - #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` - default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)] - - #: Whether this connection verifies the host's certificate. - is_verified = False - - def __init__(self, *args, **kw): - if six.PY3: # Python 3 - kw.pop('strict', None) - - # Pre-set source_address in case we have an older Python like 2.6. - self.source_address = kw.get('source_address') - - if sys.version_info < (2, 7): # Python 2.6 - # _HTTPConnection on Python 2.6 will balk at this keyword arg, but - # not newer versions. We can still use it when creating a - # connection though, so we pop it *after* we have saved it as - # self.source_address. - kw.pop('source_address', None) - - #: The socket options provided by the user. If no options are - #: provided, we use the default options. - self.socket_options = kw.pop('socket_options', self.default_socket_options) - - # Superclass also sets self.source_address in Python 2.7+. - _HTTPConnection.__init__(self, *args, **kw) - - def _new_conn(self): - """ Establish a socket connection and set nodelay settings on it. - - :return: New socket connection. - """ - extra_kw = {} - if self.source_address: - extra_kw['source_address'] = self.source_address - - if self.socket_options: - extra_kw['socket_options'] = self.socket_options - - try: - conn = connection.create_connection( - (self.host, self.port), self.timeout, **extra_kw) - - except SocketTimeout as e: - raise ConnectTimeoutError( - self, "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout)) - - except SocketError as e: - raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e) - - return conn - - def _prepare_conn(self, conn): - self.sock = conn - # the _tunnel_host attribute was added in python 2.6.3 (via - # http://hg.python.org/cpython/rev/0f57b30a152f) so pythons 2.6(0-2) do - # not have them. - if getattr(self, '_tunnel_host', None): - # TODO: Fix tunnel so it doesn't depend on self.sock state. - self._tunnel() - # Mark this connection as not reusable - self.auto_open = 0 - - def connect(self): - conn = self._new_conn() - self._prepare_conn(conn) - - def request_chunked(self, method, url, body=None, headers=None): - """ - Alternative to the common request method, which sends the - body with chunked encoding and not as one block - """ - headers = HTTPHeaderDict(headers if headers is not None else {}) - skip_accept_encoding = 'accept-encoding' in headers - skip_host = 'host' in headers - self.putrequest( - method, - url, - skip_accept_encoding=skip_accept_encoding, - skip_host=skip_host - ) - for header, value in headers.items(): - self.putheader(header, value) - if 'transfer-encoding' not in headers: - self.putheader('Transfer-Encoding', 'chunked') - self.endheaders() - - if body is not None: - stringish_types = six.string_types + (six.binary_type,) - if isinstance(body, stringish_types): - body = (body,) - for chunk in body: - if not chunk: - continue - if not isinstance(chunk, six.binary_type): - chunk = chunk.encode('utf8') - len_str = hex(len(chunk))[2:] - self.send(len_str.encode('utf-8')) - self.send(b'\r\n') - self.send(chunk) - self.send(b'\r\n') - - # After the if clause, to always have a closed body - self.send(b'0\r\n\r\n') - - -class HTTPSConnection(HTTPConnection): - default_port = port_by_scheme['https'] - - ssl_version = None - - def __init__(self, host, port=None, key_file=None, cert_file=None, - strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - ssl_context=None, **kw): - - HTTPConnection.__init__(self, host, port, strict=strict, - timeout=timeout, **kw) - - self.key_file = key_file - self.cert_file = cert_file - self.ssl_context = ssl_context - - # Required property for Google AppEngine 1.9.0 which otherwise causes - # HTTPS requests to go out as HTTP. (See Issue #356) - self._protocol = 'https' - - def connect(self): - conn = self._new_conn() - self._prepare_conn(conn) - - if self.ssl_context is None: - self.ssl_context = create_urllib3_context( - ssl_version=resolve_ssl_version(None), - cert_reqs=resolve_cert_reqs(None), - ) - - self.sock = ssl_wrap_socket( - sock=conn, - keyfile=self.key_file, - certfile=self.cert_file, - ssl_context=self.ssl_context, - ) - - -class VerifiedHTTPSConnection(HTTPSConnection): - """ - Based on httplib.HTTPSConnection but wraps the socket with - SSL certification. - """ - cert_reqs = None - ca_certs = None - ca_cert_dir = None - ssl_version = None - assert_fingerprint = None - - def set_cert(self, key_file=None, cert_file=None, - cert_reqs=None, ca_certs=None, - assert_hostname=None, assert_fingerprint=None, - ca_cert_dir=None): - """ - This method should only be called once, before the connection is used. - """ - # If cert_reqs is not provided, we can try to guess. If the user gave - # us a cert database, we assume they want to use it: otherwise, if - # they gave us an SSL Context object we should use whatever is set for - # it. - if cert_reqs is None: - if ca_certs or ca_cert_dir: - cert_reqs = 'CERT_REQUIRED' - elif self.ssl_context is not None: - cert_reqs = self.ssl_context.verify_mode - - self.key_file = key_file - self.cert_file = cert_file - self.cert_reqs = cert_reqs - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - self.ca_certs = ca_certs and os.path.expanduser(ca_certs) - self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) - - def connect(self): - # Add certificate verification - conn = self._new_conn() - - hostname = self.host - if getattr(self, '_tunnel_host', None): - # _tunnel_host was added in Python 2.6.3 - # (See: http://hg.python.org/cpython/rev/0f57b30a152f) - - self.sock = conn - # Calls self._set_hostport(), so self.host is - # self._tunnel_host below. - self._tunnel() - # Mark this connection as not reusable - self.auto_open = 0 - - # Override the host with the one we're requesting data from. - hostname = self._tunnel_host - - is_time_off = datetime.date.today() < RECENT_DATE - if is_time_off: - warnings.warn(( - 'System time is way off (before {0}). This will probably ' - 'lead to SSL verification errors').format(RECENT_DATE), - SystemTimeWarning - ) - - # Wrap socket using verification with the root certs in - # trusted_root_certs - if self.ssl_context is None: - self.ssl_context = create_urllib3_context( - ssl_version=resolve_ssl_version(self.ssl_version), - cert_reqs=resolve_cert_reqs(self.cert_reqs), - ) - - context = self.ssl_context - context.verify_mode = resolve_cert_reqs(self.cert_reqs) - self.sock = ssl_wrap_socket( - sock=conn, - keyfile=self.key_file, - certfile=self.cert_file, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - server_hostname=hostname, - ssl_context=context) - - if self.assert_fingerprint: - assert_fingerprint(self.sock.getpeercert(binary_form=True), - self.assert_fingerprint) - elif context.verify_mode != ssl.CERT_NONE \ - and self.assert_hostname is not False: - cert = self.sock.getpeercert() - if not cert.get('subjectAltName', ()): - warnings.warn(( - 'Certificate for {0} has no `subjectAltName`, falling back to check for a ' - '`commonName` for now. This feature is being removed by major browsers and ' - 'deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 ' - 'for details.)'.format(hostname)), - SubjectAltNameWarning - ) - _match_hostname(cert, self.assert_hostname or hostname) - - self.is_verified = ( - context.verify_mode == ssl.CERT_REQUIRED or - self.assert_fingerprint is not None - ) - - -def _match_hostname(cert, asserted_hostname): - try: - match_hostname(cert, asserted_hostname) - except CertificateError as e: - log.error( - 'Certificate did not match expected hostname: %s. ' - 'Certificate: %s', asserted_hostname, cert - ) - # Add cert to exception and reraise so client code can inspect - # the cert when catching the exception, if they want to - e._peer_cert = cert - raise - - -if ssl: - # Make a copy for testing. - UnverifiedHTTPSConnection = HTTPSConnection - HTTPSConnection = VerifiedHTTPSConnection -else: - HTTPSConnection = DummyConnection diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connectionpool.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connectionpool.py deleted file mode 100644 index a958f99..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/connectionpool.py +++ /dev/null @@ -1,912 +0,0 @@ -from __future__ import absolute_import -import errno -import logging -import sys -import warnings - -from socket import error as SocketError, timeout as SocketTimeout -import socket - - -from .exceptions import ( - ClosedPoolError, - ProtocolError, - EmptyPoolError, - HeaderParsingError, - HostChangedError, - LocationValueError, - MaxRetryError, - ProxyError, - ReadTimeoutError, - SSLError, - TimeoutError, - InsecureRequestWarning, - NewConnectionError, - ConnectTimeoutError, -) -from .packages.ssl_match_hostname import CertificateError -from .packages import six -from .packages.six.moves import queue -from .connection import ( - port_by_scheme, - DummyConnection, - HTTPConnection, HTTPSConnection, VerifiedHTTPSConnection, - HTTPException, BaseSSLError, -) -from .request import RequestMethods -from .response import HTTPResponse - -from .util.connection import is_connection_dropped -from .util.request import set_file_position -from .util.response import assert_header_parsing -from .util.retry import Retry -from .util.timeout import Timeout -from .util.url import get_host, Url - - -if six.PY2: - # Queue is imported for side effects on MS Windows - import Queue as _unused_module_Queue # noqa: F401 - -xrange = six.moves.xrange - -log = logging.getLogger(__name__) - -_Default = object() - - -# Pool objects -class ConnectionPool(object): - """ - Base class for all connection pools, such as - :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. - """ - - scheme = None - QueueCls = queue.LifoQueue - - def __init__(self, host, port=None): - if not host: - raise LocationValueError("No host specified.") - - self.host = _ipv6_host(host).lower() - self.port = port - - def __str__(self): - return '%s(host=%r, port=%r)' % (type(self).__name__, - self.host, self.port) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - # Return False to re-raise any potential exceptions - return False - - def close(self): - """ - Close all pooled connections and disable the pool. - """ - pass - - -# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 -_blocking_errnos = set([errno.EAGAIN, errno.EWOULDBLOCK]) - - -class HTTPConnectionPool(ConnectionPool, RequestMethods): - """ - Thread-safe connection pool for one host. - - :param host: - Host used for this HTTP Connection (e.g. "localhost"), passed into - :class:`httplib.HTTPConnection`. - - :param port: - Port used for this HTTP Connection (None is equivalent to 80), passed - into :class:`httplib.HTTPConnection`. - - :param strict: - Causes BadStatusLine to be raised if the status line can't be parsed - as a valid HTTP/1.0 or 1.1 status line, passed into - :class:`httplib.HTTPConnection`. - - .. note:: - Only works in Python 2. This parameter is ignored in Python 3. - - :param timeout: - Socket timeout in seconds for each individual connection. This can - be a float or integer, which sets the timeout for the HTTP request, - or an instance of :class:`urllib3.util.Timeout` which gives you more - fine-grained control over request timeouts. After the constructor has - been parsed, this is always a `urllib3.util.Timeout` object. - - :param maxsize: - Number of connections to save that can be reused. More than 1 is useful - in multithreaded situations. If ``block`` is set to False, more - connections will be created but they will not be saved once they've - been used. - - :param block: - If set to True, no more than ``maxsize`` connections will be used at - a time. When no free connections are available, the call will block - until a connection has been released. This is a useful side effect for - particular multithreaded situations where one does not want to use more - than maxsize connections per host to prevent flooding. - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - - :param retries: - Retry configuration to use by default with requests in this pool. - - :param _proxy: - Parsed proxy URL, should not be used directly, instead, see - :class:`urllib3.connectionpool.ProxyManager`" - - :param _proxy_headers: - A dictionary with proxy headers, should not be used directly, - instead, see :class:`urllib3.connectionpool.ProxyManager`" - - :param \\**conn_kw: - Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, - :class:`urllib3.connection.HTTPSConnection` instances. - """ - - scheme = 'http' - ConnectionCls = HTTPConnection - ResponseCls = HTTPResponse - - def __init__(self, host, port=None, strict=False, - timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, block=False, - headers=None, retries=None, - _proxy=None, _proxy_headers=None, - **conn_kw): - ConnectionPool.__init__(self, host, port) - RequestMethods.__init__(self, headers) - - self.strict = strict - - if not isinstance(timeout, Timeout): - timeout = Timeout.from_float(timeout) - - if retries is None: - retries = Retry.DEFAULT - - self.timeout = timeout - self.retries = retries - - self.pool = self.QueueCls(maxsize) - self.block = block - - self.proxy = _proxy - self.proxy_headers = _proxy_headers or {} - - # Fill the queue up so that doing get() on it will block properly - for _ in xrange(maxsize): - self.pool.put(None) - - # These are mostly for testing and debugging purposes. - self.num_connections = 0 - self.num_requests = 0 - self.conn_kw = conn_kw - - if self.proxy: - # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. - # We cannot know if the user has added default socket options, so we cannot replace the - # list. - self.conn_kw.setdefault('socket_options', []) - - def _new_conn(self): - """ - Return a fresh :class:`HTTPConnection`. - """ - self.num_connections += 1 - log.debug("Starting new HTTP connection (%d): %s", - self.num_connections, self.host) - - conn = self.ConnectionCls(host=self.host, port=self.port, - timeout=self.timeout.connect_timeout, - strict=self.strict, **self.conn_kw) - return conn - - def _get_conn(self, timeout=None): - """ - Get a connection. Will return a pooled connection if one is available. - - If no connections are available and :prop:`.block` is ``False``, then a - fresh connection is returned. - - :param timeout: - Seconds to wait before giving up and raising - :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and - :prop:`.block` is ``True``. - """ - conn = None - try: - conn = self.pool.get(block=self.block, timeout=timeout) - - except AttributeError: # self.pool is None - raise ClosedPoolError(self, "Pool is closed.") - - except queue.Empty: - if self.block: - raise EmptyPoolError(self, - "Pool reached maximum size and no more " - "connections are allowed.") - pass # Oh well, we'll create a new connection then - - # If this is a persistent connection, check if it got disconnected - if conn and is_connection_dropped(conn): - log.debug("Resetting dropped connection: %s", self.host) - conn.close() - if getattr(conn, 'auto_open', 1) == 0: - # This is a proxied connection that has been mutated by - # httplib._tunnel() and cannot be reused (since it would - # attempt to bypass the proxy) - conn = None - - return conn or self._new_conn() - - def _put_conn(self, conn): - """ - Put a connection back into the pool. - - :param conn: - Connection object for the current host and port as returned by - :meth:`._new_conn` or :meth:`._get_conn`. - - If the pool is already full, the connection is closed and discarded - because we exceeded maxsize. If connections are discarded frequently, - then maxsize should be increased. - - If the pool is closed, then the connection will be closed and discarded. - """ - try: - self.pool.put(conn, block=False) - return # Everything is dandy, done. - except AttributeError: - # self.pool is None. - pass - except queue.Full: - # This should never happen if self.block == True - log.warning( - "Connection pool is full, discarding connection: %s", - self.host) - - # Connection never got put back into the pool, close it. - if conn: - conn.close() - - def _validate_conn(self, conn): - """ - Called right before a request is made, after the socket is created. - """ - # Force connect early to allow us to set read timeout in time - if not getattr(conn, 'sock', None): # AppEngine might not have `.sock` - conn.connect() - - def _prepare_proxy(self, conn): - # Nothing to do for HTTP connections. - pass - - def _get_timeout(self, timeout): - """ Helper that always returns a :class:`urllib3.util.Timeout` """ - if timeout is _Default: - return self.timeout.clone() - - if isinstance(timeout, Timeout): - return timeout.clone() - else: - # User passed us an int/float. This is for backwards compatibility, - # can be removed later - return Timeout.from_float(timeout) - - def _raise_timeout(self, err, url, timeout_value, exc_cls): - """Is the error actually a timeout? Will raise a ReadTimeout or pass""" - - # exc_cls is either ReadTimeoutError or ConnectTimeoutError - # Only ReadTimeoutError requires the url (preserving old behaviour) - args = [self] - if exc_cls is ReadTimeoutError: - args.append(url) - desc = 'Read' - else: - desc = 'Connect' - - if isinstance(err, SocketTimeout): - args.append("%s timed out. (%s timeout=%s)" % (desc, desc.lower(), timeout_value)) - raise exc_cls(*args) - - # See the above comment about EAGAIN in Python 3. In Python 2 we have - # to specifically catch it and throw the timeout error - elif hasattr(err, 'errno') and err.errno in _blocking_errnos: - args.append("%s timed out. (%s timeout=%s)" % (desc, desc.lower(), timeout_value)) - raise exc_cls(*args) - - # Catch possible read timeouts thrown as SSL errors. If not the - # case, rethrow the original. We need to do this because of: - # http://bugs.python.org/issue10272 - elif 'timed out' in str(err) or 'did not complete (read)' in str(err): # Python 2.6 - args.append("%s timed out. (%s timeout=%s)" % (desc, desc.lower(), timeout_value)) - raise exc_cls(*args) - - def _make_request(self, conn, method, url, timeout=_Default, chunked=False, - **httplib_request_kw): - """ - Perform a request on a given urllib connection object taken from our - pool. - - :param conn: - a connection from one of our connection pools - - :param timeout: - Socket timeout in seconds for the request. This can be a - float or integer, which will set the same timeout value for - the socket connect and the socket read, or an instance of - :class:`urllib3.util.Timeout`, which gives you more fine-grained - control over your timeouts. - """ - self.num_requests += 1 - - timeout_obj = self._get_timeout(timeout) - timeout_obj.start_connect() - conn.timeout = timeout_obj.connect_timeout - - # Trigger any extra validation we need to do. - try: - self._validate_conn(conn) - except (SocketTimeout, BaseSSLError) as e: - # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout. - self._raise_timeout(err=e, url=url, timeout_value=conn.timeout, - exc_cls=ConnectTimeoutError) - raise - - # Reset the timeout for the recv() on the socket - read_timeout = timeout_obj.read_timeout - - # App Engine doesn't have a sock attr - if getattr(conn, 'sock', None): - # In Python 3 socket.py will catch EAGAIN and return None when you - # try and read into the file pointer created by http.client, which - # instead raises a BadStatusLine exception. Instead of catching - # the exception and assuming all BadStatusLine exceptions are read - # timeouts, check for a zero timeout before making the request. - if read_timeout == 0: - raise ReadTimeoutError( - self, url, "Read timed out. (read timeout=%s)" % read_timeout) - if read_timeout is Timeout.DEFAULT_TIMEOUT: - conn.sock.settimeout(socket.getdefaulttimeout()) - else: # None or a value - conn.sock.settimeout(read_timeout) - - # conn.request() calls httplib.*.request, not the method in - # urllib3.request. It also calls makefile (recv) on the socket. - if chunked: - conn.request_chunked(method, url, **httplib_request_kw) - else: - conn.request(method, url, **httplib_request_kw) - - # Receive the response from the server - try: - try: # Python 2.7, use buffering of HTTP responses - httplib_response = conn.getresponse(buffering=True) - except TypeError: # Python 2.6 and older, Python 3 - try: - httplib_response = conn.getresponse() - except Exception as e: - # Remove the TypeError from the exception chain in Python 3; - # otherwise it looks like a programming error was the cause. - six.raise_from(e, None) - except (SocketTimeout, BaseSSLError, SocketError) as e: - self._raise_timeout(err=e, url=url, timeout_value=read_timeout, - exc_cls=ReadTimeoutError) - raise - - # AppEngine doesn't have a version attr. - http_version = getattr(conn, '_http_vsn_str', 'HTTP/?') - log.debug("%s://%s:%s \"%s %s %s\" %s %s", self.scheme, self.host, self.port, - method, url, http_version, httplib_response.status, - httplib_response.length) - - try: - assert_header_parsing(httplib_response.msg) - except HeaderParsingError as hpe: # Platform-specific: Python 3 - log.warning( - 'Failed to parse headers (url=%s): %s', - self._absolute_url(url), hpe, exc_info=True) - - return httplib_response - - def _absolute_url(self, path): - return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url - - def close(self): - """ - Close all pooled connections and disable the pool. - """ - # Disable access to the pool - old_pool, self.pool = self.pool, None - - try: - while True: - conn = old_pool.get(block=False) - if conn: - conn.close() - - except queue.Empty: - pass # Done. - - def is_same_host(self, url): - """ - Check if the given ``url`` is a member of the same host as this - connection pool. - """ - if url.startswith('/'): - return True - - # TODO: Add optional support for socket.gethostbyname checking. - scheme, host, port = get_host(url) - - host = _ipv6_host(host).lower() - - # Use explicit default port for comparison when none is given - if self.port and not port: - port = port_by_scheme.get(scheme) - elif not self.port and port == port_by_scheme.get(scheme): - port = None - - return (scheme, host, port) == (self.scheme, self.host, self.port) - - def urlopen(self, method, url, body=None, headers=None, retries=None, - redirect=True, assert_same_host=True, timeout=_Default, - pool_timeout=None, release_conn=None, chunked=False, - body_pos=None, **response_kw): - """ - Get a connection from the pool and perform an HTTP request. This is the - lowest level call for making a request, so you'll need to specify all - the raw details. - - .. note:: - - More commonly, it's appropriate to use a convenience method provided - by :class:`.RequestMethods`, such as :meth:`request`. - - .. note:: - - `release_conn` will only behave as expected if - `preload_content=False` because we want to make - `preload_content=False` the default behaviour someday soon without - breaking backwards compatibility. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param body: - Data to send in the request body (useful for creating - POST requests, see HTTPConnectionPool.post_url for - more convenience). - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - - :param retries: - Configure the number of retries to allow before raising a - :class:`~urllib3.exceptions.MaxRetryError` exception. - - Pass ``None`` to retry until you receive a response. Pass a - :class:`~urllib3.util.retry.Retry` object for fine-grained control - over different types of retries. - Pass an integer number to retry connection errors that many times, - but no other types of errors. Pass zero to never retry. - - If ``False``, then retries are disabled and any exception is raised - immediately. Also, instead of raising a MaxRetryError on redirects, - the redirect response will be returned. - - :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. - - :param redirect: - If True, automatically handle redirects (status codes 301, 302, - 303, 307, 308). Each redirect counts as a retry. Disabling retries - will disable redirect, too. - - :param assert_same_host: - If ``True``, will make sure that the host of the pool requests is - consistent else will raise HostChangedError. When False, you can - use the pool on an HTTP proxy and request foreign hosts. - - :param timeout: - If specified, overrides the default timeout for this one - request. It may be a float (in seconds) or an instance of - :class:`urllib3.util.Timeout`. - - :param pool_timeout: - If set and the pool is set to block=True, then this method will - block for ``pool_timeout`` seconds and raise EmptyPoolError if no - connection is available within the time period. - - :param release_conn: - If False, then the urlopen call will not release the connection - back into the pool once a response is received (but will release if - you read the entire contents of the response such as when - `preload_content=True`). This is useful if you're not preloading - the response's content immediately. You will need to call - ``r.release_conn()`` on the response ``r`` to return the connection - back into the pool. If None, it takes the value of - ``response_kw.get('preload_content', True)``. - - :param chunked: - If True, urllib3 will send the body using chunked transfer - encoding. Otherwise, urllib3 will send the body using the standard - content-length form. Defaults to False. - - :param int body_pos: - Position to seek to in file-like body in the event of a retry or - redirect. Typically this won't need to be set because urllib3 will - auto-populate the value when needed. - - :param \\**response_kw: - Additional parameters are passed to - :meth:`urllib3.response.HTTPResponse.from_httplib` - """ - if headers is None: - headers = self.headers - - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect, default=self.retries) - - if release_conn is None: - release_conn = response_kw.get('preload_content', True) - - # Check host - if assert_same_host and not self.is_same_host(url): - raise HostChangedError(self, url, retries) - - conn = None - - # Track whether `conn` needs to be released before - # returning/raising/recursing. Update this variable if necessary, and - # leave `release_conn` constant throughout the function. That way, if - # the function recurses, the original value of `release_conn` will be - # passed down into the recursive call, and its value will be respected. - # - # See issue #651 [1] for details. - # - # [1] <https://github.com/shazow/urllib3/issues/651> - release_this_conn = release_conn - - # Merge the proxy headers. Only do this in HTTP. We have to copy the - # headers dict so we can safely change it without those changes being - # reflected in anyone else's copy. - if self.scheme == 'http': - headers = headers.copy() - headers.update(self.proxy_headers) - - # Must keep the exception bound to a separate variable or else Python 3 - # complains about UnboundLocalError. - err = None - - # Keep track of whether we cleanly exited the except block. This - # ensures we do proper cleanup in finally. - clean_exit = False - - # Rewind body position, if needed. Record current position - # for future rewinds in the event of a redirect/retry. - body_pos = set_file_position(body, body_pos) - - try: - # Request a connection from the queue. - timeout_obj = self._get_timeout(timeout) - conn = self._get_conn(timeout=pool_timeout) - - conn.timeout = timeout_obj.connect_timeout - - is_new_proxy_conn = self.proxy is not None and not getattr(conn, 'sock', None) - if is_new_proxy_conn: - self._prepare_proxy(conn) - - # Make the request on the httplib connection object. - httplib_response = self._make_request(conn, method, url, - timeout=timeout_obj, - body=body, headers=headers, - chunked=chunked) - - # If we're going to release the connection in ``finally:``, then - # the response doesn't need to know about the connection. Otherwise - # it will also try to release it and we'll have a double-release - # mess. - response_conn = conn if not release_conn else None - - # Pass method to Response for length checking - response_kw['request_method'] = method - - # Import httplib's response into our own wrapper object - response = self.ResponseCls.from_httplib(httplib_response, - pool=self, - connection=response_conn, - retries=retries, - **response_kw) - - # Everything went great! - clean_exit = True - - except queue.Empty: - # Timed out by queue. - raise EmptyPoolError(self, "No pool connections are available.") - - except (BaseSSLError, CertificateError) as e: - # Close the connection. If a connection is reused on which there - # was a Certificate error, the next request will certainly raise - # another Certificate error. - clean_exit = False - raise SSLError(e) - - except SSLError: - # Treat SSLError separately from BaseSSLError to preserve - # traceback. - clean_exit = False - raise - - except (TimeoutError, HTTPException, SocketError, ProtocolError) as e: - # Discard the connection for these exceptions. It will be - # be replaced during the next _get_conn() call. - clean_exit = False - - if isinstance(e, (SocketError, NewConnectionError)) and self.proxy: - e = ProxyError('Cannot connect to proxy.', e) - elif isinstance(e, (SocketError, HTTPException)): - e = ProtocolError('Connection aborted.', e) - - retries = retries.increment(method, url, error=e, _pool=self, - _stacktrace=sys.exc_info()[2]) - retries.sleep() - - # Keep track of the error for the retry warning. - err = e - - finally: - if not clean_exit: - # We hit some kind of exception, handled or otherwise. We need - # to throw the connection away unless explicitly told not to. - # Close the connection, set the variable to None, and make sure - # we put the None back in the pool to avoid leaking it. - conn = conn and conn.close() - release_this_conn = True - - if release_this_conn: - # Put the connection back to be reused. If the connection is - # expired then it will be None, which will get replaced with a - # fresh connection during _get_conn. - self._put_conn(conn) - - if not conn: - # Try again - log.warning("Retrying (%r) after connection " - "broken by '%r': %s", retries, err, url) - return self.urlopen(method, url, body, headers, retries, - redirect, assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, - release_conn=release_conn, body_pos=body_pos, - **response_kw) - - # Handle redirect? - redirect_location = redirect and response.get_redirect_location() - if redirect_location: - if response.status == 303: - method = 'GET' - - try: - retries = retries.increment(method, url, response=response, _pool=self) - except MaxRetryError: - if retries.raise_on_redirect: - # Release the connection for this response, since we're not - # returning it to be released manually. - response.release_conn() - raise - return response - - retries.sleep_for_retry(response) - log.debug("Redirecting %s -> %s", url, redirect_location) - return self.urlopen( - method, redirect_location, body, headers, - retries=retries, redirect=redirect, - assert_same_host=assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, - release_conn=release_conn, body_pos=body_pos, - **response_kw) - - # Check if we should retry the HTTP response. - has_retry_after = bool(response.getheader('Retry-After')) - if retries.is_retry(method, response.status, has_retry_after): - try: - retries = retries.increment(method, url, response=response, _pool=self) - except MaxRetryError: - if retries.raise_on_status: - # Release the connection for this response, since we're not - # returning it to be released manually. - response.release_conn() - raise - return response - retries.sleep(response) - log.debug("Retry: %s", url) - return self.urlopen( - method, url, body, headers, - retries=retries, redirect=redirect, - assert_same_host=assert_same_host, - timeout=timeout, pool_timeout=pool_timeout, - release_conn=release_conn, - body_pos=body_pos, **response_kw) - - return response - - -class HTTPSConnectionPool(HTTPConnectionPool): - """ - Same as :class:`.HTTPConnectionPool`, but HTTPS. - - When Python is compiled with the :mod:`ssl` module, then - :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates, - instead of :class:`.HTTPSConnection`. - - :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, - ``assert_hostname`` and ``host`` in this order to verify connections. - If ``assert_hostname`` is False, no verification is done. - - The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, - ``ca_cert_dir``, and ``ssl_version`` are only used if :mod:`ssl` is - available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade - the connection socket into an SSL socket. - """ - - scheme = 'https' - ConnectionCls = HTTPSConnection - - def __init__(self, host, port=None, - strict=False, timeout=Timeout.DEFAULT_TIMEOUT, maxsize=1, - block=False, headers=None, retries=None, - _proxy=None, _proxy_headers=None, - key_file=None, cert_file=None, cert_reqs=None, - ca_certs=None, ssl_version=None, - assert_hostname=None, assert_fingerprint=None, - ca_cert_dir=None, **conn_kw): - - HTTPConnectionPool.__init__(self, host, port, strict, timeout, maxsize, - block, headers, retries, _proxy, _proxy_headers, - **conn_kw) - - if ca_certs and cert_reqs is None: - cert_reqs = 'CERT_REQUIRED' - - self.key_file = key_file - self.cert_file = cert_file - self.cert_reqs = cert_reqs - self.ca_certs = ca_certs - self.ca_cert_dir = ca_cert_dir - self.ssl_version = ssl_version - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - - def _prepare_conn(self, conn): - """ - Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket` - and establish the tunnel if proxy is used. - """ - - if isinstance(conn, VerifiedHTTPSConnection): - conn.set_cert(key_file=self.key_file, - cert_file=self.cert_file, - cert_reqs=self.cert_reqs, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - assert_hostname=self.assert_hostname, - assert_fingerprint=self.assert_fingerprint) - conn.ssl_version = self.ssl_version - return conn - - def _prepare_proxy(self, conn): - """ - Establish tunnel connection early, because otherwise httplib - would improperly set Host: header to proxy's IP:port. - """ - # Python 2.7+ - try: - set_tunnel = conn.set_tunnel - except AttributeError: # Platform-specific: Python 2.6 - set_tunnel = conn._set_tunnel - - if sys.version_info <= (2, 6, 4) and not self.proxy_headers: # Python 2.6.4 and older - set_tunnel(self.host, self.port) - else: - set_tunnel(self.host, self.port, self.proxy_headers) - - conn.connect() - - def _new_conn(self): - """ - Return a fresh :class:`httplib.HTTPSConnection`. - """ - self.num_connections += 1 - log.debug("Starting new HTTPS connection (%d): %s", - self.num_connections, self.host) - - if not self.ConnectionCls or self.ConnectionCls is DummyConnection: - raise SSLError("Can't connect to HTTPS URL because the SSL " - "module is not available.") - - actual_host = self.host - actual_port = self.port - if self.proxy is not None: - actual_host = self.proxy.host - actual_port = self.proxy.port - - conn = self.ConnectionCls(host=actual_host, port=actual_port, - timeout=self.timeout.connect_timeout, - strict=self.strict, **self.conn_kw) - - return self._prepare_conn(conn) - - def _validate_conn(self, conn): - """ - Called right before a request is made, after the socket is created. - """ - super(HTTPSConnectionPool, self)._validate_conn(conn) - - if not conn.is_verified: - warnings.warn(( - 'Unverified HTTPS request is being made. ' - 'Adding certificate verification is strongly advised. See: ' - 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' - '#ssl-warnings'), - InsecureRequestWarning) - - -def connection_from_url(url, **kw): - """ - Given a url, return an :class:`.ConnectionPool` instance of its host. - - This is a shortcut for not having to parse out the scheme, host, and port - of the url before creating an :class:`.ConnectionPool` instance. - - :param url: - Absolute URL string that must include the scheme. Port is optional. - - :param \\**kw: - Passes additional parameters to the constructor of the appropriate - :class:`.ConnectionPool`. Useful for specifying things like - timeout, maxsize, headers, etc. - - Example:: - - >>> conn = connection_from_url('http://google.com/') - >>> r = conn.request('GET', '/') - """ - scheme, host, port = get_host(url) - port = port or port_by_scheme.get(scheme, 80) - if scheme == 'https': - return HTTPSConnectionPool(host, port=port, **kw) - else: - return HTTPConnectionPool(host, port=port, **kw) - - -def _ipv6_host(host): - """ - Process IPv6 address literals - """ - - # httplib doesn't like it when we include brackets in IPv6 addresses - # Specifically, if we include brackets but also pass the port then - # httplib crazily doubles up the square brackets on the Host header. - # Instead, we need to make sure we never pass ``None`` as the port. - # However, for backward compatibility reasons we can't actually - # *assert* that. See http://bugs.python.org/issue28539 - # - # Also if an IPv6 address literal has a zone identifier, the - # percent sign might be URIencoded, convert it back into ASCII - if host.startswith('[') and host.endswith(']'): - host = host.replace('%25', '%').strip('[]') - return host diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/__init__.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/appengine.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/appengine.py deleted file mode 100644 index 814b022..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/appengine.py +++ /dev/null @@ -1,296 +0,0 @@ -""" -This module provides a pool manager that uses Google App Engine's -`URLFetch Service <https://cloud.google.com/appengine/docs/python/urlfetch>`_. - -Example usage:: - - from urllib3 import PoolManager - from urllib3.contrib.appengine import AppEngineManager, is_appengine_sandbox - - if is_appengine_sandbox(): - # AppEngineManager uses AppEngine's URLFetch API behind the scenes - http = AppEngineManager() - else: - # PoolManager uses a socket-level API behind the scenes - http = PoolManager() - - r = http.request('GET', 'https://google.com/') - -There are `limitations <https://cloud.google.com/appengine/docs/python/\ -urlfetch/#Python_Quotas_and_limits>`_ to the URLFetch service and it may not be -the best choice for your application. There are three options for using -urllib3 on Google App Engine: - -1. You can use :class:`AppEngineManager` with URLFetch. URLFetch is - cost-effective in many circumstances as long as your usage is within the - limitations. -2. You can use a normal :class:`~urllib3.PoolManager` by enabling sockets. - Sockets also have `limitations and restrictions - <https://cloud.google.com/appengine/docs/python/sockets/\ - #limitations-and-restrictions>`_ and have a lower free quota than URLFetch. - To use sockets, be sure to specify the following in your ``app.yaml``:: - - env_variables: - GAE_USE_SOCKETS_HTTPLIB : 'true' - -3. If you are using `App Engine Flexible -<https://cloud.google.com/appengine/docs/flexible/>`_, you can use the standard -:class:`PoolManager` without any configuration or special environment variables. -""" - -from __future__ import absolute_import -import logging -import os -import warnings -from ..packages.six.moves.urllib.parse import urljoin - -from ..exceptions import ( - HTTPError, - HTTPWarning, - MaxRetryError, - ProtocolError, - TimeoutError, - SSLError -) - -from ..packages.six import BytesIO -from ..request import RequestMethods -from ..response import HTTPResponse -from ..util.timeout import Timeout -from ..util.retry import Retry - -try: - from google.appengine.api import urlfetch -except ImportError: - urlfetch = None - - -log = logging.getLogger(__name__) - - -class AppEnginePlatformWarning(HTTPWarning): - pass - - -class AppEnginePlatformError(HTTPError): - pass - - -class AppEngineManager(RequestMethods): - """ - Connection manager for Google App Engine sandbox applications. - - This manager uses the URLFetch service directly instead of using the - emulated httplib, and is subject to URLFetch limitations as described in - the App Engine documentation `here - <https://cloud.google.com/appengine/docs/python/urlfetch>`_. - - Notably it will raise an :class:`AppEnginePlatformError` if: - * URLFetch is not available. - * If you attempt to use this on App Engine Flexible, as full socket - support is available. - * If a request size is more than 10 megabytes. - * If a response size is more than 32 megabtyes. - * If you use an unsupported request method such as OPTIONS. - - Beyond those cases, it will raise normal urllib3 errors. - """ - - def __init__(self, headers=None, retries=None, validate_certificate=True, - urlfetch_retries=True): - if not urlfetch: - raise AppEnginePlatformError( - "URLFetch is not available in this environment.") - - if is_prod_appengine_mvms(): - raise AppEnginePlatformError( - "Use normal urllib3.PoolManager instead of AppEngineManager" - "on Managed VMs, as using URLFetch is not necessary in " - "this environment.") - - warnings.warn( - "urllib3 is using URLFetch on Google App Engine sandbox instead " - "of sockets. To use sockets directly instead of URLFetch see " - "https://urllib3.readthedocs.io/en/latest/reference/urllib3.contrib.html.", - AppEnginePlatformWarning) - - RequestMethods.__init__(self, headers) - self.validate_certificate = validate_certificate - self.urlfetch_retries = urlfetch_retries - - self.retries = retries or Retry.DEFAULT - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - # Return False to re-raise any potential exceptions - return False - - def urlopen(self, method, url, body=None, headers=None, - retries=None, redirect=True, timeout=Timeout.DEFAULT_TIMEOUT, - **response_kw): - - retries = self._get_retries(retries, redirect) - - try: - follow_redirects = ( - redirect and - retries.redirect != 0 and - retries.total) - response = urlfetch.fetch( - url, - payload=body, - method=method, - headers=headers or {}, - allow_truncated=False, - follow_redirects=self.urlfetch_retries and follow_redirects, - deadline=self._get_absolute_timeout(timeout), - validate_certificate=self.validate_certificate, - ) - except urlfetch.DeadlineExceededError as e: - raise TimeoutError(self, e) - - except urlfetch.InvalidURLError as e: - if 'too large' in str(e): - raise AppEnginePlatformError( - "URLFetch request too large, URLFetch only " - "supports requests up to 10mb in size.", e) - raise ProtocolError(e) - - except urlfetch.DownloadError as e: - if 'Too many redirects' in str(e): - raise MaxRetryError(self, url, reason=e) - raise ProtocolError(e) - - except urlfetch.ResponseTooLargeError as e: - raise AppEnginePlatformError( - "URLFetch response too large, URLFetch only supports" - "responses up to 32mb in size.", e) - - except urlfetch.SSLCertificateError as e: - raise SSLError(e) - - except urlfetch.InvalidMethodError as e: - raise AppEnginePlatformError( - "URLFetch does not support method: %s" % method, e) - - http_response = self._urlfetch_response_to_http_response( - response, retries=retries, **response_kw) - - # Handle redirect? - redirect_location = redirect and http_response.get_redirect_location() - if redirect_location: - # Check for redirect response - if (self.urlfetch_retries and retries.raise_on_redirect): - raise MaxRetryError(self, url, "too many redirects") - else: - if http_response.status == 303: - method = 'GET' - - try: - retries = retries.increment(method, url, response=http_response, _pool=self) - except MaxRetryError: - if retries.raise_on_redirect: - raise MaxRetryError(self, url, "too many redirects") - return http_response - - retries.sleep_for_retry(http_response) - log.debug("Redirecting %s -> %s", url, redirect_location) - redirect_url = urljoin(url, redirect_location) - return self.urlopen( - method, redirect_url, body, headers, - retries=retries, redirect=redirect, - timeout=timeout, **response_kw) - - # Check if we should retry the HTTP response. - has_retry_after = bool(http_response.getheader('Retry-After')) - if retries.is_retry(method, http_response.status, has_retry_after): - retries = retries.increment( - method, url, response=http_response, _pool=self) - log.debug("Retry: %s", url) - retries.sleep(http_response) - return self.urlopen( - method, url, - body=body, headers=headers, - retries=retries, redirect=redirect, - timeout=timeout, **response_kw) - - return http_response - - def _urlfetch_response_to_http_response(self, urlfetch_resp, **response_kw): - - if is_prod_appengine(): - # Production GAE handles deflate encoding automatically, but does - # not remove the encoding header. - content_encoding = urlfetch_resp.headers.get('content-encoding') - - if content_encoding == 'deflate': - del urlfetch_resp.headers['content-encoding'] - - transfer_encoding = urlfetch_resp.headers.get('transfer-encoding') - # We have a full response's content, - # so let's make sure we don't report ourselves as chunked data. - if transfer_encoding == 'chunked': - encodings = transfer_encoding.split(",") - encodings.remove('chunked') - urlfetch_resp.headers['transfer-encoding'] = ','.join(encodings) - - return HTTPResponse( - # In order for decoding to work, we must present the content as - # a file-like object. - body=BytesIO(urlfetch_resp.content), - headers=urlfetch_resp.headers, - status=urlfetch_resp.status_code, - **response_kw - ) - - def _get_absolute_timeout(self, timeout): - if timeout is Timeout.DEFAULT_TIMEOUT: - return None # Defer to URLFetch's default. - if isinstance(timeout, Timeout): - if timeout._read is not None or timeout._connect is not None: - warnings.warn( - "URLFetch does not support granular timeout settings, " - "reverting to total or default URLFetch timeout.", - AppEnginePlatformWarning) - return timeout.total - return timeout - - def _get_retries(self, retries, redirect): - if not isinstance(retries, Retry): - retries = Retry.from_int( - retries, redirect=redirect, default=self.retries) - - if retries.connect or retries.read or retries.redirect: - warnings.warn( - "URLFetch only supports total retries and does not " - "recognize connect, read, or redirect retry parameters.", - AppEnginePlatformWarning) - - return retries - - -def is_appengine(): - return (is_local_appengine() or - is_prod_appengine() or - is_prod_appengine_mvms()) - - -def is_appengine_sandbox(): - return is_appengine() and not is_prod_appengine_mvms() - - -def is_local_appengine(): - return ('APPENGINE_RUNTIME' in os.environ and - 'Development/' in os.environ['SERVER_SOFTWARE']) - - -def is_prod_appengine(): - return ('APPENGINE_RUNTIME' in os.environ and - 'Google App Engine/' in os.environ['SERVER_SOFTWARE'] and - not is_prod_appengine_mvms()) - - -def is_prod_appengine_mvms(): - return os.environ.get('GAE_VM', False) == 'true' diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/ntlmpool.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/ntlmpool.py deleted file mode 100644 index 642e99e..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/ntlmpool.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -NTLM authenticating pool, contributed by erikcederstran - -Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10 -""" -from __future__ import absolute_import - -from logging import getLogger -from ntlm import ntlm - -from .. import HTTPSConnectionPool -from ..packages.six.moves.http_client import HTTPSConnection - - -log = getLogger(__name__) - - -class NTLMConnectionPool(HTTPSConnectionPool): - """ - Implements an NTLM authentication version of an urllib3 connection pool - """ - - scheme = 'https' - - def __init__(self, user, pw, authurl, *args, **kwargs): - """ - authurl is a random URL on the server that is protected by NTLM. - user is the Windows user, probably in the DOMAIN\\username format. - pw is the password for the user. - """ - super(NTLMConnectionPool, self).__init__(*args, **kwargs) - self.authurl = authurl - self.rawuser = user - user_parts = user.split('\\', 1) - self.domain = user_parts[0].upper() - self.user = user_parts[1] - self.pw = pw - - def _new_conn(self): - # Performs the NTLM handshake that secures the connection. The socket - # must be kept open while requests are performed. - self.num_connections += 1 - log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s', - self.num_connections, self.host, self.authurl) - - headers = {} - headers['Connection'] = 'Keep-Alive' - req_header = 'Authorization' - resp_header = 'www-authenticate' - - conn = HTTPSConnection(host=self.host, port=self.port) - - # Send negotiation message - headers[req_header] = ( - 'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser)) - log.debug('Request headers: %s', headers) - conn.request('GET', self.authurl, None, headers) - res = conn.getresponse() - reshdr = dict(res.getheaders()) - log.debug('Response status: %s %s', res.status, res.reason) - log.debug('Response headers: %s', reshdr) - log.debug('Response data: %s [...]', res.read(100)) - - # Remove the reference to the socket, so that it can not be closed by - # the response object (we want to keep the socket open) - res.fp = None - - # Server should respond with a challenge message - auth_header_values = reshdr[resp_header].split(', ') - auth_header_value = None - for s in auth_header_values: - if s[:5] == 'NTLM ': - auth_header_value = s[5:] - if auth_header_value is None: - raise Exception('Unexpected %s response header: %s' % - (resp_header, reshdr[resp_header])) - - # Send authentication message - ServerChallenge, NegotiateFlags = \ - ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value) - auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge, - self.user, - self.domain, - self.pw, - NegotiateFlags) - headers[req_header] = 'NTLM %s' % auth_msg - log.debug('Request headers: %s', headers) - conn.request('GET', self.authurl, None, headers) - res = conn.getresponse() - log.debug('Response status: %s %s', res.status, res.reason) - log.debug('Response headers: %s', dict(res.getheaders())) - log.debug('Response data: %s [...]', res.read()[:100]) - if res.status != 200: - if res.status == 401: - raise Exception('Server rejected request: wrong ' - 'username or password') - raise Exception('Wrong server response: %s %s' % - (res.status, res.reason)) - - res.fp = None - log.debug('Connection established') - return conn - - def urlopen(self, method, url, body=None, headers=None, retries=3, - redirect=True, assert_same_host=True): - if headers is None: - headers = {} - headers['Connection'] = 'Keep-Alive' - return super(NTLMConnectionPool, self).urlopen(method, url, body, - headers, retries, - redirect, - assert_same_host) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/pyopenssl.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/pyopenssl.py deleted file mode 100644 index eb4d476..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/pyopenssl.py +++ /dev/null @@ -1,450 +0,0 @@ -""" -SSL with SNI_-support for Python 2. Follow these instructions if you would -like to verify SSL certificates in Python 2. Note, the default libraries do -*not* do certificate checking; you need to do additional work to validate -certificates yourself. - -This needs the following packages installed: - -* pyOpenSSL (tested with 16.0.0) -* cryptography (minimum 1.3.4, from pyopenssl) -* idna (minimum 2.0, from cryptography) - -However, pyopenssl depends on cryptography, which depends on idna, so while we -use all three directly here we end up having relatively few packages required. - -You can install them with the following command: - - pip install pyopenssl cryptography idna - -To activate certificate checking, call -:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code -before you begin making HTTP requests. This can be done in a ``sitecustomize`` -module, or at any other time before your application begins using ``urllib3``, -like this:: - - try: - import urllib3.contrib.pyopenssl - urllib3.contrib.pyopenssl.inject_into_urllib3() - except ImportError: - pass - -Now you can use :mod:`urllib3` as you normally would, and it will support SNI -when the required modules are installed. - -Activating this module also has the positive side effect of disabling SSL/TLS -compression in Python 2 (see `CRIME attack`_). - -If you want to configure the default list of supported cipher suites, you can -set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable. - -.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication -.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit) -""" -from __future__ import absolute_import - -import OpenSSL.SSL -from cryptography import x509 -from cryptography.hazmat.backends.openssl import backend as openssl_backend -from cryptography.hazmat.backends.openssl.x509 import _Certificate - -from socket import timeout, error as SocketError -from io import BytesIO - -try: # Platform-specific: Python 2 - from socket import _fileobject -except ImportError: # Platform-specific: Python 3 - _fileobject = None - from ..packages.backports.makefile import backport_makefile - -import logging -import ssl -import six -import sys - -from .. import util - -__all__ = ['inject_into_urllib3', 'extract_from_urllib3'] - -# SNI always works. -HAS_SNI = True - -# Map from urllib3 to PyOpenSSL compatible parameter-values. -_openssl_versions = { - ssl.PROTOCOL_SSLv23: OpenSSL.SSL.SSLv23_METHOD, - ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, -} - -if hasattr(ssl, 'PROTOCOL_TLSv1_1') and hasattr(OpenSSL.SSL, 'TLSv1_1_METHOD'): - _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD - -if hasattr(ssl, 'PROTOCOL_TLSv1_2') and hasattr(OpenSSL.SSL, 'TLSv1_2_METHOD'): - _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD - -try: - _openssl_versions.update({ssl.PROTOCOL_SSLv3: OpenSSL.SSL.SSLv3_METHOD}) -except AttributeError: - pass - -_stdlib_to_openssl_verify = { - ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, - ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, - ssl.CERT_REQUIRED: - OpenSSL.SSL.VERIFY_PEER + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, -} -_openssl_to_stdlib_verify = dict( - (v, k) for k, v in _stdlib_to_openssl_verify.items() -) - -# OpenSSL will only write 16K at a time -SSL_WRITE_BLOCKSIZE = 16384 - -orig_util_HAS_SNI = util.HAS_SNI -orig_util_SSLContext = util.ssl_.SSLContext - - -log = logging.getLogger(__name__) - - -def inject_into_urllib3(): - 'Monkey-patch urllib3 with PyOpenSSL-backed SSL-support.' - - _validate_dependencies_met() - - util.ssl_.SSLContext = PyOpenSSLContext - util.HAS_SNI = HAS_SNI - util.ssl_.HAS_SNI = HAS_SNI - util.IS_PYOPENSSL = True - util.ssl_.IS_PYOPENSSL = True - - -def extract_from_urllib3(): - 'Undo monkey-patching by :func:`inject_into_urllib3`.' - - util.ssl_.SSLContext = orig_util_SSLContext - util.HAS_SNI = orig_util_HAS_SNI - util.ssl_.HAS_SNI = orig_util_HAS_SNI - util.IS_PYOPENSSL = False - util.ssl_.IS_PYOPENSSL = False - - -def _validate_dependencies_met(): - """ - Verifies that PyOpenSSL's package-level dependencies have been met. - Throws `ImportError` if they are not met. - """ - # Method added in `cryptography==1.1`; not available in older versions - from cryptography.x509.extensions import Extensions - if getattr(Extensions, "get_extension_for_class", None) is None: - raise ImportError("'cryptography' module missing required functionality. " - "Try upgrading to v1.3.4 or newer.") - - # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 - # attribute is only present on those versions. - from OpenSSL.crypto import X509 - x509 = X509() - if getattr(x509, "_x509", None) is None: - raise ImportError("'pyOpenSSL' module missing required functionality. " - "Try upgrading to v0.14 or newer.") - - -def _dnsname_to_stdlib(name): - """ - Converts a dNSName SubjectAlternativeName field to the form used by the - standard library on the given Python version. - - Cryptography produces a dNSName as a unicode string that was idna-decoded - from ASCII bytes. We need to idna-encode that string to get it back, and - then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib - uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). - """ - def idna_encode(name): - """ - Borrowed wholesale from the Python Cryptography Project. It turns out - that we can't just safely call `idna.encode`: it can explode for - wildcard names. This avoids that problem. - """ - import idna - - for prefix in [u'*.', u'.']: - if name.startswith(prefix): - name = name[len(prefix):] - return prefix.encode('ascii') + idna.encode(name) - return idna.encode(name) - - name = idna_encode(name) - if sys.version_info >= (3, 0): - name = name.decode('utf-8') - return name - - -def get_subj_alt_name(peer_cert): - """ - Given an PyOpenSSL certificate, provides all the subject alternative names. - """ - # Pass the cert to cryptography, which has much better APIs for this. - # This is technically using private APIs, but should work across all - # relevant versions until PyOpenSSL gets something proper for this. - cert = _Certificate(openssl_backend, peer_cert._x509) - - # We want to find the SAN extension. Ask Cryptography to locate it (it's - # faster than looping in Python) - try: - ext = cert.extensions.get_extension_for_class( - x509.SubjectAlternativeName - ).value - except x509.ExtensionNotFound: - # No such extension, return the empty list. - return [] - except (x509.DuplicateExtension, x509.UnsupportedExtension, - x509.UnsupportedGeneralNameType, UnicodeError) as e: - # A problem has been found with the quality of the certificate. Assume - # no SAN field is present. - log.warning( - "A problem was encountered with the certificate that prevented " - "urllib3 from finding the SubjectAlternativeName field. This can " - "affect certificate validation. The error was %s", - e, - ) - return [] - - # We want to return dNSName and iPAddress fields. We need to cast the IPs - # back to strings because the match_hostname function wants them as - # strings. - # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 - # decoded. This is pretty frustrating, but that's what the standard library - # does with certificates, and so we need to attempt to do the same. - names = [ - ('DNS', _dnsname_to_stdlib(name)) - for name in ext.get_values_for_type(x509.DNSName) - ] - names.extend( - ('IP Address', str(name)) - for name in ext.get_values_for_type(x509.IPAddress) - ) - - return names - - -class WrappedSocket(object): - '''API-compatibility wrapper for Python OpenSSL's Connection-class. - - Note: _makefile_refs, _drop() and _reuse() are needed for the garbage - collector of pypy. - ''' - - def __init__(self, connection, socket, suppress_ragged_eofs=True): - self.connection = connection - self.socket = socket - self.suppress_ragged_eofs = suppress_ragged_eofs - self._makefile_refs = 0 - self._closed = False - - def fileno(self): - return self.socket.fileno() - - # Copy-pasted from Python 3.5 source code - def _decref_socketios(self): - if self._makefile_refs > 0: - self._makefile_refs -= 1 - if self._closed: - self.close() - - def recv(self, *args, **kwargs): - try: - data = self.connection.recv(*args, **kwargs) - except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): - return b'' - else: - raise SocketError(str(e)) - except OpenSSL.SSL.ZeroReturnError as e: - if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return b'' - else: - raise - except OpenSSL.SSL.WantReadError: - rd = util.wait_for_read(self.socket, self.socket.gettimeout()) - if not rd: - raise timeout('The read operation timed out') - else: - return self.recv(*args, **kwargs) - else: - return data - - def recv_into(self, *args, **kwargs): - try: - return self.connection.recv_into(*args, **kwargs) - except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, 'Unexpected EOF'): - return 0 - else: - raise SocketError(str(e)) - except OpenSSL.SSL.ZeroReturnError as e: - if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return 0 - else: - raise - except OpenSSL.SSL.WantReadError: - rd = util.wait_for_read(self.socket, self.socket.gettimeout()) - if not rd: - raise timeout('The read operation timed out') - else: - return self.recv_into(*args, **kwargs) - - def settimeout(self, timeout): - return self.socket.settimeout(timeout) - - def _send_until_done(self, data): - while True: - try: - return self.connection.send(data) - except OpenSSL.SSL.WantWriteError: - wr = util.wait_for_write(self.socket, self.socket.gettimeout()) - if not wr: - raise timeout() - continue - - def sendall(self, data): - total_sent = 0 - while total_sent < len(data): - sent = self._send_until_done(data[total_sent:total_sent + SSL_WRITE_BLOCKSIZE]) - total_sent += sent - - def shutdown(self): - # FIXME rethrow compatible exceptions should we ever use this - self.connection.shutdown() - - def close(self): - if self._makefile_refs < 1: - try: - self._closed = True - return self.connection.close() - except OpenSSL.SSL.Error: - return - else: - self._makefile_refs -= 1 - - def getpeercert(self, binary_form=False): - x509 = self.connection.get_peer_certificate() - - if not x509: - return x509 - - if binary_form: - return OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_ASN1, - x509) - - return { - 'subject': ( - (('commonName', x509.get_subject().CN),), - ), - 'subjectAltName': get_subj_alt_name(x509) - } - - def _reuse(self): - self._makefile_refs += 1 - - def _drop(self): - if self._makefile_refs < 1: - self.close() - else: - self._makefile_refs -= 1 - - -if _fileobject: # Platform-specific: Python 2 - def makefile(self, mode, bufsize=-1): - self._makefile_refs += 1 - return _fileobject(self, mode, bufsize, close=True) -else: # Platform-specific: Python 3 - makefile = backport_makefile - -WrappedSocket.makefile = makefile - - -class PyOpenSSLContext(object): - """ - I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible - for translating the interface of the standard library ``SSLContext`` object - to calls into PyOpenSSL. - """ - def __init__(self, protocol): - self.protocol = _openssl_versions[protocol] - self._ctx = OpenSSL.SSL.Context(self.protocol) - self._options = 0 - self.check_hostname = False - - @property - def options(self): - return self._options - - @options.setter - def options(self, value): - self._options = value - self._ctx.set_options(value) - - @property - def verify_mode(self): - return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] - - @verify_mode.setter - def verify_mode(self, value): - self._ctx.set_verify( - _stdlib_to_openssl_verify[value], - _verify_callback - ) - - def set_default_verify_paths(self): - self._ctx.set_default_verify_paths() - - def set_ciphers(self, ciphers): - if isinstance(ciphers, six.text_type): - ciphers = ciphers.encode('utf-8') - self._ctx.set_cipher_list(ciphers) - - def load_verify_locations(self, cafile=None, capath=None, cadata=None): - if cafile is not None: - cafile = cafile.encode('utf-8') - if capath is not None: - capath = capath.encode('utf-8') - self._ctx.load_verify_locations(cafile, capath) - if cadata is not None: - self._ctx.load_verify_locations(BytesIO(cadata)) - - def load_cert_chain(self, certfile, keyfile=None, password=None): - self._ctx.use_certificate_file(certfile) - if password is not None: - self._ctx.set_passwd_cb(lambda max_length, prompt_twice, userdata: password) - self._ctx.use_privatekey_file(keyfile or certfile) - - def wrap_socket(self, sock, server_side=False, - do_handshake_on_connect=True, suppress_ragged_eofs=True, - server_hostname=None): - cnx = OpenSSL.SSL.Connection(self._ctx, sock) - - if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3 - server_hostname = server_hostname.encode('utf-8') - - if server_hostname is not None: - cnx.set_tlsext_host_name(server_hostname) - - cnx.set_connect_state() - - while True: - try: - cnx.do_handshake() - except OpenSSL.SSL.WantReadError: - rd = util.wait_for_read(sock, sock.gettimeout()) - if not rd: - raise timeout('select timed out') - continue - except OpenSSL.SSL.Error as e: - raise ssl.SSLError('bad handshake: %r' % e) - break - - return WrappedSocket(cnx, sock) - - -def _verify_callback(cnx, x509, err_no, err_depth, return_code): - return err_no == 0 diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/socks.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/socks.py deleted file mode 100644 index 811e312..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/contrib/socks.py +++ /dev/null @@ -1,192 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module contains provisional support for SOCKS proxies from within -urllib3. This module supports SOCKS4 (specifically the SOCKS4A variant) and -SOCKS5. To enable its functionality, either install PySocks or install this -module with the ``socks`` extra. - -The SOCKS implementation supports the full range of urllib3 features. It also -supports the following SOCKS features: - -- SOCKS4 -- SOCKS4a -- SOCKS5 -- Usernames and passwords for the SOCKS proxy - -Known Limitations: - -- Currently PySocks does not support contacting remote websites via literal - IPv6 addresses. Any such connection attempt will fail. You must use a domain - name. -- Currently PySocks does not support IPv6 connections to the SOCKS proxy. Any - such connection attempt will fail. -""" -from __future__ import absolute_import - -try: - import socks -except ImportError: - import warnings - from ..exceptions import DependencyWarning - - warnings.warn(( - 'SOCKS support in urllib3 requires the installation of optional ' - 'dependencies: specifically, PySocks. For more information, see ' - 'https://urllib3.readthedocs.io/en/latest/contrib.html#socks-proxies' - ), - DependencyWarning - ) - raise - -from socket import error as SocketError, timeout as SocketTimeout - -from ..connection import ( - HTTPConnection, HTTPSConnection -) -from ..connectionpool import ( - HTTPConnectionPool, HTTPSConnectionPool -) -from ..exceptions import ConnectTimeoutError, NewConnectionError -from ..poolmanager import PoolManager -from ..util.url import parse_url - -try: - import ssl -except ImportError: - ssl = None - - -class SOCKSConnection(HTTPConnection): - """ - A plain-text HTTP connection that connects via a SOCKS proxy. - """ - def __init__(self, *args, **kwargs): - self._socks_options = kwargs.pop('_socks_options') - super(SOCKSConnection, self).__init__(*args, **kwargs) - - def _new_conn(self): - """ - Establish a new connection via the SOCKS proxy. - """ - extra_kw = {} - if self.source_address: - extra_kw['source_address'] = self.source_address - - if self.socket_options: - extra_kw['socket_options'] = self.socket_options - - try: - conn = socks.create_connection( - (self.host, self.port), - proxy_type=self._socks_options['socks_version'], - proxy_addr=self._socks_options['proxy_host'], - proxy_port=self._socks_options['proxy_port'], - proxy_username=self._socks_options['username'], - proxy_password=self._socks_options['password'], - proxy_rdns=self._socks_options['rdns'], - timeout=self.timeout, - **extra_kw - ) - - except SocketTimeout as e: - raise ConnectTimeoutError( - self, "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout)) - - except socks.ProxyError as e: - # This is fragile as hell, but it seems to be the only way to raise - # useful errors here. - if e.socket_err: - error = e.socket_err - if isinstance(error, SocketTimeout): - raise ConnectTimeoutError( - self, - "Connection to %s timed out. (connect timeout=%s)" % - (self.host, self.timeout) - ) - else: - raise NewConnectionError( - self, - "Failed to establish a new connection: %s" % error - ) - else: - raise NewConnectionError( - self, - "Failed to establish a new connection: %s" % e - ) - - except SocketError as e: # Defensive: PySocks should catch all these. - raise NewConnectionError( - self, "Failed to establish a new connection: %s" % e) - - return conn - - -# We don't need to duplicate the Verified/Unverified distinction from -# urllib3/connection.py here because the HTTPSConnection will already have been -# correctly set to either the Verified or Unverified form by that module. This -# means the SOCKSHTTPSConnection will automatically be the correct type. -class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): - pass - - -class SOCKSHTTPConnectionPool(HTTPConnectionPool): - ConnectionCls = SOCKSConnection - - -class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): - ConnectionCls = SOCKSHTTPSConnection - - -class SOCKSProxyManager(PoolManager): - """ - A version of the urllib3 ProxyManager that routes connections via the - defined SOCKS proxy. - """ - pool_classes_by_scheme = { - 'http': SOCKSHTTPConnectionPool, - 'https': SOCKSHTTPSConnectionPool, - } - - def __init__(self, proxy_url, username=None, password=None, - num_pools=10, headers=None, **connection_pool_kw): - parsed = parse_url(proxy_url) - - if username is None and password is None and parsed.auth is not None: - split = parsed.auth.split(':') - if len(split) == 2: - username, password = split - if parsed.scheme == 'socks5': - socks_version = socks.PROXY_TYPE_SOCKS5 - rdns = False - elif parsed.scheme == 'socks5h': - socks_version = socks.PROXY_TYPE_SOCKS5 - rdns = True - elif parsed.scheme == 'socks4': - socks_version = socks.PROXY_TYPE_SOCKS4 - rdns = False - elif parsed.scheme == 'socks4a': - socks_version = socks.PROXY_TYPE_SOCKS4 - rdns = True - else: - raise ValueError( - "Unable to determine SOCKS version from %s" % proxy_url - ) - - self.proxy_url = proxy_url - - socks_options = { - 'socks_version': socks_version, - 'proxy_host': parsed.host, - 'proxy_port': parsed.port, - 'username': username, - 'password': password, - 'rdns': rdns - } - connection_pool_kw['_socks_options'] = socks_options - - super(SOCKSProxyManager, self).__init__( - num_pools, headers, **connection_pool_kw - ) - - self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/exceptions.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/exceptions.py deleted file mode 100644 index 6c4be58..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/exceptions.py +++ /dev/null @@ -1,246 +0,0 @@ -from __future__ import absolute_import -from .packages.six.moves.http_client import ( - IncompleteRead as httplib_IncompleteRead -) -# Base Exceptions - - -class HTTPError(Exception): - "Base exception used by this module." - pass - - -class HTTPWarning(Warning): - "Base warning used by this module." - pass - - -class PoolError(HTTPError): - "Base exception for errors caused within a pool." - def __init__(self, pool, message): - self.pool = pool - HTTPError.__init__(self, "%s: %s" % (pool, message)) - - def __reduce__(self): - # For pickling purposes. - return self.__class__, (None, None) - - -class RequestError(PoolError): - "Base exception for PoolErrors that have associated URLs." - def __init__(self, pool, url, message): - self.url = url - PoolError.__init__(self, pool, message) - - def __reduce__(self): - # For pickling purposes. - return self.__class__, (None, self.url, None) - - -class SSLError(HTTPError): - "Raised when SSL certificate fails in an HTTPS connection." - pass - - -class ProxyError(HTTPError): - "Raised when the connection to a proxy fails." - pass - - -class DecodeError(HTTPError): - "Raised when automatic decoding based on Content-Type fails." - pass - - -class ProtocolError(HTTPError): - "Raised when something unexpected happens mid-request/response." - pass - - -#: Renamed to ProtocolError but aliased for backwards compatibility. -ConnectionError = ProtocolError - - -# Leaf Exceptions - -class MaxRetryError(RequestError): - """Raised when the maximum number of retries is exceeded. - - :param pool: The connection pool - :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` - :param string url: The requested Url - :param exceptions.Exception reason: The underlying error - - """ - - def __init__(self, pool, url, reason=None): - self.reason = reason - - message = "Max retries exceeded with url: %s (Caused by %r)" % ( - url, reason) - - RequestError.__init__(self, pool, url, message) - - -class HostChangedError(RequestError): - "Raised when an existing pool gets a request for a foreign host." - - def __init__(self, pool, url, retries=3): - message = "Tried to open a foreign host with url: %s" % url - RequestError.__init__(self, pool, url, message) - self.retries = retries - - -class TimeoutStateError(HTTPError): - """ Raised when passing an invalid state to a timeout """ - pass - - -class TimeoutError(HTTPError): - """ Raised when a socket timeout error occurs. - - Catching this error will catch both :exc:`ReadTimeoutErrors - <ReadTimeoutError>` and :exc:`ConnectTimeoutErrors <ConnectTimeoutError>`. - """ - pass - - -class ReadTimeoutError(TimeoutError, RequestError): - "Raised when a socket timeout occurs while receiving data from a server" - pass - - -# This timeout error does not have a URL attached and needs to inherit from the -# base HTTPError -class ConnectTimeoutError(TimeoutError): - "Raised when a socket timeout occurs while connecting to a server" - pass - - -class NewConnectionError(ConnectTimeoutError, PoolError): - "Raised when we fail to establish a new connection. Usually ECONNREFUSED." - pass - - -class EmptyPoolError(PoolError): - "Raised when a pool runs out of connections and no more are allowed." - pass - - -class ClosedPoolError(PoolError): - "Raised when a request enters a pool after the pool has been closed." - pass - - -class LocationValueError(ValueError, HTTPError): - "Raised when there is something wrong with a given URL input." - pass - - -class LocationParseError(LocationValueError): - "Raised when get_host or similar fails to parse the URL input." - - def __init__(self, location): - message = "Failed to parse: %s" % location - HTTPError.__init__(self, message) - - self.location = location - - -class ResponseError(HTTPError): - "Used as a container for an error reason supplied in a MaxRetryError." - GENERIC_ERROR = 'too many error responses' - SPECIFIC_ERROR = 'too many {status_code} error responses' - - -class SecurityWarning(HTTPWarning): - "Warned when perfoming security reducing actions" - pass - - -class SubjectAltNameWarning(SecurityWarning): - "Warned when connecting to a host with a certificate missing a SAN." - pass - - -class InsecureRequestWarning(SecurityWarning): - "Warned when making an unverified HTTPS request." - pass - - -class SystemTimeWarning(SecurityWarning): - "Warned when system time is suspected to be wrong" - pass - - -class InsecurePlatformWarning(SecurityWarning): - "Warned when certain SSL configuration is not available on a platform." - pass - - -class SNIMissingWarning(HTTPWarning): - "Warned when making a HTTPS request without SNI available." - pass - - -class DependencyWarning(HTTPWarning): - """ - Warned when an attempt is made to import a module with missing optional - dependencies. - """ - pass - - -class ResponseNotChunked(ProtocolError, ValueError): - "Response needs to be chunked in order to read it as chunks." - pass - - -class BodyNotHttplibCompatible(HTTPError): - """ - Body should be httplib.HTTPResponse like (have an fp attribute which - returns raw chunks) for read_chunked(). - """ - pass - - -class IncompleteRead(HTTPError, httplib_IncompleteRead): - """ - Response length doesn't match expected Content-Length - - Subclass of http_client.IncompleteRead to allow int value - for `partial` to avoid creating large objects on streamed - reads. - """ - def __init__(self, partial, expected): - super(IncompleteRead, self).__init__(partial, expected) - - def __repr__(self): - return ('IncompleteRead(%i bytes read, ' - '%i more expected)' % (self.partial, self.expected)) - - -class InvalidHeader(HTTPError): - "The header provided was somehow invalid." - pass - - -class ProxySchemeUnknown(AssertionError, ValueError): - "ProxyManager does not support the supplied scheme" - # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. - - def __init__(self, scheme): - message = "Not supported proxy scheme %s" % scheme - super(ProxySchemeUnknown, self).__init__(message) - - -class HeaderParsingError(HTTPError): - "Raised by assert_header_parsing, but we convert it to a log.warning statement." - def __init__(self, defects, unparsed_data): - message = '%s, unparsed data: %r' % (defects or 'Unknown', unparsed_data) - super(HeaderParsingError, self).__init__(message) - - -class UnrewindableBodyError(HTTPError): - "urllib3 encountered an error when trying to rewind a body" - pass diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/fields.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/fields.py deleted file mode 100644 index 19b0ae0..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/fields.py +++ /dev/null @@ -1,178 +0,0 @@ -from __future__ import absolute_import -import email.utils -import mimetypes - -from .packages import six - - -def guess_content_type(filename, default='application/octet-stream'): - """ - Guess the "Content-Type" of a file. - - :param filename: - The filename to guess the "Content-Type" of using :mod:`mimetypes`. - :param default: - If no "Content-Type" can be guessed, default to `default`. - """ - if filename: - return mimetypes.guess_type(filename)[0] or default - return default - - -def format_header_param(name, value): - """ - Helper function to format and quote a single header parameter. - - Particularly useful for header parameters which might contain - non-ASCII values, like file names. This follows RFC 2231, as - suggested by RFC 2388 Section 4.4. - - :param name: - The name of the parameter, a string expected to be ASCII only. - :param value: - The value of the parameter, provided as a unicode string. - """ - if not any(ch in value for ch in '"\\\r\n'): - result = '%s="%s"' % (name, value) - try: - result.encode('ascii') - except (UnicodeEncodeError, UnicodeDecodeError): - pass - else: - return result - if not six.PY3 and isinstance(value, six.text_type): # Python 2: - value = value.encode('utf-8') - value = email.utils.encode_rfc2231(value, 'utf-8') - value = '%s*=%s' % (name, value) - return value - - -class RequestField(object): - """ - A data container for request body parameters. - - :param name: - The name of this request field. - :param data: - The data/value body. - :param filename: - An optional filename of the request field. - :param headers: - An optional dict-like object of headers to initially use for the field. - """ - def __init__(self, name, data, filename=None, headers=None): - self._name = name - self._filename = filename - self.data = data - self.headers = {} - if headers: - self.headers = dict(headers) - - @classmethod - def from_tuples(cls, fieldname, value): - """ - A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. - - Supports constructing :class:`~urllib3.fields.RequestField` from - parameter of key/value strings AND key/filetuple. A filetuple is a - (filename, data, MIME type) tuple where the MIME type is optional. - For example:: - - 'foo': 'bar', - 'fakefile': ('foofile.txt', 'contents of foofile'), - 'realfile': ('barfile.txt', open('realfile').read()), - 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), - 'nonamefile': 'contents of nonamefile field', - - Field names and filenames must be unicode. - """ - if isinstance(value, tuple): - if len(value) == 3: - filename, data, content_type = value - else: - filename, data = value - content_type = guess_content_type(filename) - else: - filename = None - content_type = None - data = value - - request_param = cls(fieldname, data, filename=filename) - request_param.make_multipart(content_type=content_type) - - return request_param - - def _render_part(self, name, value): - """ - Overridable helper function to format a single header parameter. - - :param name: - The name of the parameter, a string expected to be ASCII only. - :param value: - The value of the parameter, provided as a unicode string. - """ - return format_header_param(name, value) - - def _render_parts(self, header_parts): - """ - Helper function to format and quote a single header. - - Useful for single headers that are composed of multiple items. E.g., - 'Content-Disposition' fields. - - :param header_parts: - A sequence of (k, v) typles or a :class:`dict` of (k, v) to format - as `k1="v1"; k2="v2"; ...`. - """ - parts = [] - iterable = header_parts - if isinstance(header_parts, dict): - iterable = header_parts.items() - - for name, value in iterable: - if value is not None: - parts.append(self._render_part(name, value)) - - return '; '.join(parts) - - def render_headers(self): - """ - Renders the headers for this request field. - """ - lines = [] - - sort_keys = ['Content-Disposition', 'Content-Type', 'Content-Location'] - for sort_key in sort_keys: - if self.headers.get(sort_key, False): - lines.append('%s: %s' % (sort_key, self.headers[sort_key])) - - for header_name, header_value in self.headers.items(): - if header_name not in sort_keys: - if header_value: - lines.append('%s: %s' % (header_name, header_value)) - - lines.append('\r\n') - return '\r\n'.join(lines) - - def make_multipart(self, content_disposition=None, content_type=None, - content_location=None): - """ - Makes this request field into a multipart request field. - - This method overrides "Content-Disposition", "Content-Type" and - "Content-Location" headers to the request parameter. - - :param content_type: - The 'Content-Type' of the request body. - :param content_location: - The 'Content-Location' of the request body. - - """ - self.headers['Content-Disposition'] = content_disposition or 'form-data' - self.headers['Content-Disposition'] += '; '.join([ - '', self._render_parts( - (('name', self._name), ('filename', self._filename)) - ) - ]) - self.headers['Content-Type'] = content_type - self.headers['Content-Location'] = content_location diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/filepost.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/filepost.py deleted file mode 100644 index cd11cee..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/filepost.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import absolute_import -import codecs - -from uuid import uuid4 -from io import BytesIO - -from .packages import six -from .packages.six import b -from .fields import RequestField - -writer = codecs.lookup('utf-8')[3] - - -def choose_boundary(): - """ - Our embarrassingly-simple replacement for mimetools.choose_boundary. - """ - return uuid4().hex - - -def iter_field_objects(fields): - """ - Iterate over fields. - - Supports list of (k, v) tuples and dicts, and lists of - :class:`~urllib3.fields.RequestField`. - - """ - if isinstance(fields, dict): - i = six.iteritems(fields) - else: - i = iter(fields) - - for field in i: - if isinstance(field, RequestField): - yield field - else: - yield RequestField.from_tuples(*field) - - -def iter_fields(fields): - """ - .. deprecated:: 1.6 - - Iterate over fields. - - The addition of :class:`~urllib3.fields.RequestField` makes this function - obsolete. Instead, use :func:`iter_field_objects`, which returns - :class:`~urllib3.fields.RequestField` objects. - - Supports list of (k, v) tuples and dicts. - """ - if isinstance(fields, dict): - return ((k, v) for k, v in six.iteritems(fields)) - - return ((k, v) for k, v in fields) - - -def encode_multipart_formdata(fields, boundary=None): - """ - Encode a dictionary of ``fields`` using the multipart/form-data MIME format. - - :param fields: - Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). - - :param boundary: - If not specified, then a random boundary will be generated using - :func:`mimetools.choose_boundary`. - """ - body = BytesIO() - if boundary is None: - boundary = choose_boundary() - - for field in iter_field_objects(fields): - body.write(b('--%s\r\n' % (boundary))) - - writer(body).write(field.render_headers()) - data = field.data - - if isinstance(data, int): - data = str(data) # Backwards compatibility - - if isinstance(data, six.text_type): - writer(body).write(data) - else: - body.write(data) - - body.write(b'\r\n') - - body.write(b('--%s--\r\n' % (boundary))) - - content_type = str('multipart/form-data; boundary=%s' % boundary) - - return body.getvalue(), content_type diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/__init__.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/__init__.py deleted file mode 100644 index 170e974..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import absolute_import - -from . import ssl_match_hostname - -__all__ = ('ssl_match_hostname', ) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/__init__.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/makefile.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/makefile.py deleted file mode 100644 index 75b80dc..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/backports/makefile.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -""" -backports.makefile -~~~~~~~~~~~~~~~~~~ - -Backports the Python 3 ``socket.makefile`` method for use with anything that -wants to create a "fake" socket object. -""" -import io - -from socket import SocketIO - - -def backport_makefile(self, mode="r", buffering=None, encoding=None, - errors=None, newline=None): - """ - Backport of ``socket.makefile`` from Python 3.5. - """ - if not set(mode) <= set(["r", "w", "b"]): - raise ValueError( - "invalid mode %r (only r, w, b allowed)" % (mode,) - ) - writing = "w" in mode - reading = "r" in mode or not writing - assert reading or writing - binary = "b" in mode - rawmode = "" - if reading: - rawmode += "r" - if writing: - rawmode += "w" - raw = SocketIO(self, rawmode) - self._makefile_refs += 1 - if buffering is None: - buffering = -1 - if buffering < 0: - buffering = io.DEFAULT_BUFFER_SIZE - if buffering == 0: - if not binary: - raise ValueError("unbuffered streams must be binary") - return raw - if reading and writing: - buffer = io.BufferedRWPair(raw, raw, buffering) - elif reading: - buffer = io.BufferedReader(raw, buffering) - else: - assert writing - buffer = io.BufferedWriter(raw, buffering) - if binary: - return buffer - text = io.TextIOWrapper(buffer, encoding, errors, newline) - text.mode = mode - return text diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ordered_dict.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ordered_dict.py deleted file mode 100644 index 4479363..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ordered_dict.py +++ /dev/null @@ -1,259 +0,0 @@ -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. -# Copyright 2009 Raymond Hettinger, released under the MIT License. -# http://code.activestate.com/recipes/576693/ -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - ''' - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/six.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/six.py deleted file mode 100644 index 190c023..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# 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. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson <benjamin@python.org>" -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/__init__.p b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/__init__.p deleted file mode 100644 index d6594eb..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/__init__.p +++ /dev/null @@ -1,19 +0,0 @@ -import sys - -try: - # Our match_hostname function is the same as 3.5's, so we only want to - # import the match_hostname function if it's at least that good. - if sys.version_info < (3, 5): - raise ImportError("Fallback to vendored code") - - from ssl import CertificateError, match_hostname -except ImportError: - try: - # Backport of the function from a pypi module - from backports.ssl_match_hostname import CertificateError, match_hostname - except ImportError: - # Our vendored copy - from ._implementation import CertificateError, match_hostname - -# Not needed, but documenting what we provide. -__all__ = ('CertificateError', 'match_hostname') diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/_implement b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/_implement deleted file mode 100644 index 1fd42f3..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/packages/ssl_match_hostname/_implement +++ /dev/null @@ -1,157 +0,0 @@ -"""The match_hostname() function from Python 3.3.3, essential when using SSL.""" - -# Note: This file is under the PSF license as the code comes from the python -# stdlib. http://docs.python.org/3/license.html - -import re -import sys - -# ipaddress has been backported to 2.6+ in pypi. If it is installed on the -# system, use it to handle IPAddress ServerAltnames (this was added in -# python-3.5) otherwise only do DNS matching. This allows -# backports.ssl_match_hostname to continue to be used all the way back to -# python-2.4. -try: - import ipaddress -except ImportError: - ipaddress = None - -__version__ = '3.5.0.1' - - -class CertificateError(ValueError): - pass - - -def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - http://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - # Ported from python3-syntax: - # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r'.') - leftmost = parts[0] - remainder = parts[1:] - - wildcards = leftmost.count('*') - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == '*': - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append('[^.]+') - elif leftmost.startswith('xn--') or hostname.startswith('xn--'): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) - return pat.match(hostname) - - -def _to_unicode(obj): - if isinstance(obj, str) and sys.version_info < (3,): - obj = unicode(obj, encoding='ascii', errors='strict') - return obj - -def _ipaddress_match(ipname, host_ip): - """Exact matching of IP addresses. - - RFC 6125 explicitly doesn't define an algorithm for this - (section 1.7.2 - "Out of Scope"). - """ - # OpenSSL may add a trailing newline to a subjectAltName's IP address - # Divergence from upstream: ipaddress can't handle byte str - ip = ipaddress.ip_address(_to_unicode(ipname).rstrip()) - return ip == host_ip - - -def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError("empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED") - try: - # Divergence from upstream: ipaddress can't handle byte str - host_ip = ipaddress.ip_address(_to_unicode(hostname)) - except ValueError: - # Not an IP address (common case) - host_ip = None - except UnicodeError: - # Divergence from upstream: Have to deal with ipaddress not taking - # byte strings. addresses should be all ascii, so we consider it not - # an ipaddress in this case - host_ip = None - except AttributeError: - # Divergence from upstream: Make ipaddress library optional - if ipaddress is None: - host_ip = None - else: - raise - dnsnames = [] - san = cert.get('subjectAltName', ()) - for key, value in san: - if key == 'DNS': - if host_ip is None and _dnsname_match(value, hostname): - return - dnsnames.append(value) - elif key == 'IP Address': - if host_ip is not None and _ipaddress_match(value, host_ip): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get('subject', ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == 'commonName': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError("hostname %r " - "doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) - elif len(dnsnames) == 1: - raise CertificateError("hostname %r " - "doesn't match %r" - % (hostname, dnsnames[0])) - else: - raise CertificateError("no appropriate commonName or " - "subjectAltName fields were found") diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/poolmanager.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/poolmanager.py deleted file mode 100644 index cc5a00e..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/poolmanager.py +++ /dev/null @@ -1,363 +0,0 @@ -from __future__ import absolute_import -import collections -import functools -import logging - -from ._collections import RecentlyUsedContainer -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool -from .connectionpool import port_by_scheme -from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown -from .packages.six.moves.urllib.parse import urljoin -from .request import RequestMethods -from .util.url import parse_url -from .util.retry import Retry - - -__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url'] - - -log = logging.getLogger(__name__) - -SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs', - 'ssl_version', 'ca_cert_dir', 'ssl_context') - -# The base fields to use when determining what pool to get a connection from; -# these do not rely on the ``connection_pool_kw`` and can be determined by the -# URL and potentially the ``urllib3.connection.port_by_scheme`` dictionary. -# -# All custom key schemes should include the fields in this key at a minimum. -BasePoolKey = collections.namedtuple('BasePoolKey', ('scheme', 'host', 'port')) - -# The fields to use when determining what pool to get a HTTP and HTTPS -# connection from. All additional fields must be present in the PoolManager's -# ``connection_pool_kw`` instance variable. -HTTPPoolKey = collections.namedtuple( - 'HTTPPoolKey', BasePoolKey._fields + ('timeout', 'retries', 'strict', - 'block', 'source_address') -) -HTTPSPoolKey = collections.namedtuple( - 'HTTPSPoolKey', HTTPPoolKey._fields + SSL_KEYWORDS -) - - -def _default_key_normalizer(key_class, request_context): - """ - Create a pool key of type ``key_class`` for a request. - - According to RFC 3986, both the scheme and host are case-insensitive. - Therefore, this function normalizes both before constructing the pool - key for an HTTPS request. If you wish to change this behaviour, provide - alternate callables to ``key_fn_by_scheme``. - - :param key_class: - The class to use when constructing the key. This should be a namedtuple - with the ``scheme`` and ``host`` keys at a minimum. - - :param request_context: - A dictionary-like object that contain the context for a request. - It should contain a key for each field in the :class:`HTTPPoolKey` - """ - context = {} - for key in key_class._fields: - context[key] = request_context.get(key) - context['scheme'] = context['scheme'].lower() - context['host'] = context['host'].lower() - return key_class(**context) - - -# A dictionary that maps a scheme to a callable that creates a pool key. -# This can be used to alter the way pool keys are constructed, if desired. -# Each PoolManager makes a copy of this dictionary so they can be configured -# globally here, or individually on the instance. -key_fn_by_scheme = { - 'http': functools.partial(_default_key_normalizer, HTTPPoolKey), - 'https': functools.partial(_default_key_normalizer, HTTPSPoolKey), -} - -pool_classes_by_scheme = { - 'http': HTTPConnectionPool, - 'https': HTTPSConnectionPool, -} - - -class PoolManager(RequestMethods): - """ - Allows for arbitrary requests while transparently keeping track of - necessary connection pools for you. - - :param num_pools: - Number of connection pools to cache before discarding the least - recently used pool. - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - - :param \\**connection_pool_kw: - Additional parameters are used to create fresh - :class:`urllib3.connectionpool.ConnectionPool` instances. - - Example:: - - >>> manager = PoolManager(num_pools=2) - >>> r = manager.request('GET', 'http://google.com/') - >>> r = manager.request('GET', 'http://google.com/mail') - >>> r = manager.request('GET', 'http://yahoo.com/') - >>> len(manager.pools) - 2 - - """ - - proxy = None - - def __init__(self, num_pools=10, headers=None, **connection_pool_kw): - RequestMethods.__init__(self, headers) - self.connection_pool_kw = connection_pool_kw - self.pools = RecentlyUsedContainer(num_pools, - dispose_func=lambda p: p.close()) - - # Locally set the pool classes and keys so other PoolManagers can - # override them. - self.pool_classes_by_scheme = pool_classes_by_scheme - self.key_fn_by_scheme = key_fn_by_scheme.copy() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.clear() - # Return False to re-raise any potential exceptions - return False - - def _new_pool(self, scheme, host, port): - """ - Create a new :class:`ConnectionPool` based on host, port and scheme. - - This method is used to actually create the connection pools handed out - by :meth:`connection_from_url` and companion methods. It is intended - to be overridden for customization. - """ - pool_cls = self.pool_classes_by_scheme[scheme] - kwargs = self.connection_pool_kw - if scheme == 'http': - kwargs = self.connection_pool_kw.copy() - for kw in SSL_KEYWORDS: - kwargs.pop(kw, None) - - return pool_cls(host, port, **kwargs) - - def clear(self): - """ - Empty our store of pools and direct them all to close. - - This will not affect in-flight connections, but they will not be - re-used after completion. - """ - self.pools.clear() - - def connection_from_host(self, host, port=None, scheme='http'): - """ - Get a :class:`ConnectionPool` based on the host, port, and scheme. - - If ``port`` isn't given, it will be derived from the ``scheme`` using - ``urllib3.connectionpool.port_by_scheme``. - """ - - if not host: - raise LocationValueError("No host specified.") - - request_context = self.connection_pool_kw.copy() - request_context['scheme'] = scheme or 'http' - if not port: - port = port_by_scheme.get(request_context['scheme'].lower(), 80) - request_context['port'] = port - request_context['host'] = host - - return self.connection_from_context(request_context) - - def connection_from_context(self, request_context): - """ - Get a :class:`ConnectionPool` based on the request context. - - ``request_context`` must at least contain the ``scheme`` key and its - value must be a key in ``key_fn_by_scheme`` instance variable. - """ - scheme = request_context['scheme'].lower() - pool_key_constructor = self.key_fn_by_scheme[scheme] - pool_key = pool_key_constructor(request_context) - - return self.connection_from_pool_key(pool_key) - - def connection_from_pool_key(self, pool_key): - """ - Get a :class:`ConnectionPool` based on the provided pool key. - - ``pool_key`` should be a namedtuple that only contains immutable - objects. At a minimum it must have the ``scheme``, ``host``, and - ``port`` fields. - """ - with self.pools.lock: - # If the scheme, host, or port doesn't match existing open - # connections, open a new ConnectionPool. - pool = self.pools.get(pool_key) - if pool: - return pool - - # Make a fresh ConnectionPool of the desired type - pool = self._new_pool(pool_key.scheme, pool_key.host, pool_key.port) - self.pools[pool_key] = pool - - return pool - - def connection_from_url(self, url): - """ - Similar to :func:`urllib3.connectionpool.connection_from_url` but - doesn't pass any additional parameters to the - :class:`urllib3.connectionpool.ConnectionPool` constructor. - - Additional parameters are taken from the :class:`.PoolManager` - constructor. - """ - u = parse_url(url) - return self.connection_from_host(u.host, port=u.port, scheme=u.scheme) - - def urlopen(self, method, url, redirect=True, **kw): - """ - Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen` - with custom cross-host redirect logic and only sends the request-uri - portion of the ``url``. - - The given ``url`` parameter must be absolute, such that an appropriate - :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. - """ - u = parse_url(url) - conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) - - kw['assert_same_host'] = False - kw['redirect'] = False - if 'headers' not in kw: - kw['headers'] = self.headers - - if self.proxy is not None and u.scheme == "http": - response = conn.urlopen(method, url, **kw) - else: - response = conn.urlopen(method, u.request_uri, **kw) - - redirect_location = redirect and response.get_redirect_location() - if not redirect_location: - return response - - # Support relative URLs for redirecting. - redirect_location = urljoin(url, redirect_location) - - # RFC 7231, Section 6.4.4 - if response.status == 303: - method = 'GET' - - retries = kw.get('retries') - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect) - - try: - retries = retries.increment(method, url, response=response, _pool=conn) - except MaxRetryError: - if retries.raise_on_redirect: - raise - return response - - kw['retries'] = retries - kw['redirect'] = redirect - - log.info("Redirecting %s -> %s", url, redirect_location) - return self.urlopen(method, redirect_location, **kw) - - -class ProxyManager(PoolManager): - """ - Behaves just like :class:`PoolManager`, but sends all requests through - the defined proxy, using the CONNECT method for HTTPS URLs. - - :param proxy_url: - The URL of the proxy to be used. - - :param proxy_headers: - A dictionary contaning headers that will be sent to the proxy. In case - of HTTP they are being sent with each request, while in the - HTTPS/CONNECT case they are sent only once. Could be used for proxy - authentication. - - Example: - >>> proxy = urllib3.ProxyManager('http://localhost:3128/') - >>> r1 = proxy.request('GET', 'http://google.com/') - >>> r2 = proxy.request('GET', 'http://httpbin.org/') - >>> len(proxy.pools) - 1 - >>> r3 = proxy.request('GET', 'https://httpbin.org/') - >>> r4 = proxy.request('GET', 'https://twitter.com/') - >>> len(proxy.pools) - 3 - - """ - - def __init__(self, proxy_url, num_pools=10, headers=None, - proxy_headers=None, **connection_pool_kw): - - if isinstance(proxy_url, HTTPConnectionPool): - proxy_url = '%s://%s:%i' % (proxy_url.scheme, proxy_url.host, - proxy_url.port) - proxy = parse_url(proxy_url) - if not proxy.port: - port = port_by_scheme.get(proxy.scheme, 80) - proxy = proxy._replace(port=port) - - if proxy.scheme not in ("http", "https"): - raise ProxySchemeUnknown(proxy.scheme) - - self.proxy = proxy - self.proxy_headers = proxy_headers or {} - - connection_pool_kw['_proxy'] = self.proxy - connection_pool_kw['_proxy_headers'] = self.proxy_headers - - super(ProxyManager, self).__init__( - num_pools, headers, **connection_pool_kw) - - def connection_from_host(self, host, port=None, scheme='http'): - if scheme == "https": - return super(ProxyManager, self).connection_from_host( - host, port, scheme) - - return super(ProxyManager, self).connection_from_host( - self.proxy.host, self.proxy.port, self.proxy.scheme) - - def _set_proxy_headers(self, url, headers=None): - """ - Sets headers needed by proxies: specifically, the Accept and Host - headers. Only sets headers not provided by the user. - """ - headers_ = {'Accept': '*/*'} - - netloc = parse_url(url).netloc - if netloc: - headers_['Host'] = netloc - - if headers: - headers_.update(headers) - return headers_ - - def urlopen(self, method, url, redirect=True, **kw): - "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." - u = parse_url(url) - - if u.scheme == "http": - # For proxied HTTPS requests, httplib sets the necessary headers - # on the CONNECT to the proxy. For HTTP, we'll definitely - # need to set 'Host' at the very least. - headers = kw.get('headers', self.headers) - kw['headers'] = self._set_proxy_headers(url, headers) - - return super(ProxyManager, self).urlopen(method, url, redirect=redirect, **kw) - - -def proxy_from_url(url, **kw): - return ProxyManager(proxy_url=url, **kw) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/request.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/request.py deleted file mode 100644 index c0fddff..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/request.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import absolute_import - -from .filepost import encode_multipart_formdata -from .packages.six.moves.urllib.parse import urlencode - - -__all__ = ['RequestMethods'] - - -class RequestMethods(object): - """ - Convenience mixin for classes who implement a :meth:`urlopen` method, such - as :class:`~urllib3.connectionpool.HTTPConnectionPool` and - :class:`~urllib3.poolmanager.PoolManager`. - - Provides behavior for making common types of HTTP request methods and - decides which type of request field encoding to use. - - Specifically, - - :meth:`.request_encode_url` is for sending requests whose fields are - encoded in the URL (such as GET, HEAD, DELETE). - - :meth:`.request_encode_body` is for sending requests whose fields are - encoded in the *body* of the request using multipart or www-form-urlencoded - (such as for POST, PUT, PATCH). - - :meth:`.request` is for making any kind of request, it will look up the - appropriate encoding format and use one of the above two methods to make - the request. - - Initializer parameters: - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - """ - - _encode_url_methods = set(['DELETE', 'GET', 'HEAD', 'OPTIONS']) - - def __init__(self, headers=None): - self.headers = headers or {} - - def urlopen(self, method, url, body=None, headers=None, - encode_multipart=True, multipart_boundary=None, - **kw): # Abstract - raise NotImplemented("Classes extending RequestMethods must implement " - "their own ``urlopen`` method.") - - def request(self, method, url, fields=None, headers=None, **urlopen_kw): - """ - Make a request using :meth:`urlopen` with the appropriate encoding of - ``fields`` based on the ``method`` used. - - This is a convenience method that requires the least amount of manual - effort. It can be used in most situations, while still having the - option to drop down to more specific methods when necessary, such as - :meth:`request_encode_url`, :meth:`request_encode_body`, - or even the lowest level :meth:`urlopen`. - """ - method = method.upper() - - if method in self._encode_url_methods: - return self.request_encode_url(method, url, fields=fields, - headers=headers, - **urlopen_kw) - else: - return self.request_encode_body(method, url, fields=fields, - headers=headers, - **urlopen_kw) - - def request_encode_url(self, method, url, fields=None, headers=None, - **urlopen_kw): - """ - Make a request using :meth:`urlopen` with the ``fields`` encoded in - the url. This is useful for request methods like GET, HEAD, DELETE, etc. - """ - if headers is None: - headers = self.headers - - extra_kw = {'headers': headers} - extra_kw.update(urlopen_kw) - - if fields: - url += '?' + urlencode(fields) - - return self.urlopen(method, url, **extra_kw) - - def request_encode_body(self, method, url, fields=None, headers=None, - encode_multipart=True, multipart_boundary=None, - **urlopen_kw): - """ - Make a request using :meth:`urlopen` with the ``fields`` encoded in - the body. This is useful for request methods like POST, PUT, PATCH, etc. - - When ``encode_multipart=True`` (default), then - :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode - the payload with the appropriate content type. Otherwise - :meth:`urllib.urlencode` is used with the - 'application/x-www-form-urlencoded' content type. - - Multipart encoding must be used when posting files, and it's reasonably - safe to use it in other times too. However, it may break request - signing, such as with OAuth. - - Supports an optional ``fields`` parameter of key/value strings AND - key/filetuple. A filetuple is a (filename, data, MIME type) tuple where - the MIME type is optional. For example:: - - fields = { - 'foo': 'bar', - 'fakefile': ('foofile.txt', 'contents of foofile'), - 'realfile': ('barfile.txt', open('realfile').read()), - 'typedfile': ('bazfile.bin', open('bazfile').read(), - 'image/jpeg'), - 'nonamefile': 'contents of nonamefile field', - } - - When uploading a file, providing a filename (the first parameter of the - tuple) is optional but recommended to best mimick behavior of browsers. - - Note that if ``headers`` are supplied, the 'Content-Type' header will - be overwritten because it depends on the dynamic random boundary string - which is used to compose the body of the request. The random boundary - string can be explicitly set with the ``multipart_boundary`` parameter. - """ - if headers is None: - headers = self.headers - - extra_kw = {'headers': {}} - - if fields: - if 'body' in urlopen_kw: - raise TypeError( - "request got values for both 'fields' and 'body', can only specify one.") - - if encode_multipart: - body, content_type = encode_multipart_formdata(fields, boundary=multipart_boundary) - else: - body, content_type = urlencode(fields), 'application/x-www-form-urlencoded' - - extra_kw['body'] = body - extra_kw['headers'] = {'Content-Type': content_type} - - extra_kw['headers'].update(headers) - extra_kw.update(urlopen_kw) - - return self.urlopen(method, url, **extra_kw) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/response.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/response.py deleted file mode 100644 index 6f1b63c..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/response.py +++ /dev/null @@ -1,618 +0,0 @@ -from __future__ import absolute_import -from contextlib import contextmanager -import zlib -import io -import logging -from socket import timeout as SocketTimeout -from socket import error as SocketError - -from ._collections import HTTPHeaderDict -from .exceptions import ( - BodyNotHttplibCompatible, ProtocolError, DecodeError, ReadTimeoutError, - ResponseNotChunked, IncompleteRead, InvalidHeader -) -from .packages.six import string_types as basestring, binary_type, PY3 -from .packages.six.moves import http_client as httplib -from .connection import HTTPException, BaseSSLError -from .util.response import is_fp_closed, is_response_to_head - -log = logging.getLogger(__name__) - - -class DeflateDecoder(object): - - def __init__(self): - self._first_try = True - self._data = binary_type() - self._obj = zlib.decompressobj() - - def __getattr__(self, name): - return getattr(self._obj, name) - - def decompress(self, data): - if not data: - return data - - if not self._first_try: - return self._obj.decompress(data) - - self._data += data - try: - return self._obj.decompress(data) - except zlib.error: - self._first_try = False - self._obj = zlib.decompressobj(-zlib.MAX_WBITS) - try: - return self.decompress(self._data) - finally: - self._data = None - - -class GzipDecoder(object): - - def __init__(self): - self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) - - def __getattr__(self, name): - return getattr(self._obj, name) - - def decompress(self, data): - if not data: - return data - return self._obj.decompress(data) - - -def _get_decoder(mode): - if mode == 'gzip': - return GzipDecoder() - - return DeflateDecoder() - - -class HTTPResponse(io.IOBase): - """ - HTTP Response container. - - Backwards-compatible to httplib's HTTPResponse but the response ``body`` is - loaded and decoded on-demand when the ``data`` property is accessed. This - class is also compatible with the Python standard library's :mod:`io` - module, and can hence be treated as a readable object in the context of that - framework. - - Extra parameters for behaviour not present in httplib.HTTPResponse: - - :param preload_content: - If True, the response's body will be preloaded during construction. - - :param decode_content: - If True, attempts to decode specific content-encoding's based on headers - (like 'gzip' and 'deflate') will be skipped and raw data will be used - instead. - - :param original_response: - When this HTTPResponse wrapper is generated from an httplib.HTTPResponse - object, it's convenient to include the original for debug purposes. It's - otherwise unused. - - :param retries: - The retries contains the last :class:`~urllib3.util.retry.Retry` that - was used during the request. - - :param enforce_content_length: - Enforce content length checking. Body returned by server must match - value of Content-Length header, if present. Otherwise, raise error. - """ - - CONTENT_DECODERS = ['gzip', 'deflate'] - REDIRECT_STATUSES = [301, 302, 303, 307, 308] - - def __init__(self, body='', headers=None, status=0, version=0, reason=None, - strict=0, preload_content=True, decode_content=True, - original_response=None, pool=None, connection=None, - retries=None, enforce_content_length=False, request_method=None): - - if isinstance(headers, HTTPHeaderDict): - self.headers = headers - else: - self.headers = HTTPHeaderDict(headers) - self.status = status - self.version = version - self.reason = reason - self.strict = strict - self.decode_content = decode_content - self.retries = retries - self.enforce_content_length = enforce_content_length - - self._decoder = None - self._body = None - self._fp = None - self._original_response = original_response - self._fp_bytes_read = 0 - - if body and isinstance(body, (basestring, binary_type)): - self._body = body - - self._pool = pool - self._connection = connection - - if hasattr(body, 'read'): - self._fp = body - - # Are we using the chunked-style of transfer encoding? - self.chunked = False - self.chunk_left = None - tr_enc = self.headers.get('transfer-encoding', '').lower() - # Don't incur the penalty of creating a list and then discarding it - encodings = (enc.strip() for enc in tr_enc.split(",")) - if "chunked" in encodings: - self.chunked = True - - # Determine length of response - self.length_remaining = self._init_length(request_method) - - # If requested, preload the body. - if preload_content and not self._body: - self._body = self.read(decode_content=decode_content) - - def get_redirect_location(self): - """ - Should we redirect and where to? - - :returns: Truthy redirect location string if we got a redirect status - code and valid location. ``None`` if redirect status and no - location. ``False`` if not a redirect status code. - """ - if self.status in self.REDIRECT_STATUSES: - return self.headers.get('location') - - return False - - def release_conn(self): - if not self._pool or not self._connection: - return - - self._pool._put_conn(self._connection) - self._connection = None - - @property - def data(self): - # For backwords-compat with earlier urllib3 0.4 and earlier. - if self._body: - return self._body - - if self._fp: - return self.read(cache_content=True) - - @property - def connection(self): - return self._connection - - def tell(self): - """ - Obtain the number of bytes pulled over the wire so far. May differ from - the amount of content returned by :meth:``HTTPResponse.read`` if bytes - are encoded on the wire (e.g, compressed). - """ - return self._fp_bytes_read - - def _init_length(self, request_method): - """ - Set initial length value for Response content if available. - """ - length = self.headers.get('content-length') - - if length is not None and self.chunked: - # This Response will fail with an IncompleteRead if it can't be - # received as chunked. This method falls back to attempt reading - # the response before raising an exception. - log.warning("Received response with both Content-Length and " - "Transfer-Encoding set. This is expressly forbidden " - "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " - "attempting to process response as Transfer-Encoding: " - "chunked.") - return None - - elif length is not None: - try: - # RFC 7230 section 3.3.2 specifies multiple content lengths can - # be sent in a single Content-Length header - # (e.g. Content-Length: 42, 42). This line ensures the values - # are all valid ints and that as long as the `set` length is 1, - # all values are the same. Otherwise, the header is invalid. - lengths = set([int(val) for val in length.split(',')]) - if len(lengths) > 1: - raise InvalidHeader("Content-Length contained multiple " - "unmatching values (%s)" % length) - length = lengths.pop() - except ValueError: - length = None - else: - if length < 0: - length = None - - # Convert status to int for comparison - # In some cases, httplib returns a status of "_UNKNOWN" - try: - status = int(self.status) - except ValueError: - status = 0 - - # Check for responses that shouldn't include a body - if status in (204, 304) or 100 <= status < 200 or request_method == 'HEAD': - length = 0 - - return length - - def _init_decoder(self): - """ - Set-up the _decoder attribute if necessary. - """ - # Note: content-encoding value should be case-insensitive, per RFC 7230 - # Section 3.2 - content_encoding = self.headers.get('content-encoding', '').lower() - if self._decoder is None and content_encoding in self.CONTENT_DECODERS: - self._decoder = _get_decoder(content_encoding) - - def _decode(self, data, decode_content, flush_decoder): - """ - Decode the data passed in and potentially flush the decoder. - """ - try: - if decode_content and self._decoder: - data = self._decoder.decompress(data) - except (IOError, zlib.error) as e: - content_encoding = self.headers.get('content-encoding', '').lower() - raise DecodeError( - "Received response with content-encoding: %s, but " - "failed to decode it." % content_encoding, e) - - if flush_decoder and decode_content: - data += self._flush_decoder() - - return data - - def _flush_decoder(self): - """ - Flushes the decoder. Should only be called if the decoder is actually - being used. - """ - if self._decoder: - buf = self._decoder.decompress(b'') - return buf + self._decoder.flush() - - return b'' - - @contextmanager - def _error_catcher(self): - """ - Catch low-level python exceptions, instead re-raising urllib3 - variants, so that low-level exceptions are not leaked in the - high-level api. - - On exit, release the connection back to the pool. - """ - clean_exit = False - - try: - try: - yield - - except SocketTimeout: - # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but - # there is yet no clean way to get at it from this context. - raise ReadTimeoutError(self._pool, None, 'Read timed out.') - - except BaseSSLError as e: - # FIXME: Is there a better way to differentiate between SSLErrors? - if 'read operation timed out' not in str(e): # Defensive: - # This shouldn't happen but just in case we're missing an edge - # case, let's avoid swallowing SSL errors. - raise - - raise ReadTimeoutError(self._pool, None, 'Read timed out.') - - except (HTTPException, SocketError) as e: - # This includes IncompleteRead. - raise ProtocolError('Connection broken: %r' % e, e) - - # If no exception is thrown, we should avoid cleaning up - # unnecessarily. - clean_exit = True - finally: - # If we didn't terminate cleanly, we need to throw away our - # connection. - if not clean_exit: - # The response may not be closed but we're not going to use it - # anymore so close it now to ensure that the connection is - # released back to the pool. - if self._original_response: - self._original_response.close() - - # Closing the response may not actually be sufficient to close - # everything, so if we have a hold of the connection close that - # too. - if self._connection: - self._connection.close() - - # If we hold the original response but it's closed now, we should - # return the connection back to the pool. - if self._original_response and self._original_response.isclosed(): - self.release_conn() - - def read(self, amt=None, decode_content=None, cache_content=False): - """ - Similar to :meth:`httplib.HTTPResponse.read`, but with two additional - parameters: ``decode_content`` and ``cache_content``. - - :param amt: - How much of the content to read. If specified, caching is skipped - because it doesn't make sense to cache partial content as the full - response. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param cache_content: - If True, will save the returned data such that the same result is - returned despite of the state of the underlying file object. This - is useful if you want the ``.data`` property to continue working - after having ``.read()`` the file object. (Overridden if ``amt`` is - set.) - """ - self._init_decoder() - if decode_content is None: - decode_content = self.decode_content - - if self._fp is None: - return - - flush_decoder = False - data = None - - with self._error_catcher(): - if amt is None: - # cStringIO doesn't like amt=None - data = self._fp.read() - flush_decoder = True - else: - cache_content = False - data = self._fp.read(amt) - if amt != 0 and not data: # Platform-specific: Buggy versions of Python. - # Close the connection when no data is returned - # - # This is redundant to what httplib/http.client _should_ - # already do. However, versions of python released before - # December 15, 2012 (http://bugs.python.org/issue16298) do - # not properly close the connection in all cases. There is - # no harm in redundantly calling close. - self._fp.close() - flush_decoder = True - if self.enforce_content_length and self.length_remaining not in (0, None): - # This is an edge case that httplib failed to cover due - # to concerns of backward compatibility. We're - # addressing it here to make sure IncompleteRead is - # raised during streaming, so all calls with incorrect - # Content-Length are caught. - raise IncompleteRead(self._fp_bytes_read, self.length_remaining) - - if data: - self._fp_bytes_read += len(data) - if self.length_remaining is not None: - self.length_remaining -= len(data) - - data = self._decode(data, decode_content, flush_decoder) - - if cache_content: - self._body = data - - return data - - def stream(self, amt=2**16, decode_content=None): - """ - A generator wrapper for the read() method. A call will block until - ``amt`` bytes have been read from the connection or until the - connection is closed. - - :param amt: - How much of the content to read. The generator will return up to - much data per iteration, but may return less. This is particularly - likely when using compressed data. However, the empty string will - never be returned. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - if self.chunked and self.supports_chunked_reads(): - for line in self.read_chunked(amt, decode_content=decode_content): - yield line - else: - while not is_fp_closed(self._fp): - data = self.read(amt=amt, decode_content=decode_content) - - if data: - yield data - - @classmethod - def from_httplib(ResponseCls, r, **response_kw): - """ - Given an :class:`httplib.HTTPResponse` instance ``r``, return a - corresponding :class:`urllib3.response.HTTPResponse` object. - - Remaining parameters are passed to the HTTPResponse constructor, along - with ``original_response=r``. - """ - headers = r.msg - - if not isinstance(headers, HTTPHeaderDict): - if PY3: # Python 3 - headers = HTTPHeaderDict(headers.items()) - else: # Python 2 - headers = HTTPHeaderDict.from_httplib(headers) - - # HTTPResponse objects in Python 3 don't have a .strict attribute - strict = getattr(r, 'strict', 0) - resp = ResponseCls(body=r, - headers=headers, - status=r.status, - version=r.version, - reason=r.reason, - strict=strict, - original_response=r, - **response_kw) - return resp - - # Backwards-compatibility methods for httplib.HTTPResponse - def getheaders(self): - return self.headers - - def getheader(self, name, default=None): - return self.headers.get(name, default) - - # Overrides from io.IOBase - def close(self): - if not self.closed: - self._fp.close() - - if self._connection: - self._connection.close() - - @property - def closed(self): - if self._fp is None: - return True - elif hasattr(self._fp, 'isclosed'): - return self._fp.isclosed() - elif hasattr(self._fp, 'closed'): - return self._fp.closed - else: - return True - - def fileno(self): - if self._fp is None: - raise IOError("HTTPResponse has no file to get a fileno from") - elif hasattr(self._fp, "fileno"): - return self._fp.fileno() - else: - raise IOError("The file-like object this HTTPResponse is wrapped " - "around has no file descriptor") - - def flush(self): - if self._fp is not None and hasattr(self._fp, 'flush'): - return self._fp.flush() - - def readable(self): - # This method is required for `io` module compatibility. - return True - - def readinto(self, b): - # This method is required for `io` module compatibility. - temp = self.read(len(b)) - if len(temp) == 0: - return 0 - else: - b[:len(temp)] = temp - return len(temp) - - def supports_chunked_reads(self): - """ - Checks if the underlying file-like object looks like a - httplib.HTTPResponse object. We do this by testing for the fp - attribute. If it is present we assume it returns raw chunks as - processed by read_chunked(). - """ - return hasattr(self._fp, 'fp') - - def _update_chunk_length(self): - # First, we'll figure out length of a chunk and then - # we'll try to read it from socket. - if self.chunk_left is not None: - return - line = self._fp.fp.readline() - line = line.split(b';', 1)[0] - try: - self.chunk_left = int(line, 16) - except ValueError: - # Invalid chunked protocol response, abort. - self.close() - raise httplib.IncompleteRead(line) - - def _handle_chunk(self, amt): - returned_chunk = None - if amt is None: - chunk = self._fp._safe_read(self.chunk_left) - returned_chunk = chunk - self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. - self.chunk_left = None - elif amt < self.chunk_left: - value = self._fp._safe_read(amt) - self.chunk_left = self.chunk_left - amt - returned_chunk = value - elif amt == self.chunk_left: - value = self._fp._safe_read(amt) - self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. - self.chunk_left = None - returned_chunk = value - else: # amt > self.chunk_left - returned_chunk = self._fp._safe_read(self.chunk_left) - self._fp._safe_read(2) # Toss the CRLF at the end of the chunk. - self.chunk_left = None - return returned_chunk - - def read_chunked(self, amt=None, decode_content=None): - """ - Similar to :meth:`HTTPResponse.read`, but with an additional - parameter: ``decode_content``. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - self._init_decoder() - # FIXME: Rewrite this method and make it a class with a better structured logic. - if not self.chunked: - raise ResponseNotChunked( - "Response is not chunked. " - "Header 'transfer-encoding: chunked' is missing.") - if not self.supports_chunked_reads(): - raise BodyNotHttplibCompatible( - "Body should be httplib.HTTPResponse like. " - "It should have have an fp attribute which returns raw chunks.") - - # Don't bother reading the body of a HEAD request. - if self._original_response and is_response_to_head(self._original_response): - self._original_response.close() - return - - with self._error_catcher(): - while True: - self._update_chunk_length() - if self.chunk_left == 0: - break - chunk = self._handle_chunk(amt) - decoded = self._decode(chunk, decode_content=decode_content, - flush_decoder=False) - if decoded: - yield decoded - - if decode_content: - # On CPython and PyPy, we should never need to flush the - # decoder. However, on Jython we *might* need to, so - # lets defensively do it anyway. - decoded = self._flush_decoder() - if decoded: # Platform-specific: Jython. - yield decoded - - # Chunk content ends with \r\n: discard it. - while True: - line = self._fp.fp.readline() - if not line: - # Some sites may not end with '\r\n'. - break - if line == b'\r\n': - break - - # We read everything; close the "file". - if self._original_response: - self._original_response.close() diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/__init__.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/__init__.py deleted file mode 100644 index 5ced5a4..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import absolute_import -# For backwards compatibility, provide imports that used to be here. -from .connection import is_connection_dropped -from .request import make_headers -from .response import is_fp_closed -from .ssl_ import ( - SSLContext, - HAS_SNI, - IS_PYOPENSSL, - assert_fingerprint, - resolve_cert_reqs, - resolve_ssl_version, - ssl_wrap_socket, -) -from .timeout import ( - current_time, - Timeout, -) - -from .retry import Retry -from .url import ( - get_host, - parse_url, - split_first, - Url, -) -from .wait import ( - wait_for_read, - wait_for_write -) - -__all__ = ( - 'HAS_SNI', - 'IS_PYOPENSSL', - 'SSLContext', - 'Retry', - 'Timeout', - 'Url', - 'assert_fingerprint', - 'current_time', - 'is_connection_dropped', - 'is_fp_closed', - 'get_host', - 'parse_url', - 'make_headers', - 'resolve_cert_reqs', - 'resolve_ssl_version', - 'split_first', - 'ssl_wrap_socket', - 'wait_for_read', - 'wait_for_write' -) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/connection.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/connection.py deleted file mode 100644 index bf699cf..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/connection.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import absolute_import -import socket -from .wait import wait_for_read -from .selectors import HAS_SELECT, SelectorError - - -def is_connection_dropped(conn): # Platform-specific - """ - Returns True if the connection is dropped and should be closed. - - :param conn: - :class:`httplib.HTTPConnection` object. - - Note: For platforms like AppEngine, this will always return ``False`` to - let the platform handle connection recycling transparently for us. - """ - sock = getattr(conn, 'sock', False) - if sock is False: # Platform-specific: AppEngine - return False - if sock is None: # Connection already closed (such as by httplib). - return True - - if not HAS_SELECT: - return False - - try: - return bool(wait_for_read(sock, timeout=0.0)) - except SelectorError: - return True - - -# This function is copied from socket.py in the Python 2.7 standard -# library test suite. Added to its signature is only `socket_options`. -# One additional modification is that we avoid binding to IPv6 servers -# discovered in DNS if the system doesn't have IPv6 functionality. -def create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None, socket_options=None): - """Connect to *address* and return the socket object. - - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional - *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`getdefaulttimeout` - is used. If *source_address* is set it must be a tuple of (host, port) - for the socket to bind as a source address before making the connection. - An host of '' or port 0 tells the OS to use the default. - """ - - host, port = address - if host.startswith('['): - host = host.strip('[]') - err = None - - # Using the value from allowed_gai_family() in the context of getaddrinfo lets - # us select whether to work with IPv4 DNS records, IPv6 records, or both. - # The original create_connection function always returns all records. - family = allowed_gai_family() - - for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket.socket(af, socktype, proto) - - # If provided, set socket level options before connecting. - _set_socket_options(sock, socket_options) - - if timeout is not socket._GLOBAL_DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - return sock - - except socket.error as e: - err = e - if sock is not None: - sock.close() - sock = None - - if err is not None: - raise err - - raise socket.error("getaddrinfo returns an empty list") - - -def _set_socket_options(sock, options): - if options is None: - return - - for opt in options: - sock.setsockopt(*opt) - - -def allowed_gai_family(): - """This function is designed to work in the context of - getaddrinfo, where family=socket.AF_UNSPEC is the default and - will perform a DNS search for both IPv6 and IPv4 records.""" - - family = socket.AF_INET - if HAS_IPV6: - family = socket.AF_UNSPEC - return family - - -def _has_ipv6(host): - """ Returns True if the system can bind an IPv6 address. """ - sock = None - has_ipv6 = False - - if socket.has_ipv6: - # has_ipv6 returns true if cPython was compiled with IPv6 support. - # It does not tell us if the system has IPv6 support enabled. To - # determine that we must bind to an IPv6 address. - # https://github.com/shazow/urllib3/pull/611 - # https://bugs.python.org/issue658327 - try: - sock = socket.socket(socket.AF_INET6) - sock.bind((host, 0)) - has_ipv6 = True - except Exception: - pass - - if sock: - sock.close() - return has_ipv6 - - -HAS_IPV6 = _has_ipv6('::1') diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/request.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/request.py deleted file mode 100644 index 974fc40..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/request.py +++ /dev/null @@ -1,118 +0,0 @@ -from __future__ import absolute_import -from base64 import b64encode - -from ..packages.six import b, integer_types -from ..exceptions import UnrewindableBodyError - -ACCEPT_ENCODING = 'gzip,deflate' -_FAILEDTELL = object() - - -def make_headers(keep_alive=None, accept_encoding=None, user_agent=None, - basic_auth=None, proxy_basic_auth=None, disable_cache=None): - """ - Shortcuts for generating request headers. - - :param keep_alive: - If ``True``, adds 'connection: keep-alive' header. - - :param accept_encoding: - Can be a boolean, list, or string. - ``True`` translates to 'gzip,deflate'. - List will get joined by comma. - String will be used as provided. - - :param user_agent: - String representing the user-agent you want, such as - "python-urllib3/0.6" - - :param basic_auth: - Colon-separated username:password string for 'authorization: basic ...' - auth header. - - :param proxy_basic_auth: - Colon-separated username:password string for 'proxy-authorization: basic ...' - auth header. - - :param disable_cache: - If ``True``, adds 'cache-control: no-cache' header. - - Example:: - - >>> make_headers(keep_alive=True, user_agent="Batman/1.0") - {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} - >>> make_headers(accept_encoding=True) - {'accept-encoding': 'gzip,deflate'} - """ - headers = {} - if accept_encoding: - if isinstance(accept_encoding, str): - pass - elif isinstance(accept_encoding, list): - accept_encoding = ','.join(accept_encoding) - else: - accept_encoding = ACCEPT_ENCODING - headers['accept-encoding'] = accept_encoding - - if user_agent: - headers['user-agent'] = user_agent - - if keep_alive: - headers['connection'] = 'keep-alive' - - if basic_auth: - headers['authorization'] = 'Basic ' + \ - b64encode(b(basic_auth)).decode('utf-8') - - if proxy_basic_auth: - headers['proxy-authorization'] = 'Basic ' + \ - b64encode(b(proxy_basic_auth)).decode('utf-8') - - if disable_cache: - headers['cache-control'] = 'no-cache' - - return headers - - -def set_file_position(body, pos): - """ - If a position is provided, move file to that point. - Otherwise, we'll attempt to record a position for future use. - """ - if pos is not None: - rewind_body(body, pos) - elif getattr(body, 'tell', None) is not None: - try: - pos = body.tell() - except (IOError, OSError): - # This differentiates from None, allowing us to catch - # a failed `tell()` later when trying to rewind the body. - pos = _FAILEDTELL - - return pos - - -def rewind_body(body, body_pos): - """ - Attempt to rewind body to a certain position. - Primarily used for request redirects and retries. - - :param body: - File-like object that supports seek. - - :param int pos: - Position to seek to in file. - """ - body_seek = getattr(body, 'seek', None) - if body_seek is not None and isinstance(body_pos, integer_types): - try: - body_seek(body_pos) - except (IOError, OSError): - raise UnrewindableBodyError("An error occured when rewinding request " - "body for redirect/retry.") - elif body_pos is _FAILEDTELL: - raise UnrewindableBodyError("Unable to record file position for rewinding " - "request body during a redirect/retry.") - else: - raise ValueError("body_pos must be of type integer, " - "instead it was %s." % type(body_pos)) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/response.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/response.py deleted file mode 100644 index 67cf730..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/response.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import absolute_import -from ..packages.six.moves import http_client as httplib - -from ..exceptions import HeaderParsingError - - -def is_fp_closed(obj): - """ - Checks whether a given file-like object is closed. - - :param obj: - The file-like object to check. - """ - - try: - # Check `isclosed()` first, in case Python3 doesn't set `closed`. - # GH Issue #928 - return obj.isclosed() - except AttributeError: - pass - - try: - # Check via the official file-like-object way. - return obj.closed - except AttributeError: - pass - - try: - # Check if the object is a container for another file-like object that - # gets released on exhaustion (e.g. HTTPResponse). - return obj.fp is None - except AttributeError: - pass - - raise ValueError("Unable to determine whether fp is closed.") - - -def assert_header_parsing(headers): - """ - Asserts whether all headers have been successfully parsed. - Extracts encountered errors from the result of parsing headers. - - Only works on Python 3. - - :param headers: Headers to verify. - :type headers: `httplib.HTTPMessage`. - - :raises urllib3.exceptions.HeaderParsingError: - If parsing errors are found. - """ - - # This will fail silently if we pass in the wrong kind of parameter. - # To make debugging easier add an explicit check. - if not isinstance(headers, httplib.HTTPMessage): - raise TypeError('expected httplib.Message, got {0}.'.format( - type(headers))) - - defects = getattr(headers, 'defects', None) - get_payload = getattr(headers, 'get_payload', None) - - unparsed_data = None - if get_payload: # Platform-specific: Python 3. - unparsed_data = get_payload() - - if defects or unparsed_data: - raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) - - -def is_response_to_head(response): - """ - Checks whether the request of a response has been a HEAD-request. - Handles the quirks of AppEngine. - - :param conn: - :type conn: :class:`httplib.HTTPResponse` - """ - # FIXME: Can we do this somehow without accessing private httplib _method? - method = response._method - if isinstance(method, int): # Platform-specific: Appengine - return method == 3 - return method.upper() == 'HEAD' diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/retry.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/retry.py deleted file mode 100644 index c9e7d28..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/retry.py +++ /dev/null @@ -1,389 +0,0 @@ -from __future__ import absolute_import -import time -import logging -from collections import namedtuple -from itertools import takewhile -import email -import re - -from ..exceptions import ( - ConnectTimeoutError, - MaxRetryError, - ProtocolError, - ReadTimeoutError, - ResponseError, - InvalidHeader, -) -from ..packages import six - - -log = logging.getLogger(__name__) - -# Data structure for representing the metadata of requests that result in a retry. -RequestHistory = namedtuple('RequestHistory', ["method", "url", "error", - "status", "redirect_location"]) - - -class Retry(object): - """ Retry configuration. - - Each retry attempt will create a new Retry object with updated values, so - they can be safely reused. - - Retries can be defined as a default for a pool:: - - retries = Retry(connect=5, read=2, redirect=5) - http = PoolManager(retries=retries) - response = http.request('GET', 'http://example.com/') - - Or per-request (which overrides the default for the pool):: - - response = http.request('GET', 'http://example.com/', retries=Retry(10)) - - Retries can be disabled by passing ``False``:: - - response = http.request('GET', 'http://example.com/', retries=False) - - Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless - retries are disabled, in which case the causing exception will be raised. - - :param int total: - Total number of retries to allow. Takes precedence over other counts. - - Set to ``None`` to remove this constraint and fall back on other - counts. It's a good idea to set this to some sensibly-high value to - account for unexpected edge cases and avoid infinite retry loops. - - Set to ``0`` to fail on the first retry. - - Set to ``False`` to disable and imply ``raise_on_redirect=False``. - - :param int connect: - How many connection-related errors to retry on. - - These are errors raised before the request is sent to the remote server, - which we assume has not triggered the server to process the request. - - Set to ``0`` to fail on the first retry of this type. - - :param int read: - How many times to retry on read errors. - - These errors are raised after the request was sent to the server, so the - request may have side-effects. - - Set to ``0`` to fail on the first retry of this type. - - :param int redirect: - How many redirects to perform. Limit this to avoid infinite redirect - loops. - - A redirect is a HTTP response with a status code 301, 302, 303, 307 or - 308. - - Set to ``0`` to fail on the first retry of this type. - - Set to ``False`` to disable and imply ``raise_on_redirect=False``. - - :param iterable method_whitelist: - Set of uppercased HTTP method verbs that we should retry on. - - By default, we only retry on methods which are considered to be - idempotent (multiple requests with the same parameters end with the - same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`. - - Set to a ``False`` value to retry on any verb. - - :param iterable status_forcelist: - A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``method_whitelist`` - and the response status code is in ``status_forcelist``. - - By default, this is disabled with ``None``. - - :param float backoff_factor: - A backoff factor to apply between attempts after the second try - (most errors are resolved immediately by a second try without a - delay). urllib3 will sleep for:: - - {backoff factor} * (2 ^ ({number of total retries} - 1)) - - seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep - for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer - than :attr:`Retry.BACKOFF_MAX`. - - By default, backoff is disabled (set to 0). - - :param bool raise_on_redirect: Whether, if the number of redirects is - exhausted, to raise a MaxRetryError, or to return a response with a - response code in the 3xx range. - - :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: - whether we should raise an exception, or return a response, - if status falls in ``status_forcelist`` range and retries have - been exhausted. - - :param tuple history: The history of the request encountered during - each call to :meth:`~Retry.increment`. The list is in the order - the requests occurred. Each list item is of class :class:`RequestHistory`. - - :param bool respect_retry_after_header: - Whether to respect Retry-After header on status codes defined as - :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. - - """ - - DEFAULT_METHOD_WHITELIST = frozenset([ - 'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE']) - - RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) - - #: Maximum backoff time. - BACKOFF_MAX = 120 - - def __init__(self, total=10, connect=None, read=None, redirect=None, - method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None, - backoff_factor=0, raise_on_redirect=True, raise_on_status=True, - history=None, respect_retry_after_header=True): - - self.total = total - self.connect = connect - self.read = read - - if redirect is False or total is False: - redirect = 0 - raise_on_redirect = False - - self.redirect = redirect - self.status_forcelist = status_forcelist or set() - self.method_whitelist = method_whitelist - self.backoff_factor = backoff_factor - self.raise_on_redirect = raise_on_redirect - self.raise_on_status = raise_on_status - self.history = history or tuple() - self.respect_retry_after_header = respect_retry_after_header - - def new(self, **kw): - params = dict( - total=self.total, - connect=self.connect, read=self.read, redirect=self.redirect, - method_whitelist=self.method_whitelist, - status_forcelist=self.status_forcelist, - backoff_factor=self.backoff_factor, - raise_on_redirect=self.raise_on_redirect, - raise_on_status=self.raise_on_status, - history=self.history, - ) - params.update(kw) - return type(self)(**params) - - @classmethod - def from_int(cls, retries, redirect=True, default=None): - """ Backwards-compatibility for the old retries format.""" - if retries is None: - retries = default if default is not None else cls.DEFAULT - - if isinstance(retries, Retry): - return retries - - redirect = bool(redirect) and None - new_retries = cls(retries, redirect=redirect) - log.debug("Converted retries value: %r -> %r", retries, new_retries) - return new_retries - - def get_backoff_time(self): - """ Formula for computing the current backoff - - :rtype: float - """ - # We want to consider only the last consecutive errors sequence (Ignore redirects). - consecutive_errors_len = len(list(takewhile(lambda x: x.redirect_location is None, - reversed(self.history)))) - if consecutive_errors_len <= 1: - return 0 - - backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) - return min(self.BACKOFF_MAX, backoff_value) - - def parse_retry_after(self, retry_after): - # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 - if re.match(r"^\s*[0-9]+\s*$", retry_after): - seconds = int(retry_after) - else: - retry_date_tuple = email.utils.parsedate(retry_after) - if retry_date_tuple is None: - raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) - retry_date = time.mktime(retry_date_tuple) - seconds = retry_date - time.time() - - if seconds < 0: - seconds = 0 - - return seconds - - def get_retry_after(self, response): - """ Get the value of Retry-After in seconds. """ - - retry_after = response.getheader("Retry-After") - - if retry_after is None: - return None - - return self.parse_retry_after(retry_after) - - def sleep_for_retry(self, response=None): - retry_after = self.get_retry_after(response) - if retry_after: - time.sleep(retry_after) - return True - - return False - - def _sleep_backoff(self): - backoff = self.get_backoff_time() - if backoff <= 0: - return - time.sleep(backoff) - - def sleep(self, response=None): - """ Sleep between retry attempts. - - This method will respect a server's ``Retry-After`` response header - and sleep the duration of the time requested. If that is not present, it - will use an exponential backoff. By default, the backoff factor is 0 and - this method will return immediately. - """ - - if response: - slept = self.sleep_for_retry(response) - if slept: - return - - self._sleep_backoff() - - def _is_connection_error(self, err): - """ Errors when we're fairly sure that the server did not receive the - request, so it should be safe to retry. - """ - return isinstance(err, ConnectTimeoutError) - - def _is_read_error(self, err): - """ Errors that occur after the request has been started, so we should - assume that the server began processing it. - """ - return isinstance(err, (ReadTimeoutError, ProtocolError)) - - def _is_method_retryable(self, method): - """ Checks if a given HTTP method should be retried upon, depending if - it is included on the method whitelist. - """ - if self.method_whitelist and method.upper() not in self.method_whitelist: - return False - - return True - - def is_retry(self, method, status_code, has_retry_after=False): - """ Is this method/status code retryable? (Based on whitelists and control - variables such as the number of total retries to allow, whether to - respect the Retry-After header, whether this header is present, and - whether the returned status code is on the list of status codes to - be retried upon on the presence of the aforementioned header) - """ - if not self._is_method_retryable(method): - return False - - if self.status_forcelist and status_code in self.status_forcelist: - return True - - return (self.total and self.respect_retry_after_header and - has_retry_after and (status_code in self.RETRY_AFTER_STATUS_CODES)) - - def is_exhausted(self): - """ Are we out of retries? """ - retry_counts = (self.total, self.connect, self.read, self.redirect) - retry_counts = list(filter(None, retry_counts)) - if not retry_counts: - return False - - return min(retry_counts) < 0 - - def increment(self, method=None, url=None, response=None, error=None, - _pool=None, _stacktrace=None): - """ Return a new Retry object with incremented retry counters. - - :param response: A response object, or None, if the server did not - return a response. - :type response: :class:`~urllib3.response.HTTPResponse` - :param Exception error: An error encountered during the request, or - None if the response was received successfully. - - :return: A new ``Retry`` object. - """ - if self.total is False and error: - # Disabled, indicate to re-raise the error. - raise six.reraise(type(error), error, _stacktrace) - - total = self.total - if total is not None: - total -= 1 - - connect = self.connect - read = self.read - redirect = self.redirect - cause = 'unknown' - status = None - redirect_location = None - - if error and self._is_connection_error(error): - # Connect retry? - if connect is False: - raise six.reraise(type(error), error, _stacktrace) - elif connect is not None: - connect -= 1 - - elif error and self._is_read_error(error): - # Read retry? - if read is False or not self._is_method_retryable(method): - raise six.reraise(type(error), error, _stacktrace) - elif read is not None: - read -= 1 - - elif response and response.get_redirect_location(): - # Redirect retry? - if redirect is not None: - redirect -= 1 - cause = 'too many redirects' - redirect_location = response.get_redirect_location() - status = response.status - - else: - # Incrementing because of a server error like a 500 in - # status_forcelist and a the given method is in the whitelist - cause = ResponseError.GENERIC_ERROR - if response and response.status: - cause = ResponseError.SPECIFIC_ERROR.format( - status_code=response.status) - status = response.status - - history = self.history + (RequestHistory(method, url, error, status, redirect_location),) - - new_retry = self.new( - total=total, - connect=connect, read=read, redirect=redirect, - history=history) - - if new_retry.is_exhausted(): - raise MaxRetryError(_pool, url, error or ResponseError(cause)) - - log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) - - return new_retry - - def __repr__(self): - return ('{cls.__name__}(total={self.total}, connect={self.connect}, ' - 'read={self.read}, redirect={self.redirect})').format( - cls=type(self), self=self) - - -# For backwards compatibility (equivalent to pre-v1.9): -Retry.DEFAULT = Retry(3) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/selectors.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/selectors.py deleted file mode 100644 index b381450..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/selectors.py +++ /dev/null @@ -1,529 +0,0 @@ -# Backport of selectors.py from Python 3.5+ to support Python < 3.4 -# Also has the behavior specified in PEP 475 which is to retry syscalls -# in the case of an EINTR error. This module is required because selectors34 -# does not follow this behavior and instead returns that no dile descriptor -# events have occurred rather than retry the syscall. The decision to drop -# support for select.devpoll is made to maintain 100% test coverage. - -import errno -import math -import select -from collections import namedtuple - -try: - from collections.abc import Mapping -except ImportError: - from collections import Mapping - -import time -try: - monotonic = time.monotonic -except (AttributeError, ImportError): # Python 3.3< - monotonic = time.time - -EVENT_READ = (1 << 0) -EVENT_WRITE = (1 << 1) - -HAS_SELECT = True # Variable that shows whether the platform has a selector. -_SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None. - - -class SelectorError(Exception): - def __init__(self, errcode): - super(SelectorError, self).__init__() - self.errno = errcode - - def __repr__(self): - return "<SelectorError errno={0}>".format(self.errno) - - def __str__(self): - return self.__repr__() - - -def _fileobj_to_fd(fileobj): - """ Return a file descriptor from a file object. If - given an integer will simply return that integer back. """ - if isinstance(fileobj, int): - fd = fileobj - else: - try: - fd = int(fileobj.fileno()) - except (AttributeError, TypeError, ValueError): - raise ValueError("Invalid file object: {0!r}".format(fileobj)) - if fd < 0: - raise ValueError("Invalid file descriptor: {0}".format(fd)) - return fd - - -def _syscall_wrapper(func, recalc_timeout, *args, **kwargs): - """ Wrapper function for syscalls that could fail due to EINTR. - All functions should be retried if there is time left in the timeout - in accordance with PEP 475. """ - timeout = kwargs.get("timeout", None) - if timeout is None: - expires = None - recalc_timeout = False - else: - timeout = float(timeout) - if timeout < 0.0: # Timeout less than 0 treated as no timeout. - expires = None - else: - expires = monotonic() + timeout - - args = list(args) - if recalc_timeout and "timeout" not in kwargs: - raise ValueError( - "Timeout must be in args or kwargs to be recalculated") - - result = _SYSCALL_SENTINEL - while result is _SYSCALL_SENTINEL: - try: - result = func(*args, **kwargs) - # OSError is thrown by select.select - # IOError is thrown by select.epoll.poll - # select.error is thrown by select.poll.poll - # Aren't we thankful for Python 3.x rework for exceptions? - except (OSError, IOError, select.error) as e: - # select.error wasn't a subclass of OSError in the past. - errcode = None - if hasattr(e, "errno"): - errcode = e.errno - elif hasattr(e, "args"): - errcode = e.args[0] - - # Also test for the Windows equivalent of EINTR. - is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and - errcode == errno.WSAEINTR)) - - if is_interrupt: - if expires is not None: - current_time = monotonic() - if current_time > expires: - raise OSError(errno=errno.ETIMEDOUT) - if recalc_timeout: - if "timeout" in kwargs: - kwargs["timeout"] = expires - current_time - continue - if errcode: - raise SelectorError(errcode) - else: - raise - return result - - -SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data']) - - -class _SelectorMapping(Mapping): - """ Mapping of file objects to selector keys """ - - def __init__(self, selector): - self._selector = selector - - def __len__(self): - return len(self._selector._fd_to_key) - - def __getitem__(self, fileobj): - try: - fd = self._selector._fileobj_lookup(fileobj) - return self._selector._fd_to_key[fd] - except KeyError: - raise KeyError("{0!r} is not registered.".format(fileobj)) - - def __iter__(self): - return iter(self._selector._fd_to_key) - - -class BaseSelector(object): - """ Abstract Selector class - - A selector supports registering file objects to be monitored - for specific I/O events. - - A file object is a file descriptor or any object with a - `fileno()` method. An arbitrary object can be attached to the - file object which can be used for example to store context info, - a callback, etc. - - A selector can use various implementations (select(), poll(), epoll(), - and kqueue()) depending on the platform. The 'DefaultSelector' class uses - the most efficient implementation for the current platform. - """ - def __init__(self): - # Maps file descriptors to keys. - self._fd_to_key = {} - - # Read-only mapping returned by get_map() - self._map = _SelectorMapping(self) - - def _fileobj_lookup(self, fileobj): - """ Return a file descriptor from a file object. - This wraps _fileobj_to_fd() to do an exhaustive - search in case the object is invalid but we still - have it in our map. Used by unregister() so we can - unregister an object that was previously registered - even if it is closed. It is also used by _SelectorMapping - """ - try: - return _fileobj_to_fd(fileobj) - except ValueError: - - # Search through all our mapped keys. - for key in self._fd_to_key.values(): - if key.fileobj is fileobj: - return key.fd - - # Raise ValueError after all. - raise - - def register(self, fileobj, events, data=None): - """ Register a file object for a set of events to monitor. """ - if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)): - raise ValueError("Invalid events: {0!r}".format(events)) - - key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data) - - if key.fd in self._fd_to_key: - raise KeyError("{0!r} (FD {1}) is already registered" - .format(fileobj, key.fd)) - - self._fd_to_key[key.fd] = key - return key - - def unregister(self, fileobj): - """ Unregister a file object from being monitored. """ - try: - key = self._fd_to_key.pop(self._fileobj_lookup(fileobj)) - except KeyError: - raise KeyError("{0!r} is not registered".format(fileobj)) - return key - - def modify(self, fileobj, events, data=None): - """ Change a registered file object monitored events and data. """ - # NOTE: Some subclasses optimize this operation even further. - try: - key = self._fd_to_key[self._fileobj_lookup(fileobj)] - except KeyError: - raise KeyError("{0!r} is not registered".format(fileobj)) - - if events != key.events: - self.unregister(fileobj) - key = self.register(fileobj, events, data) - - elif data != key.data: - # Use a shortcut to update the data. - key = key._replace(data=data) - self._fd_to_key[key.fd] = key - - return key - - def select(self, timeout=None): - """ Perform the actual selection until some monitored file objects - are ready or the timeout expires. """ - raise NotImplementedError() - - def close(self): - """ Close the selector. This must be called to ensure that all - underlying resources are freed. """ - self._fd_to_key.clear() - self._map = None - - def get_key(self, fileobj): - """ Return the key associated with a registered file object. """ - mapping = self.get_map() - if mapping is None: - raise RuntimeError("Selector is closed") - try: - return mapping[fileobj] - except KeyError: - raise KeyError("{0!r} is not registered".format(fileobj)) - - def get_map(self): - """ Return a mapping of file objects to selector keys """ - return self._map - - def _key_from_fd(self, fd): - """ Return the key associated to a given file descriptor - Return None if it is not found. """ - try: - return self._fd_to_key[fd] - except KeyError: - return None - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - -# Almost all platforms have select.select() -if hasattr(select, "select"): - class SelectSelector(BaseSelector): - """ Select-based selector. """ - def __init__(self): - super(SelectSelector, self).__init__() - self._readers = set() - self._writers = set() - - def register(self, fileobj, events, data=None): - key = super(SelectSelector, self).register(fileobj, events, data) - if events & EVENT_READ: - self._readers.add(key.fd) - if events & EVENT_WRITE: - self._writers.add(key.fd) - return key - - def unregister(self, fileobj): - key = super(SelectSelector, self).unregister(fileobj) - self._readers.discard(key.fd) - self._writers.discard(key.fd) - return key - - def _select(self, r, w, timeout=None): - """ Wrapper for select.select because timeout is a positional arg """ - return select.select(r, w, [], timeout) - - def select(self, timeout=None): - # Selecting on empty lists on Windows errors out. - if not len(self._readers) and not len(self._writers): - return [] - - timeout = None if timeout is None else max(timeout, 0.0) - ready = [] - r, w, _ = _syscall_wrapper(self._select, True, self._readers, - self._writers, timeout) - r = set(r) - w = set(w) - for fd in r | w: - events = 0 - if fd in r: - events |= EVENT_READ - if fd in w: - events |= EVENT_WRITE - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - return ready - - -if hasattr(select, "poll"): - class PollSelector(BaseSelector): - """ Poll-based selector """ - def __init__(self): - super(PollSelector, self).__init__() - self._poll = select.poll() - - def register(self, fileobj, events, data=None): - key = super(PollSelector, self).register(fileobj, events, data) - event_mask = 0 - if events & EVENT_READ: - event_mask |= select.POLLIN - if events & EVENT_WRITE: - event_mask |= select.POLLOUT - self._poll.register(key.fd, event_mask) - return key - - def unregister(self, fileobj): - key = super(PollSelector, self).unregister(fileobj) - self._poll.unregister(key.fd) - return key - - def _wrap_poll(self, timeout=None): - """ Wrapper function for select.poll.poll() so that - _syscall_wrapper can work with only seconds. """ - if timeout is not None: - if timeout <= 0: - timeout = 0 - else: - # select.poll.poll() has a resolution of 1 millisecond, - # round away from zero to wait *at least* timeout seconds. - timeout = math.ceil(timeout * 1e3) - - result = self._poll.poll(timeout) - return result - - def select(self, timeout=None): - ready = [] - fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout) - for fd, event_mask in fd_events: - events = 0 - if event_mask & ~select.POLLIN: - events |= EVENT_WRITE - if event_mask & ~select.POLLOUT: - events |= EVENT_READ - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - - return ready - - -if hasattr(select, "epoll"): - class EpollSelector(BaseSelector): - """ Epoll-based selector """ - def __init__(self): - super(EpollSelector, self).__init__() - self._epoll = select.epoll() - - def fileno(self): - return self._epoll.fileno() - - def register(self, fileobj, events, data=None): - key = super(EpollSelector, self).register(fileobj, events, data) - events_mask = 0 - if events & EVENT_READ: - events_mask |= select.EPOLLIN - if events & EVENT_WRITE: - events_mask |= select.EPOLLOUT - _syscall_wrapper(self._epoll.register, False, key.fd, events_mask) - return key - - def unregister(self, fileobj): - key = super(EpollSelector, self).unregister(fileobj) - try: - _syscall_wrapper(self._epoll.unregister, False, key.fd) - except SelectorError: - # This can occur when the fd was closed since registry. - pass - return key - - def select(self, timeout=None): - if timeout is not None: - if timeout <= 0: - timeout = 0.0 - else: - # select.epoll.poll() has a resolution of 1 millisecond - # but luckily takes seconds so we don't need a wrapper - # like PollSelector. Just for better rounding. - timeout = math.ceil(timeout * 1e3) * 1e-3 - timeout = float(timeout) - else: - timeout = -1.0 # epoll.poll() must have a float. - - # We always want at least 1 to ensure that select can be called - # with no file descriptors registered. Otherwise will fail. - max_events = max(len(self._fd_to_key), 1) - - ready = [] - fd_events = _syscall_wrapper(self._epoll.poll, True, - timeout=timeout, - maxevents=max_events) - for fd, event_mask in fd_events: - events = 0 - if event_mask & ~select.EPOLLIN: - events |= EVENT_WRITE - if event_mask & ~select.EPOLLOUT: - events |= EVENT_READ - - key = self._key_from_fd(fd) - if key: - ready.append((key, events & key.events)) - return ready - - def close(self): - self._epoll.close() - super(EpollSelector, self).close() - - -if hasattr(select, "kqueue"): - class KqueueSelector(BaseSelector): - """ Kqueue / Kevent-based selector """ - def __init__(self): - super(KqueueSelector, self).__init__() - self._kqueue = select.kqueue() - - def fileno(self): - return self._kqueue.fileno() - - def register(self, fileobj, events, data=None): - key = super(KqueueSelector, self).register(fileobj, events, data) - if events & EVENT_READ: - kevent = select.kevent(key.fd, - select.KQ_FILTER_READ, - select.KQ_EV_ADD) - - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) - - if events & EVENT_WRITE: - kevent = select.kevent(key.fd, - select.KQ_FILTER_WRITE, - select.KQ_EV_ADD) - - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) - - return key - - def unregister(self, fileobj): - key = super(KqueueSelector, self).unregister(fileobj) - if key.events & EVENT_READ: - kevent = select.kevent(key.fd, - select.KQ_FILTER_READ, - select.KQ_EV_DELETE) - try: - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) - except SelectorError: - pass - if key.events & EVENT_WRITE: - kevent = select.kevent(key.fd, - select.KQ_FILTER_WRITE, - select.KQ_EV_DELETE) - try: - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) - except SelectorError: - pass - - return key - - def select(self, timeout=None): - if timeout is not None: - timeout = max(timeout, 0) - - max_events = len(self._fd_to_key) * 2 - ready_fds = {} - - kevent_list = _syscall_wrapper(self._kqueue.control, True, - None, max_events, timeout) - - for kevent in kevent_list: - fd = kevent.ident - event_mask = kevent.filter - events = 0 - if event_mask == select.KQ_FILTER_READ: - events |= EVENT_READ - if event_mask == select.KQ_FILTER_WRITE: - events |= EVENT_WRITE - - key = self._key_from_fd(fd) - if key: - if key.fd not in ready_fds: - ready_fds[key.fd] = (key, events & key.events) - else: - old_events = ready_fds[key.fd][1] - ready_fds[key.fd] = (key, (events | old_events) & key.events) - - return list(ready_fds.values()) - - def close(self): - self._kqueue.close() - super(KqueueSelector, self).close() - - -# Choose the best implementation, roughly: -# kqueue == epoll > poll > select. Devpoll not supported. (See above) -# select() also can't accept a FD > FD_SETSIZE (usually around 1024) -if 'KqueueSelector' in globals(): # Platform-specific: Mac OS and BSD - DefaultSelector = KqueueSelector -elif 'EpollSelector' in globals(): # Platform-specific: Linux - DefaultSelector = EpollSelector -elif 'PollSelector' in globals(): # Platform-specific: Linux - DefaultSelector = PollSelector -elif 'SelectSelector' in globals(): # Platform-specific: Windows - DefaultSelector = SelectSelector -else: # Platform-specific: AppEngine - def no_selector(_): - raise ValueError("Platform does not have a selector") - DefaultSelector = no_selector - HAS_SELECT = False diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/ssl_.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/ssl_.py deleted file mode 100644 index c4c55df..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/ssl_.py +++ /dev/null @@ -1,336 +0,0 @@ -from __future__ import absolute_import -import errno -import warnings -import hmac - -from binascii import hexlify, unhexlify -from hashlib import md5, sha1, sha256 - -from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning - - -SSLContext = None -HAS_SNI = False -IS_PYOPENSSL = False - -# Maps the length of a digest to a possible hash function producing this digest -HASHFUNC_MAP = { - 32: md5, - 40: sha1, - 64: sha256, -} - - -def _const_compare_digest_backport(a, b): - """ - Compare two digests of equal length in constant time. - - The digests must be of type str/bytes. - Returns True if the digests match, and False otherwise. - """ - result = abs(len(a) - len(b)) - for l, r in zip(bytearray(a), bytearray(b)): - result |= l ^ r - return result == 0 - - -_const_compare_digest = getattr(hmac, 'compare_digest', - _const_compare_digest_backport) - - -try: # Test for SSL features - import ssl - from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23 - from ssl import HAS_SNI # Has SNI? -except ImportError: - pass - - -try: - from ssl import OP_NO_SSLv2, OP_NO_SSLv3, OP_NO_COMPRESSION -except ImportError: - OP_NO_SSLv2, OP_NO_SSLv3 = 0x1000000, 0x2000000 - OP_NO_COMPRESSION = 0x20000 - -# A secure default. -# Sources for more information on TLS ciphers: -# -# - https://wiki.mozilla.org/Security/Server_Side_TLS -# - https://www.ssllabs.com/projects/best-practices/index.html -# - https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ -# -# The general intent is: -# - Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE), -# - prefer ECDHE over DHE for better performance, -# - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and -# security, -# - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common, -# - disable NULL authentication, MD5 MACs and DSS for security reasons. -DEFAULT_CIPHERS = ':'.join([ - 'ECDH+AESGCM', - 'ECDH+CHACHA20', - 'DH+AESGCM', - 'DH+CHACHA20', - 'ECDH+AES256', - 'DH+AES256', - 'ECDH+AES128', - 'DH+AES', - 'RSA+AESGCM', - 'RSA+AES', - '!aNULL', - '!eNULL', - '!MD5', -]) - -try: - from ssl import SSLContext # Modern SSL? -except ImportError: - import sys - - class SSLContext(object): # Platform-specific: Python 2 & 3.1 - supports_set_ciphers = ((2, 7) <= sys.version_info < (3,) or - (3, 2) <= sys.version_info) - - def __init__(self, protocol_version): - self.protocol = protocol_version - # Use default values from a real SSLContext - self.check_hostname = False - self.verify_mode = ssl.CERT_NONE - self.ca_certs = None - self.options = 0 - self.certfile = None - self.keyfile = None - self.ciphers = None - - def load_cert_chain(self, certfile, keyfile): - self.certfile = certfile - self.keyfile = keyfile - - def load_verify_locations(self, cafile=None, capath=None): - self.ca_certs = cafile - - if capath is not None: - raise SSLError("CA directories not supported in older Pythons") - - def set_ciphers(self, cipher_suite): - if not self.supports_set_ciphers: - raise TypeError( - 'Your version of Python does not support setting ' - 'a custom cipher suite. Please upgrade to Python ' - '2.7, 3.2, or later if you need this functionality.' - ) - self.ciphers = cipher_suite - - def wrap_socket(self, socket, server_hostname=None, server_side=False): - warnings.warn( - 'A true SSLContext object is not available. This prevents ' - 'urllib3 from configuring SSL appropriately and may cause ' - 'certain SSL connections to fail. You can upgrade to a newer ' - 'version of Python to solve this. For more information, see ' - 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' - '#ssl-warnings', - InsecurePlatformWarning - ) - kwargs = { - 'keyfile': self.keyfile, - 'certfile': self.certfile, - 'ca_certs': self.ca_certs, - 'cert_reqs': self.verify_mode, - 'ssl_version': self.protocol, - 'server_side': server_side, - } - if self.supports_set_ciphers: # Platform-specific: Python 2.7+ - return wrap_socket(socket, ciphers=self.ciphers, **kwargs) - else: # Platform-specific: Python 2.6 - return wrap_socket(socket, **kwargs) - - -def assert_fingerprint(cert, fingerprint): - """ - Checks if given fingerprint matches the supplied certificate. - - :param cert: - Certificate as bytes object. - :param fingerprint: - Fingerprint as string of hexdigits, can be interspersed by colons. - """ - - fingerprint = fingerprint.replace(':', '').lower() - digest_length = len(fingerprint) - hashfunc = HASHFUNC_MAP.get(digest_length) - if not hashfunc: - raise SSLError( - 'Fingerprint of invalid length: {0}'.format(fingerprint)) - - # We need encode() here for py32; works on py2 and p33. - fingerprint_bytes = unhexlify(fingerprint.encode()) - - cert_digest = hashfunc(cert).digest() - - if not _const_compare_digest(cert_digest, fingerprint_bytes): - raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".' - .format(fingerprint, hexlify(cert_digest))) - - -def resolve_cert_reqs(candidate): - """ - Resolves the argument to a numeric constant, which can be passed to - the wrap_socket function/method from the ssl module. - Defaults to :data:`ssl.CERT_NONE`. - If given a string it is assumed to be the name of the constant in the - :mod:`ssl` module or its abbrevation. - (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. - If it's neither `None` nor a string we assume it is already the numeric - constant which can directly be passed to wrap_socket. - """ - if candidate is None: - return CERT_NONE - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, 'CERT_' + candidate) - return res - - return candidate - - -def resolve_ssl_version(candidate): - """ - like resolve_cert_reqs - """ - if candidate is None: - return PROTOCOL_SSLv23 - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, 'PROTOCOL_' + candidate) - return res - - return candidate - - -def create_urllib3_context(ssl_version=None, cert_reqs=None, - options=None, ciphers=None): - """All arguments have the same meaning as ``ssl_wrap_socket``. - - By default, this function does a lot of the same work that - ``ssl.create_default_context`` does on Python 3.4+. It: - - - Disables SSLv2, SSLv3, and compression - - Sets a restricted set of server ciphers - - If you wish to enable SSLv3, you can do:: - - from urllib3.util import ssl_ - context = ssl_.create_urllib3_context() - context.options &= ~ssl_.OP_NO_SSLv3 - - You can do the same to enable compression (substituting ``COMPRESSION`` - for ``SSLv3`` in the last line above). - - :param ssl_version: - The desired protocol version to use. This will default to - PROTOCOL_SSLv23 which will negotiate the highest protocol that both - the server and your installation of OpenSSL support. - :param cert_reqs: - Whether to require the certificate verification. This defaults to - ``ssl.CERT_REQUIRED``. - :param options: - Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, - ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``. - :param ciphers: - Which cipher suites to allow the server to select. - :returns: - Constructed SSLContext object with specified options - :rtype: SSLContext - """ - context = SSLContext(ssl_version or ssl.PROTOCOL_SSLv23) - - # Setting the default here, as we may have no ssl module on import - cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs - - if options is None: - options = 0 - # SSLv2 is easily broken and is considered harmful and dangerous - options |= OP_NO_SSLv2 - # SSLv3 has several problems and is now dangerous - options |= OP_NO_SSLv3 - # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ - # (issue #309) - options |= OP_NO_COMPRESSION - - context.options |= options - - if getattr(context, 'supports_set_ciphers', True): # Platform-specific: Python 2.6 - context.set_ciphers(ciphers or DEFAULT_CIPHERS) - - context.verify_mode = cert_reqs - if getattr(context, 'check_hostname', None) is not None: # Platform-specific: Python 3.2 - # We do our own verification, including fingerprints and alternative - # hostnames. So disable it here - context.check_hostname = False - return context - - -def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, - ca_certs=None, server_hostname=None, - ssl_version=None, ciphers=None, ssl_context=None, - ca_cert_dir=None): - """ - All arguments except for server_hostname, ssl_context, and ca_cert_dir have - the same meaning as they do when using :func:`ssl.wrap_socket`. - - :param server_hostname: - When SNI is supported, the expected hostname of the certificate - :param ssl_context: - A pre-made :class:`SSLContext` object. If none is provided, one will - be created using :func:`create_urllib3_context`. - :param ciphers: - A string of ciphers we wish the client to support. This is not - supported on Python 2.6 as the ssl module does not support it. - :param ca_cert_dir: - A directory containing CA certificates in multiple separate files, as - supported by OpenSSL's -CApath flag or the capath argument to - SSLContext.load_verify_locations(). - """ - context = ssl_context - if context is None: - # Note: This branch of code and all the variables in it are no longer - # used by urllib3 itself. We should consider deprecating and removing - # this code. - context = create_urllib3_context(ssl_version, cert_reqs, - ciphers=ciphers) - - if ca_certs or ca_cert_dir: - try: - context.load_verify_locations(ca_certs, ca_cert_dir) - except IOError as e: # Platform-specific: Python 2.6, 2.7, 3.2 - raise SSLError(e) - # Py33 raises FileNotFoundError which subclasses OSError - # These are not equivalent unless we check the errno attribute - except OSError as e: # Platform-specific: Python 3.3 and beyond - if e.errno == errno.ENOENT: - raise SSLError(e) - raise - elif getattr(context, 'load_default_certs', None) is not None: - # try to load OS default certs; works well on Windows (require Python3.4+) - context.load_default_certs() - - if certfile: - context.load_cert_chain(certfile, keyfile) - if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI - return context.wrap_socket(sock, server_hostname=server_hostname) - - warnings.warn( - 'An HTTPS request has been made, but the SNI (Subject Name ' - 'Indication) extension to TLS is not available on this platform. ' - 'This may cause the server to present an incorrect TLS ' - 'certificate, which can cause validation failures. You can upgrade to ' - 'a newer version of Python to solve this. For more information, see ' - 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html' - '#ssl-warnings', - SNIMissingWarning - ) - return context.wrap_socket(sock) diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/timeout.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/timeout.py deleted file mode 100644 index cec817e..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/timeout.py +++ /dev/null @@ -1,242 +0,0 @@ -from __future__ import absolute_import -# The default socket timeout, used by httplib to indicate that no timeout was -# specified by the user -from socket import _GLOBAL_DEFAULT_TIMEOUT -import time - -from ..exceptions import TimeoutStateError - -# A sentinel value to indicate that no timeout was specified by the user in -# urllib3 -_Default = object() - - -# Use time.monotonic if available. -current_time = getattr(time, "monotonic", time.time) - - -class Timeout(object): - """ Timeout configuration. - - Timeouts can be defined as a default for a pool:: - - timeout = Timeout(connect=2.0, read=7.0) - http = PoolManager(timeout=timeout) - response = http.request('GET', 'http://example.com/') - - Or per-request (which overrides the default for the pool):: - - response = http.request('GET', 'http://example.com/', timeout=Timeout(10)) - - Timeouts can be disabled by setting all the parameters to ``None``:: - - no_timeout = Timeout(connect=None, read=None) - response = http.request('GET', 'http://example.com/, timeout=no_timeout) - - - :param total: - This combines the connect and read timeouts into one; the read timeout - will be set to the time leftover from the connect attempt. In the - event that both a connect timeout and a total are specified, or a read - timeout and a total are specified, the shorter timeout will be applied. - - Defaults to None. - - :type total: integer, float, or None - - :param connect: - The maximum amount of time to wait for a connection attempt to a server - to succeed. Omitting the parameter will default the connect timeout to - the system default, probably `the global default timeout in socket.py - <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. - None will set an infinite timeout for connection attempts. - - :type connect: integer, float, or None - - :param read: - The maximum amount of time to wait between consecutive - read operations for a response from the server. Omitting - the parameter will default the read timeout to the system - default, probably `the global default timeout in socket.py - <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. - None will set an infinite timeout. - - :type read: integer, float, or None - - .. note:: - - Many factors can affect the total amount of time for urllib3 to return - an HTTP response. - - For example, Python's DNS resolver does not obey the timeout specified - on the socket. Other factors that can affect total request time include - high CPU load, high swap, the program running at a low priority level, - or other behaviors. - - In addition, the read and total timeouts only measure the time between - read operations on the socket connecting the client and the server, - not the total amount of time for the request to return a complete - response. For most requests, the timeout is raised because the server - has not sent the first byte in the specified time. This is not always - the case; if a server streams one byte every fifteen seconds, a timeout - of 20 seconds will not trigger, even though the request will take - several minutes to complete. - - If your goal is to cut off any request after a set amount of wall clock - time, consider having a second "watcher" thread to cut off a slow - request. - """ - - #: A sentinel object representing the default timeout value - DEFAULT_TIMEOUT = _GLOBAL_DEFAULT_TIMEOUT - - def __init__(self, total=None, connect=_Default, read=_Default): - self._connect = self._validate_timeout(connect, 'connect') - self._read = self._validate_timeout(read, 'read') - self.total = self._validate_timeout(total, 'total') - self._start_connect = None - - def __str__(self): - return '%s(connect=%r, read=%r, total=%r)' % ( - type(self).__name__, self._connect, self._read, self.total) - - @classmethod - def _validate_timeout(cls, value, name): - """ Check that a timeout attribute is valid. - - :param value: The timeout value to validate - :param name: The name of the timeout attribute to validate. This is - used to specify in error messages. - :return: The validated and casted version of the given value. - :raises ValueError: If it is a numeric value less than or equal to - zero, or the type is not an integer, float, or None. - """ - if value is _Default: - return cls.DEFAULT_TIMEOUT - - if value is None or value is cls.DEFAULT_TIMEOUT: - return value - - if isinstance(value, bool): - raise ValueError("Timeout cannot be a boolean value. It must " - "be an int, float or None.") - try: - float(value) - except (TypeError, ValueError): - raise ValueError("Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value)) - - try: - if value <= 0: - raise ValueError("Attempted to set %s timeout to %s, but the " - "timeout cannot be set to a value less " - "than or equal to 0." % (name, value)) - except TypeError: # Python 3 - raise ValueError("Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value)) - - return value - - @classmethod - def from_float(cls, timeout): - """ Create a new Timeout from a legacy timeout value. - - The timeout value used by httplib.py sets the same timeout on the - connect(), and recv() socket requests. This creates a :class:`Timeout` - object that sets the individual timeouts to the ``timeout`` value - passed to this function. - - :param timeout: The legacy timeout value. - :type timeout: integer, float, sentinel default object, or None - :return: Timeout object - :rtype: :class:`Timeout` - """ - return Timeout(read=timeout, connect=timeout) - - def clone(self): - """ Create a copy of the timeout object - - Timeout properties are stored per-pool but each request needs a fresh - Timeout object to ensure each one has its own start/stop configured. - - :return: a copy of the timeout object - :rtype: :class:`Timeout` - """ - # We can't use copy.deepcopy because that will also create a new object - # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to - # detect the user default. - return Timeout(connect=self._connect, read=self._read, - total=self.total) - - def start_connect(self): - """ Start the timeout clock, used during a connect() attempt - - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to start a timer that has been started already. - """ - if self._start_connect is not None: - raise TimeoutStateError("Timeout timer has already been started.") - self._start_connect = current_time() - return self._start_connect - - def get_connect_duration(self): - """ Gets the time elapsed since the call to :meth:`start_connect`. - - :return: Elapsed time. - :rtype: float - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to get duration for a timer that hasn't been started. - """ - if self._start_connect is None: - raise TimeoutStateError("Can't get connect duration for timer " - "that has not started.") - return current_time() - self._start_connect - - @property - def connect_timeout(self): - """ Get the value to use when setting a connection timeout. - - This will be a positive float or integer, the value None - (never timeout), or the default system timeout. - - :return: Connect timeout. - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - """ - if self.total is None: - return self._connect - - if self._connect is None or self._connect is self.DEFAULT_TIMEOUT: - return self.total - - return min(self._connect, self.total) - - @property - def read_timeout(self): - """ Get the value for the read timeout. - - This assumes some time has elapsed in the connection timeout and - computes the read timeout appropriately. - - If self.total is set, the read timeout is dependent on the amount of - time taken by the connect timeout. If the connection time has not been - established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be - raised. - - :return: Value to use for the read timeout. - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` - has not yet been called on this object. - """ - if (self.total is not None and - self.total is not self.DEFAULT_TIMEOUT and - self._read is not None and - self._read is not self.DEFAULT_TIMEOUT): - # In case the connect timeout has not yet been established. - if self._start_connect is None: - return self._read - return max(0, min(self.total - self.get_connect_duration(), - self._read)) - elif self.total is not None and self.total is not self.DEFAULT_TIMEOUT: - return max(0, self.total - self.get_connect_duration()) - else: - return self._read diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/url.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/url.py deleted file mode 100644 index 61a326e..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/url.py +++ /dev/null @@ -1,226 +0,0 @@ -from __future__ import absolute_import -from collections import namedtuple - -from ..exceptions import LocationParseError - - -url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'] - - -class Url(namedtuple('Url', url_attrs)): - """ - Datastructure for representing an HTTP URL. Used as a return value for - :func:`parse_url`. Both the scheme and host are normalized as they are - both case-insensitive according to RFC 3986. - """ - __slots__ = () - - def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, - query=None, fragment=None): - if path and not path.startswith('/'): - path = '/' + path - if scheme: - scheme = scheme.lower() - if host: - host = host.lower() - return super(Url, cls).__new__(cls, scheme, auth, host, port, path, - query, fragment) - - @property - def hostname(self): - """For backwards-compatibility with urlparse. We're nice like that.""" - return self.host - - @property - def request_uri(self): - """Absolute path including the query string.""" - uri = self.path or '/' - - if self.query is not None: - uri += '?' + self.query - - return uri - - @property - def netloc(self): - """Network location including host and port""" - if self.port: - return '%s:%d' % (self.host, self.port) - return self.host - - @property - def url(self): - """ - Convert self into a url - - This function should more or less round-trip with :func:`.parse_url`. The - returned url may not be exactly the same as the url inputted to - :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls - with a blank port will have : removed). - - Example: :: - - >>> U = parse_url('http://google.com/mail/') - >>> U.url - 'http://google.com/mail/' - >>> Url('http', 'username:password', 'host.com', 80, - ... '/path', 'query', 'fragment').url - 'http://username:password@host.com:80/path?query#fragment' - """ - scheme, auth, host, port, path, query, fragment = self - url = '' - - # We use "is not None" we want things to happen with empty strings (or 0 port) - if scheme is not None: - url += scheme + '://' - if auth is not None: - url += auth + '@' - if host is not None: - url += host - if port is not None: - url += ':' + str(port) - if path is not None: - url += path - if query is not None: - url += '?' + query - if fragment is not None: - url += '#' + fragment - - return url - - def __str__(self): - return self.url - - -def split_first(s, delims): - """ - Given a string and an iterable of delimiters, split on the first found - delimiter. Return two split parts and the matched delimiter. - - If not found, then the first part is the full input string. - - Example:: - - >>> split_first('foo/bar?baz', '?/=') - ('foo', 'bar?baz', '/') - >>> split_first('foo/bar?baz', '123') - ('foo/bar?baz', '', None) - - Scales linearly with number of delims. Not ideal for large number of delims. - """ - min_idx = None - min_delim = None - for d in delims: - idx = s.find(d) - if idx < 0: - continue - - if min_idx is None or idx < min_idx: - min_idx = idx - min_delim = d - - if min_idx is None or min_idx < 0: - return s, '', None - - return s[:min_idx], s[min_idx + 1:], min_delim - - -def parse_url(url): - """ - Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is - performed to parse incomplete urls. Fields not provided will be None. - - Partly backwards-compatible with :mod:`urlparse`. - - Example:: - - >>> parse_url('http://google.com/mail/') - Url(scheme='http', host='google.com', port=None, path='/mail/', ...) - >>> parse_url('google.com:80') - Url(scheme=None, host='google.com', port=80, path=None, ...) - >>> parse_url('/foo?bar') - Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) - """ - - # While this code has overlap with stdlib's urlparse, it is much - # simplified for our needs and less annoying. - # Additionally, this implementations does silly things to be optimal - # on CPython. - - if not url: - # Empty - return Url() - - scheme = None - auth = None - host = None - port = None - path = None - fragment = None - query = None - - # Scheme - if '://' in url: - scheme, url = url.split('://', 1) - - # Find the earliest Authority Terminator - # (http://tools.ietf.org/html/rfc3986#section-3.2) - url, path_, delim = split_first(url, ['/', '?', '#']) - - if delim: - # Reassemble the path - path = delim + path_ - - # Auth - if '@' in url: - # Last '@' denotes end of auth part - auth, url = url.rsplit('@', 1) - - # IPv6 - if url and url[0] == '[': - host, url = url.split(']', 1) - host += ']' - - # Port - if ':' in url: - _host, port = url.split(':', 1) - - if not host: - host = _host - - if port: - # If given, ports must be integers. No whitespace, no plus or - # minus prefixes, no non-integer digits such as ^2 (superscript). - if not port.isdigit(): - raise LocationParseError(url) - try: - port = int(port) - except ValueError: - raise LocationParseError(url) - else: - # Blank ports are cool, too. (rfc3986#section-3.2.3) - port = None - - elif not host and url: - host = url - - if not path: - return Url(scheme, auth, host, port, path, query, fragment) - - # Fragment - if '#' in path: - path, fragment = path.split('#', 1) - - # Query - if '?' in path: - path, query = path.split('?', 1) - - return Url(scheme, auth, host, port, path, query, fragment) - - -def get_host(url): - """ - Deprecated. Use :func:`parse_url` instead. - """ - p = parse_url(url) - return p.scheme or 'http', p.hostname, p.port diff --git a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/wait.py b/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/wait.py deleted file mode 100644 index cb396e5..0000000 --- a/telegramer/include/telegram/vendor/ptb_urllib3/urllib3/util/wait.py +++ /dev/null @@ -1,40 +0,0 @@ -from .selectors import ( - HAS_SELECT, - DefaultSelector, - EVENT_READ, - EVENT_WRITE -) - - -def _wait_for_io_events(socks, events, timeout=None): - """ Waits for IO events to be available from a list of sockets - or optionally a single socket if passed in. Returns a list of - sockets that can be interacted with immediately. """ - if not HAS_SELECT: - raise ValueError('Platform does not have a selector') - if not isinstance(socks, list): - # Probably just a single socket. - if hasattr(socks, "fileno"): - socks = [socks] - # Otherwise it might be a non-list iterable. - else: - socks = list(socks) - with DefaultSelector() as selector: - for sock in socks: - selector.register(sock, events) - return [key[0].fileobj for key in - selector.select(timeout) if key[1] & events] - - -def wait_for_read(socks, timeout=None): - """ Waits for reading to be available from a list of sockets - or optionally a single socket if passed in. Returns a list of - sockets that can be read from immediately. """ - return _wait_for_io_events(socks, EVENT_READ, timeout) - - -def wait_for_write(socks, timeout=None): - """ Waits for writing to be available from a list of sockets - or optionally a single socket if passed in. Returns a list of - sockets that can be written to immediately. """ - return _wait_for_io_events(socks, EVENT_WRITE, timeout) diff --git a/telegramer/include/telegram/version.py b/telegramer/include/telegram/version.py deleted file mode 100644 index f831eee..0000000 --- a/telegramer/include/telegram/version.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0114 - -from telegram import constants - -__version__ = '13.11' -bot_api_version = constants.BOT_API_VERSION # pylint: disable=C0103 diff --git a/telegramer/include/telegram/voicechat.py b/telegramer/include/telegram/voicechat.py deleted file mode 100644 index 1430f5d..0000000 --- a/telegramer/include/telegram/voicechat.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# pylint: disable=R0903 -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains objects related to Telegram voice chats.""" - -import datetime as dtm -from typing import TYPE_CHECKING, Any, Optional, List - -from telegram import TelegramObject, User -from telegram.utils.helpers import from_timestamp, to_timestamp -from telegram.utils.types import JSONDict - -if TYPE_CHECKING: - from telegram import Bot - - -class VoiceChatStarted(TelegramObject): - """ - This object represents a service message about a voice - chat started in the chat. Currently holds no information. - - .. versionadded:: 13.4 - """ - - __slots__ = () - - def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049 - pass - - -class VoiceChatEnded(TelegramObject): - """ - This object represents a service message about a - voice chat ended in the chat. - - Objects of this class are comparable in terms of equality. - Two objects of this class are considered equal, if their - :attr:`duration` are equal. - - .. versionadded:: 13.4 - - Args: - duration (:obj:`int`): Voice chat duration in seconds. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - duration (:obj:`int`): Voice chat duration in seconds. - - """ - - __slots__ = ('duration', '_id_attrs') - - def __init__(self, duration: int, **_kwargs: Any) -> None: - self.duration = int(duration) if duration is not None else None - self._id_attrs = (self.duration,) - - -class VoiceChatParticipantsInvited(TelegramObject): - """ - This object represents a service message about - new members invited to a voice chat. - - Objects of this class are comparable in terms of equality. - Two objects of this class are considered equal, if their - :attr:`users` are equal. - - .. versionadded:: 13.4 - - Args: - users (List[:class:`telegram.User`]): New members that - were invited to the voice chat. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - users (List[:class:`telegram.User`]): New members that - were invited to the voice chat. - - """ - - __slots__ = ('users', '_id_attrs') - - def __init__(self, users: List[User], **_kwargs: Any) -> None: - self.users = users - self._id_attrs = (self.users,) - - def __hash__(self) -> int: - return hash(tuple(self.users)) - - @classmethod - def de_json( - cls, data: Optional[JSONDict], bot: 'Bot' - ) -> Optional['VoiceChatParticipantsInvited']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['users'] = User.de_list(data.get('users', []), bot) - return cls(**data) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - data["users"] = [u.to_dict() for u in self.users] - return data - - -class VoiceChatScheduled(TelegramObject): - """This object represents a service message about a voice chat scheduled in the chat. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`start_date` are equal. - - Args: - start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the voice - chat is supposed to be started by a chat administrator - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - - Attributes: - start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the voice - chat is supposed to be started by a chat administrator - - """ - - __slots__ = ('start_date', '_id_attrs') - - def __init__(self, start_date: dtm.datetime, **_kwargs: Any) -> None: - self.start_date = start_date - - self._id_attrs = (self.start_date,) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VoiceChatScheduled']: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if not data: - return None - - data['start_date'] = from_timestamp(data['start_date']) - - return cls(**data, bot=bot) - - def to_dict(self) -> JSONDict: - """See :meth:`telegram.TelegramObject.to_dict`.""" - data = super().to_dict() - - # Required - data['start_date'] = to_timestamp(self.start_date) - - return data diff --git a/telegramer/include/telegram/webhookinfo.py b/telegramer/include/telegram/webhookinfo.py deleted file mode 100644 index 1611d6c..0000000 --- a/telegramer/include/telegram/webhookinfo.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza <devs@python-telegram-bot.org> -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains an object that represents a Telegram WebhookInfo.""" - -from typing import Any, List - -from telegram import TelegramObject - - -class WebhookInfo(TelegramObject): - """This object represents a Telegram WebhookInfo. - - Contains information about the current status of a webhook. - - Objects of this class are comparable in terms of equality. Two objects of this class are - considered equal, if their :attr:`url`, :attr:`has_custom_certificate`, - :attr:`pending_update_count`, :attr:`ip_address`, :attr:`last_error_date`, - :attr:`last_error_message`, :attr:`max_connections` and :attr:`allowed_updates` are equal. - - Args: - url (:obj:`str`): Webhook URL, may be empty if webhook is not set up. - has_custom_certificate (:obj:`bool`): :obj:`True`, if a custom certificate was provided for - webhook certificate checks. - pending_update_count (:obj:`int`): Number of updates awaiting delivery. - ip_address (:obj:`str`, optional): Currently used webhook IP address. - last_error_date (:obj:`int`, optional): Unix time for the most recent error that happened - when trying to deliver an update via webhook. - last_error_message (:obj:`str`, optional): Error message in human-readable format for the - most recent error that happened when trying to deliver an update via webhook. - max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS - connections to the webhook for update delivery. - allowed_updates (List[:obj:`str`], optional): A list of update types the bot is subscribed - to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. - - Attributes: - url (:obj:`str`): Webhook URL. - has_custom_certificate (:obj:`bool`): If a custom certificate was provided for webhook. - pending_update_count (:obj:`int`): Number of updates awaiting delivery. - ip_address (:obj:`str`): Optional. Currently used webhook IP address. - last_error_date (:obj:`int`): Optional. Unix time for the most recent error that happened. - last_error_message (:obj:`str`): Optional. Error message in human-readable format. - max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS - connections. - allowed_updates (List[:obj:`str`]): Optional. A list of update types the bot is subscribed - to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. - - """ - - __slots__ = ( - 'allowed_updates', - 'url', - 'max_connections', - 'last_error_date', - 'ip_address', - 'last_error_message', - 'pending_update_count', - 'has_custom_certificate', - '_id_attrs', - ) - - def __init__( - self, - url: str, - has_custom_certificate: bool, - pending_update_count: int, - last_error_date: int = None, - last_error_message: str = None, - max_connections: int = None, - allowed_updates: List[str] = None, - ip_address: str = None, - **_kwargs: Any, - ): - # Required - self.url = url - self.has_custom_certificate = has_custom_certificate - self.pending_update_count = pending_update_count - - # Optional - self.ip_address = ip_address - self.last_error_date = last_error_date - self.last_error_message = last_error_message - self.max_connections = max_connections - self.allowed_updates = allowed_updates - - self._id_attrs = ( - self.url, - self.has_custom_certificate, - self.pending_update_count, - self.ip_address, - self.last_error_date, - self.last_error_message, - self.max_connections, - self.allowed_updates, - ) diff --git a/telegramer/include/tornado/__init__.py b/telegramer/include/tornado/__init__.py deleted file mode 100644 index a5f45e5..0000000 --- a/telegramer/include/tornado/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""The Tornado web server and tools.""" - -# version is a human-readable version number. - -# version_info is a four-tuple for programmatic comparison. The first -# three numbers are the components of the version number. The fourth -# is zero for an official release, positive for a development branch, -# or negative for a release candidate or beta (after the base version -# number has been incremented) -version = "6.1" -version_info = (6, 1, 0, 0) diff --git a/telegramer/include/tornado/_locale_data.py b/telegramer/include/tornado/_locale_data.py deleted file mode 100644 index c706230..0000000 --- a/telegramer/include/tornado/_locale_data.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2012 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Data used by the tornado.locale module.""" - -LOCALE_NAMES = { - "af_ZA": {"name_en": u"Afrikaans", "name": u"Afrikaans"}, - "am_ET": {"name_en": u"Amharic", "name": u"አማርኛ"}, - "ar_AR": {"name_en": u"Arabic", "name": u"العربية"}, - "bg_BG": {"name_en": u"Bulgarian", "name": u"Български"}, - "bn_IN": {"name_en": u"Bengali", "name": u"বাংলা"}, - "bs_BA": {"name_en": u"Bosnian", "name": u"Bosanski"}, - "ca_ES": {"name_en": u"Catalan", "name": u"Català"}, - "cs_CZ": {"name_en": u"Czech", "name": u"Čeština"}, - "cy_GB": {"name_en": u"Welsh", "name": u"Cymraeg"}, - "da_DK": {"name_en": u"Danish", "name": u"Dansk"}, - "de_DE": {"name_en": u"German", "name": u"Deutsch"}, - "el_GR": {"name_en": u"Greek", "name": u"Ελληνικά"}, - "en_GB": {"name_en": u"English (UK)", "name": u"English (UK)"}, - "en_US": {"name_en": u"English (US)", "name": u"English (US)"}, - "es_ES": {"name_en": u"Spanish (Spain)", "name": u"Español (España)"}, - "es_LA": {"name_en": u"Spanish", "name": u"Español"}, - "et_EE": {"name_en": u"Estonian", "name": u"Eesti"}, - "eu_ES": {"name_en": u"Basque", "name": u"Euskara"}, - "fa_IR": {"name_en": u"Persian", "name": u"فارسی"}, - "fi_FI": {"name_en": u"Finnish", "name": u"Suomi"}, - "fr_CA": {"name_en": u"French (Canada)", "name": u"Français (Canada)"}, - "fr_FR": {"name_en": u"French", "name": u"Français"}, - "ga_IE": {"name_en": u"Irish", "name": u"Gaeilge"}, - "gl_ES": {"name_en": u"Galician", "name": u"Galego"}, - "he_IL": {"name_en": u"Hebrew", "name": u"עברית"}, - "hi_IN": {"name_en": u"Hindi", "name": u"हिन्दी"}, - "hr_HR": {"name_en": u"Croatian", "name": u"Hrvatski"}, - "hu_HU": {"name_en": u"Hungarian", "name": u"Magyar"}, - "id_ID": {"name_en": u"Indonesian", "name": u"Bahasa Indonesia"}, - "is_IS": {"name_en": u"Icelandic", "name": u"Íslenska"}, - "it_IT": {"name_en": u"Italian", "name": u"Italiano"}, - "ja_JP": {"name_en": u"Japanese", "name": u"日本語"}, - "ko_KR": {"name_en": u"Korean", "name": u"한국어"}, - "lt_LT": {"name_en": u"Lithuanian", "name": u"Lietuvių"}, - "lv_LV": {"name_en": u"Latvian", "name": u"Latviešu"}, - "mk_MK": {"name_en": u"Macedonian", "name": u"Македонски"}, - "ml_IN": {"name_en": u"Malayalam", "name": u"മലയാളം"}, - "ms_MY": {"name_en": u"Malay", "name": u"Bahasa Melayu"}, - "nb_NO": {"name_en": u"Norwegian (bokmal)", "name": u"Norsk (bokmål)"}, - "nl_NL": {"name_en": u"Dutch", "name": u"Nederlands"}, - "nn_NO": {"name_en": u"Norwegian (nynorsk)", "name": u"Norsk (nynorsk)"}, - "pa_IN": {"name_en": u"Punjabi", "name": u"ਪੰਜਾਬੀ"}, - "pl_PL": {"name_en": u"Polish", "name": u"Polski"}, - "pt_BR": {"name_en": u"Portuguese (Brazil)", "name": u"Português (Brasil)"}, - "pt_PT": {"name_en": u"Portuguese (Portugal)", "name": u"Português (Portugal)"}, - "ro_RO": {"name_en": u"Romanian", "name": u"Română"}, - "ru_RU": {"name_en": u"Russian", "name": u"Русский"}, - "sk_SK": {"name_en": u"Slovak", "name": u"Slovenčina"}, - "sl_SI": {"name_en": u"Slovenian", "name": u"Slovenščina"}, - "sq_AL": {"name_en": u"Albanian", "name": u"Shqip"}, - "sr_RS": {"name_en": u"Serbian", "name": u"Српски"}, - "sv_SE": {"name_en": u"Swedish", "name": u"Svenska"}, - "sw_KE": {"name_en": u"Swahili", "name": u"Kiswahili"}, - "ta_IN": {"name_en": u"Tamil", "name": u"தமிழ்"}, - "te_IN": {"name_en": u"Telugu", "name": u"తెలుగు"}, - "th_TH": {"name_en": u"Thai", "name": u"ภาษาไทย"}, - "tl_PH": {"name_en": u"Filipino", "name": u"Filipino"}, - "tr_TR": {"name_en": u"Turkish", "name": u"Türkçe"}, - "uk_UA": {"name_en": u"Ukraini ", "name": u"Українська"}, - "vi_VN": {"name_en": u"Vietnamese", "name": u"Tiếng Việt"}, - "zh_CN": {"name_en": u"Chinese (Simplified)", "name": u"中文(简体)"}, - "zh_TW": {"name_en": u"Chinese (Traditional)", "name": u"中文(繁體)"}, -} diff --git a/telegramer/include/tornado/auth.py b/telegramer/include/tornado/auth.py deleted file mode 100644 index 5f1068c..0000000 --- a/telegramer/include/tornado/auth.py +++ /dev/null @@ -1,1187 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""This module contains implementations of various third-party -authentication schemes. - -All the classes in this file are class mixins designed to be used with -the `tornado.web.RequestHandler` class. They are used in two ways: - -* On a login handler, use methods such as ``authenticate_redirect()``, - ``authorize_redirect()``, and ``get_authenticated_user()`` to - establish the user's identity and store authentication tokens to your - database and/or cookies. -* In non-login handlers, use methods such as ``facebook_request()`` - or ``twitter_request()`` to use the authentication tokens to make - requests to the respective services. - -They all take slightly different arguments due to the fact all these -services implement authentication and authorization slightly differently. -See the individual service classes below for complete documentation. - -Example usage for Google OAuth: - -.. testcode:: - - class GoogleOAuth2LoginHandler(tornado.web.RequestHandler, - tornado.auth.GoogleOAuth2Mixin): - async def get(self): - if self.get_argument('code', False): - user = await self.get_authenticated_user( - redirect_uri='http://your.site.com/auth/google', - code=self.get_argument('code')) - # Save the user with e.g. set_secure_cookie - else: - self.authorize_redirect( - redirect_uri='http://your.site.com/auth/google', - client_id=self.settings['google_oauth']['key'], - scope=['profile', 'email'], - response_type='code', - extra_params={'approval_prompt': 'auto'}) - -.. testoutput:: - :hide: - -""" - -import base64 -import binascii -import hashlib -import hmac -import time -import urllib.parse -import uuid - -from tornado import httpclient -from tornado import escape -from tornado.httputil import url_concat -from tornado.util import unicode_type -from tornado.web import RequestHandler - -from typing import List, Any, Dict, cast, Iterable, Union, Optional - - -class AuthError(Exception): - pass - - -class OpenIdMixin(object): - """Abstract implementation of OpenID and Attribute Exchange. - - Class attributes: - - * ``_OPENID_ENDPOINT``: the identity provider's URI. - """ - - def authenticate_redirect( - self, - callback_uri: Optional[str] = None, - ax_attrs: List[str] = ["name", "email", "language", "username"], - ) -> None: - """Redirects to the authentication URL for this service. - - After authentication, the service will redirect back to the given - callback URI with additional parameters including ``openid.mode``. - - We request the given attributes for the authenticated user by - default (name, email, language, and username). If you don't need - all those attributes for your app, you can request fewer with - the ax_attrs keyword argument. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed and this method no - longer returns an awaitable object. It is now an ordinary - synchronous function. - """ - handler = cast(RequestHandler, self) - callback_uri = callback_uri or handler.request.uri - assert callback_uri is not None - args = self._openid_args(callback_uri, ax_attrs=ax_attrs) - endpoint = self._OPENID_ENDPOINT # type: ignore - handler.redirect(endpoint + "?" + urllib.parse.urlencode(args)) - - async def get_authenticated_user( - self, http_client: Optional[httpclient.AsyncHTTPClient] = None - ) -> Dict[str, Any]: - """Fetches the authenticated user data upon redirect. - - This method should be called by the handler that receives the - redirect from the `authenticate_redirect()` method (which is - often the same as the one that calls it; in that case you would - call `get_authenticated_user` if the ``openid.mode`` parameter - is present and `authenticate_redirect` if it is not). - - The result of this method will generally be used to set a cookie. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - awaitable object instead. - """ - handler = cast(RequestHandler, self) - # Verify the OpenID response via direct request to the OP - args = dict( - (k, v[-1]) for k, v in handler.request.arguments.items() - ) # type: Dict[str, Union[str, bytes]] - args["openid.mode"] = u"check_authentication" - url = self._OPENID_ENDPOINT # type: ignore - if http_client is None: - http_client = self.get_auth_http_client() - resp = await http_client.fetch( - url, method="POST", body=urllib.parse.urlencode(args) - ) - return self._on_authentication_verified(resp) - - def _openid_args( - self, - callback_uri: str, - ax_attrs: Iterable[str] = [], - oauth_scope: Optional[str] = None, - ) -> Dict[str, str]: - handler = cast(RequestHandler, self) - url = urllib.parse.urljoin(handler.request.full_url(), callback_uri) - args = { - "openid.ns": "http://specs.openid.net/auth/2.0", - "openid.claimed_id": "http://specs.openid.net/auth/2.0/identifier_select", - "openid.identity": "http://specs.openid.net/auth/2.0/identifier_select", - "openid.return_to": url, - "openid.realm": urllib.parse.urljoin(url, "/"), - "openid.mode": "checkid_setup", - } - if ax_attrs: - args.update( - { - "openid.ns.ax": "http://openid.net/srv/ax/1.0", - "openid.ax.mode": "fetch_request", - } - ) - ax_attrs = set(ax_attrs) - required = [] # type: List[str] - if "name" in ax_attrs: - ax_attrs -= set(["name", "firstname", "fullname", "lastname"]) - required += ["firstname", "fullname", "lastname"] - args.update( - { - "openid.ax.type.firstname": "http://axschema.org/namePerson/first", - "openid.ax.type.fullname": "http://axschema.org/namePerson", - "openid.ax.type.lastname": "http://axschema.org/namePerson/last", - } - ) - known_attrs = { - "email": "http://axschema.org/contact/email", - "language": "http://axschema.org/pref/language", - "username": "http://axschema.org/namePerson/friendly", - } - for name in ax_attrs: - args["openid.ax.type." + name] = known_attrs[name] - required.append(name) - args["openid.ax.required"] = ",".join(required) - if oauth_scope: - args.update( - { - "openid.ns.oauth": "http://specs.openid.net/extensions/oauth/1.0", - "openid.oauth.consumer": handler.request.host.split(":")[0], - "openid.oauth.scope": oauth_scope, - } - ) - return args - - def _on_authentication_verified( - self, response: httpclient.HTTPResponse - ) -> Dict[str, Any]: - handler = cast(RequestHandler, self) - if b"is_valid:true" not in response.body: - raise AuthError("Invalid OpenID response: %r" % response.body) - - # Make sure we got back at least an email from attribute exchange - ax_ns = None - for key in handler.request.arguments: - if ( - key.startswith("openid.ns.") - and handler.get_argument(key) == u"http://openid.net/srv/ax/1.0" - ): - ax_ns = key[10:] - break - - def get_ax_arg(uri: str) -> str: - if not ax_ns: - return u"" - prefix = "openid." + ax_ns + ".type." - ax_name = None - for name in handler.request.arguments.keys(): - if handler.get_argument(name) == uri and name.startswith(prefix): - part = name[len(prefix) :] - ax_name = "openid." + ax_ns + ".value." + part - break - if not ax_name: - return u"" - return handler.get_argument(ax_name, u"") - - email = get_ax_arg("http://axschema.org/contact/email") - name = get_ax_arg("http://axschema.org/namePerson") - first_name = get_ax_arg("http://axschema.org/namePerson/first") - last_name = get_ax_arg("http://axschema.org/namePerson/last") - username = get_ax_arg("http://axschema.org/namePerson/friendly") - locale = get_ax_arg("http://axschema.org/pref/language").lower() - user = dict() - name_parts = [] - if first_name: - user["first_name"] = first_name - name_parts.append(first_name) - if last_name: - user["last_name"] = last_name - name_parts.append(last_name) - if name: - user["name"] = name - elif name_parts: - user["name"] = u" ".join(name_parts) - elif email: - user["name"] = email.split("@")[0] - if email: - user["email"] = email - if locale: - user["locale"] = locale - if username: - user["username"] = username - claimed_id = handler.get_argument("openid.claimed_id", None) - if claimed_id: - user["claimed_id"] = claimed_id - return user - - def get_auth_http_client(self) -> httpclient.AsyncHTTPClient: - """Returns the `.AsyncHTTPClient` instance to be used for auth requests. - - May be overridden by subclasses to use an HTTP client other than - the default. - """ - return httpclient.AsyncHTTPClient() - - -class OAuthMixin(object): - """Abstract implementation of OAuth 1.0 and 1.0a. - - See `TwitterMixin` below for an example implementation. - - Class attributes: - - * ``_OAUTH_AUTHORIZE_URL``: The service's OAuth authorization url. - * ``_OAUTH_ACCESS_TOKEN_URL``: The service's OAuth access token url. - * ``_OAUTH_VERSION``: May be either "1.0" or "1.0a". - * ``_OAUTH_NO_CALLBACKS``: Set this to True if the service requires - advance registration of callbacks. - - Subclasses must also override the `_oauth_get_user_future` and - `_oauth_consumer_token` methods. - """ - - async def authorize_redirect( - self, - callback_uri: Optional[str] = None, - extra_params: Optional[Dict[str, Any]] = None, - http_client: Optional[httpclient.AsyncHTTPClient] = None, - ) -> None: - """Redirects the user to obtain OAuth authorization for this service. - - The ``callback_uri`` may be omitted if you have previously - registered a callback URI with the third-party service. For - some services, you must use a previously-registered callback - URI and cannot specify a callback via this method. - - This method sets a cookie called ``_oauth_request_token`` which is - subsequently used (and cleared) in `get_authenticated_user` for - security purposes. - - This method is asynchronous and must be called with ``await`` - or ``yield`` (This is different from other ``auth*_redirect`` - methods defined in this module). It calls - `.RequestHandler.finish` for you so you should not write any - other response after it returns. - - .. versionchanged:: 3.1 - Now returns a `.Future` and takes an optional callback, for - compatibility with `.gen.coroutine`. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - awaitable object instead. - - """ - if callback_uri and getattr(self, "_OAUTH_NO_CALLBACKS", False): - raise Exception("This service does not support oauth_callback") - if http_client is None: - http_client = self.get_auth_http_client() - assert http_client is not None - if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": - response = await http_client.fetch( - self._oauth_request_token_url( - callback_uri=callback_uri, extra_params=extra_params - ) - ) - else: - response = await http_client.fetch(self._oauth_request_token_url()) - url = self._OAUTH_AUTHORIZE_URL # type: ignore - self._on_request_token(url, callback_uri, response) - - async def get_authenticated_user( - self, http_client: Optional[httpclient.AsyncHTTPClient] = None - ) -> Dict[str, Any]: - """Gets the OAuth authorized user and access token. - - This method should be called from the handler for your - OAuth callback URL to complete the registration process. We run the - callback with the authenticated user dictionary. This dictionary - will contain an ``access_key`` which can be used to make authorized - requests to this service on behalf of the user. The dictionary will - also contain other fields such as ``name``, depending on the service - used. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - awaitable object instead. - """ - handler = cast(RequestHandler, self) - request_key = escape.utf8(handler.get_argument("oauth_token")) - oauth_verifier = handler.get_argument("oauth_verifier", None) - request_cookie = handler.get_cookie("_oauth_request_token") - if not request_cookie: - raise AuthError("Missing OAuth request token cookie") - handler.clear_cookie("_oauth_request_token") - cookie_key, cookie_secret = [ - base64.b64decode(escape.utf8(i)) for i in request_cookie.split("|") - ] - if cookie_key != request_key: - raise AuthError("Request token does not match cookie") - token = dict( - key=cookie_key, secret=cookie_secret - ) # type: Dict[str, Union[str, bytes]] - if oauth_verifier: - token["verifier"] = oauth_verifier - if http_client is None: - http_client = self.get_auth_http_client() - assert http_client is not None - response = await http_client.fetch(self._oauth_access_token_url(token)) - access_token = _oauth_parse_response(response.body) - user = await self._oauth_get_user_future(access_token) - if not user: - raise AuthError("Error getting user") - user["access_token"] = access_token - return user - - def _oauth_request_token_url( - self, - callback_uri: Optional[str] = None, - extra_params: Optional[Dict[str, Any]] = None, - ) -> str: - handler = cast(RequestHandler, self) - consumer_token = self._oauth_consumer_token() - url = self._OAUTH_REQUEST_TOKEN_URL # type: ignore - args = dict( - oauth_consumer_key=escape.to_basestring(consumer_token["key"]), - oauth_signature_method="HMAC-SHA1", - oauth_timestamp=str(int(time.time())), - oauth_nonce=escape.to_basestring(binascii.b2a_hex(uuid.uuid4().bytes)), - oauth_version="1.0", - ) - if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": - if callback_uri == "oob": - args["oauth_callback"] = "oob" - elif callback_uri: - args["oauth_callback"] = urllib.parse.urljoin( - handler.request.full_url(), callback_uri - ) - if extra_params: - args.update(extra_params) - signature = _oauth10a_signature(consumer_token, "GET", url, args) - else: - signature = _oauth_signature(consumer_token, "GET", url, args) - - args["oauth_signature"] = signature - return url + "?" + urllib.parse.urlencode(args) - - def _on_request_token( - self, - authorize_url: str, - callback_uri: Optional[str], - response: httpclient.HTTPResponse, - ) -> None: - handler = cast(RequestHandler, self) - request_token = _oauth_parse_response(response.body) - data = ( - base64.b64encode(escape.utf8(request_token["key"])) - + b"|" - + base64.b64encode(escape.utf8(request_token["secret"])) - ) - handler.set_cookie("_oauth_request_token", data) - args = dict(oauth_token=request_token["key"]) - if callback_uri == "oob": - handler.finish(authorize_url + "?" + urllib.parse.urlencode(args)) - return - elif callback_uri: - args["oauth_callback"] = urllib.parse.urljoin( - handler.request.full_url(), callback_uri - ) - handler.redirect(authorize_url + "?" + urllib.parse.urlencode(args)) - - def _oauth_access_token_url(self, request_token: Dict[str, Any]) -> str: - consumer_token = self._oauth_consumer_token() - url = self._OAUTH_ACCESS_TOKEN_URL # type: ignore - args = dict( - oauth_consumer_key=escape.to_basestring(consumer_token["key"]), - oauth_token=escape.to_basestring(request_token["key"]), - oauth_signature_method="HMAC-SHA1", - oauth_timestamp=str(int(time.time())), - oauth_nonce=escape.to_basestring(binascii.b2a_hex(uuid.uuid4().bytes)), - oauth_version="1.0", - ) - if "verifier" in request_token: - args["oauth_verifier"] = request_token["verifier"] - - if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": - signature = _oauth10a_signature( - consumer_token, "GET", url, args, request_token - ) - else: - signature = _oauth_signature( - consumer_token, "GET", url, args, request_token - ) - - args["oauth_signature"] = signature - return url + "?" + urllib.parse.urlencode(args) - - def _oauth_consumer_token(self) -> Dict[str, Any]: - """Subclasses must override this to return their OAuth consumer keys. - - The return value should be a `dict` with keys ``key`` and ``secret``. - """ - raise NotImplementedError() - - async def _oauth_get_user_future( - self, access_token: Dict[str, Any] - ) -> Dict[str, Any]: - """Subclasses must override this to get basic information about the - user. - - Should be a coroutine whose result is a dictionary - containing information about the user, which may have been - retrieved by using ``access_token`` to make a request to the - service. - - The access token will be added to the returned dictionary to make - the result of `get_authenticated_user`. - - .. versionchanged:: 5.1 - - Subclasses may also define this method with ``async def``. - - .. versionchanged:: 6.0 - - A synchronous fallback to ``_oauth_get_user`` was removed. - """ - raise NotImplementedError() - - def _oauth_request_parameters( - self, - url: str, - access_token: Dict[str, Any], - parameters: Dict[str, Any] = {}, - method: str = "GET", - ) -> Dict[str, Any]: - """Returns the OAuth parameters as a dict for the given request. - - parameters should include all POST arguments and query string arguments - that will be sent with the request. - """ - consumer_token = self._oauth_consumer_token() - base_args = dict( - oauth_consumer_key=escape.to_basestring(consumer_token["key"]), - oauth_token=escape.to_basestring(access_token["key"]), - oauth_signature_method="HMAC-SHA1", - oauth_timestamp=str(int(time.time())), - oauth_nonce=escape.to_basestring(binascii.b2a_hex(uuid.uuid4().bytes)), - oauth_version="1.0", - ) - args = {} - args.update(base_args) - args.update(parameters) - if getattr(self, "_OAUTH_VERSION", "1.0a") == "1.0a": - signature = _oauth10a_signature( - consumer_token, method, url, args, access_token - ) - else: - signature = _oauth_signature( - consumer_token, method, url, args, access_token - ) - base_args["oauth_signature"] = escape.to_basestring(signature) - return base_args - - def get_auth_http_client(self) -> httpclient.AsyncHTTPClient: - """Returns the `.AsyncHTTPClient` instance to be used for auth requests. - - May be overridden by subclasses to use an HTTP client other than - the default. - """ - return httpclient.AsyncHTTPClient() - - -class OAuth2Mixin(object): - """Abstract implementation of OAuth 2.0. - - See `FacebookGraphMixin` or `GoogleOAuth2Mixin` below for example - implementations. - - Class attributes: - - * ``_OAUTH_AUTHORIZE_URL``: The service's authorization url. - * ``_OAUTH_ACCESS_TOKEN_URL``: The service's access token url. - """ - - def authorize_redirect( - self, - redirect_uri: Optional[str] = None, - client_id: Optional[str] = None, - client_secret: Optional[str] = None, - extra_params: Optional[Dict[str, Any]] = None, - scope: Optional[List[str]] = None, - response_type: str = "code", - ) -> None: - """Redirects the user to obtain OAuth authorization for this service. - - Some providers require that you register a redirect URL with - your application instead of passing one via this method. You - should call this method to log the user in, and then call - ``get_authenticated_user`` in the handler for your - redirect URL to complete the authorization process. - - .. versionchanged:: 6.0 - - The ``callback`` argument and returned awaitable were removed; - this is now an ordinary synchronous function. - """ - handler = cast(RequestHandler, self) - args = {"response_type": response_type} - if redirect_uri is not None: - args["redirect_uri"] = redirect_uri - if client_id is not None: - args["client_id"] = client_id - if extra_params: - args.update(extra_params) - if scope: - args["scope"] = " ".join(scope) - url = self._OAUTH_AUTHORIZE_URL # type: ignore - handler.redirect(url_concat(url, args)) - - def _oauth_request_token_url( - self, - redirect_uri: Optional[str] = None, - client_id: Optional[str] = None, - client_secret: Optional[str] = None, - code: Optional[str] = None, - extra_params: Optional[Dict[str, Any]] = None, - ) -> str: - url = self._OAUTH_ACCESS_TOKEN_URL # type: ignore - args = {} # type: Dict[str, str] - if redirect_uri is not None: - args["redirect_uri"] = redirect_uri - if code is not None: - args["code"] = code - if client_id is not None: - args["client_id"] = client_id - if client_secret is not None: - args["client_secret"] = client_secret - if extra_params: - args.update(extra_params) - return url_concat(url, args) - - async def oauth2_request( - self, - url: str, - access_token: Optional[str] = None, - post_args: Optional[Dict[str, Any]] = None, - **args: Any - ) -> Any: - """Fetches the given URL auth an OAuth2 access token. - - If the request is a POST, ``post_args`` should be provided. Query - string arguments should be given as keyword arguments. - - Example usage: - - ..testcode:: - - class MainHandler(tornado.web.RequestHandler, - tornado.auth.FacebookGraphMixin): - @tornado.web.authenticated - async def get(self): - new_entry = await self.oauth2_request( - "https://graph.facebook.com/me/feed", - post_args={"message": "I am posting from my Tornado application!"}, - access_token=self.current_user["access_token"]) - - if not new_entry: - # Call failed; perhaps missing permission? - self.authorize_redirect() - return - self.finish("Posted a message!") - - .. testoutput:: - :hide: - - .. versionadded:: 4.3 - - .. versionchanged::: 6.0 - - The ``callback`` argument was removed. Use the returned awaitable object instead. - """ - all_args = {} - if access_token: - all_args["access_token"] = access_token - all_args.update(args) - - if all_args: - url += "?" + urllib.parse.urlencode(all_args) - http = self.get_auth_http_client() - if post_args is not None: - response = await http.fetch( - url, method="POST", body=urllib.parse.urlencode(post_args) - ) - else: - response = await http.fetch(url) - return escape.json_decode(response.body) - - def get_auth_http_client(self) -> httpclient.AsyncHTTPClient: - """Returns the `.AsyncHTTPClient` instance to be used for auth requests. - - May be overridden by subclasses to use an HTTP client other than - the default. - - .. versionadded:: 4.3 - """ - return httpclient.AsyncHTTPClient() - - -class TwitterMixin(OAuthMixin): - """Twitter OAuth authentication. - - To authenticate with Twitter, register your application with - Twitter at http://twitter.com/apps. Then copy your Consumer Key - and Consumer Secret to the application - `~tornado.web.Application.settings` ``twitter_consumer_key`` and - ``twitter_consumer_secret``. Use this mixin on the handler for the - URL you registered as your application's callback URL. - - When your application is set up, you can use this mixin like this - to authenticate the user with Twitter and get access to their stream: - - .. testcode:: - - class TwitterLoginHandler(tornado.web.RequestHandler, - tornado.auth.TwitterMixin): - async def get(self): - if self.get_argument("oauth_token", None): - user = await self.get_authenticated_user() - # Save the user using e.g. set_secure_cookie() - else: - await self.authorize_redirect() - - .. testoutput:: - :hide: - - The user object returned by `~OAuthMixin.get_authenticated_user` - includes the attributes ``username``, ``name``, ``access_token``, - and all of the custom Twitter user attributes described at - https://dev.twitter.com/docs/api/1.1/get/users/show - """ - - _OAUTH_REQUEST_TOKEN_URL = "https://api.twitter.com/oauth/request_token" - _OAUTH_ACCESS_TOKEN_URL = "https://api.twitter.com/oauth/access_token" - _OAUTH_AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize" - _OAUTH_AUTHENTICATE_URL = "https://api.twitter.com/oauth/authenticate" - _OAUTH_NO_CALLBACKS = False - _TWITTER_BASE_URL = "https://api.twitter.com/1.1" - - async def authenticate_redirect(self, callback_uri: Optional[str] = None) -> None: - """Just like `~OAuthMixin.authorize_redirect`, but - auto-redirects if authorized. - - This is generally the right interface to use if you are using - Twitter for single-sign on. - - .. versionchanged:: 3.1 - Now returns a `.Future` and takes an optional callback, for - compatibility with `.gen.coroutine`. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - awaitable object instead. - """ - http = self.get_auth_http_client() - response = await http.fetch( - self._oauth_request_token_url(callback_uri=callback_uri) - ) - self._on_request_token(self._OAUTH_AUTHENTICATE_URL, None, response) - - async def twitter_request( - self, - path: str, - access_token: Dict[str, Any], - post_args: Optional[Dict[str, Any]] = None, - **args: Any - ) -> Any: - """Fetches the given API path, e.g., ``statuses/user_timeline/btaylor`` - - The path should not include the format or API version number. - (we automatically use JSON format and API version 1). - - If the request is a POST, ``post_args`` should be provided. Query - string arguments should be given as keyword arguments. - - All the Twitter methods are documented at http://dev.twitter.com/ - - Many methods require an OAuth access token which you can - obtain through `~OAuthMixin.authorize_redirect` and - `~OAuthMixin.get_authenticated_user`. The user returned through that - process includes an 'access_token' attribute that can be used - to make authenticated requests via this method. Example - usage: - - .. testcode:: - - class MainHandler(tornado.web.RequestHandler, - tornado.auth.TwitterMixin): - @tornado.web.authenticated - async def get(self): - new_entry = await self.twitter_request( - "/statuses/update", - post_args={"status": "Testing Tornado Web Server"}, - access_token=self.current_user["access_token"]) - if not new_entry: - # Call failed; perhaps missing permission? - await self.authorize_redirect() - return - self.finish("Posted a message!") - - .. testoutput:: - :hide: - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - awaitable object instead. - """ - if path.startswith("http:") or path.startswith("https:"): - # Raw urls are useful for e.g. search which doesn't follow the - # usual pattern: http://search.twitter.com/search.json - url = path - else: - url = self._TWITTER_BASE_URL + path + ".json" - # Add the OAuth resource request signature if we have credentials - if access_token: - all_args = {} - all_args.update(args) - all_args.update(post_args or {}) - method = "POST" if post_args is not None else "GET" - oauth = self._oauth_request_parameters( - url, access_token, all_args, method=method - ) - args.update(oauth) - if args: - url += "?" + urllib.parse.urlencode(args) - http = self.get_auth_http_client() - if post_args is not None: - response = await http.fetch( - url, method="POST", body=urllib.parse.urlencode(post_args) - ) - else: - response = await http.fetch(url) - return escape.json_decode(response.body) - - def _oauth_consumer_token(self) -> Dict[str, Any]: - handler = cast(RequestHandler, self) - handler.require_setting("twitter_consumer_key", "Twitter OAuth") - handler.require_setting("twitter_consumer_secret", "Twitter OAuth") - return dict( - key=handler.settings["twitter_consumer_key"], - secret=handler.settings["twitter_consumer_secret"], - ) - - async def _oauth_get_user_future( - self, access_token: Dict[str, Any] - ) -> Dict[str, Any]: - user = await self.twitter_request( - "/account/verify_credentials", access_token=access_token - ) - if user: - user["username"] = user["screen_name"] - return user - - -class GoogleOAuth2Mixin(OAuth2Mixin): - """Google authentication using OAuth2. - - In order to use, register your application with Google and copy the - relevant parameters to your application settings. - - * Go to the Google Dev Console at http://console.developers.google.com - * Select a project, or create a new one. - * In the sidebar on the left, select APIs & Auth. - * In the list of APIs, find the Google+ API service and set it to ON. - * In the sidebar on the left, select Credentials. - * In the OAuth section of the page, select Create New Client ID. - * Set the Redirect URI to point to your auth handler - * Copy the "Client secret" and "Client ID" to the application settings as - ``{"google_oauth": {"key": CLIENT_ID, "secret": CLIENT_SECRET}}`` - - .. versionadded:: 3.2 - """ - - _OAUTH_AUTHORIZE_URL = "https://accounts.google.com/o/oauth2/v2/auth" - _OAUTH_ACCESS_TOKEN_URL = "https://www.googleapis.com/oauth2/v4/token" - _OAUTH_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo" - _OAUTH_NO_CALLBACKS = False - _OAUTH_SETTINGS_KEY = "google_oauth" - - async def get_authenticated_user( - self, redirect_uri: str, code: str - ) -> Dict[str, Any]: - """Handles the login for the Google user, returning an access token. - - The result is a dictionary containing an ``access_token`` field - ([among others](https://developers.google.com/identity/protocols/OAuth2WebServer#handlingtheresponse)). - Unlike other ``get_authenticated_user`` methods in this package, - this method does not return any additional information about the user. - The returned access token can be used with `OAuth2Mixin.oauth2_request` - to request additional information (perhaps from - ``https://www.googleapis.com/oauth2/v2/userinfo``) - - Example usage: - - .. testcode:: - - class GoogleOAuth2LoginHandler(tornado.web.RequestHandler, - tornado.auth.GoogleOAuth2Mixin): - async def get(self): - if self.get_argument('code', False): - access = await self.get_authenticated_user( - redirect_uri='http://your.site.com/auth/google', - code=self.get_argument('code')) - user = await self.oauth2_request( - "https://www.googleapis.com/oauth2/v1/userinfo", - access_token=access["access_token"]) - # Save the user and access token with - # e.g. set_secure_cookie. - else: - self.authorize_redirect( - redirect_uri='http://your.site.com/auth/google', - client_id=self.settings['google_oauth']['key'], - scope=['profile', 'email'], - response_type='code', - extra_params={'approval_prompt': 'auto'}) - - .. testoutput:: - :hide: - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned awaitable object instead. - """ # noqa: E501 - handler = cast(RequestHandler, self) - http = self.get_auth_http_client() - body = urllib.parse.urlencode( - { - "redirect_uri": redirect_uri, - "code": code, - "client_id": handler.settings[self._OAUTH_SETTINGS_KEY]["key"], - "client_secret": handler.settings[self._OAUTH_SETTINGS_KEY]["secret"], - "grant_type": "authorization_code", - } - ) - - response = await http.fetch( - self._OAUTH_ACCESS_TOKEN_URL, - method="POST", - headers={"Content-Type": "application/x-www-form-urlencoded"}, - body=body, - ) - return escape.json_decode(response.body) - - -class FacebookGraphMixin(OAuth2Mixin): - """Facebook authentication using the new Graph API and OAuth2.""" - - _OAUTH_ACCESS_TOKEN_URL = "https://graph.facebook.com/oauth/access_token?" - _OAUTH_AUTHORIZE_URL = "https://www.facebook.com/dialog/oauth?" - _OAUTH_NO_CALLBACKS = False - _FACEBOOK_BASE_URL = "https://graph.facebook.com" - - async def get_authenticated_user( - self, - redirect_uri: str, - client_id: str, - client_secret: str, - code: str, - extra_fields: Optional[Dict[str, Any]] = None, - ) -> Optional[Dict[str, Any]]: - """Handles the login for the Facebook user, returning a user object. - - Example usage: - - .. testcode:: - - class FacebookGraphLoginHandler(tornado.web.RequestHandler, - tornado.auth.FacebookGraphMixin): - async def get(self): - if self.get_argument("code", False): - user = await self.get_authenticated_user( - redirect_uri='/auth/facebookgraph/', - client_id=self.settings["facebook_api_key"], - client_secret=self.settings["facebook_secret"], - code=self.get_argument("code")) - # Save the user with e.g. set_secure_cookie - else: - self.authorize_redirect( - redirect_uri='/auth/facebookgraph/', - client_id=self.settings["facebook_api_key"], - extra_params={"scope": "read_stream,offline_access"}) - - .. testoutput:: - :hide: - - This method returns a dictionary which may contain the following fields: - - * ``access_token``, a string which may be passed to `facebook_request` - * ``session_expires``, an integer encoded as a string representing - the time until the access token expires in seconds. This field should - be used like ``int(user['session_expires'])``; in a future version of - Tornado it will change from a string to an integer. - * ``id``, ``name``, ``first_name``, ``last_name``, ``locale``, ``picture``, - ``link``, plus any fields named in the ``extra_fields`` argument. These - fields are copied from the Facebook graph API - `user object <https://developers.facebook.com/docs/graph-api/reference/user>`_ - - .. versionchanged:: 4.5 - The ``session_expires`` field was updated to support changes made to the - Facebook API in March 2017. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned awaitable object instead. - """ - http = self.get_auth_http_client() - args = { - "redirect_uri": redirect_uri, - "code": code, - "client_id": client_id, - "client_secret": client_secret, - } - - fields = set( - ["id", "name", "first_name", "last_name", "locale", "picture", "link"] - ) - if extra_fields: - fields.update(extra_fields) - - response = await http.fetch( - self._oauth_request_token_url(**args) # type: ignore - ) - args = escape.json_decode(response.body) - session = { - "access_token": args.get("access_token"), - "expires_in": args.get("expires_in"), - } - assert session["access_token"] is not None - - user = await self.facebook_request( - path="/me", - access_token=session["access_token"], - appsecret_proof=hmac.new( - key=client_secret.encode("utf8"), - msg=session["access_token"].encode("utf8"), - digestmod=hashlib.sha256, - ).hexdigest(), - fields=",".join(fields), - ) - - if user is None: - return None - - fieldmap = {} - for field in fields: - fieldmap[field] = user.get(field) - - # session_expires is converted to str for compatibility with - # older versions in which the server used url-encoding and - # this code simply returned the string verbatim. - # This should change in Tornado 5.0. - fieldmap.update( - { - "access_token": session["access_token"], - "session_expires": str(session.get("expires_in")), - } - ) - return fieldmap - - async def facebook_request( - self, - path: str, - access_token: Optional[str] = None, - post_args: Optional[Dict[str, Any]] = None, - **args: Any - ) -> Any: - """Fetches the given relative API path, e.g., "/btaylor/picture" - - If the request is a POST, ``post_args`` should be provided. Query - string arguments should be given as keyword arguments. - - An introduction to the Facebook Graph API can be found at - http://developers.facebook.com/docs/api - - Many methods require an OAuth access token which you can - obtain through `~OAuth2Mixin.authorize_redirect` and - `get_authenticated_user`. The user returned through that - process includes an ``access_token`` attribute that can be - used to make authenticated requests via this method. - - Example usage: - - .. testcode:: - - class MainHandler(tornado.web.RequestHandler, - tornado.auth.FacebookGraphMixin): - @tornado.web.authenticated - async def get(self): - new_entry = await self.facebook_request( - "/me/feed", - post_args={"message": "I am posting from my Tornado application!"}, - access_token=self.current_user["access_token"]) - - if not new_entry: - # Call failed; perhaps missing permission? - self.authorize_redirect() - return - self.finish("Posted a message!") - - .. testoutput:: - :hide: - - The given path is relative to ``self._FACEBOOK_BASE_URL``, - by default "https://graph.facebook.com". - - This method is a wrapper around `OAuth2Mixin.oauth2_request`; - the only difference is that this method takes a relative path, - while ``oauth2_request`` takes a complete url. - - .. versionchanged:: 3.1 - Added the ability to override ``self._FACEBOOK_BASE_URL``. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned awaitable object instead. - """ - url = self._FACEBOOK_BASE_URL + path - return await self.oauth2_request( - url, access_token=access_token, post_args=post_args, **args - ) - - -def _oauth_signature( - consumer_token: Dict[str, Any], - method: str, - url: str, - parameters: Dict[str, Any] = {}, - token: Optional[Dict[str, Any]] = None, -) -> bytes: - """Calculates the HMAC-SHA1 OAuth signature for the given request. - - See http://oauth.net/core/1.0/#signing_process - """ - parts = urllib.parse.urlparse(url) - scheme, netloc, path = parts[:3] - normalized_url = scheme.lower() + "://" + netloc.lower() + path - - base_elems = [] - base_elems.append(method.upper()) - base_elems.append(normalized_url) - base_elems.append( - "&".join( - "%s=%s" % (k, _oauth_escape(str(v))) for k, v in sorted(parameters.items()) - ) - ) - base_string = "&".join(_oauth_escape(e) for e in base_elems) - - key_elems = [escape.utf8(consumer_token["secret"])] - key_elems.append(escape.utf8(token["secret"] if token else "")) - key = b"&".join(key_elems) - - hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1) - return binascii.b2a_base64(hash.digest())[:-1] - - -def _oauth10a_signature( - consumer_token: Dict[str, Any], - method: str, - url: str, - parameters: Dict[str, Any] = {}, - token: Optional[Dict[str, Any]] = None, -) -> bytes: - """Calculates the HMAC-SHA1 OAuth 1.0a signature for the given request. - - See http://oauth.net/core/1.0a/#signing_process - """ - parts = urllib.parse.urlparse(url) - scheme, netloc, path = parts[:3] - normalized_url = scheme.lower() + "://" + netloc.lower() + path - - base_elems = [] - base_elems.append(method.upper()) - base_elems.append(normalized_url) - base_elems.append( - "&".join( - "%s=%s" % (k, _oauth_escape(str(v))) for k, v in sorted(parameters.items()) - ) - ) - - base_string = "&".join(_oauth_escape(e) for e in base_elems) - key_elems = [escape.utf8(urllib.parse.quote(consumer_token["secret"], safe="~"))] - key_elems.append( - escape.utf8(urllib.parse.quote(token["secret"], safe="~") if token else "") - ) - key = b"&".join(key_elems) - - hash = hmac.new(key, escape.utf8(base_string), hashlib.sha1) - return binascii.b2a_base64(hash.digest())[:-1] - - -def _oauth_escape(val: Union[str, bytes]) -> str: - if isinstance(val, unicode_type): - val = val.encode("utf-8") - return urllib.parse.quote(val, safe="~") - - -def _oauth_parse_response(body: bytes) -> Dict[str, Any]: - # I can't find an officially-defined encoding for oauth responses and - # have never seen anyone use non-ascii. Leave the response in a byte - # string for python 2, and use utf8 on python 3. - body_str = escape.native_str(body) - p = urllib.parse.parse_qs(body_str, keep_blank_values=False) - token = dict(key=p["oauth_token"][0], secret=p["oauth_token_secret"][0]) - - # Add the extra parameters the Provider included to the token - special = ("oauth_token", "oauth_token_secret") - token.update((k, p[k][0]) for k in p if k not in special) - return token diff --git a/telegramer/include/tornado/autoreload.py b/telegramer/include/tornado/autoreload.py deleted file mode 100644 index 3299a3b..0000000 --- a/telegramer/include/tornado/autoreload.py +++ /dev/null @@ -1,363 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Automatically restart the server when a source file is modified. - -Most applications should not access this module directly. Instead, -pass the keyword argument ``autoreload=True`` to the -`tornado.web.Application` constructor (or ``debug=True``, which -enables this setting and several others). This will enable autoreload -mode as well as checking for changes to templates and static -resources. Note that restarting is a destructive operation and any -requests in progress will be aborted when the process restarts. (If -you want to disable autoreload while using other debug-mode features, -pass both ``debug=True`` and ``autoreload=False``). - -This module can also be used as a command-line wrapper around scripts -such as unit test runners. See the `main` method for details. - -The command-line wrapper and Application debug modes can be used together. -This combination is encouraged as the wrapper catches syntax errors and -other import-time failures, while debug mode catches changes once -the server has started. - -This module will not work correctly when `.HTTPServer`'s multi-process -mode is used. - -Reloading loses any Python interpreter command-line arguments (e.g. ``-u``) -because it re-executes Python using ``sys.executable`` and ``sys.argv``. -Additionally, modifying these variables will cause reloading to behave -incorrectly. - -""" - -import os -import sys - -# sys.path handling -# ----------------- -# -# If a module is run with "python -m", the current directory (i.e. "") -# is automatically prepended to sys.path, but not if it is run as -# "path/to/file.py". The processing for "-m" rewrites the former to -# the latter, so subsequent executions won't have the same path as the -# original. -# -# Conversely, when run as path/to/file.py, the directory containing -# file.py gets added to the path, which can cause confusion as imports -# may become relative in spite of the future import. -# -# We address the former problem by reconstructing the original command -# line (Python >= 3.4) or by setting the $PYTHONPATH environment -# variable (Python < 3.4) before re-execution so the new process will -# see the correct path. We attempt to address the latter problem when -# tornado.autoreload is run as __main__. - -if __name__ == "__main__": - # This sys.path manipulation must come before our imports (as much - # as possible - if we introduced a tornado.sys or tornado.os - # module we'd be in trouble), or else our imports would become - # relative again despite the future import. - # - # There is a separate __main__ block at the end of the file to call main(). - if sys.path[0] == os.path.dirname(__file__): - del sys.path[0] - -import functools -import logging -import os -import pkgutil # type: ignore -import sys -import traceback -import types -import subprocess -import weakref - -from tornado import ioloop -from tornado.log import gen_log -from tornado import process -from tornado.util import exec_in - -try: - import signal -except ImportError: - signal = None # type: ignore - -import typing -from typing import Callable, Dict - -if typing.TYPE_CHECKING: - from typing import List, Optional, Union # noqa: F401 - -# os.execv is broken on Windows and can't properly parse command line -# arguments and executable name if they contain whitespaces. subprocess -# fixes that behavior. -_has_execv = sys.platform != "win32" - -_watched_files = set() -_reload_hooks = [] -_reload_attempted = False -_io_loops = weakref.WeakKeyDictionary() # type: ignore -_autoreload_is_main = False -_original_argv = None # type: Optional[List[str]] -_original_spec = None - - -def start(check_time: int = 500) -> None: - """Begins watching source files for changes. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - """ - io_loop = ioloop.IOLoop.current() - if io_loop in _io_loops: - return - _io_loops[io_loop] = True - if len(_io_loops) > 1: - gen_log.warning("tornado.autoreload started more than once in the same process") - modify_times = {} # type: Dict[str, float] - callback = functools.partial(_reload_on_update, modify_times) - scheduler = ioloop.PeriodicCallback(callback, check_time) - scheduler.start() - - -def wait() -> None: - """Wait for a watched file to change, then restart the process. - - Intended to be used at the end of scripts like unit test runners, - to run the tests again after any source file changes (but see also - the command-line interface in `main`) - """ - io_loop = ioloop.IOLoop() - io_loop.add_callback(start) - io_loop.start() - - -def watch(filename: str) -> None: - """Add a file to the watch list. - - All imported modules are watched by default. - """ - _watched_files.add(filename) - - -def add_reload_hook(fn: Callable[[], None]) -> None: - """Add a function to be called before reloading the process. - - Note that for open file and socket handles it is generally - preferable to set the ``FD_CLOEXEC`` flag (using `fcntl` or - `os.set_inheritable`) instead of using a reload hook to close them. - """ - _reload_hooks.append(fn) - - -def _reload_on_update(modify_times: Dict[str, float]) -> None: - if _reload_attempted: - # We already tried to reload and it didn't work, so don't try again. - return - if process.task_id() is not None: - # We're in a child process created by fork_processes. If child - # processes restarted themselves, they'd all restart and then - # all call fork_processes again. - return - for module in list(sys.modules.values()): - # Some modules play games with sys.modules (e.g. email/__init__.py - # in the standard library), and occasionally this can cause strange - # failures in getattr. Just ignore anything that's not an ordinary - # module. - if not isinstance(module, types.ModuleType): - continue - path = getattr(module, "__file__", None) - if not path: - continue - if path.endswith(".pyc") or path.endswith(".pyo"): - path = path[:-1] - _check_file(modify_times, path) - for path in _watched_files: - _check_file(modify_times, path) - - -def _check_file(modify_times: Dict[str, float], path: str) -> None: - try: - modified = os.stat(path).st_mtime - except Exception: - return - if path not in modify_times: - modify_times[path] = modified - return - if modify_times[path] != modified: - gen_log.info("%s modified; restarting server", path) - _reload() - - -def _reload() -> None: - global _reload_attempted - _reload_attempted = True - for fn in _reload_hooks: - fn() - if hasattr(signal, "setitimer"): - # Clear the alarm signal set by - # ioloop.set_blocking_log_threshold so it doesn't fire - # after the exec. - signal.setitimer(signal.ITIMER_REAL, 0, 0) - # sys.path fixes: see comments at top of file. If __main__.__spec__ - # exists, we were invoked with -m and the effective path is about to - # change on re-exec. Reconstruct the original command line to - # ensure that the new process sees the same path we did. If - # __spec__ is not available (Python < 3.4), check instead if - # sys.path[0] is an empty string and add the current directory to - # $PYTHONPATH. - if _autoreload_is_main: - assert _original_argv is not None - spec = _original_spec - argv = _original_argv - else: - spec = getattr(sys.modules["__main__"], "__spec__", None) - argv = sys.argv - if spec: - argv = ["-m", spec.name] + argv[1:] - else: - path_prefix = "." + os.pathsep - if sys.path[0] == "" and not os.environ.get("PYTHONPATH", "").startswith( - path_prefix - ): - os.environ["PYTHONPATH"] = path_prefix + os.environ.get("PYTHONPATH", "") - if not _has_execv: - subprocess.Popen([sys.executable] + argv) - os._exit(0) - else: - try: - os.execv(sys.executable, [sys.executable] + argv) - except OSError: - # Mac OS X versions prior to 10.6 do not support execv in - # a process that contains multiple threads. Instead of - # re-executing in the current process, start a new one - # and cause the current process to exit. This isn't - # ideal since the new process is detached from the parent - # terminal and thus cannot easily be killed with ctrl-C, - # but it's better than not being able to autoreload at - # all. - # Unfortunately the errno returned in this case does not - # appear to be consistent, so we can't easily check for - # this error specifically. - os.spawnv( - os.P_NOWAIT, sys.executable, [sys.executable] + argv # type: ignore - ) - # At this point the IOLoop has been closed and finally - # blocks will experience errors if we allow the stack to - # unwind, so just exit uncleanly. - os._exit(0) - - -_USAGE = """\ -Usage: - python -m tornado.autoreload -m module.to.run [args...] - python -m tornado.autoreload path/to/script.py [args...] -""" - - -def main() -> None: - """Command-line wrapper to re-run a script whenever its source changes. - - Scripts may be specified by filename or module name:: - - python -m tornado.autoreload -m tornado.test.runtests - python -m tornado.autoreload tornado/test/runtests.py - - Running a script with this wrapper is similar to calling - `tornado.autoreload.wait` at the end of the script, but this wrapper - can catch import-time problems like syntax errors that would otherwise - prevent the script from reaching its call to `wait`. - """ - # Remember that we were launched with autoreload as main. - # The main module can be tricky; set the variables both in our globals - # (which may be __main__) and the real importable version. - import tornado.autoreload - - global _autoreload_is_main - global _original_argv, _original_spec - tornado.autoreload._autoreload_is_main = _autoreload_is_main = True - original_argv = sys.argv - tornado.autoreload._original_argv = _original_argv = original_argv - original_spec = getattr(sys.modules["__main__"], "__spec__", None) - tornado.autoreload._original_spec = _original_spec = original_spec - sys.argv = sys.argv[:] - if len(sys.argv) >= 3 and sys.argv[1] == "-m": - mode = "module" - module = sys.argv[2] - del sys.argv[1:3] - elif len(sys.argv) >= 2: - mode = "script" - script = sys.argv[1] - sys.argv = sys.argv[1:] - else: - print(_USAGE, file=sys.stderr) - sys.exit(1) - - try: - if mode == "module": - import runpy - - runpy.run_module(module, run_name="__main__", alter_sys=True) - elif mode == "script": - with open(script) as f: - # Execute the script in our namespace instead of creating - # a new one so that something that tries to import __main__ - # (e.g. the unittest module) will see names defined in the - # script instead of just those defined in this module. - global __file__ - __file__ = script - # If __package__ is defined, imports may be incorrectly - # interpreted as relative to this module. - global __package__ - del __package__ - exec_in(f.read(), globals(), globals()) - except SystemExit as e: - logging.basicConfig() - gen_log.info("Script exited with status %s", e.code) - except Exception as e: - logging.basicConfig() - gen_log.warning("Script exited with uncaught exception", exc_info=True) - # If an exception occurred at import time, the file with the error - # never made it into sys.modules and so we won't know to watch it. - # Just to make sure we've covered everything, walk the stack trace - # from the exception and watch every file. - for (filename, lineno, name, line) in traceback.extract_tb(sys.exc_info()[2]): - watch(filename) - if isinstance(e, SyntaxError): - # SyntaxErrors are special: their innermost stack frame is fake - # so extract_tb won't see it and we have to get the filename - # from the exception object. - watch(e.filename) - else: - logging.basicConfig() - gen_log.info("Script exited normally") - # restore sys.argv so subsequent executions will include autoreload - sys.argv = original_argv - - if mode == "module": - # runpy did a fake import of the module as __main__, but now it's - # no longer in sys.modules. Figure out where it is and watch it. - loader = pkgutil.get_loader(module) - if loader is not None: - watch(loader.get_filename()) # type: ignore - - wait() - - -if __name__ == "__main__": - # See also the other __main__ block at the top of the file, which modifies - # sys.path before our imports - main() diff --git a/telegramer/include/tornado/concurrent.py b/telegramer/include/tornado/concurrent.py deleted file mode 100644 index 7638fcf..0000000 --- a/telegramer/include/tornado/concurrent.py +++ /dev/null @@ -1,263 +0,0 @@ -# -# Copyright 2012 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Utilities for working with ``Future`` objects. - -Tornado previously provided its own ``Future`` class, but now uses -`asyncio.Future`. This module contains utility functions for working -with `asyncio.Future` in a way that is backwards-compatible with -Tornado's old ``Future`` implementation. - -While this module is an important part of Tornado's internal -implementation, applications rarely need to interact with it -directly. - -""" - -import asyncio -from concurrent import futures -import functools -import sys -import types - -from tornado.log import app_log - -import typing -from typing import Any, Callable, Optional, Tuple, Union - -_T = typing.TypeVar("_T") - - -class ReturnValueIgnoredError(Exception): - # No longer used; was previously used by @return_future - pass - - -Future = asyncio.Future - -FUTURES = (futures.Future, Future) - - -def is_future(x: Any) -> bool: - return isinstance(x, FUTURES) - - -class DummyExecutor(futures.Executor): - def submit( - self, fn: Callable[..., _T], *args: Any, **kwargs: Any - ) -> "futures.Future[_T]": - future = futures.Future() # type: futures.Future[_T] - try: - future_set_result_unless_cancelled(future, fn(*args, **kwargs)) - except Exception: - future_set_exc_info(future, sys.exc_info()) - return future - - def shutdown(self, wait: bool = True) -> None: - pass - - -dummy_executor = DummyExecutor() - - -def run_on_executor(*args: Any, **kwargs: Any) -> Callable: - """Decorator to run a synchronous method asynchronously on an executor. - - Returns a future. - - The executor to be used is determined by the ``executor`` - attributes of ``self``. To use a different attribute name, pass a - keyword argument to the decorator:: - - @run_on_executor(executor='_thread_pool') - def foo(self): - pass - - This decorator should not be confused with the similarly-named - `.IOLoop.run_in_executor`. In general, using ``run_in_executor`` - when *calling* a blocking method is recommended instead of using - this decorator when *defining* a method. If compatibility with older - versions of Tornado is required, consider defining an executor - and using ``executor.submit()`` at the call site. - - .. versionchanged:: 4.2 - Added keyword arguments to use alternative attributes. - - .. versionchanged:: 5.0 - Always uses the current IOLoop instead of ``self.io_loop``. - - .. versionchanged:: 5.1 - Returns a `.Future` compatible with ``await`` instead of a - `concurrent.futures.Future`. - - .. deprecated:: 5.1 - - The ``callback`` argument is deprecated and will be removed in - 6.0. The decorator itself is discouraged in new code but will - not be removed in 6.0. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. - """ - # Fully type-checking decorators is tricky, and this one is - # discouraged anyway so it doesn't have all the generic magic. - def run_on_executor_decorator(fn: Callable) -> Callable[..., Future]: - executor = kwargs.get("executor", "executor") - - @functools.wraps(fn) - def wrapper(self: Any, *args: Any, **kwargs: Any) -> Future: - async_future = Future() # type: Future - conc_future = getattr(self, executor).submit(fn, self, *args, **kwargs) - chain_future(conc_future, async_future) - return async_future - - return wrapper - - if args and kwargs: - raise ValueError("cannot combine positional and keyword args") - if len(args) == 1: - return run_on_executor_decorator(args[0]) - elif len(args) != 0: - raise ValueError("expected 1 argument, got %d", len(args)) - return run_on_executor_decorator - - -_NO_RESULT = object() - - -def chain_future(a: "Future[_T]", b: "Future[_T]") -> None: - """Chain two futures together so that when one completes, so does the other. - - The result (success or failure) of ``a`` will be copied to ``b``, unless - ``b`` has already been completed or cancelled by the time ``a`` finishes. - - .. versionchanged:: 5.0 - - Now accepts both Tornado/asyncio `Future` objects and - `concurrent.futures.Future`. - - """ - - def copy(future: "Future[_T]") -> None: - assert future is a - if b.done(): - return - if hasattr(a, "exc_info") and a.exc_info() is not None: # type: ignore - future_set_exc_info(b, a.exc_info()) # type: ignore - elif a.exception() is not None: - b.set_exception(a.exception()) - else: - b.set_result(a.result()) - - if isinstance(a, Future): - future_add_done_callback(a, copy) - else: - # concurrent.futures.Future - from tornado.ioloop import IOLoop - - IOLoop.current().add_future(a, copy) - - -def future_set_result_unless_cancelled( - future: "Union[futures.Future[_T], Future[_T]]", value: _T -) -> None: - """Set the given ``value`` as the `Future`'s result, if not cancelled. - - Avoids ``asyncio.InvalidStateError`` when calling ``set_result()`` on - a cancelled `asyncio.Future`. - - .. versionadded:: 5.0 - """ - if not future.cancelled(): - future.set_result(value) - - -def future_set_exception_unless_cancelled( - future: "Union[futures.Future[_T], Future[_T]]", exc: BaseException -) -> None: - """Set the given ``exc`` as the `Future`'s exception. - - If the Future is already canceled, logs the exception instead. If - this logging is not desired, the caller should explicitly check - the state of the Future and call ``Future.set_exception`` instead of - this wrapper. - - Avoids ``asyncio.InvalidStateError`` when calling ``set_exception()`` on - a cancelled `asyncio.Future`. - - .. versionadded:: 6.0 - - """ - if not future.cancelled(): - future.set_exception(exc) - else: - app_log.error("Exception after Future was cancelled", exc_info=exc) - - -def future_set_exc_info( - future: "Union[futures.Future[_T], Future[_T]]", - exc_info: Tuple[ - Optional[type], Optional[BaseException], Optional[types.TracebackType] - ], -) -> None: - """Set the given ``exc_info`` as the `Future`'s exception. - - Understands both `asyncio.Future` and the extensions in older - versions of Tornado to enable better tracebacks on Python 2. - - .. versionadded:: 5.0 - - .. versionchanged:: 6.0 - - If the future is already cancelled, this function is a no-op. - (previously ``asyncio.InvalidStateError`` would be raised) - - """ - if exc_info[1] is None: - raise Exception("future_set_exc_info called with no exception") - future_set_exception_unless_cancelled(future, exc_info[1]) - - -@typing.overload -def future_add_done_callback( - future: "futures.Future[_T]", callback: Callable[["futures.Future[_T]"], None] -) -> None: - pass - - -@typing.overload # noqa: F811 -def future_add_done_callback( - future: "Future[_T]", callback: Callable[["Future[_T]"], None] -) -> None: - pass - - -def future_add_done_callback( # noqa: F811 - future: "Union[futures.Future[_T], Future[_T]]", callback: Callable[..., None] -) -> None: - """Arrange to call ``callback`` when ``future`` is complete. - - ``callback`` is invoked with one argument, the ``future``. - - If ``future`` is already done, ``callback`` is invoked immediately. - This may differ from the behavior of ``Future.add_done_callback``, - which makes no such guarantee. - - .. versionadded:: 5.0 - """ - if future.done(): - callback(future) - else: - future.add_done_callback(callback) diff --git a/telegramer/include/tornado/curl_httpclient.py b/telegramer/include/tornado/curl_httpclient.py deleted file mode 100644 index 6553999..0000000 --- a/telegramer/include/tornado/curl_httpclient.py +++ /dev/null @@ -1,583 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Non-blocking HTTP client implementation using pycurl.""" - -import collections -import functools -import logging -import pycurl -import threading -import time -from io import BytesIO - -from tornado import httputil -from tornado import ioloop - -from tornado.escape import utf8, native_str -from tornado.httpclient import ( - HTTPRequest, - HTTPResponse, - HTTPError, - AsyncHTTPClient, - main, -) -from tornado.log import app_log - -from typing import Dict, Any, Callable, Union, Tuple, Optional -import typing - -if typing.TYPE_CHECKING: - from typing import Deque # noqa: F401 - -curl_log = logging.getLogger("tornado.curl_httpclient") - - -class CurlAsyncHTTPClient(AsyncHTTPClient): - def initialize( # type: ignore - self, max_clients: int = 10, defaults: Optional[Dict[str, Any]] = None - ) -> None: - super().initialize(defaults=defaults) - # Typeshed is incomplete for CurlMulti, so just use Any for now. - self._multi = pycurl.CurlMulti() # type: Any - self._multi.setopt(pycurl.M_TIMERFUNCTION, self._set_timeout) - self._multi.setopt(pycurl.M_SOCKETFUNCTION, self._handle_socket) - self._curls = [self._curl_create() for i in range(max_clients)] - self._free_list = self._curls[:] - self._requests = ( - collections.deque() - ) # type: Deque[Tuple[HTTPRequest, Callable[[HTTPResponse], None], float]] - self._fds = {} # type: Dict[int, int] - self._timeout = None # type: Optional[object] - - # libcurl has bugs that sometimes cause it to not report all - # relevant file descriptors and timeouts to TIMERFUNCTION/ - # SOCKETFUNCTION. Mitigate the effects of such bugs by - # forcing a periodic scan of all active requests. - self._force_timeout_callback = ioloop.PeriodicCallback( - self._handle_force_timeout, 1000 - ) - self._force_timeout_callback.start() - - # Work around a bug in libcurl 7.29.0: Some fields in the curl - # multi object are initialized lazily, and its destructor will - # segfault if it is destroyed without having been used. Add - # and remove a dummy handle to make sure everything is - # initialized. - dummy_curl_handle = pycurl.Curl() - self._multi.add_handle(dummy_curl_handle) - self._multi.remove_handle(dummy_curl_handle) - - def close(self) -> None: - self._force_timeout_callback.stop() - if self._timeout is not None: - self.io_loop.remove_timeout(self._timeout) - for curl in self._curls: - curl.close() - self._multi.close() - super().close() - - # Set below properties to None to reduce the reference count of current - # instance, because those properties hold some methods of current - # instance that will case circular reference. - self._force_timeout_callback = None # type: ignore - self._multi = None - - def fetch_impl( - self, request: HTTPRequest, callback: Callable[[HTTPResponse], None] - ) -> None: - self._requests.append((request, callback, self.io_loop.time())) - self._process_queue() - self._set_timeout(0) - - def _handle_socket(self, event: int, fd: int, multi: Any, data: bytes) -> None: - """Called by libcurl when it wants to change the file descriptors - it cares about. - """ - event_map = { - pycurl.POLL_NONE: ioloop.IOLoop.NONE, - pycurl.POLL_IN: ioloop.IOLoop.READ, - pycurl.POLL_OUT: ioloop.IOLoop.WRITE, - pycurl.POLL_INOUT: ioloop.IOLoop.READ | ioloop.IOLoop.WRITE, - } - if event == pycurl.POLL_REMOVE: - if fd in self._fds: - self.io_loop.remove_handler(fd) - del self._fds[fd] - else: - ioloop_event = event_map[event] - # libcurl sometimes closes a socket and then opens a new - # one using the same FD without giving us a POLL_NONE in - # between. This is a problem with the epoll IOLoop, - # because the kernel can tell when a socket is closed and - # removes it from the epoll automatically, causing future - # update_handler calls to fail. Since we can't tell when - # this has happened, always use remove and re-add - # instead of update. - if fd in self._fds: - self.io_loop.remove_handler(fd) - self.io_loop.add_handler(fd, self._handle_events, ioloop_event) - self._fds[fd] = ioloop_event - - def _set_timeout(self, msecs: int) -> None: - """Called by libcurl to schedule a timeout.""" - if self._timeout is not None: - self.io_loop.remove_timeout(self._timeout) - self._timeout = self.io_loop.add_timeout( - self.io_loop.time() + msecs / 1000.0, self._handle_timeout - ) - - def _handle_events(self, fd: int, events: int) -> None: - """Called by IOLoop when there is activity on one of our - file descriptors. - """ - action = 0 - if events & ioloop.IOLoop.READ: - action |= pycurl.CSELECT_IN - if events & ioloop.IOLoop.WRITE: - action |= pycurl.CSELECT_OUT - while True: - try: - ret, num_handles = self._multi.socket_action(fd, action) - except pycurl.error as e: - ret = e.args[0] - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - self._finish_pending_requests() - - def _handle_timeout(self) -> None: - """Called by IOLoop when the requested timeout has passed.""" - self._timeout = None - while True: - try: - ret, num_handles = self._multi.socket_action(pycurl.SOCKET_TIMEOUT, 0) - except pycurl.error as e: - ret = e.args[0] - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - self._finish_pending_requests() - - # In theory, we shouldn't have to do this because curl will - # call _set_timeout whenever the timeout changes. However, - # sometimes after _handle_timeout we will need to reschedule - # immediately even though nothing has changed from curl's - # perspective. This is because when socket_action is - # called with SOCKET_TIMEOUT, libcurl decides internally which - # timeouts need to be processed by using a monotonic clock - # (where available) while tornado uses python's time.time() - # to decide when timeouts have occurred. When those clocks - # disagree on elapsed time (as they will whenever there is an - # NTP adjustment), tornado might call _handle_timeout before - # libcurl is ready. After each timeout, resync the scheduled - # timeout with libcurl's current state. - new_timeout = self._multi.timeout() - if new_timeout >= 0: - self._set_timeout(new_timeout) - - def _handle_force_timeout(self) -> None: - """Called by IOLoop periodically to ask libcurl to process any - events it may have forgotten about. - """ - while True: - try: - ret, num_handles = self._multi.socket_all() - except pycurl.error as e: - ret = e.args[0] - if ret != pycurl.E_CALL_MULTI_PERFORM: - break - self._finish_pending_requests() - - def _finish_pending_requests(self) -> None: - """Process any requests that were completed by the last - call to multi.socket_action. - """ - while True: - num_q, ok_list, err_list = self._multi.info_read() - for curl in ok_list: - self._finish(curl) - for curl, errnum, errmsg in err_list: - self._finish(curl, errnum, errmsg) - if num_q == 0: - break - self._process_queue() - - def _process_queue(self) -> None: - while True: - started = 0 - while self._free_list and self._requests: - started += 1 - curl = self._free_list.pop() - (request, callback, queue_start_time) = self._requests.popleft() - # TODO: Don't smuggle extra data on an attribute of the Curl object. - curl.info = { # type: ignore - "headers": httputil.HTTPHeaders(), - "buffer": BytesIO(), - "request": request, - "callback": callback, - "queue_start_time": queue_start_time, - "curl_start_time": time.time(), - "curl_start_ioloop_time": self.io_loop.current().time(), - } - try: - self._curl_setup_request( - curl, - request, - curl.info["buffer"], # type: ignore - curl.info["headers"], # type: ignore - ) - except Exception as e: - # If there was an error in setup, pass it on - # to the callback. Note that allowing the - # error to escape here will appear to work - # most of the time since we are still in the - # caller's original stack frame, but when - # _process_queue() is called from - # _finish_pending_requests the exceptions have - # nowhere to go. - self._free_list.append(curl) - callback(HTTPResponse(request=request, code=599, error=e)) - else: - self._multi.add_handle(curl) - - if not started: - break - - def _finish( - self, - curl: pycurl.Curl, - curl_error: Optional[int] = None, - curl_message: Optional[str] = None, - ) -> None: - info = curl.info # type: ignore - curl.info = None # type: ignore - self._multi.remove_handle(curl) - self._free_list.append(curl) - buffer = info["buffer"] - if curl_error: - assert curl_message is not None - error = CurlError(curl_error, curl_message) # type: Optional[CurlError] - assert error is not None - code = error.code - effective_url = None - buffer.close() - buffer = None - else: - error = None - code = curl.getinfo(pycurl.HTTP_CODE) - effective_url = curl.getinfo(pycurl.EFFECTIVE_URL) - buffer.seek(0) - # the various curl timings are documented at - # http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html - time_info = dict( - queue=info["curl_start_ioloop_time"] - info["queue_start_time"], - namelookup=curl.getinfo(pycurl.NAMELOOKUP_TIME), - connect=curl.getinfo(pycurl.CONNECT_TIME), - appconnect=curl.getinfo(pycurl.APPCONNECT_TIME), - pretransfer=curl.getinfo(pycurl.PRETRANSFER_TIME), - starttransfer=curl.getinfo(pycurl.STARTTRANSFER_TIME), - total=curl.getinfo(pycurl.TOTAL_TIME), - redirect=curl.getinfo(pycurl.REDIRECT_TIME), - ) - try: - info["callback"]( - HTTPResponse( - request=info["request"], - code=code, - headers=info["headers"], - buffer=buffer, - effective_url=effective_url, - error=error, - reason=info["headers"].get("X-Http-Reason", None), - request_time=self.io_loop.time() - info["curl_start_ioloop_time"], - start_time=info["curl_start_time"], - time_info=time_info, - ) - ) - except Exception: - self.handle_callback_exception(info["callback"]) - - def handle_callback_exception(self, callback: Any) -> None: - app_log.error("Exception in callback %r", callback, exc_info=True) - - def _curl_create(self) -> pycurl.Curl: - curl = pycurl.Curl() - if curl_log.isEnabledFor(logging.DEBUG): - curl.setopt(pycurl.VERBOSE, 1) - curl.setopt(pycurl.DEBUGFUNCTION, self._curl_debug) - if hasattr( - pycurl, "PROTOCOLS" - ): # PROTOCOLS first appeared in pycurl 7.19.5 (2014-07-12) - curl.setopt(pycurl.PROTOCOLS, pycurl.PROTO_HTTP | pycurl.PROTO_HTTPS) - curl.setopt(pycurl.REDIR_PROTOCOLS, pycurl.PROTO_HTTP | pycurl.PROTO_HTTPS) - return curl - - def _curl_setup_request( - self, - curl: pycurl.Curl, - request: HTTPRequest, - buffer: BytesIO, - headers: httputil.HTTPHeaders, - ) -> None: - curl.setopt(pycurl.URL, native_str(request.url)) - - # libcurl's magic "Expect: 100-continue" behavior causes delays - # with servers that don't support it (which include, among others, - # Google's OpenID endpoint). Additionally, this behavior has - # a bug in conjunction with the curl_multi_socket_action API - # (https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3039744&group_id=976), - # which increases the delays. It's more trouble than it's worth, - # so just turn off the feature (yes, setting Expect: to an empty - # value is the official way to disable this) - if "Expect" not in request.headers: - request.headers["Expect"] = "" - - # libcurl adds Pragma: no-cache by default; disable that too - if "Pragma" not in request.headers: - request.headers["Pragma"] = "" - - curl.setopt( - pycurl.HTTPHEADER, - [ - "%s: %s" % (native_str(k), native_str(v)) - for k, v in request.headers.get_all() - ], - ) - - curl.setopt( - pycurl.HEADERFUNCTION, - functools.partial( - self._curl_header_callback, headers, request.header_callback - ), - ) - if request.streaming_callback: - - def write_function(b: Union[bytes, bytearray]) -> int: - assert request.streaming_callback is not None - self.io_loop.add_callback(request.streaming_callback, b) - return len(b) - - else: - write_function = buffer.write - curl.setopt(pycurl.WRITEFUNCTION, write_function) - curl.setopt(pycurl.FOLLOWLOCATION, request.follow_redirects) - curl.setopt(pycurl.MAXREDIRS, request.max_redirects) - assert request.connect_timeout is not None - curl.setopt(pycurl.CONNECTTIMEOUT_MS, int(1000 * request.connect_timeout)) - assert request.request_timeout is not None - curl.setopt(pycurl.TIMEOUT_MS, int(1000 * request.request_timeout)) - if request.user_agent: - curl.setopt(pycurl.USERAGENT, native_str(request.user_agent)) - else: - curl.setopt(pycurl.USERAGENT, "Mozilla/5.0 (compatible; pycurl)") - if request.network_interface: - curl.setopt(pycurl.INTERFACE, request.network_interface) - if request.decompress_response: - curl.setopt(pycurl.ENCODING, "gzip,deflate") - else: - curl.setopt(pycurl.ENCODING, None) - if request.proxy_host and request.proxy_port: - curl.setopt(pycurl.PROXY, request.proxy_host) - curl.setopt(pycurl.PROXYPORT, request.proxy_port) - if request.proxy_username: - assert request.proxy_password is not None - credentials = httputil.encode_username_password( - request.proxy_username, request.proxy_password - ) - curl.setopt(pycurl.PROXYUSERPWD, credentials) - - if request.proxy_auth_mode is None or request.proxy_auth_mode == "basic": - curl.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_BASIC) - elif request.proxy_auth_mode == "digest": - curl.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_DIGEST) - else: - raise ValueError( - "Unsupported proxy_auth_mode %s" % request.proxy_auth_mode - ) - else: - try: - curl.unsetopt(pycurl.PROXY) - except TypeError: # not supported, disable proxy - curl.setopt(pycurl.PROXY, "") - curl.unsetopt(pycurl.PROXYUSERPWD) - if request.validate_cert: - curl.setopt(pycurl.SSL_VERIFYPEER, 1) - curl.setopt(pycurl.SSL_VERIFYHOST, 2) - else: - curl.setopt(pycurl.SSL_VERIFYPEER, 0) - curl.setopt(pycurl.SSL_VERIFYHOST, 0) - if request.ca_certs is not None: - curl.setopt(pycurl.CAINFO, request.ca_certs) - else: - # There is no way to restore pycurl.CAINFO to its default value - # (Using unsetopt makes it reject all certificates). - # I don't see any way to read the default value from python so it - # can be restored later. We'll have to just leave CAINFO untouched - # if no ca_certs file was specified, and require that if any - # request uses a custom ca_certs file, they all must. - pass - - if request.allow_ipv6 is False: - # Curl behaves reasonably when DNS resolution gives an ipv6 address - # that we can't reach, so allow ipv6 unless the user asks to disable. - curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4) - else: - curl.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER) - - # Set the request method through curl's irritating interface which makes - # up names for almost every single method - curl_options = { - "GET": pycurl.HTTPGET, - "POST": pycurl.POST, - "PUT": pycurl.UPLOAD, - "HEAD": pycurl.NOBODY, - } - custom_methods = set(["DELETE", "OPTIONS", "PATCH"]) - for o in curl_options.values(): - curl.setopt(o, False) - if request.method in curl_options: - curl.unsetopt(pycurl.CUSTOMREQUEST) - curl.setopt(curl_options[request.method], True) - elif request.allow_nonstandard_methods or request.method in custom_methods: - curl.setopt(pycurl.CUSTOMREQUEST, request.method) - else: - raise KeyError("unknown method " + request.method) - - body_expected = request.method in ("POST", "PATCH", "PUT") - body_present = request.body is not None - if not request.allow_nonstandard_methods: - # Some HTTP methods nearly always have bodies while others - # almost never do. Fail in this case unless the user has - # opted out of sanity checks with allow_nonstandard_methods. - if (body_expected and not body_present) or ( - body_present and not body_expected - ): - raise ValueError( - "Body must %sbe None for method %s (unless " - "allow_nonstandard_methods is true)" - % ("not " if body_expected else "", request.method) - ) - - if body_expected or body_present: - if request.method == "GET": - # Even with `allow_nonstandard_methods` we disallow - # GET with a body (because libcurl doesn't allow it - # unless we use CUSTOMREQUEST). While the spec doesn't - # forbid clients from sending a body, it arguably - # disallows the server from doing anything with them. - raise ValueError("Body must be None for GET request") - request_buffer = BytesIO(utf8(request.body or "")) - - def ioctl(cmd: int) -> None: - if cmd == curl.IOCMD_RESTARTREAD: # type: ignore - request_buffer.seek(0) - - curl.setopt(pycurl.READFUNCTION, request_buffer.read) - curl.setopt(pycurl.IOCTLFUNCTION, ioctl) - if request.method == "POST": - curl.setopt(pycurl.POSTFIELDSIZE, len(request.body or "")) - else: - curl.setopt(pycurl.UPLOAD, True) - curl.setopt(pycurl.INFILESIZE, len(request.body or "")) - - if request.auth_username is not None: - assert request.auth_password is not None - if request.auth_mode is None or request.auth_mode == "basic": - curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_BASIC) - elif request.auth_mode == "digest": - curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_DIGEST) - else: - raise ValueError("Unsupported auth_mode %s" % request.auth_mode) - - userpwd = httputil.encode_username_password( - request.auth_username, request.auth_password - ) - curl.setopt(pycurl.USERPWD, userpwd) - curl_log.debug( - "%s %s (username: %r)", - request.method, - request.url, - request.auth_username, - ) - else: - curl.unsetopt(pycurl.USERPWD) - curl_log.debug("%s %s", request.method, request.url) - - if request.client_cert is not None: - curl.setopt(pycurl.SSLCERT, request.client_cert) - - if request.client_key is not None: - curl.setopt(pycurl.SSLKEY, request.client_key) - - if request.ssl_options is not None: - raise ValueError("ssl_options not supported in curl_httpclient") - - if threading.active_count() > 1: - # libcurl/pycurl is not thread-safe by default. When multiple threads - # are used, signals should be disabled. This has the side effect - # of disabling DNS timeouts in some environments (when libcurl is - # not linked against ares), so we don't do it when there is only one - # thread. Applications that use many short-lived threads may need - # to set NOSIGNAL manually in a prepare_curl_callback since - # there may not be any other threads running at the time we call - # threading.activeCount. - curl.setopt(pycurl.NOSIGNAL, 1) - if request.prepare_curl_callback is not None: - request.prepare_curl_callback(curl) - - def _curl_header_callback( - self, - headers: httputil.HTTPHeaders, - header_callback: Callable[[str], None], - header_line_bytes: bytes, - ) -> None: - header_line = native_str(header_line_bytes.decode("latin1")) - if header_callback is not None: - self.io_loop.add_callback(header_callback, header_line) - # header_line as returned by curl includes the end-of-line characters. - # whitespace at the start should be preserved to allow multi-line headers - header_line = header_line.rstrip() - if header_line.startswith("HTTP/"): - headers.clear() - try: - (__, __, reason) = httputil.parse_response_start_line(header_line) - header_line = "X-Http-Reason: %s" % reason - except httputil.HTTPInputError: - return - if not header_line: - return - headers.parse_line(header_line) - - def _curl_debug(self, debug_type: int, debug_msg: str) -> None: - debug_types = ("I", "<", ">", "<", ">") - if debug_type == 0: - debug_msg = native_str(debug_msg) - curl_log.debug("%s", debug_msg.strip()) - elif debug_type in (1, 2): - debug_msg = native_str(debug_msg) - for line in debug_msg.splitlines(): - curl_log.debug("%s %s", debug_types[debug_type], line) - elif debug_type == 4: - curl_log.debug("%s %r", debug_types[debug_type], debug_msg) - - -class CurlError(HTTPError): - def __init__(self, errno: int, message: str) -> None: - HTTPError.__init__(self, 599, message) - self.errno = errno - - -if __name__ == "__main__": - AsyncHTTPClient.configure(CurlAsyncHTTPClient) - main() diff --git a/telegramer/include/tornado/escape.py b/telegramer/include/tornado/escape.py deleted file mode 100644 index 3cf7ff2..0000000 --- a/telegramer/include/tornado/escape.py +++ /dev/null @@ -1,402 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Escaping/unescaping methods for HTML, JSON, URLs, and others. - -Also includes a few other miscellaneous string manipulation functions that -have crept in over time. -""" - -import html.entities -import json -import re -import urllib.parse - -from tornado.util import unicode_type - -import typing -from typing import Union, Any, Optional, Dict, List, Callable - - -_XHTML_ESCAPE_RE = re.compile("[&<>\"']") -_XHTML_ESCAPE_DICT = { - "&": "&amp;", - "<": "&lt;", - ">": "&gt;", - '"': "&quot;", - "'": "&#39;", -} - - -def xhtml_escape(value: Union[str, bytes]) -> str: - """Escapes a string so it is valid within HTML or XML. - - Escapes the characters ``<``, ``>``, ``"``, ``'``, and ``&``. - When used in attribute values the escaped strings must be enclosed - in quotes. - - .. versionchanged:: 3.2 - - Added the single quote to the list of escaped characters. - """ - return _XHTML_ESCAPE_RE.sub( - lambda match: _XHTML_ESCAPE_DICT[match.group(0)], to_basestring(value) - ) - - -def xhtml_unescape(value: Union[str, bytes]) -> str: - """Un-escapes an XML-escaped string.""" - return re.sub(r"&(#?)(\w+?);", _convert_entity, _unicode(value)) - - -# The fact that json_encode wraps json.dumps is an implementation detail. -# Please see https://github.com/tornadoweb/tornado/pull/706 -# before sending a pull request that adds **kwargs to this function. -def json_encode(value: Any) -> str: - """JSON-encodes the given Python object.""" - # JSON permits but does not require forward slashes to be escaped. - # This is useful when json data is emitted in a <script> tag - # in HTML, as it prevents </script> tags from prematurely terminating - # the JavaScript. Some json libraries do this escaping by default, - # although python's standard library does not, so we do it here. - # http://stackoverflow.com/questions/1580647/json-why-are-forward-slashes-escaped - return json.dumps(value).replace("</", "<\\/") - - -def json_decode(value: Union[str, bytes]) -> Any: - """Returns Python objects for the given JSON string. - - Supports both `str` and `bytes` inputs. - """ - return json.loads(to_basestring(value)) - - -def squeeze(value: str) -> str: - """Replace all sequences of whitespace chars with a single space.""" - return re.sub(r"[\x00-\x20]+", " ", value).strip() - - -def url_escape(value: Union[str, bytes], plus: bool = True) -> str: - """Returns a URL-encoded version of the given value. - - If ``plus`` is true (the default), spaces will be represented - as "+" instead of "%20". This is appropriate for query strings - but not for the path component of a URL. Note that this default - is the reverse of Python's urllib module. - - .. versionadded:: 3.1 - The ``plus`` argument - """ - quote = urllib.parse.quote_plus if plus else urllib.parse.quote - return quote(utf8(value)) - - -@typing.overload -def url_unescape(value: Union[str, bytes], encoding: None, plus: bool = True) -> bytes: - pass - - -@typing.overload # noqa: F811 -def url_unescape( - value: Union[str, bytes], encoding: str = "utf-8", plus: bool = True -) -> str: - pass - - -def url_unescape( # noqa: F811 - value: Union[str, bytes], encoding: Optional[str] = "utf-8", plus: bool = True -) -> Union[str, bytes]: - """Decodes the given value from a URL. - - The argument may be either a byte or unicode string. - - If encoding is None, the result will be a byte string. Otherwise, - the result is a unicode string in the specified encoding. - - If ``plus`` is true (the default), plus signs will be interpreted - as spaces (literal plus signs must be represented as "%2B"). This - is appropriate for query strings and form-encoded values but not - for the path component of a URL. Note that this default is the - reverse of Python's urllib module. - - .. versionadded:: 3.1 - The ``plus`` argument - """ - if encoding is None: - if plus: - # unquote_to_bytes doesn't have a _plus variant - value = to_basestring(value).replace("+", " ") - return urllib.parse.unquote_to_bytes(value) - else: - unquote = urllib.parse.unquote_plus if plus else urllib.parse.unquote - return unquote(to_basestring(value), encoding=encoding) - - -def parse_qs_bytes( - qs: Union[str, bytes], keep_blank_values: bool = False, strict_parsing: bool = False -) -> Dict[str, List[bytes]]: - """Parses a query string like urlparse.parse_qs, - but takes bytes and returns the values as byte strings. - - Keys still become type str (interpreted as latin1 in python3!) - because it's too painful to keep them as byte strings in - python3 and in practice they're nearly always ascii anyway. - """ - # This is gross, but python3 doesn't give us another way. - # Latin1 is the universal donor of character encodings. - if isinstance(qs, bytes): - qs = qs.decode("latin1") - result = urllib.parse.parse_qs( - qs, keep_blank_values, strict_parsing, encoding="latin1", errors="strict" - ) - encoded = {} - for k, v in result.items(): - encoded[k] = [i.encode("latin1") for i in v] - return encoded - - -_UTF8_TYPES = (bytes, type(None)) - - -@typing.overload -def utf8(value: bytes) -> bytes: - pass - - -@typing.overload # noqa: F811 -def utf8(value: str) -> bytes: - pass - - -@typing.overload # noqa: F811 -def utf8(value: None) -> None: - pass - - -def utf8(value: Union[None, str, bytes]) -> Optional[bytes]: # noqa: F811 - """Converts a string argument to a byte string. - - If the argument is already a byte string or None, it is returned unchanged. - Otherwise it must be a unicode string and is encoded as utf8. - """ - if isinstance(value, _UTF8_TYPES): - return value - if not isinstance(value, unicode_type): - raise TypeError("Expected bytes, unicode, or None; got %r" % type(value)) - return value.encode("utf-8") - - -_TO_UNICODE_TYPES = (unicode_type, type(None)) - - -@typing.overload -def to_unicode(value: str) -> str: - pass - - -@typing.overload # noqa: F811 -def to_unicode(value: bytes) -> str: - pass - - -@typing.overload # noqa: F811 -def to_unicode(value: None) -> None: - pass - - -def to_unicode(value: Union[None, str, bytes]) -> Optional[str]: # noqa: F811 - """Converts a string argument to a unicode string. - - If the argument is already a unicode string or None, it is returned - unchanged. Otherwise it must be a byte string and is decoded as utf8. - """ - if isinstance(value, _TO_UNICODE_TYPES): - return value - if not isinstance(value, bytes): - raise TypeError("Expected bytes, unicode, or None; got %r" % type(value)) - return value.decode("utf-8") - - -# to_unicode was previously named _unicode not because it was private, -# but to avoid conflicts with the built-in unicode() function/type -_unicode = to_unicode - -# When dealing with the standard library across python 2 and 3 it is -# sometimes useful to have a direct conversion to the native string type -native_str = to_unicode -to_basestring = to_unicode - - -def recursive_unicode(obj: Any) -> Any: - """Walks a simple data structure, converting byte strings to unicode. - - Supports lists, tuples, and dictionaries. - """ - if isinstance(obj, dict): - return dict( - (recursive_unicode(k), recursive_unicode(v)) for (k, v) in obj.items() - ) - elif isinstance(obj, list): - return list(recursive_unicode(i) for i in obj) - elif isinstance(obj, tuple): - return tuple(recursive_unicode(i) for i in obj) - elif isinstance(obj, bytes): - return to_unicode(obj) - else: - return obj - - -# I originally used the regex from -# http://daringfireball.net/2010/07/improved_regex_for_matching_urls -# but it gets all exponential on certain patterns (such as too many trailing -# dots), causing the regex matcher to never return. -# This regex should avoid those problems. -# Use to_unicode instead of tornado.util.u - we don't want backslashes getting -# processed as escapes. -_URL_RE = re.compile( - to_unicode( - r"""\b((?:([\w-]+):(/{1,3})|www[.])(?:(?:(?:[^\s&()]|&amp;|&quot;)*(?:[^!"#$%&'()*+,.:;<=>?@\[\]^`{|}~\s]))|(?:\((?:[^\s&()]|&amp;|&quot;)*\)))+)""" # noqa: E501 - ) -) - - -def linkify( - text: Union[str, bytes], - shorten: bool = False, - extra_params: Union[str, Callable[[str], str]] = "", - require_protocol: bool = False, - permitted_protocols: List[str] = ["http", "https"], -) -> str: - """Converts plain text into HTML with links. - - For example: ``linkify("Hello http://tornadoweb.org!")`` would return - ``Hello <a href="http://tornadoweb.org">http://tornadoweb.org</a>!`` - - Parameters: - - * ``shorten``: Long urls will be shortened for display. - - * ``extra_params``: Extra text to include in the link tag, or a callable - taking the link as an argument and returning the extra text - e.g. ``linkify(text, extra_params='rel="nofollow" class="external"')``, - or:: - - def extra_params_cb(url): - if url.startswith("http://example.com"): - return 'class="internal"' - else: - return 'class="external" rel="nofollow"' - linkify(text, extra_params=extra_params_cb) - - * ``require_protocol``: Only linkify urls which include a protocol. If - this is False, urls such as www.facebook.com will also be linkified. - - * ``permitted_protocols``: List (or set) of protocols which should be - linkified, e.g. ``linkify(text, permitted_protocols=["http", "ftp", - "mailto"])``. It is very unsafe to include protocols such as - ``javascript``. - """ - if extra_params and not callable(extra_params): - extra_params = " " + extra_params.strip() - - def make_link(m: typing.Match) -> str: - url = m.group(1) - proto = m.group(2) - if require_protocol and not proto: - return url # not protocol, no linkify - - if proto and proto not in permitted_protocols: - return url # bad protocol, no linkify - - href = m.group(1) - if not proto: - href = "http://" + href # no proto specified, use http - - if callable(extra_params): - params = " " + extra_params(href).strip() - else: - params = extra_params - - # clip long urls. max_len is just an approximation - max_len = 30 - if shorten and len(url) > max_len: - before_clip = url - if proto: - proto_len = len(proto) + 1 + len(m.group(3) or "") # +1 for : - else: - proto_len = 0 - - parts = url[proto_len:].split("/") - if len(parts) > 1: - # Grab the whole host part plus the first bit of the path - # The path is usually not that interesting once shortened - # (no more slug, etc), so it really just provides a little - # extra indication of shortening. - url = ( - url[:proto_len] - + parts[0] - + "/" - + parts[1][:8].split("?")[0].split(".")[0] - ) - - if len(url) > max_len * 1.5: # still too long - url = url[:max_len] - - if url != before_clip: - amp = url.rfind("&") - # avoid splitting html char entities - if amp > max_len - 5: - url = url[:amp] - url += "..." - - if len(url) >= len(before_clip): - url = before_clip - else: - # full url is visible on mouse-over (for those who don't - # have a status bar, such as Safari by default) - params += ' title="%s"' % href - - return u'<a href="%s"%s>%s</a>' % (href, params, url) - - # First HTML-escape so that our strings are all safe. - # The regex is modified to avoid character entites other than &amp; so - # that we won't pick up &quot;, etc. - text = _unicode(xhtml_escape(text)) - return _URL_RE.sub(make_link, text) - - -def _convert_entity(m: typing.Match) -> str: - if m.group(1) == "#": - try: - if m.group(2)[:1].lower() == "x": - return chr(int(m.group(2)[1:], 16)) - else: - return chr(int(m.group(2))) - except ValueError: - return "&#%s;" % m.group(2) - try: - return _HTML_UNICODE_MAP[m.group(2)] - except KeyError: - return "&%s;" % m.group(2) - - -def _build_unicode_map() -> Dict[str, str]: - unicode_map = {} - for name, value in html.entities.name2codepoint.items(): - unicode_map[name] = chr(value) - return unicode_map - - -_HTML_UNICODE_MAP = _build_unicode_map() diff --git a/telegramer/include/tornado/gen.py b/telegramer/include/tornado/gen.py deleted file mode 100644 index cab9689..0000000 --- a/telegramer/include/tornado/gen.py +++ /dev/null @@ -1,872 +0,0 @@ -"""``tornado.gen`` implements generator-based coroutines. - -.. note:: - - The "decorator and generator" approach in this module is a - precursor to native coroutines (using ``async def`` and ``await``) - which were introduced in Python 3.5. Applications that do not - require compatibility with older versions of Python should use - native coroutines instead. Some parts of this module are still - useful with native coroutines, notably `multi`, `sleep`, - `WaitIterator`, and `with_timeout`. Some of these functions have - counterparts in the `asyncio` module which may be used as well, - although the two may not necessarily be 100% compatible. - -Coroutines provide an easier way to work in an asynchronous -environment than chaining callbacks. Code using coroutines is -technically asynchronous, but it is written as a single generator -instead of a collection of separate functions. - -For example, here's a coroutine-based handler: - -.. testcode:: - - class GenAsyncHandler(RequestHandler): - @gen.coroutine - def get(self): - http_client = AsyncHTTPClient() - response = yield http_client.fetch("http://example.com") - do_something_with_response(response) - self.render("template.html") - -.. testoutput:: - :hide: - -Asynchronous functions in Tornado return an ``Awaitable`` or `.Future`; -yielding this object returns its result. - -You can also yield a list or dict of other yieldable objects, which -will be started at the same time and run in parallel; a list or dict -of results will be returned when they are all finished: - -.. testcode:: - - @gen.coroutine - def get(self): - http_client = AsyncHTTPClient() - response1, response2 = yield [http_client.fetch(url1), - http_client.fetch(url2)] - response_dict = yield dict(response3=http_client.fetch(url3), - response4=http_client.fetch(url4)) - response3 = response_dict['response3'] - response4 = response_dict['response4'] - -.. testoutput:: - :hide: - -If ``tornado.platform.twisted`` is imported, it is also possible to -yield Twisted's ``Deferred`` objects. See the `convert_yielded` -function to extend this mechanism. - -.. versionchanged:: 3.2 - Dict support added. - -.. versionchanged:: 4.1 - Support added for yielding ``asyncio`` Futures and Twisted Deferreds - via ``singledispatch``. - -""" -import asyncio -import builtins -import collections -from collections.abc import Generator -import concurrent.futures -import datetime -import functools -from functools import singledispatch -from inspect import isawaitable -import sys -import types - -from tornado.concurrent import ( - Future, - is_future, - chain_future, - future_set_exc_info, - future_add_done_callback, - future_set_result_unless_cancelled, -) -from tornado.ioloop import IOLoop -from tornado.log import app_log -from tornado.util import TimeoutError - -try: - import contextvars -except ImportError: - contextvars = None # type: ignore - -import typing -from typing import Union, Any, Callable, List, Type, Tuple, Awaitable, Dict, overload - -if typing.TYPE_CHECKING: - from typing import Sequence, Deque, Optional, Set, Iterable # noqa: F401 - -_T = typing.TypeVar("_T") - -_Yieldable = Union[ - None, Awaitable, List[Awaitable], Dict[Any, Awaitable], concurrent.futures.Future -] - - -class KeyReuseError(Exception): - pass - - -class UnknownKeyError(Exception): - pass - - -class LeakedCallbackError(Exception): - pass - - -class BadYieldError(Exception): - pass - - -class ReturnValueIgnoredError(Exception): - pass - - -def _value_from_stopiteration(e: Union[StopIteration, "Return"]) -> Any: - try: - # StopIteration has a value attribute beginning in py33. - # So does our Return class. - return e.value - except AttributeError: - pass - try: - # Cython backports coroutine functionality by putting the value in - # e.args[0]. - return e.args[0] - except (AttributeError, IndexError): - return None - - -def _create_future() -> Future: - future = Future() # type: Future - # Fixup asyncio debug info by removing extraneous stack entries - source_traceback = getattr(future, "_source_traceback", ()) - while source_traceback: - # Each traceback entry is equivalent to a - # (filename, self.lineno, self.name, self.line) tuple - filename = source_traceback[-1][0] - if filename == __file__: - del source_traceback[-1] - else: - break - return future - - -def _fake_ctx_run(f: Callable[..., _T], *args: Any, **kw: Any) -> _T: - return f(*args, **kw) - - -@overload -def coroutine( - func: Callable[..., "Generator[Any, Any, _T]"] -) -> Callable[..., "Future[_T]"]: - ... - - -@overload -def coroutine(func: Callable[..., _T]) -> Callable[..., "Future[_T]"]: - ... - - -def coroutine( - func: Union[Callable[..., "Generator[Any, Any, _T]"], Callable[..., _T]] -) -> Callable[..., "Future[_T]"]: - """Decorator for asynchronous generators. - - For compatibility with older versions of Python, coroutines may - also "return" by raising the special exception `Return(value) - <Return>`. - - Functions with this decorator return a `.Future`. - - .. warning:: - - When exceptions occur inside a coroutine, the exception - information will be stored in the `.Future` object. You must - examine the result of the `.Future` object, or the exception - may go unnoticed by your code. This means yielding the function - if called from another coroutine, using something like - `.IOLoop.run_sync` for top-level calls, or passing the `.Future` - to `.IOLoop.add_future`. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - awaitable object instead. - - """ - - @functools.wraps(func) - def wrapper(*args, **kwargs): - # type: (*Any, **Any) -> Future[_T] - # This function is type-annotated with a comment to work around - # https://bitbucket.org/pypy/pypy/issues/2868/segfault-with-args-type-annotation-in - future = _create_future() - if contextvars is not None: - ctx_run = contextvars.copy_context().run # type: Callable - else: - ctx_run = _fake_ctx_run - try: - result = ctx_run(func, *args, **kwargs) - except (Return, StopIteration) as e: - result = _value_from_stopiteration(e) - except Exception: - future_set_exc_info(future, sys.exc_info()) - try: - return future - finally: - # Avoid circular references - future = None # type: ignore - else: - if isinstance(result, Generator): - # Inline the first iteration of Runner.run. This lets us - # avoid the cost of creating a Runner when the coroutine - # never actually yields, which in turn allows us to - # use "optional" coroutines in critical path code without - # performance penalty for the synchronous case. - try: - yielded = ctx_run(next, result) - except (StopIteration, Return) as e: - future_set_result_unless_cancelled( - future, _value_from_stopiteration(e) - ) - except Exception: - future_set_exc_info(future, sys.exc_info()) - else: - # Provide strong references to Runner objects as long - # as their result future objects also have strong - # references (typically from the parent coroutine's - # Runner). This keeps the coroutine's Runner alive. - # We do this by exploiting the public API - # add_done_callback() instead of putting a private - # attribute on the Future. - # (GitHub issues #1769, #2229). - runner = Runner(ctx_run, result, future, yielded) - future.add_done_callback(lambda _: runner) - yielded = None - try: - return future - finally: - # Subtle memory optimization: if next() raised an exception, - # the future's exc_info contains a traceback which - # includes this stack frame. This creates a cycle, - # which will be collected at the next full GC but has - # been shown to greatly increase memory usage of - # benchmarks (relative to the refcount-based scheme - # used in the absence of cycles). We can avoid the - # cycle by clearing the local variable after we return it. - future = None # type: ignore - future_set_result_unless_cancelled(future, result) - return future - - wrapper.__wrapped__ = func # type: ignore - wrapper.__tornado_coroutine__ = True # type: ignore - return wrapper - - -def is_coroutine_function(func: Any) -> bool: - """Return whether *func* is a coroutine function, i.e. a function - wrapped with `~.gen.coroutine`. - - .. versionadded:: 4.5 - """ - return getattr(func, "__tornado_coroutine__", False) - - -class Return(Exception): - """Special exception to return a value from a `coroutine`. - - If this exception is raised, its value argument is used as the - result of the coroutine:: - - @gen.coroutine - def fetch_json(url): - response = yield AsyncHTTPClient().fetch(url) - raise gen.Return(json_decode(response.body)) - - In Python 3.3, this exception is no longer necessary: the ``return`` - statement can be used directly to return a value (previously - ``yield`` and ``return`` with a value could not be combined in the - same function). - - By analogy with the return statement, the value argument is optional, - but it is never necessary to ``raise gen.Return()``. The ``return`` - statement can be used with no arguments instead. - """ - - def __init__(self, value: Any = None) -> None: - super().__init__() - self.value = value - # Cython recognizes subclasses of StopIteration with a .args tuple. - self.args = (value,) - - -class WaitIterator(object): - """Provides an iterator to yield the results of awaitables as they finish. - - Yielding a set of awaitables like this: - - ``results = yield [awaitable1, awaitable2]`` - - pauses the coroutine until both ``awaitable1`` and ``awaitable2`` - return, and then restarts the coroutine with the results of both - awaitables. If either awaitable raises an exception, the - expression will raise that exception and all the results will be - lost. - - If you need to get the result of each awaitable as soon as possible, - or if you need the result of some awaitables even if others produce - errors, you can use ``WaitIterator``:: - - wait_iterator = gen.WaitIterator(awaitable1, awaitable2) - while not wait_iterator.done(): - try: - result = yield wait_iterator.next() - except Exception as e: - print("Error {} from {}".format(e, wait_iterator.current_future)) - else: - print("Result {} received from {} at {}".format( - result, wait_iterator.current_future, - wait_iterator.current_index)) - - Because results are returned as soon as they are available the - output from the iterator *will not be in the same order as the - input arguments*. If you need to know which future produced the - current result, you can use the attributes - ``WaitIterator.current_future``, or ``WaitIterator.current_index`` - to get the index of the awaitable from the input list. (if keyword - arguments were used in the construction of the `WaitIterator`, - ``current_index`` will use the corresponding keyword). - - On Python 3.5, `WaitIterator` implements the async iterator - protocol, so it can be used with the ``async for`` statement (note - that in this version the entire iteration is aborted if any value - raises an exception, while the previous example can continue past - individual errors):: - - async for result in gen.WaitIterator(future1, future2): - print("Result {} received from {} at {}".format( - result, wait_iterator.current_future, - wait_iterator.current_index)) - - .. versionadded:: 4.1 - - .. versionchanged:: 4.3 - Added ``async for`` support in Python 3.5. - - """ - - _unfinished = {} # type: Dict[Future, Union[int, str]] - - def __init__(self, *args: Future, **kwargs: Future) -> None: - if args and kwargs: - raise ValueError("You must provide args or kwargs, not both") - - if kwargs: - self._unfinished = dict((f, k) for (k, f) in kwargs.items()) - futures = list(kwargs.values()) # type: Sequence[Future] - else: - self._unfinished = dict((f, i) for (i, f) in enumerate(args)) - futures = args - - self._finished = collections.deque() # type: Deque[Future] - self.current_index = None # type: Optional[Union[str, int]] - self.current_future = None # type: Optional[Future] - self._running_future = None # type: Optional[Future] - - for future in futures: - future_add_done_callback(future, self._done_callback) - - def done(self) -> bool: - """Returns True if this iterator has no more results.""" - if self._finished or self._unfinished: - return False - # Clear the 'current' values when iteration is done. - self.current_index = self.current_future = None - return True - - def next(self) -> Future: - """Returns a `.Future` that will yield the next available result. - - Note that this `.Future` will not be the same object as any of - the inputs. - """ - self._running_future = Future() - - if self._finished: - self._return_result(self._finished.popleft()) - - return self._running_future - - def _done_callback(self, done: Future) -> None: - if self._running_future and not self._running_future.done(): - self._return_result(done) - else: - self._finished.append(done) - - def _return_result(self, done: Future) -> None: - """Called set the returned future's state that of the future - we yielded, and set the current future for the iterator. - """ - if self._running_future is None: - raise Exception("no future is running") - chain_future(done, self._running_future) - - self.current_future = done - self.current_index = self._unfinished.pop(done) - - def __aiter__(self) -> typing.AsyncIterator: - return self - - def __anext__(self) -> Future: - if self.done(): - # Lookup by name to silence pyflakes on older versions. - raise getattr(builtins, "StopAsyncIteration")() - return self.next() - - -def multi( - children: Union[List[_Yieldable], Dict[Any, _Yieldable]], - quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (), -) -> "Union[Future[List], Future[Dict]]": - """Runs multiple asynchronous operations in parallel. - - ``children`` may either be a list or a dict whose values are - yieldable objects. ``multi()`` returns a new yieldable - object that resolves to a parallel structure containing their - results. If ``children`` is a list, the result is a list of - results in the same order; if it is a dict, the result is a dict - with the same keys. - - That is, ``results = yield multi(list_of_futures)`` is equivalent - to:: - - results = [] - for future in list_of_futures: - results.append(yield future) - - If any children raise exceptions, ``multi()`` will raise the first - one. All others will be logged, unless they are of types - contained in the ``quiet_exceptions`` argument. - - In a ``yield``-based coroutine, it is not normally necessary to - call this function directly, since the coroutine runner will - do it automatically when a list or dict is yielded. However, - it is necessary in ``await``-based coroutines, or to pass - the ``quiet_exceptions`` argument. - - This function is available under the names ``multi()`` and ``Multi()`` - for historical reasons. - - Cancelling a `.Future` returned by ``multi()`` does not cancel its - children. `asyncio.gather` is similar to ``multi()``, but it does - cancel its children. - - .. versionchanged:: 4.2 - If multiple yieldables fail, any exceptions after the first - (which is raised) will be logged. Added the ``quiet_exceptions`` - argument to suppress this logging for selected exception types. - - .. versionchanged:: 4.3 - Replaced the class ``Multi`` and the function ``multi_future`` - with a unified function ``multi``. Added support for yieldables - other than ``YieldPoint`` and `.Future`. - - """ - return multi_future(children, quiet_exceptions=quiet_exceptions) - - -Multi = multi - - -def multi_future( - children: Union[List[_Yieldable], Dict[Any, _Yieldable]], - quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (), -) -> "Union[Future[List], Future[Dict]]": - """Wait for multiple asynchronous futures in parallel. - - Since Tornado 6.0, this function is exactly the same as `multi`. - - .. versionadded:: 4.0 - - .. versionchanged:: 4.2 - If multiple ``Futures`` fail, any exceptions after the first (which is - raised) will be logged. Added the ``quiet_exceptions`` - argument to suppress this logging for selected exception types. - - .. deprecated:: 4.3 - Use `multi` instead. - """ - if isinstance(children, dict): - keys = list(children.keys()) # type: Optional[List] - children_seq = children.values() # type: Iterable - else: - keys = None - children_seq = children - children_futs = list(map(convert_yielded, children_seq)) - assert all(is_future(i) or isinstance(i, _NullFuture) for i in children_futs) - unfinished_children = set(children_futs) - - future = _create_future() - if not children_futs: - future_set_result_unless_cancelled(future, {} if keys is not None else []) - - def callback(fut: Future) -> None: - unfinished_children.remove(fut) - if not unfinished_children: - result_list = [] - for f in children_futs: - try: - result_list.append(f.result()) - except Exception as e: - if future.done(): - if not isinstance(e, quiet_exceptions): - app_log.error( - "Multiple exceptions in yield list", exc_info=True - ) - else: - future_set_exc_info(future, sys.exc_info()) - if not future.done(): - if keys is not None: - future_set_result_unless_cancelled( - future, dict(zip(keys, result_list)) - ) - else: - future_set_result_unless_cancelled(future, result_list) - - listening = set() # type: Set[Future] - for f in children_futs: - if f not in listening: - listening.add(f) - future_add_done_callback(f, callback) - return future - - -def maybe_future(x: Any) -> Future: - """Converts ``x`` into a `.Future`. - - If ``x`` is already a `.Future`, it is simply returned; otherwise - it is wrapped in a new `.Future`. This is suitable for use as - ``result = yield gen.maybe_future(f())`` when you don't know whether - ``f()`` returns a `.Future` or not. - - .. deprecated:: 4.3 - This function only handles ``Futures``, not other yieldable objects. - Instead of `maybe_future`, check for the non-future result types - you expect (often just ``None``), and ``yield`` anything unknown. - """ - if is_future(x): - return x - else: - fut = _create_future() - fut.set_result(x) - return fut - - -def with_timeout( - timeout: Union[float, datetime.timedelta], - future: _Yieldable, - quiet_exceptions: "Union[Type[Exception], Tuple[Type[Exception], ...]]" = (), -) -> Future: - """Wraps a `.Future` (or other yieldable object) in a timeout. - - Raises `tornado.util.TimeoutError` if the input future does not - complete before ``timeout``, which may be specified in any form - allowed by `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or - an absolute time relative to `.IOLoop.time`) - - If the wrapped `.Future` fails after it has timed out, the exception - will be logged unless it is either of a type contained in - ``quiet_exceptions`` (which may be an exception type or a sequence of - types), or an ``asyncio.CancelledError``. - - The wrapped `.Future` is not canceled when the timeout expires, - permitting it to be reused. `asyncio.wait_for` is similar to this - function but it does cancel the wrapped `.Future` on timeout. - - .. versionadded:: 4.0 - - .. versionchanged:: 4.1 - Added the ``quiet_exceptions`` argument and the logging of unhandled - exceptions. - - .. versionchanged:: 4.4 - Added support for yieldable objects other than `.Future`. - - .. versionchanged:: 6.0.3 - ``asyncio.CancelledError`` is now always considered "quiet". - - """ - # It's tempting to optimize this by cancelling the input future on timeout - # instead of creating a new one, but A) we can't know if we are the only - # one waiting on the input future, so cancelling it might disrupt other - # callers and B) concurrent futures can only be cancelled while they are - # in the queue, so cancellation cannot reliably bound our waiting time. - future_converted = convert_yielded(future) - result = _create_future() - chain_future(future_converted, result) - io_loop = IOLoop.current() - - def error_callback(future: Future) -> None: - try: - future.result() - except asyncio.CancelledError: - pass - except Exception as e: - if not isinstance(e, quiet_exceptions): - app_log.error( - "Exception in Future %r after timeout", future, exc_info=True - ) - - def timeout_callback() -> None: - if not result.done(): - result.set_exception(TimeoutError("Timeout")) - # In case the wrapped future goes on to fail, log it. - future_add_done_callback(future_converted, error_callback) - - timeout_handle = io_loop.add_timeout(timeout, timeout_callback) - if isinstance(future_converted, Future): - # We know this future will resolve on the IOLoop, so we don't - # need the extra thread-safety of IOLoop.add_future (and we also - # don't care about StackContext here. - future_add_done_callback( - future_converted, lambda future: io_loop.remove_timeout(timeout_handle) - ) - else: - # concurrent.futures.Futures may resolve on any thread, so we - # need to route them back to the IOLoop. - io_loop.add_future( - future_converted, lambda future: io_loop.remove_timeout(timeout_handle) - ) - return result - - -def sleep(duration: float) -> "Future[None]": - """Return a `.Future` that resolves after the given number of seconds. - - When used with ``yield`` in a coroutine, this is a non-blocking - analogue to `time.sleep` (which should not be used in coroutines - because it is blocking):: - - yield gen.sleep(0.5) - - Note that calling this function on its own does nothing; you must - wait on the `.Future` it returns (usually by yielding it). - - .. versionadded:: 4.1 - """ - f = _create_future() - IOLoop.current().call_later( - duration, lambda: future_set_result_unless_cancelled(f, None) - ) - return f - - -class _NullFuture(object): - """_NullFuture resembles a Future that finished with a result of None. - - It's not actually a `Future` to avoid depending on a particular event loop. - Handled as a special case in the coroutine runner. - - We lie and tell the type checker that a _NullFuture is a Future so - we don't have to leak _NullFuture into lots of public APIs. But - this means that the type checker can't warn us when we're passing - a _NullFuture into a code path that doesn't understand what to do - with it. - """ - - def result(self) -> None: - return None - - def done(self) -> bool: - return True - - -# _null_future is used as a dummy value in the coroutine runner. It differs -# from moment in that moment always adds a delay of one IOLoop iteration -# while _null_future is processed as soon as possible. -_null_future = typing.cast(Future, _NullFuture()) - -moment = typing.cast(Future, _NullFuture()) -moment.__doc__ = """A special object which may be yielded to allow the IOLoop to run for -one iteration. - -This is not needed in normal use but it can be helpful in long-running -coroutines that are likely to yield Futures that are ready instantly. - -Usage: ``yield gen.moment`` - -In native coroutines, the equivalent of ``yield gen.moment`` is -``await asyncio.sleep(0)``. - -.. versionadded:: 4.0 - -.. deprecated:: 4.5 - ``yield None`` (or ``yield`` with no argument) is now equivalent to - ``yield gen.moment``. -""" - - -class Runner(object): - """Internal implementation of `tornado.gen.coroutine`. - - Maintains information about pending callbacks and their results. - - The results of the generator are stored in ``result_future`` (a - `.Future`) - """ - - def __init__( - self, - ctx_run: Callable, - gen: "Generator[_Yieldable, Any, _T]", - result_future: "Future[_T]", - first_yielded: _Yieldable, - ) -> None: - self.ctx_run = ctx_run - self.gen = gen - self.result_future = result_future - self.future = _null_future # type: Union[None, Future] - self.running = False - self.finished = False - self.io_loop = IOLoop.current() - if self.handle_yield(first_yielded): - gen = result_future = first_yielded = None # type: ignore - self.ctx_run(self.run) - - def run(self) -> None: - """Starts or resumes the generator, running until it reaches a - yield point that is not ready. - """ - if self.running or self.finished: - return - try: - self.running = True - while True: - future = self.future - if future is None: - raise Exception("No pending future") - if not future.done(): - return - self.future = None - try: - exc_info = None - - try: - value = future.result() - except Exception: - exc_info = sys.exc_info() - future = None - - if exc_info is not None: - try: - yielded = self.gen.throw(*exc_info) # type: ignore - finally: - # Break up a reference to itself - # for faster GC on CPython. - exc_info = None - else: - yielded = self.gen.send(value) - - except (StopIteration, Return) as e: - self.finished = True - self.future = _null_future - future_set_result_unless_cancelled( - self.result_future, _value_from_stopiteration(e) - ) - self.result_future = None # type: ignore - return - except Exception: - self.finished = True - self.future = _null_future - future_set_exc_info(self.result_future, sys.exc_info()) - self.result_future = None # type: ignore - return - if not self.handle_yield(yielded): - return - yielded = None - finally: - self.running = False - - def handle_yield(self, yielded: _Yieldable) -> bool: - try: - self.future = convert_yielded(yielded) - except BadYieldError: - self.future = Future() - future_set_exc_info(self.future, sys.exc_info()) - - if self.future is moment: - self.io_loop.add_callback(self.ctx_run, self.run) - return False - elif self.future is None: - raise Exception("no pending future") - elif not self.future.done(): - - def inner(f: Any) -> None: - # Break a reference cycle to speed GC. - f = None # noqa: F841 - self.ctx_run(self.run) - - self.io_loop.add_future(self.future, inner) - return False - return True - - def handle_exception( - self, typ: Type[Exception], value: Exception, tb: types.TracebackType - ) -> bool: - if not self.running and not self.finished: - self.future = Future() - future_set_exc_info(self.future, (typ, value, tb)) - self.ctx_run(self.run) - return True - else: - return False - - -# Convert Awaitables into Futures. -try: - _wrap_awaitable = asyncio.ensure_future -except AttributeError: - # asyncio.ensure_future was introduced in Python 3.4.4, but - # Debian jessie still ships with 3.4.2 so try the old name. - _wrap_awaitable = getattr(asyncio, "async") - - -def convert_yielded(yielded: _Yieldable) -> Future: - """Convert a yielded object into a `.Future`. - - The default implementation accepts lists, dictionaries, and - Futures. This has the side effect of starting any coroutines that - did not start themselves, similar to `asyncio.ensure_future`. - - If the `~functools.singledispatch` library is available, this function - may be extended to support additional types. For example:: - - @convert_yielded.register(asyncio.Future) - def _(asyncio_future): - return tornado.platform.asyncio.to_tornado_future(asyncio_future) - - .. versionadded:: 4.1 - - """ - if yielded is None or yielded is moment: - return moment - elif yielded is _null_future: - return _null_future - elif isinstance(yielded, (list, dict)): - return multi(yielded) # type: ignore - elif is_future(yielded): - return typing.cast(Future, yielded) - elif isawaitable(yielded): - return _wrap_awaitable(yielded) # type: ignore - else: - raise BadYieldError("yielded unknown object %r" % (yielded,)) - - -convert_yielded = singledispatch(convert_yielded) diff --git a/telegramer/include/tornado/http1connection.py b/telegramer/include/tornado/http1connection.py deleted file mode 100644 index 835027b..0000000 --- a/telegramer/include/tornado/http1connection.py +++ /dev/null @@ -1,842 +0,0 @@ -# -# Copyright 2014 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Client and server implementations of HTTP/1.x. - -.. versionadded:: 4.0 -""" - -import asyncio -import logging -import re -import types - -from tornado.concurrent import ( - Future, - future_add_done_callback, - future_set_result_unless_cancelled, -) -from tornado.escape import native_str, utf8 -from tornado import gen -from tornado import httputil -from tornado import iostream -from tornado.log import gen_log, app_log -from tornado.util import GzipDecompressor - - -from typing import cast, Optional, Type, Awaitable, Callable, Union, Tuple - - -class _QuietException(Exception): - def __init__(self) -> None: - pass - - -class _ExceptionLoggingContext(object): - """Used with the ``with`` statement when calling delegate methods to - log any exceptions with the given logger. Any exceptions caught are - converted to _QuietException - """ - - def __init__(self, logger: logging.Logger) -> None: - self.logger = logger - - def __enter__(self) -> None: - pass - - def __exit__( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: types.TracebackType, - ) -> None: - if value is not None: - assert typ is not None - self.logger.error("Uncaught exception", exc_info=(typ, value, tb)) - raise _QuietException - - -class HTTP1ConnectionParameters(object): - """Parameters for `.HTTP1Connection` and `.HTTP1ServerConnection`. - """ - - def __init__( - self, - no_keep_alive: bool = False, - chunk_size: Optional[int] = None, - max_header_size: Optional[int] = None, - header_timeout: Optional[float] = None, - max_body_size: Optional[int] = None, - body_timeout: Optional[float] = None, - decompress: bool = False, - ) -> None: - """ - :arg bool no_keep_alive: If true, always close the connection after - one request. - :arg int chunk_size: how much data to read into memory at once - :arg int max_header_size: maximum amount of data for HTTP headers - :arg float header_timeout: how long to wait for all headers (seconds) - :arg int max_body_size: maximum amount of data for body - :arg float body_timeout: how long to wait while reading body (seconds) - :arg bool decompress: if true, decode incoming - ``Content-Encoding: gzip`` - """ - self.no_keep_alive = no_keep_alive - self.chunk_size = chunk_size or 65536 - self.max_header_size = max_header_size or 65536 - self.header_timeout = header_timeout - self.max_body_size = max_body_size - self.body_timeout = body_timeout - self.decompress = decompress - - -class HTTP1Connection(httputil.HTTPConnection): - """Implements the HTTP/1.x protocol. - - This class can be on its own for clients, or via `HTTP1ServerConnection` - for servers. - """ - - def __init__( - self, - stream: iostream.IOStream, - is_client: bool, - params: Optional[HTTP1ConnectionParameters] = None, - context: Optional[object] = None, - ) -> None: - """ - :arg stream: an `.IOStream` - :arg bool is_client: client or server - :arg params: a `.HTTP1ConnectionParameters` instance or ``None`` - :arg context: an opaque application-defined object that can be accessed - as ``connection.context``. - """ - self.is_client = is_client - self.stream = stream - if params is None: - params = HTTP1ConnectionParameters() - self.params = params - self.context = context - self.no_keep_alive = params.no_keep_alive - # The body limits can be altered by the delegate, so save them - # here instead of just referencing self.params later. - self._max_body_size = self.params.max_body_size or self.stream.max_buffer_size - self._body_timeout = self.params.body_timeout - # _write_finished is set to True when finish() has been called, - # i.e. there will be no more data sent. Data may still be in the - # stream's write buffer. - self._write_finished = False - # True when we have read the entire incoming body. - self._read_finished = False - # _finish_future resolves when all data has been written and flushed - # to the IOStream. - self._finish_future = Future() # type: Future[None] - # If true, the connection should be closed after this request - # (after the response has been written in the server side, - # and after it has been read in the client) - self._disconnect_on_finish = False - self._clear_callbacks() - # Save the start lines after we read or write them; they - # affect later processing (e.g. 304 responses and HEAD methods - # have content-length but no bodies) - self._request_start_line = None # type: Optional[httputil.RequestStartLine] - self._response_start_line = None # type: Optional[httputil.ResponseStartLine] - self._request_headers = None # type: Optional[httputil.HTTPHeaders] - # True if we are writing output with chunked encoding. - self._chunking_output = False - # While reading a body with a content-length, this is the - # amount left to read. - self._expected_content_remaining = None # type: Optional[int] - # A Future for our outgoing writes, returned by IOStream.write. - self._pending_write = None # type: Optional[Future[None]] - - def read_response(self, delegate: httputil.HTTPMessageDelegate) -> Awaitable[bool]: - """Read a single HTTP response. - - Typical client-mode usage is to write a request using `write_headers`, - `write`, and `finish`, and then call ``read_response``. - - :arg delegate: a `.HTTPMessageDelegate` - - Returns a `.Future` that resolves to a bool after the full response has - been read. The result is true if the stream is still open. - """ - if self.params.decompress: - delegate = _GzipMessageDelegate(delegate, self.params.chunk_size) - return self._read_message(delegate) - - async def _read_message(self, delegate: httputil.HTTPMessageDelegate) -> bool: - need_delegate_close = False - try: - header_future = self.stream.read_until_regex( - b"\r?\n\r?\n", max_bytes=self.params.max_header_size - ) - if self.params.header_timeout is None: - header_data = await header_future - else: - try: - header_data = await gen.with_timeout( - self.stream.io_loop.time() + self.params.header_timeout, - header_future, - quiet_exceptions=iostream.StreamClosedError, - ) - except gen.TimeoutError: - self.close() - return False - start_line_str, headers = self._parse_headers(header_data) - if self.is_client: - resp_start_line = httputil.parse_response_start_line(start_line_str) - self._response_start_line = resp_start_line - start_line = ( - resp_start_line - ) # type: Union[httputil.RequestStartLine, httputil.ResponseStartLine] - # TODO: this will need to change to support client-side keepalive - self._disconnect_on_finish = False - else: - req_start_line = httputil.parse_request_start_line(start_line_str) - self._request_start_line = req_start_line - self._request_headers = headers - start_line = req_start_line - self._disconnect_on_finish = not self._can_keep_alive( - req_start_line, headers - ) - need_delegate_close = True - with _ExceptionLoggingContext(app_log): - header_recv_future = delegate.headers_received(start_line, headers) - if header_recv_future is not None: - await header_recv_future - if self.stream is None: - # We've been detached. - need_delegate_close = False - return False - skip_body = False - if self.is_client: - assert isinstance(start_line, httputil.ResponseStartLine) - if ( - self._request_start_line is not None - and self._request_start_line.method == "HEAD" - ): - skip_body = True - code = start_line.code - if code == 304: - # 304 responses may include the content-length header - # but do not actually have a body. - # http://tools.ietf.org/html/rfc7230#section-3.3 - skip_body = True - if 100 <= code < 200: - # 1xx responses should never indicate the presence of - # a body. - if "Content-Length" in headers or "Transfer-Encoding" in headers: - raise httputil.HTTPInputError( - "Response code %d cannot have body" % code - ) - # TODO: client delegates will get headers_received twice - # in the case of a 100-continue. Document or change? - await self._read_message(delegate) - else: - if headers.get("Expect") == "100-continue" and not self._write_finished: - self.stream.write(b"HTTP/1.1 100 (Continue)\r\n\r\n") - if not skip_body: - body_future = self._read_body( - resp_start_line.code if self.is_client else 0, headers, delegate - ) - if body_future is not None: - if self._body_timeout is None: - await body_future - else: - try: - await gen.with_timeout( - self.stream.io_loop.time() + self._body_timeout, - body_future, - quiet_exceptions=iostream.StreamClosedError, - ) - except gen.TimeoutError: - gen_log.info("Timeout reading body from %s", self.context) - self.stream.close() - return False - self._read_finished = True - if not self._write_finished or self.is_client: - need_delegate_close = False - with _ExceptionLoggingContext(app_log): - delegate.finish() - # If we're waiting for the application to produce an asynchronous - # response, and we're not detached, register a close callback - # on the stream (we didn't need one while we were reading) - if ( - not self._finish_future.done() - and self.stream is not None - and not self.stream.closed() - ): - self.stream.set_close_callback(self._on_connection_close) - await self._finish_future - if self.is_client and self._disconnect_on_finish: - self.close() - if self.stream is None: - return False - except httputil.HTTPInputError as e: - gen_log.info("Malformed HTTP message from %s: %s", self.context, e) - if not self.is_client: - await self.stream.write(b"HTTP/1.1 400 Bad Request\r\n\r\n") - self.close() - return False - finally: - if need_delegate_close: - with _ExceptionLoggingContext(app_log): - delegate.on_connection_close() - header_future = None # type: ignore - self._clear_callbacks() - return True - - def _clear_callbacks(self) -> None: - """Clears the callback attributes. - - This allows the request handler to be garbage collected more - quickly in CPython by breaking up reference cycles. - """ - self._write_callback = None - self._write_future = None # type: Optional[Future[None]] - self._close_callback = None # type: Optional[Callable[[], None]] - if self.stream is not None: - self.stream.set_close_callback(None) - - def set_close_callback(self, callback: Optional[Callable[[], None]]) -> None: - """Sets a callback that will be run when the connection is closed. - - Note that this callback is slightly different from - `.HTTPMessageDelegate.on_connection_close`: The - `.HTTPMessageDelegate` method is called when the connection is - closed while receiving a message. This callback is used when - there is not an active delegate (for example, on the server - side this callback is used if the client closes the connection - after sending its request but before receiving all the - response. - """ - self._close_callback = callback - - def _on_connection_close(self) -> None: - # Note that this callback is only registered on the IOStream - # when we have finished reading the request and are waiting for - # the application to produce its response. - if self._close_callback is not None: - callback = self._close_callback - self._close_callback = None - callback() - if not self._finish_future.done(): - future_set_result_unless_cancelled(self._finish_future, None) - self._clear_callbacks() - - def close(self) -> None: - if self.stream is not None: - self.stream.close() - self._clear_callbacks() - if not self._finish_future.done(): - future_set_result_unless_cancelled(self._finish_future, None) - - def detach(self) -> iostream.IOStream: - """Take control of the underlying stream. - - Returns the underlying `.IOStream` object and stops all further - HTTP processing. May only be called during - `.HTTPMessageDelegate.headers_received`. Intended for implementing - protocols like websockets that tunnel over an HTTP handshake. - """ - self._clear_callbacks() - stream = self.stream - self.stream = None # type: ignore - if not self._finish_future.done(): - future_set_result_unless_cancelled(self._finish_future, None) - return stream - - def set_body_timeout(self, timeout: float) -> None: - """Sets the body timeout for a single request. - - Overrides the value from `.HTTP1ConnectionParameters`. - """ - self._body_timeout = timeout - - def set_max_body_size(self, max_body_size: int) -> None: - """Sets the body size limit for a single request. - - Overrides the value from `.HTTP1ConnectionParameters`. - """ - self._max_body_size = max_body_size - - def write_headers( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - chunk: Optional[bytes] = None, - ) -> "Future[None]": - """Implements `.HTTPConnection.write_headers`.""" - lines = [] - if self.is_client: - assert isinstance(start_line, httputil.RequestStartLine) - self._request_start_line = start_line - lines.append(utf8("%s %s HTTP/1.1" % (start_line[0], start_line[1]))) - # Client requests with a non-empty body must have either a - # Content-Length or a Transfer-Encoding. - self._chunking_output = ( - start_line.method in ("POST", "PUT", "PATCH") - and "Content-Length" not in headers - and ( - "Transfer-Encoding" not in headers - or headers["Transfer-Encoding"] == "chunked" - ) - ) - else: - assert isinstance(start_line, httputil.ResponseStartLine) - assert self._request_start_line is not None - assert self._request_headers is not None - self._response_start_line = start_line - lines.append(utf8("HTTP/1.1 %d %s" % (start_line[1], start_line[2]))) - self._chunking_output = ( - # TODO: should this use - # self._request_start_line.version or - # start_line.version? - self._request_start_line.version == "HTTP/1.1" - # Omit payload header field for HEAD request. - and self._request_start_line.method != "HEAD" - # 1xx, 204 and 304 responses have no body (not even a zero-length - # body), and so should not have either Content-Length or - # Transfer-Encoding headers. - and start_line.code not in (204, 304) - and (start_line.code < 100 or start_line.code >= 200) - # No need to chunk the output if a Content-Length is specified. - and "Content-Length" not in headers - # Applications are discouraged from touching Transfer-Encoding, - # but if they do, leave it alone. - and "Transfer-Encoding" not in headers - ) - # If connection to a 1.1 client will be closed, inform client - if ( - self._request_start_line.version == "HTTP/1.1" - and self._disconnect_on_finish - ): - headers["Connection"] = "close" - # If a 1.0 client asked for keep-alive, add the header. - if ( - self._request_start_line.version == "HTTP/1.0" - and self._request_headers.get("Connection", "").lower() == "keep-alive" - ): - headers["Connection"] = "Keep-Alive" - if self._chunking_output: - headers["Transfer-Encoding"] = "chunked" - if not self.is_client and ( - self._request_start_line.method == "HEAD" - or cast(httputil.ResponseStartLine, start_line).code == 304 - ): - self._expected_content_remaining = 0 - elif "Content-Length" in headers: - self._expected_content_remaining = int(headers["Content-Length"]) - else: - self._expected_content_remaining = None - # TODO: headers are supposed to be of type str, but we still have some - # cases that let bytes slip through. Remove these native_str calls when those - # are fixed. - header_lines = ( - native_str(n) + ": " + native_str(v) for n, v in headers.get_all() - ) - lines.extend(line.encode("latin1") for line in header_lines) - for line in lines: - if b"\n" in line: - raise ValueError("Newline in header: " + repr(line)) - future = None - if self.stream.closed(): - future = self._write_future = Future() - future.set_exception(iostream.StreamClosedError()) - future.exception() - else: - future = self._write_future = Future() - data = b"\r\n".join(lines) + b"\r\n\r\n" - if chunk: - data += self._format_chunk(chunk) - self._pending_write = self.stream.write(data) - future_add_done_callback(self._pending_write, self._on_write_complete) - return future - - def _format_chunk(self, chunk: bytes) -> bytes: - if self._expected_content_remaining is not None: - self._expected_content_remaining -= len(chunk) - if self._expected_content_remaining < 0: - # Close the stream now to stop further framing errors. - self.stream.close() - raise httputil.HTTPOutputError( - "Tried to write more data than Content-Length" - ) - if self._chunking_output and chunk: - # Don't write out empty chunks because that means END-OF-STREAM - # with chunked encoding - return utf8("%x" % len(chunk)) + b"\r\n" + chunk + b"\r\n" - else: - return chunk - - def write(self, chunk: bytes) -> "Future[None]": - """Implements `.HTTPConnection.write`. - - For backwards compatibility it is allowed but deprecated to - skip `write_headers` and instead call `write()` with a - pre-encoded header block. - """ - future = None - if self.stream.closed(): - future = self._write_future = Future() - self._write_future.set_exception(iostream.StreamClosedError()) - self._write_future.exception() - else: - future = self._write_future = Future() - self._pending_write = self.stream.write(self._format_chunk(chunk)) - future_add_done_callback(self._pending_write, self._on_write_complete) - return future - - def finish(self) -> None: - """Implements `.HTTPConnection.finish`.""" - if ( - self._expected_content_remaining is not None - and self._expected_content_remaining != 0 - and not self.stream.closed() - ): - self.stream.close() - raise httputil.HTTPOutputError( - "Tried to write %d bytes less than Content-Length" - % self._expected_content_remaining - ) - if self._chunking_output: - if not self.stream.closed(): - self._pending_write = self.stream.write(b"0\r\n\r\n") - self._pending_write.add_done_callback(self._on_write_complete) - self._write_finished = True - # If the app finished the request while we're still reading, - # divert any remaining data away from the delegate and - # close the connection when we're done sending our response. - # Closing the connection is the only way to avoid reading the - # whole input body. - if not self._read_finished: - self._disconnect_on_finish = True - # No more data is coming, so instruct TCP to send any remaining - # data immediately instead of waiting for a full packet or ack. - self.stream.set_nodelay(True) - if self._pending_write is None: - self._finish_request(None) - else: - future_add_done_callback(self._pending_write, self._finish_request) - - def _on_write_complete(self, future: "Future[None]") -> None: - exc = future.exception() - if exc is not None and not isinstance(exc, iostream.StreamClosedError): - future.result() - if self._write_callback is not None: - callback = self._write_callback - self._write_callback = None - self.stream.io_loop.add_callback(callback) - if self._write_future is not None: - future = self._write_future - self._write_future = None - future_set_result_unless_cancelled(future, None) - - def _can_keep_alive( - self, start_line: httputil.RequestStartLine, headers: httputil.HTTPHeaders - ) -> bool: - if self.params.no_keep_alive: - return False - connection_header = headers.get("Connection") - if connection_header is not None: - connection_header = connection_header.lower() - if start_line.version == "HTTP/1.1": - return connection_header != "close" - elif ( - "Content-Length" in headers - or headers.get("Transfer-Encoding", "").lower() == "chunked" - or getattr(start_line, "method", None) in ("HEAD", "GET") - ): - # start_line may be a request or response start line; only - # the former has a method attribute. - return connection_header == "keep-alive" - return False - - def _finish_request(self, future: "Optional[Future[None]]") -> None: - self._clear_callbacks() - if not self.is_client and self._disconnect_on_finish: - self.close() - return - # Turn Nagle's algorithm back on, leaving the stream in its - # default state for the next request. - self.stream.set_nodelay(False) - if not self._finish_future.done(): - future_set_result_unless_cancelled(self._finish_future, None) - - def _parse_headers(self, data: bytes) -> Tuple[str, httputil.HTTPHeaders]: - # The lstrip removes newlines that some implementations sometimes - # insert between messages of a reused connection. Per RFC 7230, - # we SHOULD ignore at least one empty line before the request. - # http://tools.ietf.org/html/rfc7230#section-3.5 - data_str = native_str(data.decode("latin1")).lstrip("\r\n") - # RFC 7230 section allows for both CRLF and bare LF. - eol = data_str.find("\n") - start_line = data_str[:eol].rstrip("\r") - headers = httputil.HTTPHeaders.parse(data_str[eol:]) - return start_line, headers - - def _read_body( - self, - code: int, - headers: httputil.HTTPHeaders, - delegate: httputil.HTTPMessageDelegate, - ) -> Optional[Awaitable[None]]: - if "Content-Length" in headers: - if "Transfer-Encoding" in headers: - # Response cannot contain both Content-Length and - # Transfer-Encoding headers. - # http://tools.ietf.org/html/rfc7230#section-3.3.3 - raise httputil.HTTPInputError( - "Response with both Transfer-Encoding and Content-Length" - ) - if "," in headers["Content-Length"]: - # Proxies sometimes cause Content-Length headers to get - # duplicated. If all the values are identical then we can - # use them but if they differ it's an error. - pieces = re.split(r",\s*", headers["Content-Length"]) - if any(i != pieces[0] for i in pieces): - raise httputil.HTTPInputError( - "Multiple unequal Content-Lengths: %r" - % headers["Content-Length"] - ) - headers["Content-Length"] = pieces[0] - - try: - content_length = int(headers["Content-Length"]) # type: Optional[int] - except ValueError: - # Handles non-integer Content-Length value. - raise httputil.HTTPInputError( - "Only integer Content-Length is allowed: %s" - % headers["Content-Length"] - ) - - if cast(int, content_length) > self._max_body_size: - raise httputil.HTTPInputError("Content-Length too long") - else: - content_length = None - - if code == 204: - # This response code is not allowed to have a non-empty body, - # and has an implicit length of zero instead of read-until-close. - # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 - if "Transfer-Encoding" in headers or content_length not in (None, 0): - raise httputil.HTTPInputError( - "Response with code %d should not have body" % code - ) - content_length = 0 - - if content_length is not None: - return self._read_fixed_body(content_length, delegate) - if headers.get("Transfer-Encoding", "").lower() == "chunked": - return self._read_chunked_body(delegate) - if self.is_client: - return self._read_body_until_close(delegate) - return None - - async def _read_fixed_body( - self, content_length: int, delegate: httputil.HTTPMessageDelegate - ) -> None: - while content_length > 0: - body = await self.stream.read_bytes( - min(self.params.chunk_size, content_length), partial=True - ) - content_length -= len(body) - if not self._write_finished or self.is_client: - with _ExceptionLoggingContext(app_log): - ret = delegate.data_received(body) - if ret is not None: - await ret - - async def _read_chunked_body(self, delegate: httputil.HTTPMessageDelegate) -> None: - # TODO: "chunk extensions" http://tools.ietf.org/html/rfc2616#section-3.6.1 - total_size = 0 - while True: - chunk_len_str = await self.stream.read_until(b"\r\n", max_bytes=64) - chunk_len = int(chunk_len_str.strip(), 16) - if chunk_len == 0: - crlf = await self.stream.read_bytes(2) - if crlf != b"\r\n": - raise httputil.HTTPInputError( - "improperly terminated chunked request" - ) - return - total_size += chunk_len - if total_size > self._max_body_size: - raise httputil.HTTPInputError("chunked body too large") - bytes_to_read = chunk_len - while bytes_to_read: - chunk = await self.stream.read_bytes( - min(bytes_to_read, self.params.chunk_size), partial=True - ) - bytes_to_read -= len(chunk) - if not self._write_finished or self.is_client: - with _ExceptionLoggingContext(app_log): - ret = delegate.data_received(chunk) - if ret is not None: - await ret - # chunk ends with \r\n - crlf = await self.stream.read_bytes(2) - assert crlf == b"\r\n" - - async def _read_body_until_close( - self, delegate: httputil.HTTPMessageDelegate - ) -> None: - body = await self.stream.read_until_close() - if not self._write_finished or self.is_client: - with _ExceptionLoggingContext(app_log): - ret = delegate.data_received(body) - if ret is not None: - await ret - - -class _GzipMessageDelegate(httputil.HTTPMessageDelegate): - """Wraps an `HTTPMessageDelegate` to decode ``Content-Encoding: gzip``. - """ - - def __init__(self, delegate: httputil.HTTPMessageDelegate, chunk_size: int) -> None: - self._delegate = delegate - self._chunk_size = chunk_size - self._decompressor = None # type: Optional[GzipDecompressor] - - def headers_received( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - ) -> Optional[Awaitable[None]]: - if headers.get("Content-Encoding") == "gzip": - self._decompressor = GzipDecompressor() - # Downstream delegates will only see uncompressed data, - # so rename the content-encoding header. - # (but note that curl_httpclient doesn't do this). - headers.add("X-Consumed-Content-Encoding", headers["Content-Encoding"]) - del headers["Content-Encoding"] - return self._delegate.headers_received(start_line, headers) - - async def data_received(self, chunk: bytes) -> None: - if self._decompressor: - compressed_data = chunk - while compressed_data: - decompressed = self._decompressor.decompress( - compressed_data, self._chunk_size - ) - if decompressed: - ret = self._delegate.data_received(decompressed) - if ret is not None: - await ret - compressed_data = self._decompressor.unconsumed_tail - if compressed_data and not decompressed: - raise httputil.HTTPInputError( - "encountered unconsumed gzip data without making progress" - ) - else: - ret = self._delegate.data_received(chunk) - if ret is not None: - await ret - - def finish(self) -> None: - if self._decompressor is not None: - tail = self._decompressor.flush() - if tail: - # The tail should always be empty: decompress returned - # all that it can in data_received and the only - # purpose of the flush call is to detect errors such - # as truncated input. If we did legitimately get a new - # chunk at this point we'd need to change the - # interface to make finish() a coroutine. - raise ValueError( - "decompressor.flush returned data; possible truncated input" - ) - return self._delegate.finish() - - def on_connection_close(self) -> None: - return self._delegate.on_connection_close() - - -class HTTP1ServerConnection(object): - """An HTTP/1.x server.""" - - def __init__( - self, - stream: iostream.IOStream, - params: Optional[HTTP1ConnectionParameters] = None, - context: Optional[object] = None, - ) -> None: - """ - :arg stream: an `.IOStream` - :arg params: a `.HTTP1ConnectionParameters` or None - :arg context: an opaque application-defined object that is accessible - as ``connection.context`` - """ - self.stream = stream - if params is None: - params = HTTP1ConnectionParameters() - self.params = params - self.context = context - self._serving_future = None # type: Optional[Future[None]] - - async def close(self) -> None: - """Closes the connection. - - Returns a `.Future` that resolves after the serving loop has exited. - """ - self.stream.close() - # Block until the serving loop is done, but ignore any exceptions - # (start_serving is already responsible for logging them). - assert self._serving_future is not None - try: - await self._serving_future - except Exception: - pass - - def start_serving(self, delegate: httputil.HTTPServerConnectionDelegate) -> None: - """Starts serving requests on this connection. - - :arg delegate: a `.HTTPServerConnectionDelegate` - """ - assert isinstance(delegate, httputil.HTTPServerConnectionDelegate) - fut = gen.convert_yielded(self._server_request_loop(delegate)) - self._serving_future = fut - # Register the future on the IOLoop so its errors get logged. - self.stream.io_loop.add_future(fut, lambda f: f.result()) - - async def _server_request_loop( - self, delegate: httputil.HTTPServerConnectionDelegate - ) -> None: - try: - while True: - conn = HTTP1Connection(self.stream, False, self.params, self.context) - request_delegate = delegate.start_request(self, conn) - try: - ret = await conn.read_response(request_delegate) - except ( - iostream.StreamClosedError, - iostream.UnsatisfiableReadError, - asyncio.CancelledError, - ): - return - except _QuietException: - # This exception was already logged. - conn.close() - return - except Exception: - gen_log.error("Uncaught exception", exc_info=True) - conn.close() - return - if not ret: - return - await asyncio.sleep(0) - finally: - delegate.on_close(self) diff --git a/telegramer/include/tornado/httpclient.py b/telegramer/include/tornado/httpclient.py deleted file mode 100644 index 3011c37..0000000 --- a/telegramer/include/tornado/httpclient.py +++ /dev/null @@ -1,790 +0,0 @@ -"""Blocking and non-blocking HTTP client interfaces. - -This module defines a common interface shared by two implementations, -``simple_httpclient`` and ``curl_httpclient``. Applications may either -instantiate their chosen implementation class directly or use the -`AsyncHTTPClient` class from this module, which selects an implementation -that can be overridden with the `AsyncHTTPClient.configure` method. - -The default implementation is ``simple_httpclient``, and this is expected -to be suitable for most users' needs. However, some applications may wish -to switch to ``curl_httpclient`` for reasons such as the following: - -* ``curl_httpclient`` has some features not found in ``simple_httpclient``, - including support for HTTP proxies and the ability to use a specified - network interface. - -* ``curl_httpclient`` is more likely to be compatible with sites that are - not-quite-compliant with the HTTP spec, or sites that use little-exercised - features of HTTP. - -* ``curl_httpclient`` is faster. - -Note that if you are using ``curl_httpclient``, it is highly -recommended that you use a recent version of ``libcurl`` and -``pycurl``. Currently the minimum supported version of libcurl is -7.22.0, and the minimum version of pycurl is 7.18.2. It is highly -recommended that your ``libcurl`` installation is built with -asynchronous DNS resolver (threaded or c-ares), otherwise you may -encounter various problems with request timeouts (for more -information, see -http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTCONNECTTIMEOUTMS -and comments in curl_httpclient.py). - -To select ``curl_httpclient``, call `AsyncHTTPClient.configure` at startup:: - - AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") -""" - -import datetime -import functools -from io import BytesIO -import ssl -import time -import weakref - -from tornado.concurrent import ( - Future, - future_set_result_unless_cancelled, - future_set_exception_unless_cancelled, -) -from tornado.escape import utf8, native_str -from tornado import gen, httputil -from tornado.ioloop import IOLoop -from tornado.util import Configurable - -from typing import Type, Any, Union, Dict, Callable, Optional, cast - - -class HTTPClient(object): - """A blocking HTTP client. - - This interface is provided to make it easier to share code between - synchronous and asynchronous applications. Applications that are - running an `.IOLoop` must use `AsyncHTTPClient` instead. - - Typical usage looks like this:: - - http_client = httpclient.HTTPClient() - try: - response = http_client.fetch("http://www.google.com/") - print(response.body) - except httpclient.HTTPError as e: - # HTTPError is raised for non-200 responses; the response - # can be found in e.response. - print("Error: " + str(e)) - except Exception as e: - # Other errors are possible, such as IOError. - print("Error: " + str(e)) - http_client.close() - - .. versionchanged:: 5.0 - - Due to limitations in `asyncio`, it is no longer possible to - use the synchronous ``HTTPClient`` while an `.IOLoop` is running. - Use `AsyncHTTPClient` instead. - - """ - - def __init__( - self, - async_client_class: "Optional[Type[AsyncHTTPClient]]" = None, - **kwargs: Any - ) -> None: - # Initialize self._closed at the beginning of the constructor - # so that an exception raised here doesn't lead to confusing - # failures in __del__. - self._closed = True - self._io_loop = IOLoop(make_current=False) - if async_client_class is None: - async_client_class = AsyncHTTPClient - - # Create the client while our IOLoop is "current", without - # clobbering the thread's real current IOLoop (if any). - async def make_client() -> "AsyncHTTPClient": - await gen.sleep(0) - assert async_client_class is not None - return async_client_class(**kwargs) - - self._async_client = self._io_loop.run_sync(make_client) - self._closed = False - - def __del__(self) -> None: - self.close() - - def close(self) -> None: - """Closes the HTTPClient, freeing any resources used.""" - if not self._closed: - self._async_client.close() - self._io_loop.close() - self._closed = True - - def fetch( - self, request: Union["HTTPRequest", str], **kwargs: Any - ) -> "HTTPResponse": - """Executes a request, returning an `HTTPResponse`. - - The request may be either a string URL or an `HTTPRequest` object. - If it is a string, we construct an `HTTPRequest` using any additional - kwargs: ``HTTPRequest(request, **kwargs)`` - - If an error occurs during the fetch, we raise an `HTTPError` unless - the ``raise_error`` keyword argument is set to False. - """ - response = self._io_loop.run_sync( - functools.partial(self._async_client.fetch, request, **kwargs) - ) - return response - - -class AsyncHTTPClient(Configurable): - """An non-blocking HTTP client. - - Example usage:: - - async def f(): - http_client = AsyncHTTPClient() - try: - response = await http_client.fetch("http://www.google.com") - except Exception as e: - print("Error: %s" % e) - else: - print(response.body) - - The constructor for this class is magic in several respects: It - actually creates an instance of an implementation-specific - subclass, and instances are reused as a kind of pseudo-singleton - (one per `.IOLoop`). The keyword argument ``force_instance=True`` - can be used to suppress this singleton behavior. Unless - ``force_instance=True`` is used, no arguments should be passed to - the `AsyncHTTPClient` constructor. The implementation subclass as - well as arguments to its constructor can be set with the static - method `configure()` - - All `AsyncHTTPClient` implementations support a ``defaults`` - keyword argument, which can be used to set default values for - `HTTPRequest` attributes. For example:: - - AsyncHTTPClient.configure( - None, defaults=dict(user_agent="MyUserAgent")) - # or with force_instance: - client = AsyncHTTPClient(force_instance=True, - defaults=dict(user_agent="MyUserAgent")) - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - - """ - - _instance_cache = None # type: Dict[IOLoop, AsyncHTTPClient] - - @classmethod - def configurable_base(cls) -> Type[Configurable]: - return AsyncHTTPClient - - @classmethod - def configurable_default(cls) -> Type[Configurable]: - from tornado.simple_httpclient import SimpleAsyncHTTPClient - - return SimpleAsyncHTTPClient - - @classmethod - def _async_clients(cls) -> Dict[IOLoop, "AsyncHTTPClient"]: - attr_name = "_async_client_dict_" + cls.__name__ - if not hasattr(cls, attr_name): - setattr(cls, attr_name, weakref.WeakKeyDictionary()) - return getattr(cls, attr_name) - - def __new__(cls, force_instance: bool = False, **kwargs: Any) -> "AsyncHTTPClient": - io_loop = IOLoop.current() - if force_instance: - instance_cache = None - else: - instance_cache = cls._async_clients() - if instance_cache is not None and io_loop in instance_cache: - return instance_cache[io_loop] - instance = super(AsyncHTTPClient, cls).__new__(cls, **kwargs) # type: ignore - # Make sure the instance knows which cache to remove itself from. - # It can't simply call _async_clients() because we may be in - # __new__(AsyncHTTPClient) but instance.__class__ may be - # SimpleAsyncHTTPClient. - instance._instance_cache = instance_cache - if instance_cache is not None: - instance_cache[instance.io_loop] = instance - return instance - - def initialize(self, defaults: Optional[Dict[str, Any]] = None) -> None: - self.io_loop = IOLoop.current() - self.defaults = dict(HTTPRequest._DEFAULTS) - if defaults is not None: - self.defaults.update(defaults) - self._closed = False - - def close(self) -> None: - """Destroys this HTTP client, freeing any file descriptors used. - - This method is **not needed in normal use** due to the way - that `AsyncHTTPClient` objects are transparently reused. - ``close()`` is generally only necessary when either the - `.IOLoop` is also being closed, or the ``force_instance=True`` - argument was used when creating the `AsyncHTTPClient`. - - No other methods may be called on the `AsyncHTTPClient` after - ``close()``. - - """ - if self._closed: - return - self._closed = True - if self._instance_cache is not None: - cached_val = self._instance_cache.pop(self.io_loop, None) - # If there's an object other than self in the instance - # cache for our IOLoop, something has gotten mixed up. A - # value of None appears to be possible when this is called - # from a destructor (HTTPClient.__del__) as the weakref - # gets cleared before the destructor runs. - if cached_val is not None and cached_val is not self: - raise RuntimeError("inconsistent AsyncHTTPClient cache") - - def fetch( - self, - request: Union[str, "HTTPRequest"], - raise_error: bool = True, - **kwargs: Any - ) -> "Future[HTTPResponse]": - """Executes a request, asynchronously returning an `HTTPResponse`. - - The request may be either a string URL or an `HTTPRequest` object. - If it is a string, we construct an `HTTPRequest` using any additional - kwargs: ``HTTPRequest(request, **kwargs)`` - - This method returns a `.Future` whose result is an - `HTTPResponse`. By default, the ``Future`` will raise an - `HTTPError` if the request returned a non-200 response code - (other errors may also be raised if the server could not be - contacted). Instead, if ``raise_error`` is set to False, the - response will always be returned regardless of the response - code. - - If a ``callback`` is given, it will be invoked with the `HTTPResponse`. - In the callback interface, `HTTPError` is not automatically raised. - Instead, you must check the response's ``error`` attribute or - call its `~HTTPResponse.rethrow` method. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - - The ``raise_error=False`` argument only affects the - `HTTPError` raised when a non-200 response code is used, - instead of suppressing all errors. - """ - if self._closed: - raise RuntimeError("fetch() called on closed AsyncHTTPClient") - if not isinstance(request, HTTPRequest): - request = HTTPRequest(url=request, **kwargs) - else: - if kwargs: - raise ValueError( - "kwargs can't be used if request is an HTTPRequest object" - ) - # We may modify this (to add Host, Accept-Encoding, etc), - # so make sure we don't modify the caller's object. This is also - # where normal dicts get converted to HTTPHeaders objects. - request.headers = httputil.HTTPHeaders(request.headers) - request_proxy = _RequestProxy(request, self.defaults) - future = Future() # type: Future[HTTPResponse] - - def handle_response(response: "HTTPResponse") -> None: - if response.error: - if raise_error or not response._error_is_response_code: - future_set_exception_unless_cancelled(future, response.error) - return - future_set_result_unless_cancelled(future, response) - - self.fetch_impl(cast(HTTPRequest, request_proxy), handle_response) - return future - - def fetch_impl( - self, request: "HTTPRequest", callback: Callable[["HTTPResponse"], None] - ) -> None: - raise NotImplementedError() - - @classmethod - def configure( - cls, impl: "Union[None, str, Type[Configurable]]", **kwargs: Any - ) -> None: - """Configures the `AsyncHTTPClient` subclass to use. - - ``AsyncHTTPClient()`` actually creates an instance of a subclass. - This method may be called with either a class object or the - fully-qualified name of such a class (or ``None`` to use the default, - ``SimpleAsyncHTTPClient``) - - If additional keyword arguments are given, they will be passed - to the constructor of each subclass instance created. The - keyword argument ``max_clients`` determines the maximum number - of simultaneous `~AsyncHTTPClient.fetch()` operations that can - execute in parallel on each `.IOLoop`. Additional arguments - may be supported depending on the implementation class in use. - - Example:: - - AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") - """ - super(AsyncHTTPClient, cls).configure(impl, **kwargs) - - -class HTTPRequest(object): - """HTTP client request object.""" - - _headers = None # type: Union[Dict[str, str], httputil.HTTPHeaders] - - # Default values for HTTPRequest parameters. - # Merged with the values on the request object by AsyncHTTPClient - # implementations. - _DEFAULTS = dict( - connect_timeout=20.0, - request_timeout=20.0, - follow_redirects=True, - max_redirects=5, - decompress_response=True, - proxy_password="", - allow_nonstandard_methods=False, - validate_cert=True, - ) - - def __init__( - self, - url: str, - method: str = "GET", - headers: Optional[Union[Dict[str, str], httputil.HTTPHeaders]] = None, - body: Optional[Union[bytes, str]] = None, - auth_username: Optional[str] = None, - auth_password: Optional[str] = None, - auth_mode: Optional[str] = None, - connect_timeout: Optional[float] = None, - request_timeout: Optional[float] = None, - if_modified_since: Optional[Union[float, datetime.datetime]] = None, - follow_redirects: Optional[bool] = None, - max_redirects: Optional[int] = None, - user_agent: Optional[str] = None, - use_gzip: Optional[bool] = None, - network_interface: Optional[str] = None, - streaming_callback: Optional[Callable[[bytes], None]] = None, - header_callback: Optional[Callable[[str], None]] = None, - prepare_curl_callback: Optional[Callable[[Any], None]] = None, - proxy_host: Optional[str] = None, - proxy_port: Optional[int] = None, - proxy_username: Optional[str] = None, - proxy_password: Optional[str] = None, - proxy_auth_mode: Optional[str] = None, - allow_nonstandard_methods: Optional[bool] = None, - validate_cert: Optional[bool] = None, - ca_certs: Optional[str] = None, - allow_ipv6: Optional[bool] = None, - client_key: Optional[str] = None, - client_cert: Optional[str] = None, - body_producer: Optional[ - Callable[[Callable[[bytes], None]], "Future[None]"] - ] = None, - expect_100_continue: bool = False, - decompress_response: Optional[bool] = None, - ssl_options: Optional[Union[Dict[str, Any], ssl.SSLContext]] = None, - ) -> None: - r"""All parameters except ``url`` are optional. - - :arg str url: URL to fetch - :arg str method: HTTP method, e.g. "GET" or "POST" - :arg headers: Additional HTTP headers to pass on the request - :type headers: `~tornado.httputil.HTTPHeaders` or `dict` - :arg body: HTTP request body as a string (byte or unicode; if unicode - the utf-8 encoding will be used) - :type body: `str` or `bytes` - :arg collections.abc.Callable body_producer: Callable used for - lazy/asynchronous request bodies. - It is called with one argument, a ``write`` function, and should - return a `.Future`. It should call the write function with new - data as it becomes available. The write function returns a - `.Future` which can be used for flow control. - Only one of ``body`` and ``body_producer`` may - be specified. ``body_producer`` is not supported on - ``curl_httpclient``. When using ``body_producer`` it is recommended - to pass a ``Content-Length`` in the headers as otherwise chunked - encoding will be used, and many servers do not support chunked - encoding on requests. New in Tornado 4.0 - :arg str auth_username: Username for HTTP authentication - :arg str auth_password: Password for HTTP authentication - :arg str auth_mode: Authentication mode; default is "basic". - Allowed values are implementation-defined; ``curl_httpclient`` - supports "basic" and "digest"; ``simple_httpclient`` only supports - "basic" - :arg float connect_timeout: Timeout for initial connection in seconds, - default 20 seconds (0 means no timeout) - :arg float request_timeout: Timeout for entire request in seconds, - default 20 seconds (0 means no timeout) - :arg if_modified_since: Timestamp for ``If-Modified-Since`` header - :type if_modified_since: `datetime` or `float` - :arg bool follow_redirects: Should redirects be followed automatically - or return the 3xx response? Default True. - :arg int max_redirects: Limit for ``follow_redirects``, default 5. - :arg str user_agent: String to send as ``User-Agent`` header - :arg bool decompress_response: Request a compressed response from - the server and decompress it after downloading. Default is True. - New in Tornado 4.0. - :arg bool use_gzip: Deprecated alias for ``decompress_response`` - since Tornado 4.0. - :arg str network_interface: Network interface or source IP to use for request. - See ``curl_httpclient`` note below. - :arg collections.abc.Callable streaming_callback: If set, ``streaming_callback`` will - be run with each chunk of data as it is received, and - ``HTTPResponse.body`` and ``HTTPResponse.buffer`` will be empty in - the final response. - :arg collections.abc.Callable header_callback: If set, ``header_callback`` will - be run with each header line as it is received (including the - first line, e.g. ``HTTP/1.0 200 OK\r\n``, and a final line - containing only ``\r\n``. All lines include the trailing newline - characters). ``HTTPResponse.headers`` will be empty in the final - response. This is most useful in conjunction with - ``streaming_callback``, because it's the only way to get access to - header data while the request is in progress. - :arg collections.abc.Callable prepare_curl_callback: If set, will be called with - a ``pycurl.Curl`` object to allow the application to make additional - ``setopt`` calls. - :arg str proxy_host: HTTP proxy hostname. To use proxies, - ``proxy_host`` and ``proxy_port`` must be set; ``proxy_username``, - ``proxy_pass`` and ``proxy_auth_mode`` are optional. Proxies are - currently only supported with ``curl_httpclient``. - :arg int proxy_port: HTTP proxy port - :arg str proxy_username: HTTP proxy username - :arg str proxy_password: HTTP proxy password - :arg str proxy_auth_mode: HTTP proxy Authentication mode; - default is "basic". supports "basic" and "digest" - :arg bool allow_nonstandard_methods: Allow unknown values for ``method`` - argument? Default is False. - :arg bool validate_cert: For HTTPS requests, validate the server's - certificate? Default is True. - :arg str ca_certs: filename of CA certificates in PEM format, - or None to use defaults. See note below when used with - ``curl_httpclient``. - :arg str client_key: Filename for client SSL key, if any. See - note below when used with ``curl_httpclient``. - :arg str client_cert: Filename for client SSL certificate, if any. - See note below when used with ``curl_httpclient``. - :arg ssl.SSLContext ssl_options: `ssl.SSLContext` object for use in - ``simple_httpclient`` (unsupported by ``curl_httpclient``). - Overrides ``validate_cert``, ``ca_certs``, ``client_key``, - and ``client_cert``. - :arg bool allow_ipv6: Use IPv6 when available? Default is True. - :arg bool expect_100_continue: If true, send the - ``Expect: 100-continue`` header and wait for a continue response - before sending the request body. Only supported with - ``simple_httpclient``. - - .. note:: - - When using ``curl_httpclient`` certain options may be - inherited by subsequent fetches because ``pycurl`` does - not allow them to be cleanly reset. This applies to the - ``ca_certs``, ``client_key``, ``client_cert``, and - ``network_interface`` arguments. If you use these - options, you should pass them on every request (you don't - have to always use the same values, but it's not possible - to mix requests that specify these options with ones that - use the defaults). - - .. versionadded:: 3.1 - The ``auth_mode`` argument. - - .. versionadded:: 4.0 - The ``body_producer`` and ``expect_100_continue`` arguments. - - .. versionadded:: 4.2 - The ``ssl_options`` argument. - - .. versionadded:: 4.5 - The ``proxy_auth_mode`` argument. - """ - # Note that some of these attributes go through property setters - # defined below. - self.headers = headers # type: ignore - if if_modified_since: - self.headers["If-Modified-Since"] = httputil.format_timestamp( - if_modified_since - ) - self.proxy_host = proxy_host - self.proxy_port = proxy_port - self.proxy_username = proxy_username - self.proxy_password = proxy_password - self.proxy_auth_mode = proxy_auth_mode - self.url = url - self.method = method - self.body = body # type: ignore - self.body_producer = body_producer - self.auth_username = auth_username - self.auth_password = auth_password - self.auth_mode = auth_mode - self.connect_timeout = connect_timeout - self.request_timeout = request_timeout - self.follow_redirects = follow_redirects - self.max_redirects = max_redirects - self.user_agent = user_agent - if decompress_response is not None: - self.decompress_response = decompress_response # type: Optional[bool] - else: - self.decompress_response = use_gzip - self.network_interface = network_interface - self.streaming_callback = streaming_callback - self.header_callback = header_callback - self.prepare_curl_callback = prepare_curl_callback - self.allow_nonstandard_methods = allow_nonstandard_methods - self.validate_cert = validate_cert - self.ca_certs = ca_certs - self.allow_ipv6 = allow_ipv6 - self.client_key = client_key - self.client_cert = client_cert - self.ssl_options = ssl_options - self.expect_100_continue = expect_100_continue - self.start_time = time.time() - - @property - def headers(self) -> httputil.HTTPHeaders: - # TODO: headers may actually be a plain dict until fairly late in - # the process (AsyncHTTPClient.fetch), but practically speaking, - # whenever the property is used they're already HTTPHeaders. - return self._headers # type: ignore - - @headers.setter - def headers(self, value: Union[Dict[str, str], httputil.HTTPHeaders]) -> None: - if value is None: - self._headers = httputil.HTTPHeaders() - else: - self._headers = value # type: ignore - - @property - def body(self) -> bytes: - return self._body - - @body.setter - def body(self, value: Union[bytes, str]) -> None: - self._body = utf8(value) - - -class HTTPResponse(object): - """HTTP Response object. - - Attributes: - - * ``request``: HTTPRequest object - - * ``code``: numeric HTTP status code, e.g. 200 or 404 - - * ``reason``: human-readable reason phrase describing the status code - - * ``headers``: `tornado.httputil.HTTPHeaders` object - - * ``effective_url``: final location of the resource after following any - redirects - - * ``buffer``: ``cStringIO`` object for response body - - * ``body``: response body as bytes (created on demand from ``self.buffer``) - - * ``error``: Exception object, if any - - * ``request_time``: seconds from request start to finish. Includes all - network operations from DNS resolution to receiving the last byte of - data. Does not include time spent in the queue (due to the - ``max_clients`` option). If redirects were followed, only includes - the final request. - - * ``start_time``: Time at which the HTTP operation started, based on - `time.time` (not the monotonic clock used by `.IOLoop.time`). May - be ``None`` if the request timed out while in the queue. - - * ``time_info``: dictionary of diagnostic timing information from the - request. Available data are subject to change, but currently uses timings - available from http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html, - plus ``queue``, which is the delay (if any) introduced by waiting for - a slot under `AsyncHTTPClient`'s ``max_clients`` setting. - - .. versionadded:: 5.1 - - Added the ``start_time`` attribute. - - .. versionchanged:: 5.1 - - The ``request_time`` attribute previously included time spent in the queue - for ``simple_httpclient``, but not in ``curl_httpclient``. Now queueing time - is excluded in both implementations. ``request_time`` is now more accurate for - ``curl_httpclient`` because it uses a monotonic clock when available. - """ - - # I'm not sure why these don't get type-inferred from the references in __init__. - error = None # type: Optional[BaseException] - _error_is_response_code = False - request = None # type: HTTPRequest - - def __init__( - self, - request: HTTPRequest, - code: int, - headers: Optional[httputil.HTTPHeaders] = None, - buffer: Optional[BytesIO] = None, - effective_url: Optional[str] = None, - error: Optional[BaseException] = None, - request_time: Optional[float] = None, - time_info: Optional[Dict[str, float]] = None, - reason: Optional[str] = None, - start_time: Optional[float] = None, - ) -> None: - if isinstance(request, _RequestProxy): - self.request = request.request - else: - self.request = request - self.code = code - self.reason = reason or httputil.responses.get(code, "Unknown") - if headers is not None: - self.headers = headers - else: - self.headers = httputil.HTTPHeaders() - self.buffer = buffer - self._body = None # type: Optional[bytes] - if effective_url is None: - self.effective_url = request.url - else: - self.effective_url = effective_url - self._error_is_response_code = False - if error is None: - if self.code < 200 or self.code >= 300: - self._error_is_response_code = True - self.error = HTTPError(self.code, message=self.reason, response=self) - else: - self.error = None - else: - self.error = error - self.start_time = start_time - self.request_time = request_time - self.time_info = time_info or {} - - @property - def body(self) -> bytes: - if self.buffer is None: - return b"" - elif self._body is None: - self._body = self.buffer.getvalue() - - return self._body - - def rethrow(self) -> None: - """If there was an error on the request, raise an `HTTPError`.""" - if self.error: - raise self.error - - def __repr__(self) -> str: - args = ",".join("%s=%r" % i for i in sorted(self.__dict__.items())) - return "%s(%s)" % (self.__class__.__name__, args) - - -class HTTPClientError(Exception): - """Exception thrown for an unsuccessful HTTP request. - - Attributes: - - * ``code`` - HTTP error integer error code, e.g. 404. Error code 599 is - used when no HTTP response was received, e.g. for a timeout. - - * ``response`` - `HTTPResponse` object, if any. - - Note that if ``follow_redirects`` is False, redirects become HTTPErrors, - and you can look at ``error.response.headers['Location']`` to see the - destination of the redirect. - - .. versionchanged:: 5.1 - - Renamed from ``HTTPError`` to ``HTTPClientError`` to avoid collisions with - `tornado.web.HTTPError`. The name ``tornado.httpclient.HTTPError`` remains - as an alias. - """ - - def __init__( - self, - code: int, - message: Optional[str] = None, - response: Optional[HTTPResponse] = None, - ) -> None: - self.code = code - self.message = message or httputil.responses.get(code, "Unknown") - self.response = response - super().__init__(code, message, response) - - def __str__(self) -> str: - return "HTTP %d: %s" % (self.code, self.message) - - # There is a cyclic reference between self and self.response, - # which breaks the default __repr__ implementation. - # (especially on pypy, which doesn't have the same recursion - # detection as cpython). - __repr__ = __str__ - - -HTTPError = HTTPClientError - - -class _RequestProxy(object): - """Combines an object with a dictionary of defaults. - - Used internally by AsyncHTTPClient implementations. - """ - - def __init__( - self, request: HTTPRequest, defaults: Optional[Dict[str, Any]] - ) -> None: - self.request = request - self.defaults = defaults - - def __getattr__(self, name: str) -> Any: - request_attr = getattr(self.request, name) - if request_attr is not None: - return request_attr - elif self.defaults is not None: - return self.defaults.get(name, None) - else: - return None - - -def main() -> None: - from tornado.options import define, options, parse_command_line - - define("print_headers", type=bool, default=False) - define("print_body", type=bool, default=True) - define("follow_redirects", type=bool, default=True) - define("validate_cert", type=bool, default=True) - define("proxy_host", type=str) - define("proxy_port", type=int) - args = parse_command_line() - client = HTTPClient() - for arg in args: - try: - response = client.fetch( - arg, - follow_redirects=options.follow_redirects, - validate_cert=options.validate_cert, - proxy_host=options.proxy_host, - proxy_port=options.proxy_port, - ) - except HTTPError as e: - if e.response is not None: - response = e.response - else: - raise - if options.print_headers: - print(response.headers) - if options.print_body: - print(native_str(response.body)) - client.close() - - -if __name__ == "__main__": - main() diff --git a/telegramer/include/tornado/httpserver.py b/telegramer/include/tornado/httpserver.py deleted file mode 100644 index cd4a468..0000000 --- a/telegramer/include/tornado/httpserver.py +++ /dev/null @@ -1,398 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A non-blocking, single-threaded HTTP server. - -Typical applications have little direct interaction with the `HTTPServer` -class except to start a server at the beginning of the process -(and even that is often done indirectly via `tornado.web.Application.listen`). - -.. versionchanged:: 4.0 - - The ``HTTPRequest`` class that used to live in this module has been moved - to `tornado.httputil.HTTPServerRequest`. The old name remains as an alias. -""" - -import socket -import ssl - -from tornado.escape import native_str -from tornado.http1connection import HTTP1ServerConnection, HTTP1ConnectionParameters -from tornado import httputil -from tornado import iostream -from tornado import netutil -from tornado.tcpserver import TCPServer -from tornado.util import Configurable - -import typing -from typing import Union, Any, Dict, Callable, List, Type, Tuple, Optional, Awaitable - -if typing.TYPE_CHECKING: - from typing import Set # noqa: F401 - - -class HTTPServer(TCPServer, Configurable, httputil.HTTPServerConnectionDelegate): - r"""A non-blocking, single-threaded HTTP server. - - A server is defined by a subclass of `.HTTPServerConnectionDelegate`, - or, for backwards compatibility, a callback that takes an - `.HTTPServerRequest` as an argument. The delegate is usually a - `tornado.web.Application`. - - `HTTPServer` supports keep-alive connections by default - (automatically for HTTP/1.1, or for HTTP/1.0 when the client - requests ``Connection: keep-alive``). - - If ``xheaders`` is ``True``, we support the - ``X-Real-Ip``/``X-Forwarded-For`` and - ``X-Scheme``/``X-Forwarded-Proto`` headers, which override the - remote IP and URI scheme/protocol for all requests. These headers - are useful when running Tornado behind a reverse proxy or load - balancer. The ``protocol`` argument can also be set to ``https`` - if Tornado is run behind an SSL-decoding proxy that does not set one of - the supported ``xheaders``. - - By default, when parsing the ``X-Forwarded-For`` header, Tornado will - select the last (i.e., the closest) address on the list of hosts as the - remote host IP address. To select the next server in the chain, a list of - trusted downstream hosts may be passed as the ``trusted_downstream`` - argument. These hosts will be skipped when parsing the ``X-Forwarded-For`` - header. - - To make this server serve SSL traffic, send the ``ssl_options`` keyword - argument with an `ssl.SSLContext` object. For compatibility with older - versions of Python ``ssl_options`` may also be a dictionary of keyword - arguments for the `ssl.wrap_socket` method.:: - - ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_ctx.load_cert_chain(os.path.join(data_dir, "mydomain.crt"), - os.path.join(data_dir, "mydomain.key")) - HTTPServer(application, ssl_options=ssl_ctx) - - `HTTPServer` initialization follows one of three patterns (the - initialization methods are defined on `tornado.tcpserver.TCPServer`): - - 1. `~tornado.tcpserver.TCPServer.listen`: simple single-process:: - - server = HTTPServer(app) - server.listen(8888) - IOLoop.current().start() - - In many cases, `tornado.web.Application.listen` can be used to avoid - the need to explicitly create the `HTTPServer`. - - 2. `~tornado.tcpserver.TCPServer.bind`/`~tornado.tcpserver.TCPServer.start`: - simple multi-process:: - - server = HTTPServer(app) - server.bind(8888) - server.start(0) # Forks multiple sub-processes - IOLoop.current().start() - - When using this interface, an `.IOLoop` must *not* be passed - to the `HTTPServer` constructor. `~.TCPServer.start` will always start - the server on the default singleton `.IOLoop`. - - 3. `~tornado.tcpserver.TCPServer.add_sockets`: advanced multi-process:: - - sockets = tornado.netutil.bind_sockets(8888) - tornado.process.fork_processes(0) - server = HTTPServer(app) - server.add_sockets(sockets) - IOLoop.current().start() - - The `~.TCPServer.add_sockets` interface is more complicated, - but it can be used with `tornado.process.fork_processes` to - give you more flexibility in when the fork happens. - `~.TCPServer.add_sockets` can also be used in single-process - servers if you want to create your listening sockets in some - way other than `tornado.netutil.bind_sockets`. - - .. versionchanged:: 4.0 - Added ``decompress_request``, ``chunk_size``, ``max_header_size``, - ``idle_connection_timeout``, ``body_timeout``, ``max_body_size`` - arguments. Added support for `.HTTPServerConnectionDelegate` - instances as ``request_callback``. - - .. versionchanged:: 4.1 - `.HTTPServerConnectionDelegate.start_request` is now called with - two arguments ``(server_conn, request_conn)`` (in accordance with the - documentation) instead of one ``(request_conn)``. - - .. versionchanged:: 4.2 - `HTTPServer` is now a subclass of `tornado.util.Configurable`. - - .. versionchanged:: 4.5 - Added the ``trusted_downstream`` argument. - - .. versionchanged:: 5.0 - The ``io_loop`` argument has been removed. - """ - - def __init__(self, *args: Any, **kwargs: Any) -> None: - # Ignore args to __init__; real initialization belongs in - # initialize since we're Configurable. (there's something - # weird in initialization order between this class, - # Configurable, and TCPServer so we can't leave __init__ out - # completely) - pass - - def initialize( - self, - request_callback: Union[ - httputil.HTTPServerConnectionDelegate, - Callable[[httputil.HTTPServerRequest], None], - ], - no_keep_alive: bool = False, - xheaders: bool = False, - ssl_options: Optional[Union[Dict[str, Any], ssl.SSLContext]] = None, - protocol: Optional[str] = None, - decompress_request: bool = False, - chunk_size: Optional[int] = None, - max_header_size: Optional[int] = None, - idle_connection_timeout: Optional[float] = None, - body_timeout: Optional[float] = None, - max_body_size: Optional[int] = None, - max_buffer_size: Optional[int] = None, - trusted_downstream: Optional[List[str]] = None, - ) -> None: - # This method's signature is not extracted with autodoc - # because we want its arguments to appear on the class - # constructor. When changing this signature, also update the - # copy in httpserver.rst. - self.request_callback = request_callback - self.xheaders = xheaders - self.protocol = protocol - self.conn_params = HTTP1ConnectionParameters( - decompress=decompress_request, - chunk_size=chunk_size, - max_header_size=max_header_size, - header_timeout=idle_connection_timeout or 3600, - max_body_size=max_body_size, - body_timeout=body_timeout, - no_keep_alive=no_keep_alive, - ) - TCPServer.__init__( - self, - ssl_options=ssl_options, - max_buffer_size=max_buffer_size, - read_chunk_size=chunk_size, - ) - self._connections = set() # type: Set[HTTP1ServerConnection] - self.trusted_downstream = trusted_downstream - - @classmethod - def configurable_base(cls) -> Type[Configurable]: - return HTTPServer - - @classmethod - def configurable_default(cls) -> Type[Configurable]: - return HTTPServer - - async def close_all_connections(self) -> None: - """Close all open connections and asynchronously wait for them to finish. - - This method is used in combination with `~.TCPServer.stop` to - support clean shutdowns (especially for unittests). Typical - usage would call ``stop()`` first to stop accepting new - connections, then ``await close_all_connections()`` to wait for - existing connections to finish. - - This method does not currently close open websocket connections. - - Note that this method is a coroutine and must be called with ``await``. - - """ - while self._connections: - # Peek at an arbitrary element of the set - conn = next(iter(self._connections)) - await conn.close() - - def handle_stream(self, stream: iostream.IOStream, address: Tuple) -> None: - context = _HTTPRequestContext( - stream, address, self.protocol, self.trusted_downstream - ) - conn = HTTP1ServerConnection(stream, self.conn_params, context) - self._connections.add(conn) - conn.start_serving(self) - - def start_request( - self, server_conn: object, request_conn: httputil.HTTPConnection - ) -> httputil.HTTPMessageDelegate: - if isinstance(self.request_callback, httputil.HTTPServerConnectionDelegate): - delegate = self.request_callback.start_request(server_conn, request_conn) - else: - delegate = _CallableAdapter(self.request_callback, request_conn) - - if self.xheaders: - delegate = _ProxyAdapter(delegate, request_conn) - - return delegate - - def on_close(self, server_conn: object) -> None: - self._connections.remove(typing.cast(HTTP1ServerConnection, server_conn)) - - -class _CallableAdapter(httputil.HTTPMessageDelegate): - def __init__( - self, - request_callback: Callable[[httputil.HTTPServerRequest], None], - request_conn: httputil.HTTPConnection, - ) -> None: - self.connection = request_conn - self.request_callback = request_callback - self.request = None # type: Optional[httputil.HTTPServerRequest] - self.delegate = None - self._chunks = [] # type: List[bytes] - - def headers_received( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - ) -> Optional[Awaitable[None]]: - self.request = httputil.HTTPServerRequest( - connection=self.connection, - start_line=typing.cast(httputil.RequestStartLine, start_line), - headers=headers, - ) - return None - - def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: - self._chunks.append(chunk) - return None - - def finish(self) -> None: - assert self.request is not None - self.request.body = b"".join(self._chunks) - self.request._parse_body() - self.request_callback(self.request) - - def on_connection_close(self) -> None: - del self._chunks - - -class _HTTPRequestContext(object): - def __init__( - self, - stream: iostream.IOStream, - address: Tuple, - protocol: Optional[str], - trusted_downstream: Optional[List[str]] = None, - ) -> None: - self.address = address - # Save the socket's address family now so we know how to - # interpret self.address even after the stream is closed - # and its socket attribute replaced with None. - if stream.socket is not None: - self.address_family = stream.socket.family - else: - self.address_family = None - # In HTTPServerRequest we want an IP, not a full socket address. - if ( - self.address_family in (socket.AF_INET, socket.AF_INET6) - and address is not None - ): - self.remote_ip = address[0] - else: - # Unix (or other) socket; fake the remote address. - self.remote_ip = "0.0.0.0" - if protocol: - self.protocol = protocol - elif isinstance(stream, iostream.SSLIOStream): - self.protocol = "https" - else: - self.protocol = "http" - self._orig_remote_ip = self.remote_ip - self._orig_protocol = self.protocol - self.trusted_downstream = set(trusted_downstream or []) - - def __str__(self) -> str: - if self.address_family in (socket.AF_INET, socket.AF_INET6): - return self.remote_ip - elif isinstance(self.address, bytes): - # Python 3 with the -bb option warns about str(bytes), - # so convert it explicitly. - # Unix socket addresses are str on mac but bytes on linux. - return native_str(self.address) - else: - return str(self.address) - - def _apply_xheaders(self, headers: httputil.HTTPHeaders) -> None: - """Rewrite the ``remote_ip`` and ``protocol`` fields.""" - # Squid uses X-Forwarded-For, others use X-Real-Ip - ip = headers.get("X-Forwarded-For", self.remote_ip) - # Skip trusted downstream hosts in X-Forwarded-For list - for ip in (cand.strip() for cand in reversed(ip.split(","))): - if ip not in self.trusted_downstream: - break - ip = headers.get("X-Real-Ip", ip) - if netutil.is_valid_ip(ip): - self.remote_ip = ip - # AWS uses X-Forwarded-Proto - proto_header = headers.get( - "X-Scheme", headers.get("X-Forwarded-Proto", self.protocol) - ) - if proto_header: - # use only the last proto entry if there is more than one - # TODO: support trusting multiple layers of proxied protocol - proto_header = proto_header.split(",")[-1].strip() - if proto_header in ("http", "https"): - self.protocol = proto_header - - def _unapply_xheaders(self) -> None: - """Undo changes from `_apply_xheaders`. - - Xheaders are per-request so they should not leak to the next - request on the same connection. - """ - self.remote_ip = self._orig_remote_ip - self.protocol = self._orig_protocol - - -class _ProxyAdapter(httputil.HTTPMessageDelegate): - def __init__( - self, - delegate: httputil.HTTPMessageDelegate, - request_conn: httputil.HTTPConnection, - ) -> None: - self.connection = request_conn - self.delegate = delegate - - def headers_received( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - ) -> Optional[Awaitable[None]]: - # TODO: either make context an official part of the - # HTTPConnection interface or figure out some other way to do this. - self.connection.context._apply_xheaders(headers) # type: ignore - return self.delegate.headers_received(start_line, headers) - - def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: - return self.delegate.data_received(chunk) - - def finish(self) -> None: - self.delegate.finish() - self._cleanup() - - def on_connection_close(self) -> None: - self.delegate.on_connection_close() - self._cleanup() - - def _cleanup(self) -> None: - self.connection.context._unapply_xheaders() # type: ignore - - -HTTPRequest = httputil.HTTPServerRequest diff --git a/telegramer/include/tornado/httputil.py b/telegramer/include/tornado/httputil.py deleted file mode 100644 index bd32cd0..0000000 --- a/telegramer/include/tornado/httputil.py +++ /dev/null @@ -1,1133 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""HTTP utility code shared by clients and servers. - -This module also defines the `HTTPServerRequest` class which is exposed -via `tornado.web.RequestHandler.request`. -""" - -import calendar -import collections -import copy -import datetime -import email.utils -from functools import lru_cache -from http.client import responses -import http.cookies -import re -from ssl import SSLError -import time -import unicodedata -from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl - -from tornado.escape import native_str, parse_qs_bytes, utf8 -from tornado.log import gen_log -from tornado.util import ObjectDict, unicode_type - - -# responses is unused in this file, but we re-export it to other files. -# Reference it so pyflakes doesn't complain. -responses - -import typing -from typing import ( - Tuple, - Iterable, - List, - Mapping, - Iterator, - Dict, - Union, - Optional, - Awaitable, - Generator, - AnyStr, -) - -if typing.TYPE_CHECKING: - from typing import Deque # noqa: F401 - from asyncio import Future # noqa: F401 - import unittest # noqa: F401 - - -@lru_cache(1000) -def _normalize_header(name: str) -> str: - """Map a header name to Http-Header-Case. - - >>> _normalize_header("coNtent-TYPE") - 'Content-Type' - """ - return "-".join([w.capitalize() for w in name.split("-")]) - - -class HTTPHeaders(collections.abc.MutableMapping): - """A dictionary that maintains ``Http-Header-Case`` for all keys. - - Supports multiple values per key via a pair of new methods, - `add()` and `get_list()`. The regular dictionary interface - returns a single value per key, with multiple values joined by a - comma. - - >>> h = HTTPHeaders({"content-type": "text/html"}) - >>> list(h.keys()) - ['Content-Type'] - >>> h["Content-Type"] - 'text/html' - - >>> h.add("Set-Cookie", "A=B") - >>> h.add("Set-Cookie", "C=D") - >>> h["set-cookie"] - 'A=B,C=D' - >>> h.get_list("set-cookie") - ['A=B', 'C=D'] - - >>> for (k,v) in sorted(h.get_all()): - ... print('%s: %s' % (k,v)) - ... - Content-Type: text/html - Set-Cookie: A=B - Set-Cookie: C=D - """ - - @typing.overload - def __init__(self, __arg: Mapping[str, List[str]]) -> None: - pass - - @typing.overload # noqa: F811 - def __init__(self, __arg: Mapping[str, str]) -> None: - pass - - @typing.overload # noqa: F811 - def __init__(self, *args: Tuple[str, str]) -> None: - pass - - @typing.overload # noqa: F811 - def __init__(self, **kwargs: str) -> None: - pass - - def __init__(self, *args: typing.Any, **kwargs: str) -> None: # noqa: F811 - self._dict = {} # type: typing.Dict[str, str] - self._as_list = {} # type: typing.Dict[str, typing.List[str]] - self._last_key = None # type: Optional[str] - if len(args) == 1 and len(kwargs) == 0 and isinstance(args[0], HTTPHeaders): - # Copy constructor - for k, v in args[0].get_all(): - self.add(k, v) - else: - # Dict-style initialization - self.update(*args, **kwargs) - - # new public methods - - def add(self, name: str, value: str) -> None: - """Adds a new value for the given key.""" - norm_name = _normalize_header(name) - self._last_key = norm_name - if norm_name in self: - self._dict[norm_name] = ( - native_str(self[norm_name]) + "," + native_str(value) - ) - self._as_list[norm_name].append(value) - else: - self[norm_name] = value - - def get_list(self, name: str) -> List[str]: - """Returns all values for the given header as a list.""" - norm_name = _normalize_header(name) - return self._as_list.get(norm_name, []) - - def get_all(self) -> Iterable[Tuple[str, str]]: - """Returns an iterable of all (name, value) pairs. - - If a header has multiple values, multiple pairs will be - returned with the same name. - """ - for name, values in self._as_list.items(): - for value in values: - yield (name, value) - - def parse_line(self, line: str) -> None: - """Updates the dictionary with a single header line. - - >>> h = HTTPHeaders() - >>> h.parse_line("Content-Type: text/html") - >>> h.get('content-type') - 'text/html' - """ - if line[0].isspace(): - # continuation of a multi-line header - if self._last_key is None: - raise HTTPInputError("first header line cannot start with whitespace") - new_part = " " + line.lstrip() - self._as_list[self._last_key][-1] += new_part - self._dict[self._last_key] += new_part - else: - try: - name, value = line.split(":", 1) - except ValueError: - raise HTTPInputError("no colon in header line") - self.add(name, value.strip()) - - @classmethod - def parse(cls, headers: str) -> "HTTPHeaders": - """Returns a dictionary from HTTP header text. - - >>> h = HTTPHeaders.parse("Content-Type: text/html\\r\\nContent-Length: 42\\r\\n") - >>> sorted(h.items()) - [('Content-Length', '42'), ('Content-Type', 'text/html')] - - .. versionchanged:: 5.1 - - Raises `HTTPInputError` on malformed headers instead of a - mix of `KeyError`, and `ValueError`. - - """ - h = cls() - # RFC 7230 section 3.5: a recipient MAY recognize a single LF as a line - # terminator and ignore any preceding CR. - for line in headers.split("\n"): - if line.endswith("\r"): - line = line[:-1] - if line: - h.parse_line(line) - return h - - # MutableMapping abstract method implementations. - - def __setitem__(self, name: str, value: str) -> None: - norm_name = _normalize_header(name) - self._dict[norm_name] = value - self._as_list[norm_name] = [value] - - def __getitem__(self, name: str) -> str: - return self._dict[_normalize_header(name)] - - def __delitem__(self, name: str) -> None: - norm_name = _normalize_header(name) - del self._dict[norm_name] - del self._as_list[norm_name] - - def __len__(self) -> int: - return len(self._dict) - - def __iter__(self) -> Iterator[typing.Any]: - return iter(self._dict) - - def copy(self) -> "HTTPHeaders": - # defined in dict but not in MutableMapping. - return HTTPHeaders(self) - - # Use our overridden copy method for the copy.copy module. - # This makes shallow copies one level deeper, but preserves - # the appearance that HTTPHeaders is a single container. - __copy__ = copy - - def __str__(self) -> str: - lines = [] - for name, value in self.get_all(): - lines.append("%s: %s\n" % (name, value)) - return "".join(lines) - - __unicode__ = __str__ - - -class HTTPServerRequest(object): - """A single HTTP request. - - All attributes are type `str` unless otherwise noted. - - .. attribute:: method - - HTTP request method, e.g. "GET" or "POST" - - .. attribute:: uri - - The requested uri. - - .. attribute:: path - - The path portion of `uri` - - .. attribute:: query - - The query portion of `uri` - - .. attribute:: version - - HTTP version specified in request, e.g. "HTTP/1.1" - - .. attribute:: headers - - `.HTTPHeaders` dictionary-like object for request headers. Acts like - a case-insensitive dictionary with additional methods for repeated - headers. - - .. attribute:: body - - Request body, if present, as a byte string. - - .. attribute:: remote_ip - - Client's IP address as a string. If ``HTTPServer.xheaders`` is set, - will pass along the real IP address provided by a load balancer - in the ``X-Real-Ip`` or ``X-Forwarded-For`` header. - - .. versionchanged:: 3.1 - The list format of ``X-Forwarded-For`` is now supported. - - .. attribute:: protocol - - The protocol used, either "http" or "https". If ``HTTPServer.xheaders`` - is set, will pass along the protocol used by a load balancer if - reported via an ``X-Scheme`` header. - - .. attribute:: host - - The requested hostname, usually taken from the ``Host`` header. - - .. attribute:: arguments - - GET/POST arguments are available in the arguments property, which - maps arguments names to lists of values (to support multiple values - for individual names). Names are of type `str`, while arguments - are byte strings. Note that this is different from - `.RequestHandler.get_argument`, which returns argument values as - unicode strings. - - .. attribute:: query_arguments - - Same format as ``arguments``, but contains only arguments extracted - from the query string. - - .. versionadded:: 3.2 - - .. attribute:: body_arguments - - Same format as ``arguments``, but contains only arguments extracted - from the request body. - - .. versionadded:: 3.2 - - .. attribute:: files - - File uploads are available in the files property, which maps file - names to lists of `.HTTPFile`. - - .. attribute:: connection - - An HTTP request is attached to a single HTTP connection, which can - be accessed through the "connection" attribute. Since connections - are typically kept open in HTTP/1.1, multiple requests can be handled - sequentially on a single connection. - - .. versionchanged:: 4.0 - Moved from ``tornado.httpserver.HTTPRequest``. - """ - - path = None # type: str - query = None # type: str - - # HACK: Used for stream_request_body - _body_future = None # type: Future[None] - - def __init__( - self, - method: Optional[str] = None, - uri: Optional[str] = None, - version: str = "HTTP/1.0", - headers: Optional[HTTPHeaders] = None, - body: Optional[bytes] = None, - host: Optional[str] = None, - files: Optional[Dict[str, List["HTTPFile"]]] = None, - connection: Optional["HTTPConnection"] = None, - start_line: Optional["RequestStartLine"] = None, - server_connection: Optional[object] = None, - ) -> None: - if start_line is not None: - method, uri, version = start_line - self.method = method - self.uri = uri - self.version = version - self.headers = headers or HTTPHeaders() - self.body = body or b"" - - # set remote IP and protocol - context = getattr(connection, "context", None) - self.remote_ip = getattr(context, "remote_ip", None) - self.protocol = getattr(context, "protocol", "http") - - self.host = host or self.headers.get("Host") or "127.0.0.1" - self.host_name = split_host_and_port(self.host.lower())[0] - self.files = files or {} - self.connection = connection - self.server_connection = server_connection - self._start_time = time.time() - self._finish_time = None - - if uri is not None: - self.path, sep, self.query = uri.partition("?") - self.arguments = parse_qs_bytes(self.query, keep_blank_values=True) - self.query_arguments = copy.deepcopy(self.arguments) - self.body_arguments = {} # type: Dict[str, List[bytes]] - - @property - def cookies(self) -> Dict[str, http.cookies.Morsel]: - """A dictionary of ``http.cookies.Morsel`` objects.""" - if not hasattr(self, "_cookies"): - self._cookies = ( - http.cookies.SimpleCookie() - ) # type: http.cookies.SimpleCookie - if "Cookie" in self.headers: - try: - parsed = parse_cookie(self.headers["Cookie"]) - except Exception: - pass - else: - for k, v in parsed.items(): - try: - self._cookies[k] = v - except Exception: - # SimpleCookie imposes some restrictions on keys; - # parse_cookie does not. Discard any cookies - # with disallowed keys. - pass - return self._cookies - - def full_url(self) -> str: - """Reconstructs the full URL for this request.""" - return self.protocol + "://" + self.host + self.uri - - def request_time(self) -> float: - """Returns the amount of time it took for this request to execute.""" - if self._finish_time is None: - return time.time() - self._start_time - else: - return self._finish_time - self._start_time - - def get_ssl_certificate( - self, binary_form: bool = False - ) -> Union[None, Dict, bytes]: - """Returns the client's SSL certificate, if any. - - To use client certificates, the HTTPServer's - `ssl.SSLContext.verify_mode` field must be set, e.g.:: - - ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_ctx.load_cert_chain("foo.crt", "foo.key") - ssl_ctx.load_verify_locations("cacerts.pem") - ssl_ctx.verify_mode = ssl.CERT_REQUIRED - server = HTTPServer(app, ssl_options=ssl_ctx) - - By default, the return value is a dictionary (or None, if no - client certificate is present). If ``binary_form`` is true, a - DER-encoded form of the certificate is returned instead. See - SSLSocket.getpeercert() in the standard library for more - details. - http://docs.python.org/library/ssl.html#sslsocket-objects - """ - try: - if self.connection is None: - return None - # TODO: add a method to HTTPConnection for this so it can work with HTTP/2 - return self.connection.stream.socket.getpeercert( # type: ignore - binary_form=binary_form - ) - except SSLError: - return None - - def _parse_body(self) -> None: - parse_body_arguments( - self.headers.get("Content-Type", ""), - self.body, - self.body_arguments, - self.files, - self.headers, - ) - - for k, v in self.body_arguments.items(): - self.arguments.setdefault(k, []).extend(v) - - def __repr__(self) -> str: - attrs = ("protocol", "host", "method", "uri", "version", "remote_ip") - args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs]) - return "%s(%s)" % (self.__class__.__name__, args) - - -class HTTPInputError(Exception): - """Exception class for malformed HTTP requests or responses - from remote sources. - - .. versionadded:: 4.0 - """ - - pass - - -class HTTPOutputError(Exception): - """Exception class for errors in HTTP output. - - .. versionadded:: 4.0 - """ - - pass - - -class HTTPServerConnectionDelegate(object): - """Implement this interface to handle requests from `.HTTPServer`. - - .. versionadded:: 4.0 - """ - - def start_request( - self, server_conn: object, request_conn: "HTTPConnection" - ) -> "HTTPMessageDelegate": - """This method is called by the server when a new request has started. - - :arg server_conn: is an opaque object representing the long-lived - (e.g. tcp-level) connection. - :arg request_conn: is a `.HTTPConnection` object for a single - request/response exchange. - - This method should return a `.HTTPMessageDelegate`. - """ - raise NotImplementedError() - - def on_close(self, server_conn: object) -> None: - """This method is called when a connection has been closed. - - :arg server_conn: is a server connection that has previously been - passed to ``start_request``. - """ - pass - - -class HTTPMessageDelegate(object): - """Implement this interface to handle an HTTP request or response. - - .. versionadded:: 4.0 - """ - - # TODO: genericize this class to avoid exposing the Union. - def headers_received( - self, - start_line: Union["RequestStartLine", "ResponseStartLine"], - headers: HTTPHeaders, - ) -> Optional[Awaitable[None]]: - """Called when the HTTP headers have been received and parsed. - - :arg start_line: a `.RequestStartLine` or `.ResponseStartLine` - depending on whether this is a client or server message. - :arg headers: a `.HTTPHeaders` instance. - - Some `.HTTPConnection` methods can only be called during - ``headers_received``. - - May return a `.Future`; if it does the body will not be read - until it is done. - """ - pass - - def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: - """Called when a chunk of data has been received. - - May return a `.Future` for flow control. - """ - pass - - def finish(self) -> None: - """Called after the last chunk of data has been received.""" - pass - - def on_connection_close(self) -> None: - """Called if the connection is closed without finishing the request. - - If ``headers_received`` is called, either ``finish`` or - ``on_connection_close`` will be called, but not both. - """ - pass - - -class HTTPConnection(object): - """Applications use this interface to write their responses. - - .. versionadded:: 4.0 - """ - - def write_headers( - self, - start_line: Union["RequestStartLine", "ResponseStartLine"], - headers: HTTPHeaders, - chunk: Optional[bytes] = None, - ) -> "Future[None]": - """Write an HTTP header block. - - :arg start_line: a `.RequestStartLine` or `.ResponseStartLine`. - :arg headers: a `.HTTPHeaders` instance. - :arg chunk: the first (optional) chunk of data. This is an optimization - so that small responses can be written in the same call as their - headers. - - The ``version`` field of ``start_line`` is ignored. - - Returns a future for flow control. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. - """ - raise NotImplementedError() - - def write(self, chunk: bytes) -> "Future[None]": - """Writes a chunk of body data. - - Returns a future for flow control. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. - """ - raise NotImplementedError() - - def finish(self) -> None: - """Indicates that the last body data has been written. - """ - raise NotImplementedError() - - -def url_concat( - url: str, - args: Union[ - None, Dict[str, str], List[Tuple[str, str]], Tuple[Tuple[str, str], ...] - ], -) -> str: - """Concatenate url and arguments regardless of whether - url has existing query parameters. - - ``args`` may be either a dictionary or a list of key-value pairs - (the latter allows for multiple values with the same key. - - >>> url_concat("http://example.com/foo", dict(c="d")) - 'http://example.com/foo?c=d' - >>> url_concat("http://example.com/foo?a=b", dict(c="d")) - 'http://example.com/foo?a=b&c=d' - >>> url_concat("http://example.com/foo?a=b", [("c", "d"), ("c", "d2")]) - 'http://example.com/foo?a=b&c=d&c=d2' - """ - if args is None: - return url - parsed_url = urlparse(url) - if isinstance(args, dict): - parsed_query = parse_qsl(parsed_url.query, keep_blank_values=True) - parsed_query.extend(args.items()) - elif isinstance(args, list) or isinstance(args, tuple): - parsed_query = parse_qsl(parsed_url.query, keep_blank_values=True) - parsed_query.extend(args) - else: - err = "'args' parameter should be dict, list or tuple. Not {0}".format( - type(args) - ) - raise TypeError(err) - final_query = urlencode(parsed_query) - url = urlunparse( - ( - parsed_url[0], - parsed_url[1], - parsed_url[2], - parsed_url[3], - final_query, - parsed_url[5], - ) - ) - return url - - -class HTTPFile(ObjectDict): - """Represents a file uploaded via a form. - - For backwards compatibility, its instance attributes are also - accessible as dictionary keys. - - * ``filename`` - * ``body`` - * ``content_type`` - """ - - pass - - -def _parse_request_range( - range_header: str, -) -> Optional[Tuple[Optional[int], Optional[int]]]: - """Parses a Range header. - - Returns either ``None`` or tuple ``(start, end)``. - Note that while the HTTP headers use inclusive byte positions, - this method returns indexes suitable for use in slices. - - >>> start, end = _parse_request_range("bytes=1-2") - >>> start, end - (1, 3) - >>> [0, 1, 2, 3, 4][start:end] - [1, 2] - >>> _parse_request_range("bytes=6-") - (6, None) - >>> _parse_request_range("bytes=-6") - (-6, None) - >>> _parse_request_range("bytes=-0") - (None, 0) - >>> _parse_request_range("bytes=") - (None, None) - >>> _parse_request_range("foo=42") - >>> _parse_request_range("bytes=1-2,6-10") - - Note: only supports one range (ex, ``bytes=1-2,6-10`` is not allowed). - - See [0] for the details of the range header. - - [0]: http://greenbytes.de/tech/webdav/draft-ietf-httpbis-p5-range-latest.html#byte.ranges - """ - unit, _, value = range_header.partition("=") - unit, value = unit.strip(), value.strip() - if unit != "bytes": - return None - start_b, _, end_b = value.partition("-") - try: - start = _int_or_none(start_b) - end = _int_or_none(end_b) - except ValueError: - return None - if end is not None: - if start is None: - if end != 0: - start = -end - end = None - else: - end += 1 - return (start, end) - - -def _get_content_range(start: Optional[int], end: Optional[int], total: int) -> str: - """Returns a suitable Content-Range header: - - >>> print(_get_content_range(None, 1, 4)) - bytes 0-0/4 - >>> print(_get_content_range(1, 3, 4)) - bytes 1-2/4 - >>> print(_get_content_range(None, None, 4)) - bytes 0-3/4 - """ - start = start or 0 - end = (end or total) - 1 - return "bytes %s-%s/%s" % (start, end, total) - - -def _int_or_none(val: str) -> Optional[int]: - val = val.strip() - if val == "": - return None - return int(val) - - -def parse_body_arguments( - content_type: str, - body: bytes, - arguments: Dict[str, List[bytes]], - files: Dict[str, List[HTTPFile]], - headers: Optional[HTTPHeaders] = None, -) -> None: - """Parses a form request body. - - Supports ``application/x-www-form-urlencoded`` and - ``multipart/form-data``. The ``content_type`` parameter should be - a string and ``body`` should be a byte string. The ``arguments`` - and ``files`` parameters are dictionaries that will be updated - with the parsed contents. - """ - if content_type.startswith("application/x-www-form-urlencoded"): - if headers and "Content-Encoding" in headers: - gen_log.warning( - "Unsupported Content-Encoding: %s", headers["Content-Encoding"] - ) - return - try: - # real charset decoding will happen in RequestHandler.decode_argument() - uri_arguments = parse_qs_bytes(body, keep_blank_values=True) - except Exception as e: - gen_log.warning("Invalid x-www-form-urlencoded body: %s", e) - uri_arguments = {} - for name, values in uri_arguments.items(): - if values: - arguments.setdefault(name, []).extend(values) - elif content_type.startswith("multipart/form-data"): - if headers and "Content-Encoding" in headers: - gen_log.warning( - "Unsupported Content-Encoding: %s", headers["Content-Encoding"] - ) - return - try: - fields = content_type.split(";") - for field in fields: - k, sep, v = field.strip().partition("=") - if k == "boundary" and v: - parse_multipart_form_data(utf8(v), body, arguments, files) - break - else: - raise ValueError("multipart boundary not found") - except Exception as e: - gen_log.warning("Invalid multipart/form-data: %s", e) - - -def parse_multipart_form_data( - boundary: bytes, - data: bytes, - arguments: Dict[str, List[bytes]], - files: Dict[str, List[HTTPFile]], -) -> None: - """Parses a ``multipart/form-data`` body. - - The ``boundary`` and ``data`` parameters are both byte strings. - The dictionaries given in the arguments and files parameters - will be updated with the contents of the body. - - .. versionchanged:: 5.1 - - Now recognizes non-ASCII filenames in RFC 2231/5987 - (``filename*=``) format. - """ - # The standard allows for the boundary to be quoted in the header, - # although it's rare (it happens at least for google app engine - # xmpp). I think we're also supposed to handle backslash-escapes - # here but I'll save that until we see a client that uses them - # in the wild. - if boundary.startswith(b'"') and boundary.endswith(b'"'): - boundary = boundary[1:-1] - final_boundary_index = data.rfind(b"--" + boundary + b"--") - if final_boundary_index == -1: - gen_log.warning("Invalid multipart/form-data: no final boundary") - return - parts = data[:final_boundary_index].split(b"--" + boundary + b"\r\n") - for part in parts: - if not part: - continue - eoh = part.find(b"\r\n\r\n") - if eoh == -1: - gen_log.warning("multipart/form-data missing headers") - continue - headers = HTTPHeaders.parse(part[:eoh].decode("utf-8")) - disp_header = headers.get("Content-Disposition", "") - disposition, disp_params = _parse_header(disp_header) - if disposition != "form-data" or not part.endswith(b"\r\n"): - gen_log.warning("Invalid multipart/form-data") - continue - value = part[eoh + 4 : -2] - if not disp_params.get("name"): - gen_log.warning("multipart/form-data value missing name") - continue - name = disp_params["name"] - if disp_params.get("filename"): - ctype = headers.get("Content-Type", "application/unknown") - files.setdefault(name, []).append( - HTTPFile( - filename=disp_params["filename"], body=value, content_type=ctype - ) - ) - else: - arguments.setdefault(name, []).append(value) - - -def format_timestamp( - ts: Union[int, float, tuple, time.struct_time, datetime.datetime] -) -> str: - """Formats a timestamp in the format used by HTTP. - - The argument may be a numeric timestamp as returned by `time.time`, - a time tuple as returned by `time.gmtime`, or a `datetime.datetime` - object. - - >>> format_timestamp(1359312200) - 'Sun, 27 Jan 2013 18:43:20 GMT' - """ - if isinstance(ts, (int, float)): - time_num = ts - elif isinstance(ts, (tuple, time.struct_time)): - time_num = calendar.timegm(ts) - elif isinstance(ts, datetime.datetime): - time_num = calendar.timegm(ts.utctimetuple()) - else: - raise TypeError("unknown timestamp type: %r" % ts) - return email.utils.formatdate(time_num, usegmt=True) - - -RequestStartLine = collections.namedtuple( - "RequestStartLine", ["method", "path", "version"] -) - - -_http_version_re = re.compile(r"^HTTP/1\.[0-9]$") - - -def parse_request_start_line(line: str) -> RequestStartLine: - """Returns a (method, path, version) tuple for an HTTP 1.x request line. - - The response is a `collections.namedtuple`. - - >>> parse_request_start_line("GET /foo HTTP/1.1") - RequestStartLine(method='GET', path='/foo', version='HTTP/1.1') - """ - try: - method, path, version = line.split(" ") - except ValueError: - # https://tools.ietf.org/html/rfc7230#section-3.1.1 - # invalid request-line SHOULD respond with a 400 (Bad Request) - raise HTTPInputError("Malformed HTTP request line") - if not _http_version_re.match(version): - raise HTTPInputError( - "Malformed HTTP version in HTTP Request-Line: %r" % version - ) - return RequestStartLine(method, path, version) - - -ResponseStartLine = collections.namedtuple( - "ResponseStartLine", ["version", "code", "reason"] -) - - -_http_response_line_re = re.compile(r"(HTTP/1.[0-9]) ([0-9]+) ([^\r]*)") - - -def parse_response_start_line(line: str) -> ResponseStartLine: - """Returns a (version, code, reason) tuple for an HTTP 1.x response line. - - The response is a `collections.namedtuple`. - - >>> parse_response_start_line("HTTP/1.1 200 OK") - ResponseStartLine(version='HTTP/1.1', code=200, reason='OK') - """ - line = native_str(line) - match = _http_response_line_re.match(line) - if not match: - raise HTTPInputError("Error parsing response start line") - return ResponseStartLine(match.group(1), int(match.group(2)), match.group(3)) - - -# _parseparam and _parse_header are copied and modified from python2.7's cgi.py -# The original 2.7 version of this code did not correctly support some -# combinations of semicolons and double quotes. -# It has also been modified to support valueless parameters as seen in -# websocket extension negotiations, and to support non-ascii values in -# RFC 2231/5987 format. - - -def _parseparam(s: str) -> Generator[str, None, None]: - while s[:1] == ";": - s = s[1:] - end = s.find(";") - while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2: - end = s.find(";", end + 1) - if end < 0: - end = len(s) - f = s[:end] - yield f.strip() - s = s[end:] - - -def _parse_header(line: str) -> Tuple[str, Dict[str, str]]: - r"""Parse a Content-type like header. - - Return the main content-type and a dictionary of options. - - >>> d = "form-data; foo=\"b\\\\a\\\"r\"; file*=utf-8''T%C3%A4st" - >>> ct, d = _parse_header(d) - >>> ct - 'form-data' - >>> d['file'] == r'T\u00e4st'.encode('ascii').decode('unicode_escape') - True - >>> d['foo'] - 'b\\a"r' - """ - parts = _parseparam(";" + line) - key = next(parts) - # decode_params treats first argument special, but we already stripped key - params = [("Dummy", "value")] - for p in parts: - i = p.find("=") - if i >= 0: - name = p[:i].strip().lower() - value = p[i + 1 :].strip() - params.append((name, native_str(value))) - decoded_params = email.utils.decode_params(params) - decoded_params.pop(0) # get rid of the dummy again - pdict = {} - for name, decoded_value in decoded_params: - value = email.utils.collapse_rfc2231_value(decoded_value) - if len(value) >= 2 and value[0] == '"' and value[-1] == '"': - value = value[1:-1] - pdict[name] = value - return key, pdict - - -def _encode_header(key: str, pdict: Dict[str, str]) -> str: - """Inverse of _parse_header. - - >>> _encode_header('permessage-deflate', - ... {'client_max_window_bits': 15, 'client_no_context_takeover': None}) - 'permessage-deflate; client_max_window_bits=15; client_no_context_takeover' - """ - if not pdict: - return key - out = [key] - # Sort the parameters just to make it easy to test. - for k, v in sorted(pdict.items()): - if v is None: - out.append(k) - else: - # TODO: quote if necessary. - out.append("%s=%s" % (k, v)) - return "; ".join(out) - - -def encode_username_password( - username: Union[str, bytes], password: Union[str, bytes] -) -> bytes: - """Encodes a username/password pair in the format used by HTTP auth. - - The return value is a byte string in the form ``username:password``. - - .. versionadded:: 5.1 - """ - if isinstance(username, unicode_type): - username = unicodedata.normalize("NFC", username) - if isinstance(password, unicode_type): - password = unicodedata.normalize("NFC", password) - return utf8(username) + b":" + utf8(password) - - -def doctests(): - # type: () -> unittest.TestSuite - import doctest - - return doctest.DocTestSuite() - - -_netloc_re = re.compile(r"^(.+):(\d+)$") - - -def split_host_and_port(netloc: str) -> Tuple[str, Optional[int]]: - """Returns ``(host, port)`` tuple from ``netloc``. - - Returned ``port`` will be ``None`` if not present. - - .. versionadded:: 4.1 - """ - match = _netloc_re.match(netloc) - if match: - host = match.group(1) - port = int(match.group(2)) # type: Optional[int] - else: - host = netloc - port = None - return (host, port) - - -def qs_to_qsl(qs: Dict[str, List[AnyStr]]) -> Iterable[Tuple[str, AnyStr]]: - """Generator converting a result of ``parse_qs`` back to name-value pairs. - - .. versionadded:: 5.0 - """ - for k, vs in qs.items(): - for v in vs: - yield (k, v) - - -_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") -_QuotePatt = re.compile(r"[\\].") -_nulljoin = "".join - - -def _unquote_cookie(s: str) -> str: - """Handle double quotes and escaping in cookie values. - - This method is copied verbatim from the Python 3.5 standard - library (http.cookies._unquote) so we don't have to depend on - non-public interfaces. - """ - # If there aren't any doublequotes, - # then there can't be any special characters. See RFC 2109. - if s is None or len(s) < 2: - return s - if s[0] != '"' or s[-1] != '"': - return s - - # We have to assume that we must decode this string. - # Down to work. - - # Remove the "s - s = s[1:-1] - - # Check for special sequences. Examples: - # \012 --> \n - # \" --> " - # - i = 0 - n = len(s) - res = [] - while 0 <= i < n: - o_match = _OctalPatt.search(s, i) - q_match = _QuotePatt.search(s, i) - if not o_match and not q_match: # Neither matched - res.append(s[i:]) - break - # else: - j = k = -1 - if o_match: - j = o_match.start(0) - if q_match: - k = q_match.start(0) - if q_match and (not o_match or k < j): # QuotePatt matched - res.append(s[i:k]) - res.append(s[k + 1]) - i = k + 2 - else: # OctalPatt matched - res.append(s[i:j]) - res.append(chr(int(s[j + 1 : j + 4], 8))) - i = j + 4 - return _nulljoin(res) - - -def parse_cookie(cookie: str) -> Dict[str, str]: - """Parse a ``Cookie`` HTTP header into a dict of name/value pairs. - - This function attempts to mimic browser cookie parsing behavior; - it specifically does not follow any of the cookie-related RFCs - (because browsers don't either). - - The algorithm used is identical to that used by Django version 1.9.10. - - .. versionadded:: 4.4.2 - """ - cookiedict = {} - for chunk in cookie.split(str(";")): - if str("=") in chunk: - key, val = chunk.split(str("="), 1) - else: - # Assume an empty name per - # https://bugzilla.mozilla.org/show_bug.cgi?id=169091 - key, val = str(""), chunk - key, val = key.strip(), val.strip() - if key or val: - # unquote using Python's algorithm. - cookiedict[key] = _unquote_cookie(val) - return cookiedict diff --git a/telegramer/include/tornado/ioloop.py b/telegramer/include/tornado/ioloop.py deleted file mode 100644 index 2cf8844..0000000 --- a/telegramer/include/tornado/ioloop.py +++ /dev/null @@ -1,944 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""An I/O event loop for non-blocking sockets. - -In Tornado 6.0, `.IOLoop` is a wrapper around the `asyncio` event -loop, with a slightly different interface for historical reasons. -Applications can use either the `.IOLoop` interface or the underlying -`asyncio` event loop directly (unless compatibility with older -versions of Tornado is desired, in which case `.IOLoop` must be used). - -Typical applications will use a single `IOLoop` object, accessed via -`IOLoop.current` class method. The `IOLoop.start` method (or -equivalently, `asyncio.AbstractEventLoop.run_forever`) should usually -be called at the end of the ``main()`` function. Atypical applications -may use more than one `IOLoop`, such as one `IOLoop` per thread, or -per `unittest` case. - -""" - -import asyncio -import concurrent.futures -import datetime -import functools -import logging -import numbers -import os -import sys -import time -import math -import random - -from tornado.concurrent import ( - Future, - is_future, - chain_future, - future_set_exc_info, - future_add_done_callback, -) -from tornado.log import app_log -from tornado.util import Configurable, TimeoutError, import_object - -import typing -from typing import Union, Any, Type, Optional, Callable, TypeVar, Tuple, Awaitable - -if typing.TYPE_CHECKING: - from typing import Dict, List # noqa: F401 - - from typing_extensions import Protocol -else: - Protocol = object - - -class _Selectable(Protocol): - def fileno(self) -> int: - pass - - def close(self) -> None: - pass - - -_T = TypeVar("_T") -_S = TypeVar("_S", bound=_Selectable) - - -class IOLoop(Configurable): - """An I/O event loop. - - As of Tornado 6.0, `IOLoop` is a wrapper around the `asyncio` event - loop. - - Example usage for a simple TCP server: - - .. testcode:: - - import errno - import functools - import socket - - import tornado.ioloop - from tornado.iostream import IOStream - - async def handle_connection(connection, address): - stream = IOStream(connection) - message = await stream.read_until_close() - print("message from client:", message.decode().strip()) - - def connection_ready(sock, fd, events): - while True: - try: - connection, address = sock.accept() - except BlockingIOError: - return - connection.setblocking(0) - io_loop = tornado.ioloop.IOLoop.current() - io_loop.spawn_callback(handle_connection, connection, address) - - if __name__ == '__main__': - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.setblocking(0) - sock.bind(("", 8888)) - sock.listen(128) - - io_loop = tornado.ioloop.IOLoop.current() - callback = functools.partial(connection_ready, sock) - io_loop.add_handler(sock.fileno(), callback, io_loop.READ) - io_loop.start() - - .. testoutput:: - :hide: - - By default, a newly-constructed `IOLoop` becomes the thread's current - `IOLoop`, unless there already is a current `IOLoop`. This behavior - can be controlled with the ``make_current`` argument to the `IOLoop` - constructor: if ``make_current=True``, the new `IOLoop` will always - try to become current and it raises an error if there is already a - current instance. If ``make_current=False``, the new `IOLoop` will - not try to become current. - - In general, an `IOLoop` cannot survive a fork or be shared across - processes in any way. When multiple processes are being used, each - process should create its own `IOLoop`, which also implies that - any objects which depend on the `IOLoop` (such as - `.AsyncHTTPClient`) must also be created in the child processes. - As a guideline, anything that starts processes (including the - `tornado.process` and `multiprocessing` modules) should do so as - early as possible, ideally the first thing the application does - after loading its configuration in ``main()``. - - .. versionchanged:: 4.2 - Added the ``make_current`` keyword argument to the `IOLoop` - constructor. - - .. versionchanged:: 5.0 - - Uses the `asyncio` event loop by default. The - ``IOLoop.configure`` method cannot be used on Python 3 except - to redundantly specify the `asyncio` event loop. - - """ - - # These constants were originally based on constants from the epoll module. - NONE = 0 - READ = 0x001 - WRITE = 0x004 - ERROR = 0x018 - - # In Python 3, _ioloop_for_asyncio maps from asyncio loops to IOLoops. - _ioloop_for_asyncio = dict() # type: Dict[asyncio.AbstractEventLoop, IOLoop] - - @classmethod - def configure( - cls, impl: "Union[None, str, Type[Configurable]]", **kwargs: Any - ) -> None: - if asyncio is not None: - from tornado.platform.asyncio import BaseAsyncIOLoop - - if isinstance(impl, str): - impl = import_object(impl) - if isinstance(impl, type) and not issubclass(impl, BaseAsyncIOLoop): - raise RuntimeError( - "only AsyncIOLoop is allowed when asyncio is available" - ) - super(IOLoop, cls).configure(impl, **kwargs) - - @staticmethod - def instance() -> "IOLoop": - """Deprecated alias for `IOLoop.current()`. - - .. versionchanged:: 5.0 - - Previously, this method returned a global singleton - `IOLoop`, in contrast with the per-thread `IOLoop` returned - by `current()`. In nearly all cases the two were the same - (when they differed, it was generally used from non-Tornado - threads to communicate back to the main thread's `IOLoop`). - This distinction is not present in `asyncio`, so in order - to facilitate integration with that package `instance()` - was changed to be an alias to `current()`. Applications - using the cross-thread communications aspect of - `instance()` should instead set their own global variable - to point to the `IOLoop` they want to use. - - .. deprecated:: 5.0 - """ - return IOLoop.current() - - def install(self) -> None: - """Deprecated alias for `make_current()`. - - .. versionchanged:: 5.0 - - Previously, this method would set this `IOLoop` as the - global singleton used by `IOLoop.instance()`. Now that - `instance()` is an alias for `current()`, `install()` - is an alias for `make_current()`. - - .. deprecated:: 5.0 - """ - self.make_current() - - @staticmethod - def clear_instance() -> None: - """Deprecated alias for `clear_current()`. - - .. versionchanged:: 5.0 - - Previously, this method would clear the `IOLoop` used as - the global singleton by `IOLoop.instance()`. Now that - `instance()` is an alias for `current()`, - `clear_instance()` is an alias for `clear_current()`. - - .. deprecated:: 5.0 - - """ - IOLoop.clear_current() - - @typing.overload - @staticmethod - def current() -> "IOLoop": - pass - - @typing.overload - @staticmethod - def current(instance: bool = True) -> Optional["IOLoop"]: # noqa: F811 - pass - - @staticmethod - def current(instance: bool = True) -> Optional["IOLoop"]: # noqa: F811 - """Returns the current thread's `IOLoop`. - - If an `IOLoop` is currently running or has been marked as - current by `make_current`, returns that instance. If there is - no current `IOLoop` and ``instance`` is true, creates one. - - .. versionchanged:: 4.1 - Added ``instance`` argument to control the fallback to - `IOLoop.instance()`. - .. versionchanged:: 5.0 - On Python 3, control of the current `IOLoop` is delegated - to `asyncio`, with this and other methods as pass-through accessors. - The ``instance`` argument now controls whether an `IOLoop` - is created automatically when there is none, instead of - whether we fall back to `IOLoop.instance()` (which is now - an alias for this method). ``instance=False`` is deprecated, - since even if we do not create an `IOLoop`, this method - may initialize the asyncio loop. - """ - try: - loop = asyncio.get_event_loop() - except (RuntimeError, AssertionError): - if not instance: - return None - raise - try: - return IOLoop._ioloop_for_asyncio[loop] - except KeyError: - if instance: - from tornado.platform.asyncio import AsyncIOMainLoop - - current = AsyncIOMainLoop(make_current=True) # type: Optional[IOLoop] - else: - current = None - return current - - def make_current(self) -> None: - """Makes this the `IOLoop` for the current thread. - - An `IOLoop` automatically becomes current for its thread - when it is started, but it is sometimes useful to call - `make_current` explicitly before starting the `IOLoop`, - so that code run at startup time can find the right - instance. - - .. versionchanged:: 4.1 - An `IOLoop` created while there is no current `IOLoop` - will automatically become current. - - .. versionchanged:: 5.0 - This method also sets the current `asyncio` event loop. - """ - # The asyncio event loops override this method. - raise NotImplementedError() - - @staticmethod - def clear_current() -> None: - """Clears the `IOLoop` for the current thread. - - Intended primarily for use by test frameworks in between tests. - - .. versionchanged:: 5.0 - This method also clears the current `asyncio` event loop. - """ - old = IOLoop.current(instance=False) - if old is not None: - old._clear_current_hook() - if asyncio is None: - IOLoop._current.instance = None - - def _clear_current_hook(self) -> None: - """Instance method called when an IOLoop ceases to be current. - - May be overridden by subclasses as a counterpart to make_current. - """ - pass - - @classmethod - def configurable_base(cls) -> Type[Configurable]: - return IOLoop - - @classmethod - def configurable_default(cls) -> Type[Configurable]: - from tornado.platform.asyncio import AsyncIOLoop - - return AsyncIOLoop - - def initialize(self, make_current: Optional[bool] = None) -> None: - if make_current is None: - if IOLoop.current(instance=False) is None: - self.make_current() - elif make_current: - current = IOLoop.current(instance=False) - # AsyncIO loops can already be current by this point. - if current is not None and current is not self: - raise RuntimeError("current IOLoop already exists") - self.make_current() - - def close(self, all_fds: bool = False) -> None: - """Closes the `IOLoop`, freeing any resources used. - - If ``all_fds`` is true, all file descriptors registered on the - IOLoop will be closed (not just the ones created by the - `IOLoop` itself). - - Many applications will only use a single `IOLoop` that runs for the - entire lifetime of the process. In that case closing the `IOLoop` - is not necessary since everything will be cleaned up when the - process exits. `IOLoop.close` is provided mainly for scenarios - such as unit tests, which create and destroy a large number of - ``IOLoops``. - - An `IOLoop` must be completely stopped before it can be closed. This - means that `IOLoop.stop()` must be called *and* `IOLoop.start()` must - be allowed to return before attempting to call `IOLoop.close()`. - Therefore the call to `close` will usually appear just after - the call to `start` rather than near the call to `stop`. - - .. versionchanged:: 3.1 - If the `IOLoop` implementation supports non-integer objects - for "file descriptors", those objects will have their - ``close`` method when ``all_fds`` is true. - """ - raise NotImplementedError() - - @typing.overload - def add_handler( - self, fd: int, handler: Callable[[int, int], None], events: int - ) -> None: - pass - - @typing.overload # noqa: F811 - def add_handler( - self, fd: _S, handler: Callable[[_S, int], None], events: int - ) -> None: - pass - - def add_handler( # noqa: F811 - self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int - ) -> None: - """Registers the given handler to receive the given events for ``fd``. - - The ``fd`` argument may either be an integer file descriptor or - a file-like object with a ``fileno()`` and ``close()`` method. - - The ``events`` argument is a bitwise or of the constants - ``IOLoop.READ``, ``IOLoop.WRITE``, and ``IOLoop.ERROR``. - - When an event occurs, ``handler(fd, events)`` will be run. - - .. versionchanged:: 4.0 - Added the ability to pass file-like objects in addition to - raw file descriptors. - """ - raise NotImplementedError() - - def update_handler(self, fd: Union[int, _Selectable], events: int) -> None: - """Changes the events we listen for ``fd``. - - .. versionchanged:: 4.0 - Added the ability to pass file-like objects in addition to - raw file descriptors. - """ - raise NotImplementedError() - - def remove_handler(self, fd: Union[int, _Selectable]) -> None: - """Stop listening for events on ``fd``. - - .. versionchanged:: 4.0 - Added the ability to pass file-like objects in addition to - raw file descriptors. - """ - raise NotImplementedError() - - def start(self) -> None: - """Starts the I/O loop. - - The loop will run until one of the callbacks calls `stop()`, which - will make the loop stop after the current event iteration completes. - """ - raise NotImplementedError() - - def _setup_logging(self) -> None: - """The IOLoop catches and logs exceptions, so it's - important that log output be visible. However, python's - default behavior for non-root loggers (prior to python - 3.2) is to print an unhelpful "no handlers could be - found" message rather than the actual log entry, so we - must explicitly configure logging if we've made it this - far without anything. - - This method should be called from start() in subclasses. - """ - if not any( - [ - logging.getLogger().handlers, - logging.getLogger("tornado").handlers, - logging.getLogger("tornado.application").handlers, - ] - ): - logging.basicConfig() - - def stop(self) -> None: - """Stop the I/O loop. - - If the event loop is not currently running, the next call to `start()` - will return immediately. - - Note that even after `stop` has been called, the `IOLoop` is not - completely stopped until `IOLoop.start` has also returned. - Some work that was scheduled before the call to `stop` may still - be run before the `IOLoop` shuts down. - """ - raise NotImplementedError() - - def run_sync(self, func: Callable, timeout: Optional[float] = None) -> Any: - """Starts the `IOLoop`, runs the given function, and stops the loop. - - The function must return either an awaitable object or - ``None``. If the function returns an awaitable object, the - `IOLoop` will run until the awaitable is resolved (and - `run_sync()` will return the awaitable's result). If it raises - an exception, the `IOLoop` will stop and the exception will be - re-raised to the caller. - - The keyword-only argument ``timeout`` may be used to set - a maximum duration for the function. If the timeout expires, - a `tornado.util.TimeoutError` is raised. - - This method is useful to allow asynchronous calls in a - ``main()`` function:: - - async def main(): - # do stuff... - - if __name__ == '__main__': - IOLoop.current().run_sync(main) - - .. versionchanged:: 4.3 - Returning a non-``None``, non-awaitable value is now an error. - - .. versionchanged:: 5.0 - If a timeout occurs, the ``func`` coroutine will be cancelled. - - """ - future_cell = [None] # type: List[Optional[Future]] - - def run() -> None: - try: - result = func() - if result is not None: - from tornado.gen import convert_yielded - - result = convert_yielded(result) - except Exception: - fut = Future() # type: Future[Any] - future_cell[0] = fut - future_set_exc_info(fut, sys.exc_info()) - else: - if is_future(result): - future_cell[0] = result - else: - fut = Future() - future_cell[0] = fut - fut.set_result(result) - assert future_cell[0] is not None - self.add_future(future_cell[0], lambda future: self.stop()) - - self.add_callback(run) - if timeout is not None: - - def timeout_callback() -> None: - # If we can cancel the future, do so and wait on it. If not, - # Just stop the loop and return with the task still pending. - # (If we neither cancel nor wait for the task, a warning - # will be logged). - assert future_cell[0] is not None - if not future_cell[0].cancel(): - self.stop() - - timeout_handle = self.add_timeout(self.time() + timeout, timeout_callback) - self.start() - if timeout is not None: - self.remove_timeout(timeout_handle) - assert future_cell[0] is not None - if future_cell[0].cancelled() or not future_cell[0].done(): - raise TimeoutError("Operation timed out after %s seconds" % timeout) - return future_cell[0].result() - - def time(self) -> float: - """Returns the current time according to the `IOLoop`'s clock. - - The return value is a floating-point number relative to an - unspecified time in the past. - - Historically, the IOLoop could be customized to use e.g. - `time.monotonic` instead of `time.time`, but this is not - currently supported and so this method is equivalent to - `time.time`. - - """ - return time.time() - - def add_timeout( - self, - deadline: Union[float, datetime.timedelta], - callback: Callable[..., None], - *args: Any, - **kwargs: Any - ) -> object: - """Runs the ``callback`` at the time ``deadline`` from the I/O loop. - - Returns an opaque handle that may be passed to - `remove_timeout` to cancel. - - ``deadline`` may be a number denoting a time (on the same - scale as `IOLoop.time`, normally `time.time`), or a - `datetime.timedelta` object for a deadline relative to the - current time. Since Tornado 4.0, `call_later` is a more - convenient alternative for the relative case since it does not - require a timedelta object. - - Note that it is not safe to call `add_timeout` from other threads. - Instead, you must use `add_callback` to transfer control to the - `IOLoop`'s thread, and then call `add_timeout` from there. - - Subclasses of IOLoop must implement either `add_timeout` or - `call_at`; the default implementations of each will call - the other. `call_at` is usually easier to implement, but - subclasses that wish to maintain compatibility with Tornado - versions prior to 4.0 must use `add_timeout` instead. - - .. versionchanged:: 4.0 - Now passes through ``*args`` and ``**kwargs`` to the callback. - """ - if isinstance(deadline, numbers.Real): - return self.call_at(deadline, callback, *args, **kwargs) - elif isinstance(deadline, datetime.timedelta): - return self.call_at( - self.time() + deadline.total_seconds(), callback, *args, **kwargs - ) - else: - raise TypeError("Unsupported deadline %r" % deadline) - - def call_later( - self, delay: float, callback: Callable[..., None], *args: Any, **kwargs: Any - ) -> object: - """Runs the ``callback`` after ``delay`` seconds have passed. - - Returns an opaque handle that may be passed to `remove_timeout` - to cancel. Note that unlike the `asyncio` method of the same - name, the returned object does not have a ``cancel()`` method. - - See `add_timeout` for comments on thread-safety and subclassing. - - .. versionadded:: 4.0 - """ - return self.call_at(self.time() + delay, callback, *args, **kwargs) - - def call_at( - self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any - ) -> object: - """Runs the ``callback`` at the absolute time designated by ``when``. - - ``when`` must be a number using the same reference point as - `IOLoop.time`. - - Returns an opaque handle that may be passed to `remove_timeout` - to cancel. Note that unlike the `asyncio` method of the same - name, the returned object does not have a ``cancel()`` method. - - See `add_timeout` for comments on thread-safety and subclassing. - - .. versionadded:: 4.0 - """ - return self.add_timeout(when, callback, *args, **kwargs) - - def remove_timeout(self, timeout: object) -> None: - """Cancels a pending timeout. - - The argument is a handle as returned by `add_timeout`. It is - safe to call `remove_timeout` even if the callback has already - been run. - """ - raise NotImplementedError() - - def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None: - """Calls the given callback on the next I/O loop iteration. - - It is safe to call this method from any thread at any time, - except from a signal handler. Note that this is the **only** - method in `IOLoop` that makes this thread-safety guarantee; all - other interaction with the `IOLoop` must be done from that - `IOLoop`'s thread. `add_callback()` may be used to transfer - control from other threads to the `IOLoop`'s thread. - - To add a callback from a signal handler, see - `add_callback_from_signal`. - """ - raise NotImplementedError() - - def add_callback_from_signal( - self, callback: Callable, *args: Any, **kwargs: Any - ) -> None: - """Calls the given callback on the next I/O loop iteration. - - Safe for use from a Python signal handler; should not be used - otherwise. - """ - raise NotImplementedError() - - def spawn_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None: - """Calls the given callback on the next IOLoop iteration. - - As of Tornado 6.0, this method is equivalent to `add_callback`. - - .. versionadded:: 4.0 - """ - self.add_callback(callback, *args, **kwargs) - - def add_future( - self, - future: "Union[Future[_T], concurrent.futures.Future[_T]]", - callback: Callable[["Future[_T]"], None], - ) -> None: - """Schedules a callback on the ``IOLoop`` when the given - `.Future` is finished. - - The callback is invoked with one argument, the - `.Future`. - - This method only accepts `.Future` objects and not other - awaitables (unlike most of Tornado where the two are - interchangeable). - """ - if isinstance(future, Future): - # Note that we specifically do not want the inline behavior of - # tornado.concurrent.future_add_done_callback. We always want - # this callback scheduled on the next IOLoop iteration (which - # asyncio.Future always does). - # - # Wrap the callback in self._run_callback so we control - # the error logging (i.e. it goes to tornado.log.app_log - # instead of asyncio's log). - future.add_done_callback( - lambda f: self._run_callback(functools.partial(callback, future)) - ) - else: - assert is_future(future) - # For concurrent futures, we use self.add_callback, so - # it's fine if future_add_done_callback inlines that call. - future_add_done_callback( - future, lambda f: self.add_callback(callback, future) - ) - - def run_in_executor( - self, - executor: Optional[concurrent.futures.Executor], - func: Callable[..., _T], - *args: Any - ) -> Awaitable[_T]: - """Runs a function in a ``concurrent.futures.Executor``. If - ``executor`` is ``None``, the IO loop's default executor will be used. - - Use `functools.partial` to pass keyword arguments to ``func``. - - .. versionadded:: 5.0 - """ - if executor is None: - if not hasattr(self, "_executor"): - from tornado.process import cpu_count - - self._executor = concurrent.futures.ThreadPoolExecutor( - max_workers=(cpu_count() * 5) - ) # type: concurrent.futures.Executor - executor = self._executor - c_future = executor.submit(func, *args) - # Concurrent Futures are not usable with await. Wrap this in a - # Tornado Future instead, using self.add_future for thread-safety. - t_future = Future() # type: Future[_T] - self.add_future(c_future, lambda f: chain_future(f, t_future)) - return t_future - - def set_default_executor(self, executor: concurrent.futures.Executor) -> None: - """Sets the default executor to use with :meth:`run_in_executor`. - - .. versionadded:: 5.0 - """ - self._executor = executor - - def _run_callback(self, callback: Callable[[], Any]) -> None: - """Runs a callback with error handling. - - .. versionchanged:: 6.0 - - CancelledErrors are no longer logged. - """ - try: - ret = callback() - if ret is not None: - from tornado import gen - - # Functions that return Futures typically swallow all - # exceptions and store them in the Future. If a Future - # makes it out to the IOLoop, ensure its exception (if any) - # gets logged too. - try: - ret = gen.convert_yielded(ret) - except gen.BadYieldError: - # It's not unusual for add_callback to be used with - # methods returning a non-None and non-yieldable - # result, which should just be ignored. - pass - else: - self.add_future(ret, self._discard_future_result) - except asyncio.CancelledError: - pass - except Exception: - app_log.error("Exception in callback %r", callback, exc_info=True) - - def _discard_future_result(self, future: Future) -> None: - """Avoid unhandled-exception warnings from spawned coroutines.""" - future.result() - - def split_fd( - self, fd: Union[int, _Selectable] - ) -> Tuple[int, Union[int, _Selectable]]: - # """Returns an (fd, obj) pair from an ``fd`` parameter. - - # We accept both raw file descriptors and file-like objects as - # input to `add_handler` and related methods. When a file-like - # object is passed, we must retain the object itself so we can - # close it correctly when the `IOLoop` shuts down, but the - # poller interfaces favor file descriptors (they will accept - # file-like objects and call ``fileno()`` for you, but they - # always return the descriptor itself). - - # This method is provided for use by `IOLoop` subclasses and should - # not generally be used by application code. - - # .. versionadded:: 4.0 - # """ - if isinstance(fd, int): - return fd, fd - return fd.fileno(), fd - - def close_fd(self, fd: Union[int, _Selectable]) -> None: - # """Utility method to close an ``fd``. - - # If ``fd`` is a file-like object, we close it directly; otherwise - # we use `os.close`. - - # This method is provided for use by `IOLoop` subclasses (in - # implementations of ``IOLoop.close(all_fds=True)`` and should - # not generally be used by application code. - - # .. versionadded:: 4.0 - # """ - try: - if isinstance(fd, int): - os.close(fd) - else: - fd.close() - except OSError: - pass - - -class _Timeout(object): - """An IOLoop timeout, a UNIX timestamp and a callback""" - - # Reduce memory overhead when there are lots of pending callbacks - __slots__ = ["deadline", "callback", "tdeadline"] - - def __init__( - self, deadline: float, callback: Callable[[], None], io_loop: IOLoop - ) -> None: - if not isinstance(deadline, numbers.Real): - raise TypeError("Unsupported deadline %r" % deadline) - self.deadline = deadline - self.callback = callback - self.tdeadline = ( - deadline, - next(io_loop._timeout_counter), - ) # type: Tuple[float, int] - - # Comparison methods to sort by deadline, with object id as a tiebreaker - # to guarantee a consistent ordering. The heapq module uses __le__ - # in python2.5, and __lt__ in 2.6+ (sort() and most other comparisons - # use __lt__). - def __lt__(self, other: "_Timeout") -> bool: - return self.tdeadline < other.tdeadline - - def __le__(self, other: "_Timeout") -> bool: - return self.tdeadline <= other.tdeadline - - -class PeriodicCallback(object): - """Schedules the given callback to be called periodically. - - The callback is called every ``callback_time`` milliseconds. - Note that the timeout is given in milliseconds, while most other - time-related functions in Tornado use seconds. - - If ``jitter`` is specified, each callback time will be randomly selected - within a window of ``jitter * callback_time`` milliseconds. - Jitter can be used to reduce alignment of events with similar periods. - A jitter of 0.1 means allowing a 10% variation in callback time. - The window is centered on ``callback_time`` so the total number of calls - within a given interval should not be significantly affected by adding - jitter. - - If the callback runs for longer than ``callback_time`` milliseconds, - subsequent invocations will be skipped to get back on schedule. - - `start` must be called after the `PeriodicCallback` is created. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - - .. versionchanged:: 5.1 - The ``jitter`` argument is added. - """ - - def __init__( - self, callback: Callable[[], None], callback_time: float, jitter: float = 0 - ) -> None: - self.callback = callback - if callback_time <= 0: - raise ValueError("Periodic callback must have a positive callback_time") - self.callback_time = callback_time - self.jitter = jitter - self._running = False - self._timeout = None # type: object - - def start(self) -> None: - """Starts the timer.""" - # Looking up the IOLoop here allows to first instantiate the - # PeriodicCallback in another thread, then start it using - # IOLoop.add_callback(). - self.io_loop = IOLoop.current() - self._running = True - self._next_timeout = self.io_loop.time() - self._schedule_next() - - def stop(self) -> None: - """Stops the timer.""" - self._running = False - if self._timeout is not None: - self.io_loop.remove_timeout(self._timeout) - self._timeout = None - - def is_running(self) -> bool: - """Returns ``True`` if this `.PeriodicCallback` has been started. - - .. versionadded:: 4.1 - """ - return self._running - - def _run(self) -> None: - if not self._running: - return - try: - return self.callback() - except Exception: - app_log.error("Exception in callback %r", self.callback, exc_info=True) - finally: - self._schedule_next() - - def _schedule_next(self) -> None: - if self._running: - self._update_next(self.io_loop.time()) - self._timeout = self.io_loop.add_timeout(self._next_timeout, self._run) - - def _update_next(self, current_time: float) -> None: - callback_time_sec = self.callback_time / 1000.0 - if self.jitter: - # apply jitter fraction - callback_time_sec *= 1 + (self.jitter * (random.random() - 0.5)) - if self._next_timeout <= current_time: - # The period should be measured from the start of one call - # to the start of the next. If one call takes too long, - # skip cycles to get back to a multiple of the original - # schedule. - self._next_timeout += ( - math.floor((current_time - self._next_timeout) / callback_time_sec) + 1 - ) * callback_time_sec - else: - # If the clock moved backwards, ensure we advance the next - # timeout instead of recomputing the same value again. - # This may result in long gaps between callbacks if the - # clock jumps backwards by a lot, but the far more common - # scenario is a small NTP adjustment that should just be - # ignored. - # - # Note that on some systems if time.time() runs slower - # than time.monotonic() (most common on windows), we - # effectively experience a small backwards time jump on - # every iteration because PeriodicCallback uses - # time.time() while asyncio schedules callbacks using - # time.monotonic(). - # https://github.com/tornadoweb/tornado/issues/2333 - self._next_timeout += callback_time_sec diff --git a/telegramer/include/tornado/iostream.py b/telegramer/include/tornado/iostream.py deleted file mode 100644 index 19c5485..0000000 --- a/telegramer/include/tornado/iostream.py +++ /dev/null @@ -1,1660 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Utility classes to write to and read from non-blocking files and sockets. - -Contents: - -* `BaseIOStream`: Generic interface for reading and writing. -* `IOStream`: Implementation of BaseIOStream using non-blocking sockets. -* `SSLIOStream`: SSL-aware version of IOStream. -* `PipeIOStream`: Pipe-based IOStream implementation. -""" - -import asyncio -import collections -import errno -import io -import numbers -import os -import socket -import ssl -import sys -import re - -from tornado.concurrent import Future, future_set_result_unless_cancelled -from tornado import ioloop -from tornado.log import gen_log -from tornado.netutil import ssl_wrap_socket, _client_ssl_defaults, _server_ssl_defaults -from tornado.util import errno_from_exception - -import typing -from typing import ( - Union, - Optional, - Awaitable, - Callable, - Pattern, - Any, - Dict, - TypeVar, - Tuple, -) -from types import TracebackType - -if typing.TYPE_CHECKING: - from typing import Deque, List, Type # noqa: F401 - -_IOStreamType = TypeVar("_IOStreamType", bound="IOStream") - -# These errnos indicate that a connection has been abruptly terminated. -# They should be caught and handled less noisily than other errors. -_ERRNO_CONNRESET = (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE, errno.ETIMEDOUT) - -if hasattr(errno, "WSAECONNRESET"): - _ERRNO_CONNRESET += ( # type: ignore - errno.WSAECONNRESET, # type: ignore - errno.WSAECONNABORTED, # type: ignore - errno.WSAETIMEDOUT, # type: ignore - ) - -if sys.platform == "darwin": - # OSX appears to have a race condition that causes send(2) to return - # EPROTOTYPE if called while a socket is being torn down: - # http://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ - # Since the socket is being closed anyway, treat this as an ECONNRESET - # instead of an unexpected error. - _ERRNO_CONNRESET += (errno.EPROTOTYPE,) # type: ignore - -_WINDOWS = sys.platform.startswith("win") - - -class StreamClosedError(IOError): - """Exception raised by `IOStream` methods when the stream is closed. - - Note that the close callback is scheduled to run *after* other - callbacks on the stream (to allow for buffered data to be processed), - so you may see this error before you see the close callback. - - The ``real_error`` attribute contains the underlying error that caused - the stream to close (if any). - - .. versionchanged:: 4.3 - Added the ``real_error`` attribute. - """ - - def __init__(self, real_error: Optional[BaseException] = None) -> None: - super().__init__("Stream is closed") - self.real_error = real_error - - -class UnsatisfiableReadError(Exception): - """Exception raised when a read cannot be satisfied. - - Raised by ``read_until`` and ``read_until_regex`` with a ``max_bytes`` - argument. - """ - - pass - - -class StreamBufferFullError(Exception): - """Exception raised by `IOStream` methods when the buffer is full. - """ - - -class _StreamBuffer(object): - """ - A specialized buffer that tries to avoid copies when large pieces - of data are encountered. - """ - - def __init__(self) -> None: - # A sequence of (False, bytearray) and (True, memoryview) objects - self._buffers = ( - collections.deque() - ) # type: Deque[Tuple[bool, Union[bytearray, memoryview]]] - # Position in the first buffer - self._first_pos = 0 - self._size = 0 - - def __len__(self) -> int: - return self._size - - # Data above this size will be appended separately instead - # of extending an existing bytearray - _large_buf_threshold = 2048 - - def append(self, data: Union[bytes, bytearray, memoryview]) -> None: - """ - Append the given piece of data (should be a buffer-compatible object). - """ - size = len(data) - if size > self._large_buf_threshold: - if not isinstance(data, memoryview): - data = memoryview(data) - self._buffers.append((True, data)) - elif size > 0: - if self._buffers: - is_memview, b = self._buffers[-1] - new_buf = is_memview or len(b) >= self._large_buf_threshold - else: - new_buf = True - if new_buf: - self._buffers.append((False, bytearray(data))) - else: - b += data # type: ignore - - self._size += size - - def peek(self, size: int) -> memoryview: - """ - Get a view over at most ``size`` bytes (possibly fewer) at the - current buffer position. - """ - assert size > 0 - try: - is_memview, b = self._buffers[0] - except IndexError: - return memoryview(b"") - - pos = self._first_pos - if is_memview: - return typing.cast(memoryview, b[pos : pos + size]) - else: - return memoryview(b)[pos : pos + size] - - def advance(self, size: int) -> None: - """ - Advance the current buffer position by ``size`` bytes. - """ - assert 0 < size <= self._size - self._size -= size - pos = self._first_pos - - buffers = self._buffers - while buffers and size > 0: - is_large, b = buffers[0] - b_remain = len(b) - size - pos - if b_remain <= 0: - buffers.popleft() - size -= len(b) - pos - pos = 0 - elif is_large: - pos += size - size = 0 - else: - # Amortized O(1) shrink for Python 2 - pos += size - if len(b) <= 2 * pos: - del typing.cast(bytearray, b)[:pos] - pos = 0 - size = 0 - - assert size == 0 - self._first_pos = pos - - -class BaseIOStream(object): - """A utility class to write to and read from a non-blocking file or socket. - - We support a non-blocking ``write()`` and a family of ``read_*()`` - methods. When the operation completes, the ``Awaitable`` will resolve - with the data read (or ``None`` for ``write()``). All outstanding - ``Awaitables`` will resolve with a `StreamClosedError` when the - stream is closed; `.BaseIOStream.set_close_callback` can also be used - to be notified of a closed stream. - - When a stream is closed due to an error, the IOStream's ``error`` - attribute contains the exception object. - - Subclasses must implement `fileno`, `close_fd`, `write_to_fd`, - `read_from_fd`, and optionally `get_fd_error`. - - """ - - def __init__( - self, - max_buffer_size: Optional[int] = None, - read_chunk_size: Optional[int] = None, - max_write_buffer_size: Optional[int] = None, - ) -> None: - """`BaseIOStream` constructor. - - :arg max_buffer_size: Maximum amount of incoming data to buffer; - defaults to 100MB. - :arg read_chunk_size: Amount of data to read at one time from the - underlying transport; defaults to 64KB. - :arg max_write_buffer_size: Amount of outgoing data to buffer; - defaults to unlimited. - - .. versionchanged:: 4.0 - Add the ``max_write_buffer_size`` parameter. Changed default - ``read_chunk_size`` to 64KB. - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been - removed. - """ - self.io_loop = ioloop.IOLoop.current() - self.max_buffer_size = max_buffer_size or 104857600 - # A chunk size that is too close to max_buffer_size can cause - # spurious failures. - self.read_chunk_size = min(read_chunk_size or 65536, self.max_buffer_size // 2) - self.max_write_buffer_size = max_write_buffer_size - self.error = None # type: Optional[BaseException] - self._read_buffer = bytearray() - self._read_buffer_pos = 0 - self._read_buffer_size = 0 - self._user_read_buffer = False - self._after_user_read_buffer = None # type: Optional[bytearray] - self._write_buffer = _StreamBuffer() - self._total_write_index = 0 - self._total_write_done_index = 0 - self._read_delimiter = None # type: Optional[bytes] - self._read_regex = None # type: Optional[Pattern] - self._read_max_bytes = None # type: Optional[int] - self._read_bytes = None # type: Optional[int] - self._read_partial = False - self._read_until_close = False - self._read_future = None # type: Optional[Future] - self._write_futures = ( - collections.deque() - ) # type: Deque[Tuple[int, Future[None]]] - self._close_callback = None # type: Optional[Callable[[], None]] - self._connect_future = None # type: Optional[Future[IOStream]] - # _ssl_connect_future should be defined in SSLIOStream - # but it's here so we can clean it up in _signal_closed - # TODO: refactor that so subclasses can add additional futures - # to be cancelled. - self._ssl_connect_future = None # type: Optional[Future[SSLIOStream]] - self._connecting = False - self._state = None # type: Optional[int] - self._closed = False - - def fileno(self) -> Union[int, ioloop._Selectable]: - """Returns the file descriptor for this stream.""" - raise NotImplementedError() - - def close_fd(self) -> None: - """Closes the file underlying this stream. - - ``close_fd`` is called by `BaseIOStream` and should not be called - elsewhere; other users should call `close` instead. - """ - raise NotImplementedError() - - def write_to_fd(self, data: memoryview) -> int: - """Attempts to write ``data`` to the underlying file. - - Returns the number of bytes written. - """ - raise NotImplementedError() - - def read_from_fd(self, buf: Union[bytearray, memoryview]) -> Optional[int]: - """Attempts to read from the underlying file. - - Reads up to ``len(buf)`` bytes, storing them in the buffer. - Returns the number of bytes read. Returns None if there was - nothing to read (the socket returned `~errno.EWOULDBLOCK` or - equivalent), and zero on EOF. - - .. versionchanged:: 5.0 - - Interface redesigned to take a buffer and return a number - of bytes instead of a freshly-allocated object. - """ - raise NotImplementedError() - - def get_fd_error(self) -> Optional[Exception]: - """Returns information about any error on the underlying file. - - This method is called after the `.IOLoop` has signaled an error on the - file descriptor, and should return an Exception (such as `socket.error` - with additional information, or None if no such information is - available. - """ - return None - - def read_until_regex( - self, regex: bytes, max_bytes: Optional[int] = None - ) -> Awaitable[bytes]: - """Asynchronously read until we have matched the given regex. - - The result includes the data that matches the regex and anything - that came before it. - - If ``max_bytes`` is not None, the connection will be closed - if more than ``max_bytes`` bytes have been read and the regex is - not satisfied. - - .. versionchanged:: 4.0 - Added the ``max_bytes`` argument. The ``callback`` argument is - now optional and a `.Future` will be returned if it is omitted. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - - """ - future = self._start_read() - self._read_regex = re.compile(regex) - self._read_max_bytes = max_bytes - try: - self._try_inline_read() - except UnsatisfiableReadError as e: - # Handle this the same way as in _handle_events. - gen_log.info("Unsatisfiable read, closing connection: %s" % e) - self.close(exc_info=e) - return future - except: - # Ensure that the future doesn't log an error because its - # failure was never examined. - future.add_done_callback(lambda f: f.exception()) - raise - return future - - def read_until( - self, delimiter: bytes, max_bytes: Optional[int] = None - ) -> Awaitable[bytes]: - """Asynchronously read until we have found the given delimiter. - - The result includes all the data read including the delimiter. - - If ``max_bytes`` is not None, the connection will be closed - if more than ``max_bytes`` bytes have been read and the delimiter - is not found. - - .. versionchanged:: 4.0 - Added the ``max_bytes`` argument. The ``callback`` argument is - now optional and a `.Future` will be returned if it is omitted. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - """ - future = self._start_read() - self._read_delimiter = delimiter - self._read_max_bytes = max_bytes - try: - self._try_inline_read() - except UnsatisfiableReadError as e: - # Handle this the same way as in _handle_events. - gen_log.info("Unsatisfiable read, closing connection: %s" % e) - self.close(exc_info=e) - return future - except: - future.add_done_callback(lambda f: f.exception()) - raise - return future - - def read_bytes(self, num_bytes: int, partial: bool = False) -> Awaitable[bytes]: - """Asynchronously read a number of bytes. - - If ``partial`` is true, data is returned as soon as we have - any bytes to return (but never more than ``num_bytes``) - - .. versionchanged:: 4.0 - Added the ``partial`` argument. The callback argument is now - optional and a `.Future` will be returned if it is omitted. - - .. versionchanged:: 6.0 - - The ``callback`` and ``streaming_callback`` arguments have - been removed. Use the returned `.Future` (and - ``partial=True`` for ``streaming_callback``) instead. - - """ - future = self._start_read() - assert isinstance(num_bytes, numbers.Integral) - self._read_bytes = num_bytes - self._read_partial = partial - try: - self._try_inline_read() - except: - future.add_done_callback(lambda f: f.exception()) - raise - return future - - def read_into(self, buf: bytearray, partial: bool = False) -> Awaitable[int]: - """Asynchronously read a number of bytes. - - ``buf`` must be a writable buffer into which data will be read. - - If ``partial`` is true, the callback is run as soon as any bytes - have been read. Otherwise, it is run when the ``buf`` has been - entirely filled with read data. - - .. versionadded:: 5.0 - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - - """ - future = self._start_read() - - # First copy data already in read buffer - available_bytes = self._read_buffer_size - n = len(buf) - if available_bytes >= n: - end = self._read_buffer_pos + n - buf[:] = memoryview(self._read_buffer)[self._read_buffer_pos : end] - del self._read_buffer[:end] - self._after_user_read_buffer = self._read_buffer - elif available_bytes > 0: - buf[:available_bytes] = memoryview(self._read_buffer)[ - self._read_buffer_pos : - ] - - # Set up the supplied buffer as our temporary read buffer. - # The original (if it had any data remaining) has been - # saved for later. - self._user_read_buffer = True - self._read_buffer = buf - self._read_buffer_pos = 0 - self._read_buffer_size = available_bytes - self._read_bytes = n - self._read_partial = partial - - try: - self._try_inline_read() - except: - future.add_done_callback(lambda f: f.exception()) - raise - return future - - def read_until_close(self) -> Awaitable[bytes]: - """Asynchronously reads all data from the socket until it is closed. - - This will buffer all available data until ``max_buffer_size`` - is reached. If flow control or cancellation are desired, use a - loop with `read_bytes(partial=True) <.read_bytes>` instead. - - .. versionchanged:: 4.0 - The callback argument is now optional and a `.Future` will - be returned if it is omitted. - - .. versionchanged:: 6.0 - - The ``callback`` and ``streaming_callback`` arguments have - been removed. Use the returned `.Future` (and `read_bytes` - with ``partial=True`` for ``streaming_callback``) instead. - - """ - future = self._start_read() - if self.closed(): - self._finish_read(self._read_buffer_size, False) - return future - self._read_until_close = True - try: - self._try_inline_read() - except: - future.add_done_callback(lambda f: f.exception()) - raise - return future - - def write(self, data: Union[bytes, memoryview]) -> "Future[None]": - """Asynchronously write the given data to this stream. - - This method returns a `.Future` that resolves (with a result - of ``None``) when the write has been completed. - - The ``data`` argument may be of type `bytes` or `memoryview`. - - .. versionchanged:: 4.0 - Now returns a `.Future` if no callback is given. - - .. versionchanged:: 4.5 - Added support for `memoryview` arguments. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - - """ - self._check_closed() - if data: - if ( - self.max_write_buffer_size is not None - and len(self._write_buffer) + len(data) > self.max_write_buffer_size - ): - raise StreamBufferFullError("Reached maximum write buffer size") - self._write_buffer.append(data) - self._total_write_index += len(data) - future = Future() # type: Future[None] - future.add_done_callback(lambda f: f.exception()) - self._write_futures.append((self._total_write_index, future)) - if not self._connecting: - self._handle_write() - if self._write_buffer: - self._add_io_state(self.io_loop.WRITE) - self._maybe_add_error_listener() - return future - - def set_close_callback(self, callback: Optional[Callable[[], None]]) -> None: - """Call the given callback when the stream is closed. - - This mostly is not necessary for applications that use the - `.Future` interface; all outstanding ``Futures`` will resolve - with a `StreamClosedError` when the stream is closed. However, - it is still useful as a way to signal that the stream has been - closed while no other read or write is in progress. - - Unlike other callback-based interfaces, ``set_close_callback`` - was not removed in Tornado 6.0. - """ - self._close_callback = callback - self._maybe_add_error_listener() - - def close( - self, - exc_info: Union[ - None, - bool, - BaseException, - Tuple[ - "Optional[Type[BaseException]]", - Optional[BaseException], - Optional[TracebackType], - ], - ] = False, - ) -> None: - """Close this stream. - - If ``exc_info`` is true, set the ``error`` attribute to the current - exception from `sys.exc_info` (or if ``exc_info`` is a tuple, - use that instead of `sys.exc_info`). - """ - if not self.closed(): - if exc_info: - if isinstance(exc_info, tuple): - self.error = exc_info[1] - elif isinstance(exc_info, BaseException): - self.error = exc_info - else: - exc_info = sys.exc_info() - if any(exc_info): - self.error = exc_info[1] - if self._read_until_close: - self._read_until_close = False - self._finish_read(self._read_buffer_size, False) - elif self._read_future is not None: - # resolve reads that are pending and ready to complete - try: - pos = self._find_read_pos() - except UnsatisfiableReadError: - pass - else: - if pos is not None: - self._read_from_buffer(pos) - if self._state is not None: - self.io_loop.remove_handler(self.fileno()) - self._state = None - self.close_fd() - self._closed = True - self._signal_closed() - - def _signal_closed(self) -> None: - futures = [] # type: List[Future] - if self._read_future is not None: - futures.append(self._read_future) - self._read_future = None - futures += [future for _, future in self._write_futures] - self._write_futures.clear() - if self._connect_future is not None: - futures.append(self._connect_future) - self._connect_future = None - for future in futures: - if not future.done(): - future.set_exception(StreamClosedError(real_error=self.error)) - # Reference the exception to silence warnings. Annoyingly, - # this raises if the future was cancelled, but just - # returns any other error. - try: - future.exception() - except asyncio.CancelledError: - pass - if self._ssl_connect_future is not None: - # _ssl_connect_future expects to see the real exception (typically - # an ssl.SSLError), not just StreamClosedError. - if not self._ssl_connect_future.done(): - if self.error is not None: - self._ssl_connect_future.set_exception(self.error) - else: - self._ssl_connect_future.set_exception(StreamClosedError()) - self._ssl_connect_future.exception() - self._ssl_connect_future = None - if self._close_callback is not None: - cb = self._close_callback - self._close_callback = None - self.io_loop.add_callback(cb) - # Clear the buffers so they can be cleared immediately even - # if the IOStream object is kept alive by a reference cycle. - # TODO: Clear the read buffer too; it currently breaks some tests. - self._write_buffer = None # type: ignore - - def reading(self) -> bool: - """Returns ``True`` if we are currently reading from the stream.""" - return self._read_future is not None - - def writing(self) -> bool: - """Returns ``True`` if we are currently writing to the stream.""" - return bool(self._write_buffer) - - def closed(self) -> bool: - """Returns ``True`` if the stream has been closed.""" - return self._closed - - def set_nodelay(self, value: bool) -> None: - """Sets the no-delay flag for this stream. - - By default, data written to TCP streams may be held for a time - to make the most efficient use of bandwidth (according to - Nagle's algorithm). The no-delay flag requests that data be - written as soon as possible, even if doing so would consume - additional bandwidth. - - This flag is currently defined only for TCP-based ``IOStreams``. - - .. versionadded:: 3.1 - """ - pass - - def _handle_connect(self) -> None: - raise NotImplementedError() - - def _handle_events(self, fd: Union[int, ioloop._Selectable], events: int) -> None: - if self.closed(): - gen_log.warning("Got events for closed stream %s", fd) - return - try: - if self._connecting: - # Most IOLoops will report a write failed connect - # with the WRITE event, but SelectIOLoop reports a - # READ as well so we must check for connecting before - # either. - self._handle_connect() - if self.closed(): - return - if events & self.io_loop.READ: - self._handle_read() - if self.closed(): - return - if events & self.io_loop.WRITE: - self._handle_write() - if self.closed(): - return - if events & self.io_loop.ERROR: - self.error = self.get_fd_error() - # We may have queued up a user callback in _handle_read or - # _handle_write, so don't close the IOStream until those - # callbacks have had a chance to run. - self.io_loop.add_callback(self.close) - return - state = self.io_loop.ERROR - if self.reading(): - state |= self.io_loop.READ - if self.writing(): - state |= self.io_loop.WRITE - if state == self.io_loop.ERROR and self._read_buffer_size == 0: - # If the connection is idle, listen for reads too so - # we can tell if the connection is closed. If there is - # data in the read buffer we won't run the close callback - # yet anyway, so we don't need to listen in this case. - state |= self.io_loop.READ - if state != self._state: - assert ( - self._state is not None - ), "shouldn't happen: _handle_events without self._state" - self._state = state - self.io_loop.update_handler(self.fileno(), self._state) - except UnsatisfiableReadError as e: - gen_log.info("Unsatisfiable read, closing connection: %s" % e) - self.close(exc_info=e) - except Exception as e: - gen_log.error("Uncaught exception, closing connection.", exc_info=True) - self.close(exc_info=e) - raise - - def _read_to_buffer_loop(self) -> Optional[int]: - # This method is called from _handle_read and _try_inline_read. - if self._read_bytes is not None: - target_bytes = self._read_bytes # type: Optional[int] - elif self._read_max_bytes is not None: - target_bytes = self._read_max_bytes - elif self.reading(): - # For read_until without max_bytes, or - # read_until_close, read as much as we can before - # scanning for the delimiter. - target_bytes = None - else: - target_bytes = 0 - next_find_pos = 0 - while not self.closed(): - # Read from the socket until we get EWOULDBLOCK or equivalent. - # SSL sockets do some internal buffering, and if the data is - # sitting in the SSL object's buffer select() and friends - # can't see it; the only way to find out if it's there is to - # try to read it. - if self._read_to_buffer() == 0: - break - - # If we've read all the bytes we can use, break out of - # this loop. - - # If we've reached target_bytes, we know we're done. - if target_bytes is not None and self._read_buffer_size >= target_bytes: - break - - # Otherwise, we need to call the more expensive find_read_pos. - # It's inefficient to do this on every read, so instead - # do it on the first read and whenever the read buffer - # size has doubled. - if self._read_buffer_size >= next_find_pos: - pos = self._find_read_pos() - if pos is not None: - return pos - next_find_pos = self._read_buffer_size * 2 - return self._find_read_pos() - - def _handle_read(self) -> None: - try: - pos = self._read_to_buffer_loop() - except UnsatisfiableReadError: - raise - except asyncio.CancelledError: - raise - except Exception as e: - gen_log.warning("error on read: %s" % e) - self.close(exc_info=e) - return - if pos is not None: - self._read_from_buffer(pos) - - def _start_read(self) -> Future: - if self._read_future is not None: - # It is an error to start a read while a prior read is unresolved. - # However, if the prior read is unresolved because the stream was - # closed without satisfying it, it's better to raise - # StreamClosedError instead of AssertionError. In particular, this - # situation occurs in harmless situations in http1connection.py and - # an AssertionError would be logged noisily. - # - # On the other hand, it is legal to start a new read while the - # stream is closed, in case the read can be satisfied from the - # read buffer. So we only want to check the closed status of the - # stream if we need to decide what kind of error to raise for - # "already reading". - # - # These conditions have proven difficult to test; we have no - # unittests that reliably verify this behavior so be careful - # when making changes here. See #2651 and #2719. - self._check_closed() - assert self._read_future is None, "Already reading" - self._read_future = Future() - return self._read_future - - def _finish_read(self, size: int, streaming: bool) -> None: - if self._user_read_buffer: - self._read_buffer = self._after_user_read_buffer or bytearray() - self._after_user_read_buffer = None - self._read_buffer_pos = 0 - self._read_buffer_size = len(self._read_buffer) - self._user_read_buffer = False - result = size # type: Union[int, bytes] - else: - result = self._consume(size) - if self._read_future is not None: - future = self._read_future - self._read_future = None - future_set_result_unless_cancelled(future, result) - self._maybe_add_error_listener() - - def _try_inline_read(self) -> None: - """Attempt to complete the current read operation from buffered data. - - If the read can be completed without blocking, schedules the - read callback on the next IOLoop iteration; otherwise starts - listening for reads on the socket. - """ - # See if we've already got the data from a previous read - pos = self._find_read_pos() - if pos is not None: - self._read_from_buffer(pos) - return - self._check_closed() - pos = self._read_to_buffer_loop() - if pos is not None: - self._read_from_buffer(pos) - return - # We couldn't satisfy the read inline, so make sure we're - # listening for new data unless the stream is closed. - if not self.closed(): - self._add_io_state(ioloop.IOLoop.READ) - - def _read_to_buffer(self) -> Optional[int]: - """Reads from the socket and appends the result to the read buffer. - - Returns the number of bytes read. Returns 0 if there is nothing - to read (i.e. the read returns EWOULDBLOCK or equivalent). On - error closes the socket and raises an exception. - """ - try: - while True: - try: - if self._user_read_buffer: - buf = memoryview(self._read_buffer)[ - self._read_buffer_size : - ] # type: Union[memoryview, bytearray] - else: - buf = bytearray(self.read_chunk_size) - bytes_read = self.read_from_fd(buf) - except (socket.error, IOError, OSError) as e: - # ssl.SSLError is a subclass of socket.error - if self._is_connreset(e): - # Treat ECONNRESET as a connection close rather than - # an error to minimize log spam (the exception will - # be available on self.error for apps that care). - self.close(exc_info=e) - return None - self.close(exc_info=e) - raise - break - if bytes_read is None: - return 0 - elif bytes_read == 0: - self.close() - return 0 - if not self._user_read_buffer: - self._read_buffer += memoryview(buf)[:bytes_read] - self._read_buffer_size += bytes_read - finally: - # Break the reference to buf so we don't waste a chunk's worth of - # memory in case an exception hangs on to our stack frame. - del buf - if self._read_buffer_size > self.max_buffer_size: - gen_log.error("Reached maximum read buffer size") - self.close() - raise StreamBufferFullError("Reached maximum read buffer size") - return bytes_read - - def _read_from_buffer(self, pos: int) -> None: - """Attempts to complete the currently-pending read from the buffer. - - The argument is either a position in the read buffer or None, - as returned by _find_read_pos. - """ - self._read_bytes = self._read_delimiter = self._read_regex = None - self._read_partial = False - self._finish_read(pos, False) - - def _find_read_pos(self) -> Optional[int]: - """Attempts to find a position in the read buffer that satisfies - the currently-pending read. - - Returns a position in the buffer if the current read can be satisfied, - or None if it cannot. - """ - if self._read_bytes is not None and ( - self._read_buffer_size >= self._read_bytes - or (self._read_partial and self._read_buffer_size > 0) - ): - num_bytes = min(self._read_bytes, self._read_buffer_size) - return num_bytes - elif self._read_delimiter is not None: - # Multi-byte delimiters (e.g. '\r\n') may straddle two - # chunks in the read buffer, so we can't easily find them - # without collapsing the buffer. However, since protocols - # using delimited reads (as opposed to reads of a known - # length) tend to be "line" oriented, the delimiter is likely - # to be in the first few chunks. Merge the buffer gradually - # since large merges are relatively expensive and get undone in - # _consume(). - if self._read_buffer: - loc = self._read_buffer.find( - self._read_delimiter, self._read_buffer_pos - ) - if loc != -1: - loc -= self._read_buffer_pos - delimiter_len = len(self._read_delimiter) - self._check_max_bytes(self._read_delimiter, loc + delimiter_len) - return loc + delimiter_len - self._check_max_bytes(self._read_delimiter, self._read_buffer_size) - elif self._read_regex is not None: - if self._read_buffer: - m = self._read_regex.search(self._read_buffer, self._read_buffer_pos) - if m is not None: - loc = m.end() - self._read_buffer_pos - self._check_max_bytes(self._read_regex, loc) - return loc - self._check_max_bytes(self._read_regex, self._read_buffer_size) - return None - - def _check_max_bytes(self, delimiter: Union[bytes, Pattern], size: int) -> None: - if self._read_max_bytes is not None and size > self._read_max_bytes: - raise UnsatisfiableReadError( - "delimiter %r not found within %d bytes" - % (delimiter, self._read_max_bytes) - ) - - def _handle_write(self) -> None: - while True: - size = len(self._write_buffer) - if not size: - break - assert size > 0 - try: - if _WINDOWS: - # On windows, socket.send blows up if given a - # write buffer that's too large, instead of just - # returning the number of bytes it was able to - # process. Therefore we must not call socket.send - # with more than 128KB at a time. - size = 128 * 1024 - - num_bytes = self.write_to_fd(self._write_buffer.peek(size)) - if num_bytes == 0: - break - self._write_buffer.advance(num_bytes) - self._total_write_done_index += num_bytes - except BlockingIOError: - break - except (socket.error, IOError, OSError) as e: - if not self._is_connreset(e): - # Broken pipe errors are usually caused by connection - # reset, and its better to not log EPIPE errors to - # minimize log spam - gen_log.warning("Write error on %s: %s", self.fileno(), e) - self.close(exc_info=e) - return - - while self._write_futures: - index, future = self._write_futures[0] - if index > self._total_write_done_index: - break - self._write_futures.popleft() - future_set_result_unless_cancelled(future, None) - - def _consume(self, loc: int) -> bytes: - # Consume loc bytes from the read buffer and return them - if loc == 0: - return b"" - assert loc <= self._read_buffer_size - # Slice the bytearray buffer into bytes, without intermediate copying - b = ( - memoryview(self._read_buffer)[ - self._read_buffer_pos : self._read_buffer_pos + loc - ] - ).tobytes() - self._read_buffer_pos += loc - self._read_buffer_size -= loc - # Amortized O(1) shrink - # (this heuristic is implemented natively in Python 3.4+ - # but is replicated here for Python 2) - if self._read_buffer_pos > self._read_buffer_size: - del self._read_buffer[: self._read_buffer_pos] - self._read_buffer_pos = 0 - return b - - def _check_closed(self) -> None: - if self.closed(): - raise StreamClosedError(real_error=self.error) - - def _maybe_add_error_listener(self) -> None: - # This method is part of an optimization: to detect a connection that - # is closed when we're not actively reading or writing, we must listen - # for read events. However, it is inefficient to do this when the - # connection is first established because we are going to read or write - # immediately anyway. Instead, we insert checks at various times to - # see if the connection is idle and add the read listener then. - if self._state is None or self._state == ioloop.IOLoop.ERROR: - if ( - not self.closed() - and self._read_buffer_size == 0 - and self._close_callback is not None - ): - self._add_io_state(ioloop.IOLoop.READ) - - def _add_io_state(self, state: int) -> None: - """Adds `state` (IOLoop.{READ,WRITE} flags) to our event handler. - - Implementation notes: Reads and writes have a fast path and a - slow path. The fast path reads synchronously from socket - buffers, while the slow path uses `_add_io_state` to schedule - an IOLoop callback. - - To detect closed connections, we must have called - `_add_io_state` at some point, but we want to delay this as - much as possible so we don't have to set an `IOLoop.ERROR` - listener that will be overwritten by the next slow-path - operation. If a sequence of fast-path ops do not end in a - slow-path op, (e.g. for an @asynchronous long-poll request), - we must add the error handler. - - TODO: reevaluate this now that callbacks are gone. - - """ - if self.closed(): - # connection has been closed, so there can be no future events - return - if self._state is None: - self._state = ioloop.IOLoop.ERROR | state - self.io_loop.add_handler(self.fileno(), self._handle_events, self._state) - elif not self._state & state: - self._state = self._state | state - self.io_loop.update_handler(self.fileno(), self._state) - - def _is_connreset(self, exc: BaseException) -> bool: - """Return ``True`` if exc is ECONNRESET or equivalent. - - May be overridden in subclasses. - """ - return ( - isinstance(exc, (socket.error, IOError)) - and errno_from_exception(exc) in _ERRNO_CONNRESET - ) - - -class IOStream(BaseIOStream): - r"""Socket-based `IOStream` implementation. - - This class supports the read and write methods from `BaseIOStream` - plus a `connect` method. - - The ``socket`` parameter may either be connected or unconnected. - For server operations the socket is the result of calling - `socket.accept <socket.socket.accept>`. For client operations the - socket is created with `socket.socket`, and may either be - connected before passing it to the `IOStream` or connected with - `IOStream.connect`. - - A very simple (and broken) HTTP client using this class: - - .. testcode:: - - import tornado.ioloop - import tornado.iostream - import socket - - async def main(): - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) - stream = tornado.iostream.IOStream(s) - await stream.connect(("friendfeed.com", 80)) - await stream.write(b"GET / HTTP/1.0\r\nHost: friendfeed.com\r\n\r\n") - header_data = await stream.read_until(b"\r\n\r\n") - headers = {} - for line in header_data.split(b"\r\n"): - parts = line.split(b":") - if len(parts) == 2: - headers[parts[0].strip()] = parts[1].strip() - body_data = await stream.read_bytes(int(headers[b"Content-Length"])) - print(body_data) - stream.close() - - if __name__ == '__main__': - tornado.ioloop.IOLoop.current().run_sync(main) - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) - stream = tornado.iostream.IOStream(s) - stream.connect(("friendfeed.com", 80), send_request) - tornado.ioloop.IOLoop.current().start() - - .. testoutput:: - :hide: - - """ - - def __init__(self, socket: socket.socket, *args: Any, **kwargs: Any) -> None: - self.socket = socket - self.socket.setblocking(False) - super().__init__(*args, **kwargs) - - def fileno(self) -> Union[int, ioloop._Selectable]: - return self.socket - - def close_fd(self) -> None: - self.socket.close() - self.socket = None # type: ignore - - def get_fd_error(self) -> Optional[Exception]: - errno = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - return socket.error(errno, os.strerror(errno)) - - def read_from_fd(self, buf: Union[bytearray, memoryview]) -> Optional[int]: - try: - return self.socket.recv_into(buf, len(buf)) - except BlockingIOError: - return None - finally: - del buf - - def write_to_fd(self, data: memoryview) -> int: - try: - return self.socket.send(data) # type: ignore - finally: - # Avoid keeping to data, which can be a memoryview. - # See https://github.com/tornadoweb/tornado/pull/2008 - del data - - def connect( - self: _IOStreamType, address: Any, server_hostname: Optional[str] = None - ) -> "Future[_IOStreamType]": - """Connects the socket to a remote address without blocking. - - May only be called if the socket passed to the constructor was - not previously connected. The address parameter is in the - same format as for `socket.connect <socket.socket.connect>` for - the type of socket passed to the IOStream constructor, - e.g. an ``(ip, port)`` tuple. Hostnames are accepted here, - but will be resolved synchronously and block the IOLoop. - If you have a hostname instead of an IP address, the `.TCPClient` - class is recommended instead of calling this method directly. - `.TCPClient` will do asynchronous DNS resolution and handle - both IPv4 and IPv6. - - If ``callback`` is specified, it will be called with no - arguments when the connection is completed; if not this method - returns a `.Future` (whose result after a successful - connection will be the stream itself). - - In SSL mode, the ``server_hostname`` parameter will be used - for certificate validation (unless disabled in the - ``ssl_options``) and SNI (if supported; requires Python - 2.7.9+). - - Note that it is safe to call `IOStream.write - <BaseIOStream.write>` while the connection is pending, in - which case the data will be written as soon as the connection - is ready. Calling `IOStream` read methods before the socket is - connected works on some platforms but is non-portable. - - .. versionchanged:: 4.0 - If no callback is given, returns a `.Future`. - - .. versionchanged:: 4.2 - SSL certificates are validated by default; pass - ``ssl_options=dict(cert_reqs=ssl.CERT_NONE)`` or a - suitably-configured `ssl.SSLContext` to the - `SSLIOStream` constructor to disable. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - - """ - self._connecting = True - future = Future() # type: Future[_IOStreamType] - self._connect_future = typing.cast("Future[IOStream]", future) - try: - self.socket.connect(address) - except BlockingIOError: - # In non-blocking mode we expect connect() to raise an - # exception with EINPROGRESS or EWOULDBLOCK. - pass - except socket.error as e: - # On freebsd, other errors such as ECONNREFUSED may be - # returned immediately when attempting to connect to - # localhost, so handle them the same way as an error - # reported later in _handle_connect. - if future is None: - gen_log.warning("Connect error on fd %s: %s", self.socket.fileno(), e) - self.close(exc_info=e) - return future - self._add_io_state(self.io_loop.WRITE) - return future - - def start_tls( - self, - server_side: bool, - ssl_options: Optional[Union[Dict[str, Any], ssl.SSLContext]] = None, - server_hostname: Optional[str] = None, - ) -> Awaitable["SSLIOStream"]: - """Convert this `IOStream` to an `SSLIOStream`. - - This enables protocols that begin in clear-text mode and - switch to SSL after some initial negotiation (such as the - ``STARTTLS`` extension to SMTP and IMAP). - - This method cannot be used if there are outstanding reads - or writes on the stream, or if there is any data in the - IOStream's buffer (data in the operating system's socket - buffer is allowed). This means it must generally be used - immediately after reading or writing the last clear-text - data. It can also be used immediately after connecting, - before any reads or writes. - - The ``ssl_options`` argument may be either an `ssl.SSLContext` - object or a dictionary of keyword arguments for the - `ssl.wrap_socket` function. The ``server_hostname`` argument - will be used for certificate validation unless disabled - in the ``ssl_options``. - - This method returns a `.Future` whose result is the new - `SSLIOStream`. After this method has been called, - any other operation on the original stream is undefined. - - If a close callback is defined on this stream, it will be - transferred to the new stream. - - .. versionadded:: 4.0 - - .. versionchanged:: 4.2 - SSL certificates are validated by default; pass - ``ssl_options=dict(cert_reqs=ssl.CERT_NONE)`` or a - suitably-configured `ssl.SSLContext` to disable. - """ - if ( - self._read_future - or self._write_futures - or self._connect_future - or self._closed - or self._read_buffer - or self._write_buffer - ): - raise ValueError("IOStream is not idle; cannot convert to SSL") - if ssl_options is None: - if server_side: - ssl_options = _server_ssl_defaults - else: - ssl_options = _client_ssl_defaults - - socket = self.socket - self.io_loop.remove_handler(socket) - self.socket = None # type: ignore - socket = ssl_wrap_socket( - socket, - ssl_options, - server_hostname=server_hostname, - server_side=server_side, - do_handshake_on_connect=False, - ) - orig_close_callback = self._close_callback - self._close_callback = None - - future = Future() # type: Future[SSLIOStream] - ssl_stream = SSLIOStream(socket, ssl_options=ssl_options) - ssl_stream.set_close_callback(orig_close_callback) - ssl_stream._ssl_connect_future = future - ssl_stream.max_buffer_size = self.max_buffer_size - ssl_stream.read_chunk_size = self.read_chunk_size - return future - - def _handle_connect(self) -> None: - try: - err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - except socket.error as e: - # Hurd doesn't allow SO_ERROR for loopback sockets because all - # errors for such sockets are reported synchronously. - if errno_from_exception(e) == errno.ENOPROTOOPT: - err = 0 - if err != 0: - self.error = socket.error(err, os.strerror(err)) - # IOLoop implementations may vary: some of them return - # an error state before the socket becomes writable, so - # in that case a connection failure would be handled by the - # error path in _handle_events instead of here. - if self._connect_future is None: - gen_log.warning( - "Connect error on fd %s: %s", - self.socket.fileno(), - errno.errorcode[err], - ) - self.close() - return - if self._connect_future is not None: - future = self._connect_future - self._connect_future = None - future_set_result_unless_cancelled(future, self) - self._connecting = False - - def set_nodelay(self, value: bool) -> None: - if self.socket is not None and self.socket.family in ( - socket.AF_INET, - socket.AF_INET6, - ): - try: - self.socket.setsockopt( - socket.IPPROTO_TCP, socket.TCP_NODELAY, 1 if value else 0 - ) - except socket.error as e: - # Sometimes setsockopt will fail if the socket is closed - # at the wrong time. This can happen with HTTPServer - # resetting the value to ``False`` between requests. - if e.errno != errno.EINVAL and not self._is_connreset(e): - raise - - -class SSLIOStream(IOStream): - """A utility class to write to and read from a non-blocking SSL socket. - - If the socket passed to the constructor is already connected, - it should be wrapped with:: - - ssl.wrap_socket(sock, do_handshake_on_connect=False, **kwargs) - - before constructing the `SSLIOStream`. Unconnected sockets will be - wrapped when `IOStream.connect` is finished. - """ - - socket = None # type: ssl.SSLSocket - - def __init__(self, *args: Any, **kwargs: Any) -> None: - """The ``ssl_options`` keyword argument may either be an - `ssl.SSLContext` object or a dictionary of keywords arguments - for `ssl.wrap_socket` - """ - self._ssl_options = kwargs.pop("ssl_options", _client_ssl_defaults) - super().__init__(*args, **kwargs) - self._ssl_accepting = True - self._handshake_reading = False - self._handshake_writing = False - self._server_hostname = None # type: Optional[str] - - # If the socket is already connected, attempt to start the handshake. - try: - self.socket.getpeername() - except socket.error: - pass - else: - # Indirectly start the handshake, which will run on the next - # IOLoop iteration and then the real IO state will be set in - # _handle_events. - self._add_io_state(self.io_loop.WRITE) - - def reading(self) -> bool: - return self._handshake_reading or super().reading() - - def writing(self) -> bool: - return self._handshake_writing or super().writing() - - def _do_ssl_handshake(self) -> None: - # Based on code from test_ssl.py in the python stdlib - try: - self._handshake_reading = False - self._handshake_writing = False - self.socket.do_handshake() - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - self._handshake_reading = True - return - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - self._handshake_writing = True - return - elif err.args[0] in (ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_ZERO_RETURN): - return self.close(exc_info=err) - elif err.args[0] == ssl.SSL_ERROR_SSL: - try: - peer = self.socket.getpeername() - except Exception: - peer = "(not connected)" - gen_log.warning( - "SSL Error on %s %s: %s", self.socket.fileno(), peer, err - ) - return self.close(exc_info=err) - raise - except ssl.CertificateError as err: - # CertificateError can happen during handshake (hostname - # verification) and should be passed to user. Starting - # in Python 3.7, this error is a subclass of SSLError - # and will be handled by the previous block instead. - return self.close(exc_info=err) - except socket.error as err: - # Some port scans (e.g. nmap in -sT mode) have been known - # to cause do_handshake to raise EBADF and ENOTCONN, so make - # those errors quiet as well. - # https://groups.google.com/forum/?fromgroups#!topic/python-tornado/ApucKJat1_0 - # Errno 0 is also possible in some cases (nc -z). - # https://github.com/tornadoweb/tornado/issues/2504 - if self._is_connreset(err) or err.args[0] in ( - 0, - errno.EBADF, - errno.ENOTCONN, - ): - return self.close(exc_info=err) - raise - except AttributeError as err: - # On Linux, if the connection was reset before the call to - # wrap_socket, do_handshake will fail with an - # AttributeError. - return self.close(exc_info=err) - else: - self._ssl_accepting = False - if not self._verify_cert(self.socket.getpeercert()): - self.close() - return - self._finish_ssl_connect() - - def _finish_ssl_connect(self) -> None: - if self._ssl_connect_future is not None: - future = self._ssl_connect_future - self._ssl_connect_future = None - future_set_result_unless_cancelled(future, self) - - def _verify_cert(self, peercert: Any) -> bool: - """Returns ``True`` if peercert is valid according to the configured - validation mode and hostname. - - The ssl handshake already tested the certificate for a valid - CA signature; the only thing that remains is to check - the hostname. - """ - if isinstance(self._ssl_options, dict): - verify_mode = self._ssl_options.get("cert_reqs", ssl.CERT_NONE) - elif isinstance(self._ssl_options, ssl.SSLContext): - verify_mode = self._ssl_options.verify_mode - assert verify_mode in (ssl.CERT_NONE, ssl.CERT_REQUIRED, ssl.CERT_OPTIONAL) - if verify_mode == ssl.CERT_NONE or self._server_hostname is None: - return True - cert = self.socket.getpeercert() - if cert is None and verify_mode == ssl.CERT_REQUIRED: - gen_log.warning("No SSL certificate given") - return False - try: - ssl.match_hostname(peercert, self._server_hostname) - except ssl.CertificateError as e: - gen_log.warning("Invalid SSL certificate: %s" % e) - return False - else: - return True - - def _handle_read(self) -> None: - if self._ssl_accepting: - self._do_ssl_handshake() - return - super()._handle_read() - - def _handle_write(self) -> None: - if self._ssl_accepting: - self._do_ssl_handshake() - return - super()._handle_write() - - def connect( - self, address: Tuple, server_hostname: Optional[str] = None - ) -> "Future[SSLIOStream]": - self._server_hostname = server_hostname - # Ignore the result of connect(). If it fails, - # wait_for_handshake will raise an error too. This is - # necessary for the old semantics of the connect callback - # (which takes no arguments). In 6.0 this can be refactored to - # be a regular coroutine. - # TODO: This is trickier than it looks, since if write() - # is called with a connect() pending, we want the connect - # to resolve before the write. Or do we care about this? - # (There's a test for it, but I think in practice users - # either wait for the connect before performing a write or - # they don't care about the connect Future at all) - fut = super().connect(address) - fut.add_done_callback(lambda f: f.exception()) - return self.wait_for_handshake() - - def _handle_connect(self) -> None: - # Call the superclass method to check for errors. - super()._handle_connect() - if self.closed(): - return - # When the connection is complete, wrap the socket for SSL - # traffic. Note that we do this by overriding _handle_connect - # instead of by passing a callback to super().connect because - # user callbacks are enqueued asynchronously on the IOLoop, - # but since _handle_events calls _handle_connect immediately - # followed by _handle_write we need this to be synchronous. - # - # The IOLoop will get confused if we swap out self.socket while the - # fd is registered, so remove it now and re-register after - # wrap_socket(). - self.io_loop.remove_handler(self.socket) - old_state = self._state - assert old_state is not None - self._state = None - self.socket = ssl_wrap_socket( - self.socket, - self._ssl_options, - server_hostname=self._server_hostname, - do_handshake_on_connect=False, - ) - self._add_io_state(old_state) - - def wait_for_handshake(self) -> "Future[SSLIOStream]": - """Wait for the initial SSL handshake to complete. - - If a ``callback`` is given, it will be called with no - arguments once the handshake is complete; otherwise this - method returns a `.Future` which will resolve to the - stream itself after the handshake is complete. - - Once the handshake is complete, information such as - the peer's certificate and NPN/ALPN selections may be - accessed on ``self.socket``. - - This method is intended for use on server-side streams - or after using `IOStream.start_tls`; it should not be used - with `IOStream.connect` (which already waits for the - handshake to complete). It may only be called once per stream. - - .. versionadded:: 4.2 - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. Use the returned - `.Future` instead. - - """ - if self._ssl_connect_future is not None: - raise RuntimeError("Already waiting") - future = self._ssl_connect_future = Future() - if not self._ssl_accepting: - self._finish_ssl_connect() - return future - - def write_to_fd(self, data: memoryview) -> int: - try: - return self.socket.send(data) # type: ignore - except ssl.SSLError as e: - if e.args[0] == ssl.SSL_ERROR_WANT_WRITE: - # In Python 3.5+, SSLSocket.send raises a WANT_WRITE error if - # the socket is not writeable; we need to transform this into - # an EWOULDBLOCK socket.error or a zero return value, - # either of which will be recognized by the caller of this - # method. Prior to Python 3.5, an unwriteable socket would - # simply return 0 bytes written. - return 0 - raise - finally: - # Avoid keeping to data, which can be a memoryview. - # See https://github.com/tornadoweb/tornado/pull/2008 - del data - - def read_from_fd(self, buf: Union[bytearray, memoryview]) -> Optional[int]: - try: - if self._ssl_accepting: - # If the handshake hasn't finished yet, there can't be anything - # to read (attempting to read may or may not raise an exception - # depending on the SSL version) - return None - try: - return self.socket.recv_into(buf, len(buf)) - except ssl.SSLError as e: - # SSLError is a subclass of socket.error, so this except - # block must come first. - if e.args[0] == ssl.SSL_ERROR_WANT_READ: - return None - else: - raise - except BlockingIOError: - return None - finally: - del buf - - def _is_connreset(self, e: BaseException) -> bool: - if isinstance(e, ssl.SSLError) and e.args[0] == ssl.SSL_ERROR_EOF: - return True - return super()._is_connreset(e) - - -class PipeIOStream(BaseIOStream): - """Pipe-based `IOStream` implementation. - - The constructor takes an integer file descriptor (such as one returned - by `os.pipe`) rather than an open file object. Pipes are generally - one-way, so a `PipeIOStream` can be used for reading or writing but not - both. - - ``PipeIOStream`` is only available on Unix-based platforms. - """ - - def __init__(self, fd: int, *args: Any, **kwargs: Any) -> None: - self.fd = fd - self._fio = io.FileIO(self.fd, "r+") - os.set_blocking(fd, False) - super().__init__(*args, **kwargs) - - def fileno(self) -> int: - return self.fd - - def close_fd(self) -> None: - self._fio.close() - - def write_to_fd(self, data: memoryview) -> int: - try: - return os.write(self.fd, data) # type: ignore - finally: - # Avoid keeping to data, which can be a memoryview. - # See https://github.com/tornadoweb/tornado/pull/2008 - del data - - def read_from_fd(self, buf: Union[bytearray, memoryview]) -> Optional[int]: - try: - return self._fio.readinto(buf) # type: ignore - except (IOError, OSError) as e: - if errno_from_exception(e) == errno.EBADF: - # If the writing half of a pipe is closed, select will - # report it as readable but reads will fail with EBADF. - self.close(exc_info=e) - return None - else: - raise - finally: - del buf - - -def doctests() -> Any: - import doctest - - return doctest.DocTestSuite() diff --git a/telegramer/include/tornado/locale.py b/telegramer/include/tornado/locale.py deleted file mode 100644 index adb1f77..0000000 --- a/telegramer/include/tornado/locale.py +++ /dev/null @@ -1,581 +0,0 @@ -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Translation methods for generating localized strings. - -To load a locale and generate a translated string:: - - user_locale = tornado.locale.get("es_LA") - print(user_locale.translate("Sign out")) - -`tornado.locale.get()` returns the closest matching locale, not necessarily the -specific locale you requested. You can support pluralization with -additional arguments to `~Locale.translate()`, e.g.:: - - people = [...] - message = user_locale.translate( - "%(list)s is online", "%(list)s are online", len(people)) - print(message % {"list": user_locale.list(people)}) - -The first string is chosen if ``len(people) == 1``, otherwise the second -string is chosen. - -Applications should call one of `load_translations` (which uses a simple -CSV format) or `load_gettext_translations` (which uses the ``.mo`` format -supported by `gettext` and related tools). If neither method is called, -the `Locale.translate` method will simply return the original string. -""" - -import codecs -import csv -import datetime -import gettext -import os -import re - -from tornado import escape -from tornado.log import gen_log - -from tornado._locale_data import LOCALE_NAMES - -from typing import Iterable, Any, Union, Dict, Optional - -_default_locale = "en_US" -_translations = {} # type: Dict[str, Any] -_supported_locales = frozenset([_default_locale]) -_use_gettext = False -CONTEXT_SEPARATOR = "\x04" - - -def get(*locale_codes: str) -> "Locale": - """Returns the closest match for the given locale codes. - - We iterate over all given locale codes in order. If we have a tight - or a loose match for the code (e.g., "en" for "en_US"), we return - the locale. Otherwise we move to the next code in the list. - - By default we return ``en_US`` if no translations are found for any of - the specified locales. You can change the default locale with - `set_default_locale()`. - """ - return Locale.get_closest(*locale_codes) - - -def set_default_locale(code: str) -> None: - """Sets the default locale. - - The default locale is assumed to be the language used for all strings - in the system. The translations loaded from disk are mappings from - the default locale to the destination locale. Consequently, you don't - need to create a translation file for the default locale. - """ - global _default_locale - global _supported_locales - _default_locale = code - _supported_locales = frozenset(list(_translations.keys()) + [_default_locale]) - - -def load_translations(directory: str, encoding: Optional[str] = None) -> None: - """Loads translations from CSV files in a directory. - - Translations are strings with optional Python-style named placeholders - (e.g., ``My name is %(name)s``) and their associated translations. - - The directory should have translation files of the form ``LOCALE.csv``, - e.g. ``es_GT.csv``. The CSV files should have two or three columns: string, - translation, and an optional plural indicator. Plural indicators should - be one of "plural" or "singular". A given string can have both singular - and plural forms. For example ``%(name)s liked this`` may have a - different verb conjugation depending on whether %(name)s is one - name or a list of names. There should be two rows in the CSV file for - that string, one with plural indicator "singular", and one "plural". - For strings with no verbs that would change on translation, simply - use "unknown" or the empty string (or don't include the column at all). - - The file is read using the `csv` module in the default "excel" dialect. - In this format there should not be spaces after the commas. - - If no ``encoding`` parameter is given, the encoding will be - detected automatically (among UTF-8 and UTF-16) if the file - contains a byte-order marker (BOM), defaulting to UTF-8 if no BOM - is present. - - Example translation ``es_LA.csv``:: - - "I love you","Te amo" - "%(name)s liked this","A %(name)s les gustó esto","plural" - "%(name)s liked this","A %(name)s le gustó esto","singular" - - .. versionchanged:: 4.3 - Added ``encoding`` parameter. Added support for BOM-based encoding - detection, UTF-16, and UTF-8-with-BOM. - """ - global _translations - global _supported_locales - _translations = {} - for path in os.listdir(directory): - if not path.endswith(".csv"): - continue - locale, extension = path.split(".") - if not re.match("[a-z]+(_[A-Z]+)?$", locale): - gen_log.error( - "Unrecognized locale %r (path: %s)", - locale, - os.path.join(directory, path), - ) - continue - full_path = os.path.join(directory, path) - if encoding is None: - # Try to autodetect encoding based on the BOM. - with open(full_path, "rb") as bf: - data = bf.read(len(codecs.BOM_UTF16_LE)) - if data in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): - encoding = "utf-16" - else: - # utf-8-sig is "utf-8 with optional BOM". It's discouraged - # in most cases but is common with CSV files because Excel - # cannot read utf-8 files without a BOM. - encoding = "utf-8-sig" - # python 3: csv.reader requires a file open in text mode. - # Specify an encoding to avoid dependence on $LANG environment variable. - with open(full_path, encoding=encoding) as f: - _translations[locale] = {} - for i, row in enumerate(csv.reader(f)): - if not row or len(row) < 2: - continue - row = [escape.to_unicode(c).strip() for c in row] - english, translation = row[:2] - if len(row) > 2: - plural = row[2] or "unknown" - else: - plural = "unknown" - if plural not in ("plural", "singular", "unknown"): - gen_log.error( - "Unrecognized plural indicator %r in %s line %d", - plural, - path, - i + 1, - ) - continue - _translations[locale].setdefault(plural, {})[english] = translation - _supported_locales = frozenset(list(_translations.keys()) + [_default_locale]) - gen_log.debug("Supported locales: %s", sorted(_supported_locales)) - - -def load_gettext_translations(directory: str, domain: str) -> None: - """Loads translations from `gettext`'s locale tree - - Locale tree is similar to system's ``/usr/share/locale``, like:: - - {directory}/{lang}/LC_MESSAGES/{domain}.mo - - Three steps are required to have your app translated: - - 1. Generate POT translation file:: - - xgettext --language=Python --keyword=_:1,2 -d mydomain file1.py file2.html etc - - 2. Merge against existing POT file:: - - msgmerge old.po mydomain.po > new.po - - 3. Compile:: - - msgfmt mydomain.po -o {directory}/pt_BR/LC_MESSAGES/mydomain.mo - """ - global _translations - global _supported_locales - global _use_gettext - _translations = {} - for lang in os.listdir(directory): - if lang.startswith("."): - continue # skip .svn, etc - if os.path.isfile(os.path.join(directory, lang)): - continue - try: - os.stat(os.path.join(directory, lang, "LC_MESSAGES", domain + ".mo")) - _translations[lang] = gettext.translation( - domain, directory, languages=[lang] - ) - except Exception as e: - gen_log.error("Cannot load translation for '%s': %s", lang, str(e)) - continue - _supported_locales = frozenset(list(_translations.keys()) + [_default_locale]) - _use_gettext = True - gen_log.debug("Supported locales: %s", sorted(_supported_locales)) - - -def get_supported_locales() -> Iterable[str]: - """Returns a list of all the supported locale codes.""" - return _supported_locales - - -class Locale(object): - """Object representing a locale. - - After calling one of `load_translations` or `load_gettext_translations`, - call `get` or `get_closest` to get a Locale object. - """ - - _cache = {} # type: Dict[str, Locale] - - @classmethod - def get_closest(cls, *locale_codes: str) -> "Locale": - """Returns the closest match for the given locale code.""" - for code in locale_codes: - if not code: - continue - code = code.replace("-", "_") - parts = code.split("_") - if len(parts) > 2: - continue - elif len(parts) == 2: - code = parts[0].lower() + "_" + parts[1].upper() - if code in _supported_locales: - return cls.get(code) - if parts[0].lower() in _supported_locales: - return cls.get(parts[0].lower()) - return cls.get(_default_locale) - - @classmethod - def get(cls, code: str) -> "Locale": - """Returns the Locale for the given locale code. - - If it is not supported, we raise an exception. - """ - if code not in cls._cache: - assert code in _supported_locales - translations = _translations.get(code, None) - if translations is None: - locale = CSVLocale(code, {}) # type: Locale - elif _use_gettext: - locale = GettextLocale(code, translations) - else: - locale = CSVLocale(code, translations) - cls._cache[code] = locale - return cls._cache[code] - - def __init__(self, code: str) -> None: - self.code = code - self.name = LOCALE_NAMES.get(code, {}).get("name", u"Unknown") - self.rtl = False - for prefix in ["fa", "ar", "he"]: - if self.code.startswith(prefix): - self.rtl = True - break - - # Initialize strings for date formatting - _ = self.translate - self._months = [ - _("January"), - _("February"), - _("March"), - _("April"), - _("May"), - _("June"), - _("July"), - _("August"), - _("September"), - _("October"), - _("November"), - _("December"), - ] - self._weekdays = [ - _("Monday"), - _("Tuesday"), - _("Wednesday"), - _("Thursday"), - _("Friday"), - _("Saturday"), - _("Sunday"), - ] - - def translate( - self, - message: str, - plural_message: Optional[str] = None, - count: Optional[int] = None, - ) -> str: - """Returns the translation for the given message for this locale. - - If ``plural_message`` is given, you must also provide - ``count``. We return ``plural_message`` when ``count != 1``, - and we return the singular form for the given message when - ``count == 1``. - """ - raise NotImplementedError() - - def pgettext( - self, - context: str, - message: str, - plural_message: Optional[str] = None, - count: Optional[int] = None, - ) -> str: - raise NotImplementedError() - - def format_date( - self, - date: Union[int, float, datetime.datetime], - gmt_offset: int = 0, - relative: bool = True, - shorter: bool = False, - full_format: bool = False, - ) -> str: - """Formats the given date (which should be GMT). - - By default, we return a relative time (e.g., "2 minutes ago"). You - can return an absolute date string with ``relative=False``. - - You can force a full format date ("July 10, 1980") with - ``full_format=True``. - - This method is primarily intended for dates in the past. - For dates in the future, we fall back to full format. - """ - if isinstance(date, (int, float)): - date = datetime.datetime.utcfromtimestamp(date) - now = datetime.datetime.utcnow() - if date > now: - if relative and (date - now).seconds < 60: - # Due to click skew, things are some things slightly - # in the future. Round timestamps in the immediate - # future down to now in relative mode. - date = now - else: - # Otherwise, future dates always use the full format. - full_format = True - local_date = date - datetime.timedelta(minutes=gmt_offset) - local_now = now - datetime.timedelta(minutes=gmt_offset) - local_yesterday = local_now - datetime.timedelta(hours=24) - difference = now - date - seconds = difference.seconds - days = difference.days - - _ = self.translate - format = None - if not full_format: - if relative and days == 0: - if seconds < 50: - return _("1 second ago", "%(seconds)d seconds ago", seconds) % { - "seconds": seconds - } - - if seconds < 50 * 60: - minutes = round(seconds / 60.0) - return _("1 minute ago", "%(minutes)d minutes ago", minutes) % { - "minutes": minutes - } - - hours = round(seconds / (60.0 * 60)) - return _("1 hour ago", "%(hours)d hours ago", hours) % {"hours": hours} - - if days == 0: - format = _("%(time)s") - elif days == 1 and local_date.day == local_yesterday.day and relative: - format = _("yesterday") if shorter else _("yesterday at %(time)s") - elif days < 5: - format = _("%(weekday)s") if shorter else _("%(weekday)s at %(time)s") - elif days < 334: # 11mo, since confusing for same month last year - format = ( - _("%(month_name)s %(day)s") - if shorter - else _("%(month_name)s %(day)s at %(time)s") - ) - - if format is None: - format = ( - _("%(month_name)s %(day)s, %(year)s") - if shorter - else _("%(month_name)s %(day)s, %(year)s at %(time)s") - ) - - tfhour_clock = self.code not in ("en", "en_US", "zh_CN") - if tfhour_clock: - str_time = "%d:%02d" % (local_date.hour, local_date.minute) - elif self.code == "zh_CN": - str_time = "%s%d:%02d" % ( - (u"\u4e0a\u5348", u"\u4e0b\u5348")[local_date.hour >= 12], - local_date.hour % 12 or 12, - local_date.minute, - ) - else: - str_time = "%d:%02d %s" % ( - local_date.hour % 12 or 12, - local_date.minute, - ("am", "pm")[local_date.hour >= 12], - ) - - return format % { - "month_name": self._months[local_date.month - 1], - "weekday": self._weekdays[local_date.weekday()], - "day": str(local_date.day), - "year": str(local_date.year), - "time": str_time, - } - - def format_day( - self, date: datetime.datetime, gmt_offset: int = 0, dow: bool = True - ) -> bool: - """Formats the given date as a day of week. - - Example: "Monday, January 22". You can remove the day of week with - ``dow=False``. - """ - local_date = date - datetime.timedelta(minutes=gmt_offset) - _ = self.translate - if dow: - return _("%(weekday)s, %(month_name)s %(day)s") % { - "month_name": self._months[local_date.month - 1], - "weekday": self._weekdays[local_date.weekday()], - "day": str(local_date.day), - } - else: - return _("%(month_name)s %(day)s") % { - "month_name": self._months[local_date.month - 1], - "day": str(local_date.day), - } - - def list(self, parts: Any) -> str: - """Returns a comma-separated list for the given list of parts. - - The format is, e.g., "A, B and C", "A and B" or just "A" for lists - of size 1. - """ - _ = self.translate - if len(parts) == 0: - return "" - if len(parts) == 1: - return parts[0] - comma = u" \u0648 " if self.code.startswith("fa") else u", " - return _("%(commas)s and %(last)s") % { - "commas": comma.join(parts[:-1]), - "last": parts[len(parts) - 1], - } - - def friendly_number(self, value: int) -> str: - """Returns a comma-separated number for the given integer.""" - if self.code not in ("en", "en_US"): - return str(value) - s = str(value) - parts = [] - while s: - parts.append(s[-3:]) - s = s[:-3] - return ",".join(reversed(parts)) - - -class CSVLocale(Locale): - """Locale implementation using tornado's CSV translation format.""" - - def __init__(self, code: str, translations: Dict[str, Dict[str, str]]) -> None: - self.translations = translations - super().__init__(code) - - def translate( - self, - message: str, - plural_message: Optional[str] = None, - count: Optional[int] = None, - ) -> str: - if plural_message is not None: - assert count is not None - if count != 1: - message = plural_message - message_dict = self.translations.get("plural", {}) - else: - message_dict = self.translations.get("singular", {}) - else: - message_dict = self.translations.get("unknown", {}) - return message_dict.get(message, message) - - def pgettext( - self, - context: str, - message: str, - plural_message: Optional[str] = None, - count: Optional[int] = None, - ) -> str: - if self.translations: - gen_log.warning("pgettext is not supported by CSVLocale") - return self.translate(message, plural_message, count) - - -class GettextLocale(Locale): - """Locale implementation using the `gettext` module.""" - - def __init__(self, code: str, translations: gettext.NullTranslations) -> None: - self.ngettext = translations.ngettext - self.gettext = translations.gettext - # self.gettext must exist before __init__ is called, since it - # calls into self.translate - super().__init__(code) - - def translate( - self, - message: str, - plural_message: Optional[str] = None, - count: Optional[int] = None, - ) -> str: - if plural_message is not None: - assert count is not None - return self.ngettext(message, plural_message, count) - else: - return self.gettext(message) - - def pgettext( - self, - context: str, - message: str, - plural_message: Optional[str] = None, - count: Optional[int] = None, - ) -> str: - """Allows to set context for translation, accepts plural forms. - - Usage example:: - - pgettext("law", "right") - pgettext("good", "right") - - Plural message example:: - - pgettext("organization", "club", "clubs", len(clubs)) - pgettext("stick", "club", "clubs", len(clubs)) - - To generate POT file with context, add following options to step 1 - of `load_gettext_translations` sequence:: - - xgettext [basic options] --keyword=pgettext:1c,2 --keyword=pgettext:1c,2,3 - - .. versionadded:: 4.2 - """ - if plural_message is not None: - assert count is not None - msgs_with_ctxt = ( - "%s%s%s" % (context, CONTEXT_SEPARATOR, message), - "%s%s%s" % (context, CONTEXT_SEPARATOR, plural_message), - count, - ) - result = self.ngettext(*msgs_with_ctxt) - if CONTEXT_SEPARATOR in result: - # Translation not found - result = self.ngettext(message, plural_message, count) - return result - else: - msg_with_ctxt = "%s%s%s" % (context, CONTEXT_SEPARATOR, message) - result = self.gettext(msg_with_ctxt) - if CONTEXT_SEPARATOR in result: - # Translation not found - result = message - return result diff --git a/telegramer/include/tornado/locks.py b/telegramer/include/tornado/locks.py deleted file mode 100644 index 0898eba..0000000 --- a/telegramer/include/tornado/locks.py +++ /dev/null @@ -1,571 +0,0 @@ -# Copyright 2015 The Tornado Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import collections -import datetime -import types - -from tornado import gen, ioloop -from tornado.concurrent import Future, future_set_result_unless_cancelled - -from typing import Union, Optional, Type, Any, Awaitable -import typing - -if typing.TYPE_CHECKING: - from typing import Deque, Set # noqa: F401 - -__all__ = ["Condition", "Event", "Semaphore", "BoundedSemaphore", "Lock"] - - -class _TimeoutGarbageCollector(object): - """Base class for objects that periodically clean up timed-out waiters. - - Avoids memory leak in a common pattern like: - - while True: - yield condition.wait(short_timeout) - print('looping....') - """ - - def __init__(self) -> None: - self._waiters = collections.deque() # type: Deque[Future] - self._timeouts = 0 - - def _garbage_collect(self) -> None: - # Occasionally clear timed-out waiters. - self._timeouts += 1 - if self._timeouts > 100: - self._timeouts = 0 - self._waiters = collections.deque(w for w in self._waiters if not w.done()) - - -class Condition(_TimeoutGarbageCollector): - """A condition allows one or more coroutines to wait until notified. - - Like a standard `threading.Condition`, but does not need an underlying lock - that is acquired and released. - - With a `Condition`, coroutines can wait to be notified by other coroutines: - - .. testcode:: - - from tornado import gen - from tornado.ioloop import IOLoop - from tornado.locks import Condition - - condition = Condition() - - async def waiter(): - print("I'll wait right here") - await condition.wait() - print("I'm done waiting") - - async def notifier(): - print("About to notify") - condition.notify() - print("Done notifying") - - async def runner(): - # Wait for waiter() and notifier() in parallel - await gen.multi([waiter(), notifier()]) - - IOLoop.current().run_sync(runner) - - .. testoutput:: - - I'll wait right here - About to notify - Done notifying - I'm done waiting - - `wait` takes an optional ``timeout`` argument, which is either an absolute - timestamp:: - - io_loop = IOLoop.current() - - # Wait up to 1 second for a notification. - await condition.wait(timeout=io_loop.time() + 1) - - ...or a `datetime.timedelta` for a timeout relative to the current time:: - - # Wait up to 1 second. - await condition.wait(timeout=datetime.timedelta(seconds=1)) - - The method returns False if there's no notification before the deadline. - - .. versionchanged:: 5.0 - Previously, waiters could be notified synchronously from within - `notify`. Now, the notification will always be received on the - next iteration of the `.IOLoop`. - """ - - def __init__(self) -> None: - super().__init__() - self.io_loop = ioloop.IOLoop.current() - - def __repr__(self) -> str: - result = "<%s" % (self.__class__.__name__,) - if self._waiters: - result += " waiters[%s]" % len(self._waiters) - return result + ">" - - def wait( - self, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> Awaitable[bool]: - """Wait for `.notify`. - - Returns a `.Future` that resolves ``True`` if the condition is notified, - or ``False`` after a timeout. - """ - waiter = Future() # type: Future[bool] - self._waiters.append(waiter) - if timeout: - - def on_timeout() -> None: - if not waiter.done(): - future_set_result_unless_cancelled(waiter, False) - self._garbage_collect() - - io_loop = ioloop.IOLoop.current() - timeout_handle = io_loop.add_timeout(timeout, on_timeout) - waiter.add_done_callback(lambda _: io_loop.remove_timeout(timeout_handle)) - return waiter - - def notify(self, n: int = 1) -> None: - """Wake ``n`` waiters.""" - waiters = [] # Waiters we plan to run right now. - while n and self._waiters: - waiter = self._waiters.popleft() - if not waiter.done(): # Might have timed out. - n -= 1 - waiters.append(waiter) - - for waiter in waiters: - future_set_result_unless_cancelled(waiter, True) - - def notify_all(self) -> None: - """Wake all waiters.""" - self.notify(len(self._waiters)) - - -class Event(object): - """An event blocks coroutines until its internal flag is set to True. - - Similar to `threading.Event`. - - A coroutine can wait for an event to be set. Once it is set, calls to - ``yield event.wait()`` will not block unless the event has been cleared: - - .. testcode:: - - from tornado import gen - from tornado.ioloop import IOLoop - from tornado.locks import Event - - event = Event() - - async def waiter(): - print("Waiting for event") - await event.wait() - print("Not waiting this time") - await event.wait() - print("Done") - - async def setter(): - print("About to set the event") - event.set() - - async def runner(): - await gen.multi([waiter(), setter()]) - - IOLoop.current().run_sync(runner) - - .. testoutput:: - - Waiting for event - About to set the event - Not waiting this time - Done - """ - - def __init__(self) -> None: - self._value = False - self._waiters = set() # type: Set[Future[None]] - - def __repr__(self) -> str: - return "<%s %s>" % ( - self.__class__.__name__, - "set" if self.is_set() else "clear", - ) - - def is_set(self) -> bool: - """Return ``True`` if the internal flag is true.""" - return self._value - - def set(self) -> None: - """Set the internal flag to ``True``. All waiters are awakened. - - Calling `.wait` once the flag is set will not block. - """ - if not self._value: - self._value = True - - for fut in self._waiters: - if not fut.done(): - fut.set_result(None) - - def clear(self) -> None: - """Reset the internal flag to ``False``. - - Calls to `.wait` will block until `.set` is called. - """ - self._value = False - - def wait( - self, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> Awaitable[None]: - """Block until the internal flag is true. - - Returns an awaitable, which raises `tornado.util.TimeoutError` after a - timeout. - """ - fut = Future() # type: Future[None] - if self._value: - fut.set_result(None) - return fut - self._waiters.add(fut) - fut.add_done_callback(lambda fut: self._waiters.remove(fut)) - if timeout is None: - return fut - else: - timeout_fut = gen.with_timeout(timeout, fut) - # This is a slightly clumsy workaround for the fact that - # gen.with_timeout doesn't cancel its futures. Cancelling - # fut will remove it from the waiters list. - timeout_fut.add_done_callback( - lambda tf: fut.cancel() if not fut.done() else None - ) - return timeout_fut - - -class _ReleasingContextManager(object): - """Releases a Lock or Semaphore at the end of a "with" statement. - - with (yield semaphore.acquire()): - pass - - # Now semaphore.release() has been called. - """ - - def __init__(self, obj: Any) -> None: - self._obj = obj - - def __enter__(self) -> None: - pass - - def __exit__( - self, - exc_type: "Optional[Type[BaseException]]", - exc_val: Optional[BaseException], - exc_tb: Optional[types.TracebackType], - ) -> None: - self._obj.release() - - -class Semaphore(_TimeoutGarbageCollector): - """A lock that can be acquired a fixed number of times before blocking. - - A Semaphore manages a counter representing the number of `.release` calls - minus the number of `.acquire` calls, plus an initial value. The `.acquire` - method blocks if necessary until it can return without making the counter - negative. - - Semaphores limit access to a shared resource. To allow access for two - workers at a time: - - .. testsetup:: semaphore - - from collections import deque - - from tornado import gen - from tornado.ioloop import IOLoop - from tornado.concurrent import Future - - # Ensure reliable doctest output: resolve Futures one at a time. - futures_q = deque([Future() for _ in range(3)]) - - async def simulator(futures): - for f in futures: - # simulate the asynchronous passage of time - await gen.sleep(0) - await gen.sleep(0) - f.set_result(None) - - IOLoop.current().add_callback(simulator, list(futures_q)) - - def use_some_resource(): - return futures_q.popleft() - - .. testcode:: semaphore - - from tornado import gen - from tornado.ioloop import IOLoop - from tornado.locks import Semaphore - - sem = Semaphore(2) - - async def worker(worker_id): - await sem.acquire() - try: - print("Worker %d is working" % worker_id) - await use_some_resource() - finally: - print("Worker %d is done" % worker_id) - sem.release() - - async def runner(): - # Join all workers. - await gen.multi([worker(i) for i in range(3)]) - - IOLoop.current().run_sync(runner) - - .. testoutput:: semaphore - - Worker 0 is working - Worker 1 is working - Worker 0 is done - Worker 2 is working - Worker 1 is done - Worker 2 is done - - Workers 0 and 1 are allowed to run concurrently, but worker 2 waits until - the semaphore has been released once, by worker 0. - - The semaphore can be used as an async context manager:: - - async def worker(worker_id): - async with sem: - print("Worker %d is working" % worker_id) - await use_some_resource() - - # Now the semaphore has been released. - print("Worker %d is done" % worker_id) - - For compatibility with older versions of Python, `.acquire` is a - context manager, so ``worker`` could also be written as:: - - @gen.coroutine - def worker(worker_id): - with (yield sem.acquire()): - print("Worker %d is working" % worker_id) - yield use_some_resource() - - # Now the semaphore has been released. - print("Worker %d is done" % worker_id) - - .. versionchanged:: 4.3 - Added ``async with`` support in Python 3.5. - - """ - - def __init__(self, value: int = 1) -> None: - super().__init__() - if value < 0: - raise ValueError("semaphore initial value must be >= 0") - - self._value = value - - def __repr__(self) -> str: - res = super().__repr__() - extra = ( - "locked" if self._value == 0 else "unlocked,value:{0}".format(self._value) - ) - if self._waiters: - extra = "{0},waiters:{1}".format(extra, len(self._waiters)) - return "<{0} [{1}]>".format(res[1:-1], extra) - - def release(self) -> None: - """Increment the counter and wake one waiter.""" - self._value += 1 - while self._waiters: - waiter = self._waiters.popleft() - if not waiter.done(): - self._value -= 1 - - # If the waiter is a coroutine paused at - # - # with (yield semaphore.acquire()): - # - # then the context manager's __exit__ calls release() at the end - # of the "with" block. - waiter.set_result(_ReleasingContextManager(self)) - break - - def acquire( - self, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> Awaitable[_ReleasingContextManager]: - """Decrement the counter. Returns an awaitable. - - Block if the counter is zero and wait for a `.release`. The awaitable - raises `.TimeoutError` after the deadline. - """ - waiter = Future() # type: Future[_ReleasingContextManager] - if self._value > 0: - self._value -= 1 - waiter.set_result(_ReleasingContextManager(self)) - else: - self._waiters.append(waiter) - if timeout: - - def on_timeout() -> None: - if not waiter.done(): - waiter.set_exception(gen.TimeoutError()) - self._garbage_collect() - - io_loop = ioloop.IOLoop.current() - timeout_handle = io_loop.add_timeout(timeout, on_timeout) - waiter.add_done_callback( - lambda _: io_loop.remove_timeout(timeout_handle) - ) - return waiter - - def __enter__(self) -> None: - raise RuntimeError("Use 'async with' instead of 'with' for Semaphore") - - def __exit__( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - traceback: Optional[types.TracebackType], - ) -> None: - self.__enter__() - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[types.TracebackType], - ) -> None: - self.release() - - -class BoundedSemaphore(Semaphore): - """A semaphore that prevents release() being called too many times. - - If `.release` would increment the semaphore's value past the initial - value, it raises `ValueError`. Semaphores are mostly used to guard - resources with limited capacity, so a semaphore released too many times - is a sign of a bug. - """ - - def __init__(self, value: int = 1) -> None: - super().__init__(value=value) - self._initial_value = value - - def release(self) -> None: - """Increment the counter and wake one waiter.""" - if self._value >= self._initial_value: - raise ValueError("Semaphore released too many times") - super().release() - - -class Lock(object): - """A lock for coroutines. - - A Lock begins unlocked, and `acquire` locks it immediately. While it is - locked, a coroutine that yields `acquire` waits until another coroutine - calls `release`. - - Releasing an unlocked lock raises `RuntimeError`. - - A Lock can be used as an async context manager with the ``async - with`` statement: - - >>> from tornado import locks - >>> lock = locks.Lock() - >>> - >>> async def f(): - ... async with lock: - ... # Do something holding the lock. - ... pass - ... - ... # Now the lock is released. - - For compatibility with older versions of Python, the `.acquire` - method asynchronously returns a regular context manager: - - >>> async def f2(): - ... with (yield lock.acquire()): - ... # Do something holding the lock. - ... pass - ... - ... # Now the lock is released. - - .. versionchanged:: 4.3 - Added ``async with`` support in Python 3.5. - - """ - - def __init__(self) -> None: - self._block = BoundedSemaphore(value=1) - - def __repr__(self) -> str: - return "<%s _block=%s>" % (self.__class__.__name__, self._block) - - def acquire( - self, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> Awaitable[_ReleasingContextManager]: - """Attempt to lock. Returns an awaitable. - - Returns an awaitable, which raises `tornado.util.TimeoutError` after a - timeout. - """ - return self._block.acquire(timeout) - - def release(self) -> None: - """Unlock. - - The first coroutine in line waiting for `acquire` gets the lock. - - If not locked, raise a `RuntimeError`. - """ - try: - self._block.release() - except ValueError: - raise RuntimeError("release unlocked lock") - - def __enter__(self) -> None: - raise RuntimeError("Use `async with` instead of `with` for Lock") - - def __exit__( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[types.TracebackType], - ) -> None: - self.__enter__() - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[types.TracebackType], - ) -> None: - self.release() diff --git a/telegramer/include/tornado/log.py b/telegramer/include/tornado/log.py deleted file mode 100644 index 810a037..0000000 --- a/telegramer/include/tornado/log.py +++ /dev/null @@ -1,339 +0,0 @@ -# -# Copyright 2012 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Logging support for Tornado. - -Tornado uses three logger streams: - -* ``tornado.access``: Per-request logging for Tornado's HTTP servers (and - potentially other servers in the future) -* ``tornado.application``: Logging of errors from application code (i.e. - uncaught exceptions from callbacks) -* ``tornado.general``: General-purpose logging, including any errors - or warnings from Tornado itself. - -These streams may be configured independently using the standard library's -`logging` module. For example, you may wish to send ``tornado.access`` logs -to a separate file for analysis. -""" -import logging -import logging.handlers -import sys - -from tornado.escape import _unicode -from tornado.util import unicode_type, basestring_type - -try: - import colorama # type: ignore -except ImportError: - colorama = None - -try: - import curses -except ImportError: - curses = None # type: ignore - -from typing import Dict, Any, cast, Optional - -# Logger objects for internal tornado use -access_log = logging.getLogger("tornado.access") -app_log = logging.getLogger("tornado.application") -gen_log = logging.getLogger("tornado.general") - - -def _stderr_supports_color() -> bool: - try: - if hasattr(sys.stderr, "isatty") and sys.stderr.isatty(): - if curses: - curses.setupterm() - if curses.tigetnum("colors") > 0: - return True - elif colorama: - if sys.stderr is getattr( - colorama.initialise, "wrapped_stderr", object() - ): - return True - except Exception: - # Very broad exception handling because it's always better to - # fall back to non-colored logs than to break at startup. - pass - return False - - -def _safe_unicode(s: Any) -> str: - try: - return _unicode(s) - except UnicodeDecodeError: - return repr(s) - - -class LogFormatter(logging.Formatter): - """Log formatter used in Tornado. - - Key features of this formatter are: - - * Color support when logging to a terminal that supports it. - * Timestamps on every log line. - * Robust against str/bytes encoding problems. - - This formatter is enabled automatically by - `tornado.options.parse_command_line` or `tornado.options.parse_config_file` - (unless ``--logging=none`` is used). - - Color support on Windows versions that do not support ANSI color codes is - enabled by use of the colorama__ library. Applications that wish to use - this must first initialize colorama with a call to ``colorama.init``. - See the colorama documentation for details. - - __ https://pypi.python.org/pypi/colorama - - .. versionchanged:: 4.5 - Added support for ``colorama``. Changed the constructor - signature to be compatible with `logging.config.dictConfig`. - """ - - DEFAULT_FORMAT = "%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s" # noqa: E501 - DEFAULT_DATE_FORMAT = "%y%m%d %H:%M:%S" - DEFAULT_COLORS = { - logging.DEBUG: 4, # Blue - logging.INFO: 2, # Green - logging.WARNING: 3, # Yellow - logging.ERROR: 1, # Red - logging.CRITICAL: 5, # Magenta - } - - def __init__( - self, - fmt: str = DEFAULT_FORMAT, - datefmt: str = DEFAULT_DATE_FORMAT, - style: str = "%", - color: bool = True, - colors: Dict[int, int] = DEFAULT_COLORS, - ) -> None: - r""" - :arg bool color: Enables color support. - :arg str fmt: Log message format. - It will be applied to the attributes dict of log records. The - text between ``%(color)s`` and ``%(end_color)s`` will be colored - depending on the level if color support is on. - :arg dict colors: color mappings from logging level to terminal color - code - :arg str datefmt: Datetime format. - Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. - - .. versionchanged:: 3.2 - - Added ``fmt`` and ``datefmt`` arguments. - """ - logging.Formatter.__init__(self, datefmt=datefmt) - self._fmt = fmt - - self._colors = {} # type: Dict[int, str] - if color and _stderr_supports_color(): - if curses is not None: - fg_color = curses.tigetstr("setaf") or curses.tigetstr("setf") or b"" - - for levelno, code in colors.items(): - # Convert the terminal control characters from - # bytes to unicode strings for easier use with the - # logging module. - self._colors[levelno] = unicode_type( - curses.tparm(fg_color, code), "ascii" - ) - self._normal = unicode_type(curses.tigetstr("sgr0"), "ascii") - else: - # If curses is not present (currently we'll only get here for - # colorama on windows), assume hard-coded ANSI color codes. - for levelno, code in colors.items(): - self._colors[levelno] = "\033[2;3%dm" % code - self._normal = "\033[0m" - else: - self._normal = "" - - def format(self, record: Any) -> str: - try: - message = record.getMessage() - assert isinstance(message, basestring_type) # guaranteed by logging - # Encoding notes: The logging module prefers to work with character - # strings, but only enforces that log messages are instances of - # basestring. In python 2, non-ascii bytestrings will make - # their way through the logging framework until they blow up with - # an unhelpful decoding error (with this formatter it happens - # when we attach the prefix, but there are other opportunities for - # exceptions further along in the framework). - # - # If a byte string makes it this far, convert it to unicode to - # ensure it will make it out to the logs. Use repr() as a fallback - # to ensure that all byte strings can be converted successfully, - # but don't do it by default so we don't add extra quotes to ascii - # bytestrings. This is a bit of a hacky place to do this, but - # it's worth it since the encoding errors that would otherwise - # result are so useless (and tornado is fond of using utf8-encoded - # byte strings wherever possible). - record.message = _safe_unicode(message) - except Exception as e: - record.message = "Bad message (%r): %r" % (e, record.__dict__) - - record.asctime = self.formatTime(record, cast(str, self.datefmt)) - - if record.levelno in self._colors: - record.color = self._colors[record.levelno] - record.end_color = self._normal - else: - record.color = record.end_color = "" - - formatted = self._fmt % record.__dict__ - - if record.exc_info: - if not record.exc_text: - record.exc_text = self.formatException(record.exc_info) - if record.exc_text: - # exc_text contains multiple lines. We need to _safe_unicode - # each line separately so that non-utf8 bytes don't cause - # all the newlines to turn into '\n'. - lines = [formatted.rstrip()] - lines.extend(_safe_unicode(ln) for ln in record.exc_text.split("\n")) - formatted = "\n".join(lines) - return formatted.replace("\n", "\n ") - - -def enable_pretty_logging( - options: Any = None, logger: Optional[logging.Logger] = None -) -> None: - """Turns on formatted logging output as configured. - - This is called automatically by `tornado.options.parse_command_line` - and `tornado.options.parse_config_file`. - """ - if options is None: - import tornado.options - - options = tornado.options.options - if options.logging is None or options.logging.lower() == "none": - return - if logger is None: - logger = logging.getLogger() - logger.setLevel(getattr(logging, options.logging.upper())) - if options.log_file_prefix: - rotate_mode = options.log_rotate_mode - if rotate_mode == "size": - channel = logging.handlers.RotatingFileHandler( - filename=options.log_file_prefix, - maxBytes=options.log_file_max_size, - backupCount=options.log_file_num_backups, - encoding="utf-8", - ) # type: logging.Handler - elif rotate_mode == "time": - channel = logging.handlers.TimedRotatingFileHandler( - filename=options.log_file_prefix, - when=options.log_rotate_when, - interval=options.log_rotate_interval, - backupCount=options.log_file_num_backups, - encoding="utf-8", - ) - else: - error_message = ( - "The value of log_rotate_mode option should be " - + '"size" or "time", not "%s".' % rotate_mode - ) - raise ValueError(error_message) - channel.setFormatter(LogFormatter(color=False)) - logger.addHandler(channel) - - if options.log_to_stderr or (options.log_to_stderr is None and not logger.handlers): - # Set up color if we are in a tty and curses is installed - channel = logging.StreamHandler() - channel.setFormatter(LogFormatter()) - logger.addHandler(channel) - - -def define_logging_options(options: Any = None) -> None: - """Add logging-related flags to ``options``. - - These options are present automatically on the default options instance; - this method is only necessary if you have created your own `.OptionParser`. - - .. versionadded:: 4.2 - This function existed in prior versions but was broken and undocumented until 4.2. - """ - if options is None: - # late import to prevent cycle - import tornado.options - - options = tornado.options.options - options.define( - "logging", - default="info", - help=( - "Set the Python log level. If 'none', tornado won't touch the " - "logging configuration." - ), - metavar="debug|info|warning|error|none", - ) - options.define( - "log_to_stderr", - type=bool, - default=None, - help=( - "Send log output to stderr (colorized if possible). " - "By default use stderr if --log_file_prefix is not set and " - "no other logging is configured." - ), - ) - options.define( - "log_file_prefix", - type=str, - default=None, - metavar="PATH", - help=( - "Path prefix for log files. " - "Note that if you are running multiple tornado processes, " - "log_file_prefix must be different for each of them (e.g. " - "include the port number)" - ), - ) - options.define( - "log_file_max_size", - type=int, - default=100 * 1000 * 1000, - help="max size of log files before rollover", - ) - options.define( - "log_file_num_backups", type=int, default=10, help="number of log files to keep" - ) - - options.define( - "log_rotate_when", - type=str, - default="midnight", - help=( - "specify the type of TimedRotatingFileHandler interval " - "other options:('S', 'M', 'H', 'D', 'W0'-'W6')" - ), - ) - options.define( - "log_rotate_interval", - type=int, - default=1, - help="The interval value of timed rotating", - ) - - options.define( - "log_rotate_mode", - type=str, - default="size", - help="The mode of rotating files(time or size)", - ) - - options.add_parse_callback(lambda: enable_pretty_logging(options)) diff --git a/telegramer/include/tornado/netutil.py b/telegramer/include/tornado/netutil.py deleted file mode 100644 index 868d3e9..0000000 --- a/telegramer/include/tornado/netutil.py +++ /dev/null @@ -1,617 +0,0 @@ -# -# Copyright 2011 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Miscellaneous network utility code.""" - -import concurrent.futures -import errno -import os -import sys -import socket -import ssl -import stat - -from tornado.concurrent import dummy_executor, run_on_executor -from tornado.ioloop import IOLoop -from tornado.util import Configurable, errno_from_exception - -from typing import List, Callable, Any, Type, Dict, Union, Tuple, Awaitable, Optional - -# Note that the naming of ssl.Purpose is confusing; the purpose -# of a context is to authentiate the opposite side of the connection. -_client_ssl_defaults = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) -_server_ssl_defaults = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) -if hasattr(ssl, "OP_NO_COMPRESSION"): - # See netutil.ssl_options_to_context - _client_ssl_defaults.options |= ssl.OP_NO_COMPRESSION - _server_ssl_defaults.options |= ssl.OP_NO_COMPRESSION - -# ThreadedResolver runs getaddrinfo on a thread. If the hostname is unicode, -# getaddrinfo attempts to import encodings.idna. If this is done at -# module-import time, the import lock is already held by the main thread, -# leading to deadlock. Avoid it by caching the idna encoder on the main -# thread now. -u"foo".encode("idna") - -# For undiagnosed reasons, 'latin1' codec may also need to be preloaded. -u"foo".encode("latin1") - -# Default backlog used when calling sock.listen() -_DEFAULT_BACKLOG = 128 - - -def bind_sockets( - port: int, - address: Optional[str] = None, - family: socket.AddressFamily = socket.AF_UNSPEC, - backlog: int = _DEFAULT_BACKLOG, - flags: Optional[int] = None, - reuse_port: bool = False, -) -> List[socket.socket]: - """Creates listening sockets bound to the given port and address. - - Returns a list of socket objects (multiple sockets are returned if - the given address maps to multiple IP addresses, which is most common - for mixed IPv4 and IPv6 use). - - Address may be either an IP address or hostname. If it's a hostname, - the server will listen on all IP addresses associated with the - name. Address may be an empty string or None to listen on all - available interfaces. Family may be set to either `socket.AF_INET` - or `socket.AF_INET6` to restrict to IPv4 or IPv6 addresses, otherwise - both will be used if available. - - The ``backlog`` argument has the same meaning as for - `socket.listen() <socket.socket.listen>`. - - ``flags`` is a bitmask of AI_* flags to `~socket.getaddrinfo`, like - ``socket.AI_PASSIVE | socket.AI_NUMERICHOST``. - - ``reuse_port`` option sets ``SO_REUSEPORT`` option for every socket - in the list. If your platform doesn't support this option ValueError will - be raised. - """ - if reuse_port and not hasattr(socket, "SO_REUSEPORT"): - raise ValueError("the platform doesn't support SO_REUSEPORT") - - sockets = [] - if address == "": - address = None - if not socket.has_ipv6 and family == socket.AF_UNSPEC: - # Python can be compiled with --disable-ipv6, which causes - # operations on AF_INET6 sockets to fail, but does not - # automatically exclude those results from getaddrinfo - # results. - # http://bugs.python.org/issue16208 - family = socket.AF_INET - if flags is None: - flags = socket.AI_PASSIVE - bound_port = None - unique_addresses = set() # type: set - for res in sorted( - socket.getaddrinfo(address, port, family, socket.SOCK_STREAM, 0, flags), - key=lambda x: x[0], - ): - if res in unique_addresses: - continue - - unique_addresses.add(res) - - af, socktype, proto, canonname, sockaddr = res - if ( - sys.platform == "darwin" - and address == "localhost" - and af == socket.AF_INET6 - and sockaddr[3] != 0 - ): - # Mac OS X includes a link-local address fe80::1%lo0 in the - # getaddrinfo results for 'localhost'. However, the firewall - # doesn't understand that this is a local address and will - # prompt for access (often repeatedly, due to an apparent - # bug in its ability to remember granting access to an - # application). Skip these addresses. - continue - try: - sock = socket.socket(af, socktype, proto) - except socket.error as e: - if errno_from_exception(e) == errno.EAFNOSUPPORT: - continue - raise - if os.name != "nt": - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - except socket.error as e: - if errno_from_exception(e) != errno.ENOPROTOOPT: - # Hurd doesn't support SO_REUSEADDR. - raise - if reuse_port: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - if af == socket.AF_INET6: - # On linux, ipv6 sockets accept ipv4 too by default, - # but this makes it impossible to bind to both - # 0.0.0.0 in ipv4 and :: in ipv6. On other systems, - # separate sockets *must* be used to listen for both ipv4 - # and ipv6. For consistency, always disable ipv4 on our - # ipv6 sockets and use a separate ipv4 socket when needed. - # - # Python 2.x on windows doesn't have IPPROTO_IPV6. - if hasattr(socket, "IPPROTO_IPV6"): - sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) - - # automatic port allocation with port=None - # should bind on the same port on IPv4 and IPv6 - host, requested_port = sockaddr[:2] - if requested_port == 0 and bound_port is not None: - sockaddr = tuple([host, bound_port] + list(sockaddr[2:])) - - sock.setblocking(False) - try: - sock.bind(sockaddr) - except OSError as e: - if ( - errno_from_exception(e) == errno.EADDRNOTAVAIL - and address == "localhost" - and sockaddr[0] == "::1" - ): - # On some systems (most notably docker with default - # configurations), ipv6 is partially disabled: - # socket.has_ipv6 is true, we can create AF_INET6 - # sockets, and getaddrinfo("localhost", ..., - # AF_PASSIVE) resolves to ::1, but we get an error - # when binding. - # - # Swallow the error, but only for this specific case. - # If EADDRNOTAVAIL occurs in other situations, it - # might be a real problem like a typo in a - # configuration. - sock.close() - continue - else: - raise - bound_port = sock.getsockname()[1] - sock.listen(backlog) - sockets.append(sock) - return sockets - - -if hasattr(socket, "AF_UNIX"): - - def bind_unix_socket( - file: str, mode: int = 0o600, backlog: int = _DEFAULT_BACKLOG - ) -> socket.socket: - """Creates a listening unix socket. - - If a socket with the given name already exists, it will be deleted. - If any other file with that name exists, an exception will be - raised. - - Returns a socket object (not a list of socket objects like - `bind_sockets`) - """ - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - except socket.error as e: - if errno_from_exception(e) != errno.ENOPROTOOPT: - # Hurd doesn't support SO_REUSEADDR - raise - sock.setblocking(False) - try: - st = os.stat(file) - except FileNotFoundError: - pass - else: - if stat.S_ISSOCK(st.st_mode): - os.remove(file) - else: - raise ValueError("File %s exists and is not a socket", file) - sock.bind(file) - os.chmod(file, mode) - sock.listen(backlog) - return sock - - -def add_accept_handler( - sock: socket.socket, callback: Callable[[socket.socket, Any], None] -) -> Callable[[], None]: - """Adds an `.IOLoop` event handler to accept new connections on ``sock``. - - When a connection is accepted, ``callback(connection, address)`` will - be run (``connection`` is a socket object, and ``address`` is the - address of the other end of the connection). Note that this signature - is different from the ``callback(fd, events)`` signature used for - `.IOLoop` handlers. - - A callable is returned which, when called, will remove the `.IOLoop` - event handler and stop processing further incoming connections. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - - .. versionchanged:: 5.0 - A callable is returned (``None`` was returned before). - """ - io_loop = IOLoop.current() - removed = [False] - - def accept_handler(fd: socket.socket, events: int) -> None: - # More connections may come in while we're handling callbacks; - # to prevent starvation of other tasks we must limit the number - # of connections we accept at a time. Ideally we would accept - # up to the number of connections that were waiting when we - # entered this method, but this information is not available - # (and rearranging this method to call accept() as many times - # as possible before running any callbacks would have adverse - # effects on load balancing in multiprocess configurations). - # Instead, we use the (default) listen backlog as a rough - # heuristic for the number of connections we can reasonably - # accept at once. - for i in range(_DEFAULT_BACKLOG): - if removed[0]: - # The socket was probably closed - return - try: - connection, address = sock.accept() - except BlockingIOError: - # EWOULDBLOCK indicates we have accepted every - # connection that is available. - return - except ConnectionAbortedError: - # ECONNABORTED indicates that there was a connection - # but it was closed while still in the accept queue. - # (observed on FreeBSD). - continue - callback(connection, address) - - def remove_handler() -> None: - io_loop.remove_handler(sock) - removed[0] = True - - io_loop.add_handler(sock, accept_handler, IOLoop.READ) - return remove_handler - - -def is_valid_ip(ip: str) -> bool: - """Returns ``True`` if the given string is a well-formed IP address. - - Supports IPv4 and IPv6. - """ - if not ip or "\x00" in ip: - # getaddrinfo resolves empty strings to localhost, and truncates - # on zero bytes. - return False - try: - res = socket.getaddrinfo( - ip, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_NUMERICHOST - ) - return bool(res) - except socket.gaierror as e: - if e.args[0] == socket.EAI_NONAME: - return False - raise - return True - - -class Resolver(Configurable): - """Configurable asynchronous DNS resolver interface. - - By default, a blocking implementation is used (which simply calls - `socket.getaddrinfo`). An alternative implementation can be - chosen with the `Resolver.configure <.Configurable.configure>` - class method:: - - Resolver.configure('tornado.netutil.ThreadedResolver') - - The implementations of this interface included with Tornado are - - * `tornado.netutil.DefaultExecutorResolver` - * `tornado.netutil.BlockingResolver` (deprecated) - * `tornado.netutil.ThreadedResolver` (deprecated) - * `tornado.netutil.OverrideResolver` - * `tornado.platform.twisted.TwistedResolver` - * `tornado.platform.caresresolver.CaresResolver` - - .. versionchanged:: 5.0 - The default implementation has changed from `BlockingResolver` to - `DefaultExecutorResolver`. - """ - - @classmethod - def configurable_base(cls) -> Type["Resolver"]: - return Resolver - - @classmethod - def configurable_default(cls) -> Type["Resolver"]: - return DefaultExecutorResolver - - def resolve( - self, host: str, port: int, family: socket.AddressFamily = socket.AF_UNSPEC - ) -> Awaitable[List[Tuple[int, Any]]]: - """Resolves an address. - - The ``host`` argument is a string which may be a hostname or a - literal IP address. - - Returns a `.Future` whose result is a list of (family, - address) pairs, where address is a tuple suitable to pass to - `socket.connect <socket.socket.connect>` (i.e. a ``(host, - port)`` pair for IPv4; additional fields may be present for - IPv6). If a ``callback`` is passed, it will be run with the - result as an argument when it is complete. - - :raises IOError: if the address cannot be resolved. - - .. versionchanged:: 4.4 - Standardized all implementations to raise `IOError`. - - .. versionchanged:: 6.0 The ``callback`` argument was removed. - Use the returned awaitable object instead. - - """ - raise NotImplementedError() - - def close(self) -> None: - """Closes the `Resolver`, freeing any resources used. - - .. versionadded:: 3.1 - - """ - pass - - -def _resolve_addr( - host: str, port: int, family: socket.AddressFamily = socket.AF_UNSPEC -) -> List[Tuple[int, Any]]: - # On Solaris, getaddrinfo fails if the given port is not found - # in /etc/services and no socket type is given, so we must pass - # one here. The socket type used here doesn't seem to actually - # matter (we discard the one we get back in the results), - # so the addresses we return should still be usable with SOCK_DGRAM. - addrinfo = socket.getaddrinfo(host, port, family, socket.SOCK_STREAM) - results = [] - for fam, socktype, proto, canonname, address in addrinfo: - results.append((fam, address)) - return results # type: ignore - - -class DefaultExecutorResolver(Resolver): - """Resolver implementation using `.IOLoop.run_in_executor`. - - .. versionadded:: 5.0 - """ - - async def resolve( - self, host: str, port: int, family: socket.AddressFamily = socket.AF_UNSPEC - ) -> List[Tuple[int, Any]]: - result = await IOLoop.current().run_in_executor( - None, _resolve_addr, host, port, family - ) - return result - - -class ExecutorResolver(Resolver): - """Resolver implementation using a `concurrent.futures.Executor`. - - Use this instead of `ThreadedResolver` when you require additional - control over the executor being used. - - The executor will be shut down when the resolver is closed unless - ``close_resolver=False``; use this if you want to reuse the same - executor elsewhere. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - - .. deprecated:: 5.0 - The default `Resolver` now uses `.IOLoop.run_in_executor`; use that instead - of this class. - """ - - def initialize( - self, - executor: Optional[concurrent.futures.Executor] = None, - close_executor: bool = True, - ) -> None: - self.io_loop = IOLoop.current() - if executor is not None: - self.executor = executor - self.close_executor = close_executor - else: - self.executor = dummy_executor - self.close_executor = False - - def close(self) -> None: - if self.close_executor: - self.executor.shutdown() - self.executor = None # type: ignore - - @run_on_executor - def resolve( - self, host: str, port: int, family: socket.AddressFamily = socket.AF_UNSPEC - ) -> List[Tuple[int, Any]]: - return _resolve_addr(host, port, family) - - -class BlockingResolver(ExecutorResolver): - """Default `Resolver` implementation, using `socket.getaddrinfo`. - - The `.IOLoop` will be blocked during the resolution, although the - callback will not be run until the next `.IOLoop` iteration. - - .. deprecated:: 5.0 - The default `Resolver` now uses `.IOLoop.run_in_executor`; use that instead - of this class. - """ - - def initialize(self) -> None: # type: ignore - super().initialize() - - -class ThreadedResolver(ExecutorResolver): - """Multithreaded non-blocking `Resolver` implementation. - - Requires the `concurrent.futures` package to be installed - (available in the standard library since Python 3.2, - installable with ``pip install futures`` in older versions). - - The thread pool size can be configured with:: - - Resolver.configure('tornado.netutil.ThreadedResolver', - num_threads=10) - - .. versionchanged:: 3.1 - All ``ThreadedResolvers`` share a single thread pool, whose - size is set by the first one to be created. - - .. deprecated:: 5.0 - The default `Resolver` now uses `.IOLoop.run_in_executor`; use that instead - of this class. - """ - - _threadpool = None # type: ignore - _threadpool_pid = None # type: int - - def initialize(self, num_threads: int = 10) -> None: # type: ignore - threadpool = ThreadedResolver._create_threadpool(num_threads) - super().initialize(executor=threadpool, close_executor=False) - - @classmethod - def _create_threadpool( - cls, num_threads: int - ) -> concurrent.futures.ThreadPoolExecutor: - pid = os.getpid() - if cls._threadpool_pid != pid: - # Threads cannot survive after a fork, so if our pid isn't what it - # was when we created the pool then delete it. - cls._threadpool = None - if cls._threadpool is None: - cls._threadpool = concurrent.futures.ThreadPoolExecutor(num_threads) - cls._threadpool_pid = pid - return cls._threadpool - - -class OverrideResolver(Resolver): - """Wraps a resolver with a mapping of overrides. - - This can be used to make local DNS changes (e.g. for testing) - without modifying system-wide settings. - - The mapping can be in three formats:: - - { - # Hostname to host or ip - "example.com": "127.0.1.1", - - # Host+port to host+port - ("login.example.com", 443): ("localhost", 1443), - - # Host+port+address family to host+port - ("login.example.com", 443, socket.AF_INET6): ("::1", 1443), - } - - .. versionchanged:: 5.0 - Added support for host-port-family triplets. - """ - - def initialize(self, resolver: Resolver, mapping: dict) -> None: - self.resolver = resolver - self.mapping = mapping - - def close(self) -> None: - self.resolver.close() - - def resolve( - self, host: str, port: int, family: socket.AddressFamily = socket.AF_UNSPEC - ) -> Awaitable[List[Tuple[int, Any]]]: - if (host, port, family) in self.mapping: - host, port = self.mapping[(host, port, family)] - elif (host, port) in self.mapping: - host, port = self.mapping[(host, port)] - elif host in self.mapping: - host = self.mapping[host] - return self.resolver.resolve(host, port, family) - - -# These are the keyword arguments to ssl.wrap_socket that must be translated -# to their SSLContext equivalents (the other arguments are still passed -# to SSLContext.wrap_socket). -_SSL_CONTEXT_KEYWORDS = frozenset( - ["ssl_version", "certfile", "keyfile", "cert_reqs", "ca_certs", "ciphers"] -) - - -def ssl_options_to_context( - ssl_options: Union[Dict[str, Any], ssl.SSLContext] -) -> ssl.SSLContext: - """Try to convert an ``ssl_options`` dictionary to an - `~ssl.SSLContext` object. - - The ``ssl_options`` dictionary contains keywords to be passed to - `ssl.wrap_socket`. In Python 2.7.9+, `ssl.SSLContext` objects can - be used instead. This function converts the dict form to its - `~ssl.SSLContext` equivalent, and may be used when a component which - accepts both forms needs to upgrade to the `~ssl.SSLContext` version - to use features like SNI or NPN. - """ - if isinstance(ssl_options, ssl.SSLContext): - return ssl_options - assert isinstance(ssl_options, dict) - assert all(k in _SSL_CONTEXT_KEYWORDS for k in ssl_options), ssl_options - # Can't use create_default_context since this interface doesn't - # tell us client vs server. - context = ssl.SSLContext(ssl_options.get("ssl_version", ssl.PROTOCOL_SSLv23)) - if "certfile" in ssl_options: - context.load_cert_chain( - ssl_options["certfile"], ssl_options.get("keyfile", None) - ) - if "cert_reqs" in ssl_options: - context.verify_mode = ssl_options["cert_reqs"] - if "ca_certs" in ssl_options: - context.load_verify_locations(ssl_options["ca_certs"]) - if "ciphers" in ssl_options: - context.set_ciphers(ssl_options["ciphers"]) - if hasattr(ssl, "OP_NO_COMPRESSION"): - # Disable TLS compression to avoid CRIME and related attacks. - # This constant depends on openssl version 1.0. - # TODO: Do we need to do this ourselves or can we trust - # the defaults? - context.options |= ssl.OP_NO_COMPRESSION - return context - - -def ssl_wrap_socket( - socket: socket.socket, - ssl_options: Union[Dict[str, Any], ssl.SSLContext], - server_hostname: Optional[str] = None, - **kwargs: Any -) -> ssl.SSLSocket: - """Returns an ``ssl.SSLSocket`` wrapping the given socket. - - ``ssl_options`` may be either an `ssl.SSLContext` object or a - dictionary (as accepted by `ssl_options_to_context`). Additional - keyword arguments are passed to ``wrap_socket`` (either the - `~ssl.SSLContext` method or the `ssl` module function as - appropriate). - """ - context = ssl_options_to_context(ssl_options) - if ssl.HAS_SNI: - # In python 3.4, wrap_socket only accepts the server_hostname - # argument if HAS_SNI is true. - # TODO: add a unittest (python added server-side SNI support in 3.4) - # In the meantime it can be manually tested with - # python3 -m tornado.httpclient https://sni.velox.ch - return context.wrap_socket(socket, server_hostname=server_hostname, **kwargs) - else: - return context.wrap_socket(socket, **kwargs) diff --git a/telegramer/include/tornado/options.py b/telegramer/include/tornado/options.py deleted file mode 100644 index f0b89a9..0000000 --- a/telegramer/include/tornado/options.py +++ /dev/null @@ -1,735 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A command line parsing module that lets modules define their own options. - -This module is inspired by Google's `gflags -<https://github.com/google/python-gflags>`_. The primary difference -with libraries such as `argparse` is that a global registry is used so -that options may be defined in any module (it also enables -`tornado.log` by default). The rest of Tornado does not depend on this -module, so feel free to use `argparse` or other configuration -libraries if you prefer them. - -Options must be defined with `tornado.options.define` before use, -generally at the top level of a module. The options are then -accessible as attributes of `tornado.options.options`:: - - # myapp/db.py - from tornado.options import define, options - - define("mysql_host", default="127.0.0.1:3306", help="Main user DB") - define("memcache_hosts", default="127.0.0.1:11011", multiple=True, - help="Main user memcache servers") - - def connect(): - db = database.Connection(options.mysql_host) - ... - - # myapp/server.py - from tornado.options import define, options - - define("port", default=8080, help="port to listen on") - - def start_server(): - app = make_app() - app.listen(options.port) - -The ``main()`` method of your application does not need to be aware of all of -the options used throughout your program; they are all automatically loaded -when the modules are loaded. However, all modules that define options -must have been imported before the command line is parsed. - -Your ``main()`` method can parse the command line or parse a config file with -either `parse_command_line` or `parse_config_file`:: - - import myapp.db, myapp.server - import tornado.options - - if __name__ == '__main__': - tornado.options.parse_command_line() - # or - tornado.options.parse_config_file("/etc/server.conf") - -.. note:: - - When using multiple ``parse_*`` functions, pass ``final=False`` to all - but the last one, or side effects may occur twice (in particular, - this can result in log messages being doubled). - -`tornado.options.options` is a singleton instance of `OptionParser`, and -the top-level functions in this module (`define`, `parse_command_line`, etc) -simply call methods on it. You may create additional `OptionParser` -instances to define isolated sets of options, such as for subcommands. - -.. note:: - - By default, several options are defined that will configure the - standard `logging` module when `parse_command_line` or `parse_config_file` - are called. If you want Tornado to leave the logging configuration - alone so you can manage it yourself, either pass ``--logging=none`` - on the command line or do the following to disable it in code:: - - from tornado.options import options, parse_command_line - options.logging = None - parse_command_line() - -.. versionchanged:: 4.3 - Dashes and underscores are fully interchangeable in option names; - options can be defined, set, and read with any mix of the two. - Dashes are typical for command-line usage while config files require - underscores. -""" - -import datetime -import numbers -import re -import sys -import os -import textwrap - -from tornado.escape import _unicode, native_str -from tornado.log import define_logging_options -from tornado.util import basestring_type, exec_in - -from typing import ( - Any, - Iterator, - Iterable, - Tuple, - Set, - Dict, - Callable, - List, - TextIO, - Optional, -) - - -class Error(Exception): - """Exception raised by errors in the options module.""" - - pass - - -class OptionParser(object): - """A collection of options, a dictionary with object-like access. - - Normally accessed via static functions in the `tornado.options` module, - which reference a global instance. - """ - - def __init__(self) -> None: - # we have to use self.__dict__ because we override setattr. - self.__dict__["_options"] = {} - self.__dict__["_parse_callbacks"] = [] - self.define( - "help", - type=bool, - help="show this help information", - callback=self._help_callback, - ) - - def _normalize_name(self, name: str) -> str: - return name.replace("_", "-") - - def __getattr__(self, name: str) -> Any: - name = self._normalize_name(name) - if isinstance(self._options.get(name), _Option): - return self._options[name].value() - raise AttributeError("Unrecognized option %r" % name) - - def __setattr__(self, name: str, value: Any) -> None: - name = self._normalize_name(name) - if isinstance(self._options.get(name), _Option): - return self._options[name].set(value) - raise AttributeError("Unrecognized option %r" % name) - - def __iter__(self) -> Iterator: - return (opt.name for opt in self._options.values()) - - def __contains__(self, name: str) -> bool: - name = self._normalize_name(name) - return name in self._options - - def __getitem__(self, name: str) -> Any: - return self.__getattr__(name) - - def __setitem__(self, name: str, value: Any) -> None: - return self.__setattr__(name, value) - - def items(self) -> Iterable[Tuple[str, Any]]: - """An iterable of (name, value) pairs. - - .. versionadded:: 3.1 - """ - return [(opt.name, opt.value()) for name, opt in self._options.items()] - - def groups(self) -> Set[str]: - """The set of option-groups created by ``define``. - - .. versionadded:: 3.1 - """ - return set(opt.group_name for opt in self._options.values()) - - def group_dict(self, group: str) -> Dict[str, Any]: - """The names and values of options in a group. - - Useful for copying options into Application settings:: - - from tornado.options import define, parse_command_line, options - - define('template_path', group='application') - define('static_path', group='application') - - parse_command_line() - - application = Application( - handlers, **options.group_dict('application')) - - .. versionadded:: 3.1 - """ - return dict( - (opt.name, opt.value()) - for name, opt in self._options.items() - if not group or group == opt.group_name - ) - - def as_dict(self) -> Dict[str, Any]: - """The names and values of all options. - - .. versionadded:: 3.1 - """ - return dict((opt.name, opt.value()) for name, opt in self._options.items()) - - def define( - self, - name: str, - default: Any = None, - type: Optional[type] = None, - help: Optional[str] = None, - metavar: Optional[str] = None, - multiple: bool = False, - group: Optional[str] = None, - callback: Optional[Callable[[Any], None]] = None, - ) -> None: - """Defines a new command line option. - - ``type`` can be any of `str`, `int`, `float`, `bool`, - `~datetime.datetime`, or `~datetime.timedelta`. If no ``type`` - is given but a ``default`` is, ``type`` is the type of - ``default``. Otherwise, ``type`` defaults to `str`. - - If ``multiple`` is True, the option value is a list of ``type`` - instead of an instance of ``type``. - - ``help`` and ``metavar`` are used to construct the - automatically generated command line help string. The help - message is formatted like:: - - --name=METAVAR help string - - ``group`` is used to group the defined options in logical - groups. By default, command line options are grouped by the - file in which they are defined. - - Command line option names must be unique globally. - - If a ``callback`` is given, it will be run with the new value whenever - the option is changed. This can be used to combine command-line - and file-based options:: - - define("config", type=str, help="path to config file", - callback=lambda path: parse_config_file(path, final=False)) - - With this definition, options in the file specified by ``--config`` will - override options set earlier on the command line, but can be overridden - by later flags. - - """ - normalized = self._normalize_name(name) - if normalized in self._options: - raise Error( - "Option %r already defined in %s" - % (normalized, self._options[normalized].file_name) - ) - frame = sys._getframe(0) - options_file = frame.f_code.co_filename - - # Can be called directly, or through top level define() fn, in which - # case, step up above that frame to look for real caller. - if ( - frame.f_back.f_code.co_filename == options_file - and frame.f_back.f_code.co_name == "define" - ): - frame = frame.f_back - - file_name = frame.f_back.f_code.co_filename - if file_name == options_file: - file_name = "" - if type is None: - if not multiple and default is not None: - type = default.__class__ - else: - type = str - if group: - group_name = group # type: Optional[str] - else: - group_name = file_name - option = _Option( - name, - file_name=file_name, - default=default, - type=type, - help=help, - metavar=metavar, - multiple=multiple, - group_name=group_name, - callback=callback, - ) - self._options[normalized] = option - - def parse_command_line( - self, args: Optional[List[str]] = None, final: bool = True - ) -> List[str]: - """Parses all options given on the command line (defaults to - `sys.argv`). - - Options look like ``--option=value`` and are parsed according - to their ``type``. For boolean options, ``--option`` is - equivalent to ``--option=true`` - - If the option has ``multiple=True``, comma-separated values - are accepted. For multi-value integer options, the syntax - ``x:y`` is also accepted and equivalent to ``range(x, y)``. - - Note that ``args[0]`` is ignored since it is the program name - in `sys.argv`. - - We return a list of all arguments that are not parsed as options. - - If ``final`` is ``False``, parse callbacks will not be run. - This is useful for applications that wish to combine configurations - from multiple sources. - - """ - if args is None: - args = sys.argv - remaining = [] # type: List[str] - for i in range(1, len(args)): - # All things after the last option are command line arguments - if not args[i].startswith("-"): - remaining = args[i:] - break - if args[i] == "--": - remaining = args[i + 1 :] - break - arg = args[i].lstrip("-") - name, equals, value = arg.partition("=") - name = self._normalize_name(name) - if name not in self._options: - self.print_help() - raise Error("Unrecognized command line option: %r" % name) - option = self._options[name] - if not equals: - if option.type == bool: - value = "true" - else: - raise Error("Option %r requires a value" % name) - option.parse(value) - - if final: - self.run_parse_callbacks() - - return remaining - - def parse_config_file(self, path: str, final: bool = True) -> None: - """Parses and loads the config file at the given path. - - The config file contains Python code that will be executed (so - it is **not safe** to use untrusted config files). Anything in - the global namespace that matches a defined option will be - used to set that option's value. - - Options may either be the specified type for the option or - strings (in which case they will be parsed the same way as in - `.parse_command_line`) - - Example (using the options defined in the top-level docs of - this module):: - - port = 80 - mysql_host = 'mydb.example.com:3306' - # Both lists and comma-separated strings are allowed for - # multiple=True. - memcache_hosts = ['cache1.example.com:11011', - 'cache2.example.com:11011'] - memcache_hosts = 'cache1.example.com:11011,cache2.example.com:11011' - - If ``final`` is ``False``, parse callbacks will not be run. - This is useful for applications that wish to combine configurations - from multiple sources. - - .. note:: - - `tornado.options` is primarily a command-line library. - Config file support is provided for applications that wish - to use it, but applications that prefer config files may - wish to look at other libraries instead. - - .. versionchanged:: 4.1 - Config files are now always interpreted as utf-8 instead of - the system default encoding. - - .. versionchanged:: 4.4 - The special variable ``__file__`` is available inside config - files, specifying the absolute path to the config file itself. - - .. versionchanged:: 5.1 - Added the ability to set options via strings in config files. - - """ - config = {"__file__": os.path.abspath(path)} - with open(path, "rb") as f: - exec_in(native_str(f.read()), config, config) - for name in config: - normalized = self._normalize_name(name) - if normalized in self._options: - option = self._options[normalized] - if option.multiple: - if not isinstance(config[name], (list, str)): - raise Error( - "Option %r is required to be a list of %s " - "or a comma-separated string" - % (option.name, option.type.__name__) - ) - - if type(config[name]) == str and option.type != str: - option.parse(config[name]) - else: - option.set(config[name]) - - if final: - self.run_parse_callbacks() - - def print_help(self, file: Optional[TextIO] = None) -> None: - """Prints all the command line options to stderr (or another file).""" - if file is None: - file = sys.stderr - print("Usage: %s [OPTIONS]" % sys.argv[0], file=file) - print("\nOptions:\n", file=file) - by_group = {} # type: Dict[str, List[_Option]] - for option in self._options.values(): - by_group.setdefault(option.group_name, []).append(option) - - for filename, o in sorted(by_group.items()): - if filename: - print("\n%s options:\n" % os.path.normpath(filename), file=file) - o.sort(key=lambda option: option.name) - for option in o: - # Always print names with dashes in a CLI context. - prefix = self._normalize_name(option.name) - if option.metavar: - prefix += "=" + option.metavar - description = option.help or "" - if option.default is not None and option.default != "": - description += " (default %s)" % option.default - lines = textwrap.wrap(description, 79 - 35) - if len(prefix) > 30 or len(lines) == 0: - lines.insert(0, "") - print(" --%-30s %s" % (prefix, lines[0]), file=file) - for line in lines[1:]: - print("%-34s %s" % (" ", line), file=file) - print(file=file) - - def _help_callback(self, value: bool) -> None: - if value: - self.print_help() - sys.exit(0) - - def add_parse_callback(self, callback: Callable[[], None]) -> None: - """Adds a parse callback, to be invoked when option parsing is done.""" - self._parse_callbacks.append(callback) - - def run_parse_callbacks(self) -> None: - for callback in self._parse_callbacks: - callback() - - def mockable(self) -> "_Mockable": - """Returns a wrapper around self that is compatible with - `mock.patch <unittest.mock.patch>`. - - The `mock.patch <unittest.mock.patch>` function (included in - the standard library `unittest.mock` package since Python 3.3, - or in the third-party ``mock`` package for older versions of - Python) is incompatible with objects like ``options`` that - override ``__getattr__`` and ``__setattr__``. This function - returns an object that can be used with `mock.patch.object - <unittest.mock.patch.object>` to modify option values:: - - with mock.patch.object(options.mockable(), 'name', value): - assert options.name == value - """ - return _Mockable(self) - - -class _Mockable(object): - """`mock.patch` compatible wrapper for `OptionParser`. - - As of ``mock`` version 1.0.1, when an object uses ``__getattr__`` - hooks instead of ``__dict__``, ``patch.__exit__`` tries to delete - the attribute it set instead of setting a new one (assuming that - the object does not capture ``__setattr__``, so the patch - created a new attribute in ``__dict__``). - - _Mockable's getattr and setattr pass through to the underlying - OptionParser, and delattr undoes the effect of a previous setattr. - """ - - def __init__(self, options: OptionParser) -> None: - # Modify __dict__ directly to bypass __setattr__ - self.__dict__["_options"] = options - self.__dict__["_originals"] = {} - - def __getattr__(self, name: str) -> Any: - return getattr(self._options, name) - - def __setattr__(self, name: str, value: Any) -> None: - assert name not in self._originals, "don't reuse mockable objects" - self._originals[name] = getattr(self._options, name) - setattr(self._options, name, value) - - def __delattr__(self, name: str) -> None: - setattr(self._options, name, self._originals.pop(name)) - - -class _Option(object): - # This class could almost be made generic, but the way the types - # interact with the multiple argument makes this tricky. (default - # and the callback use List[T], but type is still Type[T]). - UNSET = object() - - def __init__( - self, - name: str, - default: Any = None, - type: Optional[type] = None, - help: Optional[str] = None, - metavar: Optional[str] = None, - multiple: bool = False, - file_name: Optional[str] = None, - group_name: Optional[str] = None, - callback: Optional[Callable[[Any], None]] = None, - ) -> None: - if default is None and multiple: - default = [] - self.name = name - if type is None: - raise ValueError("type must not be None") - self.type = type - self.help = help - self.metavar = metavar - self.multiple = multiple - self.file_name = file_name - self.group_name = group_name - self.callback = callback - self.default = default - self._value = _Option.UNSET # type: Any - - def value(self) -> Any: - return self.default if self._value is _Option.UNSET else self._value - - def parse(self, value: str) -> Any: - _parse = { - datetime.datetime: self._parse_datetime, - datetime.timedelta: self._parse_timedelta, - bool: self._parse_bool, - basestring_type: self._parse_string, - }.get( - self.type, self.type - ) # type: Callable[[str], Any] - if self.multiple: - self._value = [] - for part in value.split(","): - if issubclass(self.type, numbers.Integral): - # allow ranges of the form X:Y (inclusive at both ends) - lo_str, _, hi_str = part.partition(":") - lo = _parse(lo_str) - hi = _parse(hi_str) if hi_str else lo - self._value.extend(range(lo, hi + 1)) - else: - self._value.append(_parse(part)) - else: - self._value = _parse(value) - if self.callback is not None: - self.callback(self._value) - return self.value() - - def set(self, value: Any) -> None: - if self.multiple: - if not isinstance(value, list): - raise Error( - "Option %r is required to be a list of %s" - % (self.name, self.type.__name__) - ) - for item in value: - if item is not None and not isinstance(item, self.type): - raise Error( - "Option %r is required to be a list of %s" - % (self.name, self.type.__name__) - ) - else: - if value is not None and not isinstance(value, self.type): - raise Error( - "Option %r is required to be a %s (%s given)" - % (self.name, self.type.__name__, type(value)) - ) - self._value = value - if self.callback is not None: - self.callback(self._value) - - # Supported date/time formats in our options - _DATETIME_FORMATS = [ - "%a %b %d %H:%M:%S %Y", - "%Y-%m-%d %H:%M:%S", - "%Y-%m-%d %H:%M", - "%Y-%m-%dT%H:%M", - "%Y%m%d %H:%M:%S", - "%Y%m%d %H:%M", - "%Y-%m-%d", - "%Y%m%d", - "%H:%M:%S", - "%H:%M", - ] - - def _parse_datetime(self, value: str) -> datetime.datetime: - for format in self._DATETIME_FORMATS: - try: - return datetime.datetime.strptime(value, format) - except ValueError: - pass - raise Error("Unrecognized date/time format: %r" % value) - - _TIMEDELTA_ABBREV_DICT = { - "h": "hours", - "m": "minutes", - "min": "minutes", - "s": "seconds", - "sec": "seconds", - "ms": "milliseconds", - "us": "microseconds", - "d": "days", - "w": "weeks", - } - - _FLOAT_PATTERN = r"[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?" - - _TIMEDELTA_PATTERN = re.compile( - r"\s*(%s)\s*(\w*)\s*" % _FLOAT_PATTERN, re.IGNORECASE - ) - - def _parse_timedelta(self, value: str) -> datetime.timedelta: - try: - sum = datetime.timedelta() - start = 0 - while start < len(value): - m = self._TIMEDELTA_PATTERN.match(value, start) - if not m: - raise Exception() - num = float(m.group(1)) - units = m.group(2) or "seconds" - units = self._TIMEDELTA_ABBREV_DICT.get(units, units) - sum += datetime.timedelta(**{units: num}) - start = m.end() - return sum - except Exception: - raise - - def _parse_bool(self, value: str) -> bool: - return value.lower() not in ("false", "0", "f") - - def _parse_string(self, value: str) -> str: - return _unicode(value) - - -options = OptionParser() -"""Global options object. - -All defined options are available as attributes on this object. -""" - - -def define( - name: str, - default: Any = None, - type: Optional[type] = None, - help: Optional[str] = None, - metavar: Optional[str] = None, - multiple: bool = False, - group: Optional[str] = None, - callback: Optional[Callable[[Any], None]] = None, -) -> None: - """Defines an option in the global namespace. - - See `OptionParser.define`. - """ - return options.define( - name, - default=default, - type=type, - help=help, - metavar=metavar, - multiple=multiple, - group=group, - callback=callback, - ) - - -def parse_command_line( - args: Optional[List[str]] = None, final: bool = True -) -> List[str]: - """Parses global options from the command line. - - See `OptionParser.parse_command_line`. - """ - return options.parse_command_line(args, final=final) - - -def parse_config_file(path: str, final: bool = True) -> None: - """Parses global options from a config file. - - See `OptionParser.parse_config_file`. - """ - return options.parse_config_file(path, final=final) - - -def print_help(file: Optional[TextIO] = None) -> None: - """Prints all the command line options to stderr (or another file). - - See `OptionParser.print_help`. - """ - return options.print_help(file) - - -def add_parse_callback(callback: Callable[[], None]) -> None: - """Adds a parse callback, to be invoked when option parsing is done. - - See `OptionParser.add_parse_callback` - """ - options.add_parse_callback(callback) - - -# Default options -define_logging_options(options) diff --git a/telegramer/include/tornado/platform/__init__.py b/telegramer/include/tornado/platform/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/tornado/platform/asyncio.py b/telegramer/include/tornado/platform/asyncio.py deleted file mode 100644 index 012948b..0000000 --- a/telegramer/include/tornado/platform/asyncio.py +++ /dev/null @@ -1,611 +0,0 @@ -"""Bridges between the `asyncio` module and Tornado IOLoop. - -.. versionadded:: 3.2 - -This module integrates Tornado with the ``asyncio`` module introduced -in Python 3.4. This makes it possible to combine the two libraries on -the same event loop. - -.. deprecated:: 5.0 - - While the code in this module is still used, it is now enabled - automatically when `asyncio` is available, so applications should - no longer need to refer to this module directly. - -.. note:: - - Tornado is designed to use a selector-based event loop. On Windows, - where a proactor-based event loop has been the default since Python 3.8, - a selector event loop is emulated by running ``select`` on a separate thread. - Configuring ``asyncio`` to use a selector event loop may improve performance - of Tornado (but may reduce performance of other ``asyncio``-based libraries - in the same process). -""" - -import asyncio -import atexit -import concurrent.futures -import errno -import functools -import select -import socket -import sys -import threading -import typing -from tornado.gen import convert_yielded -from tornado.ioloop import IOLoop, _Selectable - -from typing import Any, TypeVar, Awaitable, Callable, Union, Optional, List, Tuple, Dict - -if typing.TYPE_CHECKING: - from typing import Set # noqa: F401 - from typing_extensions import Protocol - - class _HasFileno(Protocol): - def fileno(self) -> int: - pass - - _FileDescriptorLike = Union[int, _HasFileno] - -_T = TypeVar("_T") - - -# Collection of selector thread event loops to shut down on exit. -_selector_loops = set() # type: Set[AddThreadSelectorEventLoop] - - -def _atexit_callback() -> None: - for loop in _selector_loops: - with loop._select_cond: - loop._closing_selector = True - loop._select_cond.notify() - try: - loop._waker_w.send(b"a") - except BlockingIOError: - pass - # If we don't join our (daemon) thread here, we may get a deadlock - # during interpreter shutdown. I don't really understand why. This - # deadlock happens every time in CI (both travis and appveyor) but - # I've never been able to reproduce locally. - loop._thread.join() - _selector_loops.clear() - - -atexit.register(_atexit_callback) - - -class BaseAsyncIOLoop(IOLoop): - def initialize( # type: ignore - self, asyncio_loop: asyncio.AbstractEventLoop, **kwargs: Any - ) -> None: - # asyncio_loop is always the real underlying IOLoop. This is used in - # ioloop.py to maintain the asyncio-to-ioloop mappings. - self.asyncio_loop = asyncio_loop - # selector_loop is an event loop that implements the add_reader family of - # methods. Usually the same as asyncio_loop but differs on platforms such - # as windows where the default event loop does not implement these methods. - self.selector_loop = asyncio_loop - if hasattr(asyncio, "ProactorEventLoop") and isinstance( - asyncio_loop, asyncio.ProactorEventLoop # type: ignore - ): - # Ignore this line for mypy because the abstract method checker - # doesn't understand dynamic proxies. - self.selector_loop = AddThreadSelectorEventLoop(asyncio_loop) # type: ignore - # Maps fd to (fileobj, handler function) pair (as in IOLoop.add_handler) - self.handlers = {} # type: Dict[int, Tuple[Union[int, _Selectable], Callable]] - # Set of fds listening for reads/writes - self.readers = set() # type: Set[int] - self.writers = set() # type: Set[int] - self.closing = False - # If an asyncio loop was closed through an asyncio interface - # instead of IOLoop.close(), we'd never hear about it and may - # have left a dangling reference in our map. In case an - # application (or, more likely, a test suite) creates and - # destroys a lot of event loops in this way, check here to - # ensure that we don't have a lot of dead loops building up in - # the map. - # - # TODO(bdarnell): consider making self.asyncio_loop a weakref - # for AsyncIOMainLoop and make _ioloop_for_asyncio a - # WeakKeyDictionary. - for loop in list(IOLoop._ioloop_for_asyncio): - if loop.is_closed(): - del IOLoop._ioloop_for_asyncio[loop] - IOLoop._ioloop_for_asyncio[asyncio_loop] = self - - self._thread_identity = 0 - - super().initialize(**kwargs) - - def assign_thread_identity() -> None: - self._thread_identity = threading.get_ident() - - self.add_callback(assign_thread_identity) - - def close(self, all_fds: bool = False) -> None: - self.closing = True - for fd in list(self.handlers): - fileobj, handler_func = self.handlers[fd] - self.remove_handler(fd) - if all_fds: - self.close_fd(fileobj) - # Remove the mapping before closing the asyncio loop. If this - # happened in the other order, we could race against another - # initialize() call which would see the closed asyncio loop, - # assume it was closed from the asyncio side, and do this - # cleanup for us, leading to a KeyError. - del IOLoop._ioloop_for_asyncio[self.asyncio_loop] - if self.selector_loop is not self.asyncio_loop: - self.selector_loop.close() - self.asyncio_loop.close() - - def add_handler( - self, fd: Union[int, _Selectable], handler: Callable[..., None], events: int - ) -> None: - fd, fileobj = self.split_fd(fd) - if fd in self.handlers: - raise ValueError("fd %s added twice" % fd) - self.handlers[fd] = (fileobj, handler) - if events & IOLoop.READ: - self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ) - self.readers.add(fd) - if events & IOLoop.WRITE: - self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE) - self.writers.add(fd) - - def update_handler(self, fd: Union[int, _Selectable], events: int) -> None: - fd, fileobj = self.split_fd(fd) - if events & IOLoop.READ: - if fd not in self.readers: - self.selector_loop.add_reader(fd, self._handle_events, fd, IOLoop.READ) - self.readers.add(fd) - else: - if fd in self.readers: - self.selector_loop.remove_reader(fd) - self.readers.remove(fd) - if events & IOLoop.WRITE: - if fd not in self.writers: - self.selector_loop.add_writer(fd, self._handle_events, fd, IOLoop.WRITE) - self.writers.add(fd) - else: - if fd in self.writers: - self.selector_loop.remove_writer(fd) - self.writers.remove(fd) - - def remove_handler(self, fd: Union[int, _Selectable]) -> None: - fd, fileobj = self.split_fd(fd) - if fd not in self.handlers: - return - if fd in self.readers: - self.selector_loop.remove_reader(fd) - self.readers.remove(fd) - if fd in self.writers: - self.selector_loop.remove_writer(fd) - self.writers.remove(fd) - del self.handlers[fd] - - def _handle_events(self, fd: int, events: int) -> None: - fileobj, handler_func = self.handlers[fd] - handler_func(fileobj, events) - - def start(self) -> None: - try: - old_loop = asyncio.get_event_loop() - except (RuntimeError, AssertionError): - old_loop = None # type: ignore - try: - self._setup_logging() - asyncio.set_event_loop(self.asyncio_loop) - self.asyncio_loop.run_forever() - finally: - asyncio.set_event_loop(old_loop) - - def stop(self) -> None: - self.asyncio_loop.stop() - - def call_at( - self, when: float, callback: Callable[..., None], *args: Any, **kwargs: Any - ) -> object: - # asyncio.call_at supports *args but not **kwargs, so bind them here. - # We do not synchronize self.time and asyncio_loop.time, so - # convert from absolute to relative. - return self.asyncio_loop.call_later( - max(0, when - self.time()), - self._run_callback, - functools.partial(callback, *args, **kwargs), - ) - - def remove_timeout(self, timeout: object) -> None: - timeout.cancel() # type: ignore - - def add_callback(self, callback: Callable, *args: Any, **kwargs: Any) -> None: - if threading.get_ident() == self._thread_identity: - call_soon = self.asyncio_loop.call_soon - else: - call_soon = self.asyncio_loop.call_soon_threadsafe - try: - call_soon(self._run_callback, functools.partial(callback, *args, **kwargs)) - except RuntimeError: - # "Event loop is closed". Swallow the exception for - # consistency with PollIOLoop (and logical consistency - # with the fact that we can't guarantee that an - # add_callback that completes without error will - # eventually execute). - pass - except AttributeError: - # ProactorEventLoop may raise this instead of RuntimeError - # if call_soon_threadsafe races with a call to close(). - # Swallow it too for consistency. - pass - - def add_callback_from_signal( - self, callback: Callable, *args: Any, **kwargs: Any - ) -> None: - try: - self.asyncio_loop.call_soon_threadsafe( - self._run_callback, functools.partial(callback, *args, **kwargs) - ) - except RuntimeError: - pass - - def run_in_executor( - self, - executor: Optional[concurrent.futures.Executor], - func: Callable[..., _T], - *args: Any - ) -> Awaitable[_T]: - return self.asyncio_loop.run_in_executor(executor, func, *args) - - def set_default_executor(self, executor: concurrent.futures.Executor) -> None: - return self.asyncio_loop.set_default_executor(executor) - - -class AsyncIOMainLoop(BaseAsyncIOLoop): - """``AsyncIOMainLoop`` creates an `.IOLoop` that corresponds to the - current ``asyncio`` event loop (i.e. the one returned by - ``asyncio.get_event_loop()``). - - .. deprecated:: 5.0 - - Now used automatically when appropriate; it is no longer necessary - to refer to this class directly. - - .. versionchanged:: 5.0 - - Closing an `AsyncIOMainLoop` now closes the underlying asyncio loop. - """ - - def initialize(self, **kwargs: Any) -> None: # type: ignore - super().initialize(asyncio.get_event_loop(), **kwargs) - - def make_current(self) -> None: - # AsyncIOMainLoop already refers to the current asyncio loop so - # nothing to do here. - pass - - -class AsyncIOLoop(BaseAsyncIOLoop): - """``AsyncIOLoop`` is an `.IOLoop` that runs on an ``asyncio`` event loop. - This class follows the usual Tornado semantics for creating new - ``IOLoops``; these loops are not necessarily related to the - ``asyncio`` default event loop. - - Each ``AsyncIOLoop`` creates a new ``asyncio.EventLoop``; this object - can be accessed with the ``asyncio_loop`` attribute. - - .. versionchanged:: 5.0 - - When an ``AsyncIOLoop`` becomes the current `.IOLoop`, it also sets - the current `asyncio` event loop. - - .. deprecated:: 5.0 - - Now used automatically when appropriate; it is no longer necessary - to refer to this class directly. - """ - - def initialize(self, **kwargs: Any) -> None: # type: ignore - self.is_current = False - loop = asyncio.new_event_loop() - try: - super().initialize(loop, **kwargs) - except Exception: - # If initialize() does not succeed (taking ownership of the loop), - # we have to close it. - loop.close() - raise - - def close(self, all_fds: bool = False) -> None: - if self.is_current: - self.clear_current() - super().close(all_fds=all_fds) - - def make_current(self) -> None: - if not self.is_current: - try: - self.old_asyncio = asyncio.get_event_loop() - except (RuntimeError, AssertionError): - self.old_asyncio = None # type: ignore - self.is_current = True - asyncio.set_event_loop(self.asyncio_loop) - - def _clear_current_hook(self) -> None: - if self.is_current: - asyncio.set_event_loop(self.old_asyncio) - self.is_current = False - - -def to_tornado_future(asyncio_future: asyncio.Future) -> asyncio.Future: - """Convert an `asyncio.Future` to a `tornado.concurrent.Future`. - - .. versionadded:: 4.1 - - .. deprecated:: 5.0 - Tornado ``Futures`` have been merged with `asyncio.Future`, - so this method is now a no-op. - """ - return asyncio_future - - -def to_asyncio_future(tornado_future: asyncio.Future) -> asyncio.Future: - """Convert a Tornado yieldable object to an `asyncio.Future`. - - .. versionadded:: 4.1 - - .. versionchanged:: 4.3 - Now accepts any yieldable object, not just - `tornado.concurrent.Future`. - - .. deprecated:: 5.0 - Tornado ``Futures`` have been merged with `asyncio.Future`, - so this method is now equivalent to `tornado.gen.convert_yielded`. - """ - return convert_yielded(tornado_future) - - -if sys.platform == "win32" and hasattr(asyncio, "WindowsSelectorEventLoopPolicy"): - # "Any thread" and "selector" should be orthogonal, but there's not a clean - # interface for composing policies so pick the right base. - _BasePolicy = asyncio.WindowsSelectorEventLoopPolicy # type: ignore -else: - _BasePolicy = asyncio.DefaultEventLoopPolicy - - -class AnyThreadEventLoopPolicy(_BasePolicy): # type: ignore - """Event loop policy that allows loop creation on any thread. - - The default `asyncio` event loop policy only automatically creates - event loops in the main threads. Other threads must create event - loops explicitly or `asyncio.get_event_loop` (and therefore - `.IOLoop.current`) will fail. Installing this policy allows event - loops to be created automatically on any thread, matching the - behavior of Tornado versions prior to 5.0 (or 5.0 on Python 2). - - Usage:: - - asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy()) - - .. versionadded:: 5.0 - - """ - - def get_event_loop(self) -> asyncio.AbstractEventLoop: - try: - return super().get_event_loop() - except (RuntimeError, AssertionError): - # This was an AssertionError in Python 3.4.2 (which ships with Debian Jessie) - # and changed to a RuntimeError in 3.4.3. - # "There is no current event loop in thread %r" - loop = self.new_event_loop() - self.set_event_loop(loop) - return loop - - -class AddThreadSelectorEventLoop(asyncio.AbstractEventLoop): - """Wrap an event loop to add implementations of the ``add_reader`` method family. - - Instances of this class start a second thread to run a selector. - This thread is completely hidden from the user; all callbacks are - run on the wrapped event loop's thread. - - This class is used automatically by Tornado; applications should not need - to refer to it directly. - - It is safe to wrap any event loop with this class, although it only makes sense - for event loops that do not implement the ``add_reader`` family of methods - themselves (i.e. ``WindowsProactorEventLoop``) - - Closing the ``AddThreadSelectorEventLoop`` also closes the wrapped event loop. - - """ - - # This class is a __getattribute__-based proxy. All attributes other than those - # in this set are proxied through to the underlying loop. - MY_ATTRIBUTES = { - "_consume_waker", - "_select_cond", - "_select_args", - "_closing_selector", - "_thread", - "_handle_event", - "_readers", - "_real_loop", - "_start_select", - "_run_select", - "_handle_select", - "_wake_selector", - "_waker_r", - "_waker_w", - "_writers", - "add_reader", - "add_writer", - "close", - "remove_reader", - "remove_writer", - } - - def __getattribute__(self, name: str) -> Any: - if name in AddThreadSelectorEventLoop.MY_ATTRIBUTES: - return super().__getattribute__(name) - return getattr(self._real_loop, name) - - def __init__(self, real_loop: asyncio.AbstractEventLoop) -> None: - self._real_loop = real_loop - - # Create a thread to run the select system call. We manage this thread - # manually so we can trigger a clean shutdown from an atexit hook. Note - # that due to the order of operations at shutdown, only daemon threads - # can be shut down in this way (non-daemon threads would require the - # introduction of a new hook: https://bugs.python.org/issue41962) - self._select_cond = threading.Condition() - self._select_args = ( - None - ) # type: Optional[Tuple[List[_FileDescriptorLike], List[_FileDescriptorLike]]] - self._closing_selector = False - self._thread = threading.Thread( - name="Tornado selector", daemon=True, target=self._run_select, - ) - self._thread.start() - # Start the select loop once the loop is started. - self._real_loop.call_soon(self._start_select) - - self._readers = {} # type: Dict[_FileDescriptorLike, Callable] - self._writers = {} # type: Dict[_FileDescriptorLike, Callable] - - # Writing to _waker_w will wake up the selector thread, which - # watches for _waker_r to be readable. - self._waker_r, self._waker_w = socket.socketpair() - self._waker_r.setblocking(False) - self._waker_w.setblocking(False) - _selector_loops.add(self) - self.add_reader(self._waker_r, self._consume_waker) - - def __del__(self) -> None: - # If the top-level application code uses asyncio interfaces to - # start and stop the event loop, no objects created in Tornado - # can get a clean shutdown notification. If we're just left to - # be GC'd, we must explicitly close our sockets to avoid - # logging warnings. - _selector_loops.discard(self) - self._waker_r.close() - self._waker_w.close() - - def close(self) -> None: - with self._select_cond: - self._closing_selector = True - self._select_cond.notify() - self._wake_selector() - self._thread.join() - _selector_loops.discard(self) - self._waker_r.close() - self._waker_w.close() - self._real_loop.close() - - def _wake_selector(self) -> None: - try: - self._waker_w.send(b"a") - except BlockingIOError: - pass - - def _consume_waker(self) -> None: - try: - self._waker_r.recv(1024) - except BlockingIOError: - pass - - def _start_select(self) -> None: - # Capture reader and writer sets here in the event loop - # thread to avoid any problems with concurrent - # modification while the select loop uses them. - with self._select_cond: - assert self._select_args is None - self._select_args = (list(self._readers.keys()), list(self._writers.keys())) - self._select_cond.notify() - - def _run_select(self) -> None: - while True: - with self._select_cond: - while self._select_args is None and not self._closing_selector: - self._select_cond.wait() - if self._closing_selector: - return - assert self._select_args is not None - to_read, to_write = self._select_args - self._select_args = None - - # We use the simpler interface of the select module instead of - # the more stateful interface in the selectors module because - # this class is only intended for use on windows, where - # select.select is the only option. The selector interface - # does not have well-documented thread-safety semantics that - # we can rely on so ensuring proper synchronization would be - # tricky. - try: - # On windows, selecting on a socket for write will not - # return the socket when there is an error (but selecting - # for reads works). Also select for errors when selecting - # for writes, and merge the results. - # - # This pattern is also used in - # https://github.com/python/cpython/blob/v3.8.0/Lib/selectors.py#L312-L317 - rs, ws, xs = select.select(to_read, to_write, to_write) - ws = ws + xs - except OSError as e: - # After remove_reader or remove_writer is called, the file - # descriptor may subsequently be closed on the event loop - # thread. It's possible that this select thread hasn't - # gotten into the select system call by the time that - # happens in which case (at least on macOS), select may - # raise a "bad file descriptor" error. If we get that - # error, check and see if we're also being woken up by - # polling the waker alone. If we are, just return to the - # event loop and we'll get the updated set of file - # descriptors on the next iteration. Otherwise, raise the - # original error. - if e.errno == getattr(errno, "WSAENOTSOCK", errno.EBADF): - rs, _, _ = select.select([self._waker_r.fileno()], [], [], 0) - if rs: - ws = [] - else: - raise - else: - raise - self._real_loop.call_soon_threadsafe(self._handle_select, rs, ws) - - def _handle_select( - self, rs: List["_FileDescriptorLike"], ws: List["_FileDescriptorLike"] - ) -> None: - for r in rs: - self._handle_event(r, self._readers) - for w in ws: - self._handle_event(w, self._writers) - self._start_select() - - def _handle_event( - self, fd: "_FileDescriptorLike", cb_map: Dict["_FileDescriptorLike", Callable], - ) -> None: - try: - callback = cb_map[fd] - except KeyError: - return - callback() - - def add_reader( - self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any - ) -> None: - self._readers[fd] = functools.partial(callback, *args) - self._wake_selector() - - def add_writer( - self, fd: "_FileDescriptorLike", callback: Callable[..., None], *args: Any - ) -> None: - self._writers[fd] = functools.partial(callback, *args) - self._wake_selector() - - def remove_reader(self, fd: "_FileDescriptorLike") -> None: - del self._readers[fd] - self._wake_selector() - - def remove_writer(self, fd: "_FileDescriptorLike") -> None: - del self._writers[fd] - self._wake_selector() diff --git a/telegramer/include/tornado/platform/caresresolver.py b/telegramer/include/tornado/platform/caresresolver.py deleted file mode 100644 index e2c5009..0000000 --- a/telegramer/include/tornado/platform/caresresolver.py +++ /dev/null @@ -1,89 +0,0 @@ -import pycares # type: ignore -import socket - -from tornado.concurrent import Future -from tornado import gen -from tornado.ioloop import IOLoop -from tornado.netutil import Resolver, is_valid_ip - -import typing - -if typing.TYPE_CHECKING: - from typing import Generator, Any, List, Tuple, Dict # noqa: F401 - - -class CaresResolver(Resolver): - """Name resolver based on the c-ares library. - - This is a non-blocking and non-threaded resolver. It may not produce - the same results as the system resolver, but can be used for non-blocking - resolution when threads cannot be used. - - c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``, - so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is - the default for ``tornado.simple_httpclient``, but other libraries - may default to ``AF_UNSPEC``. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - """ - - def initialize(self) -> None: - self.io_loop = IOLoop.current() - self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb) - self.fds = {} # type: Dict[int, int] - - def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None: - state = (IOLoop.READ if readable else 0) | (IOLoop.WRITE if writable else 0) - if not state: - self.io_loop.remove_handler(fd) - del self.fds[fd] - elif fd in self.fds: - self.io_loop.update_handler(fd, state) - self.fds[fd] = state - else: - self.io_loop.add_handler(fd, self._handle_events, state) - self.fds[fd] = state - - def _handle_events(self, fd: int, events: int) -> None: - read_fd = pycares.ARES_SOCKET_BAD - write_fd = pycares.ARES_SOCKET_BAD - if events & IOLoop.READ: - read_fd = fd - if events & IOLoop.WRITE: - write_fd = fd - self.channel.process_fd(read_fd, write_fd) - - @gen.coroutine - def resolve( - self, host: str, port: int, family: int = 0 - ) -> "Generator[Any, Any, List[Tuple[int, Any]]]": - if is_valid_ip(host): - addresses = [host] - else: - # gethostbyname doesn't take callback as a kwarg - fut = Future() # type: Future[Tuple[Any, Any]] - self.channel.gethostbyname( - host, family, lambda result, error: fut.set_result((result, error)) - ) - result, error = yield fut - if error: - raise IOError( - "C-Ares returned error %s: %s while resolving %s" - % (error, pycares.errno.strerror(error), host) - ) - addresses = result.addresses - addrinfo = [] - for address in addresses: - if "." in address: - address_family = socket.AF_INET - elif ":" in address: - address_family = socket.AF_INET6 - else: - address_family = socket.AF_UNSPEC - if family != socket.AF_UNSPEC and family != address_family: - raise IOError( - "Requested socket family %d but got %d" % (family, address_family) - ) - addrinfo.append((typing.cast(int, address_family), (address, port))) - return addrinfo diff --git a/telegramer/include/tornado/platform/twisted.py b/telegramer/include/tornado/platform/twisted.py deleted file mode 100644 index 0987a84..0000000 --- a/telegramer/include/tornado/platform/twisted.py +++ /dev/null @@ -1,146 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -"""Bridges between the Twisted package and Tornado. -""" - -import socket -import sys - -import twisted.internet.abstract # type: ignore -import twisted.internet.asyncioreactor # type: ignore -from twisted.internet.defer import Deferred # type: ignore -from twisted.python import failure # type: ignore -import twisted.names.cache # type: ignore -import twisted.names.client # type: ignore -import twisted.names.hosts # type: ignore -import twisted.names.resolve # type: ignore - - -from tornado.concurrent import Future, future_set_exc_info -from tornado.escape import utf8 -from tornado import gen -from tornado.netutil import Resolver - -import typing - -if typing.TYPE_CHECKING: - from typing import Generator, Any, List, Tuple # noqa: F401 - - -class TwistedResolver(Resolver): - """Twisted-based asynchronous resolver. - - This is a non-blocking and non-threaded resolver. It is - recommended only when threads cannot be used, since it has - limitations compared to the standard ``getaddrinfo``-based - `~tornado.netutil.Resolver` and - `~tornado.netutil.DefaultExecutorResolver`. Specifically, it returns at - most one result, and arguments other than ``host`` and ``family`` - are ignored. It may fail to resolve when ``family`` is not - ``socket.AF_UNSPEC``. - - Requires Twisted 12.1 or newer. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - """ - - def initialize(self) -> None: - # partial copy of twisted.names.client.createResolver, which doesn't - # allow for a reactor to be passed in. - self.reactor = twisted.internet.asyncioreactor.AsyncioSelectorReactor() - - host_resolver = twisted.names.hosts.Resolver("/etc/hosts") - cache_resolver = twisted.names.cache.CacheResolver(reactor=self.reactor) - real_resolver = twisted.names.client.Resolver( - "/etc/resolv.conf", reactor=self.reactor - ) - self.resolver = twisted.names.resolve.ResolverChain( - [host_resolver, cache_resolver, real_resolver] - ) - - @gen.coroutine - def resolve( - self, host: str, port: int, family: int = 0 - ) -> "Generator[Any, Any, List[Tuple[int, Any]]]": - # getHostByName doesn't accept IP addresses, so if the input - # looks like an IP address just return it immediately. - if twisted.internet.abstract.isIPAddress(host): - resolved = host - resolved_family = socket.AF_INET - elif twisted.internet.abstract.isIPv6Address(host): - resolved = host - resolved_family = socket.AF_INET6 - else: - deferred = self.resolver.getHostByName(utf8(host)) - fut = Future() # type: Future[Any] - deferred.addBoth(fut.set_result) - resolved = yield fut - if isinstance(resolved, failure.Failure): - try: - resolved.raiseException() - except twisted.names.error.DomainError as e: - raise IOError(e) - elif twisted.internet.abstract.isIPAddress(resolved): - resolved_family = socket.AF_INET - elif twisted.internet.abstract.isIPv6Address(resolved): - resolved_family = socket.AF_INET6 - else: - resolved_family = socket.AF_UNSPEC - if family != socket.AF_UNSPEC and family != resolved_family: - raise Exception( - "Requested socket family %d but got %d" % (family, resolved_family) - ) - result = [(typing.cast(int, resolved_family), (resolved, port))] - return result - - -def install() -> None: - """Install ``AsyncioSelectorReactor`` as the default Twisted reactor. - - .. deprecated:: 5.1 - - This function is provided for backwards compatibility; code - that does not require compatibility with older versions of - Tornado should use - ``twisted.internet.asyncioreactor.install()`` directly. - - .. versionchanged:: 6.0.3 - - In Tornado 5.x and before, this function installed a reactor - based on the Tornado ``IOLoop``. When that reactor - implementation was removed in Tornado 6.0.0, this function was - removed as well. It was restored in Tornado 6.0.3 using the - ``asyncio`` reactor instead. - - """ - from twisted.internet.asyncioreactor import install - - install() - - -if hasattr(gen.convert_yielded, "register"): - - @gen.convert_yielded.register(Deferred) # type: ignore - def _(d: Deferred) -> Future: - f = Future() # type: Future[Any] - - def errback(failure: failure.Failure) -> None: - try: - failure.raiseException() - # Should never happen, but just in case - raise Exception("errback called without error") - except: - future_set_exc_info(f, sys.exc_info()) - - d.addCallbacks(f.set_result, errback) - return f diff --git a/telegramer/include/tornado/process.py b/telegramer/include/tornado/process.py deleted file mode 100644 index 26428fe..0000000 --- a/telegramer/include/tornado/process.py +++ /dev/null @@ -1,373 +0,0 @@ -# -# Copyright 2011 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Utilities for working with multiple processes, including both forking -the server into multiple processes and managing subprocesses. -""" - -import os -import multiprocessing -import signal -import subprocess -import sys -import time - -from binascii import hexlify - -from tornado.concurrent import ( - Future, - future_set_result_unless_cancelled, - future_set_exception_unless_cancelled, -) -from tornado import ioloop -from tornado.iostream import PipeIOStream -from tornado.log import gen_log - -import typing -from typing import Optional, Any, Callable - -if typing.TYPE_CHECKING: - from typing import List # noqa: F401 - -# Re-export this exception for convenience. -CalledProcessError = subprocess.CalledProcessError - - -def cpu_count() -> int: - """Returns the number of processors on this machine.""" - if multiprocessing is None: - return 1 - try: - return multiprocessing.cpu_count() - except NotImplementedError: - pass - try: - return os.sysconf("SC_NPROCESSORS_CONF") # type: ignore - except (AttributeError, ValueError): - pass - gen_log.error("Could not detect number of processors; assuming 1") - return 1 - - -def _reseed_random() -> None: - if "random" not in sys.modules: - return - import random - - # If os.urandom is available, this method does the same thing as - # random.seed (at least as of python 2.6). If os.urandom is not - # available, we mix in the pid in addition to a timestamp. - try: - seed = int(hexlify(os.urandom(16)), 16) - except NotImplementedError: - seed = int(time.time() * 1000) ^ os.getpid() - random.seed(seed) - - -_task_id = None - - -def fork_processes( - num_processes: Optional[int], max_restarts: Optional[int] = None -) -> int: - """Starts multiple worker processes. - - If ``num_processes`` is None or <= 0, we detect the number of cores - available on this machine and fork that number of child - processes. If ``num_processes`` is given and > 0, we fork that - specific number of sub-processes. - - Since we use processes and not threads, there is no shared memory - between any server code. - - Note that multiple processes are not compatible with the autoreload - module (or the ``autoreload=True`` option to `tornado.web.Application` - which defaults to True when ``debug=True``). - When using multiple processes, no IOLoops can be created or - referenced until after the call to ``fork_processes``. - - In each child process, ``fork_processes`` returns its *task id*, a - number between 0 and ``num_processes``. Processes that exit - abnormally (due to a signal or non-zero exit status) are restarted - with the same id (up to ``max_restarts`` times). In the parent - process, ``fork_processes`` calls ``sys.exit(0)`` after all child - processes have exited normally. - - max_restarts defaults to 100. - - Availability: Unix - """ - if sys.platform == "win32": - # The exact form of this condition matters to mypy; it understands - # if but not assert in this context. - raise Exception("fork not available on windows") - if max_restarts is None: - max_restarts = 100 - - global _task_id - assert _task_id is None - if num_processes is None or num_processes <= 0: - num_processes = cpu_count() - gen_log.info("Starting %d processes", num_processes) - children = {} - - def start_child(i: int) -> Optional[int]: - pid = os.fork() - if pid == 0: - # child process - _reseed_random() - global _task_id - _task_id = i - return i - else: - children[pid] = i - return None - - for i in range(num_processes): - id = start_child(i) - if id is not None: - return id - num_restarts = 0 - while children: - pid, status = os.wait() - if pid not in children: - continue - id = children.pop(pid) - if os.WIFSIGNALED(status): - gen_log.warning( - "child %d (pid %d) killed by signal %d, restarting", - id, - pid, - os.WTERMSIG(status), - ) - elif os.WEXITSTATUS(status) != 0: - gen_log.warning( - "child %d (pid %d) exited with status %d, restarting", - id, - pid, - os.WEXITSTATUS(status), - ) - else: - gen_log.info("child %d (pid %d) exited normally", id, pid) - continue - num_restarts += 1 - if num_restarts > max_restarts: - raise RuntimeError("Too many child restarts, giving up") - new_id = start_child(id) - if new_id is not None: - return new_id - # All child processes exited cleanly, so exit the master process - # instead of just returning to right after the call to - # fork_processes (which will probably just start up another IOLoop - # unless the caller checks the return value). - sys.exit(0) - - -def task_id() -> Optional[int]: - """Returns the current task id, if any. - - Returns None if this process was not created by `fork_processes`. - """ - global _task_id - return _task_id - - -class Subprocess(object): - """Wraps ``subprocess.Popen`` with IOStream support. - - The constructor is the same as ``subprocess.Popen`` with the following - additions: - - * ``stdin``, ``stdout``, and ``stderr`` may have the value - ``tornado.process.Subprocess.STREAM``, which will make the corresponding - attribute of the resulting Subprocess a `.PipeIOStream`. If this option - is used, the caller is responsible for closing the streams when done - with them. - - The ``Subprocess.STREAM`` option and the ``set_exit_callback`` and - ``wait_for_exit`` methods do not work on Windows. There is - therefore no reason to use this class instead of - ``subprocess.Popen`` on that platform. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - - """ - - STREAM = object() - - _initialized = False - _waiting = {} # type: ignore - _old_sigchld = None - - def __init__(self, *args: Any, **kwargs: Any) -> None: - self.io_loop = ioloop.IOLoop.current() - # All FDs we create should be closed on error; those in to_close - # should be closed in the parent process on success. - pipe_fds = [] # type: List[int] - to_close = [] # type: List[int] - if kwargs.get("stdin") is Subprocess.STREAM: - in_r, in_w = os.pipe() - kwargs["stdin"] = in_r - pipe_fds.extend((in_r, in_w)) - to_close.append(in_r) - self.stdin = PipeIOStream(in_w) - if kwargs.get("stdout") is Subprocess.STREAM: - out_r, out_w = os.pipe() - kwargs["stdout"] = out_w - pipe_fds.extend((out_r, out_w)) - to_close.append(out_w) - self.stdout = PipeIOStream(out_r) - if kwargs.get("stderr") is Subprocess.STREAM: - err_r, err_w = os.pipe() - kwargs["stderr"] = err_w - pipe_fds.extend((err_r, err_w)) - to_close.append(err_w) - self.stderr = PipeIOStream(err_r) - try: - self.proc = subprocess.Popen(*args, **kwargs) - except: - for fd in pipe_fds: - os.close(fd) - raise - for fd in to_close: - os.close(fd) - self.pid = self.proc.pid - for attr in ["stdin", "stdout", "stderr"]: - if not hasattr(self, attr): # don't clobber streams set above - setattr(self, attr, getattr(self.proc, attr)) - self._exit_callback = None # type: Optional[Callable[[int], None]] - self.returncode = None # type: Optional[int] - - def set_exit_callback(self, callback: Callable[[int], None]) -> None: - """Runs ``callback`` when this process exits. - - The callback takes one argument, the return code of the process. - - This method uses a ``SIGCHLD`` handler, which is a global setting - and may conflict if you have other libraries trying to handle the - same signal. If you are using more than one ``IOLoop`` it may - be necessary to call `Subprocess.initialize` first to designate - one ``IOLoop`` to run the signal handlers. - - In many cases a close callback on the stdout or stderr streams - can be used as an alternative to an exit callback if the - signal handler is causing a problem. - - Availability: Unix - """ - self._exit_callback = callback - Subprocess.initialize() - Subprocess._waiting[self.pid] = self - Subprocess._try_cleanup_process(self.pid) - - def wait_for_exit(self, raise_error: bool = True) -> "Future[int]": - """Returns a `.Future` which resolves when the process exits. - - Usage:: - - ret = yield proc.wait_for_exit() - - This is a coroutine-friendly alternative to `set_exit_callback` - (and a replacement for the blocking `subprocess.Popen.wait`). - - By default, raises `subprocess.CalledProcessError` if the process - has a non-zero exit status. Use ``wait_for_exit(raise_error=False)`` - to suppress this behavior and return the exit status without raising. - - .. versionadded:: 4.2 - - Availability: Unix - """ - future = Future() # type: Future[int] - - def callback(ret: int) -> None: - if ret != 0 and raise_error: - # Unfortunately we don't have the original args any more. - future_set_exception_unless_cancelled( - future, CalledProcessError(ret, "unknown") - ) - else: - future_set_result_unless_cancelled(future, ret) - - self.set_exit_callback(callback) - return future - - @classmethod - def initialize(cls) -> None: - """Initializes the ``SIGCHLD`` handler. - - The signal handler is run on an `.IOLoop` to avoid locking issues. - Note that the `.IOLoop` used for signal handling need not be the - same one used by individual Subprocess objects (as long as the - ``IOLoops`` are each running in separate threads). - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been - removed. - - Availability: Unix - """ - if cls._initialized: - return - io_loop = ioloop.IOLoop.current() - cls._old_sigchld = signal.signal( - signal.SIGCHLD, - lambda sig, frame: io_loop.add_callback_from_signal(cls._cleanup), - ) - cls._initialized = True - - @classmethod - def uninitialize(cls) -> None: - """Removes the ``SIGCHLD`` handler.""" - if not cls._initialized: - return - signal.signal(signal.SIGCHLD, cls._old_sigchld) - cls._initialized = False - - @classmethod - def _cleanup(cls) -> None: - for pid in list(cls._waiting.keys()): # make a copy - cls._try_cleanup_process(pid) - - @classmethod - def _try_cleanup_process(cls, pid: int) -> None: - try: - ret_pid, status = os.waitpid(pid, os.WNOHANG) # type: ignore - except ChildProcessError: - return - if ret_pid == 0: - return - assert ret_pid == pid - subproc = cls._waiting.pop(pid) - subproc.io_loop.add_callback_from_signal(subproc._set_returncode, status) - - def _set_returncode(self, status: int) -> None: - if sys.platform == "win32": - self.returncode = -1 - else: - if os.WIFSIGNALED(status): - self.returncode = -os.WTERMSIG(status) - else: - assert os.WIFEXITED(status) - self.returncode = os.WEXITSTATUS(status) - # We've taken over wait() duty from the subprocess.Popen - # object. If we don't inform it of the process's return code, - # it will log a warning at destruction in python 3.6+. - self.proc.returncode = self.returncode - if self._exit_callback: - callback = self._exit_callback - self._exit_callback = None - callback(self.returncode) diff --git a/telegramer/include/tornado/py.typed b/telegramer/include/tornado/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/telegramer/include/tornado/queues.py b/telegramer/include/tornado/queues.py deleted file mode 100644 index 1e87f62..0000000 --- a/telegramer/include/tornado/queues.py +++ /dev/null @@ -1,414 +0,0 @@ -# Copyright 2015 The Tornado Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Asynchronous queues for coroutines. These classes are very similar -to those provided in the standard library's `asyncio package -<https://docs.python.org/3/library/asyncio-queue.html>`_. - -.. warning:: - - Unlike the standard library's `queue` module, the classes defined here - are *not* thread-safe. To use these queues from another thread, - use `.IOLoop.add_callback` to transfer control to the `.IOLoop` thread - before calling any queue methods. - -""" - -import collections -import datetime -import heapq - -from tornado import gen, ioloop -from tornado.concurrent import Future, future_set_result_unless_cancelled -from tornado.locks import Event - -from typing import Union, TypeVar, Generic, Awaitable, Optional -import typing - -if typing.TYPE_CHECKING: - from typing import Deque, Tuple, Any # noqa: F401 - -_T = TypeVar("_T") - -__all__ = ["Queue", "PriorityQueue", "LifoQueue", "QueueFull", "QueueEmpty"] - - -class QueueEmpty(Exception): - """Raised by `.Queue.get_nowait` when the queue has no items.""" - - pass - - -class QueueFull(Exception): - """Raised by `.Queue.put_nowait` when a queue is at its maximum size.""" - - pass - - -def _set_timeout( - future: Future, timeout: Union[None, float, datetime.timedelta] -) -> None: - if timeout: - - def on_timeout() -> None: - if not future.done(): - future.set_exception(gen.TimeoutError()) - - io_loop = ioloop.IOLoop.current() - timeout_handle = io_loop.add_timeout(timeout, on_timeout) - future.add_done_callback(lambda _: io_loop.remove_timeout(timeout_handle)) - - -class _QueueIterator(Generic[_T]): - def __init__(self, q: "Queue[_T]") -> None: - self.q = q - - def __anext__(self) -> Awaitable[_T]: - return self.q.get() - - -class Queue(Generic[_T]): - """Coordinate producer and consumer coroutines. - - If maxsize is 0 (the default) the queue size is unbounded. - - .. testcode:: - - from tornado import gen - from tornado.ioloop import IOLoop - from tornado.queues import Queue - - q = Queue(maxsize=2) - - async def consumer(): - async for item in q: - try: - print('Doing work on %s' % item) - await gen.sleep(0.01) - finally: - q.task_done() - - async def producer(): - for item in range(5): - await q.put(item) - print('Put %s' % item) - - async def main(): - # Start consumer without waiting (since it never finishes). - IOLoop.current().spawn_callback(consumer) - await producer() # Wait for producer to put all tasks. - await q.join() # Wait for consumer to finish all tasks. - print('Done') - - IOLoop.current().run_sync(main) - - .. testoutput:: - - Put 0 - Put 1 - Doing work on 0 - Put 2 - Doing work on 1 - Put 3 - Doing work on 2 - Put 4 - Doing work on 3 - Doing work on 4 - Done - - - In versions of Python without native coroutines (before 3.5), - ``consumer()`` could be written as:: - - @gen.coroutine - def consumer(): - while True: - item = yield q.get() - try: - print('Doing work on %s' % item) - yield gen.sleep(0.01) - finally: - q.task_done() - - .. versionchanged:: 4.3 - Added ``async for`` support in Python 3.5. - - """ - - # Exact type depends on subclass. Could be another generic - # parameter and use protocols to be more precise here. - _queue = None # type: Any - - def __init__(self, maxsize: int = 0) -> None: - if maxsize is None: - raise TypeError("maxsize can't be None") - - if maxsize < 0: - raise ValueError("maxsize can't be negative") - - self._maxsize = maxsize - self._init() - self._getters = collections.deque([]) # type: Deque[Future[_T]] - self._putters = collections.deque([]) # type: Deque[Tuple[_T, Future[None]]] - self._unfinished_tasks = 0 - self._finished = Event() - self._finished.set() - - @property - def maxsize(self) -> int: - """Number of items allowed in the queue.""" - return self._maxsize - - def qsize(self) -> int: - """Number of items in the queue.""" - return len(self._queue) - - def empty(self) -> bool: - return not self._queue - - def full(self) -> bool: - if self.maxsize == 0: - return False - else: - return self.qsize() >= self.maxsize - - def put( - self, item: _T, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> "Future[None]": - """Put an item into the queue, perhaps waiting until there is room. - - Returns a Future, which raises `tornado.util.TimeoutError` after a - timeout. - - ``timeout`` may be a number denoting a time (on the same - scale as `tornado.ioloop.IOLoop.time`, normally `time.time`), or a - `datetime.timedelta` object for a deadline relative to the - current time. - """ - future = Future() # type: Future[None] - try: - self.put_nowait(item) - except QueueFull: - self._putters.append((item, future)) - _set_timeout(future, timeout) - else: - future.set_result(None) - return future - - def put_nowait(self, item: _T) -> None: - """Put an item into the queue without blocking. - - If no free slot is immediately available, raise `QueueFull`. - """ - self._consume_expired() - if self._getters: - assert self.empty(), "queue non-empty, why are getters waiting?" - getter = self._getters.popleft() - self.__put_internal(item) - future_set_result_unless_cancelled(getter, self._get()) - elif self.full(): - raise QueueFull - else: - self.__put_internal(item) - - def get( - self, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> Awaitable[_T]: - """Remove and return an item from the queue. - - Returns an awaitable which resolves once an item is available, or raises - `tornado.util.TimeoutError` after a timeout. - - ``timeout`` may be a number denoting a time (on the same - scale as `tornado.ioloop.IOLoop.time`, normally `time.time`), or a - `datetime.timedelta` object for a deadline relative to the - current time. - - .. note:: - - The ``timeout`` argument of this method differs from that - of the standard library's `queue.Queue.get`. That method - interprets numeric values as relative timeouts; this one - interprets them as absolute deadlines and requires - ``timedelta`` objects for relative timeouts (consistent - with other timeouts in Tornado). - - """ - future = Future() # type: Future[_T] - try: - future.set_result(self.get_nowait()) - except QueueEmpty: - self._getters.append(future) - _set_timeout(future, timeout) - return future - - def get_nowait(self) -> _T: - """Remove and return an item from the queue without blocking. - - Return an item if one is immediately available, else raise - `QueueEmpty`. - """ - self._consume_expired() - if self._putters: - assert self.full(), "queue not full, why are putters waiting?" - item, putter = self._putters.popleft() - self.__put_internal(item) - future_set_result_unless_cancelled(putter, None) - return self._get() - elif self.qsize(): - return self._get() - else: - raise QueueEmpty - - def task_done(self) -> None: - """Indicate that a formerly enqueued task is complete. - - Used by queue consumers. For each `.get` used to fetch a task, a - subsequent call to `.task_done` tells the queue that the processing - on the task is complete. - - If a `.join` is blocking, it resumes when all items have been - processed; that is, when every `.put` is matched by a `.task_done`. - - Raises `ValueError` if called more times than `.put`. - """ - if self._unfinished_tasks <= 0: - raise ValueError("task_done() called too many times") - self._unfinished_tasks -= 1 - if self._unfinished_tasks == 0: - self._finished.set() - - def join( - self, timeout: Optional[Union[float, datetime.timedelta]] = None - ) -> Awaitable[None]: - """Block until all items in the queue are processed. - - Returns an awaitable, which raises `tornado.util.TimeoutError` after a - timeout. - """ - return self._finished.wait(timeout) - - def __aiter__(self) -> _QueueIterator[_T]: - return _QueueIterator(self) - - # These three are overridable in subclasses. - def _init(self) -> None: - self._queue = collections.deque() - - def _get(self) -> _T: - return self._queue.popleft() - - def _put(self, item: _T) -> None: - self._queue.append(item) - - # End of the overridable methods. - - def __put_internal(self, item: _T) -> None: - self._unfinished_tasks += 1 - self._finished.clear() - self._put(item) - - def _consume_expired(self) -> None: - # Remove timed-out waiters. - while self._putters and self._putters[0][1].done(): - self._putters.popleft() - - while self._getters and self._getters[0].done(): - self._getters.popleft() - - def __repr__(self) -> str: - return "<%s at %s %s>" % (type(self).__name__, hex(id(self)), self._format()) - - def __str__(self) -> str: - return "<%s %s>" % (type(self).__name__, self._format()) - - def _format(self) -> str: - result = "maxsize=%r" % (self.maxsize,) - if getattr(self, "_queue", None): - result += " queue=%r" % self._queue - if self._getters: - result += " getters[%s]" % len(self._getters) - if self._putters: - result += " putters[%s]" % len(self._putters) - if self._unfinished_tasks: - result += " tasks=%s" % self._unfinished_tasks - return result - - -class PriorityQueue(Queue): - """A `.Queue` that retrieves entries in priority order, lowest first. - - Entries are typically tuples like ``(priority number, data)``. - - .. testcode:: - - from tornado.queues import PriorityQueue - - q = PriorityQueue() - q.put((1, 'medium-priority item')) - q.put((0, 'high-priority item')) - q.put((10, 'low-priority item')) - - print(q.get_nowait()) - print(q.get_nowait()) - print(q.get_nowait()) - - .. testoutput:: - - (0, 'high-priority item') - (1, 'medium-priority item') - (10, 'low-priority item') - """ - - def _init(self) -> None: - self._queue = [] - - def _put(self, item: _T) -> None: - heapq.heappush(self._queue, item) - - def _get(self) -> _T: - return heapq.heappop(self._queue) - - -class LifoQueue(Queue): - """A `.Queue` that retrieves the most recently put items first. - - .. testcode:: - - from tornado.queues import LifoQueue - - q = LifoQueue() - q.put(3) - q.put(2) - q.put(1) - - print(q.get_nowait()) - print(q.get_nowait()) - print(q.get_nowait()) - - .. testoutput:: - - 1 - 2 - 3 - """ - - def _init(self) -> None: - self._queue = [] - - def _put(self, item: _T) -> None: - self._queue.append(item) - - def _get(self) -> _T: - return self._queue.pop() diff --git a/telegramer/include/tornado/routing.py b/telegramer/include/tornado/routing.py deleted file mode 100644 index a145d71..0000000 --- a/telegramer/include/tornado/routing.py +++ /dev/null @@ -1,717 +0,0 @@ -# Copyright 2015 The Tornado Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Flexible routing implementation. - -Tornado routes HTTP requests to appropriate handlers using `Router` -class implementations. The `tornado.web.Application` class is a -`Router` implementation and may be used directly, or the classes in -this module may be used for additional flexibility. The `RuleRouter` -class can match on more criteria than `.Application`, or the `Router` -interface can be subclassed for maximum customization. - -`Router` interface extends `~.httputil.HTTPServerConnectionDelegate` -to provide additional routing capabilities. This also means that any -`Router` implementation can be used directly as a ``request_callback`` -for `~.httpserver.HTTPServer` constructor. - -`Router` subclass must implement a ``find_handler`` method to provide -a suitable `~.httputil.HTTPMessageDelegate` instance to handle the -request: - -.. code-block:: python - - class CustomRouter(Router): - def find_handler(self, request, **kwargs): - # some routing logic providing a suitable HTTPMessageDelegate instance - return MessageDelegate(request.connection) - - class MessageDelegate(HTTPMessageDelegate): - def __init__(self, connection): - self.connection = connection - - def finish(self): - self.connection.write_headers( - ResponseStartLine("HTTP/1.1", 200, "OK"), - HTTPHeaders({"Content-Length": "2"}), - b"OK") - self.connection.finish() - - router = CustomRouter() - server = HTTPServer(router) - -The main responsibility of `Router` implementation is to provide a -mapping from a request to `~.httputil.HTTPMessageDelegate` instance -that will handle this request. In the example above we can see that -routing is possible even without instantiating an `~.web.Application`. - -For routing to `~.web.RequestHandler` implementations we need an -`~.web.Application` instance. `~.web.Application.get_handler_delegate` -provides a convenient way to create `~.httputil.HTTPMessageDelegate` -for a given request and `~.web.RequestHandler`. - -Here is a simple example of how we can we route to -`~.web.RequestHandler` subclasses by HTTP method: - -.. code-block:: python - - resources = {} - - class GetResource(RequestHandler): - def get(self, path): - if path not in resources: - raise HTTPError(404) - - self.finish(resources[path]) - - class PostResource(RequestHandler): - def post(self, path): - resources[path] = self.request.body - - class HTTPMethodRouter(Router): - def __init__(self, app): - self.app = app - - def find_handler(self, request, **kwargs): - handler = GetResource if request.method == "GET" else PostResource - return self.app.get_handler_delegate(request, handler, path_args=[request.path]) - - router = HTTPMethodRouter(Application()) - server = HTTPServer(router) - -`ReversibleRouter` interface adds the ability to distinguish between -the routes and reverse them to the original urls using route's name -and additional arguments. `~.web.Application` is itself an -implementation of `ReversibleRouter` class. - -`RuleRouter` and `ReversibleRuleRouter` are implementations of -`Router` and `ReversibleRouter` interfaces and can be used for -creating rule-based routing configurations. - -Rules are instances of `Rule` class. They contain a `Matcher`, which -provides the logic for determining whether the rule is a match for a -particular request and a target, which can be one of the following. - -1) An instance of `~.httputil.HTTPServerConnectionDelegate`: - -.. code-block:: python - - router = RuleRouter([ - Rule(PathMatches("/handler"), ConnectionDelegate()), - # ... more rules - ]) - - class ConnectionDelegate(HTTPServerConnectionDelegate): - def start_request(self, server_conn, request_conn): - return MessageDelegate(request_conn) - -2) A callable accepting a single argument of `~.httputil.HTTPServerRequest` type: - -.. code-block:: python - - router = RuleRouter([ - Rule(PathMatches("/callable"), request_callable) - ]) - - def request_callable(request): - request.write(b"HTTP/1.1 200 OK\\r\\nContent-Length: 2\\r\\n\\r\\nOK") - request.finish() - -3) Another `Router` instance: - -.. code-block:: python - - router = RuleRouter([ - Rule(PathMatches("/router.*"), CustomRouter()) - ]) - -Of course a nested `RuleRouter` or a `~.web.Application` is allowed: - -.. code-block:: python - - router = RuleRouter([ - Rule(HostMatches("example.com"), RuleRouter([ - Rule(PathMatches("/app1/.*"), Application([(r"/app1/handler", Handler)])), - ])) - ]) - - server = HTTPServer(router) - -In the example below `RuleRouter` is used to route between applications: - -.. code-block:: python - - app1 = Application([ - (r"/app1/handler", Handler1), - # other handlers ... - ]) - - app2 = Application([ - (r"/app2/handler", Handler2), - # other handlers ... - ]) - - router = RuleRouter([ - Rule(PathMatches("/app1.*"), app1), - Rule(PathMatches("/app2.*"), app2) - ]) - - server = HTTPServer(router) - -For more information on application-level routing see docs for `~.web.Application`. - -.. versionadded:: 4.5 - -""" - -import re -from functools import partial - -from tornado import httputil -from tornado.httpserver import _CallableAdapter -from tornado.escape import url_escape, url_unescape, utf8 -from tornado.log import app_log -from tornado.util import basestring_type, import_object, re_unescape, unicode_type - -from typing import Any, Union, Optional, Awaitable, List, Dict, Pattern, Tuple, overload - - -class Router(httputil.HTTPServerConnectionDelegate): - """Abstract router interface.""" - - def find_handler( - self, request: httputil.HTTPServerRequest, **kwargs: Any - ) -> Optional[httputil.HTTPMessageDelegate]: - """Must be implemented to return an appropriate instance of `~.httputil.HTTPMessageDelegate` - that can serve the request. - Routing implementations may pass additional kwargs to extend the routing logic. - - :arg httputil.HTTPServerRequest request: current HTTP request. - :arg kwargs: additional keyword arguments passed by routing implementation. - :returns: an instance of `~.httputil.HTTPMessageDelegate` that will be used to - process the request. - """ - raise NotImplementedError() - - def start_request( - self, server_conn: object, request_conn: httputil.HTTPConnection - ) -> httputil.HTTPMessageDelegate: - return _RoutingDelegate(self, server_conn, request_conn) - - -class ReversibleRouter(Router): - """Abstract router interface for routers that can handle named routes - and support reversing them to original urls. - """ - - def reverse_url(self, name: str, *args: Any) -> Optional[str]: - """Returns url string for a given route name and arguments - or ``None`` if no match is found. - - :arg str name: route name. - :arg args: url parameters. - :returns: parametrized url string for a given route name (or ``None``). - """ - raise NotImplementedError() - - -class _RoutingDelegate(httputil.HTTPMessageDelegate): - def __init__( - self, router: Router, server_conn: object, request_conn: httputil.HTTPConnection - ) -> None: - self.server_conn = server_conn - self.request_conn = request_conn - self.delegate = None # type: Optional[httputil.HTTPMessageDelegate] - self.router = router # type: Router - - def headers_received( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - ) -> Optional[Awaitable[None]]: - assert isinstance(start_line, httputil.RequestStartLine) - request = httputil.HTTPServerRequest( - connection=self.request_conn, - server_connection=self.server_conn, - start_line=start_line, - headers=headers, - ) - - self.delegate = self.router.find_handler(request) - if self.delegate is None: - app_log.debug( - "Delegate for %s %s request not found", - start_line.method, - start_line.path, - ) - self.delegate = _DefaultMessageDelegate(self.request_conn) - - return self.delegate.headers_received(start_line, headers) - - def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: - assert self.delegate is not None - return self.delegate.data_received(chunk) - - def finish(self) -> None: - assert self.delegate is not None - self.delegate.finish() - - def on_connection_close(self) -> None: - assert self.delegate is not None - self.delegate.on_connection_close() - - -class _DefaultMessageDelegate(httputil.HTTPMessageDelegate): - def __init__(self, connection: httputil.HTTPConnection) -> None: - self.connection = connection - - def finish(self) -> None: - self.connection.write_headers( - httputil.ResponseStartLine("HTTP/1.1", 404, "Not Found"), - httputil.HTTPHeaders(), - ) - self.connection.finish() - - -# _RuleList can either contain pre-constructed Rules or a sequence of -# arguments to be passed to the Rule constructor. -_RuleList = List[ - Union[ - "Rule", - List[Any], # Can't do detailed typechecking of lists. - Tuple[Union[str, "Matcher"], Any], - Tuple[Union[str, "Matcher"], Any, Dict[str, Any]], - Tuple[Union[str, "Matcher"], Any, Dict[str, Any], str], - ] -] - - -class RuleRouter(Router): - """Rule-based router implementation.""" - - def __init__(self, rules: Optional[_RuleList] = None) -> None: - """Constructs a router from an ordered list of rules:: - - RuleRouter([ - Rule(PathMatches("/handler"), Target), - # ... more rules - ]) - - You can also omit explicit `Rule` constructor and use tuples of arguments:: - - RuleRouter([ - (PathMatches("/handler"), Target), - ]) - - `PathMatches` is a default matcher, so the example above can be simplified:: - - RuleRouter([ - ("/handler", Target), - ]) - - In the examples above, ``Target`` can be a nested `Router` instance, an instance of - `~.httputil.HTTPServerConnectionDelegate` or an old-style callable, - accepting a request argument. - - :arg rules: a list of `Rule` instances or tuples of `Rule` - constructor arguments. - """ - self.rules = [] # type: List[Rule] - if rules: - self.add_rules(rules) - - def add_rules(self, rules: _RuleList) -> None: - """Appends new rules to the router. - - :arg rules: a list of Rule instances (or tuples of arguments, which are - passed to Rule constructor). - """ - for rule in rules: - if isinstance(rule, (tuple, list)): - assert len(rule) in (2, 3, 4) - if isinstance(rule[0], basestring_type): - rule = Rule(PathMatches(rule[0]), *rule[1:]) - else: - rule = Rule(*rule) - - self.rules.append(self.process_rule(rule)) - - def process_rule(self, rule: "Rule") -> "Rule": - """Override this method for additional preprocessing of each rule. - - :arg Rule rule: a rule to be processed. - :returns: the same or modified Rule instance. - """ - return rule - - def find_handler( - self, request: httputil.HTTPServerRequest, **kwargs: Any - ) -> Optional[httputil.HTTPMessageDelegate]: - for rule in self.rules: - target_params = rule.matcher.match(request) - if target_params is not None: - if rule.target_kwargs: - target_params["target_kwargs"] = rule.target_kwargs - - delegate = self.get_target_delegate( - rule.target, request, **target_params - ) - - if delegate is not None: - return delegate - - return None - - def get_target_delegate( - self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any - ) -> Optional[httputil.HTTPMessageDelegate]: - """Returns an instance of `~.httputil.HTTPMessageDelegate` for a - Rule's target. This method is called by `~.find_handler` and can be - extended to provide additional target types. - - :arg target: a Rule's target. - :arg httputil.HTTPServerRequest request: current request. - :arg target_params: additional parameters that can be useful - for `~.httputil.HTTPMessageDelegate` creation. - """ - if isinstance(target, Router): - return target.find_handler(request, **target_params) - - elif isinstance(target, httputil.HTTPServerConnectionDelegate): - assert request.connection is not None - return target.start_request(request.server_connection, request.connection) - - elif callable(target): - assert request.connection is not None - return _CallableAdapter( - partial(target, **target_params), request.connection - ) - - return None - - -class ReversibleRuleRouter(ReversibleRouter, RuleRouter): - """A rule-based router that implements ``reverse_url`` method. - - Each rule added to this router may have a ``name`` attribute that can be - used to reconstruct an original uri. The actual reconstruction takes place - in a rule's matcher (see `Matcher.reverse`). - """ - - def __init__(self, rules: Optional[_RuleList] = None) -> None: - self.named_rules = {} # type: Dict[str, Any] - super().__init__(rules) - - def process_rule(self, rule: "Rule") -> "Rule": - rule = super().process_rule(rule) - - if rule.name: - if rule.name in self.named_rules: - app_log.warning( - "Multiple handlers named %s; replacing previous value", rule.name - ) - self.named_rules[rule.name] = rule - - return rule - - def reverse_url(self, name: str, *args: Any) -> Optional[str]: - if name in self.named_rules: - return self.named_rules[name].matcher.reverse(*args) - - for rule in self.rules: - if isinstance(rule.target, ReversibleRouter): - reversed_url = rule.target.reverse_url(name, *args) - if reversed_url is not None: - return reversed_url - - return None - - -class Rule(object): - """A routing rule.""" - - def __init__( - self, - matcher: "Matcher", - target: Any, - target_kwargs: Optional[Dict[str, Any]] = None, - name: Optional[str] = None, - ) -> None: - """Constructs a Rule instance. - - :arg Matcher matcher: a `Matcher` instance used for determining - whether the rule should be considered a match for a specific - request. - :arg target: a Rule's target (typically a ``RequestHandler`` or - `~.httputil.HTTPServerConnectionDelegate` subclass or even a nested `Router`, - depending on routing implementation). - :arg dict target_kwargs: a dict of parameters that can be useful - at the moment of target instantiation (for example, ``status_code`` - for a ``RequestHandler`` subclass). They end up in - ``target_params['target_kwargs']`` of `RuleRouter.get_target_delegate` - method. - :arg str name: the name of the rule that can be used to find it - in `ReversibleRouter.reverse_url` implementation. - """ - if isinstance(target, str): - # import the Module and instantiate the class - # Must be a fully qualified name (module.ClassName) - target = import_object(target) - - self.matcher = matcher # type: Matcher - self.target = target - self.target_kwargs = target_kwargs if target_kwargs else {} - self.name = name - - def reverse(self, *args: Any) -> Optional[str]: - return self.matcher.reverse(*args) - - def __repr__(self) -> str: - return "%s(%r, %s, kwargs=%r, name=%r)" % ( - self.__class__.__name__, - self.matcher, - self.target, - self.target_kwargs, - self.name, - ) - - -class Matcher(object): - """Represents a matcher for request features.""" - - def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]: - """Matches current instance against the request. - - :arg httputil.HTTPServerRequest request: current HTTP request - :returns: a dict of parameters to be passed to the target handler - (for example, ``handler_kwargs``, ``path_args``, ``path_kwargs`` - can be passed for proper `~.web.RequestHandler` instantiation). - An empty dict is a valid (and common) return value to indicate a match - when the argument-passing features are not used. - ``None`` must be returned to indicate that there is no match.""" - raise NotImplementedError() - - def reverse(self, *args: Any) -> Optional[str]: - """Reconstructs full url from matcher instance and additional arguments.""" - return None - - -class AnyMatches(Matcher): - """Matches any request.""" - - def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]: - return {} - - -class HostMatches(Matcher): - """Matches requests from hosts specified by ``host_pattern`` regex.""" - - def __init__(self, host_pattern: Union[str, Pattern]) -> None: - if isinstance(host_pattern, basestring_type): - if not host_pattern.endswith("$"): - host_pattern += "$" - self.host_pattern = re.compile(host_pattern) - else: - self.host_pattern = host_pattern - - def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]: - if self.host_pattern.match(request.host_name): - return {} - - return None - - -class DefaultHostMatches(Matcher): - """Matches requests from host that is equal to application's default_host. - Always returns no match if ``X-Real-Ip`` header is present. - """ - - def __init__(self, application: Any, host_pattern: Pattern) -> None: - self.application = application - self.host_pattern = host_pattern - - def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]: - # Look for default host if not behind load balancer (for debugging) - if "X-Real-Ip" not in request.headers: - if self.host_pattern.match(self.application.default_host): - return {} - return None - - -class PathMatches(Matcher): - """Matches requests with paths specified by ``path_pattern`` regex.""" - - def __init__(self, path_pattern: Union[str, Pattern]) -> None: - if isinstance(path_pattern, basestring_type): - if not path_pattern.endswith("$"): - path_pattern += "$" - self.regex = re.compile(path_pattern) - else: - self.regex = path_pattern - - assert len(self.regex.groupindex) in (0, self.regex.groups), ( - "groups in url regexes must either be all named or all " - "positional: %r" % self.regex.pattern - ) - - self._path, self._group_count = self._find_groups() - - def match(self, request: httputil.HTTPServerRequest) -> Optional[Dict[str, Any]]: - match = self.regex.match(request.path) - if match is None: - return None - if not self.regex.groups: - return {} - - path_args = [] # type: List[bytes] - path_kwargs = {} # type: Dict[str, bytes] - - # Pass matched groups to the handler. Since - # match.groups() includes both named and - # unnamed groups, we want to use either groups - # or groupdict but not both. - if self.regex.groupindex: - path_kwargs = dict( - (str(k), _unquote_or_none(v)) for (k, v) in match.groupdict().items() - ) - else: - path_args = [_unquote_or_none(s) for s in match.groups()] - - return dict(path_args=path_args, path_kwargs=path_kwargs) - - def reverse(self, *args: Any) -> Optional[str]: - if self._path is None: - raise ValueError("Cannot reverse url regex " + self.regex.pattern) - assert len(args) == self._group_count, ( - "required number of arguments " "not found" - ) - if not len(args): - return self._path - converted_args = [] - for a in args: - if not isinstance(a, (unicode_type, bytes)): - a = str(a) - converted_args.append(url_escape(utf8(a), plus=False)) - return self._path % tuple(converted_args) - - def _find_groups(self) -> Tuple[Optional[str], Optional[int]]: - """Returns a tuple (reverse string, group count) for a url. - - For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method - would return ('/%s/%s/', 2). - """ - pattern = self.regex.pattern - if pattern.startswith("^"): - pattern = pattern[1:] - if pattern.endswith("$"): - pattern = pattern[:-1] - - if self.regex.groups != pattern.count("("): - # The pattern is too complicated for our simplistic matching, - # so we can't support reversing it. - return None, None - - pieces = [] - for fragment in pattern.split("("): - if ")" in fragment: - paren_loc = fragment.index(")") - if paren_loc >= 0: - try: - unescaped_fragment = re_unescape(fragment[paren_loc + 1 :]) - except ValueError: - # If we can't unescape part of it, we can't - # reverse this url. - return (None, None) - pieces.append("%s" + unescaped_fragment) - else: - try: - unescaped_fragment = re_unescape(fragment) - except ValueError: - # If we can't unescape part of it, we can't - # reverse this url. - return (None, None) - pieces.append(unescaped_fragment) - - return "".join(pieces), self.regex.groups - - -class URLSpec(Rule): - """Specifies mappings between URLs and handlers. - - .. versionchanged: 4.5 - `URLSpec` is now a subclass of a `Rule` with `PathMatches` matcher and is preserved for - backwards compatibility. - """ - - def __init__( - self, - pattern: Union[str, Pattern], - handler: Any, - kwargs: Optional[Dict[str, Any]] = None, - name: Optional[str] = None, - ) -> None: - """Parameters: - - * ``pattern``: Regular expression to be matched. Any capturing - groups in the regex will be passed in to the handler's - get/post/etc methods as arguments (by keyword if named, by - position if unnamed. Named and unnamed capturing groups - may not be mixed in the same rule). - - * ``handler``: `~.web.RequestHandler` subclass to be invoked. - - * ``kwargs`` (optional): A dictionary of additional arguments - to be passed to the handler's constructor. - - * ``name`` (optional): A name for this handler. Used by - `~.web.Application.reverse_url`. - - """ - matcher = PathMatches(pattern) - super().__init__(matcher, handler, kwargs, name) - - self.regex = matcher.regex - self.handler_class = self.target - self.kwargs = kwargs - - def __repr__(self) -> str: - return "%s(%r, %s, kwargs=%r, name=%r)" % ( - self.__class__.__name__, - self.regex.pattern, - self.handler_class, - self.kwargs, - self.name, - ) - - -@overload -def _unquote_or_none(s: str) -> bytes: - pass - - -@overload # noqa: F811 -def _unquote_or_none(s: None) -> None: - pass - - -def _unquote_or_none(s: Optional[str]) -> Optional[bytes]: # noqa: F811 - """None-safe wrapper around url_unescape to handle unmatched optional - groups correctly. - - Note that args are passed as bytes so the handler can decide what - encoding to use. - """ - if s is None: - return s - return url_unescape(s, encoding=None, plus=False) diff --git a/telegramer/include/tornado/simple_httpclient.py b/telegramer/include/tornado/simple_httpclient.py deleted file mode 100644 index f99f391..0000000 --- a/telegramer/include/tornado/simple_httpclient.py +++ /dev/null @@ -1,699 +0,0 @@ -from tornado.escape import _unicode -from tornado import gen, version -from tornado.httpclient import ( - HTTPResponse, - HTTPError, - AsyncHTTPClient, - main, - _RequestProxy, - HTTPRequest, -) -from tornado import httputil -from tornado.http1connection import HTTP1Connection, HTTP1ConnectionParameters -from tornado.ioloop import IOLoop -from tornado.iostream import StreamClosedError, IOStream -from tornado.netutil import ( - Resolver, - OverrideResolver, - _client_ssl_defaults, - is_valid_ip, -) -from tornado.log import gen_log -from tornado.tcpclient import TCPClient - -import base64 -import collections -import copy -import functools -import re -import socket -import ssl -import sys -import time -from io import BytesIO -import urllib.parse - -from typing import Dict, Any, Callable, Optional, Type, Union -from types import TracebackType -import typing - -if typing.TYPE_CHECKING: - from typing import Deque, Tuple, List # noqa: F401 - - -class HTTPTimeoutError(HTTPError): - """Error raised by SimpleAsyncHTTPClient on timeout. - - For historical reasons, this is a subclass of `.HTTPClientError` - which simulates a response code of 599. - - .. versionadded:: 5.1 - """ - - def __init__(self, message: str) -> None: - super().__init__(599, message=message) - - def __str__(self) -> str: - return self.message or "Timeout" - - -class HTTPStreamClosedError(HTTPError): - """Error raised by SimpleAsyncHTTPClient when the underlying stream is closed. - - When a more specific exception is available (such as `ConnectionResetError`), - it may be raised instead of this one. - - For historical reasons, this is a subclass of `.HTTPClientError` - which simulates a response code of 599. - - .. versionadded:: 5.1 - """ - - def __init__(self, message: str) -> None: - super().__init__(599, message=message) - - def __str__(self) -> str: - return self.message or "Stream closed" - - -class SimpleAsyncHTTPClient(AsyncHTTPClient): - """Non-blocking HTTP client with no external dependencies. - - This class implements an HTTP 1.1 client on top of Tornado's IOStreams. - Some features found in the curl-based AsyncHTTPClient are not yet - supported. In particular, proxies are not supported, connections - are not reused, and callers cannot select the network interface to be - used. - """ - - def initialize( # type: ignore - self, - max_clients: int = 10, - hostname_mapping: Optional[Dict[str, str]] = None, - max_buffer_size: int = 104857600, - resolver: Optional[Resolver] = None, - defaults: Optional[Dict[str, Any]] = None, - max_header_size: Optional[int] = None, - max_body_size: Optional[int] = None, - ) -> None: - """Creates a AsyncHTTPClient. - - Only a single AsyncHTTPClient instance exists per IOLoop - in order to provide limitations on the number of pending connections. - ``force_instance=True`` may be used to suppress this behavior. - - Note that because of this implicit reuse, unless ``force_instance`` - is used, only the first call to the constructor actually uses - its arguments. It is recommended to use the ``configure`` method - instead of the constructor to ensure that arguments take effect. - - ``max_clients`` is the number of concurrent requests that can be - in progress; when this limit is reached additional requests will be - queued. Note that time spent waiting in this queue still counts - against the ``request_timeout``. - - ``hostname_mapping`` is a dictionary mapping hostnames to IP addresses. - It can be used to make local DNS changes when modifying system-wide - settings like ``/etc/hosts`` is not possible or desirable (e.g. in - unittests). - - ``max_buffer_size`` (default 100MB) is the number of bytes - that can be read into memory at once. ``max_body_size`` - (defaults to ``max_buffer_size``) is the largest response body - that the client will accept. Without a - ``streaming_callback``, the smaller of these two limits - applies; with a ``streaming_callback`` only ``max_body_size`` - does. - - .. versionchanged:: 4.2 - Added the ``max_body_size`` argument. - """ - super().initialize(defaults=defaults) - self.max_clients = max_clients - self.queue = ( - collections.deque() - ) # type: Deque[Tuple[object, HTTPRequest, Callable[[HTTPResponse], None]]] - self.active = ( - {} - ) # type: Dict[object, Tuple[HTTPRequest, Callable[[HTTPResponse], None]]] - self.waiting = ( - {} - ) # type: Dict[object, Tuple[HTTPRequest, Callable[[HTTPResponse], None], object]] - self.max_buffer_size = max_buffer_size - self.max_header_size = max_header_size - self.max_body_size = max_body_size - # TCPClient could create a Resolver for us, but we have to do it - # ourselves to support hostname_mapping. - if resolver: - self.resolver = resolver - self.own_resolver = False - else: - self.resolver = Resolver() - self.own_resolver = True - if hostname_mapping is not None: - self.resolver = OverrideResolver( - resolver=self.resolver, mapping=hostname_mapping - ) - self.tcp_client = TCPClient(resolver=self.resolver) - - def close(self) -> None: - super().close() - if self.own_resolver: - self.resolver.close() - self.tcp_client.close() - - def fetch_impl( - self, request: HTTPRequest, callback: Callable[[HTTPResponse], None] - ) -> None: - key = object() - self.queue.append((key, request, callback)) - assert request.connect_timeout is not None - assert request.request_timeout is not None - timeout_handle = None - if len(self.active) >= self.max_clients: - timeout = ( - min(request.connect_timeout, request.request_timeout) - or request.connect_timeout - or request.request_timeout - ) # min but skip zero - if timeout: - timeout_handle = self.io_loop.add_timeout( - self.io_loop.time() + timeout, - functools.partial(self._on_timeout, key, "in request queue"), - ) - self.waiting[key] = (request, callback, timeout_handle) - self._process_queue() - if self.queue: - gen_log.debug( - "max_clients limit reached, request queued. " - "%d active, %d queued requests." % (len(self.active), len(self.queue)) - ) - - def _process_queue(self) -> None: - while self.queue and len(self.active) < self.max_clients: - key, request, callback = self.queue.popleft() - if key not in self.waiting: - continue - self._remove_timeout(key) - self.active[key] = (request, callback) - release_callback = functools.partial(self._release_fetch, key) - self._handle_request(request, release_callback, callback) - - def _connection_class(self) -> type: - return _HTTPConnection - - def _handle_request( - self, - request: HTTPRequest, - release_callback: Callable[[], None], - final_callback: Callable[[HTTPResponse], None], - ) -> None: - self._connection_class()( - self, - request, - release_callback, - final_callback, - self.max_buffer_size, - self.tcp_client, - self.max_header_size, - self.max_body_size, - ) - - def _release_fetch(self, key: object) -> None: - del self.active[key] - self._process_queue() - - def _remove_timeout(self, key: object) -> None: - if key in self.waiting: - request, callback, timeout_handle = self.waiting[key] - if timeout_handle is not None: - self.io_loop.remove_timeout(timeout_handle) - del self.waiting[key] - - def _on_timeout(self, key: object, info: Optional[str] = None) -> None: - """Timeout callback of request. - - Construct a timeout HTTPResponse when a timeout occurs. - - :arg object key: A simple object to mark the request. - :info string key: More detailed timeout information. - """ - request, callback, timeout_handle = self.waiting[key] - self.queue.remove((key, request, callback)) - - error_message = "Timeout {0}".format(info) if info else "Timeout" - timeout_response = HTTPResponse( - request, - 599, - error=HTTPTimeoutError(error_message), - request_time=self.io_loop.time() - request.start_time, - ) - self.io_loop.add_callback(callback, timeout_response) - del self.waiting[key] - - -class _HTTPConnection(httputil.HTTPMessageDelegate): - _SUPPORTED_METHODS = set( - ["GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"] - ) - - def __init__( - self, - client: Optional[SimpleAsyncHTTPClient], - request: HTTPRequest, - release_callback: Callable[[], None], - final_callback: Callable[[HTTPResponse], None], - max_buffer_size: int, - tcp_client: TCPClient, - max_header_size: int, - max_body_size: int, - ) -> None: - self.io_loop = IOLoop.current() - self.start_time = self.io_loop.time() - self.start_wall_time = time.time() - self.client = client - self.request = request - self.release_callback = release_callback - self.final_callback = final_callback - self.max_buffer_size = max_buffer_size - self.tcp_client = tcp_client - self.max_header_size = max_header_size - self.max_body_size = max_body_size - self.code = None # type: Optional[int] - self.headers = None # type: Optional[httputil.HTTPHeaders] - self.chunks = [] # type: List[bytes] - self._decompressor = None - # Timeout handle returned by IOLoop.add_timeout - self._timeout = None # type: object - self._sockaddr = None - IOLoop.current().add_future( - gen.convert_yielded(self.run()), lambda f: f.result() - ) - - async def run(self) -> None: - try: - self.parsed = urllib.parse.urlsplit(_unicode(self.request.url)) - if self.parsed.scheme not in ("http", "https"): - raise ValueError("Unsupported url scheme: %s" % self.request.url) - # urlsplit results have hostname and port results, but they - # didn't support ipv6 literals until python 2.7. - netloc = self.parsed.netloc - if "@" in netloc: - userpass, _, netloc = netloc.rpartition("@") - host, port = httputil.split_host_and_port(netloc) - if port is None: - port = 443 if self.parsed.scheme == "https" else 80 - if re.match(r"^\[.*\]$", host): - # raw ipv6 addresses in urls are enclosed in brackets - host = host[1:-1] - self.parsed_hostname = host # save final host for _on_connect - - if self.request.allow_ipv6 is False: - af = socket.AF_INET - else: - af = socket.AF_UNSPEC - - ssl_options = self._get_ssl_options(self.parsed.scheme) - - source_ip = None - if self.request.network_interface: - if is_valid_ip(self.request.network_interface): - source_ip = self.request.network_interface - else: - raise ValueError( - "Unrecognized IPv4 or IPv6 address for network_interface, got %r" - % (self.request.network_interface,) - ) - - timeout = ( - min(self.request.connect_timeout, self.request.request_timeout) - or self.request.connect_timeout - or self.request.request_timeout - ) # min but skip zero - if timeout: - self._timeout = self.io_loop.add_timeout( - self.start_time + timeout, - functools.partial(self._on_timeout, "while connecting"), - ) - stream = await self.tcp_client.connect( - host, - port, - af=af, - ssl_options=ssl_options, - max_buffer_size=self.max_buffer_size, - source_ip=source_ip, - ) - - if self.final_callback is None: - # final_callback is cleared if we've hit our timeout. - stream.close() - return - self.stream = stream - self.stream.set_close_callback(self.on_connection_close) - self._remove_timeout() - if self.final_callback is None: - return - if self.request.request_timeout: - self._timeout = self.io_loop.add_timeout( - self.start_time + self.request.request_timeout, - functools.partial(self._on_timeout, "during request"), - ) - if ( - self.request.method not in self._SUPPORTED_METHODS - and not self.request.allow_nonstandard_methods - ): - raise KeyError("unknown method %s" % self.request.method) - for key in ( - "proxy_host", - "proxy_port", - "proxy_username", - "proxy_password", - "proxy_auth_mode", - ): - if getattr(self.request, key, None): - raise NotImplementedError("%s not supported" % key) - if "Connection" not in self.request.headers: - self.request.headers["Connection"] = "close" - if "Host" not in self.request.headers: - if "@" in self.parsed.netloc: - self.request.headers["Host"] = self.parsed.netloc.rpartition("@")[ - -1 - ] - else: - self.request.headers["Host"] = self.parsed.netloc - username, password = None, None - if self.parsed.username is not None: - username, password = self.parsed.username, self.parsed.password - elif self.request.auth_username is not None: - username = self.request.auth_username - password = self.request.auth_password or "" - if username is not None: - assert password is not None - if self.request.auth_mode not in (None, "basic"): - raise ValueError("unsupported auth_mode %s", self.request.auth_mode) - self.request.headers["Authorization"] = "Basic " + _unicode( - base64.b64encode( - httputil.encode_username_password(username, password) - ) - ) - if self.request.user_agent: - self.request.headers["User-Agent"] = self.request.user_agent - elif self.request.headers.get("User-Agent") is None: - self.request.headers["User-Agent"] = "Tornado/{}".format(version) - if not self.request.allow_nonstandard_methods: - # Some HTTP methods nearly always have bodies while others - # almost never do. Fail in this case unless the user has - # opted out of sanity checks with allow_nonstandard_methods. - body_expected = self.request.method in ("POST", "PATCH", "PUT") - body_present = ( - self.request.body is not None - or self.request.body_producer is not None - ) - if (body_expected and not body_present) or ( - body_present and not body_expected - ): - raise ValueError( - "Body must %sbe None for method %s (unless " - "allow_nonstandard_methods is true)" - % ("not " if body_expected else "", self.request.method) - ) - if self.request.expect_100_continue: - self.request.headers["Expect"] = "100-continue" - if self.request.body is not None: - # When body_producer is used the caller is responsible for - # setting Content-Length (or else chunked encoding will be used). - self.request.headers["Content-Length"] = str(len(self.request.body)) - if ( - self.request.method == "POST" - and "Content-Type" not in self.request.headers - ): - self.request.headers[ - "Content-Type" - ] = "application/x-www-form-urlencoded" - if self.request.decompress_response: - self.request.headers["Accept-Encoding"] = "gzip" - req_path = (self.parsed.path or "/") + ( - ("?" + self.parsed.query) if self.parsed.query else "" - ) - self.connection = self._create_connection(stream) - start_line = httputil.RequestStartLine(self.request.method, req_path, "") - self.connection.write_headers(start_line, self.request.headers) - if self.request.expect_100_continue: - await self.connection.read_response(self) - else: - await self._write_body(True) - except Exception: - if not self._handle_exception(*sys.exc_info()): - raise - - def _get_ssl_options( - self, scheme: str - ) -> Union[None, Dict[str, Any], ssl.SSLContext]: - if scheme == "https": - if self.request.ssl_options is not None: - return self.request.ssl_options - # If we are using the defaults, don't construct a - # new SSLContext. - if ( - self.request.validate_cert - and self.request.ca_certs is None - and self.request.client_cert is None - and self.request.client_key is None - ): - return _client_ssl_defaults - ssl_ctx = ssl.create_default_context( - ssl.Purpose.SERVER_AUTH, cafile=self.request.ca_certs - ) - if not self.request.validate_cert: - ssl_ctx.check_hostname = False - ssl_ctx.verify_mode = ssl.CERT_NONE - if self.request.client_cert is not None: - ssl_ctx.load_cert_chain( - self.request.client_cert, self.request.client_key - ) - if hasattr(ssl, "OP_NO_COMPRESSION"): - # See netutil.ssl_options_to_context - ssl_ctx.options |= ssl.OP_NO_COMPRESSION - return ssl_ctx - return None - - def _on_timeout(self, info: Optional[str] = None) -> None: - """Timeout callback of _HTTPConnection instance. - - Raise a `HTTPTimeoutError` when a timeout occurs. - - :info string key: More detailed timeout information. - """ - self._timeout = None - error_message = "Timeout {0}".format(info) if info else "Timeout" - if self.final_callback is not None: - self._handle_exception( - HTTPTimeoutError, HTTPTimeoutError(error_message), None - ) - - def _remove_timeout(self) -> None: - if self._timeout is not None: - self.io_loop.remove_timeout(self._timeout) - self._timeout = None - - def _create_connection(self, stream: IOStream) -> HTTP1Connection: - stream.set_nodelay(True) - connection = HTTP1Connection( - stream, - True, - HTTP1ConnectionParameters( - no_keep_alive=True, - max_header_size=self.max_header_size, - max_body_size=self.max_body_size, - decompress=bool(self.request.decompress_response), - ), - self._sockaddr, - ) - return connection - - async def _write_body(self, start_read: bool) -> None: - if self.request.body is not None: - self.connection.write(self.request.body) - elif self.request.body_producer is not None: - fut = self.request.body_producer(self.connection.write) - if fut is not None: - await fut - self.connection.finish() - if start_read: - try: - await self.connection.read_response(self) - except StreamClosedError: - if not self._handle_exception(*sys.exc_info()): - raise - - def _release(self) -> None: - if self.release_callback is not None: - release_callback = self.release_callback - self.release_callback = None # type: ignore - release_callback() - - def _run_callback(self, response: HTTPResponse) -> None: - self._release() - if self.final_callback is not None: - final_callback = self.final_callback - self.final_callback = None # type: ignore - self.io_loop.add_callback(final_callback, response) - - def _handle_exception( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[TracebackType], - ) -> bool: - if self.final_callback: - self._remove_timeout() - if isinstance(value, StreamClosedError): - if value.real_error is None: - value = HTTPStreamClosedError("Stream closed") - else: - value = value.real_error - self._run_callback( - HTTPResponse( - self.request, - 599, - error=value, - request_time=self.io_loop.time() - self.start_time, - start_time=self.start_wall_time, - ) - ) - - if hasattr(self, "stream"): - # TODO: this may cause a StreamClosedError to be raised - # by the connection's Future. Should we cancel the - # connection more gracefully? - self.stream.close() - return True - else: - # If our callback has already been called, we are probably - # catching an exception that is not caused by us but rather - # some child of our callback. Rather than drop it on the floor, - # pass it along, unless it's just the stream being closed. - return isinstance(value, StreamClosedError) - - def on_connection_close(self) -> None: - if self.final_callback is not None: - message = "Connection closed" - if self.stream.error: - raise self.stream.error - try: - raise HTTPStreamClosedError(message) - except HTTPStreamClosedError: - self._handle_exception(*sys.exc_info()) - - async def headers_received( - self, - first_line: Union[httputil.ResponseStartLine, httputil.RequestStartLine], - headers: httputil.HTTPHeaders, - ) -> None: - assert isinstance(first_line, httputil.ResponseStartLine) - if self.request.expect_100_continue and first_line.code == 100: - await self._write_body(False) - return - self.code = first_line.code - self.reason = first_line.reason - self.headers = headers - - if self._should_follow_redirect(): - return - - if self.request.header_callback is not None: - # Reassemble the start line. - self.request.header_callback("%s %s %s\r\n" % first_line) - for k, v in self.headers.get_all(): - self.request.header_callback("%s: %s\r\n" % (k, v)) - self.request.header_callback("\r\n") - - def _should_follow_redirect(self) -> bool: - if self.request.follow_redirects: - assert self.request.max_redirects is not None - return ( - self.code in (301, 302, 303, 307, 308) - and self.request.max_redirects > 0 - and self.headers is not None - and self.headers.get("Location") is not None - ) - return False - - def finish(self) -> None: - assert self.code is not None - data = b"".join(self.chunks) - self._remove_timeout() - original_request = getattr(self.request, "original_request", self.request) - if self._should_follow_redirect(): - assert isinstance(self.request, _RequestProxy) - new_request = copy.copy(self.request.request) - new_request.url = urllib.parse.urljoin( - self.request.url, self.headers["Location"] - ) - new_request.max_redirects = self.request.max_redirects - 1 - del new_request.headers["Host"] - # https://tools.ietf.org/html/rfc7231#section-6.4 - # - # The original HTTP spec said that after a 301 or 302 - # redirect, the request method should be preserved. - # However, browsers implemented this by changing the - # method to GET, and the behavior stuck. 303 redirects - # always specified this POST-to-GET behavior, arguably - # for *all* methods, but libcurl < 7.70 only does this - # for POST, while libcurl >= 7.70 does it for other methods. - if (self.code == 303 and self.request.method != "HEAD") or ( - self.code in (301, 302) and self.request.method == "POST" - ): - new_request.method = "GET" - new_request.body = None - for h in [ - "Content-Length", - "Content-Type", - "Content-Encoding", - "Transfer-Encoding", - ]: - try: - del self.request.headers[h] - except KeyError: - pass - new_request.original_request = original_request - final_callback = self.final_callback - self.final_callback = None - self._release() - fut = self.client.fetch(new_request, raise_error=False) - fut.add_done_callback(lambda f: final_callback(f.result())) - self._on_end_request() - return - if self.request.streaming_callback: - buffer = BytesIO() - else: - buffer = BytesIO(data) # TODO: don't require one big string? - response = HTTPResponse( - original_request, - self.code, - reason=getattr(self, "reason", None), - headers=self.headers, - request_time=self.io_loop.time() - self.start_time, - start_time=self.start_wall_time, - buffer=buffer, - effective_url=self.request.url, - ) - self._run_callback(response) - self._on_end_request() - - def _on_end_request(self) -> None: - self.stream.close() - - def data_received(self, chunk: bytes) -> None: - if self._should_follow_redirect(): - # We're going to follow a redirect so just discard the body. - return - if self.request.streaming_callback is not None: - self.request.streaming_callback(chunk) - else: - self.chunks.append(chunk) - - -if __name__ == "__main__": - AsyncHTTPClient.configure(SimpleAsyncHTTPClient) - main() diff --git a/telegramer/include/tornado/speedups.c b/telegramer/include/tornado/speedups.c deleted file mode 100644 index 525d660..0000000 --- a/telegramer/include/tornado/speedups.c +++ /dev/null @@ -1,70 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <stdint.h> - -static PyObject* websocket_mask(PyObject* self, PyObject* args) { - const char* mask; - Py_ssize_t mask_len; - uint32_t uint32_mask; - uint64_t uint64_mask; - const char* data; - Py_ssize_t data_len; - Py_ssize_t i; - PyObject* result; - char* buf; - - if (!PyArg_ParseTuple(args, "s#s#", &mask, &mask_len, &data, &data_len)) { - return NULL; - } - - uint32_mask = ((uint32_t*)mask)[0]; - - result = PyBytes_FromStringAndSize(NULL, data_len); - if (!result) { - return NULL; - } - buf = PyBytes_AsString(result); - - if (sizeof(size_t) >= 8) { - uint64_mask = uint32_mask; - uint64_mask = (uint64_mask << 32) | uint32_mask; - - while (data_len >= 8) { - ((uint64_t*)buf)[0] = ((uint64_t*)data)[0] ^ uint64_mask; - data += 8; - buf += 8; - data_len -= 8; - } - } - - while (data_len >= 4) { - ((uint32_t*)buf)[0] = ((uint32_t*)data)[0] ^ uint32_mask; - data += 4; - buf += 4; - data_len -= 4; - } - - for (i = 0; i < data_len; i++) { - buf[i] = data[i] ^ mask[i]; - } - - return result; -} - -static PyMethodDef methods[] = { - {"websocket_mask", websocket_mask, METH_VARARGS, ""}, - {NULL, NULL, 0, NULL} -}; - -static struct PyModuleDef speedupsmodule = { - PyModuleDef_HEAD_INIT, - "speedups", - NULL, - -1, - methods -}; - -PyMODINIT_FUNC -PyInit_speedups(void) { - return PyModule_Create(&speedupsmodule); -} diff --git a/telegramer/include/tornado/tcpclient.py b/telegramer/include/tornado/tcpclient.py deleted file mode 100644 index e2d682e..0000000 --- a/telegramer/include/tornado/tcpclient.py +++ /dev/null @@ -1,328 +0,0 @@ -# -# Copyright 2014 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A non-blocking TCP connection factory. -""" - -import functools -import socket -import numbers -import datetime -import ssl - -from tornado.concurrent import Future, future_add_done_callback -from tornado.ioloop import IOLoop -from tornado.iostream import IOStream -from tornado import gen -from tornado.netutil import Resolver -from tornado.gen import TimeoutError - -from typing import Any, Union, Dict, Tuple, List, Callable, Iterator, Optional, Set - -_INITIAL_CONNECT_TIMEOUT = 0.3 - - -class _Connector(object): - """A stateless implementation of the "Happy Eyeballs" algorithm. - - "Happy Eyeballs" is documented in RFC6555 as the recommended practice - for when both IPv4 and IPv6 addresses are available. - - In this implementation, we partition the addresses by family, and - make the first connection attempt to whichever address was - returned first by ``getaddrinfo``. If that connection fails or - times out, we begin a connection in parallel to the first address - of the other family. If there are additional failures we retry - with other addresses, keeping one connection attempt per family - in flight at a time. - - http://tools.ietf.org/html/rfc6555 - - """ - - def __init__( - self, - addrinfo: List[Tuple], - connect: Callable[ - [socket.AddressFamily, Tuple], Tuple[IOStream, "Future[IOStream]"] - ], - ) -> None: - self.io_loop = IOLoop.current() - self.connect = connect - - self.future = ( - Future() - ) # type: Future[Tuple[socket.AddressFamily, Any, IOStream]] - self.timeout = None # type: Optional[object] - self.connect_timeout = None # type: Optional[object] - self.last_error = None # type: Optional[Exception] - self.remaining = len(addrinfo) - self.primary_addrs, self.secondary_addrs = self.split(addrinfo) - self.streams = set() # type: Set[IOStream] - - @staticmethod - def split( - addrinfo: List[Tuple], - ) -> Tuple[ - List[Tuple[socket.AddressFamily, Tuple]], - List[Tuple[socket.AddressFamily, Tuple]], - ]: - """Partition the ``addrinfo`` list by address family. - - Returns two lists. The first list contains the first entry from - ``addrinfo`` and all others with the same family, and the - second list contains all other addresses (normally one list will - be AF_INET and the other AF_INET6, although non-standard resolvers - may return additional families). - """ - primary = [] - secondary = [] - primary_af = addrinfo[0][0] - for af, addr in addrinfo: - if af == primary_af: - primary.append((af, addr)) - else: - secondary.append((af, addr)) - return primary, secondary - - def start( - self, - timeout: float = _INITIAL_CONNECT_TIMEOUT, - connect_timeout: Optional[Union[float, datetime.timedelta]] = None, - ) -> "Future[Tuple[socket.AddressFamily, Any, IOStream]]": - self.try_connect(iter(self.primary_addrs)) - self.set_timeout(timeout) - if connect_timeout is not None: - self.set_connect_timeout(connect_timeout) - return self.future - - def try_connect(self, addrs: Iterator[Tuple[socket.AddressFamily, Tuple]]) -> None: - try: - af, addr = next(addrs) - except StopIteration: - # We've reached the end of our queue, but the other queue - # might still be working. Send a final error on the future - # only when both queues are finished. - if self.remaining == 0 and not self.future.done(): - self.future.set_exception( - self.last_error or IOError("connection failed") - ) - return - stream, future = self.connect(af, addr) - self.streams.add(stream) - future_add_done_callback( - future, functools.partial(self.on_connect_done, addrs, af, addr) - ) - - def on_connect_done( - self, - addrs: Iterator[Tuple[socket.AddressFamily, Tuple]], - af: socket.AddressFamily, - addr: Tuple, - future: "Future[IOStream]", - ) -> None: - self.remaining -= 1 - try: - stream = future.result() - except Exception as e: - if self.future.done(): - return - # Error: try again (but remember what happened so we have an - # error to raise in the end) - self.last_error = e - self.try_connect(addrs) - if self.timeout is not None: - # If the first attempt failed, don't wait for the - # timeout to try an address from the secondary queue. - self.io_loop.remove_timeout(self.timeout) - self.on_timeout() - return - self.clear_timeouts() - if self.future.done(): - # This is a late arrival; just drop it. - stream.close() - else: - self.streams.discard(stream) - self.future.set_result((af, addr, stream)) - self.close_streams() - - def set_timeout(self, timeout: float) -> None: - self.timeout = self.io_loop.add_timeout( - self.io_loop.time() + timeout, self.on_timeout - ) - - def on_timeout(self) -> None: - self.timeout = None - if not self.future.done(): - self.try_connect(iter(self.secondary_addrs)) - - def clear_timeout(self) -> None: - if self.timeout is not None: - self.io_loop.remove_timeout(self.timeout) - - def set_connect_timeout( - self, connect_timeout: Union[float, datetime.timedelta] - ) -> None: - self.connect_timeout = self.io_loop.add_timeout( - connect_timeout, self.on_connect_timeout - ) - - def on_connect_timeout(self) -> None: - if not self.future.done(): - self.future.set_exception(TimeoutError()) - self.close_streams() - - def clear_timeouts(self) -> None: - if self.timeout is not None: - self.io_loop.remove_timeout(self.timeout) - if self.connect_timeout is not None: - self.io_loop.remove_timeout(self.connect_timeout) - - def close_streams(self) -> None: - for stream in self.streams: - stream.close() - - -class TCPClient(object): - """A non-blocking TCP connection factory. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - """ - - def __init__(self, resolver: Optional[Resolver] = None) -> None: - if resolver is not None: - self.resolver = resolver - self._own_resolver = False - else: - self.resolver = Resolver() - self._own_resolver = True - - def close(self) -> None: - if self._own_resolver: - self.resolver.close() - - async def connect( - self, - host: str, - port: int, - af: socket.AddressFamily = socket.AF_UNSPEC, - ssl_options: Optional[Union[Dict[str, Any], ssl.SSLContext]] = None, - max_buffer_size: Optional[int] = None, - source_ip: Optional[str] = None, - source_port: Optional[int] = None, - timeout: Optional[Union[float, datetime.timedelta]] = None, - ) -> IOStream: - """Connect to the given host and port. - - Asynchronously returns an `.IOStream` (or `.SSLIOStream` if - ``ssl_options`` is not None). - - Using the ``source_ip`` kwarg, one can specify the source - IP address to use when establishing the connection. - In case the user needs to resolve and - use a specific interface, it has to be handled outside - of Tornado as this depends very much on the platform. - - Raises `TimeoutError` if the input future does not complete before - ``timeout``, which may be specified in any form allowed by - `.IOLoop.add_timeout` (i.e. a `datetime.timedelta` or an absolute time - relative to `.IOLoop.time`) - - Similarly, when the user requires a certain source port, it can - be specified using the ``source_port`` arg. - - .. versionchanged:: 4.5 - Added the ``source_ip`` and ``source_port`` arguments. - - .. versionchanged:: 5.0 - Added the ``timeout`` argument. - """ - if timeout is not None: - if isinstance(timeout, numbers.Real): - timeout = IOLoop.current().time() + timeout - elif isinstance(timeout, datetime.timedelta): - timeout = IOLoop.current().time() + timeout.total_seconds() - else: - raise TypeError("Unsupported timeout %r" % timeout) - if timeout is not None: - addrinfo = await gen.with_timeout( - timeout, self.resolver.resolve(host, port, af) - ) - else: - addrinfo = await self.resolver.resolve(host, port, af) - connector = _Connector( - addrinfo, - functools.partial( - self._create_stream, - max_buffer_size, - source_ip=source_ip, - source_port=source_port, - ), - ) - af, addr, stream = await connector.start(connect_timeout=timeout) - # TODO: For better performance we could cache the (af, addr) - # information here and re-use it on subsequent connections to - # the same host. (http://tools.ietf.org/html/rfc6555#section-4.2) - if ssl_options is not None: - if timeout is not None: - stream = await gen.with_timeout( - timeout, - stream.start_tls( - False, ssl_options=ssl_options, server_hostname=host - ), - ) - else: - stream = await stream.start_tls( - False, ssl_options=ssl_options, server_hostname=host - ) - return stream - - def _create_stream( - self, - max_buffer_size: int, - af: socket.AddressFamily, - addr: Tuple, - source_ip: Optional[str] = None, - source_port: Optional[int] = None, - ) -> Tuple[IOStream, "Future[IOStream]"]: - # Always connect in plaintext; we'll convert to ssl if necessary - # after one connection has completed. - source_port_bind = source_port if isinstance(source_port, int) else 0 - source_ip_bind = source_ip - if source_port_bind and not source_ip: - # User required a specific port, but did not specify - # a certain source IP, will bind to the default loopback. - source_ip_bind = "::1" if af == socket.AF_INET6 else "127.0.0.1" - # Trying to use the same address family as the requested af socket: - # - 127.0.0.1 for IPv4 - # - ::1 for IPv6 - socket_obj = socket.socket(af) - if source_port_bind or source_ip_bind: - # If the user requires binding also to a specific IP/port. - try: - socket_obj.bind((source_ip_bind, source_port_bind)) - except socket.error: - socket_obj.close() - # Fail loudly if unable to use the IP/port. - raise - try: - stream = IOStream(socket_obj, max_buffer_size=max_buffer_size) - except socket.error as e: - fu = Future() # type: Future[IOStream] - fu.set_exception(e) - return stream, fu - else: - return stream, stream.connect(addr) diff --git a/telegramer/include/tornado/tcpserver.py b/telegramer/include/tornado/tcpserver.py deleted file mode 100644 index 476ffc9..0000000 --- a/telegramer/include/tornado/tcpserver.py +++ /dev/null @@ -1,334 +0,0 @@ -# -# Copyright 2011 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A non-blocking, single-threaded TCP server.""" - -import errno -import os -import socket -import ssl - -from tornado import gen -from tornado.log import app_log -from tornado.ioloop import IOLoop -from tornado.iostream import IOStream, SSLIOStream -from tornado.netutil import bind_sockets, add_accept_handler, ssl_wrap_socket -from tornado import process -from tornado.util import errno_from_exception - -import typing -from typing import Union, Dict, Any, Iterable, Optional, Awaitable - -if typing.TYPE_CHECKING: - from typing import Callable, List # noqa: F401 - - -class TCPServer(object): - r"""A non-blocking, single-threaded TCP server. - - To use `TCPServer`, define a subclass which overrides the `handle_stream` - method. For example, a simple echo server could be defined like this:: - - from tornado.tcpserver import TCPServer - from tornado.iostream import StreamClosedError - from tornado import gen - - class EchoServer(TCPServer): - async def handle_stream(self, stream, address): - while True: - try: - data = await stream.read_until(b"\n") - await stream.write(data) - except StreamClosedError: - break - - To make this server serve SSL traffic, send the ``ssl_options`` keyword - argument with an `ssl.SSLContext` object. For compatibility with older - versions of Python ``ssl_options`` may also be a dictionary of keyword - arguments for the `ssl.wrap_socket` method.:: - - ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_ctx.load_cert_chain(os.path.join(data_dir, "mydomain.crt"), - os.path.join(data_dir, "mydomain.key")) - TCPServer(ssl_options=ssl_ctx) - - `TCPServer` initialization follows one of three patterns: - - 1. `listen`: simple single-process:: - - server = TCPServer() - server.listen(8888) - IOLoop.current().start() - - 2. `bind`/`start`: simple multi-process:: - - server = TCPServer() - server.bind(8888) - server.start(0) # Forks multiple sub-processes - IOLoop.current().start() - - When using this interface, an `.IOLoop` must *not* be passed - to the `TCPServer` constructor. `start` will always start - the server on the default singleton `.IOLoop`. - - 3. `add_sockets`: advanced multi-process:: - - sockets = bind_sockets(8888) - tornado.process.fork_processes(0) - server = TCPServer() - server.add_sockets(sockets) - IOLoop.current().start() - - The `add_sockets` interface is more complicated, but it can be - used with `tornado.process.fork_processes` to give you more - flexibility in when the fork happens. `add_sockets` can - also be used in single-process servers if you want to create - your listening sockets in some way other than - `~tornado.netutil.bind_sockets`. - - .. versionadded:: 3.1 - The ``max_buffer_size`` argument. - - .. versionchanged:: 5.0 - The ``io_loop`` argument has been removed. - """ - - def __init__( - self, - ssl_options: Optional[Union[Dict[str, Any], ssl.SSLContext]] = None, - max_buffer_size: Optional[int] = None, - read_chunk_size: Optional[int] = None, - ) -> None: - self.ssl_options = ssl_options - self._sockets = {} # type: Dict[int, socket.socket] - self._handlers = {} # type: Dict[int, Callable[[], None]] - self._pending_sockets = [] # type: List[socket.socket] - self._started = False - self._stopped = False - self.max_buffer_size = max_buffer_size - self.read_chunk_size = read_chunk_size - - # Verify the SSL options. Otherwise we don't get errors until clients - # connect. This doesn't verify that the keys are legitimate, but - # the SSL module doesn't do that until there is a connected socket - # which seems like too much work - if self.ssl_options is not None and isinstance(self.ssl_options, dict): - # Only certfile is required: it can contain both keys - if "certfile" not in self.ssl_options: - raise KeyError('missing key "certfile" in ssl_options') - - if not os.path.exists(self.ssl_options["certfile"]): - raise ValueError( - 'certfile "%s" does not exist' % self.ssl_options["certfile"] - ) - if "keyfile" in self.ssl_options and not os.path.exists( - self.ssl_options["keyfile"] - ): - raise ValueError( - 'keyfile "%s" does not exist' % self.ssl_options["keyfile"] - ) - - def listen(self, port: int, address: str = "") -> None: - """Starts accepting connections on the given port. - - This method may be called more than once to listen on multiple ports. - `listen` takes effect immediately; it is not necessary to call - `TCPServer.start` afterwards. It is, however, necessary to start - the `.IOLoop`. - """ - sockets = bind_sockets(port, address=address) - self.add_sockets(sockets) - - def add_sockets(self, sockets: Iterable[socket.socket]) -> None: - """Makes this server start accepting connections on the given sockets. - - The ``sockets`` parameter is a list of socket objects such as - those returned by `~tornado.netutil.bind_sockets`. - `add_sockets` is typically used in combination with that - method and `tornado.process.fork_processes` to provide greater - control over the initialization of a multi-process server. - """ - for sock in sockets: - self._sockets[sock.fileno()] = sock - self._handlers[sock.fileno()] = add_accept_handler( - sock, self._handle_connection - ) - - def add_socket(self, socket: socket.socket) -> None: - """Singular version of `add_sockets`. Takes a single socket object.""" - self.add_sockets([socket]) - - def bind( - self, - port: int, - address: Optional[str] = None, - family: socket.AddressFamily = socket.AF_UNSPEC, - backlog: int = 128, - reuse_port: bool = False, - ) -> None: - """Binds this server to the given port on the given address. - - To start the server, call `start`. If you want to run this server - in a single process, you can call `listen` as a shortcut to the - sequence of `bind` and `start` calls. - - Address may be either an IP address or hostname. If it's a hostname, - the server will listen on all IP addresses associated with the - name. Address may be an empty string or None to listen on all - available interfaces. Family may be set to either `socket.AF_INET` - or `socket.AF_INET6` to restrict to IPv4 or IPv6 addresses, otherwise - both will be used if available. - - The ``backlog`` argument has the same meaning as for - `socket.listen <socket.socket.listen>`. The ``reuse_port`` argument - has the same meaning as for `.bind_sockets`. - - This method may be called multiple times prior to `start` to listen - on multiple ports or interfaces. - - .. versionchanged:: 4.4 - Added the ``reuse_port`` argument. - """ - sockets = bind_sockets( - port, address=address, family=family, backlog=backlog, reuse_port=reuse_port - ) - if self._started: - self.add_sockets(sockets) - else: - self._pending_sockets.extend(sockets) - - def start( - self, num_processes: Optional[int] = 1, max_restarts: Optional[int] = None - ) -> None: - """Starts this server in the `.IOLoop`. - - By default, we run the server in this process and do not fork any - additional child process. - - If num_processes is ``None`` or <= 0, we detect the number of cores - available on this machine and fork that number of child - processes. If num_processes is given and > 1, we fork that - specific number of sub-processes. - - Since we use processes and not threads, there is no shared memory - between any server code. - - Note that multiple processes are not compatible with the autoreload - module (or the ``autoreload=True`` option to `tornado.web.Application` - which defaults to True when ``debug=True``). - When using multiple processes, no IOLoops can be created or - referenced until after the call to ``TCPServer.start(n)``. - - Values of ``num_processes`` other than 1 are not supported on Windows. - - The ``max_restarts`` argument is passed to `.fork_processes`. - - .. versionchanged:: 6.0 - - Added ``max_restarts`` argument. - """ - assert not self._started - self._started = True - if num_processes != 1: - process.fork_processes(num_processes, max_restarts) - sockets = self._pending_sockets - self._pending_sockets = [] - self.add_sockets(sockets) - - def stop(self) -> None: - """Stops listening for new connections. - - Requests currently in progress may still continue after the - server is stopped. - """ - if self._stopped: - return - self._stopped = True - for fd, sock in self._sockets.items(): - assert sock.fileno() == fd - # Unregister socket from IOLoop - self._handlers.pop(fd)() - sock.close() - - def handle_stream( - self, stream: IOStream, address: tuple - ) -> Optional[Awaitable[None]]: - """Override to handle a new `.IOStream` from an incoming connection. - - This method may be a coroutine; if so any exceptions it raises - asynchronously will be logged. Accepting of incoming connections - will not be blocked by this coroutine. - - If this `TCPServer` is configured for SSL, ``handle_stream`` - may be called before the SSL handshake has completed. Use - `.SSLIOStream.wait_for_handshake` if you need to verify the client's - certificate or use NPN/ALPN. - - .. versionchanged:: 4.2 - Added the option for this method to be a coroutine. - """ - raise NotImplementedError() - - def _handle_connection(self, connection: socket.socket, address: Any) -> None: - if self.ssl_options is not None: - assert ssl, "Python 2.6+ and OpenSSL required for SSL" - try: - connection = ssl_wrap_socket( - connection, - self.ssl_options, - server_side=True, - do_handshake_on_connect=False, - ) - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_EOF: - return connection.close() - else: - raise - except socket.error as err: - # If the connection is closed immediately after it is created - # (as in a port scan), we can get one of several errors. - # wrap_socket makes an internal call to getpeername, - # which may return either EINVAL (Mac OS X) or ENOTCONN - # (Linux). If it returns ENOTCONN, this error is - # silently swallowed by the ssl module, so we need to - # catch another error later on (AttributeError in - # SSLIOStream._do_ssl_handshake). - # To test this behavior, try nmap with the -sT flag. - # https://github.com/tornadoweb/tornado/pull/750 - if errno_from_exception(err) in (errno.ECONNABORTED, errno.EINVAL): - return connection.close() - else: - raise - try: - if self.ssl_options is not None: - stream = SSLIOStream( - connection, - max_buffer_size=self.max_buffer_size, - read_chunk_size=self.read_chunk_size, - ) # type: IOStream - else: - stream = IOStream( - connection, - max_buffer_size=self.max_buffer_size, - read_chunk_size=self.read_chunk_size, - ) - - future = self.handle_stream(stream, address) - if future is not None: - IOLoop.current().add_future( - gen.convert_yielded(future), lambda f: f.result() - ) - except Exception: - app_log.error("Error in connection callback", exc_info=True) diff --git a/telegramer/include/tornado/template.py b/telegramer/include/tornado/template.py deleted file mode 100644 index 2e6e0a2..0000000 --- a/telegramer/include/tornado/template.py +++ /dev/null @@ -1,1048 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""A simple template system that compiles templates to Python code. - -Basic usage looks like:: - - t = template.Template("<html>{{ myvalue }}</html>") - print(t.generate(myvalue="XXX")) - -`Loader` is a class that loads templates from a root directory and caches -the compiled templates:: - - loader = template.Loader("/home/btaylor") - print(loader.load("test.html").generate(myvalue="XXX")) - -We compile all templates to raw Python. Error-reporting is currently... uh, -interesting. Syntax for the templates:: - - ### base.html - <html> - <head> - <title>{% block title %}Default title{% end %}</title> - </head> - <body> - <ul> - {% for student in students %} - {% block student %} - <li>{{ escape(student.name) }}</li> - {% end %} - {% end %} - </ul> - </body> - </html> - - ### bold.html - {% extends "base.html" %} - - {% block title %}A bolder title{% end %} - - {% block student %} - <li><span style="bold">{{ escape(student.name) }}</span></li> - {% end %} - -Unlike most other template systems, we do not put any restrictions on the -expressions you can include in your statements. ``if`` and ``for`` blocks get -translated exactly into Python, so you can do complex expressions like:: - - {% for student in [p for p in people if p.student and p.age > 23] %} - <li>{{ escape(student.name) }}</li> - {% end %} - -Translating directly to Python means you can apply functions to expressions -easily, like the ``escape()`` function in the examples above. You can pass -functions in to your template just like any other variable -(In a `.RequestHandler`, override `.RequestHandler.get_template_namespace`):: - - ### Python code - def add(x, y): - return x + y - template.execute(add=add) - - ### The template - {{ add(1, 2) }} - -We provide the functions `escape() <.xhtml_escape>`, `.url_escape()`, -`.json_encode()`, and `.squeeze()` to all templates by default. - -Typical applications do not create `Template` or `Loader` instances by -hand, but instead use the `~.RequestHandler.render` and -`~.RequestHandler.render_string` methods of -`tornado.web.RequestHandler`, which load templates automatically based -on the ``template_path`` `.Application` setting. - -Variable names beginning with ``_tt_`` are reserved by the template -system and should not be used by application code. - -Syntax Reference ----------------- - -Template expressions are surrounded by double curly braces: ``{{ ... }}``. -The contents may be any python expression, which will be escaped according -to the current autoescape setting and inserted into the output. Other -template directives use ``{% %}``. - -To comment out a section so that it is omitted from the output, surround it -with ``{# ... #}``. - - -To include a literal ``{{``, ``{%``, or ``{#`` in the output, escape them as -``{{!``, ``{%!``, and ``{#!``, respectively. - - -``{% apply *function* %}...{% end %}`` - Applies a function to the output of all template code between ``apply`` - and ``end``:: - - {% apply linkify %}{{name}} said: {{message}}{% end %} - - Note that as an implementation detail apply blocks are implemented - as nested functions and thus may interact strangely with variables - set via ``{% set %}``, or the use of ``{% break %}`` or ``{% continue %}`` - within loops. - -``{% autoescape *function* %}`` - Sets the autoescape mode for the current file. This does not affect - other files, even those referenced by ``{% include %}``. Note that - autoescaping can also be configured globally, at the `.Application` - or `Loader`.:: - - {% autoescape xhtml_escape %} - {% autoescape None %} - -``{% block *name* %}...{% end %}`` - Indicates a named, replaceable block for use with ``{% extends %}``. - Blocks in the parent template will be replaced with the contents of - the same-named block in a child template.:: - - <!-- base.html --> - <title>{% block title %}Default title{% end %}</title> - - <!-- mypage.html --> - {% extends "base.html" %} - {% block title %}My page title{% end %} - -``{% comment ... %}`` - A comment which will be removed from the template output. Note that - there is no ``{% end %}`` tag; the comment goes from the word ``comment`` - to the closing ``%}`` tag. - -``{% extends *filename* %}`` - Inherit from another template. Templates that use ``extends`` should - contain one or more ``block`` tags to replace content from the parent - template. Anything in the child template not contained in a ``block`` - tag will be ignored. For an example, see the ``{% block %}`` tag. - -``{% for *var* in *expr* %}...{% end %}`` - Same as the python ``for`` statement. ``{% break %}`` and - ``{% continue %}`` may be used inside the loop. - -``{% from *x* import *y* %}`` - Same as the python ``import`` statement. - -``{% if *condition* %}...{% elif *condition* %}...{% else %}...{% end %}`` - Conditional statement - outputs the first section whose condition is - true. (The ``elif`` and ``else`` sections are optional) - -``{% import *module* %}`` - Same as the python ``import`` statement. - -``{% include *filename* %}`` - Includes another template file. The included file can see all the local - variables as if it were copied directly to the point of the ``include`` - directive (the ``{% autoescape %}`` directive is an exception). - Alternately, ``{% module Template(filename, **kwargs) %}`` may be used - to include another template with an isolated namespace. - -``{% module *expr* %}`` - Renders a `~tornado.web.UIModule`. The output of the ``UIModule`` is - not escaped:: - - {% module Template("foo.html", arg=42) %} - - ``UIModules`` are a feature of the `tornado.web.RequestHandler` - class (and specifically its ``render`` method) and will not work - when the template system is used on its own in other contexts. - -``{% raw *expr* %}`` - Outputs the result of the given expression without autoescaping. - -``{% set *x* = *y* %}`` - Sets a local variable. - -``{% try %}...{% except %}...{% else %}...{% finally %}...{% end %}`` - Same as the python ``try`` statement. - -``{% while *condition* %}... {% end %}`` - Same as the python ``while`` statement. ``{% break %}`` and - ``{% continue %}`` may be used inside the loop. - -``{% whitespace *mode* %}`` - Sets the whitespace mode for the remainder of the current file - (or until the next ``{% whitespace %}`` directive). See - `filter_whitespace` for available options. New in Tornado 4.3. -""" - -import datetime -from io import StringIO -import linecache -import os.path -import posixpath -import re -import threading - -from tornado import escape -from tornado.log import app_log -from tornado.util import ObjectDict, exec_in, unicode_type - -from typing import Any, Union, Callable, List, Dict, Iterable, Optional, TextIO -import typing - -if typing.TYPE_CHECKING: - from typing import Tuple, ContextManager # noqa: F401 - -_DEFAULT_AUTOESCAPE = "xhtml_escape" - - -class _UnsetMarker: - pass - - -_UNSET = _UnsetMarker() - - -def filter_whitespace(mode: str, text: str) -> str: - """Transform whitespace in ``text`` according to ``mode``. - - Available modes are: - - * ``all``: Return all whitespace unmodified. - * ``single``: Collapse consecutive whitespace with a single whitespace - character, preserving newlines. - * ``oneline``: Collapse all runs of whitespace into a single space - character, removing all newlines in the process. - - .. versionadded:: 4.3 - """ - if mode == "all": - return text - elif mode == "single": - text = re.sub(r"([\t ]+)", " ", text) - text = re.sub(r"(\s*\n\s*)", "\n", text) - return text - elif mode == "oneline": - return re.sub(r"(\s+)", " ", text) - else: - raise Exception("invalid whitespace mode %s" % mode) - - -class Template(object): - """A compiled template. - - We compile into Python from the given template_string. You can generate - the template from variables with generate(). - """ - - # note that the constructor's signature is not extracted with - # autodoc because _UNSET looks like garbage. When changing - # this signature update website/sphinx/template.rst too. - def __init__( - self, - template_string: Union[str, bytes], - name: str = "<string>", - loader: Optional["BaseLoader"] = None, - compress_whitespace: Union[bool, _UnsetMarker] = _UNSET, - autoescape: Optional[Union[str, _UnsetMarker]] = _UNSET, - whitespace: Optional[str] = None, - ) -> None: - """Construct a Template. - - :arg str template_string: the contents of the template file. - :arg str name: the filename from which the template was loaded - (used for error message). - :arg tornado.template.BaseLoader loader: the `~tornado.template.BaseLoader` responsible - for this template, used to resolve ``{% include %}`` and ``{% extend %}`` directives. - :arg bool compress_whitespace: Deprecated since Tornado 4.3. - Equivalent to ``whitespace="single"`` if true and - ``whitespace="all"`` if false. - :arg str autoescape: The name of a function in the template - namespace, or ``None`` to disable escaping by default. - :arg str whitespace: A string specifying treatment of whitespace; - see `filter_whitespace` for options. - - .. versionchanged:: 4.3 - Added ``whitespace`` parameter; deprecated ``compress_whitespace``. - """ - self.name = escape.native_str(name) - - if compress_whitespace is not _UNSET: - # Convert deprecated compress_whitespace (bool) to whitespace (str). - if whitespace is not None: - raise Exception("cannot set both whitespace and compress_whitespace") - whitespace = "single" if compress_whitespace else "all" - if whitespace is None: - if loader and loader.whitespace: - whitespace = loader.whitespace - else: - # Whitespace defaults by filename. - if name.endswith(".html") or name.endswith(".js"): - whitespace = "single" - else: - whitespace = "all" - # Validate the whitespace setting. - assert whitespace is not None - filter_whitespace(whitespace, "") - - if not isinstance(autoescape, _UnsetMarker): - self.autoescape = autoescape # type: Optional[str] - elif loader: - self.autoescape = loader.autoescape - else: - self.autoescape = _DEFAULT_AUTOESCAPE - - self.namespace = loader.namespace if loader else {} - reader = _TemplateReader(name, escape.native_str(template_string), whitespace) - self.file = _File(self, _parse(reader, self)) - self.code = self._generate_python(loader) - self.loader = loader - try: - # Under python2.5, the fake filename used here must match - # the module name used in __name__ below. - # The dont_inherit flag prevents template.py's future imports - # from being applied to the generated code. - self.compiled = compile( - escape.to_unicode(self.code), - "%s.generated.py" % self.name.replace(".", "_"), - "exec", - dont_inherit=True, - ) - except Exception: - formatted_code = _format_code(self.code).rstrip() - app_log.error("%s code:\n%s", self.name, formatted_code) - raise - - def generate(self, **kwargs: Any) -> bytes: - """Generate this template with the given arguments.""" - namespace = { - "escape": escape.xhtml_escape, - "xhtml_escape": escape.xhtml_escape, - "url_escape": escape.url_escape, - "json_encode": escape.json_encode, - "squeeze": escape.squeeze, - "linkify": escape.linkify, - "datetime": datetime, - "_tt_utf8": escape.utf8, # for internal use - "_tt_string_types": (unicode_type, bytes), - # __name__ and __loader__ allow the traceback mechanism to find - # the generated source code. - "__name__": self.name.replace(".", "_"), - "__loader__": ObjectDict(get_source=lambda name: self.code), - } - namespace.update(self.namespace) - namespace.update(kwargs) - exec_in(self.compiled, namespace) - execute = typing.cast(Callable[[], bytes], namespace["_tt_execute"]) - # Clear the traceback module's cache of source data now that - # we've generated a new template (mainly for this module's - # unittests, where different tests reuse the same name). - linecache.clearcache() - return execute() - - def _generate_python(self, loader: Optional["BaseLoader"]) -> str: - buffer = StringIO() - try: - # named_blocks maps from names to _NamedBlock objects - named_blocks = {} # type: Dict[str, _NamedBlock] - ancestors = self._get_ancestors(loader) - ancestors.reverse() - for ancestor in ancestors: - ancestor.find_named_blocks(loader, named_blocks) - writer = _CodeWriter(buffer, named_blocks, loader, ancestors[0].template) - ancestors[0].generate(writer) - return buffer.getvalue() - finally: - buffer.close() - - def _get_ancestors(self, loader: Optional["BaseLoader"]) -> List["_File"]: - ancestors = [self.file] - for chunk in self.file.body.chunks: - if isinstance(chunk, _ExtendsBlock): - if not loader: - raise ParseError( - "{% extends %} block found, but no " "template loader" - ) - template = loader.load(chunk.name, self.name) - ancestors.extend(template._get_ancestors(loader)) - return ancestors - - -class BaseLoader(object): - """Base class for template loaders. - - You must use a template loader to use template constructs like - ``{% extends %}`` and ``{% include %}``. The loader caches all - templates after they are loaded the first time. - """ - - def __init__( - self, - autoescape: str = _DEFAULT_AUTOESCAPE, - namespace: Optional[Dict[str, Any]] = None, - whitespace: Optional[str] = None, - ) -> None: - """Construct a template loader. - - :arg str autoescape: The name of a function in the template - namespace, such as "xhtml_escape", or ``None`` to disable - autoescaping by default. - :arg dict namespace: A dictionary to be added to the default template - namespace, or ``None``. - :arg str whitespace: A string specifying default behavior for - whitespace in templates; see `filter_whitespace` for options. - Default is "single" for files ending in ".html" and ".js" and - "all" for other files. - - .. versionchanged:: 4.3 - Added ``whitespace`` parameter. - """ - self.autoescape = autoescape - self.namespace = namespace or {} - self.whitespace = whitespace - self.templates = {} # type: Dict[str, Template] - # self.lock protects self.templates. It's a reentrant lock - # because templates may load other templates via `include` or - # `extends`. Note that thanks to the GIL this code would be safe - # even without the lock, but could lead to wasted work as multiple - # threads tried to compile the same template simultaneously. - self.lock = threading.RLock() - - def reset(self) -> None: - """Resets the cache of compiled templates.""" - with self.lock: - self.templates = {} - - def resolve_path(self, name: str, parent_path: Optional[str] = None) -> str: - """Converts a possibly-relative path to absolute (used internally).""" - raise NotImplementedError() - - def load(self, name: str, parent_path: Optional[str] = None) -> Template: - """Loads a template.""" - name = self.resolve_path(name, parent_path=parent_path) - with self.lock: - if name not in self.templates: - self.templates[name] = self._create_template(name) - return self.templates[name] - - def _create_template(self, name: str) -> Template: - raise NotImplementedError() - - -class Loader(BaseLoader): - """A template loader that loads from a single root directory. - """ - - def __init__(self, root_directory: str, **kwargs: Any) -> None: - super().__init__(**kwargs) - self.root = os.path.abspath(root_directory) - - def resolve_path(self, name: str, parent_path: Optional[str] = None) -> str: - if ( - parent_path - and not parent_path.startswith("<") - and not parent_path.startswith("/") - and not name.startswith("/") - ): - current_path = os.path.join(self.root, parent_path) - file_dir = os.path.dirname(os.path.abspath(current_path)) - relative_path = os.path.abspath(os.path.join(file_dir, name)) - if relative_path.startswith(self.root): - name = relative_path[len(self.root) + 1 :] - return name - - def _create_template(self, name: str) -> Template: - path = os.path.join(self.root, name) - with open(path, "rb") as f: - template = Template(f.read(), name=name, loader=self) - return template - - -class DictLoader(BaseLoader): - """A template loader that loads from a dictionary.""" - - def __init__(self, dict: Dict[str, str], **kwargs: Any) -> None: - super().__init__(**kwargs) - self.dict = dict - - def resolve_path(self, name: str, parent_path: Optional[str] = None) -> str: - if ( - parent_path - and not parent_path.startswith("<") - and not parent_path.startswith("/") - and not name.startswith("/") - ): - file_dir = posixpath.dirname(parent_path) - name = posixpath.normpath(posixpath.join(file_dir, name)) - return name - - def _create_template(self, name: str) -> Template: - return Template(self.dict[name], name=name, loader=self) - - -class _Node(object): - def each_child(self) -> Iterable["_Node"]: - return () - - def generate(self, writer: "_CodeWriter") -> None: - raise NotImplementedError() - - def find_named_blocks( - self, loader: Optional[BaseLoader], named_blocks: Dict[str, "_NamedBlock"] - ) -> None: - for child in self.each_child(): - child.find_named_blocks(loader, named_blocks) - - -class _File(_Node): - def __init__(self, template: Template, body: "_ChunkList") -> None: - self.template = template - self.body = body - self.line = 0 - - def generate(self, writer: "_CodeWriter") -> None: - writer.write_line("def _tt_execute():", self.line) - with writer.indent(): - writer.write_line("_tt_buffer = []", self.line) - writer.write_line("_tt_append = _tt_buffer.append", self.line) - self.body.generate(writer) - writer.write_line("return _tt_utf8('').join(_tt_buffer)", self.line) - - def each_child(self) -> Iterable["_Node"]: - return (self.body,) - - -class _ChunkList(_Node): - def __init__(self, chunks: List[_Node]) -> None: - self.chunks = chunks - - def generate(self, writer: "_CodeWriter") -> None: - for chunk in self.chunks: - chunk.generate(writer) - - def each_child(self) -> Iterable["_Node"]: - return self.chunks - - -class _NamedBlock(_Node): - def __init__(self, name: str, body: _Node, template: Template, line: int) -> None: - self.name = name - self.body = body - self.template = template - self.line = line - - def each_child(self) -> Iterable["_Node"]: - return (self.body,) - - def generate(self, writer: "_CodeWriter") -> None: - block = writer.named_blocks[self.name] - with writer.include(block.template, self.line): - block.body.generate(writer) - - def find_named_blocks( - self, loader: Optional[BaseLoader], named_blocks: Dict[str, "_NamedBlock"] - ) -> None: - named_blocks[self.name] = self - _Node.find_named_blocks(self, loader, named_blocks) - - -class _ExtendsBlock(_Node): - def __init__(self, name: str) -> None: - self.name = name - - -class _IncludeBlock(_Node): - def __init__(self, name: str, reader: "_TemplateReader", line: int) -> None: - self.name = name - self.template_name = reader.name - self.line = line - - def find_named_blocks( - self, loader: Optional[BaseLoader], named_blocks: Dict[str, _NamedBlock] - ) -> None: - assert loader is not None - included = loader.load(self.name, self.template_name) - included.file.find_named_blocks(loader, named_blocks) - - def generate(self, writer: "_CodeWriter") -> None: - assert writer.loader is not None - included = writer.loader.load(self.name, self.template_name) - with writer.include(included, self.line): - included.file.body.generate(writer) - - -class _ApplyBlock(_Node): - def __init__(self, method: str, line: int, body: _Node) -> None: - self.method = method - self.line = line - self.body = body - - def each_child(self) -> Iterable["_Node"]: - return (self.body,) - - def generate(self, writer: "_CodeWriter") -> None: - method_name = "_tt_apply%d" % writer.apply_counter - writer.apply_counter += 1 - writer.write_line("def %s():" % method_name, self.line) - with writer.indent(): - writer.write_line("_tt_buffer = []", self.line) - writer.write_line("_tt_append = _tt_buffer.append", self.line) - self.body.generate(writer) - writer.write_line("return _tt_utf8('').join(_tt_buffer)", self.line) - writer.write_line( - "_tt_append(_tt_utf8(%s(%s())))" % (self.method, method_name), self.line - ) - - -class _ControlBlock(_Node): - def __init__(self, statement: str, line: int, body: _Node) -> None: - self.statement = statement - self.line = line - self.body = body - - def each_child(self) -> Iterable[_Node]: - return (self.body,) - - def generate(self, writer: "_CodeWriter") -> None: - writer.write_line("%s:" % self.statement, self.line) - with writer.indent(): - self.body.generate(writer) - # Just in case the body was empty - writer.write_line("pass", self.line) - - -class _IntermediateControlBlock(_Node): - def __init__(self, statement: str, line: int) -> None: - self.statement = statement - self.line = line - - def generate(self, writer: "_CodeWriter") -> None: - # In case the previous block was empty - writer.write_line("pass", self.line) - writer.write_line("%s:" % self.statement, self.line, writer.indent_size() - 1) - - -class _Statement(_Node): - def __init__(self, statement: str, line: int) -> None: - self.statement = statement - self.line = line - - def generate(self, writer: "_CodeWriter") -> None: - writer.write_line(self.statement, self.line) - - -class _Expression(_Node): - def __init__(self, expression: str, line: int, raw: bool = False) -> None: - self.expression = expression - self.line = line - self.raw = raw - - def generate(self, writer: "_CodeWriter") -> None: - writer.write_line("_tt_tmp = %s" % self.expression, self.line) - writer.write_line( - "if isinstance(_tt_tmp, _tt_string_types):" " _tt_tmp = _tt_utf8(_tt_tmp)", - self.line, - ) - writer.write_line("else: _tt_tmp = _tt_utf8(str(_tt_tmp))", self.line) - if not self.raw and writer.current_template.autoescape is not None: - # In python3 functions like xhtml_escape return unicode, - # so we have to convert to utf8 again. - writer.write_line( - "_tt_tmp = _tt_utf8(%s(_tt_tmp))" % writer.current_template.autoescape, - self.line, - ) - writer.write_line("_tt_append(_tt_tmp)", self.line) - - -class _Module(_Expression): - def __init__(self, expression: str, line: int) -> None: - super().__init__("_tt_modules." + expression, line, raw=True) - - -class _Text(_Node): - def __init__(self, value: str, line: int, whitespace: str) -> None: - self.value = value - self.line = line - self.whitespace = whitespace - - def generate(self, writer: "_CodeWriter") -> None: - value = self.value - - # Compress whitespace if requested, with a crude heuristic to avoid - # altering preformatted whitespace. - if "<pre>" not in value: - value = filter_whitespace(self.whitespace, value) - - if value: - writer.write_line("_tt_append(%r)" % escape.utf8(value), self.line) - - -class ParseError(Exception): - """Raised for template syntax errors. - - ``ParseError`` instances have ``filename`` and ``lineno`` attributes - indicating the position of the error. - - .. versionchanged:: 4.3 - Added ``filename`` and ``lineno`` attributes. - """ - - def __init__( - self, message: str, filename: Optional[str] = None, lineno: int = 0 - ) -> None: - self.message = message - # The names "filename" and "lineno" are chosen for consistency - # with python SyntaxError. - self.filename = filename - self.lineno = lineno - - def __str__(self) -> str: - return "%s at %s:%d" % (self.message, self.filename, self.lineno) - - -class _CodeWriter(object): - def __init__( - self, - file: TextIO, - named_blocks: Dict[str, _NamedBlock], - loader: Optional[BaseLoader], - current_template: Template, - ) -> None: - self.file = file - self.named_blocks = named_blocks - self.loader = loader - self.current_template = current_template - self.apply_counter = 0 - self.include_stack = [] # type: List[Tuple[Template, int]] - self._indent = 0 - - def indent_size(self) -> int: - return self._indent - - def indent(self) -> "ContextManager": - class Indenter(object): - def __enter__(_) -> "_CodeWriter": - self._indent += 1 - return self - - def __exit__(_, *args: Any) -> None: - assert self._indent > 0 - self._indent -= 1 - - return Indenter() - - def include(self, template: Template, line: int) -> "ContextManager": - self.include_stack.append((self.current_template, line)) - self.current_template = template - - class IncludeTemplate(object): - def __enter__(_) -> "_CodeWriter": - return self - - def __exit__(_, *args: Any) -> None: - self.current_template = self.include_stack.pop()[0] - - return IncludeTemplate() - - def write_line( - self, line: str, line_number: int, indent: Optional[int] = None - ) -> None: - if indent is None: - indent = self._indent - line_comment = " # %s:%d" % (self.current_template.name, line_number) - if self.include_stack: - ancestors = [ - "%s:%d" % (tmpl.name, lineno) for (tmpl, lineno) in self.include_stack - ] - line_comment += " (via %s)" % ", ".join(reversed(ancestors)) - print(" " * indent + line + line_comment, file=self.file) - - -class _TemplateReader(object): - def __init__(self, name: str, text: str, whitespace: str) -> None: - self.name = name - self.text = text - self.whitespace = whitespace - self.line = 1 - self.pos = 0 - - def find(self, needle: str, start: int = 0, end: Optional[int] = None) -> int: - assert start >= 0, start - pos = self.pos - start += pos - if end is None: - index = self.text.find(needle, start) - else: - end += pos - assert end >= start - index = self.text.find(needle, start, end) - if index != -1: - index -= pos - return index - - def consume(self, count: Optional[int] = None) -> str: - if count is None: - count = len(self.text) - self.pos - newpos = self.pos + count - self.line += self.text.count("\n", self.pos, newpos) - s = self.text[self.pos : newpos] - self.pos = newpos - return s - - def remaining(self) -> int: - return len(self.text) - self.pos - - def __len__(self) -> int: - return self.remaining() - - def __getitem__(self, key: Union[int, slice]) -> str: - if isinstance(key, slice): - size = len(self) - start, stop, step = key.indices(size) - if start is None: - start = self.pos - else: - start += self.pos - if stop is not None: - stop += self.pos - return self.text[slice(start, stop, step)] - elif key < 0: - return self.text[key] - else: - return self.text[self.pos + key] - - def __str__(self) -> str: - return self.text[self.pos :] - - def raise_parse_error(self, msg: str) -> None: - raise ParseError(msg, self.name, self.line) - - -def _format_code(code: str) -> str: - lines = code.splitlines() - format = "%%%dd %%s\n" % len(repr(len(lines) + 1)) - return "".join([format % (i + 1, line) for (i, line) in enumerate(lines)]) - - -def _parse( - reader: _TemplateReader, - template: Template, - in_block: Optional[str] = None, - in_loop: Optional[str] = None, -) -> _ChunkList: - body = _ChunkList([]) - while True: - # Find next template directive - curly = 0 - while True: - curly = reader.find("{", curly) - if curly == -1 or curly + 1 == reader.remaining(): - # EOF - if in_block: - reader.raise_parse_error( - "Missing {%% end %%} block for %s" % in_block - ) - body.chunks.append( - _Text(reader.consume(), reader.line, reader.whitespace) - ) - return body - # If the first curly brace is not the start of a special token, - # start searching from the character after it - if reader[curly + 1] not in ("{", "%", "#"): - curly += 1 - continue - # When there are more than 2 curlies in a row, use the - # innermost ones. This is useful when generating languages - # like latex where curlies are also meaningful - if ( - curly + 2 < reader.remaining() - and reader[curly + 1] == "{" - and reader[curly + 2] == "{" - ): - curly += 1 - continue - break - - # Append any text before the special token - if curly > 0: - cons = reader.consume(curly) - body.chunks.append(_Text(cons, reader.line, reader.whitespace)) - - start_brace = reader.consume(2) - line = reader.line - - # Template directives may be escaped as "{{!" or "{%!". - # In this case output the braces and consume the "!". - # This is especially useful in conjunction with jquery templates, - # which also use double braces. - if reader.remaining() and reader[0] == "!": - reader.consume(1) - body.chunks.append(_Text(start_brace, line, reader.whitespace)) - continue - - # Comment - if start_brace == "{#": - end = reader.find("#}") - if end == -1: - reader.raise_parse_error("Missing end comment #}") - contents = reader.consume(end).strip() - reader.consume(2) - continue - - # Expression - if start_brace == "{{": - end = reader.find("}}") - if end == -1: - reader.raise_parse_error("Missing end expression }}") - contents = reader.consume(end).strip() - reader.consume(2) - if not contents: - reader.raise_parse_error("Empty expression") - body.chunks.append(_Expression(contents, line)) - continue - - # Block - assert start_brace == "{%", start_brace - end = reader.find("%}") - if end == -1: - reader.raise_parse_error("Missing end block %}") - contents = reader.consume(end).strip() - reader.consume(2) - if not contents: - reader.raise_parse_error("Empty block tag ({% %})") - - operator, space, suffix = contents.partition(" ") - suffix = suffix.strip() - - # Intermediate ("else", "elif", etc) blocks - intermediate_blocks = { - "else": set(["if", "for", "while", "try"]), - "elif": set(["if"]), - "except": set(["try"]), - "finally": set(["try"]), - } - allowed_parents = intermediate_blocks.get(operator) - if allowed_parents is not None: - if not in_block: - reader.raise_parse_error( - "%s outside %s block" % (operator, allowed_parents) - ) - if in_block not in allowed_parents: - reader.raise_parse_error( - "%s block cannot be attached to %s block" % (operator, in_block) - ) - body.chunks.append(_IntermediateControlBlock(contents, line)) - continue - - # End tag - elif operator == "end": - if not in_block: - reader.raise_parse_error("Extra {% end %} block") - return body - - elif operator in ( - "extends", - "include", - "set", - "import", - "from", - "comment", - "autoescape", - "whitespace", - "raw", - "module", - ): - if operator == "comment": - continue - if operator == "extends": - suffix = suffix.strip('"').strip("'") - if not suffix: - reader.raise_parse_error("extends missing file path") - block = _ExtendsBlock(suffix) # type: _Node - elif operator in ("import", "from"): - if not suffix: - reader.raise_parse_error("import missing statement") - block = _Statement(contents, line) - elif operator == "include": - suffix = suffix.strip('"').strip("'") - if not suffix: - reader.raise_parse_error("include missing file path") - block = _IncludeBlock(suffix, reader, line) - elif operator == "set": - if not suffix: - reader.raise_parse_error("set missing statement") - block = _Statement(suffix, line) - elif operator == "autoescape": - fn = suffix.strip() # type: Optional[str] - if fn == "None": - fn = None - template.autoescape = fn - continue - elif operator == "whitespace": - mode = suffix.strip() - # Validate the selected mode - filter_whitespace(mode, "") - reader.whitespace = mode - continue - elif operator == "raw": - block = _Expression(suffix, line, raw=True) - elif operator == "module": - block = _Module(suffix, line) - body.chunks.append(block) - continue - - elif operator in ("apply", "block", "try", "if", "for", "while"): - # parse inner body recursively - if operator in ("for", "while"): - block_body = _parse(reader, template, operator, operator) - elif operator == "apply": - # apply creates a nested function so syntactically it's not - # in the loop. - block_body = _parse(reader, template, operator, None) - else: - block_body = _parse(reader, template, operator, in_loop) - - if operator == "apply": - if not suffix: - reader.raise_parse_error("apply missing method name") - block = _ApplyBlock(suffix, line, block_body) - elif operator == "block": - if not suffix: - reader.raise_parse_error("block missing name") - block = _NamedBlock(suffix, block_body, template, line) - else: - block = _ControlBlock(contents, line, block_body) - body.chunks.append(block) - continue - - elif operator in ("break", "continue"): - if not in_loop: - reader.raise_parse_error( - "%s outside %s block" % (operator, set(["for", "while"])) - ) - body.chunks.append(_Statement(contents, line)) - continue - - else: - reader.raise_parse_error("unknown operator: %r" % operator) diff --git a/telegramer/include/tornado/testing.py b/telegramer/include/tornado/testing.py deleted file mode 100644 index 3351b92..0000000 --- a/telegramer/include/tornado/testing.py +++ /dev/null @@ -1,818 +0,0 @@ -"""Support classes for automated testing. - -* `AsyncTestCase` and `AsyncHTTPTestCase`: Subclasses of unittest.TestCase - with additional support for testing asynchronous (`.IOLoop`-based) code. - -* `ExpectLog`: Make test logs less spammy. - -* `main()`: A simple test runner (wrapper around unittest.main()) with support - for the tornado.autoreload module to rerun the tests when code changes. -""" - -import asyncio -from collections.abc import Generator -import functools -import inspect -import logging -import os -import re -import signal -import socket -import sys -import unittest - -from tornado import gen -from tornado.httpclient import AsyncHTTPClient, HTTPResponse -from tornado.httpserver import HTTPServer -from tornado.ioloop import IOLoop, TimeoutError -from tornado import netutil -from tornado.platform.asyncio import AsyncIOMainLoop -from tornado.process import Subprocess -from tornado.log import app_log -from tornado.util import raise_exc_info, basestring_type -from tornado.web import Application - -import typing -from typing import Tuple, Any, Callable, Type, Dict, Union, Optional -from types import TracebackType - -if typing.TYPE_CHECKING: - # Coroutine wasn't added to typing until 3.5.3, so only import it - # when mypy is running and use forward references. - from typing import Coroutine # noqa: F401 - - _ExcInfoTuple = Tuple[ - Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType] - ] - - -_NON_OWNED_IOLOOPS = AsyncIOMainLoop - - -def bind_unused_port(reuse_port: bool = False) -> Tuple[socket.socket, int]: - """Binds a server socket to an available port on localhost. - - Returns a tuple (socket, port). - - .. versionchanged:: 4.4 - Always binds to ``127.0.0.1`` without resolving the name - ``localhost``. - """ - sock = netutil.bind_sockets( - 0, "127.0.0.1", family=socket.AF_INET, reuse_port=reuse_port - )[0] - port = sock.getsockname()[1] - return sock, port - - -def get_async_test_timeout() -> float: - """Get the global timeout setting for async tests. - - Returns a float, the timeout in seconds. - - .. versionadded:: 3.1 - """ - env = os.environ.get("ASYNC_TEST_TIMEOUT") - if env is not None: - try: - return float(env) - except ValueError: - pass - return 5 - - -class _TestMethodWrapper(object): - """Wraps a test method to raise an error if it returns a value. - - This is mainly used to detect undecorated generators (if a test - method yields it must use a decorator to consume the generator), - but will also detect other kinds of return values (these are not - necessarily errors, but we alert anyway since there is no good - reason to return a value from a test). - """ - - def __init__(self, orig_method: Callable) -> None: - self.orig_method = orig_method - - def __call__(self, *args: Any, **kwargs: Any) -> None: - result = self.orig_method(*args, **kwargs) - if isinstance(result, Generator) or inspect.iscoroutine(result): - raise TypeError( - "Generator and coroutine test methods should be" - " decorated with tornado.testing.gen_test" - ) - elif result is not None: - raise ValueError("Return value from test method ignored: %r" % result) - - def __getattr__(self, name: str) -> Any: - """Proxy all unknown attributes to the original method. - - This is important for some of the decorators in the `unittest` - module, such as `unittest.skipIf`. - """ - return getattr(self.orig_method, name) - - -class AsyncTestCase(unittest.TestCase): - """`~unittest.TestCase` subclass for testing `.IOLoop`-based - asynchronous code. - - The unittest framework is synchronous, so the test must be - complete by the time the test method returns. This means that - asynchronous code cannot be used in quite the same way as usual - and must be adapted to fit. To write your tests with coroutines, - decorate your test methods with `tornado.testing.gen_test` instead - of `tornado.gen.coroutine`. - - This class also provides the (deprecated) `stop()` and `wait()` - methods for a more manual style of testing. The test method itself - must call ``self.wait()``, and asynchronous callbacks should call - ``self.stop()`` to signal completion. - - By default, a new `.IOLoop` is constructed for each test and is available - as ``self.io_loop``. If the code being tested requires a - global `.IOLoop`, subclasses should override `get_new_ioloop` to return it. - - The `.IOLoop`'s ``start`` and ``stop`` methods should not be - called directly. Instead, use `self.stop <stop>` and `self.wait - <wait>`. Arguments passed to ``self.stop`` are returned from - ``self.wait``. It is possible to have multiple ``wait``/``stop`` - cycles in the same test. - - Example:: - - # This test uses coroutine style. - class MyTestCase(AsyncTestCase): - @tornado.testing.gen_test - def test_http_fetch(self): - client = AsyncHTTPClient() - response = yield client.fetch("http://www.tornadoweb.org") - # Test contents of response - self.assertIn("FriendFeed", response.body) - - # This test uses argument passing between self.stop and self.wait. - class MyTestCase2(AsyncTestCase): - def test_http_fetch(self): - client = AsyncHTTPClient() - client.fetch("http://www.tornadoweb.org/", self.stop) - response = self.wait() - # Test contents of response - self.assertIn("FriendFeed", response.body) - """ - - def __init__(self, methodName: str = "runTest") -> None: - super().__init__(methodName) - self.__stopped = False - self.__running = False - self.__failure = None # type: Optional[_ExcInfoTuple] - self.__stop_args = None # type: Any - self.__timeout = None # type: Optional[object] - - # It's easy to forget the @gen_test decorator, but if you do - # the test will silently be ignored because nothing will consume - # the generator. Replace the test method with a wrapper that will - # make sure it's not an undecorated generator. - setattr(self, methodName, _TestMethodWrapper(getattr(self, methodName))) - - # Not used in this class itself, but used by @gen_test - self._test_generator = None # type: Optional[Union[Generator, Coroutine]] - - def setUp(self) -> None: - super().setUp() - self.io_loop = self.get_new_ioloop() - self.io_loop.make_current() - - def tearDown(self) -> None: - # Native coroutines tend to produce warnings if they're not - # allowed to run to completion. It's difficult to ensure that - # this always happens in tests, so cancel any tasks that are - # still pending by the time we get here. - asyncio_loop = self.io_loop.asyncio_loop # type: ignore - if hasattr(asyncio, "all_tasks"): # py37 - tasks = asyncio.all_tasks(asyncio_loop) # type: ignore - else: - tasks = asyncio.Task.all_tasks(asyncio_loop) - # Tasks that are done may still appear here and may contain - # non-cancellation exceptions, so filter them out. - tasks = [t for t in tasks if not t.done()] - for t in tasks: - t.cancel() - # Allow the tasks to run and finalize themselves (which means - # raising a CancelledError inside the coroutine). This may - # just transform the "task was destroyed but it is pending" - # warning into a "uncaught CancelledError" warning, but - # catching CancelledErrors in coroutines that may leak is - # simpler than ensuring that no coroutines leak. - if tasks: - done, pending = self.io_loop.run_sync(lambda: asyncio.wait(tasks)) - assert not pending - # If any task failed with anything but a CancelledError, raise it. - for f in done: - try: - f.result() - except asyncio.CancelledError: - pass - - # Clean up Subprocess, so it can be used again with a new ioloop. - Subprocess.uninitialize() - self.io_loop.clear_current() - if not isinstance(self.io_loop, _NON_OWNED_IOLOOPS): - # Try to clean up any file descriptors left open in the ioloop. - # This avoids leaks, especially when tests are run repeatedly - # in the same process with autoreload (because curl does not - # set FD_CLOEXEC on its file descriptors) - self.io_loop.close(all_fds=True) - super().tearDown() - # In case an exception escaped or the StackContext caught an exception - # when there wasn't a wait() to re-raise it, do so here. - # This is our last chance to raise an exception in a way that the - # unittest machinery understands. - self.__rethrow() - - def get_new_ioloop(self) -> IOLoop: - """Returns the `.IOLoop` to use for this test. - - By default, a new `.IOLoop` is created for each test. - Subclasses may override this method to return - `.IOLoop.current()` if it is not appropriate to use a new - `.IOLoop` in each tests (for example, if there are global - singletons using the default `.IOLoop`) or if a per-test event - loop is being provided by another system (such as - ``pytest-asyncio``). - """ - return IOLoop() - - def _handle_exception( - self, typ: Type[Exception], value: Exception, tb: TracebackType - ) -> bool: - if self.__failure is None: - self.__failure = (typ, value, tb) - else: - app_log.error( - "multiple unhandled exceptions in test", exc_info=(typ, value, tb) - ) - self.stop() - return True - - def __rethrow(self) -> None: - if self.__failure is not None: - failure = self.__failure - self.__failure = None - raise_exc_info(failure) - - def run( - self, result: Optional[unittest.TestResult] = None - ) -> Optional[unittest.TestResult]: - ret = super().run(result) - # As a last resort, if an exception escaped super.run() and wasn't - # re-raised in tearDown, raise it here. This will cause the - # unittest run to fail messily, but that's better than silently - # ignoring an error. - self.__rethrow() - return ret - - def stop(self, _arg: Any = None, **kwargs: Any) -> None: - """Stops the `.IOLoop`, causing one pending (or future) call to `wait()` - to return. - - Keyword arguments or a single positional argument passed to `stop()` are - saved and will be returned by `wait()`. - - .. deprecated:: 5.1 - - `stop` and `wait` are deprecated; use ``@gen_test`` instead. - """ - assert _arg is None or not kwargs - self.__stop_args = kwargs or _arg - if self.__running: - self.io_loop.stop() - self.__running = False - self.__stopped = True - - def wait( - self, - condition: Optional[Callable[..., bool]] = None, - timeout: Optional[float] = None, - ) -> Any: - """Runs the `.IOLoop` until stop is called or timeout has passed. - - In the event of a timeout, an exception will be thrown. The - default timeout is 5 seconds; it may be overridden with a - ``timeout`` keyword argument or globally with the - ``ASYNC_TEST_TIMEOUT`` environment variable. - - If ``condition`` is not ``None``, the `.IOLoop` will be restarted - after `stop()` until ``condition()`` returns ``True``. - - .. versionchanged:: 3.1 - Added the ``ASYNC_TEST_TIMEOUT`` environment variable. - - .. deprecated:: 5.1 - - `stop` and `wait` are deprecated; use ``@gen_test`` instead. - """ - if timeout is None: - timeout = get_async_test_timeout() - - if not self.__stopped: - if timeout: - - def timeout_func() -> None: - try: - raise self.failureException( - "Async operation timed out after %s seconds" % timeout - ) - except Exception: - self.__failure = sys.exc_info() - self.stop() - - self.__timeout = self.io_loop.add_timeout( - self.io_loop.time() + timeout, timeout_func - ) - while True: - self.__running = True - self.io_loop.start() - if self.__failure is not None or condition is None or condition(): - break - if self.__timeout is not None: - self.io_loop.remove_timeout(self.__timeout) - self.__timeout = None - assert self.__stopped - self.__stopped = False - self.__rethrow() - result = self.__stop_args - self.__stop_args = None - return result - - -class AsyncHTTPTestCase(AsyncTestCase): - """A test case that starts up an HTTP server. - - Subclasses must override `get_app()`, which returns the - `tornado.web.Application` (or other `.HTTPServer` callback) to be tested. - Tests will typically use the provided ``self.http_client`` to fetch - URLs from this server. - - Example, assuming the "Hello, world" example from the user guide is in - ``hello.py``:: - - import hello - - class TestHelloApp(AsyncHTTPTestCase): - def get_app(self): - return hello.make_app() - - def test_homepage(self): - response = self.fetch('/') - self.assertEqual(response.code, 200) - self.assertEqual(response.body, 'Hello, world') - - That call to ``self.fetch()`` is equivalent to :: - - self.http_client.fetch(self.get_url('/'), self.stop) - response = self.wait() - - which illustrates how AsyncTestCase can turn an asynchronous operation, - like ``http_client.fetch()``, into a synchronous operation. If you need - to do other asynchronous operations in tests, you'll probably need to use - ``stop()`` and ``wait()`` yourself. - """ - - def setUp(self) -> None: - super().setUp() - sock, port = bind_unused_port() - self.__port = port - - self.http_client = self.get_http_client() - self._app = self.get_app() - self.http_server = self.get_http_server() - self.http_server.add_sockets([sock]) - - def get_http_client(self) -> AsyncHTTPClient: - return AsyncHTTPClient() - - def get_http_server(self) -> HTTPServer: - return HTTPServer(self._app, **self.get_httpserver_options()) - - def get_app(self) -> Application: - """Should be overridden by subclasses to return a - `tornado.web.Application` or other `.HTTPServer` callback. - """ - raise NotImplementedError() - - def fetch( - self, path: str, raise_error: bool = False, **kwargs: Any - ) -> HTTPResponse: - """Convenience method to synchronously fetch a URL. - - The given path will be appended to the local server's host and - port. Any additional keyword arguments will be passed directly to - `.AsyncHTTPClient.fetch` (and so could be used to pass - ``method="POST"``, ``body="..."``, etc). - - If the path begins with http:// or https://, it will be treated as a - full URL and will be fetched as-is. - - If ``raise_error`` is ``True``, a `tornado.httpclient.HTTPError` will - be raised if the response code is not 200. This is the same behavior - as the ``raise_error`` argument to `.AsyncHTTPClient.fetch`, but - the default is ``False`` here (it's ``True`` in `.AsyncHTTPClient`) - because tests often need to deal with non-200 response codes. - - .. versionchanged:: 5.0 - Added support for absolute URLs. - - .. versionchanged:: 5.1 - - Added the ``raise_error`` argument. - - .. deprecated:: 5.1 - - This method currently turns any exception into an - `.HTTPResponse` with status code 599. In Tornado 6.0, - errors other than `tornado.httpclient.HTTPError` will be - passed through, and ``raise_error=False`` will only - suppress errors that would be raised due to non-200 - response codes. - - """ - if path.lower().startswith(("http://", "https://")): - url = path - else: - url = self.get_url(path) - return self.io_loop.run_sync( - lambda: self.http_client.fetch(url, raise_error=raise_error, **kwargs), - timeout=get_async_test_timeout(), - ) - - def get_httpserver_options(self) -> Dict[str, Any]: - """May be overridden by subclasses to return additional - keyword arguments for the server. - """ - return {} - - def get_http_port(self) -> int: - """Returns the port used by the server. - - A new port is chosen for each test. - """ - return self.__port - - def get_protocol(self) -> str: - return "http" - - def get_url(self, path: str) -> str: - """Returns an absolute url for the given path on the test server.""" - return "%s://127.0.0.1:%s%s" % (self.get_protocol(), self.get_http_port(), path) - - def tearDown(self) -> None: - self.http_server.stop() - self.io_loop.run_sync( - self.http_server.close_all_connections, timeout=get_async_test_timeout() - ) - self.http_client.close() - del self.http_server - del self._app - super().tearDown() - - -class AsyncHTTPSTestCase(AsyncHTTPTestCase): - """A test case that starts an HTTPS server. - - Interface is generally the same as `AsyncHTTPTestCase`. - """ - - def get_http_client(self) -> AsyncHTTPClient: - return AsyncHTTPClient(force_instance=True, defaults=dict(validate_cert=False)) - - def get_httpserver_options(self) -> Dict[str, Any]: - return dict(ssl_options=self.get_ssl_options()) - - def get_ssl_options(self) -> Dict[str, Any]: - """May be overridden by subclasses to select SSL options. - - By default includes a self-signed testing certificate. - """ - return AsyncHTTPSTestCase.default_ssl_options() - - @staticmethod - def default_ssl_options() -> Dict[str, Any]: - # Testing keys were generated with: - # openssl req -new -keyout tornado/test/test.key \ - # -out tornado/test/test.crt -nodes -days 3650 -x509 - module_dir = os.path.dirname(__file__) - return dict( - certfile=os.path.join(module_dir, "test", "test.crt"), - keyfile=os.path.join(module_dir, "test", "test.key"), - ) - - def get_protocol(self) -> str: - return "https" - - -@typing.overload -def gen_test( - *, timeout: Optional[float] = None -) -> Callable[[Callable[..., Union[Generator, "Coroutine"]]], Callable[..., None]]: - pass - - -@typing.overload # noqa: F811 -def gen_test(func: Callable[..., Union[Generator, "Coroutine"]]) -> Callable[..., None]: - pass - - -def gen_test( # noqa: F811 - func: Optional[Callable[..., Union[Generator, "Coroutine"]]] = None, - timeout: Optional[float] = None, -) -> Union[ - Callable[..., None], - Callable[[Callable[..., Union[Generator, "Coroutine"]]], Callable[..., None]], -]: - """Testing equivalent of ``@gen.coroutine``, to be applied to test methods. - - ``@gen.coroutine`` cannot be used on tests because the `.IOLoop` is not - already running. ``@gen_test`` should be applied to test methods - on subclasses of `AsyncTestCase`. - - Example:: - - class MyTest(AsyncHTTPTestCase): - @gen_test - def test_something(self): - response = yield self.http_client.fetch(self.get_url('/')) - - By default, ``@gen_test`` times out after 5 seconds. The timeout may be - overridden globally with the ``ASYNC_TEST_TIMEOUT`` environment variable, - or for each test with the ``timeout`` keyword argument:: - - class MyTest(AsyncHTTPTestCase): - @gen_test(timeout=10) - def test_something_slow(self): - response = yield self.http_client.fetch(self.get_url('/')) - - Note that ``@gen_test`` is incompatible with `AsyncTestCase.stop`, - `AsyncTestCase.wait`, and `AsyncHTTPTestCase.fetch`. Use ``yield - self.http_client.fetch(self.get_url())`` as shown above instead. - - .. versionadded:: 3.1 - The ``timeout`` argument and ``ASYNC_TEST_TIMEOUT`` environment - variable. - - .. versionchanged:: 4.0 - The wrapper now passes along ``*args, **kwargs`` so it can be used - on functions with arguments. - - """ - if timeout is None: - timeout = get_async_test_timeout() - - def wrap(f: Callable[..., Union[Generator, "Coroutine"]]) -> Callable[..., None]: - # Stack up several decorators to allow us to access the generator - # object itself. In the innermost wrapper, we capture the generator - # and save it in an attribute of self. Next, we run the wrapped - # function through @gen.coroutine. Finally, the coroutine is - # wrapped again to make it synchronous with run_sync. - # - # This is a good case study arguing for either some sort of - # extensibility in the gen decorators or cancellation support. - @functools.wraps(f) - def pre_coroutine(self, *args, **kwargs): - # type: (AsyncTestCase, *Any, **Any) -> Union[Generator, Coroutine] - # Type comments used to avoid pypy3 bug. - result = f(self, *args, **kwargs) - if isinstance(result, Generator) or inspect.iscoroutine(result): - self._test_generator = result - else: - self._test_generator = None - return result - - if inspect.iscoroutinefunction(f): - coro = pre_coroutine - else: - coro = gen.coroutine(pre_coroutine) - - @functools.wraps(coro) - def post_coroutine(self, *args, **kwargs): - # type: (AsyncTestCase, *Any, **Any) -> None - try: - return self.io_loop.run_sync( - functools.partial(coro, self, *args, **kwargs), timeout=timeout - ) - except TimeoutError as e: - # run_sync raises an error with an unhelpful traceback. - # If the underlying generator is still running, we can throw the - # exception back into it so the stack trace is replaced by the - # point where the test is stopped. The only reason the generator - # would not be running would be if it were cancelled, which means - # a native coroutine, so we can rely on the cr_running attribute. - if self._test_generator is not None and getattr( - self._test_generator, "cr_running", True - ): - self._test_generator.throw(type(e), e) - # In case the test contains an overly broad except - # clause, we may get back here. - # Coroutine was stopped or didn't raise a useful stack trace, - # so re-raise the original exception which is better than nothing. - raise - - return post_coroutine - - if func is not None: - # Used like: - # @gen_test - # def f(self): - # pass - return wrap(func) - else: - # Used like @gen_test(timeout=10) - return wrap - - -# Without this attribute, nosetests will try to run gen_test as a test -# anywhere it is imported. -gen_test.__test__ = False # type: ignore - - -class ExpectLog(logging.Filter): - """Context manager to capture and suppress expected log output. - - Useful to make tests of error conditions less noisy, while still - leaving unexpected log entries visible. *Not thread safe.* - - The attribute ``logged_stack`` is set to ``True`` if any exception - stack trace was logged. - - Usage:: - - with ExpectLog('tornado.application', "Uncaught exception"): - error_response = self.fetch("/some_page") - - .. versionchanged:: 4.3 - Added the ``logged_stack`` attribute. - """ - - def __init__( - self, - logger: Union[logging.Logger, basestring_type], - regex: str, - required: bool = True, - level: Optional[int] = None, - ) -> None: - """Constructs an ExpectLog context manager. - - :param logger: Logger object (or name of logger) to watch. Pass - an empty string to watch the root logger. - :param regex: Regular expression to match. Any log entries on - the specified logger that match this regex will be suppressed. - :param required: If true, an exception will be raised if the end of - the ``with`` statement is reached without matching any log entries. - :param level: A constant from the ``logging`` module indicating the - expected log level. If this parameter is provided, only log messages - at this level will be considered to match. Additionally, the - supplied ``logger`` will have its level adjusted if necessary - (for the duration of the ``ExpectLog`` to enable the expected - message. - - .. versionchanged:: 6.1 - Added the ``level`` parameter. - """ - if isinstance(logger, basestring_type): - logger = logging.getLogger(logger) - self.logger = logger - self.regex = re.compile(regex) - self.required = required - self.matched = False - self.logged_stack = False - self.level = level - self.orig_level = None # type: Optional[int] - - def filter(self, record: logging.LogRecord) -> bool: - if record.exc_info: - self.logged_stack = True - message = record.getMessage() - if self.regex.match(message): - if self.level is not None and record.levelno != self.level: - app_log.warning( - "Got expected log message %r at unexpected level (%s vs %s)" - % (message, logging.getLevelName(self.level), record.levelname) - ) - return True - self.matched = True - return False - return True - - def __enter__(self) -> "ExpectLog": - if self.level is not None and self.level < self.logger.getEffectiveLevel(): - self.orig_level = self.logger.level - self.logger.setLevel(self.level) - self.logger.addFilter(self) - return self - - def __exit__( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[TracebackType], - ) -> None: - if self.orig_level is not None: - self.logger.setLevel(self.orig_level) - self.logger.removeFilter(self) - if not typ and self.required and not self.matched: - raise Exception("did not get expected log message") - - -def main(**kwargs: Any) -> None: - """A simple test runner. - - This test runner is essentially equivalent to `unittest.main` from - the standard library, but adds support for Tornado-style option - parsing and log formatting. It is *not* necessary to use this - `main` function to run tests using `AsyncTestCase`; these tests - are self-contained and can run with any test runner. - - The easiest way to run a test is via the command line:: - - python -m tornado.testing tornado.test.web_test - - See the standard library ``unittest`` module for ways in which - tests can be specified. - - Projects with many tests may wish to define a test script like - ``tornado/test/runtests.py``. This script should define a method - ``all()`` which returns a test suite and then call - `tornado.testing.main()`. Note that even when a test script is - used, the ``all()`` test suite may be overridden by naming a - single test on the command line:: - - # Runs all tests - python -m tornado.test.runtests - # Runs one test - python -m tornado.test.runtests tornado.test.web_test - - Additional keyword arguments passed through to ``unittest.main()``. - For example, use ``tornado.testing.main(verbosity=2)`` - to show many test details as they are run. - See http://docs.python.org/library/unittest.html#unittest.main - for full argument list. - - .. versionchanged:: 5.0 - - This function produces no output of its own; only that produced - by the `unittest` module (previously it would add a PASS or FAIL - log message). - """ - from tornado.options import define, options, parse_command_line - - define( - "exception_on_interrupt", - type=bool, - default=True, - help=( - "If true (default), ctrl-c raises a KeyboardInterrupt " - "exception. This prints a stack trace but cannot interrupt " - "certain operations. If false, the process is more reliably " - "killed, but does not print a stack trace." - ), - ) - - # support the same options as unittest's command-line interface - define("verbose", type=bool) - define("quiet", type=bool) - define("failfast", type=bool) - define("catch", type=bool) - define("buffer", type=bool) - - argv = [sys.argv[0]] + parse_command_line(sys.argv) - - if not options.exception_on_interrupt: - signal.signal(signal.SIGINT, signal.SIG_DFL) - - if options.verbose is not None: - kwargs["verbosity"] = 2 - if options.quiet is not None: - kwargs["verbosity"] = 0 - if options.failfast is not None: - kwargs["failfast"] = True - if options.catch is not None: - kwargs["catchbreak"] = True - if options.buffer is not None: - kwargs["buffer"] = True - - if __name__ == "__main__" and len(argv) == 1: - print("No tests specified", file=sys.stderr) - sys.exit(1) - # In order to be able to run tests by their fully-qualified name - # on the command line without importing all tests here, - # module must be set to None. Python 3.2's unittest.main ignores - # defaultTest if no module is given (it tries to do its own - # test discovery, which is incompatible with auto2to3), so don't - # set module if we're not asking for a specific test. - if len(argv) > 1: - unittest.main(module=None, argv=argv, **kwargs) # type: ignore - else: - unittest.main(defaultTest="all", argv=argv, **kwargs) - - -if __name__ == "__main__": - main() diff --git a/telegramer/include/tornado/util.py b/telegramer/include/tornado/util.py deleted file mode 100644 index 77c5f94..0000000 --- a/telegramer/include/tornado/util.py +++ /dev/null @@ -1,474 +0,0 @@ -"""Miscellaneous utility functions and classes. - -This module is used internally by Tornado. It is not necessarily expected -that the functions and classes defined here will be useful to other -applications, but they are documented here in case they are. - -The one public-facing part of this module is the `Configurable` class -and its `~Configurable.configure` method, which becomes a part of the -interface of its subclasses, including `.AsyncHTTPClient`, `.IOLoop`, -and `.Resolver`. -""" - -import array -import atexit -from inspect import getfullargspec -import os -import re -import typing -import zlib - -from typing import ( - Any, - Optional, - Dict, - Mapping, - List, - Tuple, - Match, - Callable, - Type, - Sequence, -) - -if typing.TYPE_CHECKING: - # Additional imports only used in type comments. - # This lets us make these imports lazy. - import datetime # noqa: F401 - from types import TracebackType # noqa: F401 - from typing import Union # noqa: F401 - import unittest # noqa: F401 - -# Aliases for types that are spelled differently in different Python -# versions. bytes_type is deprecated and no longer used in Tornado -# itself but is left in case anyone outside Tornado is using it. -bytes_type = bytes -unicode_type = str -basestring_type = str - -try: - from sys import is_finalizing -except ImportError: - # Emulate it - def _get_emulated_is_finalizing() -> Callable[[], bool]: - L = [] # type: List[None] - atexit.register(lambda: L.append(None)) - - def is_finalizing() -> bool: - # Not referencing any globals here - return L != [] - - return is_finalizing - - is_finalizing = _get_emulated_is_finalizing() - - -class TimeoutError(Exception): - """Exception raised by `.with_timeout` and `.IOLoop.run_sync`. - - .. versionchanged:: 5.0: - Unified ``tornado.gen.TimeoutError`` and - ``tornado.ioloop.TimeoutError`` as ``tornado.util.TimeoutError``. - Both former names remain as aliases. - """ - - -class ObjectDict(Dict[str, Any]): - """Makes a dictionary behave like an object, with attribute-style access. - """ - - def __getattr__(self, name: str) -> Any: - try: - return self[name] - except KeyError: - raise AttributeError(name) - - def __setattr__(self, name: str, value: Any) -> None: - self[name] = value - - -class GzipDecompressor(object): - """Streaming gzip decompressor. - - The interface is like that of `zlib.decompressobj` (without some of the - optional arguments, but it understands gzip headers and checksums. - """ - - def __init__(self) -> None: - # Magic parameter makes zlib module understand gzip header - # http://stackoverflow.com/questions/1838699/how-can-i-decompress-a-gzip-stream-with-zlib - # This works on cpython and pypy, but not jython. - self.decompressobj = zlib.decompressobj(16 + zlib.MAX_WBITS) - - def decompress(self, value: bytes, max_length: int = 0) -> bytes: - """Decompress a chunk, returning newly-available data. - - Some data may be buffered for later processing; `flush` must - be called when there is no more input data to ensure that - all data was processed. - - If ``max_length`` is given, some input data may be left over - in ``unconsumed_tail``; you must retrieve this value and pass - it back to a future call to `decompress` if it is not empty. - """ - return self.decompressobj.decompress(value, max_length) - - @property - def unconsumed_tail(self) -> bytes: - """Returns the unconsumed portion left over - """ - return self.decompressobj.unconsumed_tail - - def flush(self) -> bytes: - """Return any remaining buffered data not yet returned by decompress. - - Also checks for errors such as truncated input. - No other methods may be called on this object after `flush`. - """ - return self.decompressobj.flush() - - -def import_object(name: str) -> Any: - """Imports an object by name. - - ``import_object('x')`` is equivalent to ``import x``. - ``import_object('x.y.z')`` is equivalent to ``from x.y import z``. - - >>> import tornado.escape - >>> import_object('tornado.escape') is tornado.escape - True - >>> import_object('tornado.escape.utf8') is tornado.escape.utf8 - True - >>> import_object('tornado') is tornado - True - >>> import_object('tornado.missing_module') - Traceback (most recent call last): - ... - ImportError: No module named missing_module - """ - if name.count(".") == 0: - return __import__(name) - - parts = name.split(".") - obj = __import__(".".join(parts[:-1]), fromlist=[parts[-1]]) - try: - return getattr(obj, parts[-1]) - except AttributeError: - raise ImportError("No module named %s" % parts[-1]) - - -def exec_in( - code: Any, glob: Dict[str, Any], loc: Optional[Optional[Mapping[str, Any]]] = None -) -> None: - if isinstance(code, str): - # exec(string) inherits the caller's future imports; compile - # the string first to prevent that. - code = compile(code, "<string>", "exec", dont_inherit=True) - exec(code, glob, loc) - - -def raise_exc_info( - exc_info, # type: Tuple[Optional[type], Optional[BaseException], Optional[TracebackType]] -): - # type: (...) -> typing.NoReturn - # - # This function's type annotation must use comments instead of - # real annotations because typing.NoReturn does not exist in - # python 3.5's typing module. The formatting is funky because this - # is apparently what flake8 wants. - try: - if exc_info[1] is not None: - raise exc_info[1].with_traceback(exc_info[2]) - else: - raise TypeError("raise_exc_info called with no exception") - finally: - # Clear the traceback reference from our stack frame to - # minimize circular references that slow down GC. - exc_info = (None, None, None) - - -def errno_from_exception(e: BaseException) -> Optional[int]: - """Provides the errno from an Exception object. - - There are cases that the errno attribute was not set so we pull - the errno out of the args but if someone instantiates an Exception - without any args you will get a tuple error. So this function - abstracts all that behavior to give you a safe way to get the - errno. - """ - - if hasattr(e, "errno"): - return e.errno # type: ignore - elif e.args: - return e.args[0] - else: - return None - - -_alphanum = frozenset("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - - -def _re_unescape_replacement(match: Match[str]) -> str: - group = match.group(1) - if group[0] in _alphanum: - raise ValueError("cannot unescape '\\\\%s'" % group[0]) - return group - - -_re_unescape_pattern = re.compile(r"\\(.)", re.DOTALL) - - -def re_unescape(s: str) -> str: - r"""Unescape a string escaped by `re.escape`. - - May raise ``ValueError`` for regular expressions which could not - have been produced by `re.escape` (for example, strings containing - ``\d`` cannot be unescaped). - - .. versionadded:: 4.4 - """ - return _re_unescape_pattern.sub(_re_unescape_replacement, s) - - -class Configurable(object): - """Base class for configurable interfaces. - - A configurable interface is an (abstract) class whose constructor - acts as a factory function for one of its implementation subclasses. - The implementation subclass as well as optional keyword arguments to - its initializer can be set globally at runtime with `configure`. - - By using the constructor as the factory method, the interface - looks like a normal class, `isinstance` works as usual, etc. This - pattern is most useful when the choice of implementation is likely - to be a global decision (e.g. when `~select.epoll` is available, - always use it instead of `~select.select`), or when a - previously-monolithic class has been split into specialized - subclasses. - - Configurable subclasses must define the class methods - `configurable_base` and `configurable_default`, and use the instance - method `initialize` instead of ``__init__``. - - .. versionchanged:: 5.0 - - It is now possible for configuration to be specified at - multiple levels of a class hierarchy. - - """ - - # Type annotations on this class are mostly done with comments - # because they need to refer to Configurable, which isn't defined - # until after the class definition block. These can use regular - # annotations when our minimum python version is 3.7. - # - # There may be a clever way to use generics here to get more - # precise types (i.e. for a particular Configurable subclass T, - # all the types are subclasses of T, not just Configurable). - __impl_class = None # type: Optional[Type[Configurable]] - __impl_kwargs = None # type: Dict[str, Any] - - def __new__(cls, *args: Any, **kwargs: Any) -> Any: - base = cls.configurable_base() - init_kwargs = {} # type: Dict[str, Any] - if cls is base: - impl = cls.configured_class() - if base.__impl_kwargs: - init_kwargs.update(base.__impl_kwargs) - else: - impl = cls - init_kwargs.update(kwargs) - if impl.configurable_base() is not base: - # The impl class is itself configurable, so recurse. - return impl(*args, **init_kwargs) - instance = super(Configurable, cls).__new__(impl) - # initialize vs __init__ chosen for compatibility with AsyncHTTPClient - # singleton magic. If we get rid of that we can switch to __init__ - # here too. - instance.initialize(*args, **init_kwargs) - return instance - - @classmethod - def configurable_base(cls): - # type: () -> Type[Configurable] - """Returns the base class of a configurable hierarchy. - - This will normally return the class in which it is defined. - (which is *not* necessarily the same as the ``cls`` classmethod - parameter). - - """ - raise NotImplementedError() - - @classmethod - def configurable_default(cls): - # type: () -> Type[Configurable] - """Returns the implementation class to be used if none is configured.""" - raise NotImplementedError() - - def _initialize(self) -> None: - pass - - initialize = _initialize # type: Callable[..., None] - """Initialize a `Configurable` subclass instance. - - Configurable classes should use `initialize` instead of ``__init__``. - - .. versionchanged:: 4.2 - Now accepts positional arguments in addition to keyword arguments. - """ - - @classmethod - def configure(cls, impl, **kwargs): - # type: (Union[None, str, Type[Configurable]], Any) -> None - """Sets the class to use when the base class is instantiated. - - Keyword arguments will be saved and added to the arguments passed - to the constructor. This can be used to set global defaults for - some parameters. - """ - base = cls.configurable_base() - if isinstance(impl, str): - impl = typing.cast(Type[Configurable], import_object(impl)) - if impl is not None and not issubclass(impl, cls): - raise ValueError("Invalid subclass of %s" % cls) - base.__impl_class = impl - base.__impl_kwargs = kwargs - - @classmethod - def configured_class(cls): - # type: () -> Type[Configurable] - """Returns the currently configured class.""" - base = cls.configurable_base() - # Manually mangle the private name to see whether this base - # has been configured (and not another base higher in the - # hierarchy). - if base.__dict__.get("_Configurable__impl_class") is None: - base.__impl_class = cls.configurable_default() - if base.__impl_class is not None: - return base.__impl_class - else: - # Should be impossible, but mypy wants an explicit check. - raise ValueError("configured class not found") - - @classmethod - def _save_configuration(cls): - # type: () -> Tuple[Optional[Type[Configurable]], Dict[str, Any]] - base = cls.configurable_base() - return (base.__impl_class, base.__impl_kwargs) - - @classmethod - def _restore_configuration(cls, saved): - # type: (Tuple[Optional[Type[Configurable]], Dict[str, Any]]) -> None - base = cls.configurable_base() - base.__impl_class = saved[0] - base.__impl_kwargs = saved[1] - - -class ArgReplacer(object): - """Replaces one value in an ``args, kwargs`` pair. - - Inspects the function signature to find an argument by name - whether it is passed by position or keyword. For use in decorators - and similar wrappers. - """ - - def __init__(self, func: Callable, name: str) -> None: - self.name = name - try: - self.arg_pos = self._getargnames(func).index(name) # type: Optional[int] - except ValueError: - # Not a positional parameter - self.arg_pos = None - - def _getargnames(self, func: Callable) -> List[str]: - try: - return getfullargspec(func).args - except TypeError: - if hasattr(func, "func_code"): - # Cython-generated code has all the attributes needed - # by inspect.getfullargspec, but the inspect module only - # works with ordinary functions. Inline the portion of - # getfullargspec that we need here. Note that for static - # functions the @cython.binding(True) decorator must - # be used (for methods it works out of the box). - code = func.func_code # type: ignore - return code.co_varnames[: code.co_argcount] - raise - - def get_old_value( - self, args: Sequence[Any], kwargs: Dict[str, Any], default: Any = None - ) -> Any: - """Returns the old value of the named argument without replacing it. - - Returns ``default`` if the argument is not present. - """ - if self.arg_pos is not None and len(args) > self.arg_pos: - return args[self.arg_pos] - else: - return kwargs.get(self.name, default) - - def replace( - self, new_value: Any, args: Sequence[Any], kwargs: Dict[str, Any] - ) -> Tuple[Any, Sequence[Any], Dict[str, Any]]: - """Replace the named argument in ``args, kwargs`` with ``new_value``. - - Returns ``(old_value, args, kwargs)``. The returned ``args`` and - ``kwargs`` objects may not be the same as the input objects, or - the input objects may be mutated. - - If the named argument was not found, ``new_value`` will be added - to ``kwargs`` and None will be returned as ``old_value``. - """ - if self.arg_pos is not None and len(args) > self.arg_pos: - # The arg to replace is passed positionally - old_value = args[self.arg_pos] - args = list(args) # *args is normally a tuple - args[self.arg_pos] = new_value - else: - # The arg to replace is either omitted or passed by keyword. - old_value = kwargs.get(self.name) - kwargs[self.name] = new_value - return old_value, args, kwargs - - -def timedelta_to_seconds(td): - # type: (datetime.timedelta) -> float - """Equivalent to ``td.total_seconds()`` (introduced in Python 2.7).""" - return td.total_seconds() - - -def _websocket_mask_python(mask: bytes, data: bytes) -> bytes: - """Websocket masking function. - - `mask` is a `bytes` object of length 4; `data` is a `bytes` object of any length. - Returns a `bytes` object of the same length as `data` with the mask applied - as specified in section 5.3 of RFC 6455. - - This pure-python implementation may be replaced by an optimized version when available. - """ - mask_arr = array.array("B", mask) - unmasked_arr = array.array("B", data) - for i in range(len(data)): - unmasked_arr[i] = unmasked_arr[i] ^ mask_arr[i % 4] - return unmasked_arr.tobytes() - - -if os.environ.get("TORNADO_NO_EXTENSION") or os.environ.get("TORNADO_EXTENSION") == "0": - # These environment variables exist to make it easier to do performance - # comparisons; they are not guaranteed to remain supported in the future. - _websocket_mask = _websocket_mask_python -else: - try: - from tornado.speedups import websocket_mask as _websocket_mask - except ImportError: - if os.environ.get("TORNADO_EXTENSION") == "1": - raise - _websocket_mask = _websocket_mask_python - - -def doctests(): - # type: () -> unittest.TestSuite - import doctest - - return doctest.DocTestSuite() diff --git a/telegramer/include/tornado/web.py b/telegramer/include/tornado/web.py deleted file mode 100644 index 546e6ec..0000000 --- a/telegramer/include/tornado/web.py +++ /dev/null @@ -1,3588 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""``tornado.web`` provides a simple web framework with asynchronous -features that allow it to scale to large numbers of open connections, -making it ideal for `long polling -<http://en.wikipedia.org/wiki/Push_technology#Long_polling>`_. - -Here is a simple "Hello, world" example app: - -.. testcode:: - - import tornado.ioloop - import tornado.web - - class MainHandler(tornado.web.RequestHandler): - def get(self): - self.write("Hello, world") - - if __name__ == "__main__": - application = tornado.web.Application([ - (r"/", MainHandler), - ]) - application.listen(8888) - tornado.ioloop.IOLoop.current().start() - -.. testoutput:: - :hide: - - -See the :doc:`guide` for additional information. - -Thread-safety notes -------------------- - -In general, methods on `RequestHandler` and elsewhere in Tornado are -not thread-safe. In particular, methods such as -`~RequestHandler.write()`, `~RequestHandler.finish()`, and -`~RequestHandler.flush()` must only be called from the main thread. If -you use multiple threads it is important to use `.IOLoop.add_callback` -to transfer control back to the main thread before finishing the -request, or to limit your use of other threads to -`.IOLoop.run_in_executor` and ensure that your callbacks running in -the executor do not refer to Tornado objects. - -""" - -import base64 -import binascii -import datetime -import email.utils -import functools -import gzip -import hashlib -import hmac -import http.cookies -from inspect import isclass -from io import BytesIO -import mimetypes -import numbers -import os.path -import re -import sys -import threading -import time -import tornado -import traceback -import types -import urllib.parse -from urllib.parse import urlencode - -from tornado.concurrent import Future, future_set_result_unless_cancelled -from tornado import escape -from tornado import gen -from tornado.httpserver import HTTPServer -from tornado import httputil -from tornado import iostream -import tornado.locale -from tornado import locale -from tornado.log import access_log, app_log, gen_log -from tornado import template -from tornado.escape import utf8, _unicode -from tornado.routing import ( - AnyMatches, - DefaultHostMatches, - HostMatches, - ReversibleRouter, - Rule, - ReversibleRuleRouter, - URLSpec, - _RuleList, -) -from tornado.util import ObjectDict, unicode_type, _websocket_mask - -url = URLSpec - -from typing import ( - Dict, - Any, - Union, - Optional, - Awaitable, - Tuple, - List, - Callable, - Iterable, - Generator, - Type, - cast, - overload, -) -from types import TracebackType -import typing - -if typing.TYPE_CHECKING: - from typing import Set # noqa: F401 - - -# The following types are accepted by RequestHandler.set_header -# and related methods. -_HeaderTypes = Union[bytes, unicode_type, int, numbers.Integral, datetime.datetime] - -_CookieSecretTypes = Union[str, bytes, Dict[int, str], Dict[int, bytes]] - - -MIN_SUPPORTED_SIGNED_VALUE_VERSION = 1 -"""The oldest signed value version supported by this version of Tornado. - -Signed values older than this version cannot be decoded. - -.. versionadded:: 3.2.1 -""" - -MAX_SUPPORTED_SIGNED_VALUE_VERSION = 2 -"""The newest signed value version supported by this version of Tornado. - -Signed values newer than this version cannot be decoded. - -.. versionadded:: 3.2.1 -""" - -DEFAULT_SIGNED_VALUE_VERSION = 2 -"""The signed value version produced by `.RequestHandler.create_signed_value`. - -May be overridden by passing a ``version`` keyword argument. - -.. versionadded:: 3.2.1 -""" - -DEFAULT_SIGNED_VALUE_MIN_VERSION = 1 -"""The oldest signed value accepted by `.RequestHandler.get_secure_cookie`. - -May be overridden by passing a ``min_version`` keyword argument. - -.. versionadded:: 3.2.1 -""" - - -class _ArgDefaultMarker: - pass - - -_ARG_DEFAULT = _ArgDefaultMarker() - - -class RequestHandler(object): - """Base class for HTTP request handlers. - - Subclasses must define at least one of the methods defined in the - "Entry points" section below. - - Applications should not construct `RequestHandler` objects - directly and subclasses should not override ``__init__`` (override - `~RequestHandler.initialize` instead). - - """ - - SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT", "OPTIONS") - - _template_loaders = {} # type: Dict[str, template.BaseLoader] - _template_loader_lock = threading.Lock() - _remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]") - - _stream_request_body = False - - # Will be set in _execute. - _transforms = None # type: List[OutputTransform] - path_args = None # type: List[str] - path_kwargs = None # type: Dict[str, str] - - def __init__( - self, - application: "Application", - request: httputil.HTTPServerRequest, - **kwargs: Any - ) -> None: - super().__init__() - - self.application = application - self.request = request - self._headers_written = False - self._finished = False - self._auto_finish = True - self._prepared_future = None - self.ui = ObjectDict( - (n, self._ui_method(m)) for n, m in application.ui_methods.items() - ) - # UIModules are available as both `modules` and `_tt_modules` in the - # template namespace. Historically only `modules` was available - # but could be clobbered by user additions to the namespace. - # The template {% module %} directive looks in `_tt_modules` to avoid - # possible conflicts. - self.ui["_tt_modules"] = _UIModuleNamespace(self, application.ui_modules) - self.ui["modules"] = self.ui["_tt_modules"] - self.clear() - assert self.request.connection is not None - # TODO: need to add set_close_callback to HTTPConnection interface - self.request.connection.set_close_callback( # type: ignore - self.on_connection_close - ) - self.initialize(**kwargs) # type: ignore - - def _initialize(self) -> None: - pass - - initialize = _initialize # type: Callable[..., None] - """Hook for subclass initialization. Called for each request. - - A dictionary passed as the third argument of a ``URLSpec`` will be - supplied as keyword arguments to ``initialize()``. - - Example:: - - class ProfileHandler(RequestHandler): - def initialize(self, database): - self.database = database - - def get(self, username): - ... - - app = Application([ - (r'/user/(.*)', ProfileHandler, dict(database=database)), - ]) - """ - - @property - def settings(self) -> Dict[str, Any]: - """An alias for `self.application.settings <Application.settings>`.""" - return self.application.settings - - def _unimplemented_method(self, *args: str, **kwargs: str) -> None: - raise HTTPError(405) - - head = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - get = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - post = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - delete = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - patch = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - put = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - options = _unimplemented_method # type: Callable[..., Optional[Awaitable[None]]] - - def prepare(self) -> Optional[Awaitable[None]]: - """Called at the beginning of a request before `get`/`post`/etc. - - Override this method to perform common initialization regardless - of the request method. - - Asynchronous support: Use ``async def`` or decorate this method with - `.gen.coroutine` to make it asynchronous. - If this method returns an ``Awaitable`` execution will not proceed - until the ``Awaitable`` is done. - - .. versionadded:: 3.1 - Asynchronous support. - """ - pass - - def on_finish(self) -> None: - """Called after the end of a request. - - Override this method to perform cleanup, logging, etc. - This method is a counterpart to `prepare`. ``on_finish`` may - not produce any output, as it is called after the response - has been sent to the client. - """ - pass - - def on_connection_close(self) -> None: - """Called in async handlers if the client closed the connection. - - Override this to clean up resources associated with - long-lived connections. Note that this method is called only if - the connection was closed during asynchronous processing; if you - need to do cleanup after every request override `on_finish` - instead. - - Proxies may keep a connection open for a time (perhaps - indefinitely) after the client has gone away, so this method - may not be called promptly after the end user closes their - connection. - """ - if _has_stream_request_body(self.__class__): - if not self.request._body_future.done(): - self.request._body_future.set_exception(iostream.StreamClosedError()) - self.request._body_future.exception() - - def clear(self) -> None: - """Resets all headers and content for this response.""" - self._headers = httputil.HTTPHeaders( - { - "Server": "TornadoServer/%s" % tornado.version, - "Content-Type": "text/html; charset=UTF-8", - "Date": httputil.format_timestamp(time.time()), - } - ) - self.set_default_headers() - self._write_buffer = [] # type: List[bytes] - self._status_code = 200 - self._reason = httputil.responses[200] - - def set_default_headers(self) -> None: - """Override this to set HTTP headers at the beginning of the request. - - For example, this is the place to set a custom ``Server`` header. - Note that setting such headers in the normal flow of request - processing may not do what you want, since headers may be reset - during error handling. - """ - pass - - def set_status(self, status_code: int, reason: Optional[str] = None) -> None: - """Sets the status code for our response. - - :arg int status_code: Response status code. - :arg str reason: Human-readable reason phrase describing the status - code. If ``None``, it will be filled in from - `http.client.responses` or "Unknown". - - .. versionchanged:: 5.0 - - No longer validates that the response code is in - `http.client.responses`. - """ - self._status_code = status_code - if reason is not None: - self._reason = escape.native_str(reason) - else: - self._reason = httputil.responses.get(status_code, "Unknown") - - def get_status(self) -> int: - """Returns the status code for our response.""" - return self._status_code - - def set_header(self, name: str, value: _HeaderTypes) -> None: - """Sets the given response header name and value. - - All header values are converted to strings (`datetime` objects - are formatted according to the HTTP specification for the - ``Date`` header). - - """ - self._headers[name] = self._convert_header_value(value) - - def add_header(self, name: str, value: _HeaderTypes) -> None: - """Adds the given response header and value. - - Unlike `set_header`, `add_header` may be called multiple times - to return multiple values for the same header. - """ - self._headers.add(name, self._convert_header_value(value)) - - def clear_header(self, name: str) -> None: - """Clears an outgoing header, undoing a previous `set_header` call. - - Note that this method does not apply to multi-valued headers - set by `add_header`. - """ - if name in self._headers: - del self._headers[name] - - _INVALID_HEADER_CHAR_RE = re.compile(r"[\x00-\x1f]") - - def _convert_header_value(self, value: _HeaderTypes) -> str: - # Convert the input value to a str. This type check is a bit - # subtle: The bytes case only executes on python 3, and the - # unicode case only executes on python 2, because the other - # cases are covered by the first match for str. - if isinstance(value, str): - retval = value - elif isinstance(value, bytes): # py3 - # Non-ascii characters in headers are not well supported, - # but if you pass bytes, use latin1 so they pass through as-is. - retval = value.decode("latin1") - elif isinstance(value, unicode_type): # py2 - # TODO: This is inconsistent with the use of latin1 above, - # but it's been that way for a long time. Should it change? - retval = escape.utf8(value) - elif isinstance(value, numbers.Integral): - # return immediately since we know the converted value will be safe - return str(value) - elif isinstance(value, datetime.datetime): - return httputil.format_timestamp(value) - else: - raise TypeError("Unsupported header value %r" % value) - # If \n is allowed into the header, it is possible to inject - # additional headers or split the request. - if RequestHandler._INVALID_HEADER_CHAR_RE.search(retval): - raise ValueError("Unsafe header value %r", retval) - return retval - - @overload - def get_argument(self, name: str, default: str, strip: bool = True) -> str: - pass - - @overload - def get_argument( # noqa: F811 - self, name: str, default: _ArgDefaultMarker = _ARG_DEFAULT, strip: bool = True - ) -> str: - pass - - @overload - def get_argument( # noqa: F811 - self, name: str, default: None, strip: bool = True - ) -> Optional[str]: - pass - - def get_argument( # noqa: F811 - self, - name: str, - default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT, - strip: bool = True, - ) -> Optional[str]: - """Returns the value of the argument with the given name. - - If default is not provided, the argument is considered to be - required, and we raise a `MissingArgumentError` if it is missing. - - If the argument appears in the request more than once, we return the - last value. - - This method searches both the query and body arguments. - """ - return self._get_argument(name, default, self.request.arguments, strip) - - def get_arguments(self, name: str, strip: bool = True) -> List[str]: - """Returns a list of the arguments with the given name. - - If the argument is not present, returns an empty list. - - This method searches both the query and body arguments. - """ - - # Make sure `get_arguments` isn't accidentally being called with a - # positional argument that's assumed to be a default (like in - # `get_argument`.) - assert isinstance(strip, bool) - - return self._get_arguments(name, self.request.arguments, strip) - - def get_body_argument( - self, - name: str, - default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT, - strip: bool = True, - ) -> Optional[str]: - """Returns the value of the argument with the given name - from the request body. - - If default is not provided, the argument is considered to be - required, and we raise a `MissingArgumentError` if it is missing. - - If the argument appears in the url more than once, we return the - last value. - - .. versionadded:: 3.2 - """ - return self._get_argument(name, default, self.request.body_arguments, strip) - - def get_body_arguments(self, name: str, strip: bool = True) -> List[str]: - """Returns a list of the body arguments with the given name. - - If the argument is not present, returns an empty list. - - .. versionadded:: 3.2 - """ - return self._get_arguments(name, self.request.body_arguments, strip) - - def get_query_argument( - self, - name: str, - default: Union[None, str, _ArgDefaultMarker] = _ARG_DEFAULT, - strip: bool = True, - ) -> Optional[str]: - """Returns the value of the argument with the given name - from the request query string. - - If default is not provided, the argument is considered to be - required, and we raise a `MissingArgumentError` if it is missing. - - If the argument appears in the url more than once, we return the - last value. - - .. versionadded:: 3.2 - """ - return self._get_argument(name, default, self.request.query_arguments, strip) - - def get_query_arguments(self, name: str, strip: bool = True) -> List[str]: - """Returns a list of the query arguments with the given name. - - If the argument is not present, returns an empty list. - - .. versionadded:: 3.2 - """ - return self._get_arguments(name, self.request.query_arguments, strip) - - def _get_argument( - self, - name: str, - default: Union[None, str, _ArgDefaultMarker], - source: Dict[str, List[bytes]], - strip: bool = True, - ) -> Optional[str]: - args = self._get_arguments(name, source, strip=strip) - if not args: - if isinstance(default, _ArgDefaultMarker): - raise MissingArgumentError(name) - return default - return args[-1] - - def _get_arguments( - self, name: str, source: Dict[str, List[bytes]], strip: bool = True - ) -> List[str]: - values = [] - for v in source.get(name, []): - s = self.decode_argument(v, name=name) - if isinstance(s, unicode_type): - # Get rid of any weird control chars (unless decoding gave - # us bytes, in which case leave it alone) - s = RequestHandler._remove_control_chars_regex.sub(" ", s) - if strip: - s = s.strip() - values.append(s) - return values - - def decode_argument(self, value: bytes, name: Optional[str] = None) -> str: - """Decodes an argument from the request. - - The argument has been percent-decoded and is now a byte string. - By default, this method decodes the argument as utf-8 and returns - a unicode string, but this may be overridden in subclasses. - - This method is used as a filter for both `get_argument()` and for - values extracted from the url and passed to `get()`/`post()`/etc. - - The name of the argument is provided if known, but may be None - (e.g. for unnamed groups in the url regex). - """ - try: - return _unicode(value) - except UnicodeDecodeError: - raise HTTPError( - 400, "Invalid unicode in %s: %r" % (name or "url", value[:40]) - ) - - @property - def cookies(self) -> Dict[str, http.cookies.Morsel]: - """An alias for - `self.request.cookies <.httputil.HTTPServerRequest.cookies>`.""" - return self.request.cookies - - def get_cookie(self, name: str, default: Optional[str] = None) -> Optional[str]: - """Returns the value of the request cookie with the given name. - - If the named cookie is not present, returns ``default``. - - This method only returns cookies that were present in the request. - It does not see the outgoing cookies set by `set_cookie` in this - handler. - """ - if self.request.cookies is not None and name in self.request.cookies: - return self.request.cookies[name].value - return default - - def set_cookie( - self, - name: str, - value: Union[str, bytes], - domain: Optional[str] = None, - expires: Optional[Union[float, Tuple, datetime.datetime]] = None, - path: str = "/", - expires_days: Optional[float] = None, - **kwargs: Any - ) -> None: - """Sets an outgoing cookie name/value with the given options. - - Newly-set cookies are not immediately visible via `get_cookie`; - they are not present until the next request. - - expires may be a numeric timestamp as returned by `time.time`, - a time tuple as returned by `time.gmtime`, or a - `datetime.datetime` object. - - Additional keyword arguments are set on the cookies.Morsel - directly. - See https://docs.python.org/3/library/http.cookies.html#http.cookies.Morsel - for available attributes. - """ - # The cookie library only accepts type str, in both python 2 and 3 - name = escape.native_str(name) - value = escape.native_str(value) - if re.search(r"[\x00-\x20]", name + value): - # Don't let us accidentally inject bad stuff - raise ValueError("Invalid cookie %r: %r" % (name, value)) - if not hasattr(self, "_new_cookie"): - self._new_cookie = ( - http.cookies.SimpleCookie() - ) # type: http.cookies.SimpleCookie - if name in self._new_cookie: - del self._new_cookie[name] - self._new_cookie[name] = value - morsel = self._new_cookie[name] - if domain: - morsel["domain"] = domain - if expires_days is not None and not expires: - expires = datetime.datetime.utcnow() + datetime.timedelta(days=expires_days) - if expires: - morsel["expires"] = httputil.format_timestamp(expires) - if path: - morsel["path"] = path - for k, v in kwargs.items(): - if k == "max_age": - k = "max-age" - - # skip falsy values for httponly and secure flags because - # SimpleCookie sets them regardless - if k in ["httponly", "secure"] and not v: - continue - - morsel[k] = v - - def clear_cookie( - self, name: str, path: str = "/", domain: Optional[str] = None - ) -> None: - """Deletes the cookie with the given name. - - Due to limitations of the cookie protocol, you must pass the same - path and domain to clear a cookie as were used when that cookie - was set (but there is no way to find out on the server side - which values were used for a given cookie). - - Similar to `set_cookie`, the effect of this method will not be - seen until the following request. - """ - expires = datetime.datetime.utcnow() - datetime.timedelta(days=365) - self.set_cookie(name, value="", path=path, expires=expires, domain=domain) - - def clear_all_cookies(self, path: str = "/", domain: Optional[str] = None) -> None: - """Deletes all the cookies the user sent with this request. - - See `clear_cookie` for more information on the path and domain - parameters. - - Similar to `set_cookie`, the effect of this method will not be - seen until the following request. - - .. versionchanged:: 3.2 - - Added the ``path`` and ``domain`` parameters. - """ - for name in self.request.cookies: - self.clear_cookie(name, path=path, domain=domain) - - def set_secure_cookie( - self, - name: str, - value: Union[str, bytes], - expires_days: Optional[float] = 30, - version: Optional[int] = None, - **kwargs: Any - ) -> None: - """Signs and timestamps a cookie so it cannot be forged. - - You must specify the ``cookie_secret`` setting in your Application - to use this method. It should be a long, random sequence of bytes - to be used as the HMAC secret for the signature. - - To read a cookie set with this method, use `get_secure_cookie()`. - - Note that the ``expires_days`` parameter sets the lifetime of the - cookie in the browser, but is independent of the ``max_age_days`` - parameter to `get_secure_cookie`. - A value of None limits the lifetime to the current browser session. - - Secure cookies may contain arbitrary byte values, not just unicode - strings (unlike regular cookies) - - Similar to `set_cookie`, the effect of this method will not be - seen until the following request. - - .. versionchanged:: 3.2.1 - - Added the ``version`` argument. Introduced cookie version 2 - and made it the default. - """ - self.set_cookie( - name, - self.create_signed_value(name, value, version=version), - expires_days=expires_days, - **kwargs - ) - - def create_signed_value( - self, name: str, value: Union[str, bytes], version: Optional[int] = None - ) -> bytes: - """Signs and timestamps a string so it cannot be forged. - - Normally used via set_secure_cookie, but provided as a separate - method for non-cookie uses. To decode a value not stored - as a cookie use the optional value argument to get_secure_cookie. - - .. versionchanged:: 3.2.1 - - Added the ``version`` argument. Introduced cookie version 2 - and made it the default. - """ - self.require_setting("cookie_secret", "secure cookies") - secret = self.application.settings["cookie_secret"] - key_version = None - if isinstance(secret, dict): - if self.application.settings.get("key_version") is None: - raise Exception("key_version setting must be used for secret_key dicts") - key_version = self.application.settings["key_version"] - - return create_signed_value( - secret, name, value, version=version, key_version=key_version - ) - - def get_secure_cookie( - self, - name: str, - value: Optional[str] = None, - max_age_days: float = 31, - min_version: Optional[int] = None, - ) -> Optional[bytes]: - """Returns the given signed cookie if it validates, or None. - - The decoded cookie value is returned as a byte string (unlike - `get_cookie`). - - Similar to `get_cookie`, this method only returns cookies that - were present in the request. It does not see outgoing cookies set by - `set_secure_cookie` in this handler. - - .. versionchanged:: 3.2.1 - - Added the ``min_version`` argument. Introduced cookie version 2; - both versions 1 and 2 are accepted by default. - """ - self.require_setting("cookie_secret", "secure cookies") - if value is None: - value = self.get_cookie(name) - return decode_signed_value( - self.application.settings["cookie_secret"], - name, - value, - max_age_days=max_age_days, - min_version=min_version, - ) - - def get_secure_cookie_key_version( - self, name: str, value: Optional[str] = None - ) -> Optional[int]: - """Returns the signing key version of the secure cookie. - - The version is returned as int. - """ - self.require_setting("cookie_secret", "secure cookies") - if value is None: - value = self.get_cookie(name) - if value is None: - return None - return get_signature_key_version(value) - - def redirect( - self, url: str, permanent: bool = False, status: Optional[int] = None - ) -> None: - """Sends a redirect to the given (optionally relative) URL. - - If the ``status`` argument is specified, that value is used as the - HTTP status code; otherwise either 301 (permanent) or 302 - (temporary) is chosen based on the ``permanent`` argument. - The default is 302 (temporary). - """ - if self._headers_written: - raise Exception("Cannot redirect after headers have been written") - if status is None: - status = 301 if permanent else 302 - else: - assert isinstance(status, int) and 300 <= status <= 399 - self.set_status(status) - self.set_header("Location", utf8(url)) - self.finish() - - def write(self, chunk: Union[str, bytes, dict]) -> None: - """Writes the given chunk to the output buffer. - - To write the output to the network, use the `flush()` method below. - - If the given chunk is a dictionary, we write it as JSON and set - the Content-Type of the response to be ``application/json``. - (if you want to send JSON as a different ``Content-Type``, call - ``set_header`` *after* calling ``write()``). - - Note that lists are not converted to JSON because of a potential - cross-site security vulnerability. All JSON output should be - wrapped in a dictionary. More details at - http://haacked.com/archive/2009/06/25/json-hijacking.aspx/ and - https://github.com/facebook/tornado/issues/1009 - """ - if self._finished: - raise RuntimeError("Cannot write() after finish()") - if not isinstance(chunk, (bytes, unicode_type, dict)): - message = "write() only accepts bytes, unicode, and dict objects" - if isinstance(chunk, list): - message += ( - ". Lists not accepted for security reasons; see " - + "http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write" # noqa: E501 - ) - raise TypeError(message) - if isinstance(chunk, dict): - chunk = escape.json_encode(chunk) - self.set_header("Content-Type", "application/json; charset=UTF-8") - chunk = utf8(chunk) - self._write_buffer.append(chunk) - - def render(self, template_name: str, **kwargs: Any) -> "Future[None]": - """Renders the template with the given arguments as the response. - - ``render()`` calls ``finish()``, so no other output methods can be called - after it. - - Returns a `.Future` with the same semantics as the one returned by `finish`. - Awaiting this `.Future` is optional. - - .. versionchanged:: 5.1 - - Now returns a `.Future` instead of ``None``. - """ - if self._finished: - raise RuntimeError("Cannot render() after finish()") - html = self.render_string(template_name, **kwargs) - - # Insert the additional JS and CSS added by the modules on the page - js_embed = [] - js_files = [] - css_embed = [] - css_files = [] - html_heads = [] - html_bodies = [] - for module in getattr(self, "_active_modules", {}).values(): - embed_part = module.embedded_javascript() - if embed_part: - js_embed.append(utf8(embed_part)) - file_part = module.javascript_files() - if file_part: - if isinstance(file_part, (unicode_type, bytes)): - js_files.append(_unicode(file_part)) - else: - js_files.extend(file_part) - embed_part = module.embedded_css() - if embed_part: - css_embed.append(utf8(embed_part)) - file_part = module.css_files() - if file_part: - if isinstance(file_part, (unicode_type, bytes)): - css_files.append(_unicode(file_part)) - else: - css_files.extend(file_part) - head_part = module.html_head() - if head_part: - html_heads.append(utf8(head_part)) - body_part = module.html_body() - if body_part: - html_bodies.append(utf8(body_part)) - - if js_files: - # Maintain order of JavaScript files given by modules - js = self.render_linked_js(js_files) - sloc = html.rindex(b"</body>") - html = html[:sloc] + utf8(js) + b"\n" + html[sloc:] - if js_embed: - js_bytes = self.render_embed_js(js_embed) - sloc = html.rindex(b"</body>") - html = html[:sloc] + js_bytes + b"\n" + html[sloc:] - if css_files: - css = self.render_linked_css(css_files) - hloc = html.index(b"</head>") - html = html[:hloc] + utf8(css) + b"\n" + html[hloc:] - if css_embed: - css_bytes = self.render_embed_css(css_embed) - hloc = html.index(b"</head>") - html = html[:hloc] + css_bytes + b"\n" + html[hloc:] - if html_heads: - hloc = html.index(b"</head>") - html = html[:hloc] + b"".join(html_heads) + b"\n" + html[hloc:] - if html_bodies: - hloc = html.index(b"</body>") - html = html[:hloc] + b"".join(html_bodies) + b"\n" + html[hloc:] - return self.finish(html) - - def render_linked_js(self, js_files: Iterable[str]) -> str: - """Default method used to render the final js links for the - rendered webpage. - - Override this method in a sub-classed controller to change the output. - """ - paths = [] - unique_paths = set() # type: Set[str] - - for path in js_files: - if not is_absolute(path): - path = self.static_url(path) - if path not in unique_paths: - paths.append(path) - unique_paths.add(path) - - return "".join( - '<script src="' - + escape.xhtml_escape(p) - + '" type="text/javascript"></script>' - for p in paths - ) - - def render_embed_js(self, js_embed: Iterable[bytes]) -> bytes: - """Default method used to render the final embedded js for the - rendered webpage. - - Override this method in a sub-classed controller to change the output. - """ - return ( - b'<script type="text/javascript">\n//<![CDATA[\n' - + b"\n".join(js_embed) - + b"\n//]]>\n</script>" - ) - - def render_linked_css(self, css_files: Iterable[str]) -> str: - """Default method used to render the final css links for the - rendered webpage. - - Override this method in a sub-classed controller to change the output. - """ - paths = [] - unique_paths = set() # type: Set[str] - - for path in css_files: - if not is_absolute(path): - path = self.static_url(path) - if path not in unique_paths: - paths.append(path) - unique_paths.add(path) - - return "".join( - '<link href="' + escape.xhtml_escape(p) + '" ' - 'type="text/css" rel="stylesheet"/>' - for p in paths - ) - - def render_embed_css(self, css_embed: Iterable[bytes]) -> bytes: - """Default method used to render the final embedded css for the - rendered webpage. - - Override this method in a sub-classed controller to change the output. - """ - return b'<style type="text/css">\n' + b"\n".join(css_embed) + b"\n</style>" - - def render_string(self, template_name: str, **kwargs: Any) -> bytes: - """Generate the given template with the given arguments. - - We return the generated byte string (in utf8). To generate and - write a template as a response, use render() above. - """ - # If no template_path is specified, use the path of the calling file - template_path = self.get_template_path() - if not template_path: - frame = sys._getframe(0) - web_file = frame.f_code.co_filename - while frame.f_code.co_filename == web_file: - frame = frame.f_back - assert frame.f_code.co_filename is not None - template_path = os.path.dirname(frame.f_code.co_filename) - with RequestHandler._template_loader_lock: - if template_path not in RequestHandler._template_loaders: - loader = self.create_template_loader(template_path) - RequestHandler._template_loaders[template_path] = loader - else: - loader = RequestHandler._template_loaders[template_path] - t = loader.load(template_name) - namespace = self.get_template_namespace() - namespace.update(kwargs) - return t.generate(**namespace) - - def get_template_namespace(self) -> Dict[str, Any]: - """Returns a dictionary to be used as the default template namespace. - - May be overridden by subclasses to add or modify values. - - The results of this method will be combined with additional - defaults in the `tornado.template` module and keyword arguments - to `render` or `render_string`. - """ - namespace = dict( - handler=self, - request=self.request, - current_user=self.current_user, - locale=self.locale, - _=self.locale.translate, - pgettext=self.locale.pgettext, - static_url=self.static_url, - xsrf_form_html=self.xsrf_form_html, - reverse_url=self.reverse_url, - ) - namespace.update(self.ui) - return namespace - - def create_template_loader(self, template_path: str) -> template.BaseLoader: - """Returns a new template loader for the given path. - - May be overridden by subclasses. By default returns a - directory-based loader on the given path, using the - ``autoescape`` and ``template_whitespace`` application - settings. If a ``template_loader`` application setting is - supplied, uses that instead. - """ - settings = self.application.settings - if "template_loader" in settings: - return settings["template_loader"] - kwargs = {} - if "autoescape" in settings: - # autoescape=None means "no escaping", so we have to be sure - # to only pass this kwarg if the user asked for it. - kwargs["autoescape"] = settings["autoescape"] - if "template_whitespace" in settings: - kwargs["whitespace"] = settings["template_whitespace"] - return template.Loader(template_path, **kwargs) - - def flush(self, include_footers: bool = False) -> "Future[None]": - """Flushes the current output buffer to the network. - - .. versionchanged:: 4.0 - Now returns a `.Future` if no callback is given. - - .. versionchanged:: 6.0 - - The ``callback`` argument was removed. - """ - assert self.request.connection is not None - chunk = b"".join(self._write_buffer) - self._write_buffer = [] - if not self._headers_written: - self._headers_written = True - for transform in self._transforms: - assert chunk is not None - ( - self._status_code, - self._headers, - chunk, - ) = transform.transform_first_chunk( - self._status_code, self._headers, chunk, include_footers - ) - # Ignore the chunk and only write the headers for HEAD requests - if self.request.method == "HEAD": - chunk = b"" - - # Finalize the cookie headers (which have been stored in a side - # object so an outgoing cookie could be overwritten before it - # is sent). - if hasattr(self, "_new_cookie"): - for cookie in self._new_cookie.values(): - self.add_header("Set-Cookie", cookie.OutputString(None)) - - start_line = httputil.ResponseStartLine("", self._status_code, self._reason) - return self.request.connection.write_headers( - start_line, self._headers, chunk - ) - else: - for transform in self._transforms: - chunk = transform.transform_chunk(chunk, include_footers) - # Ignore the chunk and only write the headers for HEAD requests - if self.request.method != "HEAD": - return self.request.connection.write(chunk) - else: - future = Future() # type: Future[None] - future.set_result(None) - return future - - def finish(self, chunk: Optional[Union[str, bytes, dict]] = None) -> "Future[None]": - """Finishes this response, ending the HTTP request. - - Passing a ``chunk`` to ``finish()`` is equivalent to passing that - chunk to ``write()`` and then calling ``finish()`` with no arguments. - - Returns a `.Future` which may optionally be awaited to track the sending - of the response to the client. This `.Future` resolves when all the response - data has been sent, and raises an error if the connection is closed before all - data can be sent. - - .. versionchanged:: 5.1 - - Now returns a `.Future` instead of ``None``. - """ - if self._finished: - raise RuntimeError("finish() called twice") - - if chunk is not None: - self.write(chunk) - - # Automatically support ETags and add the Content-Length header if - # we have not flushed any content yet. - if not self._headers_written: - if ( - self._status_code == 200 - and self.request.method in ("GET", "HEAD") - and "Etag" not in self._headers - ): - self.set_etag_header() - if self.check_etag_header(): - self._write_buffer = [] - self.set_status(304) - if self._status_code in (204, 304) or (100 <= self._status_code < 200): - assert not self._write_buffer, ( - "Cannot send body with %s" % self._status_code - ) - self._clear_representation_headers() - elif "Content-Length" not in self._headers: - content_length = sum(len(part) for part in self._write_buffer) - self.set_header("Content-Length", content_length) - - assert self.request.connection is not None - # Now that the request is finished, clear the callback we - # set on the HTTPConnection (which would otherwise prevent the - # garbage collection of the RequestHandler when there - # are keepalive connections) - self.request.connection.set_close_callback(None) # type: ignore - - future = self.flush(include_footers=True) - self.request.connection.finish() - self._log() - self._finished = True - self.on_finish() - self._break_cycles() - return future - - def detach(self) -> iostream.IOStream: - """Take control of the underlying stream. - - Returns the underlying `.IOStream` object and stops all - further HTTP processing. Intended for implementing protocols - like websockets that tunnel over an HTTP handshake. - - This method is only supported when HTTP/1.1 is used. - - .. versionadded:: 5.1 - """ - self._finished = True - # TODO: add detach to HTTPConnection? - return self.request.connection.detach() # type: ignore - - def _break_cycles(self) -> None: - # Break up a reference cycle between this handler and the - # _ui_module closures to allow for faster GC on CPython. - self.ui = None # type: ignore - - def send_error(self, status_code: int = 500, **kwargs: Any) -> None: - """Sends the given HTTP error code to the browser. - - If `flush()` has already been called, it is not possible to send - an error, so this method will simply terminate the response. - If output has been written but not yet flushed, it will be discarded - and replaced with the error page. - - Override `write_error()` to customize the error page that is returned. - Additional keyword arguments are passed through to `write_error`. - """ - if self._headers_written: - gen_log.error("Cannot send error response after headers written") - if not self._finished: - # If we get an error between writing headers and finishing, - # we are unlikely to be able to finish due to a - # Content-Length mismatch. Try anyway to release the - # socket. - try: - self.finish() - except Exception: - gen_log.error("Failed to flush partial response", exc_info=True) - return - self.clear() - - reason = kwargs.get("reason") - if "exc_info" in kwargs: - exception = kwargs["exc_info"][1] - if isinstance(exception, HTTPError) and exception.reason: - reason = exception.reason - self.set_status(status_code, reason=reason) - try: - self.write_error(status_code, **kwargs) - except Exception: - app_log.error("Uncaught exception in write_error", exc_info=True) - if not self._finished: - self.finish() - - def write_error(self, status_code: int, **kwargs: Any) -> None: - """Override to implement custom error pages. - - ``write_error`` may call `write`, `render`, `set_header`, etc - to produce output as usual. - - If this error was caused by an uncaught exception (including - HTTPError), an ``exc_info`` triple will be available as - ``kwargs["exc_info"]``. Note that this exception may not be - the "current" exception for purposes of methods like - ``sys.exc_info()`` or ``traceback.format_exc``. - """ - if self.settings.get("serve_traceback") and "exc_info" in kwargs: - # in debug mode, try to send a traceback - self.set_header("Content-Type", "text/plain") - for line in traceback.format_exception(*kwargs["exc_info"]): - self.write(line) - self.finish() - else: - self.finish( - "<html><title>%(code)d: %(message)s</title>" - "<body>%(code)d: %(message)s</body></html>" - % {"code": status_code, "message": self._reason} - ) - - @property - def locale(self) -> tornado.locale.Locale: - """The locale for the current session. - - Determined by either `get_user_locale`, which you can override to - set the locale based on, e.g., a user preference stored in a - database, or `get_browser_locale`, which uses the ``Accept-Language`` - header. - - .. versionchanged: 4.1 - Added a property setter. - """ - if not hasattr(self, "_locale"): - loc = self.get_user_locale() - if loc is not None: - self._locale = loc - else: - self._locale = self.get_browser_locale() - assert self._locale - return self._locale - - @locale.setter - def locale(self, value: tornado.locale.Locale) -> None: - self._locale = value - - def get_user_locale(self) -> Optional[tornado.locale.Locale]: - """Override to determine the locale from the authenticated user. - - If None is returned, we fall back to `get_browser_locale()`. - - This method should return a `tornado.locale.Locale` object, - most likely obtained via a call like ``tornado.locale.get("en")`` - """ - return None - - def get_browser_locale(self, default: str = "en_US") -> tornado.locale.Locale: - """Determines the user's locale from ``Accept-Language`` header. - - See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 - """ - if "Accept-Language" in self.request.headers: - languages = self.request.headers["Accept-Language"].split(",") - locales = [] - for language in languages: - parts = language.strip().split(";") - if len(parts) > 1 and parts[1].startswith("q="): - try: - score = float(parts[1][2:]) - except (ValueError, TypeError): - score = 0.0 - else: - score = 1.0 - locales.append((parts[0], score)) - if locales: - locales.sort(key=lambda pair: pair[1], reverse=True) - codes = [loc[0] for loc in locales] - return locale.get(*codes) - return locale.get(default) - - @property - def current_user(self) -> Any: - """The authenticated user for this request. - - This is set in one of two ways: - - * A subclass may override `get_current_user()`, which will be called - automatically the first time ``self.current_user`` is accessed. - `get_current_user()` will only be called once per request, - and is cached for future access:: - - def get_current_user(self): - user_cookie = self.get_secure_cookie("user") - if user_cookie: - return json.loads(user_cookie) - return None - - * It may be set as a normal variable, typically from an overridden - `prepare()`:: - - @gen.coroutine - def prepare(self): - user_id_cookie = self.get_secure_cookie("user_id") - if user_id_cookie: - self.current_user = yield load_user(user_id_cookie) - - Note that `prepare()` may be a coroutine while `get_current_user()` - may not, so the latter form is necessary if loading the user requires - asynchronous operations. - - The user object may be any type of the application's choosing. - """ - if not hasattr(self, "_current_user"): - self._current_user = self.get_current_user() - return self._current_user - - @current_user.setter - def current_user(self, value: Any) -> None: - self._current_user = value - - def get_current_user(self) -> Any: - """Override to determine the current user from, e.g., a cookie. - - This method may not be a coroutine. - """ - return None - - def get_login_url(self) -> str: - """Override to customize the login URL based on the request. - - By default, we use the ``login_url`` application setting. - """ - self.require_setting("login_url", "@tornado.web.authenticated") - return self.application.settings["login_url"] - - def get_template_path(self) -> Optional[str]: - """Override to customize template path for each handler. - - By default, we use the ``template_path`` application setting. - Return None to load templates relative to the calling file. - """ - return self.application.settings.get("template_path") - - @property - def xsrf_token(self) -> bytes: - """The XSRF-prevention token for the current user/session. - - To prevent cross-site request forgery, we set an '_xsrf' cookie - and include the same '_xsrf' value as an argument with all POST - requests. If the two do not match, we reject the form submission - as a potential forgery. - - See http://en.wikipedia.org/wiki/Cross-site_request_forgery - - This property is of type `bytes`, but it contains only ASCII - characters. If a character string is required, there is no - need to base64-encode it; just decode the byte string as - UTF-8. - - .. versionchanged:: 3.2.2 - The xsrf token will now be have a random mask applied in every - request, which makes it safe to include the token in pages - that are compressed. See http://breachattack.com for more - information on the issue fixed by this change. Old (version 1) - cookies will be converted to version 2 when this method is called - unless the ``xsrf_cookie_version`` `Application` setting is - set to 1. - - .. versionchanged:: 4.3 - The ``xsrf_cookie_kwargs`` `Application` setting may be - used to supply additional cookie options (which will be - passed directly to `set_cookie`). For example, - ``xsrf_cookie_kwargs=dict(httponly=True, secure=True)`` - will set the ``secure`` and ``httponly`` flags on the - ``_xsrf`` cookie. - """ - if not hasattr(self, "_xsrf_token"): - version, token, timestamp = self._get_raw_xsrf_token() - output_version = self.settings.get("xsrf_cookie_version", 2) - cookie_kwargs = self.settings.get("xsrf_cookie_kwargs", {}) - if output_version == 1: - self._xsrf_token = binascii.b2a_hex(token) - elif output_version == 2: - mask = os.urandom(4) - self._xsrf_token = b"|".join( - [ - b"2", - binascii.b2a_hex(mask), - binascii.b2a_hex(_websocket_mask(mask, token)), - utf8(str(int(timestamp))), - ] - ) - else: - raise ValueError("unknown xsrf cookie version %d", output_version) - if version is None: - if self.current_user and "expires_days" not in cookie_kwargs: - cookie_kwargs["expires_days"] = 30 - self.set_cookie("_xsrf", self._xsrf_token, **cookie_kwargs) - return self._xsrf_token - - def _get_raw_xsrf_token(self) -> Tuple[Optional[int], bytes, float]: - """Read or generate the xsrf token in its raw form. - - The raw_xsrf_token is a tuple containing: - - * version: the version of the cookie from which this token was read, - or None if we generated a new token in this request. - * token: the raw token data; random (non-ascii) bytes. - * timestamp: the time this token was generated (will not be accurate - for version 1 cookies) - """ - if not hasattr(self, "_raw_xsrf_token"): - cookie = self.get_cookie("_xsrf") - if cookie: - version, token, timestamp = self._decode_xsrf_token(cookie) - else: - version, token, timestamp = None, None, None - if token is None: - version = None - token = os.urandom(16) - timestamp = time.time() - assert token is not None - assert timestamp is not None - self._raw_xsrf_token = (version, token, timestamp) - return self._raw_xsrf_token - - def _decode_xsrf_token( - self, cookie: str - ) -> Tuple[Optional[int], Optional[bytes], Optional[float]]: - """Convert a cookie string into a the tuple form returned by - _get_raw_xsrf_token. - """ - - try: - m = _signed_value_version_re.match(utf8(cookie)) - - if m: - version = int(m.group(1)) - if version == 2: - _, mask_str, masked_token, timestamp_str = cookie.split("|") - - mask = binascii.a2b_hex(utf8(mask_str)) - token = _websocket_mask(mask, binascii.a2b_hex(utf8(masked_token))) - timestamp = int(timestamp_str) - return version, token, timestamp - else: - # Treat unknown versions as not present instead of failing. - raise Exception("Unknown xsrf cookie version") - else: - version = 1 - try: - token = binascii.a2b_hex(utf8(cookie)) - except (binascii.Error, TypeError): - token = utf8(cookie) - # We don't have a usable timestamp in older versions. - timestamp = int(time.time()) - return (version, token, timestamp) - except Exception: - # Catch exceptions and return nothing instead of failing. - gen_log.debug("Uncaught exception in _decode_xsrf_token", exc_info=True) - return None, None, None - - def check_xsrf_cookie(self) -> None: - """Verifies that the ``_xsrf`` cookie matches the ``_xsrf`` argument. - - To prevent cross-site request forgery, we set an ``_xsrf`` - cookie and include the same value as a non-cookie - field with all ``POST`` requests. If the two do not match, we - reject the form submission as a potential forgery. - - The ``_xsrf`` value may be set as either a form field named ``_xsrf`` - or in a custom HTTP header named ``X-XSRFToken`` or ``X-CSRFToken`` - (the latter is accepted for compatibility with Django). - - See http://en.wikipedia.org/wiki/Cross-site_request_forgery - - .. versionchanged:: 3.2.2 - Added support for cookie version 2. Both versions 1 and 2 are - supported. - """ - # Prior to release 1.1.1, this check was ignored if the HTTP header - # ``X-Requested-With: XMLHTTPRequest`` was present. This exception - # has been shown to be insecure and has been removed. For more - # information please see - # http://www.djangoproject.com/weblog/2011/feb/08/security/ - # http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails - token = ( - self.get_argument("_xsrf", None) - or self.request.headers.get("X-Xsrftoken") - or self.request.headers.get("X-Csrftoken") - ) - if not token: - raise HTTPError(403, "'_xsrf' argument missing from POST") - _, token, _ = self._decode_xsrf_token(token) - _, expected_token, _ = self._get_raw_xsrf_token() - if not token: - raise HTTPError(403, "'_xsrf' argument has invalid format") - if not hmac.compare_digest(utf8(token), utf8(expected_token)): - raise HTTPError(403, "XSRF cookie does not match POST argument") - - def xsrf_form_html(self) -> str: - """An HTML ``<input/>`` element to be included with all POST forms. - - It defines the ``_xsrf`` input value, which we check on all POST - requests to prevent cross-site request forgery. If you have set - the ``xsrf_cookies`` application setting, you must include this - HTML within all of your HTML forms. - - In a template, this method should be called with ``{% module - xsrf_form_html() %}`` - - See `check_xsrf_cookie()` above for more information. - """ - return ( - '<input type="hidden" name="_xsrf" value="' - + escape.xhtml_escape(self.xsrf_token) - + '"/>' - ) - - def static_url( - self, path: str, include_host: Optional[bool] = None, **kwargs: Any - ) -> str: - """Returns a static URL for the given relative static file path. - - This method requires you set the ``static_path`` setting in your - application (which specifies the root directory of your static - files). - - This method returns a versioned url (by default appending - ``?v=<signature>``), which allows the static files to be - cached indefinitely. This can be disabled by passing - ``include_version=False`` (in the default implementation; - other static file implementations are not required to support - this, but they may support other options). - - By default this method returns URLs relative to the current - host, but if ``include_host`` is true the URL returned will be - absolute. If this handler has an ``include_host`` attribute, - that value will be used as the default for all `static_url` - calls that do not pass ``include_host`` as a keyword argument. - - """ - self.require_setting("static_path", "static_url") - get_url = self.settings.get( - "static_handler_class", StaticFileHandler - ).make_static_url - - if include_host is None: - include_host = getattr(self, "include_host", False) - - if include_host: - base = self.request.protocol + "://" + self.request.host - else: - base = "" - - return base + get_url(self.settings, path, **kwargs) - - def require_setting(self, name: str, feature: str = "this feature") -> None: - """Raises an exception if the given app setting is not defined.""" - if not self.application.settings.get(name): - raise Exception( - "You must define the '%s' setting in your " - "application to use %s" % (name, feature) - ) - - def reverse_url(self, name: str, *args: Any) -> str: - """Alias for `Application.reverse_url`.""" - return self.application.reverse_url(name, *args) - - def compute_etag(self) -> Optional[str]: - """Computes the etag header to be used for this request. - - By default uses a hash of the content written so far. - - May be overridden to provide custom etag implementations, - or may return None to disable tornado's default etag support. - """ - hasher = hashlib.sha1() - for part in self._write_buffer: - hasher.update(part) - return '"%s"' % hasher.hexdigest() - - def set_etag_header(self) -> None: - """Sets the response's Etag header using ``self.compute_etag()``. - - Note: no header will be set if ``compute_etag()`` returns ``None``. - - This method is called automatically when the request is finished. - """ - etag = self.compute_etag() - if etag is not None: - self.set_header("Etag", etag) - - def check_etag_header(self) -> bool: - """Checks the ``Etag`` header against requests's ``If-None-Match``. - - Returns ``True`` if the request's Etag matches and a 304 should be - returned. For example:: - - self.set_etag_header() - if self.check_etag_header(): - self.set_status(304) - return - - This method is called automatically when the request is finished, - but may be called earlier for applications that override - `compute_etag` and want to do an early check for ``If-None-Match`` - before completing the request. The ``Etag`` header should be set - (perhaps with `set_etag_header`) before calling this method. - """ - computed_etag = utf8(self._headers.get("Etag", "")) - # Find all weak and strong etag values from If-None-Match header - # because RFC 7232 allows multiple etag values in a single header. - etags = re.findall( - br'\*|(?:W/)?"[^"]*"', utf8(self.request.headers.get("If-None-Match", "")) - ) - if not computed_etag or not etags: - return False - - match = False - if etags[0] == b"*": - match = True - else: - # Use a weak comparison when comparing entity-tags. - def val(x: bytes) -> bytes: - return x[2:] if x.startswith(b"W/") else x - - for etag in etags: - if val(etag) == val(computed_etag): - match = True - break - return match - - async def _execute( - self, transforms: List["OutputTransform"], *args: bytes, **kwargs: bytes - ) -> None: - """Executes this request with the given output transforms.""" - self._transforms = transforms - try: - if self.request.method not in self.SUPPORTED_METHODS: - raise HTTPError(405) - self.path_args = [self.decode_argument(arg) for arg in args] - self.path_kwargs = dict( - (k, self.decode_argument(v, name=k)) for (k, v) in kwargs.items() - ) - # If XSRF cookies are turned on, reject form submissions without - # the proper cookie - if self.request.method not in ( - "GET", - "HEAD", - "OPTIONS", - ) and self.application.settings.get("xsrf_cookies"): - self.check_xsrf_cookie() - - result = self.prepare() - if result is not None: - result = await result - if self._prepared_future is not None: - # Tell the Application we've finished with prepare() - # and are ready for the body to arrive. - future_set_result_unless_cancelled(self._prepared_future, None) - if self._finished: - return - - if _has_stream_request_body(self.__class__): - # In streaming mode request.body is a Future that signals - # the body has been completely received. The Future has no - # result; the data has been passed to self.data_received - # instead. - try: - await self.request._body_future - except iostream.StreamClosedError: - return - - method = getattr(self, self.request.method.lower()) - result = method(*self.path_args, **self.path_kwargs) - if result is not None: - result = await result - if self._auto_finish and not self._finished: - self.finish() - except Exception as e: - try: - self._handle_request_exception(e) - except Exception: - app_log.error("Exception in exception handler", exc_info=True) - finally: - # Unset result to avoid circular references - result = None - if self._prepared_future is not None and not self._prepared_future.done(): - # In case we failed before setting _prepared_future, do it - # now (to unblock the HTTP server). Note that this is not - # in a finally block to avoid GC issues prior to Python 3.4. - self._prepared_future.set_result(None) - - def data_received(self, chunk: bytes) -> Optional[Awaitable[None]]: - """Implement this method to handle streamed request data. - - Requires the `.stream_request_body` decorator. - - May be a coroutine for flow control. - """ - raise NotImplementedError() - - def _log(self) -> None: - """Logs the current request. - - Sort of deprecated since this functionality was moved to the - Application, but left in place for the benefit of existing apps - that have overridden this method. - """ - self.application.log_request(self) - - def _request_summary(self) -> str: - return "%s %s (%s)" % ( - self.request.method, - self.request.uri, - self.request.remote_ip, - ) - - def _handle_request_exception(self, e: BaseException) -> None: - if isinstance(e, Finish): - # Not an error; just finish the request without logging. - if not self._finished: - self.finish(*e.args) - return - try: - self.log_exception(*sys.exc_info()) - except Exception: - # An error here should still get a best-effort send_error() - # to avoid leaking the connection. - app_log.error("Error in exception logger", exc_info=True) - if self._finished: - # Extra errors after the request has been finished should - # be logged, but there is no reason to continue to try and - # send a response. - return - if isinstance(e, HTTPError): - self.send_error(e.status_code, exc_info=sys.exc_info()) - else: - self.send_error(500, exc_info=sys.exc_info()) - - def log_exception( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[TracebackType], - ) -> None: - """Override to customize logging of uncaught exceptions. - - By default logs instances of `HTTPError` as warnings without - stack traces (on the ``tornado.general`` logger), and all - other exceptions as errors with stack traces (on the - ``tornado.application`` logger). - - .. versionadded:: 3.1 - """ - if isinstance(value, HTTPError): - if value.log_message: - format = "%d %s: " + value.log_message - args = [value.status_code, self._request_summary()] + list(value.args) - gen_log.warning(format, *args) - else: - app_log.error( - "Uncaught exception %s\n%r", - self._request_summary(), - self.request, - exc_info=(typ, value, tb), # type: ignore - ) - - def _ui_module(self, name: str, module: Type["UIModule"]) -> Callable[..., str]: - def render(*args, **kwargs) -> str: # type: ignore - if not hasattr(self, "_active_modules"): - self._active_modules = {} # type: Dict[str, UIModule] - if name not in self._active_modules: - self._active_modules[name] = module(self) - rendered = self._active_modules[name].render(*args, **kwargs) - return rendered - - return render - - def _ui_method(self, method: Callable[..., str]) -> Callable[..., str]: - return lambda *args, **kwargs: method(self, *args, **kwargs) - - def _clear_representation_headers(self) -> None: - # 304 responses should not contain representation metadata - # headers (defined in - # https://tools.ietf.org/html/rfc7231#section-3.1) - # not explicitly allowed by - # https://tools.ietf.org/html/rfc7232#section-4.1 - headers = ["Content-Encoding", "Content-Language", "Content-Type"] - for h in headers: - self.clear_header(h) - - -def stream_request_body(cls: Type[RequestHandler]) -> Type[RequestHandler]: - """Apply to `RequestHandler` subclasses to enable streaming body support. - - This decorator implies the following changes: - - * `.HTTPServerRequest.body` is undefined, and body arguments will not - be included in `RequestHandler.get_argument`. - * `RequestHandler.prepare` is called when the request headers have been - read instead of after the entire body has been read. - * The subclass must define a method ``data_received(self, data):``, which - will be called zero or more times as data is available. Note that - if the request has an empty body, ``data_received`` may not be called. - * ``prepare`` and ``data_received`` may return Futures (such as via - ``@gen.coroutine``, in which case the next method will not be called - until those futures have completed. - * The regular HTTP method (``post``, ``put``, etc) will be called after - the entire body has been read. - - See the `file receiver demo <https://github.com/tornadoweb/tornado/tree/master/demos/file_upload/>`_ - for example usage. - """ # noqa: E501 - if not issubclass(cls, RequestHandler): - raise TypeError("expected subclass of RequestHandler, got %r", cls) - cls._stream_request_body = True - return cls - - -def _has_stream_request_body(cls: Type[RequestHandler]) -> bool: - if not issubclass(cls, RequestHandler): - raise TypeError("expected subclass of RequestHandler, got %r", cls) - return cls._stream_request_body - - -def removeslash( - method: Callable[..., Optional[Awaitable[None]]] -) -> Callable[..., Optional[Awaitable[None]]]: - """Use this decorator to remove trailing slashes from the request path. - - For example, a request to ``/foo/`` would redirect to ``/foo`` with this - decorator. Your request handler mapping should use a regular expression - like ``r'/foo/*'`` in conjunction with using the decorator. - """ - - @functools.wraps(method) - def wrapper( # type: ignore - self: RequestHandler, *args, **kwargs - ) -> Optional[Awaitable[None]]: - if self.request.path.endswith("/"): - if self.request.method in ("GET", "HEAD"): - uri = self.request.path.rstrip("/") - if uri: # don't try to redirect '/' to '' - if self.request.query: - uri += "?" + self.request.query - self.redirect(uri, permanent=True) - return None - else: - raise HTTPError(404) - return method(self, *args, **kwargs) - - return wrapper - - -def addslash( - method: Callable[..., Optional[Awaitable[None]]] -) -> Callable[..., Optional[Awaitable[None]]]: - """Use this decorator to add a missing trailing slash to the request path. - - For example, a request to ``/foo`` would redirect to ``/foo/`` with this - decorator. Your request handler mapping should use a regular expression - like ``r'/foo/?'`` in conjunction with using the decorator. - """ - - @functools.wraps(method) - def wrapper( # type: ignore - self: RequestHandler, *args, **kwargs - ) -> Optional[Awaitable[None]]: - if not self.request.path.endswith("/"): - if self.request.method in ("GET", "HEAD"): - uri = self.request.path + "/" - if self.request.query: - uri += "?" + self.request.query - self.redirect(uri, permanent=True) - return None - raise HTTPError(404) - return method(self, *args, **kwargs) - - return wrapper - - -class _ApplicationRouter(ReversibleRuleRouter): - """Routing implementation used internally by `Application`. - - Provides a binding between `Application` and `RequestHandler`. - This implementation extends `~.routing.ReversibleRuleRouter` in a couple of ways: - * it allows to use `RequestHandler` subclasses as `~.routing.Rule` target and - * it allows to use a list/tuple of rules as `~.routing.Rule` target. - ``process_rule`` implementation will substitute this list with an appropriate - `_ApplicationRouter` instance. - """ - - def __init__( - self, application: "Application", rules: Optional[_RuleList] = None - ) -> None: - assert isinstance(application, Application) - self.application = application - super().__init__(rules) - - def process_rule(self, rule: Rule) -> Rule: - rule = super().process_rule(rule) - - if isinstance(rule.target, (list, tuple)): - rule.target = _ApplicationRouter( - self.application, rule.target # type: ignore - ) - - return rule - - def get_target_delegate( - self, target: Any, request: httputil.HTTPServerRequest, **target_params: Any - ) -> Optional[httputil.HTTPMessageDelegate]: - if isclass(target) and issubclass(target, RequestHandler): - return self.application.get_handler_delegate( - request, target, **target_params - ) - - return super().get_target_delegate(target, request, **target_params) - - -class Application(ReversibleRouter): - r"""A collection of request handlers that make up a web application. - - Instances of this class are callable and can be passed directly to - HTTPServer to serve the application:: - - application = web.Application([ - (r"/", MainPageHandler), - ]) - http_server = httpserver.HTTPServer(application) - http_server.listen(8080) - ioloop.IOLoop.current().start() - - The constructor for this class takes in a list of `~.routing.Rule` - objects or tuples of values corresponding to the arguments of - `~.routing.Rule` constructor: ``(matcher, target, [target_kwargs], [name])``, - the values in square brackets being optional. The default matcher is - `~.routing.PathMatches`, so ``(regexp, target)`` tuples can also be used - instead of ``(PathMatches(regexp), target)``. - - A common routing target is a `RequestHandler` subclass, but you can also - use lists of rules as a target, which create a nested routing configuration:: - - application = web.Application([ - (HostMatches("example.com"), [ - (r"/", MainPageHandler), - (r"/feed", FeedHandler), - ]), - ]) - - In addition to this you can use nested `~.routing.Router` instances, - `~.httputil.HTTPMessageDelegate` subclasses and callables as routing targets - (see `~.routing` module docs for more information). - - When we receive requests, we iterate over the list in order and - instantiate an instance of the first request class whose regexp - matches the request path. The request class can be specified as - either a class object or a (fully-qualified) name. - - A dictionary may be passed as the third element (``target_kwargs``) - of the tuple, which will be used as keyword arguments to the handler's - constructor and `~RequestHandler.initialize` method. This pattern - is used for the `StaticFileHandler` in this example (note that a - `StaticFileHandler` can be installed automatically with the - static_path setting described below):: - - application = web.Application([ - (r"/static/(.*)", web.StaticFileHandler, {"path": "/var/www"}), - ]) - - We support virtual hosts with the `add_handlers` method, which takes in - a host regular expression as the first argument:: - - application.add_handlers(r"www\.myhost\.com", [ - (r"/article/([0-9]+)", ArticleHandler), - ]) - - If there's no match for the current request's host, then ``default_host`` - parameter value is matched against host regular expressions. - - - .. warning:: - - Applications that do not use TLS may be vulnerable to :ref:`DNS - rebinding <dnsrebinding>` attacks. This attack is especially - relevant to applications that only listen on ``127.0.0.1`` or - other private networks. Appropriate host patterns must be used - (instead of the default of ``r'.*'``) to prevent this risk. The - ``default_host`` argument must not be used in applications that - may be vulnerable to DNS rebinding. - - You can serve static files by sending the ``static_path`` setting - as a keyword argument. We will serve those files from the - ``/static/`` URI (this is configurable with the - ``static_url_prefix`` setting), and we will serve ``/favicon.ico`` - and ``/robots.txt`` from the same directory. A custom subclass of - `StaticFileHandler` can be specified with the - ``static_handler_class`` setting. - - .. versionchanged:: 4.5 - Integration with the new `tornado.routing` module. - - """ - - def __init__( - self, - handlers: Optional[_RuleList] = None, - default_host: Optional[str] = None, - transforms: Optional[List[Type["OutputTransform"]]] = None, - **settings: Any - ) -> None: - if transforms is None: - self.transforms = [] # type: List[Type[OutputTransform]] - if settings.get("compress_response") or settings.get("gzip"): - self.transforms.append(GZipContentEncoding) - else: - self.transforms = transforms - self.default_host = default_host - self.settings = settings - self.ui_modules = { - "linkify": _linkify, - "xsrf_form_html": _xsrf_form_html, - "Template": TemplateModule, - } - self.ui_methods = {} # type: Dict[str, Callable[..., str]] - self._load_ui_modules(settings.get("ui_modules", {})) - self._load_ui_methods(settings.get("ui_methods", {})) - if self.settings.get("static_path"): - path = self.settings["static_path"] - handlers = list(handlers or []) - static_url_prefix = settings.get("static_url_prefix", "/static/") - static_handler_class = settings.get( - "static_handler_class", StaticFileHandler - ) - static_handler_args = settings.get("static_handler_args", {}) - static_handler_args["path"] = path - for pattern in [ - re.escape(static_url_prefix) + r"(.*)", - r"/(favicon\.ico)", - r"/(robots\.txt)", - ]: - handlers.insert(0, (pattern, static_handler_class, static_handler_args)) - - if self.settings.get("debug"): - self.settings.setdefault("autoreload", True) - self.settings.setdefault("compiled_template_cache", False) - self.settings.setdefault("static_hash_cache", False) - self.settings.setdefault("serve_traceback", True) - - self.wildcard_router = _ApplicationRouter(self, handlers) - self.default_router = _ApplicationRouter( - self, [Rule(AnyMatches(), self.wildcard_router)] - ) - - # Automatically reload modified modules - if self.settings.get("autoreload"): - from tornado import autoreload - - autoreload.start() - - def listen(self, port: int, address: str = "", **kwargs: Any) -> HTTPServer: - """Starts an HTTP server for this application on the given port. - - This is a convenience alias for creating an `.HTTPServer` - object and calling its listen method. Keyword arguments not - supported by `HTTPServer.listen <.TCPServer.listen>` are passed to the - `.HTTPServer` constructor. For advanced uses - (e.g. multi-process mode), do not use this method; create an - `.HTTPServer` and call its - `.TCPServer.bind`/`.TCPServer.start` methods directly. - - Note that after calling this method you still need to call - ``IOLoop.current().start()`` to start the server. - - Returns the `.HTTPServer` object. - - .. versionchanged:: 4.3 - Now returns the `.HTTPServer` object. - """ - server = HTTPServer(self, **kwargs) - server.listen(port, address) - return server - - def add_handlers(self, host_pattern: str, host_handlers: _RuleList) -> None: - """Appends the given handlers to our handler list. - - Host patterns are processed sequentially in the order they were - added. All matching patterns will be considered. - """ - host_matcher = HostMatches(host_pattern) - rule = Rule(host_matcher, _ApplicationRouter(self, host_handlers)) - - self.default_router.rules.insert(-1, rule) - - if self.default_host is not None: - self.wildcard_router.add_rules( - [(DefaultHostMatches(self, host_matcher.host_pattern), host_handlers)] - ) - - def add_transform(self, transform_class: Type["OutputTransform"]) -> None: - self.transforms.append(transform_class) - - def _load_ui_methods(self, methods: Any) -> None: - if isinstance(methods, types.ModuleType): - self._load_ui_methods(dict((n, getattr(methods, n)) for n in dir(methods))) - elif isinstance(methods, list): - for m in methods: - self._load_ui_methods(m) - else: - for name, fn in methods.items(): - if ( - not name.startswith("_") - and hasattr(fn, "__call__") - and name[0].lower() == name[0] - ): - self.ui_methods[name] = fn - - def _load_ui_modules(self, modules: Any) -> None: - if isinstance(modules, types.ModuleType): - self._load_ui_modules(dict((n, getattr(modules, n)) for n in dir(modules))) - elif isinstance(modules, list): - for m in modules: - self._load_ui_modules(m) - else: - assert isinstance(modules, dict) - for name, cls in modules.items(): - try: - if issubclass(cls, UIModule): - self.ui_modules[name] = cls - except TypeError: - pass - - def __call__( - self, request: httputil.HTTPServerRequest - ) -> Optional[Awaitable[None]]: - # Legacy HTTPServer interface - dispatcher = self.find_handler(request) - return dispatcher.execute() - - def find_handler( - self, request: httputil.HTTPServerRequest, **kwargs: Any - ) -> "_HandlerDelegate": - route = self.default_router.find_handler(request) - if route is not None: - return cast("_HandlerDelegate", route) - - if self.settings.get("default_handler_class"): - return self.get_handler_delegate( - request, - self.settings["default_handler_class"], - self.settings.get("default_handler_args", {}), - ) - - return self.get_handler_delegate(request, ErrorHandler, {"status_code": 404}) - - def get_handler_delegate( - self, - request: httputil.HTTPServerRequest, - target_class: Type[RequestHandler], - target_kwargs: Optional[Dict[str, Any]] = None, - path_args: Optional[List[bytes]] = None, - path_kwargs: Optional[Dict[str, bytes]] = None, - ) -> "_HandlerDelegate": - """Returns `~.httputil.HTTPMessageDelegate` that can serve a request - for application and `RequestHandler` subclass. - - :arg httputil.HTTPServerRequest request: current HTTP request. - :arg RequestHandler target_class: a `RequestHandler` class. - :arg dict target_kwargs: keyword arguments for ``target_class`` constructor. - :arg list path_args: positional arguments for ``target_class`` HTTP method that - will be executed while handling a request (``get``, ``post`` or any other). - :arg dict path_kwargs: keyword arguments for ``target_class`` HTTP method. - """ - return _HandlerDelegate( - self, request, target_class, target_kwargs, path_args, path_kwargs - ) - - def reverse_url(self, name: str, *args: Any) -> str: - """Returns a URL path for handler named ``name`` - - The handler must be added to the application as a named `URLSpec`. - - Args will be substituted for capturing groups in the `URLSpec` regex. - They will be converted to strings if necessary, encoded as utf8, - and url-escaped. - """ - reversed_url = self.default_router.reverse_url(name, *args) - if reversed_url is not None: - return reversed_url - - raise KeyError("%s not found in named urls" % name) - - def log_request(self, handler: RequestHandler) -> None: - """Writes a completed HTTP request to the logs. - - By default writes to the python root logger. To change - this behavior either subclass Application and override this method, - or pass a function in the application settings dictionary as - ``log_function``. - """ - if "log_function" in self.settings: - self.settings["log_function"](handler) - return - if handler.get_status() < 400: - log_method = access_log.info - elif handler.get_status() < 500: - log_method = access_log.warning - else: - log_method = access_log.error - request_time = 1000.0 * handler.request.request_time() - log_method( - "%d %s %.2fms", - handler.get_status(), - handler._request_summary(), - request_time, - ) - - -class _HandlerDelegate(httputil.HTTPMessageDelegate): - def __init__( - self, - application: Application, - request: httputil.HTTPServerRequest, - handler_class: Type[RequestHandler], - handler_kwargs: Optional[Dict[str, Any]], - path_args: Optional[List[bytes]], - path_kwargs: Optional[Dict[str, bytes]], - ) -> None: - self.application = application - self.connection = request.connection - self.request = request - self.handler_class = handler_class - self.handler_kwargs = handler_kwargs or {} - self.path_args = path_args or [] - self.path_kwargs = path_kwargs or {} - self.chunks = [] # type: List[bytes] - self.stream_request_body = _has_stream_request_body(self.handler_class) - - def headers_received( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - ) -> Optional[Awaitable[None]]: - if self.stream_request_body: - self.request._body_future = Future() - return self.execute() - return None - - def data_received(self, data: bytes) -> Optional[Awaitable[None]]: - if self.stream_request_body: - return self.handler.data_received(data) - else: - self.chunks.append(data) - return None - - def finish(self) -> None: - if self.stream_request_body: - future_set_result_unless_cancelled(self.request._body_future, None) - else: - self.request.body = b"".join(self.chunks) - self.request._parse_body() - self.execute() - - def on_connection_close(self) -> None: - if self.stream_request_body: - self.handler.on_connection_close() - else: - self.chunks = None # type: ignore - - def execute(self) -> Optional[Awaitable[None]]: - # If template cache is disabled (usually in the debug mode), - # re-compile templates and reload static files on every - # request so you don't need to restart to see changes - if not self.application.settings.get("compiled_template_cache", True): - with RequestHandler._template_loader_lock: - for loader in RequestHandler._template_loaders.values(): - loader.reset() - if not self.application.settings.get("static_hash_cache", True): - StaticFileHandler.reset() - - self.handler = self.handler_class( - self.application, self.request, **self.handler_kwargs - ) - transforms = [t(self.request) for t in self.application.transforms] - - if self.stream_request_body: - self.handler._prepared_future = Future() - # Note that if an exception escapes handler._execute it will be - # trapped in the Future it returns (which we are ignoring here, - # leaving it to be logged when the Future is GC'd). - # However, that shouldn't happen because _execute has a blanket - # except handler, and we cannot easily access the IOLoop here to - # call add_future (because of the requirement to remain compatible - # with WSGI) - fut = gen.convert_yielded( - self.handler._execute(transforms, *self.path_args, **self.path_kwargs) - ) - fut.add_done_callback(lambda f: f.result()) - # If we are streaming the request body, then execute() is finished - # when the handler has prepared to receive the body. If not, - # it doesn't matter when execute() finishes (so we return None) - return self.handler._prepared_future - - -class HTTPError(Exception): - """An exception that will turn into an HTTP error response. - - Raising an `HTTPError` is a convenient alternative to calling - `RequestHandler.send_error` since it automatically ends the - current function. - - To customize the response sent with an `HTTPError`, override - `RequestHandler.write_error`. - - :arg int status_code: HTTP status code. Must be listed in - `httplib.responses <http.client.responses>` unless the ``reason`` - keyword argument is given. - :arg str log_message: Message to be written to the log for this error - (will not be shown to the user unless the `Application` is in debug - mode). May contain ``%s``-style placeholders, which will be filled - in with remaining positional parameters. - :arg str reason: Keyword-only argument. The HTTP "reason" phrase - to pass in the status line along with ``status_code``. Normally - determined automatically from ``status_code``, but can be used - to use a non-standard numeric code. - """ - - def __init__( - self, - status_code: int = 500, - log_message: Optional[str] = None, - *args: Any, - **kwargs: Any - ) -> None: - self.status_code = status_code - self.log_message = log_message - self.args = args - self.reason = kwargs.get("reason", None) - if log_message and not args: - self.log_message = log_message.replace("%", "%%") - - def __str__(self) -> str: - message = "HTTP %d: %s" % ( - self.status_code, - self.reason or httputil.responses.get(self.status_code, "Unknown"), - ) - if self.log_message: - return message + " (" + (self.log_message % self.args) + ")" - else: - return message - - -class Finish(Exception): - """An exception that ends the request without producing an error response. - - When `Finish` is raised in a `RequestHandler`, the request will - end (calling `RequestHandler.finish` if it hasn't already been - called), but the error-handling methods (including - `RequestHandler.write_error`) will not be called. - - If `Finish()` was created with no arguments, the pending response - will be sent as-is. If `Finish()` was given an argument, that - argument will be passed to `RequestHandler.finish()`. - - This can be a more convenient way to implement custom error pages - than overriding ``write_error`` (especially in library code):: - - if self.current_user is None: - self.set_status(401) - self.set_header('WWW-Authenticate', 'Basic realm="something"') - raise Finish() - - .. versionchanged:: 4.3 - Arguments passed to ``Finish()`` will be passed on to - `RequestHandler.finish`. - """ - - pass - - -class MissingArgumentError(HTTPError): - """Exception raised by `RequestHandler.get_argument`. - - This is a subclass of `HTTPError`, so if it is uncaught a 400 response - code will be used instead of 500 (and a stack trace will not be logged). - - .. versionadded:: 3.1 - """ - - def __init__(self, arg_name: str) -> None: - super().__init__(400, "Missing argument %s" % arg_name) - self.arg_name = arg_name - - -class ErrorHandler(RequestHandler): - """Generates an error response with ``status_code`` for all requests.""" - - def initialize(self, status_code: int) -> None: - self.set_status(status_code) - - def prepare(self) -> None: - raise HTTPError(self._status_code) - - def check_xsrf_cookie(self) -> None: - # POSTs to an ErrorHandler don't actually have side effects, - # so we don't need to check the xsrf token. This allows POSTs - # to the wrong url to return a 404 instead of 403. - pass - - -class RedirectHandler(RequestHandler): - """Redirects the client to the given URL for all GET requests. - - You should provide the keyword argument ``url`` to the handler, e.g.:: - - application = web.Application([ - (r"/oldpath", web.RedirectHandler, {"url": "/newpath"}), - ]) - - `RedirectHandler` supports regular expression substitutions. E.g., to - swap the first and second parts of a path while preserving the remainder:: - - application = web.Application([ - (r"/(.*?)/(.*?)/(.*)", web.RedirectHandler, {"url": "/{1}/{0}/{2}"}), - ]) - - The final URL is formatted with `str.format` and the substrings that match - the capturing groups. In the above example, a request to "/a/b/c" would be - formatted like:: - - str.format("/{1}/{0}/{2}", "a", "b", "c") # -> "/b/a/c" - - Use Python's :ref:`format string syntax <formatstrings>` to customize how - values are substituted. - - .. versionchanged:: 4.5 - Added support for substitutions into the destination URL. - - .. versionchanged:: 5.0 - If any query arguments are present, they will be copied to the - destination URL. - """ - - def initialize(self, url: str, permanent: bool = True) -> None: - self._url = url - self._permanent = permanent - - def get(self, *args: Any, **kwargs: Any) -> None: - to_url = self._url.format(*args, **kwargs) - if self.request.query_arguments: - # TODO: figure out typing for the next line. - to_url = httputil.url_concat( - to_url, - list(httputil.qs_to_qsl(self.request.query_arguments)), # type: ignore - ) - self.redirect(to_url, permanent=self._permanent) - - -class StaticFileHandler(RequestHandler): - """A simple handler that can serve static content from a directory. - - A `StaticFileHandler` is configured automatically if you pass the - ``static_path`` keyword argument to `Application`. This handler - can be customized with the ``static_url_prefix``, ``static_handler_class``, - and ``static_handler_args`` settings. - - To map an additional path to this handler for a static data directory - you would add a line to your application like:: - - application = web.Application([ - (r"/content/(.*)", web.StaticFileHandler, {"path": "/var/www"}), - ]) - - The handler constructor requires a ``path`` argument, which specifies the - local root directory of the content to be served. - - Note that a capture group in the regex is required to parse the value for - the ``path`` argument to the get() method (different than the constructor - argument above); see `URLSpec` for details. - - To serve a file like ``index.html`` automatically when a directory is - requested, set ``static_handler_args=dict(default_filename="index.html")`` - in your application settings, or add ``default_filename`` as an initializer - argument for your ``StaticFileHandler``. - - To maximize the effectiveness of browser caching, this class supports - versioned urls (by default using the argument ``?v=``). If a version - is given, we instruct the browser to cache this file indefinitely. - `make_static_url` (also available as `RequestHandler.static_url`) can - be used to construct a versioned url. - - This handler is intended primarily for use in development and light-duty - file serving; for heavy traffic it will be more efficient to use - a dedicated static file server (such as nginx or Apache). We support - the HTTP ``Accept-Ranges`` mechanism to return partial content (because - some browsers require this functionality to be present to seek in - HTML5 audio or video). - - **Subclassing notes** - - This class is designed to be extensible by subclassing, but because - of the way static urls are generated with class methods rather than - instance methods, the inheritance patterns are somewhat unusual. - Be sure to use the ``@classmethod`` decorator when overriding a - class method. Instance methods may use the attributes ``self.path`` - ``self.absolute_path``, and ``self.modified``. - - Subclasses should only override methods discussed in this section; - overriding other methods is error-prone. Overriding - ``StaticFileHandler.get`` is particularly problematic due to the - tight coupling with ``compute_etag`` and other methods. - - To change the way static urls are generated (e.g. to match the behavior - of another server or CDN), override `make_static_url`, `parse_url_path`, - `get_cache_time`, and/or `get_version`. - - To replace all interaction with the filesystem (e.g. to serve - static content from a database), override `get_content`, - `get_content_size`, `get_modified_time`, `get_absolute_path`, and - `validate_absolute_path`. - - .. versionchanged:: 3.1 - Many of the methods for subclasses were added in Tornado 3.1. - """ - - CACHE_MAX_AGE = 86400 * 365 * 10 # 10 years - - _static_hashes = {} # type: Dict[str, Optional[str]] - _lock = threading.Lock() # protects _static_hashes - - def initialize(self, path: str, default_filename: Optional[str] = None) -> None: - self.root = path - self.default_filename = default_filename - - @classmethod - def reset(cls) -> None: - with cls._lock: - cls._static_hashes = {} - - def head(self, path: str) -> Awaitable[None]: - return self.get(path, include_body=False) - - async def get(self, path: str, include_body: bool = True) -> None: - # Set up our path instance variables. - self.path = self.parse_url_path(path) - del path # make sure we don't refer to path instead of self.path again - absolute_path = self.get_absolute_path(self.root, self.path) - self.absolute_path = self.validate_absolute_path(self.root, absolute_path) - if self.absolute_path is None: - return - - self.modified = self.get_modified_time() - self.set_headers() - - if self.should_return_304(): - self.set_status(304) - return - - request_range = None - range_header = self.request.headers.get("Range") - if range_header: - # As per RFC 2616 14.16, if an invalid Range header is specified, - # the request will be treated as if the header didn't exist. - request_range = httputil._parse_request_range(range_header) - - size = self.get_content_size() - if request_range: - start, end = request_range - if start is not None and start < 0: - start += size - if start < 0: - start = 0 - if ( - start is not None - and (start >= size or (end is not None and start >= end)) - ) or end == 0: - # As per RFC 2616 14.35.1, a range is not satisfiable only: if - # the first requested byte is equal to or greater than the - # content, or when a suffix with length 0 is specified. - # https://tools.ietf.org/html/rfc7233#section-2.1 - # A byte-range-spec is invalid if the last-byte-pos value is present - # and less than the first-byte-pos. - self.set_status(416) # Range Not Satisfiable - self.set_header("Content-Type", "text/plain") - self.set_header("Content-Range", "bytes */%s" % (size,)) - return - if end is not None and end > size: - # Clients sometimes blindly use a large range to limit their - # download size; cap the endpoint at the actual file size. - end = size - # Note: only return HTTP 206 if less than the entire range has been - # requested. Not only is this semantically correct, but Chrome - # refuses to play audio if it gets an HTTP 206 in response to - # ``Range: bytes=0-``. - if size != (end or size) - (start or 0): - self.set_status(206) # Partial Content - self.set_header( - "Content-Range", httputil._get_content_range(start, end, size) - ) - else: - start = end = None - - if start is not None and end is not None: - content_length = end - start - elif end is not None: - content_length = end - elif start is not None: - content_length = size - start - else: - content_length = size - self.set_header("Content-Length", content_length) - - if include_body: - content = self.get_content(self.absolute_path, start, end) - if isinstance(content, bytes): - content = [content] - for chunk in content: - try: - self.write(chunk) - await self.flush() - except iostream.StreamClosedError: - return - else: - assert self.request.method == "HEAD" - - def compute_etag(self) -> Optional[str]: - """Sets the ``Etag`` header based on static url version. - - This allows efficient ``If-None-Match`` checks against cached - versions, and sends the correct ``Etag`` for a partial response - (i.e. the same ``Etag`` as the full file). - - .. versionadded:: 3.1 - """ - assert self.absolute_path is not None - version_hash = self._get_cached_version(self.absolute_path) - if not version_hash: - return None - return '"%s"' % (version_hash,) - - def set_headers(self) -> None: - """Sets the content and caching headers on the response. - - .. versionadded:: 3.1 - """ - self.set_header("Accept-Ranges", "bytes") - self.set_etag_header() - - if self.modified is not None: - self.set_header("Last-Modified", self.modified) - - content_type = self.get_content_type() - if content_type: - self.set_header("Content-Type", content_type) - - cache_time = self.get_cache_time(self.path, self.modified, content_type) - if cache_time > 0: - self.set_header( - "Expires", - datetime.datetime.utcnow() + datetime.timedelta(seconds=cache_time), - ) - self.set_header("Cache-Control", "max-age=" + str(cache_time)) - - self.set_extra_headers(self.path) - - def should_return_304(self) -> bool: - """Returns True if the headers indicate that we should return 304. - - .. versionadded:: 3.1 - """ - # If client sent If-None-Match, use it, ignore If-Modified-Since - if self.request.headers.get("If-None-Match"): - return self.check_etag_header() - - # Check the If-Modified-Since, and don't send the result if the - # content has not been modified - ims_value = self.request.headers.get("If-Modified-Since") - if ims_value is not None: - date_tuple = email.utils.parsedate(ims_value) - if date_tuple is not None: - if_since = datetime.datetime(*date_tuple[:6]) - assert self.modified is not None - if if_since >= self.modified: - return True - - return False - - @classmethod - def get_absolute_path(cls, root: str, path: str) -> str: - """Returns the absolute location of ``path`` relative to ``root``. - - ``root`` is the path configured for this `StaticFileHandler` - (in most cases the ``static_path`` `Application` setting). - - This class method may be overridden in subclasses. By default - it returns a filesystem path, but other strings may be used - as long as they are unique and understood by the subclass's - overridden `get_content`. - - .. versionadded:: 3.1 - """ - abspath = os.path.abspath(os.path.join(root, path)) - return abspath - - def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]: - """Validate and return the absolute path. - - ``root`` is the configured path for the `StaticFileHandler`, - and ``path`` is the result of `get_absolute_path` - - This is an instance method called during request processing, - so it may raise `HTTPError` or use methods like - `RequestHandler.redirect` (return None after redirecting to - halt further processing). This is where 404 errors for missing files - are generated. - - This method may modify the path before returning it, but note that - any such modifications will not be understood by `make_static_url`. - - In instance methods, this method's result is available as - ``self.absolute_path``. - - .. versionadded:: 3.1 - """ - # os.path.abspath strips a trailing /. - # We must add it back to `root` so that we only match files - # in a directory named `root` instead of files starting with - # that prefix. - root = os.path.abspath(root) - if not root.endswith(os.path.sep): - # abspath always removes a trailing slash, except when - # root is '/'. This is an unusual case, but several projects - # have independently discovered this technique to disable - # Tornado's path validation and (hopefully) do their own, - # so we need to support it. - root += os.path.sep - # The trailing slash also needs to be temporarily added back - # the requested path so a request to root/ will match. - if not (absolute_path + os.path.sep).startswith(root): - raise HTTPError(403, "%s is not in root static directory", self.path) - if os.path.isdir(absolute_path) and self.default_filename is not None: - # need to look at the request.path here for when path is empty - # but there is some prefix to the path that was already - # trimmed by the routing - if not self.request.path.endswith("/"): - self.redirect(self.request.path + "/", permanent=True) - return None - absolute_path = os.path.join(absolute_path, self.default_filename) - if not os.path.exists(absolute_path): - raise HTTPError(404) - if not os.path.isfile(absolute_path): - raise HTTPError(403, "%s is not a file", self.path) - return absolute_path - - @classmethod - def get_content( - cls, abspath: str, start: Optional[int] = None, end: Optional[int] = None - ) -> Generator[bytes, None, None]: - """Retrieve the content of the requested resource which is located - at the given absolute path. - - This class method may be overridden by subclasses. Note that its - signature is different from other overridable class methods - (no ``settings`` argument); this is deliberate to ensure that - ``abspath`` is able to stand on its own as a cache key. - - This method should either return a byte string or an iterator - of byte strings. The latter is preferred for large files - as it helps reduce memory fragmentation. - - .. versionadded:: 3.1 - """ - with open(abspath, "rb") as file: - if start is not None: - file.seek(start) - if end is not None: - remaining = end - (start or 0) # type: Optional[int] - else: - remaining = None - while True: - chunk_size = 64 * 1024 - if remaining is not None and remaining < chunk_size: - chunk_size = remaining - chunk = file.read(chunk_size) - if chunk: - if remaining is not None: - remaining -= len(chunk) - yield chunk - else: - if remaining is not None: - assert remaining == 0 - return - - @classmethod - def get_content_version(cls, abspath: str) -> str: - """Returns a version string for the resource at the given path. - - This class method may be overridden by subclasses. The - default implementation is a SHA-512 hash of the file's contents. - - .. versionadded:: 3.1 - """ - data = cls.get_content(abspath) - hasher = hashlib.sha512() - if isinstance(data, bytes): - hasher.update(data) - else: - for chunk in data: - hasher.update(chunk) - return hasher.hexdigest() - - def _stat(self) -> os.stat_result: - assert self.absolute_path is not None - if not hasattr(self, "_stat_result"): - self._stat_result = os.stat(self.absolute_path) - return self._stat_result - - def get_content_size(self) -> int: - """Retrieve the total size of the resource at the given path. - - This method may be overridden by subclasses. - - .. versionadded:: 3.1 - - .. versionchanged:: 4.0 - This method is now always called, instead of only when - partial results are requested. - """ - stat_result = self._stat() - return stat_result.st_size - - def get_modified_time(self) -> Optional[datetime.datetime]: - """Returns the time that ``self.absolute_path`` was last modified. - - May be overridden in subclasses. Should return a `~datetime.datetime` - object or None. - - .. versionadded:: 3.1 - """ - stat_result = self._stat() - # NOTE: Historically, this used stat_result[stat.ST_MTIME], - # which truncates the fractional portion of the timestamp. It - # was changed from that form to stat_result.st_mtime to - # satisfy mypy (which disallows the bracket operator), but the - # latter form returns a float instead of an int. For - # consistency with the past (and because we have a unit test - # that relies on this), we truncate the float here, although - # I'm not sure that's the right thing to do. - modified = datetime.datetime.utcfromtimestamp(int(stat_result.st_mtime)) - return modified - - def get_content_type(self) -> str: - """Returns the ``Content-Type`` header to be used for this request. - - .. versionadded:: 3.1 - """ - assert self.absolute_path is not None - mime_type, encoding = mimetypes.guess_type(self.absolute_path) - # per RFC 6713, use the appropriate type for a gzip compressed file - if encoding == "gzip": - return "application/gzip" - # As of 2015-07-21 there is no bzip2 encoding defined at - # http://www.iana.org/assignments/media-types/media-types.xhtml - # So for that (and any other encoding), use octet-stream. - elif encoding is not None: - return "application/octet-stream" - elif mime_type is not None: - return mime_type - # if mime_type not detected, use application/octet-stream - else: - return "application/octet-stream" - - def set_extra_headers(self, path: str) -> None: - """For subclass to add extra headers to the response""" - pass - - def get_cache_time( - self, path: str, modified: Optional[datetime.datetime], mime_type: str - ) -> int: - """Override to customize cache control behavior. - - Return a positive number of seconds to make the result - cacheable for that amount of time or 0 to mark resource as - cacheable for an unspecified amount of time (subject to - browser heuristics). - - By default returns cache expiry of 10 years for resources requested - with ``v`` argument. - """ - return self.CACHE_MAX_AGE if "v" in self.request.arguments else 0 - - @classmethod - def make_static_url( - cls, settings: Dict[str, Any], path: str, include_version: bool = True - ) -> str: - """Constructs a versioned url for the given path. - - This method may be overridden in subclasses (but note that it - is a class method rather than an instance method). Subclasses - are only required to implement the signature - ``make_static_url(cls, settings, path)``; other keyword - arguments may be passed through `~RequestHandler.static_url` - but are not standard. - - ``settings`` is the `Application.settings` dictionary. ``path`` - is the static path being requested. The url returned should be - relative to the current host. - - ``include_version`` determines whether the generated URL should - include the query string containing the version hash of the - file corresponding to the given ``path``. - - """ - url = settings.get("static_url_prefix", "/static/") + path - if not include_version: - return url - - version_hash = cls.get_version(settings, path) - if not version_hash: - return url - - return "%s?v=%s" % (url, version_hash) - - def parse_url_path(self, url_path: str) -> str: - """Converts a static URL path into a filesystem path. - - ``url_path`` is the path component of the URL with - ``static_url_prefix`` removed. The return value should be - filesystem path relative to ``static_path``. - - This is the inverse of `make_static_url`. - """ - if os.path.sep != "/": - url_path = url_path.replace("/", os.path.sep) - return url_path - - @classmethod - def get_version(cls, settings: Dict[str, Any], path: str) -> Optional[str]: - """Generate the version string to be used in static URLs. - - ``settings`` is the `Application.settings` dictionary and ``path`` - is the relative location of the requested asset on the filesystem. - The returned value should be a string, or ``None`` if no version - could be determined. - - .. versionchanged:: 3.1 - This method was previously recommended for subclasses to override; - `get_content_version` is now preferred as it allows the base - class to handle caching of the result. - """ - abs_path = cls.get_absolute_path(settings["static_path"], path) - return cls._get_cached_version(abs_path) - - @classmethod - def _get_cached_version(cls, abs_path: str) -> Optional[str]: - with cls._lock: - hashes = cls._static_hashes - if abs_path not in hashes: - try: - hashes[abs_path] = cls.get_content_version(abs_path) - except Exception: - gen_log.error("Could not open static file %r", abs_path) - hashes[abs_path] = None - hsh = hashes.get(abs_path) - if hsh: - return hsh - return None - - -class FallbackHandler(RequestHandler): - """A `RequestHandler` that wraps another HTTP server callback. - - The fallback is a callable object that accepts an - `~.httputil.HTTPServerRequest`, such as an `Application` or - `tornado.wsgi.WSGIContainer`. This is most useful to use both - Tornado ``RequestHandlers`` and WSGI in the same server. Typical - usage:: - - wsgi_app = tornado.wsgi.WSGIContainer( - django.core.handlers.wsgi.WSGIHandler()) - application = tornado.web.Application([ - (r"/foo", FooHandler), - (r".*", FallbackHandler, dict(fallback=wsgi_app), - ]) - """ - - def initialize( - self, fallback: Callable[[httputil.HTTPServerRequest], None] - ) -> None: - self.fallback = fallback - - def prepare(self) -> None: - self.fallback(self.request) - self._finished = True - self.on_finish() - - -class OutputTransform(object): - """A transform modifies the result of an HTTP request (e.g., GZip encoding) - - Applications are not expected to create their own OutputTransforms - or interact with them directly; the framework chooses which transforms - (if any) to apply. - """ - - def __init__(self, request: httputil.HTTPServerRequest) -> None: - pass - - def transform_first_chunk( - self, - status_code: int, - headers: httputil.HTTPHeaders, - chunk: bytes, - finishing: bool, - ) -> Tuple[int, httputil.HTTPHeaders, bytes]: - return status_code, headers, chunk - - def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes: - return chunk - - -class GZipContentEncoding(OutputTransform): - """Applies the gzip content encoding to the response. - - See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11 - - .. versionchanged:: 4.0 - Now compresses all mime types beginning with ``text/``, instead - of just a whitelist. (the whitelist is still used for certain - non-text mime types). - """ - - # Whitelist of compressible mime types (in addition to any types - # beginning with "text/"). - CONTENT_TYPES = set( - [ - "application/javascript", - "application/x-javascript", - "application/xml", - "application/atom+xml", - "application/json", - "application/xhtml+xml", - "image/svg+xml", - ] - ) - # Python's GzipFile defaults to level 9, while most other gzip - # tools (including gzip itself) default to 6, which is probably a - # better CPU/size tradeoff. - GZIP_LEVEL = 6 - # Responses that are too short are unlikely to benefit from gzipping - # after considering the "Content-Encoding: gzip" header and the header - # inside the gzip encoding. - # Note that responses written in multiple chunks will be compressed - # regardless of size. - MIN_LENGTH = 1024 - - def __init__(self, request: httputil.HTTPServerRequest) -> None: - self._gzipping = "gzip" in request.headers.get("Accept-Encoding", "") - - def _compressible_type(self, ctype: str) -> bool: - return ctype.startswith("text/") or ctype in self.CONTENT_TYPES - - def transform_first_chunk( - self, - status_code: int, - headers: httputil.HTTPHeaders, - chunk: bytes, - finishing: bool, - ) -> Tuple[int, httputil.HTTPHeaders, bytes]: - # TODO: can/should this type be inherited from the superclass? - if "Vary" in headers: - headers["Vary"] += ", Accept-Encoding" - else: - headers["Vary"] = "Accept-Encoding" - if self._gzipping: - ctype = _unicode(headers.get("Content-Type", "")).split(";")[0] - self._gzipping = ( - self._compressible_type(ctype) - and (not finishing or len(chunk) >= self.MIN_LENGTH) - and ("Content-Encoding" not in headers) - ) - if self._gzipping: - headers["Content-Encoding"] = "gzip" - self._gzip_value = BytesIO() - self._gzip_file = gzip.GzipFile( - mode="w", fileobj=self._gzip_value, compresslevel=self.GZIP_LEVEL - ) - chunk = self.transform_chunk(chunk, finishing) - if "Content-Length" in headers: - # The original content length is no longer correct. - # If this is the last (and only) chunk, we can set the new - # content-length; otherwise we remove it and fall back to - # chunked encoding. - if finishing: - headers["Content-Length"] = str(len(chunk)) - else: - del headers["Content-Length"] - return status_code, headers, chunk - - def transform_chunk(self, chunk: bytes, finishing: bool) -> bytes: - if self._gzipping: - self._gzip_file.write(chunk) - if finishing: - self._gzip_file.close() - else: - self._gzip_file.flush() - chunk = self._gzip_value.getvalue() - self._gzip_value.truncate(0) - self._gzip_value.seek(0) - return chunk - - -def authenticated( - method: Callable[..., Optional[Awaitable[None]]] -) -> Callable[..., Optional[Awaitable[None]]]: - """Decorate methods with this to require that the user be logged in. - - If the user is not logged in, they will be redirected to the configured - `login url <RequestHandler.get_login_url>`. - - If you configure a login url with a query parameter, Tornado will - assume you know what you're doing and use it as-is. If not, it - will add a `next` parameter so the login page knows where to send - you once you're logged in. - """ - - @functools.wraps(method) - def wrapper( # type: ignore - self: RequestHandler, *args, **kwargs - ) -> Optional[Awaitable[None]]: - if not self.current_user: - if self.request.method in ("GET", "HEAD"): - url = self.get_login_url() - if "?" not in url: - if urllib.parse.urlsplit(url).scheme: - # if login url is absolute, make next absolute too - next_url = self.request.full_url() - else: - assert self.request.uri is not None - next_url = self.request.uri - url += "?" + urlencode(dict(next=next_url)) - self.redirect(url) - return None - raise HTTPError(403) - return method(self, *args, **kwargs) - - return wrapper - - -class UIModule(object): - """A re-usable, modular UI unit on a page. - - UI modules often execute additional queries, and they can include - additional CSS and JavaScript that will be included in the output - page, which is automatically inserted on page render. - - Subclasses of UIModule must override the `render` method. - """ - - def __init__(self, handler: RequestHandler) -> None: - self.handler = handler - self.request = handler.request - self.ui = handler.ui - self.locale = handler.locale - - @property - def current_user(self) -> Any: - return self.handler.current_user - - def render(self, *args: Any, **kwargs: Any) -> str: - """Override in subclasses to return this module's output.""" - raise NotImplementedError() - - def embedded_javascript(self) -> Optional[str]: - """Override to return a JavaScript string - to be embedded in the page.""" - return None - - def javascript_files(self) -> Optional[Iterable[str]]: - """Override to return a list of JavaScript files needed by this module. - - If the return values are relative paths, they will be passed to - `RequestHandler.static_url`; otherwise they will be used as-is. - """ - return None - - def embedded_css(self) -> Optional[str]: - """Override to return a CSS string - that will be embedded in the page.""" - return None - - def css_files(self) -> Optional[Iterable[str]]: - """Override to returns a list of CSS files required by this module. - - If the return values are relative paths, they will be passed to - `RequestHandler.static_url`; otherwise they will be used as-is. - """ - return None - - def html_head(self) -> Optional[str]: - """Override to return an HTML string that will be put in the <head/> - element. - """ - return None - - def html_body(self) -> Optional[str]: - """Override to return an HTML string that will be put at the end of - the <body/> element. - """ - return None - - def render_string(self, path: str, **kwargs: Any) -> bytes: - """Renders a template and returns it as a string.""" - return self.handler.render_string(path, **kwargs) - - -class _linkify(UIModule): - def render(self, text: str, **kwargs: Any) -> str: # type: ignore - return escape.linkify(text, **kwargs) - - -class _xsrf_form_html(UIModule): - def render(self) -> str: # type: ignore - return self.handler.xsrf_form_html() - - -class TemplateModule(UIModule): - """UIModule that simply renders the given template. - - {% module Template("foo.html") %} is similar to {% include "foo.html" %}, - but the module version gets its own namespace (with kwargs passed to - Template()) instead of inheriting the outer template's namespace. - - Templates rendered through this module also get access to UIModule's - automatic JavaScript/CSS features. Simply call set_resources - inside the template and give it keyword arguments corresponding to - the methods on UIModule: {{ set_resources(js_files=static_url("my.js")) }} - Note that these resources are output once per template file, not once - per instantiation of the template, so they must not depend on - any arguments to the template. - """ - - def __init__(self, handler: RequestHandler) -> None: - super().__init__(handler) - # keep resources in both a list and a dict to preserve order - self._resource_list = [] # type: List[Dict[str, Any]] - self._resource_dict = {} # type: Dict[str, Dict[str, Any]] - - def render(self, path: str, **kwargs: Any) -> bytes: # type: ignore - def set_resources(**kwargs) -> str: # type: ignore - if path not in self._resource_dict: - self._resource_list.append(kwargs) - self._resource_dict[path] = kwargs - else: - if self._resource_dict[path] != kwargs: - raise ValueError( - "set_resources called with different " - "resources for the same template" - ) - return "" - - return self.render_string(path, set_resources=set_resources, **kwargs) - - def _get_resources(self, key: str) -> Iterable[str]: - return (r[key] for r in self._resource_list if key in r) - - def embedded_javascript(self) -> str: - return "\n".join(self._get_resources("embedded_javascript")) - - def javascript_files(self) -> Iterable[str]: - result = [] - for f in self._get_resources("javascript_files"): - if isinstance(f, (unicode_type, bytes)): - result.append(f) - else: - result.extend(f) - return result - - def embedded_css(self) -> str: - return "\n".join(self._get_resources("embedded_css")) - - def css_files(self) -> Iterable[str]: - result = [] - for f in self._get_resources("css_files"): - if isinstance(f, (unicode_type, bytes)): - result.append(f) - else: - result.extend(f) - return result - - def html_head(self) -> str: - return "".join(self._get_resources("html_head")) - - def html_body(self) -> str: - return "".join(self._get_resources("html_body")) - - -class _UIModuleNamespace(object): - """Lazy namespace which creates UIModule proxies bound to a handler.""" - - def __init__( - self, handler: RequestHandler, ui_modules: Dict[str, Type[UIModule]] - ) -> None: - self.handler = handler - self.ui_modules = ui_modules - - def __getitem__(self, key: str) -> Callable[..., str]: - return self.handler._ui_module(key, self.ui_modules[key]) - - def __getattr__(self, key: str) -> Callable[..., str]: - try: - return self[key] - except KeyError as e: - raise AttributeError(str(e)) - - -def create_signed_value( - secret: _CookieSecretTypes, - name: str, - value: Union[str, bytes], - version: Optional[int] = None, - clock: Optional[Callable[[], float]] = None, - key_version: Optional[int] = None, -) -> bytes: - if version is None: - version = DEFAULT_SIGNED_VALUE_VERSION - if clock is None: - clock = time.time - - timestamp = utf8(str(int(clock()))) - value = base64.b64encode(utf8(value)) - if version == 1: - assert not isinstance(secret, dict) - signature = _create_signature_v1(secret, name, value, timestamp) - value = b"|".join([value, timestamp, signature]) - return value - elif version == 2: - # The v2 format consists of a version number and a series of - # length-prefixed fields "%d:%s", the last of which is a - # signature, all separated by pipes. All numbers are in - # decimal format with no leading zeros. The signature is an - # HMAC-SHA256 of the whole string up to that point, including - # the final pipe. - # - # The fields are: - # - format version (i.e. 2; no length prefix) - # - key version (integer, default is 0) - # - timestamp (integer seconds since epoch) - # - name (not encoded; assumed to be ~alphanumeric) - # - value (base64-encoded) - # - signature (hex-encoded; no length prefix) - def format_field(s: Union[str, bytes]) -> bytes: - return utf8("%d:" % len(s)) + utf8(s) - - to_sign = b"|".join( - [ - b"2", - format_field(str(key_version or 0)), - format_field(timestamp), - format_field(name), - format_field(value), - b"", - ] - ) - - if isinstance(secret, dict): - assert ( - key_version is not None - ), "Key version must be set when sign key dict is used" - assert version >= 2, "Version must be at least 2 for key version support" - secret = secret[key_version] - - signature = _create_signature_v2(secret, to_sign) - return to_sign + signature - else: - raise ValueError("Unsupported version %d" % version) - - -# A leading version number in decimal -# with no leading zeros, followed by a pipe. -_signed_value_version_re = re.compile(br"^([1-9][0-9]*)\|(.*)$") - - -def _get_version(value: bytes) -> int: - # Figures out what version value is. Version 1 did not include an - # explicit version field and started with arbitrary base64 data, - # which makes this tricky. - m = _signed_value_version_re.match(value) - if m is None: - version = 1 - else: - try: - version = int(m.group(1)) - if version > 999: - # Certain payloads from the version-less v1 format may - # be parsed as valid integers. Due to base64 padding - # restrictions, this can only happen for numbers whose - # length is a multiple of 4, so we can treat all - # numbers up to 999 as versions, and for the rest we - # fall back to v1 format. - version = 1 - except ValueError: - version = 1 - return version - - -def decode_signed_value( - secret: _CookieSecretTypes, - name: str, - value: Union[None, str, bytes], - max_age_days: float = 31, - clock: Optional[Callable[[], float]] = None, - min_version: Optional[int] = None, -) -> Optional[bytes]: - if clock is None: - clock = time.time - if min_version is None: - min_version = DEFAULT_SIGNED_VALUE_MIN_VERSION - if min_version > 2: - raise ValueError("Unsupported min_version %d" % min_version) - if not value: - return None - - value = utf8(value) - version = _get_version(value) - - if version < min_version: - return None - if version == 1: - assert not isinstance(secret, dict) - return _decode_signed_value_v1(secret, name, value, max_age_days, clock) - elif version == 2: - return _decode_signed_value_v2(secret, name, value, max_age_days, clock) - else: - return None - - -def _decode_signed_value_v1( - secret: Union[str, bytes], - name: str, - value: bytes, - max_age_days: float, - clock: Callable[[], float], -) -> Optional[bytes]: - parts = utf8(value).split(b"|") - if len(parts) != 3: - return None - signature = _create_signature_v1(secret, name, parts[0], parts[1]) - if not hmac.compare_digest(parts[2], signature): - gen_log.warning("Invalid cookie signature %r", value) - return None - timestamp = int(parts[1]) - if timestamp < clock() - max_age_days * 86400: - gen_log.warning("Expired cookie %r", value) - return None - if timestamp > clock() + 31 * 86400: - # _cookie_signature does not hash a delimiter between the - # parts of the cookie, so an attacker could transfer trailing - # digits from the payload to the timestamp without altering the - # signature. For backwards compatibility, sanity-check timestamp - # here instead of modifying _cookie_signature. - gen_log.warning("Cookie timestamp in future; possible tampering %r", value) - return None - if parts[1].startswith(b"0"): - gen_log.warning("Tampered cookie %r", value) - return None - try: - return base64.b64decode(parts[0]) - except Exception: - return None - - -def _decode_fields_v2(value: bytes) -> Tuple[int, bytes, bytes, bytes, bytes]: - def _consume_field(s: bytes) -> Tuple[bytes, bytes]: - length, _, rest = s.partition(b":") - n = int(length) - field_value = rest[:n] - # In python 3, indexing bytes returns small integers; we must - # use a slice to get a byte string as in python 2. - if rest[n : n + 1] != b"|": - raise ValueError("malformed v2 signed value field") - rest = rest[n + 1 :] - return field_value, rest - - rest = value[2:] # remove version number - key_version, rest = _consume_field(rest) - timestamp, rest = _consume_field(rest) - name_field, rest = _consume_field(rest) - value_field, passed_sig = _consume_field(rest) - return int(key_version), timestamp, name_field, value_field, passed_sig - - -def _decode_signed_value_v2( - secret: _CookieSecretTypes, - name: str, - value: bytes, - max_age_days: float, - clock: Callable[[], float], -) -> Optional[bytes]: - try: - ( - key_version, - timestamp_bytes, - name_field, - value_field, - passed_sig, - ) = _decode_fields_v2(value) - except ValueError: - return None - signed_string = value[: -len(passed_sig)] - - if isinstance(secret, dict): - try: - secret = secret[key_version] - except KeyError: - return None - - expected_sig = _create_signature_v2(secret, signed_string) - if not hmac.compare_digest(passed_sig, expected_sig): - return None - if name_field != utf8(name): - return None - timestamp = int(timestamp_bytes) - if timestamp < clock() - max_age_days * 86400: - # The signature has expired. - return None - try: - return base64.b64decode(value_field) - except Exception: - return None - - -def get_signature_key_version(value: Union[str, bytes]) -> Optional[int]: - value = utf8(value) - version = _get_version(value) - if version < 2: - return None - try: - key_version, _, _, _, _ = _decode_fields_v2(value) - except ValueError: - return None - - return key_version - - -def _create_signature_v1(secret: Union[str, bytes], *parts: Union[str, bytes]) -> bytes: - hash = hmac.new(utf8(secret), digestmod=hashlib.sha1) - for part in parts: - hash.update(utf8(part)) - return utf8(hash.hexdigest()) - - -def _create_signature_v2(secret: Union[str, bytes], s: bytes) -> bytes: - hash = hmac.new(utf8(secret), digestmod=hashlib.sha256) - hash.update(utf8(s)) - return utf8(hash.hexdigest()) - - -def is_absolute(path: str) -> bool: - return any(path.startswith(x) for x in ["/", "http:", "https:"]) diff --git a/telegramer/include/tornado/websocket.py b/telegramer/include/tornado/websocket.py deleted file mode 100644 index eef49e7..0000000 --- a/telegramer/include/tornado/websocket.py +++ /dev/null @@ -1,1666 +0,0 @@ -"""Implementation of the WebSocket protocol. - -`WebSockets <http://dev.w3.org/html5/websockets/>`_ allow for bidirectional -communication between the browser and server. - -WebSockets are supported in the current versions of all major browsers, -although older versions that do not support WebSockets are still in use -(refer to http://caniuse.com/websockets for details). - -This module implements the final version of the WebSocket protocol as -defined in `RFC 6455 <http://tools.ietf.org/html/rfc6455>`_. Certain -browser versions (notably Safari 5.x) implemented an earlier draft of -the protocol (known as "draft 76") and are not compatible with this module. - -.. versionchanged:: 4.0 - Removed support for the draft 76 protocol version. -""" - -import abc -import asyncio -import base64 -import hashlib -import os -import sys -import struct -import tornado.escape -import tornado.web -from urllib.parse import urlparse -import zlib - -from tornado.concurrent import Future, future_set_result_unless_cancelled -from tornado.escape import utf8, native_str, to_unicode -from tornado import gen, httpclient, httputil -from tornado.ioloop import IOLoop, PeriodicCallback -from tornado.iostream import StreamClosedError, IOStream -from tornado.log import gen_log, app_log -from tornado import simple_httpclient -from tornado.queues import Queue -from tornado.tcpclient import TCPClient -from tornado.util import _websocket_mask - -from typing import ( - TYPE_CHECKING, - cast, - Any, - Optional, - Dict, - Union, - List, - Awaitable, - Callable, - Tuple, - Type, -) -from types import TracebackType - -if TYPE_CHECKING: - from typing_extensions import Protocol - - # The zlib compressor types aren't actually exposed anywhere - # publicly, so declare protocols for the portions we use. - class _Compressor(Protocol): - def compress(self, data: bytes) -> bytes: - pass - - def flush(self, mode: int) -> bytes: - pass - - class _Decompressor(Protocol): - unconsumed_tail = b"" # type: bytes - - def decompress(self, data: bytes, max_length: int) -> bytes: - pass - - class _WebSocketDelegate(Protocol): - # The common base interface implemented by WebSocketHandler on - # the server side and WebSocketClientConnection on the client - # side. - def on_ws_connection_close( - self, close_code: Optional[int] = None, close_reason: Optional[str] = None - ) -> None: - pass - - def on_message(self, message: Union[str, bytes]) -> Optional["Awaitable[None]"]: - pass - - def on_ping(self, data: bytes) -> None: - pass - - def on_pong(self, data: bytes) -> None: - pass - - def log_exception( - self, - typ: Optional[Type[BaseException]], - value: Optional[BaseException], - tb: Optional[TracebackType], - ) -> None: - pass - - -_default_max_message_size = 10 * 1024 * 1024 - - -class WebSocketError(Exception): - pass - - -class WebSocketClosedError(WebSocketError): - """Raised by operations on a closed connection. - - .. versionadded:: 3.2 - """ - - pass - - -class _DecompressTooLargeError(Exception): - pass - - -class _WebSocketParams(object): - def __init__( - self, - ping_interval: Optional[float] = None, - ping_timeout: Optional[float] = None, - max_message_size: int = _default_max_message_size, - compression_options: Optional[Dict[str, Any]] = None, - ) -> None: - self.ping_interval = ping_interval - self.ping_timeout = ping_timeout - self.max_message_size = max_message_size - self.compression_options = compression_options - - -class WebSocketHandler(tornado.web.RequestHandler): - """Subclass this class to create a basic WebSocket handler. - - Override `on_message` to handle incoming messages, and use - `write_message` to send messages to the client. You can also - override `open` and `on_close` to handle opened and closed - connections. - - Custom upgrade response headers can be sent by overriding - `~tornado.web.RequestHandler.set_default_headers` or - `~tornado.web.RequestHandler.prepare`. - - See http://dev.w3.org/html5/websockets/ for details on the - JavaScript interface. The protocol is specified at - http://tools.ietf.org/html/rfc6455. - - Here is an example WebSocket handler that echos back all received messages - back to the client: - - .. testcode:: - - class EchoWebSocket(tornado.websocket.WebSocketHandler): - def open(self): - print("WebSocket opened") - - def on_message(self, message): - self.write_message(u"You said: " + message) - - def on_close(self): - print("WebSocket closed") - - .. testoutput:: - :hide: - - WebSockets are not standard HTTP connections. The "handshake" is - HTTP, but after the handshake, the protocol is - message-based. Consequently, most of the Tornado HTTP facilities - are not available in handlers of this type. The only communication - methods available to you are `write_message()`, `ping()`, and - `close()`. Likewise, your request handler class should implement - `open()` method rather than ``get()`` or ``post()``. - - If you map the handler above to ``/websocket`` in your application, you can - invoke it in JavaScript with:: - - var ws = new WebSocket("ws://localhost:8888/websocket"); - ws.onopen = function() { - ws.send("Hello, world"); - }; - ws.onmessage = function (evt) { - alert(evt.data); - }; - - This script pops up an alert box that says "You said: Hello, world". - - Web browsers allow any site to open a websocket connection to any other, - instead of using the same-origin policy that governs other network - access from JavaScript. This can be surprising and is a potential - security hole, so since Tornado 4.0 `WebSocketHandler` requires - applications that wish to receive cross-origin websockets to opt in - by overriding the `~WebSocketHandler.check_origin` method (see that - method's docs for details). Failure to do so is the most likely - cause of 403 errors when making a websocket connection. - - When using a secure websocket connection (``wss://``) with a self-signed - certificate, the connection from a browser may fail because it wants - to show the "accept this certificate" dialog but has nowhere to show it. - You must first visit a regular HTML page using the same certificate - to accept it before the websocket connection will succeed. - - If the application setting ``websocket_ping_interval`` has a non-zero - value, a ping will be sent periodically, and the connection will be - closed if a response is not received before the ``websocket_ping_timeout``. - - Messages larger than the ``websocket_max_message_size`` application setting - (default 10MiB) will not be accepted. - - .. versionchanged:: 4.5 - Added ``websocket_ping_interval``, ``websocket_ping_timeout``, and - ``websocket_max_message_size``. - """ - - def __init__( - self, - application: tornado.web.Application, - request: httputil.HTTPServerRequest, - **kwargs: Any - ) -> None: - super().__init__(application, request, **kwargs) - self.ws_connection = None # type: Optional[WebSocketProtocol] - self.close_code = None # type: Optional[int] - self.close_reason = None # type: Optional[str] - self.stream = None # type: Optional[IOStream] - self._on_close_called = False - - async def get(self, *args: Any, **kwargs: Any) -> None: - self.open_args = args - self.open_kwargs = kwargs - - # Upgrade header should be present and should be equal to WebSocket - if self.request.headers.get("Upgrade", "").lower() != "websocket": - self.set_status(400) - log_msg = 'Can "Upgrade" only to "WebSocket".' - self.finish(log_msg) - gen_log.debug(log_msg) - return - - # Connection header should be upgrade. - # Some proxy servers/load balancers - # might mess with it. - headers = self.request.headers - connection = map( - lambda s: s.strip().lower(), headers.get("Connection", "").split(",") - ) - if "upgrade" not in connection: - self.set_status(400) - log_msg = '"Connection" must be "Upgrade".' - self.finish(log_msg) - gen_log.debug(log_msg) - return - - # Handle WebSocket Origin naming convention differences - # The difference between version 8 and 13 is that in 8 the - # client sends a "Sec-Websocket-Origin" header and in 13 it's - # simply "Origin". - if "Origin" in self.request.headers: - origin = self.request.headers.get("Origin") - else: - origin = self.request.headers.get("Sec-Websocket-Origin", None) - - # If there was an origin header, check to make sure it matches - # according to check_origin. When the origin is None, we assume it - # did not come from a browser and that it can be passed on. - if origin is not None and not self.check_origin(origin): - self.set_status(403) - log_msg = "Cross origin websockets not allowed" - self.finish(log_msg) - gen_log.debug(log_msg) - return - - self.ws_connection = self.get_websocket_protocol() - if self.ws_connection: - await self.ws_connection.accept_connection(self) - else: - self.set_status(426, "Upgrade Required") - self.set_header("Sec-WebSocket-Version", "7, 8, 13") - - @property - def ping_interval(self) -> Optional[float]: - """The interval for websocket keep-alive pings. - - Set websocket_ping_interval = 0 to disable pings. - """ - return self.settings.get("websocket_ping_interval", None) - - @property - def ping_timeout(self) -> Optional[float]: - """If no ping is received in this many seconds, - close the websocket connection (VPNs, etc. can fail to cleanly close ws connections). - Default is max of 3 pings or 30 seconds. - """ - return self.settings.get("websocket_ping_timeout", None) - - @property - def max_message_size(self) -> int: - """Maximum allowed message size. - - If the remote peer sends a message larger than this, the connection - will be closed. - - Default is 10MiB. - """ - return self.settings.get( - "websocket_max_message_size", _default_max_message_size - ) - - def write_message( - self, message: Union[bytes, str, Dict[str, Any]], binary: bool = False - ) -> "Future[None]": - """Sends the given message to the client of this Web Socket. - - The message may be either a string or a dict (which will be - encoded as json). If the ``binary`` argument is false, the - message will be sent as utf8; in binary mode any byte string - is allowed. - - If the connection is already closed, raises `WebSocketClosedError`. - Returns a `.Future` which can be used for flow control. - - .. versionchanged:: 3.2 - `WebSocketClosedError` was added (previously a closed connection - would raise an `AttributeError`) - - .. versionchanged:: 4.3 - Returns a `.Future` which can be used for flow control. - - .. versionchanged:: 5.0 - Consistently raises `WebSocketClosedError`. Previously could - sometimes raise `.StreamClosedError`. - """ - if self.ws_connection is None or self.ws_connection.is_closing(): - raise WebSocketClosedError() - if isinstance(message, dict): - message = tornado.escape.json_encode(message) - return self.ws_connection.write_message(message, binary=binary) - - def select_subprotocol(self, subprotocols: List[str]) -> Optional[str]: - """Override to implement subprotocol negotiation. - - ``subprotocols`` is a list of strings identifying the - subprotocols proposed by the client. This method may be - overridden to return one of those strings to select it, or - ``None`` to not select a subprotocol. - - Failure to select a subprotocol does not automatically abort - the connection, although clients may close the connection if - none of their proposed subprotocols was selected. - - The list may be empty, in which case this method must return - None. This method is always called exactly once even if no - subprotocols were proposed so that the handler can be advised - of this fact. - - .. versionchanged:: 5.1 - - Previously, this method was called with a list containing - an empty string instead of an empty list if no subprotocols - were proposed by the client. - """ - return None - - @property - def selected_subprotocol(self) -> Optional[str]: - """The subprotocol returned by `select_subprotocol`. - - .. versionadded:: 5.1 - """ - assert self.ws_connection is not None - return self.ws_connection.selected_subprotocol - - def get_compression_options(self) -> Optional[Dict[str, Any]]: - """Override to return compression options for the connection. - - If this method returns None (the default), compression will - be disabled. If it returns a dict (even an empty one), it - will be enabled. The contents of the dict may be used to - control the following compression options: - - ``compression_level`` specifies the compression level. - - ``mem_level`` specifies the amount of memory used for the internal compression state. - - These parameters are documented in details here: - https://docs.python.org/3.6/library/zlib.html#zlib.compressobj - - .. versionadded:: 4.1 - - .. versionchanged:: 4.5 - - Added ``compression_level`` and ``mem_level``. - """ - # TODO: Add wbits option. - return None - - def open(self, *args: str, **kwargs: str) -> Optional[Awaitable[None]]: - """Invoked when a new WebSocket is opened. - - The arguments to `open` are extracted from the `tornado.web.URLSpec` - regular expression, just like the arguments to - `tornado.web.RequestHandler.get`. - - `open` may be a coroutine. `on_message` will not be called until - `open` has returned. - - .. versionchanged:: 5.1 - - ``open`` may be a coroutine. - """ - pass - - def on_message(self, message: Union[str, bytes]) -> Optional[Awaitable[None]]: - """Handle incoming messages on the WebSocket - - This method must be overridden. - - .. versionchanged:: 4.5 - - ``on_message`` can be a coroutine. - """ - raise NotImplementedError - - def ping(self, data: Union[str, bytes] = b"") -> None: - """Send ping frame to the remote end. - - The data argument allows a small amount of data (up to 125 - bytes) to be sent as a part of the ping message. Note that not - all websocket implementations expose this data to - applications. - - Consider using the ``websocket_ping_interval`` application - setting instead of sending pings manually. - - .. versionchanged:: 5.1 - - The data argument is now optional. - - """ - data = utf8(data) - if self.ws_connection is None or self.ws_connection.is_closing(): - raise WebSocketClosedError() - self.ws_connection.write_ping(data) - - def on_pong(self, data: bytes) -> None: - """Invoked when the response to a ping frame is received.""" - pass - - def on_ping(self, data: bytes) -> None: - """Invoked when the a ping frame is received.""" - pass - - def on_close(self) -> None: - """Invoked when the WebSocket is closed. - - If the connection was closed cleanly and a status code or reason - phrase was supplied, these values will be available as the attributes - ``self.close_code`` and ``self.close_reason``. - - .. versionchanged:: 4.0 - - Added ``close_code`` and ``close_reason`` attributes. - """ - pass - - def close(self, code: Optional[int] = None, reason: Optional[str] = None) -> None: - """Closes this Web Socket. - - Once the close handshake is successful the socket will be closed. - - ``code`` may be a numeric status code, taken from the values - defined in `RFC 6455 section 7.4.1 - <https://tools.ietf.org/html/rfc6455#section-7.4.1>`_. - ``reason`` may be a textual message about why the connection is - closing. These values are made available to the client, but are - not otherwise interpreted by the websocket protocol. - - .. versionchanged:: 4.0 - - Added the ``code`` and ``reason`` arguments. - """ - if self.ws_connection: - self.ws_connection.close(code, reason) - self.ws_connection = None - - def check_origin(self, origin: str) -> bool: - """Override to enable support for allowing alternate origins. - - The ``origin`` argument is the value of the ``Origin`` HTTP - header, the url responsible for initiating this request. This - method is not called for clients that do not send this header; - such requests are always allowed (because all browsers that - implement WebSockets support this header, and non-browser - clients do not have the same cross-site security concerns). - - Should return ``True`` to accept the request or ``False`` to - reject it. By default, rejects all requests with an origin on - a host other than this one. - - This is a security protection against cross site scripting attacks on - browsers, since WebSockets are allowed to bypass the usual same-origin - policies and don't use CORS headers. - - .. warning:: - - This is an important security measure; don't disable it - without understanding the security implications. In - particular, if your authentication is cookie-based, you - must either restrict the origins allowed by - ``check_origin()`` or implement your own XSRF-like - protection for websocket connections. See `these - <https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html>`_ - `articles - <https://devcenter.heroku.com/articles/websocket-security>`_ - for more. - - To accept all cross-origin traffic (which was the default prior to - Tornado 4.0), simply override this method to always return ``True``:: - - def check_origin(self, origin): - return True - - To allow connections from any subdomain of your site, you might - do something like:: - - def check_origin(self, origin): - parsed_origin = urllib.parse.urlparse(origin) - return parsed_origin.netloc.endswith(".mydomain.com") - - .. versionadded:: 4.0 - - """ - parsed_origin = urlparse(origin) - origin = parsed_origin.netloc - origin = origin.lower() - - host = self.request.headers.get("Host") - - # Check to see that origin matches host directly, including ports - return origin == host - - def set_nodelay(self, value: bool) -> None: - """Set the no-delay flag for this stream. - - By default, small messages may be delayed and/or combined to minimize - the number of packets sent. This can sometimes cause 200-500ms delays - due to the interaction between Nagle's algorithm and TCP delayed - ACKs. To reduce this delay (at the expense of possibly increasing - bandwidth usage), call ``self.set_nodelay(True)`` once the websocket - connection is established. - - See `.BaseIOStream.set_nodelay` for additional details. - - .. versionadded:: 3.1 - """ - assert self.ws_connection is not None - self.ws_connection.set_nodelay(value) - - def on_connection_close(self) -> None: - if self.ws_connection: - self.ws_connection.on_connection_close() - self.ws_connection = None - if not self._on_close_called: - self._on_close_called = True - self.on_close() - self._break_cycles() - - def on_ws_connection_close( - self, close_code: Optional[int] = None, close_reason: Optional[str] = None - ) -> None: - self.close_code = close_code - self.close_reason = close_reason - self.on_connection_close() - - def _break_cycles(self) -> None: - # WebSocketHandlers call finish() early, but we don't want to - # break up reference cycles (which makes it impossible to call - # self.render_string) until after we've really closed the - # connection (if it was established in the first place, - # indicated by status code 101). - if self.get_status() != 101 or self._on_close_called: - super()._break_cycles() - - def send_error(self, *args: Any, **kwargs: Any) -> None: - if self.stream is None: - super().send_error(*args, **kwargs) - else: - # If we get an uncaught exception during the handshake, - # we have no choice but to abruptly close the connection. - # TODO: for uncaught exceptions after the handshake, - # we can close the connection more gracefully. - self.stream.close() - - def get_websocket_protocol(self) -> Optional["WebSocketProtocol"]: - websocket_version = self.request.headers.get("Sec-WebSocket-Version") - if websocket_version in ("7", "8", "13"): - params = _WebSocketParams( - ping_interval=self.ping_interval, - ping_timeout=self.ping_timeout, - max_message_size=self.max_message_size, - compression_options=self.get_compression_options(), - ) - return WebSocketProtocol13(self, False, params) - return None - - def _detach_stream(self) -> IOStream: - # disable non-WS methods - for method in [ - "write", - "redirect", - "set_header", - "set_cookie", - "set_status", - "flush", - "finish", - ]: - setattr(self, method, _raise_not_supported_for_websockets) - return self.detach() - - -def _raise_not_supported_for_websockets(*args: Any, **kwargs: Any) -> None: - raise RuntimeError("Method not supported for Web Sockets") - - -class WebSocketProtocol(abc.ABC): - """Base class for WebSocket protocol versions. - """ - - def __init__(self, handler: "_WebSocketDelegate") -> None: - self.handler = handler - self.stream = None # type: Optional[IOStream] - self.client_terminated = False - self.server_terminated = False - - def _run_callback( - self, callback: Callable, *args: Any, **kwargs: Any - ) -> "Optional[Future[Any]]": - """Runs the given callback with exception handling. - - If the callback is a coroutine, returns its Future. On error, aborts the - websocket connection and returns None. - """ - try: - result = callback(*args, **kwargs) - except Exception: - self.handler.log_exception(*sys.exc_info()) - self._abort() - return None - else: - if result is not None: - result = gen.convert_yielded(result) - assert self.stream is not None - self.stream.io_loop.add_future(result, lambda f: f.result()) - return result - - def on_connection_close(self) -> None: - self._abort() - - def _abort(self) -> None: - """Instantly aborts the WebSocket connection by closing the socket""" - self.client_terminated = True - self.server_terminated = True - if self.stream is not None: - self.stream.close() # forcibly tear down the connection - self.close() # let the subclass cleanup - - @abc.abstractmethod - def close(self, code: Optional[int] = None, reason: Optional[str] = None) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def is_closing(self) -> bool: - raise NotImplementedError() - - @abc.abstractmethod - async def accept_connection(self, handler: WebSocketHandler) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def write_message( - self, message: Union[str, bytes], binary: bool = False - ) -> "Future[None]": - raise NotImplementedError() - - @property - @abc.abstractmethod - def selected_subprotocol(self) -> Optional[str]: - raise NotImplementedError() - - @abc.abstractmethod - def write_ping(self, data: bytes) -> None: - raise NotImplementedError() - - # The entry points below are used by WebSocketClientConnection, - # which was introduced after we only supported a single version of - # WebSocketProtocol. The WebSocketProtocol/WebSocketProtocol13 - # boundary is currently pretty ad-hoc. - @abc.abstractmethod - def _process_server_headers( - self, key: Union[str, bytes], headers: httputil.HTTPHeaders - ) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def start_pinging(self) -> None: - raise NotImplementedError() - - @abc.abstractmethod - async def _receive_frame_loop(self) -> None: - raise NotImplementedError() - - @abc.abstractmethod - def set_nodelay(self, x: bool) -> None: - raise NotImplementedError() - - -class _PerMessageDeflateCompressor(object): - def __init__( - self, - persistent: bool, - max_wbits: Optional[int], - compression_options: Optional[Dict[str, Any]] = None, - ) -> None: - if max_wbits is None: - max_wbits = zlib.MAX_WBITS - # There is no symbolic constant for the minimum wbits value. - if not (8 <= max_wbits <= zlib.MAX_WBITS): - raise ValueError( - "Invalid max_wbits value %r; allowed range 8-%d", - max_wbits, - zlib.MAX_WBITS, - ) - self._max_wbits = max_wbits - - if ( - compression_options is None - or "compression_level" not in compression_options - ): - self._compression_level = tornado.web.GZipContentEncoding.GZIP_LEVEL - else: - self._compression_level = compression_options["compression_level"] - - if compression_options is None or "mem_level" not in compression_options: - self._mem_level = 8 - else: - self._mem_level = compression_options["mem_level"] - - if persistent: - self._compressor = self._create_compressor() # type: Optional[_Compressor] - else: - self._compressor = None - - def _create_compressor(self) -> "_Compressor": - return zlib.compressobj( - self._compression_level, zlib.DEFLATED, -self._max_wbits, self._mem_level - ) - - def compress(self, data: bytes) -> bytes: - compressor = self._compressor or self._create_compressor() - data = compressor.compress(data) + compressor.flush(zlib.Z_SYNC_FLUSH) - assert data.endswith(b"\x00\x00\xff\xff") - return data[:-4] - - -class _PerMessageDeflateDecompressor(object): - def __init__( - self, - persistent: bool, - max_wbits: Optional[int], - max_message_size: int, - compression_options: Optional[Dict[str, Any]] = None, - ) -> None: - self._max_message_size = max_message_size - if max_wbits is None: - max_wbits = zlib.MAX_WBITS - if not (8 <= max_wbits <= zlib.MAX_WBITS): - raise ValueError( - "Invalid max_wbits value %r; allowed range 8-%d", - max_wbits, - zlib.MAX_WBITS, - ) - self._max_wbits = max_wbits - if persistent: - self._decompressor = ( - self._create_decompressor() - ) # type: Optional[_Decompressor] - else: - self._decompressor = None - - def _create_decompressor(self) -> "_Decompressor": - return zlib.decompressobj(-self._max_wbits) - - def decompress(self, data: bytes) -> bytes: - decompressor = self._decompressor or self._create_decompressor() - result = decompressor.decompress( - data + b"\x00\x00\xff\xff", self._max_message_size - ) - if decompressor.unconsumed_tail: - raise _DecompressTooLargeError() - return result - - -class WebSocketProtocol13(WebSocketProtocol): - """Implementation of the WebSocket protocol from RFC 6455. - - This class supports versions 7 and 8 of the protocol in addition to the - final version 13. - """ - - # Bit masks for the first byte of a frame. - FIN = 0x80 - RSV1 = 0x40 - RSV2 = 0x20 - RSV3 = 0x10 - RSV_MASK = RSV1 | RSV2 | RSV3 - OPCODE_MASK = 0x0F - - stream = None # type: IOStream - - def __init__( - self, - handler: "_WebSocketDelegate", - mask_outgoing: bool, - params: _WebSocketParams, - ) -> None: - WebSocketProtocol.__init__(self, handler) - self.mask_outgoing = mask_outgoing - self.params = params - self._final_frame = False - self._frame_opcode = None - self._masked_frame = None - self._frame_mask = None # type: Optional[bytes] - self._frame_length = None - self._fragmented_message_buffer = None # type: Optional[bytes] - self._fragmented_message_opcode = None - self._waiting = None # type: object - self._compression_options = params.compression_options - self._decompressor = None # type: Optional[_PerMessageDeflateDecompressor] - self._compressor = None # type: Optional[_PerMessageDeflateCompressor] - self._frame_compressed = None # type: Optional[bool] - # The total uncompressed size of all messages received or sent. - # Unicode messages are encoded to utf8. - # Only for testing; subject to change. - self._message_bytes_in = 0 - self._message_bytes_out = 0 - # The total size of all packets received or sent. Includes - # the effect of compression, frame overhead, and control frames. - self._wire_bytes_in = 0 - self._wire_bytes_out = 0 - self.ping_callback = None # type: Optional[PeriodicCallback] - self.last_ping = 0.0 - self.last_pong = 0.0 - self.close_code = None # type: Optional[int] - self.close_reason = None # type: Optional[str] - - # Use a property for this to satisfy the abc. - @property - def selected_subprotocol(self) -> Optional[str]: - return self._selected_subprotocol - - @selected_subprotocol.setter - def selected_subprotocol(self, value: Optional[str]) -> None: - self._selected_subprotocol = value - - async def accept_connection(self, handler: WebSocketHandler) -> None: - try: - self._handle_websocket_headers(handler) - except ValueError: - handler.set_status(400) - log_msg = "Missing/Invalid WebSocket headers" - handler.finish(log_msg) - gen_log.debug(log_msg) - return - - try: - await self._accept_connection(handler) - except asyncio.CancelledError: - self._abort() - return - except ValueError: - gen_log.debug("Malformed WebSocket request received", exc_info=True) - self._abort() - return - - def _handle_websocket_headers(self, handler: WebSocketHandler) -> None: - """Verifies all invariant- and required headers - - If a header is missing or have an incorrect value ValueError will be - raised - """ - fields = ("Host", "Sec-Websocket-Key", "Sec-Websocket-Version") - if not all(map(lambda f: handler.request.headers.get(f), fields)): - raise ValueError("Missing/Invalid WebSocket headers") - - @staticmethod - def compute_accept_value(key: Union[str, bytes]) -> str: - """Computes the value for the Sec-WebSocket-Accept header, - given the value for Sec-WebSocket-Key. - """ - sha1 = hashlib.sha1() - sha1.update(utf8(key)) - sha1.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11") # Magic value - return native_str(base64.b64encode(sha1.digest())) - - def _challenge_response(self, handler: WebSocketHandler) -> str: - return WebSocketProtocol13.compute_accept_value( - cast(str, handler.request.headers.get("Sec-Websocket-Key")) - ) - - async def _accept_connection(self, handler: WebSocketHandler) -> None: - subprotocol_header = handler.request.headers.get("Sec-WebSocket-Protocol") - if subprotocol_header: - subprotocols = [s.strip() for s in subprotocol_header.split(",")] - else: - subprotocols = [] - self.selected_subprotocol = handler.select_subprotocol(subprotocols) - if self.selected_subprotocol: - assert self.selected_subprotocol in subprotocols - handler.set_header("Sec-WebSocket-Protocol", self.selected_subprotocol) - - extensions = self._parse_extensions_header(handler.request.headers) - for ext in extensions: - if ext[0] == "permessage-deflate" and self._compression_options is not None: - # TODO: negotiate parameters if compression_options - # specifies limits. - self._create_compressors("server", ext[1], self._compression_options) - if ( - "client_max_window_bits" in ext[1] - and ext[1]["client_max_window_bits"] is None - ): - # Don't echo an offered client_max_window_bits - # parameter with no value. - del ext[1]["client_max_window_bits"] - handler.set_header( - "Sec-WebSocket-Extensions", - httputil._encode_header("permessage-deflate", ext[1]), - ) - break - - handler.clear_header("Content-Type") - handler.set_status(101) - handler.set_header("Upgrade", "websocket") - handler.set_header("Connection", "Upgrade") - handler.set_header("Sec-WebSocket-Accept", self._challenge_response(handler)) - handler.finish() - - self.stream = handler._detach_stream() - - self.start_pinging() - try: - open_result = handler.open(*handler.open_args, **handler.open_kwargs) - if open_result is not None: - await open_result - except Exception: - handler.log_exception(*sys.exc_info()) - self._abort() - return - - await self._receive_frame_loop() - - def _parse_extensions_header( - self, headers: httputil.HTTPHeaders - ) -> List[Tuple[str, Dict[str, str]]]: - extensions = headers.get("Sec-WebSocket-Extensions", "") - if extensions: - return [httputil._parse_header(e.strip()) for e in extensions.split(",")] - return [] - - def _process_server_headers( - self, key: Union[str, bytes], headers: httputil.HTTPHeaders - ) -> None: - """Process the headers sent by the server to this client connection. - - 'key' is the websocket handshake challenge/response key. - """ - assert headers["Upgrade"].lower() == "websocket" - assert headers["Connection"].lower() == "upgrade" - accept = self.compute_accept_value(key) - assert headers["Sec-Websocket-Accept"] == accept - - extensions = self._parse_extensions_header(headers) - for ext in extensions: - if ext[0] == "permessage-deflate" and self._compression_options is not None: - self._create_compressors("client", ext[1]) - else: - raise ValueError("unsupported extension %r", ext) - - self.selected_subprotocol = headers.get("Sec-WebSocket-Protocol", None) - - def _get_compressor_options( - self, - side: str, - agreed_parameters: Dict[str, Any], - compression_options: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Any]: - """Converts a websocket agreed_parameters set to keyword arguments - for our compressor objects. - """ - options = dict( - persistent=(side + "_no_context_takeover") not in agreed_parameters - ) # type: Dict[str, Any] - wbits_header = agreed_parameters.get(side + "_max_window_bits", None) - if wbits_header is None: - options["max_wbits"] = zlib.MAX_WBITS - else: - options["max_wbits"] = int(wbits_header) - options["compression_options"] = compression_options - return options - - def _create_compressors( - self, - side: str, - agreed_parameters: Dict[str, Any], - compression_options: Optional[Dict[str, Any]] = None, - ) -> None: - # TODO: handle invalid parameters gracefully - allowed_keys = set( - [ - "server_no_context_takeover", - "client_no_context_takeover", - "server_max_window_bits", - "client_max_window_bits", - ] - ) - for key in agreed_parameters: - if key not in allowed_keys: - raise ValueError("unsupported compression parameter %r" % key) - other_side = "client" if (side == "server") else "server" - self._compressor = _PerMessageDeflateCompressor( - **self._get_compressor_options(side, agreed_parameters, compression_options) - ) - self._decompressor = _PerMessageDeflateDecompressor( - max_message_size=self.params.max_message_size, - **self._get_compressor_options( - other_side, agreed_parameters, compression_options - ) - ) - - def _write_frame( - self, fin: bool, opcode: int, data: bytes, flags: int = 0 - ) -> "Future[None]": - data_len = len(data) - if opcode & 0x8: - # All control frames MUST have a payload length of 125 - # bytes or less and MUST NOT be fragmented. - if not fin: - raise ValueError("control frames may not be fragmented") - if data_len > 125: - raise ValueError("control frame payloads may not exceed 125 bytes") - if fin: - finbit = self.FIN - else: - finbit = 0 - frame = struct.pack("B", finbit | opcode | flags) - if self.mask_outgoing: - mask_bit = 0x80 - else: - mask_bit = 0 - if data_len < 126: - frame += struct.pack("B", data_len | mask_bit) - elif data_len <= 0xFFFF: - frame += struct.pack("!BH", 126 | mask_bit, data_len) - else: - frame += struct.pack("!BQ", 127 | mask_bit, data_len) - if self.mask_outgoing: - mask = os.urandom(4) - data = mask + _websocket_mask(mask, data) - frame += data - self._wire_bytes_out += len(frame) - return self.stream.write(frame) - - def write_message( - self, message: Union[str, bytes], binary: bool = False - ) -> "Future[None]": - """Sends the given message to the client of this Web Socket.""" - if binary: - opcode = 0x2 - else: - opcode = 0x1 - message = tornado.escape.utf8(message) - assert isinstance(message, bytes) - self._message_bytes_out += len(message) - flags = 0 - if self._compressor: - message = self._compressor.compress(message) - flags |= self.RSV1 - # For historical reasons, write methods in Tornado operate in a semi-synchronous - # mode in which awaiting the Future they return is optional (But errors can - # still be raised). This requires us to go through an awkward dance here - # to transform the errors that may be returned while presenting the same - # semi-synchronous interface. - try: - fut = self._write_frame(True, opcode, message, flags=flags) - except StreamClosedError: - raise WebSocketClosedError() - - async def wrapper() -> None: - try: - await fut - except StreamClosedError: - raise WebSocketClosedError() - - return asyncio.ensure_future(wrapper()) - - def write_ping(self, data: bytes) -> None: - """Send ping frame.""" - assert isinstance(data, bytes) - self._write_frame(True, 0x9, data) - - async def _receive_frame_loop(self) -> None: - try: - while not self.client_terminated: - await self._receive_frame() - except StreamClosedError: - self._abort() - self.handler.on_ws_connection_close(self.close_code, self.close_reason) - - async def _read_bytes(self, n: int) -> bytes: - data = await self.stream.read_bytes(n) - self._wire_bytes_in += n - return data - - async def _receive_frame(self) -> None: - # Read the frame header. - data = await self._read_bytes(2) - header, mask_payloadlen = struct.unpack("BB", data) - is_final_frame = header & self.FIN - reserved_bits = header & self.RSV_MASK - opcode = header & self.OPCODE_MASK - opcode_is_control = opcode & 0x8 - if self._decompressor is not None and opcode != 0: - # Compression flag is present in the first frame's header, - # but we can't decompress until we have all the frames of - # the message. - self._frame_compressed = bool(reserved_bits & self.RSV1) - reserved_bits &= ~self.RSV1 - if reserved_bits: - # client is using as-yet-undefined extensions; abort - self._abort() - return - is_masked = bool(mask_payloadlen & 0x80) - payloadlen = mask_payloadlen & 0x7F - - # Parse and validate the length. - if opcode_is_control and payloadlen >= 126: - # control frames must have payload < 126 - self._abort() - return - if payloadlen < 126: - self._frame_length = payloadlen - elif payloadlen == 126: - data = await self._read_bytes(2) - payloadlen = struct.unpack("!H", data)[0] - elif payloadlen == 127: - data = await self._read_bytes(8) - payloadlen = struct.unpack("!Q", data)[0] - new_len = payloadlen - if self._fragmented_message_buffer is not None: - new_len += len(self._fragmented_message_buffer) - if new_len > self.params.max_message_size: - self.close(1009, "message too big") - self._abort() - return - - # Read the payload, unmasking if necessary. - if is_masked: - self._frame_mask = await self._read_bytes(4) - data = await self._read_bytes(payloadlen) - if is_masked: - assert self._frame_mask is not None - data = _websocket_mask(self._frame_mask, data) - - # Decide what to do with this frame. - if opcode_is_control: - # control frames may be interleaved with a series of fragmented - # data frames, so control frames must not interact with - # self._fragmented_* - if not is_final_frame: - # control frames must not be fragmented - self._abort() - return - elif opcode == 0: # continuation frame - if self._fragmented_message_buffer is None: - # nothing to continue - self._abort() - return - self._fragmented_message_buffer += data - if is_final_frame: - opcode = self._fragmented_message_opcode - data = self._fragmented_message_buffer - self._fragmented_message_buffer = None - else: # start of new data message - if self._fragmented_message_buffer is not None: - # can't start new message until the old one is finished - self._abort() - return - if not is_final_frame: - self._fragmented_message_opcode = opcode - self._fragmented_message_buffer = data - - if is_final_frame: - handled_future = self._handle_message(opcode, data) - if handled_future is not None: - await handled_future - - def _handle_message(self, opcode: int, data: bytes) -> "Optional[Future[None]]": - """Execute on_message, returning its Future if it is a coroutine.""" - if self.client_terminated: - return None - - if self._frame_compressed: - assert self._decompressor is not None - try: - data = self._decompressor.decompress(data) - except _DecompressTooLargeError: - self.close(1009, "message too big after decompression") - self._abort() - return None - - if opcode == 0x1: - # UTF-8 data - self._message_bytes_in += len(data) - try: - decoded = data.decode("utf-8") - except UnicodeDecodeError: - self._abort() - return None - return self._run_callback(self.handler.on_message, decoded) - elif opcode == 0x2: - # Binary data - self._message_bytes_in += len(data) - return self._run_callback(self.handler.on_message, data) - elif opcode == 0x8: - # Close - self.client_terminated = True - if len(data) >= 2: - self.close_code = struct.unpack(">H", data[:2])[0] - if len(data) > 2: - self.close_reason = to_unicode(data[2:]) - # Echo the received close code, if any (RFC 6455 section 5.5.1). - self.close(self.close_code) - elif opcode == 0x9: - # Ping - try: - self._write_frame(True, 0xA, data) - except StreamClosedError: - self._abort() - self._run_callback(self.handler.on_ping, data) - elif opcode == 0xA: - # Pong - self.last_pong = IOLoop.current().time() - return self._run_callback(self.handler.on_pong, data) - else: - self._abort() - return None - - def close(self, code: Optional[int] = None, reason: Optional[str] = None) -> None: - """Closes the WebSocket connection.""" - if not self.server_terminated: - if not self.stream.closed(): - if code is None and reason is not None: - code = 1000 # "normal closure" status code - if code is None: - close_data = b"" - else: - close_data = struct.pack(">H", code) - if reason is not None: - close_data += utf8(reason) - try: - self._write_frame(True, 0x8, close_data) - except StreamClosedError: - self._abort() - self.server_terminated = True - if self.client_terminated: - if self._waiting is not None: - self.stream.io_loop.remove_timeout(self._waiting) - self._waiting = None - self.stream.close() - elif self._waiting is None: - # Give the client a few seconds to complete a clean shutdown, - # otherwise just close the connection. - self._waiting = self.stream.io_loop.add_timeout( - self.stream.io_loop.time() + 5, self._abort - ) - if self.ping_callback: - self.ping_callback.stop() - self.ping_callback = None - - def is_closing(self) -> bool: - """Return ``True`` if this connection is closing. - - The connection is considered closing if either side has - initiated its closing handshake or if the stream has been - shut down uncleanly. - """ - return self.stream.closed() or self.client_terminated or self.server_terminated - - @property - def ping_interval(self) -> Optional[float]: - interval = self.params.ping_interval - if interval is not None: - return interval - return 0 - - @property - def ping_timeout(self) -> Optional[float]: - timeout = self.params.ping_timeout - if timeout is not None: - return timeout - assert self.ping_interval is not None - return max(3 * self.ping_interval, 30) - - def start_pinging(self) -> None: - """Start sending periodic pings to keep the connection alive""" - assert self.ping_interval is not None - if self.ping_interval > 0: - self.last_ping = self.last_pong = IOLoop.current().time() - self.ping_callback = PeriodicCallback( - self.periodic_ping, self.ping_interval * 1000 - ) - self.ping_callback.start() - - def periodic_ping(self) -> None: - """Send a ping to keep the websocket alive - - Called periodically if the websocket_ping_interval is set and non-zero. - """ - if self.is_closing() and self.ping_callback is not None: - self.ping_callback.stop() - return - - # Check for timeout on pong. Make sure that we really have - # sent a recent ping in case the machine with both server and - # client has been suspended since the last ping. - now = IOLoop.current().time() - since_last_pong = now - self.last_pong - since_last_ping = now - self.last_ping - assert self.ping_interval is not None - assert self.ping_timeout is not None - if ( - since_last_ping < 2 * self.ping_interval - and since_last_pong > self.ping_timeout - ): - self.close() - return - - self.write_ping(b"") - self.last_ping = now - - def set_nodelay(self, x: bool) -> None: - self.stream.set_nodelay(x) - - -class WebSocketClientConnection(simple_httpclient._HTTPConnection): - """WebSocket client connection. - - This class should not be instantiated directly; use the - `websocket_connect` function instead. - """ - - protocol = None # type: WebSocketProtocol - - def __init__( - self, - request: httpclient.HTTPRequest, - on_message_callback: Optional[Callable[[Union[None, str, bytes]], None]] = None, - compression_options: Optional[Dict[str, Any]] = None, - ping_interval: Optional[float] = None, - ping_timeout: Optional[float] = None, - max_message_size: int = _default_max_message_size, - subprotocols: Optional[List[str]] = [], - ) -> None: - self.connect_future = Future() # type: Future[WebSocketClientConnection] - self.read_queue = Queue(1) # type: Queue[Union[None, str, bytes]] - self.key = base64.b64encode(os.urandom(16)) - self._on_message_callback = on_message_callback - self.close_code = None # type: Optional[int] - self.close_reason = None # type: Optional[str] - self.params = _WebSocketParams( - ping_interval=ping_interval, - ping_timeout=ping_timeout, - max_message_size=max_message_size, - compression_options=compression_options, - ) - - scheme, sep, rest = request.url.partition(":") - scheme = {"ws": "http", "wss": "https"}[scheme] - request.url = scheme + sep + rest - request.headers.update( - { - "Upgrade": "websocket", - "Connection": "Upgrade", - "Sec-WebSocket-Key": self.key, - "Sec-WebSocket-Version": "13", - } - ) - if subprotocols is not None: - request.headers["Sec-WebSocket-Protocol"] = ",".join(subprotocols) - if compression_options is not None: - # Always offer to let the server set our max_wbits (and even though - # we don't offer it, we will accept a client_no_context_takeover - # from the server). - # TODO: set server parameters for deflate extension - # if requested in self.compression_options. - request.headers[ - "Sec-WebSocket-Extensions" - ] = "permessage-deflate; client_max_window_bits" - - # Websocket connection is currently unable to follow redirects - request.follow_redirects = False - - self.tcp_client = TCPClient() - super().__init__( - None, - request, - lambda: None, - self._on_http_response, - 104857600, - self.tcp_client, - 65536, - 104857600, - ) - - def close(self, code: Optional[int] = None, reason: Optional[str] = None) -> None: - """Closes the websocket connection. - - ``code`` and ``reason`` are documented under - `WebSocketHandler.close`. - - .. versionadded:: 3.2 - - .. versionchanged:: 4.0 - - Added the ``code`` and ``reason`` arguments. - """ - if self.protocol is not None: - self.protocol.close(code, reason) - self.protocol = None # type: ignore - - def on_connection_close(self) -> None: - if not self.connect_future.done(): - self.connect_future.set_exception(StreamClosedError()) - self._on_message(None) - self.tcp_client.close() - super().on_connection_close() - - def on_ws_connection_close( - self, close_code: Optional[int] = None, close_reason: Optional[str] = None - ) -> None: - self.close_code = close_code - self.close_reason = close_reason - self.on_connection_close() - - def _on_http_response(self, response: httpclient.HTTPResponse) -> None: - if not self.connect_future.done(): - if response.error: - self.connect_future.set_exception(response.error) - else: - self.connect_future.set_exception( - WebSocketError("Non-websocket response") - ) - - async def headers_received( - self, - start_line: Union[httputil.RequestStartLine, httputil.ResponseStartLine], - headers: httputil.HTTPHeaders, - ) -> None: - assert isinstance(start_line, httputil.ResponseStartLine) - if start_line.code != 101: - await super().headers_received(start_line, headers) - return - - if self._timeout is not None: - self.io_loop.remove_timeout(self._timeout) - self._timeout = None - - self.headers = headers - self.protocol = self.get_websocket_protocol() - self.protocol._process_server_headers(self.key, self.headers) - self.protocol.stream = self.connection.detach() - - IOLoop.current().add_callback(self.protocol._receive_frame_loop) - self.protocol.start_pinging() - - # Once we've taken over the connection, clear the final callback - # we set on the http request. This deactivates the error handling - # in simple_httpclient that would otherwise interfere with our - # ability to see exceptions. - self.final_callback = None # type: ignore - - future_set_result_unless_cancelled(self.connect_future, self) - - def write_message( - self, message: Union[str, bytes], binary: bool = False - ) -> "Future[None]": - """Sends a message to the WebSocket server. - - If the stream is closed, raises `WebSocketClosedError`. - Returns a `.Future` which can be used for flow control. - - .. versionchanged:: 5.0 - Exception raised on a closed stream changed from `.StreamClosedError` - to `WebSocketClosedError`. - """ - return self.protocol.write_message(message, binary=binary) - - def read_message( - self, - callback: Optional[Callable[["Future[Union[None, str, bytes]]"], None]] = None, - ) -> Awaitable[Union[None, str, bytes]]: - """Reads a message from the WebSocket server. - - If on_message_callback was specified at WebSocket - initialization, this function will never return messages - - Returns a future whose result is the message, or None - if the connection is closed. If a callback argument - is given it will be called with the future when it is - ready. - """ - - awaitable = self.read_queue.get() - if callback is not None: - self.io_loop.add_future(asyncio.ensure_future(awaitable), callback) - return awaitable - - def on_message(self, message: Union[str, bytes]) -> Optional[Awaitable[None]]: - return self._on_message(message) - - def _on_message( - self, message: Union[None, str, bytes] - ) -> Optional[Awaitable[None]]: - if self._on_message_callback: - self._on_message_callback(message) - return None - else: - return self.read_queue.put(message) - - def ping(self, data: bytes = b"") -> None: - """Send ping frame to the remote end. - - The data argument allows a small amount of data (up to 125 - bytes) to be sent as a part of the ping message. Note that not - all websocket implementations expose this data to - applications. - - Consider using the ``ping_interval`` argument to - `websocket_connect` instead of sending pings manually. - - .. versionadded:: 5.1 - - """ - data = utf8(data) - if self.protocol is None: - raise WebSocketClosedError() - self.protocol.write_ping(data) - - def on_pong(self, data: bytes) -> None: - pass - - def on_ping(self, data: bytes) -> None: - pass - - def get_websocket_protocol(self) -> WebSocketProtocol: - return WebSocketProtocol13(self, mask_outgoing=True, params=self.params) - - @property - def selected_subprotocol(self) -> Optional[str]: - """The subprotocol selected by the server. - - .. versionadded:: 5.1 - """ - return self.protocol.selected_subprotocol - - def log_exception( - self, - typ: "Optional[Type[BaseException]]", - value: Optional[BaseException], - tb: Optional[TracebackType], - ) -> None: - assert typ is not None - assert value is not None - app_log.error("Uncaught exception %s", value, exc_info=(typ, value, tb)) - - -def websocket_connect( - url: Union[str, httpclient.HTTPRequest], - callback: Optional[Callable[["Future[WebSocketClientConnection]"], None]] = None, - connect_timeout: Optional[float] = None, - on_message_callback: Optional[Callable[[Union[None, str, bytes]], None]] = None, - compression_options: Optional[Dict[str, Any]] = None, - ping_interval: Optional[float] = None, - ping_timeout: Optional[float] = None, - max_message_size: int = _default_max_message_size, - subprotocols: Optional[List[str]] = None, -) -> "Awaitable[WebSocketClientConnection]": - """Client-side websocket support. - - Takes a url and returns a Future whose result is a - `WebSocketClientConnection`. - - ``compression_options`` is interpreted in the same way as the - return value of `.WebSocketHandler.get_compression_options`. - - The connection supports two styles of operation. In the coroutine - style, the application typically calls - `~.WebSocketClientConnection.read_message` in a loop:: - - conn = yield websocket_connect(url) - while True: - msg = yield conn.read_message() - if msg is None: break - # Do something with msg - - In the callback style, pass an ``on_message_callback`` to - ``websocket_connect``. In both styles, a message of ``None`` - indicates that the connection has been closed. - - ``subprotocols`` may be a list of strings specifying proposed - subprotocols. The selected protocol may be found on the - ``selected_subprotocol`` attribute of the connection object - when the connection is complete. - - .. versionchanged:: 3.2 - Also accepts ``HTTPRequest`` objects in place of urls. - - .. versionchanged:: 4.1 - Added ``compression_options`` and ``on_message_callback``. - - .. versionchanged:: 4.5 - Added the ``ping_interval``, ``ping_timeout``, and ``max_message_size`` - arguments, which have the same meaning as in `WebSocketHandler`. - - .. versionchanged:: 5.0 - The ``io_loop`` argument (deprecated since version 4.1) has been removed. - - .. versionchanged:: 5.1 - Added the ``subprotocols`` argument. - """ - if isinstance(url, httpclient.HTTPRequest): - assert connect_timeout is None - request = url - # Copy and convert the headers dict/object (see comments in - # AsyncHTTPClient.fetch) - request.headers = httputil.HTTPHeaders(request.headers) - else: - request = httpclient.HTTPRequest(url, connect_timeout=connect_timeout) - request = cast( - httpclient.HTTPRequest, - httpclient._RequestProxy(request, httpclient.HTTPRequest._DEFAULTS), - ) - conn = WebSocketClientConnection( - request, - on_message_callback=on_message_callback, - compression_options=compression_options, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - max_message_size=max_message_size, - subprotocols=subprotocols, - ) - if callback is not None: - IOLoop.current().add_future(conn.connect_future, callback) - return conn.connect_future diff --git a/telegramer/include/tornado/wsgi.py b/telegramer/include/tornado/wsgi.py deleted file mode 100644 index 77124aa..0000000 --- a/telegramer/include/tornado/wsgi.py +++ /dev/null @@ -1,199 +0,0 @@ -# -# Copyright 2009 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""WSGI support for the Tornado web framework. - -WSGI is the Python standard for web servers, and allows for interoperability -between Tornado and other Python web frameworks and servers. - -This module provides WSGI support via the `WSGIContainer` class, which -makes it possible to run applications using other WSGI frameworks on -the Tornado HTTP server. The reverse is not supported; the Tornado -`.Application` and `.RequestHandler` classes are designed for use with -the Tornado `.HTTPServer` and cannot be used in a generic WSGI -container. - -""" - -import sys -from io import BytesIO -import tornado - -from tornado import escape -from tornado import httputil -from tornado.log import access_log - -from typing import List, Tuple, Optional, Callable, Any, Dict, Text -from types import TracebackType -import typing - -if typing.TYPE_CHECKING: - from typing import Type # noqa: F401 - from wsgiref.types import WSGIApplication as WSGIAppType # noqa: F401 - - -# PEP 3333 specifies that WSGI on python 3 generally deals with byte strings -# that are smuggled inside objects of type unicode (via the latin1 encoding). -# This function is like those in the tornado.escape module, but defined -# here to minimize the temptation to use it in non-wsgi contexts. -def to_wsgi_str(s: bytes) -> str: - assert isinstance(s, bytes) - return s.decode("latin1") - - -class WSGIContainer(object): - r"""Makes a WSGI-compatible function runnable on Tornado's HTTP server. - - .. warning:: - - WSGI is a *synchronous* interface, while Tornado's concurrency model - is based on single-threaded asynchronous execution. This means that - running a WSGI app with Tornado's `WSGIContainer` is *less scalable* - than running the same app in a multi-threaded WSGI server like - ``gunicorn`` or ``uwsgi``. Use `WSGIContainer` only when there are - benefits to combining Tornado and WSGI in the same process that - outweigh the reduced scalability. - - Wrap a WSGI function in a `WSGIContainer` and pass it to `.HTTPServer` to - run it. For example:: - - def simple_app(environ, start_response): - status = "200 OK" - response_headers = [("Content-type", "text/plain")] - start_response(status, response_headers) - return ["Hello world!\n"] - - container = tornado.wsgi.WSGIContainer(simple_app) - http_server = tornado.httpserver.HTTPServer(container) - http_server.listen(8888) - tornado.ioloop.IOLoop.current().start() - - This class is intended to let other frameworks (Django, web.py, etc) - run on the Tornado HTTP server and I/O loop. - - The `tornado.web.FallbackHandler` class is often useful for mixing - Tornado and WSGI apps in the same server. See - https://github.com/bdarnell/django-tornado-demo for a complete example. - """ - - def __init__(self, wsgi_application: "WSGIAppType") -> None: - self.wsgi_application = wsgi_application - - def __call__(self, request: httputil.HTTPServerRequest) -> None: - data = {} # type: Dict[str, Any] - response = [] # type: List[bytes] - - def start_response( - status: str, - headers: List[Tuple[str, str]], - exc_info: Optional[ - Tuple[ - "Optional[Type[BaseException]]", - Optional[BaseException], - Optional[TracebackType], - ] - ] = None, - ) -> Callable[[bytes], Any]: - data["status"] = status - data["headers"] = headers - return response.append - - app_response = self.wsgi_application( - WSGIContainer.environ(request), start_response - ) - try: - response.extend(app_response) - body = b"".join(response) - finally: - if hasattr(app_response, "close"): - app_response.close() # type: ignore - if not data: - raise Exception("WSGI app did not call start_response") - - status_code_str, reason = data["status"].split(" ", 1) - status_code = int(status_code_str) - headers = data["headers"] # type: List[Tuple[str, str]] - header_set = set(k.lower() for (k, v) in headers) - body = escape.utf8(body) - if status_code != 304: - if "content-length" not in header_set: - headers.append(("Content-Length", str(len(body)))) - if "content-type" not in header_set: - headers.append(("Content-Type", "text/html; charset=UTF-8")) - if "server" not in header_set: - headers.append(("Server", "TornadoServer/%s" % tornado.version)) - - start_line = httputil.ResponseStartLine("HTTP/1.1", status_code, reason) - header_obj = httputil.HTTPHeaders() - for key, value in headers: - header_obj.add(key, value) - assert request.connection is not None - request.connection.write_headers(start_line, header_obj, chunk=body) - request.connection.finish() - self._log(status_code, request) - - @staticmethod - def environ(request: httputil.HTTPServerRequest) -> Dict[Text, Any]: - """Converts a `tornado.httputil.HTTPServerRequest` to a WSGI environment. - """ - hostport = request.host.split(":") - if len(hostport) == 2: - host = hostport[0] - port = int(hostport[1]) - else: - host = request.host - port = 443 if request.protocol == "https" else 80 - environ = { - "REQUEST_METHOD": request.method, - "SCRIPT_NAME": "", - "PATH_INFO": to_wsgi_str( - escape.url_unescape(request.path, encoding=None, plus=False) - ), - "QUERY_STRING": request.query, - "REMOTE_ADDR": request.remote_ip, - "SERVER_NAME": host, - "SERVER_PORT": str(port), - "SERVER_PROTOCOL": request.version, - "wsgi.version": (1, 0), - "wsgi.url_scheme": request.protocol, - "wsgi.input": BytesIO(escape.utf8(request.body)), - "wsgi.errors": sys.stderr, - "wsgi.multithread": False, - "wsgi.multiprocess": True, - "wsgi.run_once": False, - } - if "Content-Type" in request.headers: - environ["CONTENT_TYPE"] = request.headers.pop("Content-Type") - if "Content-Length" in request.headers: - environ["CONTENT_LENGTH"] = request.headers.pop("Content-Length") - for key, value in request.headers.items(): - environ["HTTP_" + key.replace("-", "_").upper()] = value - return environ - - def _log(self, status_code: int, request: httputil.HTTPServerRequest) -> None: - if status_code < 400: - log_method = access_log.info - elif status_code < 500: - log_method = access_log.warning - else: - log_method = access_log.error - request_time = 1000.0 * request.request_time() - assert request.method is not None - assert request.uri is not None - summary = request.method + " " + request.uri + " (" + request.remote_ip + ")" - log_method("%d %s %.2fms", status_code, summary, request_time) - - -HTTPRequest = httputil.HTTPServerRequest diff --git a/telegramer/include/tzlocal/__init__.py b/telegramer/include/tzlocal/__init__.py deleted file mode 100644 index 98ed04f..0000000 --- a/telegramer/include/tzlocal/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys - -if sys.platform == "win32": - from tzlocal.win32 import ( - get_localzone, - get_localzone_name, - reload_localzone, - ) # pragma: no cover -else: - from tzlocal.unix import get_localzone, get_localzone_name, reload_localzone - - -__all__ = ["get_localzone", "get_localzone_name", "reload_localzone"] diff --git a/telegramer/include/tzlocal/unix.py b/telegramer/include/tzlocal/unix.py deleted file mode 100644 index eaf96d9..0000000 --- a/telegramer/include/tzlocal/unix.py +++ /dev/null @@ -1,215 +0,0 @@ -import os -import re -import sys -import warnings -from datetime import timezone -import pytz_deprecation_shim as pds - -from tzlocal import utils - -if sys.version_info >= (3, 9): - from zoneinfo import ZoneInfo # pragma: no cover -else: - from backports.zoneinfo import ZoneInfo # pragma: no cover - -_cache_tz = None -_cache_tz_name = None - - -def _get_localzone_name(_root="/"): - """Tries to find the local timezone configuration. - - This method finds the timezone name, if it can, or it returns None. - - The parameter _root makes the function look for files like /etc/localtime - beneath the _root directory. This is primarily used by the tests. - In normal usage you call the function without parameters.""" - - # First try the ENV setting. - tzenv = utils._tz_name_from_env() - if tzenv: - return tzenv - - # Are we under Termux on Android? - if os.path.exists(os.path.join(_root, "system/bin/getprop")): - import subprocess - - androidtz = ( - subprocess.check_output(["getprop", "persist.sys.timezone"]) - .strip() - .decode() - ) - return androidtz - - # Now look for distribution specific configuration files - # that contain the timezone name. - - # Stick all of them in a dict, to compare later. - found_configs = {} - - for configfile in ("etc/timezone", "var/db/zoneinfo"): - tzpath = os.path.join(_root, configfile) - try: - with open(tzpath, "rt") as tzfile: - data = tzfile.read() - - etctz = data.strip('/ \t\r\n') - if not etctz: - # Empty file, skip - continue - for etctz in etctz.splitlines(): - # Get rid of host definitions and comments: - if " " in etctz: - etctz, dummy = etctz.split(" ", 1) - if "#" in etctz: - etctz, dummy = etctz.split("#", 1) - if not etctz: - continue - - found_configs[tzpath] = etctz.replace(" ", "_") - - except (IOError, UnicodeDecodeError): - # File doesn't exist or is a directory, or it's a binary file. - continue - - # CentOS has a ZONE setting in /etc/sysconfig/clock, - # OpenSUSE has a TIMEZONE setting in /etc/sysconfig/clock and - # Gentoo has a TIMEZONE setting in /etc/conf.d/clock - # We look through these files for a timezone: - - zone_re = re.compile(r"\s*ZONE\s*=\s*\"") - timezone_re = re.compile(r"\s*TIMEZONE\s*=\s*\"") - end_re = re.compile('"') - - for filename in ("etc/sysconfig/clock", "etc/conf.d/clock"): - tzpath = os.path.join(_root, filename) - try: - with open(tzpath, "rt") as tzfile: - data = tzfile.readlines() - - for line in data: - # Look for the ZONE= setting. - match = zone_re.match(line) - if match is None: - # No ZONE= setting. Look for the TIMEZONE= setting. - match = timezone_re.match(line) - if match is not None: - # Some setting existed - line = line[match.end():] - etctz = line[: end_re.search(line).start()] - - # We found a timezone - found_configs[tzpath] = etctz.replace(" ", "_") - - except (IOError, UnicodeDecodeError): - # UnicodeDecode handles when clock is symlink to /etc/localtime - continue - - # systemd distributions use symlinks that include the zone name, - # see manpage of localtime(5) and timedatectl(1) - tzpath = os.path.join(_root, "etc/localtime") - if os.path.exists(tzpath) and os.path.islink(tzpath): - etctz = realtzpath = os.path.realpath(tzpath) - start = etctz.find("/") + 1 - while start != 0: - etctz = etctz[start:] - try: - pds.timezone(etctz) - tzinfo = f"{tzpath} is a symlink to" - found_configs[tzinfo] = etctz.replace(" ", "_") - except pds.UnknownTimeZoneError: - pass - start = etctz.find("/") + 1 - - if len(found_configs) > 0: - # We found some explicit config of some sort! - if len(found_configs) > 1: - # Uh-oh, multiple configs. See if they match: - unique_tzs = set() - zoneinfo = os.path.join(_root, "usr", "share", "zoneinfo") - directory_depth = len(zoneinfo.split(os.path.sep)) - - for tzname in found_configs.values(): - # Look them up in /usr/share/zoneinfo, and find what they - # really point to: - path = os.path.realpath(os.path.join(zoneinfo, *tzname.split("/"))) - real_zone_name = "/".join(path.split(os.path.sep)[directory_depth:]) - unique_tzs.add(real_zone_name) - - if len(unique_tzs) != 1: - message = "Multiple conflicting time zone configurations found:\n" - for key, value in found_configs.items(): - message += f"{key}: {value}\n" - message += "Fix the configuration, or set the time zone in a TZ environment variable.\n" - raise utils.ZoneInfoNotFoundError(message) - - # We found exactly one config! Use it. - return list(found_configs.values())[0] - - -def _get_localzone(_root="/"): - """Creates a timezone object from the timezone name. - - If there is no timezone config, it will try to create a file from the - localtime timezone, and if there isn't one, it will default to UTC. - - The parameter _root makes the function look for files like /etc/localtime - beneath the _root directory. This is primarily used by the tests. - In normal usage you call the function without parameters.""" - - # First try the ENV setting. - tzenv = utils._tz_from_env() - if tzenv: - return tzenv - - tzname = _get_localzone_name(_root) - if tzname is None: - # No explicit setting existed. Use localtime - for filename in ("etc/localtime", "usr/local/etc/localtime"): - tzpath = os.path.join(_root, filename) - - if not os.path.exists(tzpath): - continue - with open(tzpath, "rb") as tzfile: - tz = pds.wrap_zone(ZoneInfo.from_file(tzfile, key="local")) - break - else: - warnings.warn("Can not find any timezone configuration, defaulting to UTC.") - tz = timezone.utc - else: - tz = pds.timezone(tzname) - - if _root == "/": - # We are using a file in etc to name the timezone. - # Verify that the timezone specified there is actually used: - utils.assert_tz_offset(tz) - return tz - - -def get_localzone_name(): - """Get the computers configured local timezone name, if any.""" - global _cache_tz_name - if _cache_tz_name is None: - _cache_tz_name = _get_localzone_name() - - return _cache_tz_name - - -def get_localzone(): - """Get the computers configured local timezone, if any.""" - - global _cache_tz - if _cache_tz is None: - _cache_tz = _get_localzone() - - return _cache_tz - - -def reload_localzone(): - """Reload the cached localzone. You need to call this if the timezone has changed.""" - global _cache_tz_name - global _cache_tz - _cache_tz_name = _get_localzone_name() - _cache_tz = _get_localzone() - - return _cache_tz diff --git a/telegramer/include/tzlocal/utils.py b/telegramer/include/tzlocal/utils.py deleted file mode 100644 index d3f9242..0000000 --- a/telegramer/include/tzlocal/utils.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import time -import datetime -import calendar -import pytz_deprecation_shim as pds - -try: - import zoneinfo # pragma: no cover -except ImportError: - from backports import zoneinfo # pragma: no cover - -from tzlocal import windows_tz - - -class ZoneInfoNotFoundError(pds.UnknownTimeZoneError, zoneinfo.ZoneInfoNotFoundError): - """An exception derived from both pytz and zoneinfo - - This exception will be trappable both by pytz expecting clients and - zoneinfo expecting clients. - """ - - -def get_system_offset(): - """Get system's timezone offset using built-in library time. - - For the Timezone constants (altzone, daylight, timezone, and tzname), the - value is determined by the timezone rules in effect at module load time or - the last time tzset() is called and may be incorrect for times in the past. - - To keep compatibility with Windows, we're always importing time module here. - """ - - localtime = calendar.timegm(time.localtime()) - gmtime = calendar.timegm(time.gmtime()) - offset = gmtime - localtime - # We could get the localtime and gmtime on either side of a second switch - # so we check that the difference is less than one minute, because nobody - # has that small DST differences. - if abs(offset - time.altzone) < 60: - return -time.altzone # pragma: no cover - else: - return -time.timezone # pragma: no cover - - -def get_tz_offset(tz): - """Get timezone's offset using built-in function datetime.utcoffset().""" - return int(datetime.datetime.now(tz).utcoffset().total_seconds()) - - -def assert_tz_offset(tz): - """Assert that system's timezone offset equals to the timezone offset found. - - If they don't match, we probably have a misconfiguration, for example, an - incorrect timezone set in /etc/timezone file in systemd distributions.""" - tz_offset = get_tz_offset(tz) - system_offset = get_system_offset() - if tz_offset != system_offset: - msg = ( - "Timezone offset does not match system offset: {} != {}. " - "Please, check your config files." - ).format(tz_offset, system_offset) - raise ValueError(msg) - - -def _tz_name_from_env(tzenv=None): - if tzenv is None: - tzenv = os.environ.get("TZ") - - if not tzenv: - return None - - if tzenv in windows_tz.tz_win: - # Yup, it's a timezone - return tzenv - - if os.path.isabs(tzenv) and os.path.exists(tzenv): - # It's a file specification - parts = tzenv.split(os.sep) - - # Is it a zone info zone? - possible_tz = "/".join(parts[-2:]) - if possible_tz in windows_tz.tz_win: - # Yup, it is - return possible_tz - - # Maybe it's a short one, like UTC? - if parts[-1] in windows_tz.tz_win: - # Indeed - return parts[-1] - - -def _tz_from_env(tzenv=None): - if tzenv is None: - tzenv = os.environ.get("TZ") - - if not tzenv: - return None - - # Some weird format that exists: - if tzenv[0] == ":": - tzenv = tzenv[1:] - - # TZ specifies a file - if os.path.isabs(tzenv) and os.path.exists(tzenv): - # Try to see if we can figure out the name - tzname = _tz_name_from_env(tzenv) - if not tzname: - # Nope, not a standard timezone name, just take the filename - tzname = tzenv.split(os.sep)[-1] - with open(tzenv, "rb") as tzfile: - zone = zoneinfo.ZoneInfo.from_file(tzfile, key=tzname) - return pds.wrap_zone(zone) - - # TZ must specify a zoneinfo zone. - try: - tz = pds.timezone(tzenv) - # That worked, so we return this: - return tz - except pds.UnknownTimeZoneError: - # Nope, it's something like "PST4DST" etc, we can't handle that. - raise ZoneInfoNotFoundError( - "tzlocal() does not support non-zoneinfo timezones like %s. \n" - "Please use a timezone in the form of Continent/City" - ) from None diff --git a/telegramer/include/tzlocal/win32.py b/telegramer/include/tzlocal/win32.py deleted file mode 100644 index 720ab2b..0000000 --- a/telegramer/include/tzlocal/win32.py +++ /dev/null @@ -1,137 +0,0 @@ -from datetime import datetime -import pytz_deprecation_shim as pds - -try: - import _winreg as winreg -except ImportError: - import winreg - -from tzlocal.windows_tz import win_tz -from tzlocal import utils - -_cache_tz = None -_cache_tz_name = None - - -def valuestodict(key): - """Convert a registry key's values to a dictionary.""" - result = {} - size = winreg.QueryInfoKey(key)[1] - for i in range(size): - data = winreg.EnumValue(key, i) - result[data[0]] = data[1] - return result - - -def _get_dst_info(tz): - # Find the offset for when it doesn't have DST: - dst_offset = std_offset = None - has_dst = False - year = datetime.now().year - for dt in (datetime(year, 1, 1), datetime(year, 6, 1)): - if tz.dst(dt).total_seconds() == 0.0: - # OK, no DST during winter, get this offset - std_offset = tz.utcoffset(dt).total_seconds() - else: - has_dst = True - - return has_dst, std_offset, dst_offset - - -def _get_localzone_name(): - # Windows is special. It has unique time zone names (in several - # meanings of the word) available, but unfortunately, they can be - # translated to the language of the operating system, so we need to - # do a backwards lookup, by going through all time zones and see which - # one matches. - tzenv = utils._tz_name_from_env() - if tzenv: - return tzenv - - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - - TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - localtz = winreg.OpenKey(handle, TZLOCALKEYNAME) - keyvalues = valuestodict(localtz) - localtz.Close() - - if "TimeZoneKeyName" in keyvalues: - # Windows 7 and later - - # For some reason this returns a string with loads of NUL bytes at - # least on some systems. I don't know if this is a bug somewhere, I - # just work around it. - tzkeyname = keyvalues["TimeZoneKeyName"].split("\x00", 1)[0] - else: - # Don't support XP any longer - raise LookupError("Can not find Windows timezone configuration") - - timezone = win_tz.get(tzkeyname) - if timezone is None: - # Nope, that didn't work. Try adding "Standard Time", - # it seems to work a lot of times: - timezone = win_tz.get(tzkeyname + " Standard Time") - - # Return what we have. - if timezone is None: - raise utils.ZoneInfoNotFoundError(tzkeyname) - - if keyvalues.get("DynamicDaylightTimeDisabled", 0) == 1: - # DST is disabled, so don't return the timezone name, - # instead return Etc/GMT+offset - - tz = pds.timezone(timezone) - has_dst, std_offset, dst_offset = _get_dst_info(tz) - if not has_dst: - # The DST is turned off in the windows configuration, - # but this timezone doesn't have DST so it doesn't matter - return timezone - - if std_offset is None: - raise utils.ZoneInfoNotFoundError( - f"{tzkeyname} claims to not have a non-DST time!?") - - if std_offset % 3600: - # I can't convert this to an hourly offset - raise utils.ZoneInfoNotFoundError( - f"tzlocal can't support disabling DST in the {timezone} zone.") - - # This has whole hours as offset, return it as Etc/GMT - return f"Etc/GMT{-std_offset//3600:+.0f}" - - return timezone - - -def get_localzone_name(): - """Get the zoneinfo timezone name that matches the Windows-configured timezone.""" - global _cache_tz_name - if _cache_tz_name is None: - _cache_tz_name = _get_localzone_name() - - return _cache_tz_name - - -def get_localzone(): - """Returns the zoneinfo-based tzinfo object that matches the Windows-configured timezone.""" - - global _cache_tz - if _cache_tz is None: - _cache_tz = pds.timezone(get_localzone_name()) - - if not utils._tz_name_from_env(): - # If the timezone does NOT come from a TZ environment variable, - # verify that it's correct. If it's from the environment, - # we accept it, this is so you can run tests with different timezones. - utils.assert_tz_offset(_cache_tz) - - return _cache_tz - - -def reload_localzone(): - """Reload the cached localzone. You need to call this if the timezone has changed.""" - global _cache_tz - global _cache_tz_name - _cache_tz_name = _get_localzone_name() - _cache_tz = pds.timezone(_cache_tz_name) - utils.assert_tz_offset(_cache_tz) - return _cache_tz diff --git a/telegramer/include/tzlocal/windows_tz.py b/telegramer/include/tzlocal/windows_tz.py deleted file mode 100644 index 0d28503..0000000 --- a/telegramer/include/tzlocal/windows_tz.py +++ /dev/null @@ -1,699 +0,0 @@ -# This file is autogenerated by the update_windows_mapping.py script -# Do not edit. -win_tz = {'AUS Central Standard Time': 'Australia/Darwin', - 'AUS Eastern Standard Time': 'Australia/Sydney', - 'Afghanistan Standard Time': 'Asia/Kabul', - 'Alaskan Standard Time': 'America/Anchorage', - 'Aleutian Standard Time': 'America/Adak', - 'Altai Standard Time': 'Asia/Barnaul', - 'Arab Standard Time': 'Asia/Riyadh', - 'Arabian Standard Time': 'Asia/Dubai', - 'Arabic Standard Time': 'Asia/Baghdad', - 'Argentina Standard Time': 'America/Buenos_Aires', - 'Astrakhan Standard Time': 'Europe/Astrakhan', - 'Atlantic Standard Time': 'America/Halifax', - 'Aus Central W. Standard Time': 'Australia/Eucla', - 'Azerbaijan Standard Time': 'Asia/Baku', - 'Azores Standard Time': 'Atlantic/Azores', - 'Bahia Standard Time': 'America/Bahia', - 'Bangladesh Standard Time': 'Asia/Dhaka', - 'Belarus Standard Time': 'Europe/Minsk', - 'Bougainville Standard Time': 'Pacific/Bougainville', - 'Canada Central Standard Time': 'America/Regina', - 'Cape Verde Standard Time': 'Atlantic/Cape_Verde', - 'Caucasus Standard Time': 'Asia/Yerevan', - 'Cen. Australia Standard Time': 'Australia/Adelaide', - 'Central America Standard Time': 'America/Guatemala', - 'Central Asia Standard Time': 'Asia/Almaty', - 'Central Brazilian Standard Time': 'America/Cuiaba', - 'Central Europe Standard Time': 'Europe/Budapest', - 'Central European Standard Time': 'Europe/Warsaw', - 'Central Pacific Standard Time': 'Pacific/Guadalcanal', - 'Central Standard Time': 'America/Chicago', - 'Central Standard Time (Mexico)': 'America/Mexico_City', - 'Chatham Islands Standard Time': 'Pacific/Chatham', - 'China Standard Time': 'Asia/Shanghai', - 'Cuba Standard Time': 'America/Havana', - 'Dateline Standard Time': 'Etc/GMT+12', - 'E. Africa Standard Time': 'Africa/Nairobi', - 'E. Australia Standard Time': 'Australia/Brisbane', - 'E. Europe Standard Time': 'Europe/Chisinau', - 'E. South America Standard Time': 'America/Sao_Paulo', - 'Easter Island Standard Time': 'Pacific/Easter', - 'Eastern Standard Time': 'America/New_York', - 'Eastern Standard Time (Mexico)': 'America/Cancun', - 'Egypt Standard Time': 'Africa/Cairo', - 'Ekaterinburg Standard Time': 'Asia/Yekaterinburg', - 'FLE Standard Time': 'Europe/Kiev', - 'Fiji Standard Time': 'Pacific/Fiji', - 'GMT Standard Time': 'Europe/London', - 'GTB Standard Time': 'Europe/Bucharest', - 'Georgian Standard Time': 'Asia/Tbilisi', - 'Greenland Standard Time': 'America/Godthab', - 'Greenwich Standard Time': 'Atlantic/Reykjavik', - 'Haiti Standard Time': 'America/Port-au-Prince', - 'Hawaiian Standard Time': 'Pacific/Honolulu', - 'India Standard Time': 'Asia/Calcutta', - 'Iran Standard Time': 'Asia/Tehran', - 'Israel Standard Time': 'Asia/Jerusalem', - 'Jordan Standard Time': 'Asia/Amman', - 'Kaliningrad Standard Time': 'Europe/Kaliningrad', - 'Korea Standard Time': 'Asia/Seoul', - 'Libya Standard Time': 'Africa/Tripoli', - 'Line Islands Standard Time': 'Pacific/Kiritimati', - 'Lord Howe Standard Time': 'Australia/Lord_Howe', - 'Magadan Standard Time': 'Asia/Magadan', - 'Magallanes Standard Time': 'America/Punta_Arenas', - 'Marquesas Standard Time': 'Pacific/Marquesas', - 'Mauritius Standard Time': 'Indian/Mauritius', - 'Middle East Standard Time': 'Asia/Beirut', - 'Montevideo Standard Time': 'America/Montevideo', - 'Morocco Standard Time': 'Africa/Casablanca', - 'Mountain Standard Time': 'America/Denver', - 'Mountain Standard Time (Mexico)': 'America/Chihuahua', - 'Myanmar Standard Time': 'Asia/Rangoon', - 'N. Central Asia Standard Time': 'Asia/Novosibirsk', - 'Namibia Standard Time': 'Africa/Windhoek', - 'Nepal Standard Time': 'Asia/Katmandu', - 'New Zealand Standard Time': 'Pacific/Auckland', - 'Newfoundland Standard Time': 'America/St_Johns', - 'Norfolk Standard Time': 'Pacific/Norfolk', - 'North Asia East Standard Time': 'Asia/Irkutsk', - 'North Asia Standard Time': 'Asia/Krasnoyarsk', - 'North Korea Standard Time': 'Asia/Pyongyang', - 'Omsk Standard Time': 'Asia/Omsk', - 'Pacific SA Standard Time': 'America/Santiago', - 'Pacific Standard Time': 'America/Los_Angeles', - 'Pacific Standard Time (Mexico)': 'America/Tijuana', - 'Pakistan Standard Time': 'Asia/Karachi', - 'Paraguay Standard Time': 'America/Asuncion', - 'Qyzylorda Standard Time': 'Asia/Qyzylorda', - 'Romance Standard Time': 'Europe/Paris', - 'Russia Time Zone 10': 'Asia/Srednekolymsk', - 'Russia Time Zone 11': 'Asia/Kamchatka', - 'Russia Time Zone 3': 'Europe/Samara', - 'Russian Standard Time': 'Europe/Moscow', - 'SA Eastern Standard Time': 'America/Cayenne', - 'SA Pacific Standard Time': 'America/Bogota', - 'SA Western Standard Time': 'America/La_Paz', - 'SE Asia Standard Time': 'Asia/Bangkok', - 'Saint Pierre Standard Time': 'America/Miquelon', - 'Sakhalin Standard Time': 'Asia/Sakhalin', - 'Samoa Standard Time': 'Pacific/Apia', - 'Sao Tome Standard Time': 'Africa/Sao_Tome', - 'Saratov Standard Time': 'Europe/Saratov', - 'Singapore Standard Time': 'Asia/Singapore', - 'South Africa Standard Time': 'Africa/Johannesburg', - 'South Sudan Standard Time': 'Africa/Juba', - 'Sri Lanka Standard Time': 'Asia/Colombo', - 'Sudan Standard Time': 'Africa/Khartoum', - 'Syria Standard Time': 'Asia/Damascus', - 'Taipei Standard Time': 'Asia/Taipei', - 'Tasmania Standard Time': 'Australia/Hobart', - 'Tocantins Standard Time': 'America/Araguaina', - 'Tokyo Standard Time': 'Asia/Tokyo', - 'Tomsk Standard Time': 'Asia/Tomsk', - 'Tonga Standard Time': 'Pacific/Tongatapu', - 'Transbaikal Standard Time': 'Asia/Chita', - 'Turkey Standard Time': 'Europe/Istanbul', - 'Turks And Caicos Standard Time': 'America/Grand_Turk', - 'US Eastern Standard Time': 'America/Indianapolis', - 'US Mountain Standard Time': 'America/Phoenix', - 'UTC': 'Etc/UTC', - 'UTC+12': 'Etc/GMT-12', - 'UTC+13': 'Etc/GMT-13', - 'UTC-02': 'Etc/GMT+2', - 'UTC-08': 'Etc/GMT+8', - 'UTC-09': 'Etc/GMT+9', - 'UTC-11': 'Etc/GMT+11', - 'Ulaanbaatar Standard Time': 'Asia/Ulaanbaatar', - 'Venezuela Standard Time': 'America/Caracas', - 'Vladivostok Standard Time': 'Asia/Vladivostok', - 'Volgograd Standard Time': 'Europe/Volgograd', - 'W. Australia Standard Time': 'Australia/Perth', - 'W. Central Africa Standard Time': 'Africa/Lagos', - 'W. Europe Standard Time': 'Europe/Berlin', - 'W. Mongolia Standard Time': 'Asia/Hovd', - 'West Asia Standard Time': 'Asia/Tashkent', - 'West Bank Standard Time': 'Asia/Hebron', - 'West Pacific Standard Time': 'Pacific/Port_Moresby', - 'Yakutsk Standard Time': 'Asia/Yakutsk', - 'Yukon Standard Time': 'America/Whitehorse'} - -# Old name for the win_tz variable: -tz_names = win_tz - -tz_win = {'Africa/Abidjan': 'Greenwich Standard Time', - 'Africa/Accra': 'Greenwich Standard Time', - 'Africa/Addis_Ababa': 'E. Africa Standard Time', - 'Africa/Algiers': 'W. Central Africa Standard Time', - 'Africa/Asmera': 'E. Africa Standard Time', - 'Africa/Bamako': 'Greenwich Standard Time', - 'Africa/Bangui': 'W. Central Africa Standard Time', - 'Africa/Banjul': 'Greenwich Standard Time', - 'Africa/Bissau': 'Greenwich Standard Time', - 'Africa/Blantyre': 'South Africa Standard Time', - 'Africa/Brazzaville': 'W. Central Africa Standard Time', - 'Africa/Bujumbura': 'South Africa Standard Time', - 'Africa/Cairo': 'Egypt Standard Time', - 'Africa/Casablanca': 'Morocco Standard Time', - 'Africa/Ceuta': 'Romance Standard Time', - 'Africa/Conakry': 'Greenwich Standard Time', - 'Africa/Dakar': 'Greenwich Standard Time', - 'Africa/Dar_es_Salaam': 'E. Africa Standard Time', - 'Africa/Djibouti': 'E. Africa Standard Time', - 'Africa/Douala': 'W. Central Africa Standard Time', - 'Africa/El_Aaiun': 'Morocco Standard Time', - 'Africa/Freetown': 'Greenwich Standard Time', - 'Africa/Gaborone': 'South Africa Standard Time', - 'Africa/Harare': 'South Africa Standard Time', - 'Africa/Johannesburg': 'South Africa Standard Time', - 'Africa/Juba': 'South Sudan Standard Time', - 'Africa/Kampala': 'E. Africa Standard Time', - 'Africa/Khartoum': 'Sudan Standard Time', - 'Africa/Kigali': 'South Africa Standard Time', - 'Africa/Kinshasa': 'W. Central Africa Standard Time', - 'Africa/Lagos': 'W. Central Africa Standard Time', - 'Africa/Libreville': 'W. Central Africa Standard Time', - 'Africa/Lome': 'Greenwich Standard Time', - 'Africa/Luanda': 'W. Central Africa Standard Time', - 'Africa/Lubumbashi': 'South Africa Standard Time', - 'Africa/Lusaka': 'South Africa Standard Time', - 'Africa/Malabo': 'W. Central Africa Standard Time', - 'Africa/Maputo': 'South Africa Standard Time', - 'Africa/Maseru': 'South Africa Standard Time', - 'Africa/Mbabane': 'South Africa Standard Time', - 'Africa/Mogadishu': 'E. Africa Standard Time', - 'Africa/Monrovia': 'Greenwich Standard Time', - 'Africa/Nairobi': 'E. Africa Standard Time', - 'Africa/Ndjamena': 'W. Central Africa Standard Time', - 'Africa/Niamey': 'W. Central Africa Standard Time', - 'Africa/Nouakchott': 'Greenwich Standard Time', - 'Africa/Ouagadougou': 'Greenwich Standard Time', - 'Africa/Porto-Novo': 'W. Central Africa Standard Time', - 'Africa/Sao_Tome': 'Sao Tome Standard Time', - 'Africa/Timbuktu': 'Greenwich Standard Time', - 'Africa/Tripoli': 'Libya Standard Time', - 'Africa/Tunis': 'W. Central Africa Standard Time', - 'Africa/Windhoek': 'Namibia Standard Time', - 'America/Adak': 'Aleutian Standard Time', - 'America/Anchorage': 'Alaskan Standard Time', - 'America/Anguilla': 'SA Western Standard Time', - 'America/Antigua': 'SA Western Standard Time', - 'America/Araguaina': 'Tocantins Standard Time', - 'America/Argentina/La_Rioja': 'Argentina Standard Time', - 'America/Argentina/Rio_Gallegos': 'Argentina Standard Time', - 'America/Argentina/Salta': 'Argentina Standard Time', - 'America/Argentina/San_Juan': 'Argentina Standard Time', - 'America/Argentina/San_Luis': 'Argentina Standard Time', - 'America/Argentina/Tucuman': 'Argentina Standard Time', - 'America/Argentina/Ushuaia': 'Argentina Standard Time', - 'America/Aruba': 'SA Western Standard Time', - 'America/Asuncion': 'Paraguay Standard Time', - 'America/Atka': 'Aleutian Standard Time', - 'America/Bahia': 'Bahia Standard Time', - 'America/Bahia_Banderas': 'Central Standard Time (Mexico)', - 'America/Barbados': 'SA Western Standard Time', - 'America/Belem': 'SA Eastern Standard Time', - 'America/Belize': 'Central America Standard Time', - 'America/Blanc-Sablon': 'SA Western Standard Time', - 'America/Boa_Vista': 'SA Western Standard Time', - 'America/Bogota': 'SA Pacific Standard Time', - 'America/Boise': 'Mountain Standard Time', - 'America/Buenos_Aires': 'Argentina Standard Time', - 'America/Cambridge_Bay': 'Mountain Standard Time', - 'America/Campo_Grande': 'Central Brazilian Standard Time', - 'America/Cancun': 'Eastern Standard Time (Mexico)', - 'America/Caracas': 'Venezuela Standard Time', - 'America/Catamarca': 'Argentina Standard Time', - 'America/Cayenne': 'SA Eastern Standard Time', - 'America/Cayman': 'SA Pacific Standard Time', - 'America/Chicago': 'Central Standard Time', - 'America/Chihuahua': 'Mountain Standard Time (Mexico)', - 'America/Coral_Harbour': 'SA Pacific Standard Time', - 'America/Cordoba': 'Argentina Standard Time', - 'America/Costa_Rica': 'Central America Standard Time', - 'America/Creston': 'US Mountain Standard Time', - 'America/Cuiaba': 'Central Brazilian Standard Time', - 'America/Curacao': 'SA Western Standard Time', - 'America/Danmarkshavn': 'Greenwich Standard Time', - 'America/Dawson': 'Yukon Standard Time', - 'America/Dawson_Creek': 'US Mountain Standard Time', - 'America/Denver': 'Mountain Standard Time', - 'America/Detroit': 'Eastern Standard Time', - 'America/Dominica': 'SA Western Standard Time', - 'America/Edmonton': 'Mountain Standard Time', - 'America/Eirunepe': 'SA Pacific Standard Time', - 'America/El_Salvador': 'Central America Standard Time', - 'America/Ensenada': 'Pacific Standard Time (Mexico)', - 'America/Fort_Nelson': 'US Mountain Standard Time', - 'America/Fortaleza': 'SA Eastern Standard Time', - 'America/Glace_Bay': 'Atlantic Standard Time', - 'America/Godthab': 'Greenland Standard Time', - 'America/Goose_Bay': 'Atlantic Standard Time', - 'America/Grand_Turk': 'Turks And Caicos Standard Time', - 'America/Grenada': 'SA Western Standard Time', - 'America/Guadeloupe': 'SA Western Standard Time', - 'America/Guatemala': 'Central America Standard Time', - 'America/Guayaquil': 'SA Pacific Standard Time', - 'America/Guyana': 'SA Western Standard Time', - 'America/Halifax': 'Atlantic Standard Time', - 'America/Havana': 'Cuba Standard Time', - 'America/Hermosillo': 'US Mountain Standard Time', - 'America/Indiana/Knox': 'Central Standard Time', - 'America/Indiana/Marengo': 'US Eastern Standard Time', - 'America/Indiana/Petersburg': 'Eastern Standard Time', - 'America/Indiana/Tell_City': 'Central Standard Time', - 'America/Indiana/Vevay': 'US Eastern Standard Time', - 'America/Indiana/Vincennes': 'Eastern Standard Time', - 'America/Indiana/Winamac': 'Eastern Standard Time', - 'America/Indianapolis': 'US Eastern Standard Time', - 'America/Inuvik': 'Mountain Standard Time', - 'America/Iqaluit': 'Eastern Standard Time', - 'America/Jamaica': 'SA Pacific Standard Time', - 'America/Jujuy': 'Argentina Standard Time', - 'America/Juneau': 'Alaskan Standard Time', - 'America/Kentucky/Monticello': 'Eastern Standard Time', - 'America/Knox_IN': 'Central Standard Time', - 'America/Kralendijk': 'SA Western Standard Time', - 'America/La_Paz': 'SA Western Standard Time', - 'America/Lima': 'SA Pacific Standard Time', - 'America/Los_Angeles': 'Pacific Standard Time', - 'America/Louisville': 'Eastern Standard Time', - 'America/Lower_Princes': 'SA Western Standard Time', - 'America/Maceio': 'SA Eastern Standard Time', - 'America/Managua': 'Central America Standard Time', - 'America/Manaus': 'SA Western Standard Time', - 'America/Marigot': 'SA Western Standard Time', - 'America/Martinique': 'SA Western Standard Time', - 'America/Matamoros': 'Central Standard Time', - 'America/Mazatlan': 'Mountain Standard Time (Mexico)', - 'America/Mendoza': 'Argentina Standard Time', - 'America/Menominee': 'Central Standard Time', - 'America/Merida': 'Central Standard Time (Mexico)', - 'America/Metlakatla': 'Alaskan Standard Time', - 'America/Mexico_City': 'Central Standard Time (Mexico)', - 'America/Miquelon': 'Saint Pierre Standard Time', - 'America/Moncton': 'Atlantic Standard Time', - 'America/Monterrey': 'Central Standard Time (Mexico)', - 'America/Montevideo': 'Montevideo Standard Time', - 'America/Montreal': 'Eastern Standard Time', - 'America/Montserrat': 'SA Western Standard Time', - 'America/Nassau': 'Eastern Standard Time', - 'America/New_York': 'Eastern Standard Time', - 'America/Nipigon': 'Eastern Standard Time', - 'America/Nome': 'Alaskan Standard Time', - 'America/Noronha': 'UTC-02', - 'America/North_Dakota/Beulah': 'Central Standard Time', - 'America/North_Dakota/Center': 'Central Standard Time', - 'America/North_Dakota/New_Salem': 'Central Standard Time', - 'America/Ojinaga': 'Mountain Standard Time', - 'America/Panama': 'SA Pacific Standard Time', - 'America/Pangnirtung': 'Eastern Standard Time', - 'America/Paramaribo': 'SA Eastern Standard Time', - 'America/Phoenix': 'US Mountain Standard Time', - 'America/Port-au-Prince': 'Haiti Standard Time', - 'America/Port_of_Spain': 'SA Western Standard Time', - 'America/Porto_Acre': 'SA Pacific Standard Time', - 'America/Porto_Velho': 'SA Western Standard Time', - 'America/Puerto_Rico': 'SA Western Standard Time', - 'America/Punta_Arenas': 'Magallanes Standard Time', - 'America/Rainy_River': 'Central Standard Time', - 'America/Rankin_Inlet': 'Central Standard Time', - 'America/Recife': 'SA Eastern Standard Time', - 'America/Regina': 'Canada Central Standard Time', - 'America/Resolute': 'Central Standard Time', - 'America/Rio_Branco': 'SA Pacific Standard Time', - 'America/Santa_Isabel': 'Pacific Standard Time (Mexico)', - 'America/Santarem': 'SA Eastern Standard Time', - 'America/Santiago': 'Pacific SA Standard Time', - 'America/Santo_Domingo': 'SA Western Standard Time', - 'America/Sao_Paulo': 'E. South America Standard Time', - 'America/Scoresbysund': 'Azores Standard Time', - 'America/Shiprock': 'Mountain Standard Time', - 'America/Sitka': 'Alaskan Standard Time', - 'America/St_Barthelemy': 'SA Western Standard Time', - 'America/St_Johns': 'Newfoundland Standard Time', - 'America/St_Kitts': 'SA Western Standard Time', - 'America/St_Lucia': 'SA Western Standard Time', - 'America/St_Thomas': 'SA Western Standard Time', - 'America/St_Vincent': 'SA Western Standard Time', - 'America/Swift_Current': 'Canada Central Standard Time', - 'America/Tegucigalpa': 'Central America Standard Time', - 'America/Thule': 'Atlantic Standard Time', - 'America/Thunder_Bay': 'Eastern Standard Time', - 'America/Tijuana': 'Pacific Standard Time (Mexico)', - 'America/Toronto': 'Eastern Standard Time', - 'America/Tortola': 'SA Western Standard Time', - 'America/Vancouver': 'Pacific Standard Time', - 'America/Virgin': 'SA Western Standard Time', - 'America/Whitehorse': 'Yukon Standard Time', - 'America/Winnipeg': 'Central Standard Time', - 'America/Yakutat': 'Alaskan Standard Time', - 'America/Yellowknife': 'Mountain Standard Time', - 'Antarctica/Casey': 'Central Pacific Standard Time', - 'Antarctica/Davis': 'SE Asia Standard Time', - 'Antarctica/DumontDUrville': 'West Pacific Standard Time', - 'Antarctica/Macquarie': 'Tasmania Standard Time', - 'Antarctica/Mawson': 'West Asia Standard Time', - 'Antarctica/McMurdo': 'New Zealand Standard Time', - 'Antarctica/Palmer': 'SA Eastern Standard Time', - 'Antarctica/Rothera': 'SA Eastern Standard Time', - 'Antarctica/South_Pole': 'New Zealand Standard Time', - 'Antarctica/Syowa': 'E. Africa Standard Time', - 'Antarctica/Vostok': 'Central Asia Standard Time', - 'Arctic/Longyearbyen': 'W. Europe Standard Time', - 'Asia/Aden': 'Arab Standard Time', - 'Asia/Almaty': 'Central Asia Standard Time', - 'Asia/Amman': 'Jordan Standard Time', - 'Asia/Anadyr': 'Russia Time Zone 11', - 'Asia/Aqtau': 'West Asia Standard Time', - 'Asia/Aqtobe': 'West Asia Standard Time', - 'Asia/Ashgabat': 'West Asia Standard Time', - 'Asia/Ashkhabad': 'West Asia Standard Time', - 'Asia/Atyrau': 'West Asia Standard Time', - 'Asia/Baghdad': 'Arabic Standard Time', - 'Asia/Bahrain': 'Arab Standard Time', - 'Asia/Baku': 'Azerbaijan Standard Time', - 'Asia/Bangkok': 'SE Asia Standard Time', - 'Asia/Barnaul': 'Altai Standard Time', - 'Asia/Beirut': 'Middle East Standard Time', - 'Asia/Bishkek': 'Central Asia Standard Time', - 'Asia/Brunei': 'Singapore Standard Time', - 'Asia/Calcutta': 'India Standard Time', - 'Asia/Chita': 'Transbaikal Standard Time', - 'Asia/Choibalsan': 'Ulaanbaatar Standard Time', - 'Asia/Chongqing': 'China Standard Time', - 'Asia/Chungking': 'China Standard Time', - 'Asia/Colombo': 'Sri Lanka Standard Time', - 'Asia/Dacca': 'Bangladesh Standard Time', - 'Asia/Damascus': 'Syria Standard Time', - 'Asia/Dhaka': 'Bangladesh Standard Time', - 'Asia/Dili': 'Tokyo Standard Time', - 'Asia/Dubai': 'Arabian Standard Time', - 'Asia/Dushanbe': 'West Asia Standard Time', - 'Asia/Famagusta': 'GTB Standard Time', - 'Asia/Gaza': 'West Bank Standard Time', - 'Asia/Harbin': 'China Standard Time', - 'Asia/Hebron': 'West Bank Standard Time', - 'Asia/Hong_Kong': 'China Standard Time', - 'Asia/Hovd': 'W. Mongolia Standard Time', - 'Asia/Irkutsk': 'North Asia East Standard Time', - 'Asia/Jakarta': 'SE Asia Standard Time', - 'Asia/Jayapura': 'Tokyo Standard Time', - 'Asia/Jerusalem': 'Israel Standard Time', - 'Asia/Kabul': 'Afghanistan Standard Time', - 'Asia/Kamchatka': 'Russia Time Zone 11', - 'Asia/Karachi': 'Pakistan Standard Time', - 'Asia/Kashgar': 'Central Asia Standard Time', - 'Asia/Katmandu': 'Nepal Standard Time', - 'Asia/Khandyga': 'Yakutsk Standard Time', - 'Asia/Krasnoyarsk': 'North Asia Standard Time', - 'Asia/Kuala_Lumpur': 'Singapore Standard Time', - 'Asia/Kuching': 'Singapore Standard Time', - 'Asia/Kuwait': 'Arab Standard Time', - 'Asia/Macao': 'China Standard Time', - 'Asia/Macau': 'China Standard Time', - 'Asia/Magadan': 'Magadan Standard Time', - 'Asia/Makassar': 'Singapore Standard Time', - 'Asia/Manila': 'Singapore Standard Time', - 'Asia/Muscat': 'Arabian Standard Time', - 'Asia/Nicosia': 'GTB Standard Time', - 'Asia/Novokuznetsk': 'North Asia Standard Time', - 'Asia/Novosibirsk': 'N. Central Asia Standard Time', - 'Asia/Omsk': 'Omsk Standard Time', - 'Asia/Oral': 'West Asia Standard Time', - 'Asia/Phnom_Penh': 'SE Asia Standard Time', - 'Asia/Pontianak': 'SE Asia Standard Time', - 'Asia/Pyongyang': 'North Korea Standard Time', - 'Asia/Qatar': 'Arab Standard Time', - 'Asia/Qostanay': 'Central Asia Standard Time', - 'Asia/Qyzylorda': 'Qyzylorda Standard Time', - 'Asia/Rangoon': 'Myanmar Standard Time', - 'Asia/Riyadh': 'Arab Standard Time', - 'Asia/Saigon': 'SE Asia Standard Time', - 'Asia/Sakhalin': 'Sakhalin Standard Time', - 'Asia/Samarkand': 'West Asia Standard Time', - 'Asia/Seoul': 'Korea Standard Time', - 'Asia/Shanghai': 'China Standard Time', - 'Asia/Singapore': 'Singapore Standard Time', - 'Asia/Srednekolymsk': 'Russia Time Zone 10', - 'Asia/Taipei': 'Taipei Standard Time', - 'Asia/Tashkent': 'West Asia Standard Time', - 'Asia/Tbilisi': 'Georgian Standard Time', - 'Asia/Tehran': 'Iran Standard Time', - 'Asia/Tel_Aviv': 'Israel Standard Time', - 'Asia/Thimbu': 'Bangladesh Standard Time', - 'Asia/Thimphu': 'Bangladesh Standard Time', - 'Asia/Tokyo': 'Tokyo Standard Time', - 'Asia/Tomsk': 'Tomsk Standard Time', - 'Asia/Ujung_Pandang': 'Singapore Standard Time', - 'Asia/Ulaanbaatar': 'Ulaanbaatar Standard Time', - 'Asia/Ulan_Bator': 'Ulaanbaatar Standard Time', - 'Asia/Urumqi': 'Central Asia Standard Time', - 'Asia/Ust-Nera': 'Vladivostok Standard Time', - 'Asia/Vientiane': 'SE Asia Standard Time', - 'Asia/Vladivostok': 'Vladivostok Standard Time', - 'Asia/Yakutsk': 'Yakutsk Standard Time', - 'Asia/Yekaterinburg': 'Ekaterinburg Standard Time', - 'Asia/Yerevan': 'Caucasus Standard Time', - 'Atlantic/Azores': 'Azores Standard Time', - 'Atlantic/Bermuda': 'Atlantic Standard Time', - 'Atlantic/Canary': 'GMT Standard Time', - 'Atlantic/Cape_Verde': 'Cape Verde Standard Time', - 'Atlantic/Faeroe': 'GMT Standard Time', - 'Atlantic/Jan_Mayen': 'W. Europe Standard Time', - 'Atlantic/Madeira': 'GMT Standard Time', - 'Atlantic/Reykjavik': 'Greenwich Standard Time', - 'Atlantic/South_Georgia': 'UTC-02', - 'Atlantic/St_Helena': 'Greenwich Standard Time', - 'Atlantic/Stanley': 'SA Eastern Standard Time', - 'Australia/ACT': 'AUS Eastern Standard Time', - 'Australia/Adelaide': 'Cen. Australia Standard Time', - 'Australia/Brisbane': 'E. Australia Standard Time', - 'Australia/Broken_Hill': 'Cen. Australia Standard Time', - 'Australia/Canberra': 'AUS Eastern Standard Time', - 'Australia/Currie': 'Tasmania Standard Time', - 'Australia/Darwin': 'AUS Central Standard Time', - 'Australia/Eucla': 'Aus Central W. Standard Time', - 'Australia/Hobart': 'Tasmania Standard Time', - 'Australia/LHI': 'Lord Howe Standard Time', - 'Australia/Lindeman': 'E. Australia Standard Time', - 'Australia/Lord_Howe': 'Lord Howe Standard Time', - 'Australia/Melbourne': 'AUS Eastern Standard Time', - 'Australia/NSW': 'AUS Eastern Standard Time', - 'Australia/North': 'AUS Central Standard Time', - 'Australia/Perth': 'W. Australia Standard Time', - 'Australia/Queensland': 'E. Australia Standard Time', - 'Australia/South': 'Cen. Australia Standard Time', - 'Australia/Sydney': 'AUS Eastern Standard Time', - 'Australia/Tasmania': 'Tasmania Standard Time', - 'Australia/Victoria': 'AUS Eastern Standard Time', - 'Australia/West': 'W. Australia Standard Time', - 'Australia/Yancowinna': 'Cen. Australia Standard Time', - 'Brazil/Acre': 'SA Pacific Standard Time', - 'Brazil/DeNoronha': 'UTC-02', - 'Brazil/East': 'E. South America Standard Time', - 'Brazil/West': 'SA Western Standard Time', - 'CST6CDT': 'Central Standard Time', - 'Canada/Atlantic': 'Atlantic Standard Time', - 'Canada/Central': 'Central Standard Time', - 'Canada/Eastern': 'Eastern Standard Time', - 'Canada/Mountain': 'Mountain Standard Time', - 'Canada/Newfoundland': 'Newfoundland Standard Time', - 'Canada/Pacific': 'Pacific Standard Time', - 'Canada/Saskatchewan': 'Canada Central Standard Time', - 'Canada/Yukon': 'Yukon Standard Time', - 'Chile/Continental': 'Pacific SA Standard Time', - 'Chile/EasterIsland': 'Easter Island Standard Time', - 'Cuba': 'Cuba Standard Time', - 'EST5EDT': 'Eastern Standard Time', - 'Egypt': 'Egypt Standard Time', - 'Eire': 'GMT Standard Time', - 'Etc/GMT': 'UTC', - 'Etc/GMT+1': 'Cape Verde Standard Time', - 'Etc/GMT+10': 'Hawaiian Standard Time', - 'Etc/GMT+11': 'UTC-11', - 'Etc/GMT+12': 'Dateline Standard Time', - 'Etc/GMT+2': 'UTC-02', - 'Etc/GMT+3': 'SA Eastern Standard Time', - 'Etc/GMT+4': 'SA Western Standard Time', - 'Etc/GMT+5': 'SA Pacific Standard Time', - 'Etc/GMT+6': 'Central America Standard Time', - 'Etc/GMT+7': 'US Mountain Standard Time', - 'Etc/GMT+8': 'UTC-08', - 'Etc/GMT+9': 'UTC-09', - 'Etc/GMT-1': 'W. Central Africa Standard Time', - 'Etc/GMT-10': 'West Pacific Standard Time', - 'Etc/GMT-11': 'Central Pacific Standard Time', - 'Etc/GMT-12': 'UTC+12', - 'Etc/GMT-13': 'UTC+13', - 'Etc/GMT-14': 'Line Islands Standard Time', - 'Etc/GMT-2': 'South Africa Standard Time', - 'Etc/GMT-3': 'E. Africa Standard Time', - 'Etc/GMT-4': 'Arabian Standard Time', - 'Etc/GMT-5': 'West Asia Standard Time', - 'Etc/GMT-6': 'Central Asia Standard Time', - 'Etc/GMT-7': 'SE Asia Standard Time', - 'Etc/GMT-8': 'Singapore Standard Time', - 'Etc/GMT-9': 'Tokyo Standard Time', - 'Etc/UCT': 'UTC', - 'Etc/UTC': 'UTC', - 'Europe/Amsterdam': 'W. Europe Standard Time', - 'Europe/Andorra': 'W. Europe Standard Time', - 'Europe/Astrakhan': 'Astrakhan Standard Time', - 'Europe/Athens': 'GTB Standard Time', - 'Europe/Belfast': 'GMT Standard Time', - 'Europe/Belgrade': 'Central Europe Standard Time', - 'Europe/Berlin': 'W. Europe Standard Time', - 'Europe/Bratislava': 'Central Europe Standard Time', - 'Europe/Brussels': 'Romance Standard Time', - 'Europe/Bucharest': 'GTB Standard Time', - 'Europe/Budapest': 'Central Europe Standard Time', - 'Europe/Busingen': 'W. Europe Standard Time', - 'Europe/Chisinau': 'E. Europe Standard Time', - 'Europe/Copenhagen': 'Romance Standard Time', - 'Europe/Dublin': 'GMT Standard Time', - 'Europe/Gibraltar': 'W. Europe Standard Time', - 'Europe/Guernsey': 'GMT Standard Time', - 'Europe/Helsinki': 'FLE Standard Time', - 'Europe/Isle_of_Man': 'GMT Standard Time', - 'Europe/Istanbul': 'Turkey Standard Time', - 'Europe/Jersey': 'GMT Standard Time', - 'Europe/Kaliningrad': 'Kaliningrad Standard Time', - 'Europe/Kiev': 'FLE Standard Time', - 'Europe/Kirov': 'Russian Standard Time', - 'Europe/Lisbon': 'GMT Standard Time', - 'Europe/Ljubljana': 'Central Europe Standard Time', - 'Europe/London': 'GMT Standard Time', - 'Europe/Luxembourg': 'W. Europe Standard Time', - 'Europe/Madrid': 'Romance Standard Time', - 'Europe/Malta': 'W. Europe Standard Time', - 'Europe/Mariehamn': 'FLE Standard Time', - 'Europe/Minsk': 'Belarus Standard Time', - 'Europe/Monaco': 'W. Europe Standard Time', - 'Europe/Moscow': 'Russian Standard Time', - 'Europe/Oslo': 'W. Europe Standard Time', - 'Europe/Paris': 'Romance Standard Time', - 'Europe/Podgorica': 'Central Europe Standard Time', - 'Europe/Prague': 'Central Europe Standard Time', - 'Europe/Riga': 'FLE Standard Time', - 'Europe/Rome': 'W. Europe Standard Time', - 'Europe/Samara': 'Russia Time Zone 3', - 'Europe/San_Marino': 'W. Europe Standard Time', - 'Europe/Sarajevo': 'Central European Standard Time', - 'Europe/Saratov': 'Saratov Standard Time', - 'Europe/Simferopol': 'Russian Standard Time', - 'Europe/Skopje': 'Central European Standard Time', - 'Europe/Sofia': 'FLE Standard Time', - 'Europe/Stockholm': 'W. Europe Standard Time', - 'Europe/Tallinn': 'FLE Standard Time', - 'Europe/Tirane': 'Central Europe Standard Time', - 'Europe/Tiraspol': 'E. Europe Standard Time', - 'Europe/Ulyanovsk': 'Astrakhan Standard Time', - 'Europe/Uzhgorod': 'FLE Standard Time', - 'Europe/Vaduz': 'W. Europe Standard Time', - 'Europe/Vatican': 'W. Europe Standard Time', - 'Europe/Vienna': 'W. Europe Standard Time', - 'Europe/Vilnius': 'FLE Standard Time', - 'Europe/Volgograd': 'Volgograd Standard Time', - 'Europe/Warsaw': 'Central European Standard Time', - 'Europe/Zagreb': 'Central European Standard Time', - 'Europe/Zaporozhye': 'FLE Standard Time', - 'Europe/Zurich': 'W. Europe Standard Time', - 'GB': 'GMT Standard Time', - 'GB-Eire': 'GMT Standard Time', - 'GMT+0': 'UTC', - 'GMT-0': 'UTC', - 'GMT0': 'UTC', - 'Greenwich': 'UTC', - 'Hongkong': 'China Standard Time', - 'Iceland': 'Greenwich Standard Time', - 'Indian/Antananarivo': 'E. Africa Standard Time', - 'Indian/Chagos': 'Central Asia Standard Time', - 'Indian/Christmas': 'SE Asia Standard Time', - 'Indian/Cocos': 'Myanmar Standard Time', - 'Indian/Comoro': 'E. Africa Standard Time', - 'Indian/Kerguelen': 'West Asia Standard Time', - 'Indian/Mahe': 'Mauritius Standard Time', - 'Indian/Maldives': 'West Asia Standard Time', - 'Indian/Mauritius': 'Mauritius Standard Time', - 'Indian/Mayotte': 'E. Africa Standard Time', - 'Indian/Reunion': 'Mauritius Standard Time', - 'Iran': 'Iran Standard Time', - 'Israel': 'Israel Standard Time', - 'Jamaica': 'SA Pacific Standard Time', - 'Japan': 'Tokyo Standard Time', - 'Kwajalein': 'UTC+12', - 'Libya': 'Libya Standard Time', - 'MST7MDT': 'Mountain Standard Time', - 'Mexico/BajaNorte': 'Pacific Standard Time (Mexico)', - 'Mexico/BajaSur': 'Mountain Standard Time (Mexico)', - 'Mexico/General': 'Central Standard Time (Mexico)', - 'NZ': 'New Zealand Standard Time', - 'NZ-CHAT': 'Chatham Islands Standard Time', - 'Navajo': 'Mountain Standard Time', - 'PRC': 'China Standard Time', - 'PST8PDT': 'Pacific Standard Time', - 'Pacific/Apia': 'Samoa Standard Time', - 'Pacific/Auckland': 'New Zealand Standard Time', - 'Pacific/Bougainville': 'Bougainville Standard Time', - 'Pacific/Chatham': 'Chatham Islands Standard Time', - 'Pacific/Easter': 'Easter Island Standard Time', - 'Pacific/Efate': 'Central Pacific Standard Time', - 'Pacific/Enderbury': 'UTC+13', - 'Pacific/Fakaofo': 'UTC+13', - 'Pacific/Fiji': 'Fiji Standard Time', - 'Pacific/Funafuti': 'UTC+12', - 'Pacific/Galapagos': 'Central America Standard Time', - 'Pacific/Gambier': 'UTC-09', - 'Pacific/Guadalcanal': 'Central Pacific Standard Time', - 'Pacific/Guam': 'West Pacific Standard Time', - 'Pacific/Honolulu': 'Hawaiian Standard Time', - 'Pacific/Johnston': 'Hawaiian Standard Time', - 'Pacific/Kiritimati': 'Line Islands Standard Time', - 'Pacific/Kosrae': 'Central Pacific Standard Time', - 'Pacific/Kwajalein': 'UTC+12', - 'Pacific/Majuro': 'UTC+12', - 'Pacific/Marquesas': 'Marquesas Standard Time', - 'Pacific/Midway': 'UTC-11', - 'Pacific/Nauru': 'UTC+12', - 'Pacific/Niue': 'UTC-11', - 'Pacific/Norfolk': 'Norfolk Standard Time', - 'Pacific/Noumea': 'Central Pacific Standard Time', - 'Pacific/Pago_Pago': 'UTC-11', - 'Pacific/Palau': 'Tokyo Standard Time', - 'Pacific/Pitcairn': 'UTC-08', - 'Pacific/Ponape': 'Central Pacific Standard Time', - 'Pacific/Port_Moresby': 'West Pacific Standard Time', - 'Pacific/Rarotonga': 'Hawaiian Standard Time', - 'Pacific/Saipan': 'West Pacific Standard Time', - 'Pacific/Samoa': 'UTC-11', - 'Pacific/Tahiti': 'Hawaiian Standard Time', - 'Pacific/Tarawa': 'UTC+12', - 'Pacific/Tongatapu': 'Tonga Standard Time', - 'Pacific/Truk': 'West Pacific Standard Time', - 'Pacific/Wake': 'UTC+12', - 'Pacific/Wallis': 'UTC+12', - 'Poland': 'Central European Standard Time', - 'Portugal': 'GMT Standard Time', - 'ROC': 'Taipei Standard Time', - 'ROK': 'Korea Standard Time', - 'Singapore': 'Singapore Standard Time', - 'Turkey': 'Turkey Standard Time', - 'UCT': 'UTC', - 'US/Alaska': 'Alaskan Standard Time', - 'US/Aleutian': 'Aleutian Standard Time', - 'US/Arizona': 'US Mountain Standard Time', - 'US/Central': 'Central Standard Time', - 'US/Eastern': 'Eastern Standard Time', - 'US/Hawaii': 'Hawaiian Standard Time', - 'US/Indiana-Starke': 'Central Standard Time', - 'US/Michigan': 'Eastern Standard Time', - 'US/Mountain': 'Mountain Standard Time', - 'US/Pacific': 'Pacific Standard Time', - 'US/Samoa': 'UTC-11', - 'UTC': 'UTC', - 'Universal': 'UTC', - 'W-SU': 'Russian Standard Time', - 'Zulu': 'UTC'}