Repository: matrix-org/sygnal

Issue:
Tests failing with Twisted 23.10
```
dmr on titan in sygnal on  dmr/fix-version via 🐍 v3.11.6 (python-3.11.6) 
2023-11-21 15:55:11 ✔  $ python -m twisted.trial tests.test_httpproxy_twisted 
tests.test_httpproxy_twisted
  SygnalTwistedProxyTests
    test_https_request_via_proxy ... 2023-11-21 15:55:16,808 [541273] DEBUG sygnal.sygnal Started logging
2023-11-21 15:55:16,808 [541273] INFO  sygnal.sygnal Importing pushkin module: tests.test_gcm
2023-11-21 15:55:16,809 [541273] INFO  sygnal.sygnal Creating pushkin: TestGcmPushkin
2023-11-21 15:55:16,822 [541273] INFO  sygnal.sygnal Configured with app IDs: dict_keys(['com.example.gcm'])
2023-11-21 15:55:16,823 [541273] INFO  sygnal.sygnal Starting listening on 127.0.0.1 port 5000
2023-11-21 15:55:16,823 [541273] DEBUG sygnal.helper.proxy.proxyagent_twisted Requesting b'https://test.com/abc' via <HTTPConnectProxyEndpoint <HostnameEndpoint proxy.com:1080>>
2023-11-21 15:55:16,824 [541273] DEBUG sygnal.helper.proxy.connectproxyclient_twisted Connected to proxy, sending CONNECT
2023-11-21 15:55:16,824 [541273] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7f9c0a838a50>-><twisted.web.http.HTTPChannel object at 0x7f9c0d1b0a90>: b'CONNECT test.com:443 HTTP/1.0\r\n\r\n'
2023-11-21 15:55:16,824 [541273] INFO  tests.test_httpproxy_twisted Completed request <Request at 0x7f9c0ad410d0 method=CONNECT uri=test.com:443 clientproto=HTTP/1.0>
Certificate request self-signature ok
subject=
2023-11-21 15:55:16,858 [541273] INFO  tests.twisted_test_helpers <twisted.web.http.HTTPChannel object at 0x7f9c0d1b0a90>-><twisted.internet.endpoints._WrappingProtocol object at 0x7f9c0a838a50>: b'HTTP/1.0 200 OK\r\n\r\n'
2023-11-21 15:55:16,858 [541273] DEBUG sygnal.helper.proxy.connectproxyclient_twisted Got Status: b'200' b'OK' b'HTTP/1.0'
2023-11-21 15:55:16,858 [541273] DEBUG sygnal.helper.proxy.connectproxyclient_twisted End Headers
2023-11-21 15:55:16,858 [541273] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7f9c0a838a50>-><twisted.protocols.tls.BufferingTLSTransport object at 0x7f9c0c198490>: b"\x16\x03\x01\x01E\x01\x00\x01A\x03\x03\x8e\x9d\xc0/\xffL\x85\xe5\xa8hn\x93\xf45X\x97b\xe3\xb3\x02\x84\x98\x13V\x16#V5F\xf6\x05\xc7 \x93\x1d\xe7\xa6\x12=a\xf3_\xc4(\xf0\x84=\x99\xf9\xe6v\xb7\x17\xcf\xbd\xb3\xe4h[_G\xfb\xff\x1a\xc5\x00V\x13\x02\x13\x03\x13\x01\xc0,\xc00\xc0+\xc0/\xcc\xa9\xcc\xa8\x00\x9f\x00\x9e\xcc\xaa\xc0\xaf\xc0\xad\xc0$\xc0(\xc0\n\xc0\x14\xc0\xa3\xc0\x9f\x00k\x009\xc0\xae\xc0\xac\xc0#\xc0'\xc0\t\xc0\x13\xc0\xa2\xc0\x9e\x00g\x003\x00\x9d\x00\x9c\xc0\xa1\xc0\x9d\xc0\xa0\xc0\x9c\x00=\x00<\x005\x00/\x00\xff\x01\x00\x00\xa2\x00\x00\x00\r\x00\x0b\x00\x00\x08test.com\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x00\x16\x00\x14\x00\x1d\x00\x17\x00\x1e\x00\x19\x00\x18\x01\x00\x01\x01\x01\x02\x01\x03\x01\x04\x00\x16\x00\x00\x00\x17\x00\x00\x00\r\x00*\x00(\x04\x03\x05\x03\x06\x03\x08\x07\x08\x08\x08\t\x08\n\x08\x0b\x08\x04\x08\x05\x08\x06\x04\x01\x05\x01\x06\x01\x03\x03\x03\x01\x03\x02\x04\x02\x05\x02\x06\x02\x00+\x00\x05\x04\x03\x04\x03\x03\x00-\x00\x02\x01\x01\x003\x00&\x00$\x00\x1d\x00 ^\x1c{\xe6\x0b\xdf\xf2C\xe0\xee\x81\\\xe3^\xd7xt\xf6\\\xb2\xc65XX\x83\xcf\xcfD\xcd\xab\x1c,"
2023-11-21 15:55:16,860 [541273] INFO  tests.twisted_test_helpers <twisted.protocols.tls.BufferingTLSTransport object at 0x7f9c0c198490>-><twisted.internet.endpoints._WrappingProtocol object at 0x7f9c0a838a50>: b'\x16\x03\x03\x00z\x02\x00\x00v\x03\x03TZ\x84\xdbM\xe3\xd1S\x84d-\x18Vr4\xc7\xc3\xbf\xe3I\xf39\xcf!n\xea\x7f\xf0M\x8d\x912 \x93\x1d\xe7\xa6\x12=a\xf3_\xc4(\xf0\x84=\x99\xf9\xe6v\xb7\x17\xcf\xbd\xb3\xe4h[_G\xfb\xff\x1a\xc5\x13\x02\x00\x00.\x00+\x00\x02\x03\x04\x003\x00$\x00\x1d\x00 \xc7\x05\xb6\xfc\x1c\xf1\x08\xea>y\xee\xa0\x97\xcaO\xd9[\xb2X\xe0\xec\x87\x9bv\xd5\xaf:)\x89\xd1NA\x14\x03\x03\x00\x01\x01\x17\x03\x03\x00\x17\x96\xc0<\x92/I\xfd\xe4\x9by\xc4\xa6\xa2\xa3\xb9dq\xcdt9\x9d\xa8\xdd\x17\x03\x03\x03&\xd3X\x8b\xdb\xb9\x9eu\xe9KO\xfb\xc9\x9f\tL\xcf\x169\x11\x87\x0c(\x05*\nC5\x9e\xc3w\x83\x11\xb8\xff\xd6\xf5s"\xb6"\xad\x9d\xa5\xfc\x93\x16#\xeeL!\x1c\xf37#\xfb\xba\x16\xdb`\xad\xf3\x04<\xa8\xb3\xae\xb9G\x118\xb8\xdf\xabx\xbf.\xa1\x0b3\xca\xe3\x02\xba\x96\x80\xc4\xe5\x95(\xe2\x1e\xc3\xe9\xe0\xb8\x1b\x93\xba\xa4\x15\t\x95B"\xa4\\\x16k\x0f\x8a\x97K\xfa\x83\x03Po\xaf\xaeDcH\x92z\xd1\x92P"\x080\xeb\x82\x11\x8b\x96\xbb#\x82\xcbu\xe4\x8cY\x87\xaa\xe4"\xd0|p\x9aP\x9c\xde\x88\r*\xd9t\xb69R\xb4\n\xaa\x8a\xae\xcbP\xd1yfW\x07\xb5M-\x9e\x17\xea\x1e\xe3\x10fox\xe1/\xdb\x14o\xab\xeb6\xc5\xf5i\xc6\x8c\x87b\x01\xf7\xfd\xa5\xd6iOJ\xb0K\xc5\xe7\xd44\xf0L\x88\x04\xf7\xfe\xe9L"s(\xaf\x86\xd9\x1b\r\xd8U>_\xa4\xce\xc2\xf9#>=\xde-\xf4F\x1e\xd0\'\xe0\xcaTK\xd4\nz\xd7\'\x12\xa2\xb9\xbd\xcb\x1c\xb5Nnn\xdbp\xd2\\Tr\x91j\xe6\xee=@R\x16\xc0\xc6\xa9\xdc\x92\x15\xe5y.\xda\xba\xff\xfa\x12\xa1\xca\x80\tMs\xe8\x1b6M\xcfv\xae\x97\xff\xd6=\x1f\xfeN\x05\x8f\x9a\x19n\x17\x8f\xbf\x95@\xb7-\xc9N\xfcf\x99\xbdC\xa8`m.+g\xd3p\x04-\xc7@\x9b\x02\xac\xa4\x86f\x15\xf1r\x94i\x93\xb1\x8f1Fr\xaf|A6\xf0.nr\xbf\x84\xfa;\xf3\x1c&gf\xa9\xd1\xb69s\xd5\xfd\x1d\x9c\x87\x19\x87\nGl\xf9\xc2\x92\x01\xa6\xed\x9a\xd3\xc5N\xc1\xd8.\xd7\x19\xc2\xd7\xad=\xb4\xae\xfdB\x1e3Hw\'\x18\x9b\xe4\x19\xf5\x1b\x96\xf8\xbf1.I\xcc\xd1A<\x98*\xed\xb1c\x1c\x12Z\x84\x88\xc5Z\x05\xdb\xa4/H\xb5\x9e\xc2^\xe7\x8f\xb6\xae\x15\xb0\xec\xe2\xd7\xfa\x03\x0e\x13\x7f\x83\xc4\xb0\xf3\x12$O\xdb~\x11\x0b\x11\xbd\xbc\xff!\xec`=\x95+\x1d8S\xc5\xcev\x81!\x9f\x8f/\xc7\x184\x8d"n\x7f\x1c\xa6r\xa2\x1a+\x9bQfVWl\xc2\xf9I\xe4\x18;/\xc5z\xc8\x91\xb5\xad\xa7\xee<\xb9\x8c&\x9a\'*\xb5\xa7\xb8Q2\'\xc5\x11\xb2B\xfc\x1cu{\xf2\xd6\x88\xe7Z\x95\x8cI\x92\x03=:@"Cc\xc3\xff\x8f\xa5U\xdd\x85vU\xafx\xffO\xb49\\.\xa5\xc1\xe5\xa1\x98\x9bS\xf1\xe2UP\x1dIi7\xb8\x99K\xe0\x82\x88\xb2;\x9bmj,m\xc6\xa1*\xc1\x13\x0f\x0f\xc4(\xc5\xf6\x92\x9ecE[\xc9t\x90\xca(3\xd3\xecp\xd8\x08\x9cu\xa1\xb4\xa1I\x1d\xf8#.,\x9e9\x13\xeb\xed\x8f{\xa1\x8e\x17;)\x95\xd9\x9a\x0b.\xe0\xea\x8c<>\x9b\xd3u\xda\xc9\xf5\x0c\xd7~yye\x1c\xe1&m.1\xc6\x06\xc5\x13\x08\x90\xd1\x1f\xf3\xa8`\xb8\xc2\x9cnv6\x89\x1e}u\x1b\xe6\x1b\xdb\xech\xfd\x9aW\x83or~nz\xf6\xe6r\'?ZHE\xad\xe9\xc1\x14!]\x8b\x99f\x11*\x84\x83\xfd\xcepu\x99{v$9T!p\x0e\x00\xc2\x1c)\x8f)\x04\xbd\xd7\xf2^\xcfFRW\xf8\xfbGe\xd8yk\xc0\xf0\xd0\xc1\x16h\x85Vpk\xee\xca\x0e\x17\x03\x03\x01\x19\xde\xd2\x82\x95|\x90d\xb9\xd5\x831\xe7\x92\xaa\x04\xa9S\xe3 \x15a\xfbN\xa9\xc6\xcb\xcd\xa1\xdc\xeb\x9e\xbe\xb8\x10\xa6\x9b\x02\xe2\xb3\xf58U\xa2M\xc0(9\xec_m\xff\xe4y\x9d\x92\xa1\x07y\x98\xa050\xd4\x8d9\xf0G\x92IK\r\xe9\xf4!\xb7\x98\xa8\xcd\x13I\x072\x85!\x93\x9c%\xdc\xc8\xcd\xe1\x93\t\xf7#\x1a\x04=f\x89\xdcd\xa5It\xd0\x06.\xd84\xfd\x99\xcc\xef&\x8a\x88\x12\x80.\xa2\xa3\xf0W;\x14\xbf\xd7\\\xad\xa4\x05?B\x82\xcf\x18\xc8=1\x17\x11\x85R\xe5\\\xd89\xe0O8A\x8f\xc1,c\xe6]^$\xd3\xee\x7f/4\xe1\xd6?\xc4\xd1\xd2P\x90\x02\xe4|\xf2\xe5\x8fY\r\x98\xca\r\xc2\xa0lj\xfd\xef\xaf\xf8\x11\xe8\x9e\xd6\xbc\xf0*\xa1%\xc0hu!\x981\xe5Ka\xce\x8a\x91\xf9\t\x8c\xfd\x80\x97\xec\x91\x16\xd8<m\x1dY\x94\xc4\xc6\xaa\x16\x99\xf4\xf3\xd3q?*\x03\\\x11\x8e3\xd2fm\xb9\xd8\xb7N\xd3\x9a\x00\xc5%\'\xf8\\\x0b\x8e\x9f\x04(\xb6\x19B\xc9\xc66\xe6\xce\xdf\\\xa2\x0b\x17q\t\x8a\xdd\x17\x03\x03\x00E\xfd\x94P\x97\xacs\xc2\x1cm\xc2\x8a(\xe8\xaa\xe7\x832q<:\x0e\x17\xd3?\xd4\xc6\xb2!\x1b\x8e\xe1P\x1e\xfd#\x03\'\x80\xce\xd6 \xe9?\xd1K\x81\xd4\x9f\xd9\x88#]\xf13\x14\x91\xe0\x81\xc19\x18t\x0f\x93\xd0<c\x1d\x97'
2023-11-21 15:55:16,860 [541273] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7f9c0a838a50>-><twisted.protocols.tls.BufferingTLSTransport object at 0x7f9c0c198490>: b'\x14\x03\x03\x00\x01\x01\x17\x03\x03\x00Ep\xf0\xd2\x1c\xcc\xeapb\xe7\x91\'\x85i\x1d^\x86\x00\x13\xddD\x99\xe49\xed\xa9\x04\xf0\x0f\xea4\xe1"p\xfd\xde\x85r\xab\xfb\x81>\xf9\xddxaF\x82\xb7\xba\x8aa\x17\xd1\x8bp^\xc1\xe3\x1ex?\x04}\xaf\xf95x\x8a\x14'
2023-11-21 15:55:16,861 [541273] INFO  tests.twisted_test_helpers <twisted.protocols.tls.BufferingTLSTransport object at 0x7f9c0c198490>-><twisted.internet.endpoints._WrappingProtocol object at 0x7f9c0a838a50>: b"\x17\x03\x03\x00\xfa\xe8C\xfb\x19\xf6h\x1dD\x8b\x13\x12O\xcc\xfc#\xd9C\x8e\x7f\x979vgU\x9b\xfe\x04TI\xeb\xba\xbe ~C\\\rG\x8en\x8e\x16\x06\x12\xa3M\xe3\\\x12\xd4*\xf1\xc0u\xdak\xa5\x9e\x9b\xec\x0e\xf8\x927\x0cS3\xb0\x08_\xcc\x81-}\x84\xcbs\xd2D\xd6E`\xd0f\xdd\xd0\x8f\x98\xa87\xeb\xf9\x87\xf2\x174y\xc0a\\U\xc9\xad\x14xq\xbe[%/\xe3 R\xd0G\x88\xed\x981\xd3\xf7\xd2\x95$n-0\xf7\xa6\xed\x9a\xdbnm\xf1\x16~\x07\xb3cT\x9a\xfe3\xbaG5\x07Ge\xa2z{\xa3\x172\x88\x7fR\xb4\xd0\xeaC\x96\x80+\x801\x14u,\xc5\\\x18\x93\x06P\xfe\xf7\xe9\x9f\xfa\xcfd\x83<\x9c\x91\xf5\x9fq\x14({\xe9\xa4%N\xfdR\xb8\xf0\x80~]\x04\xd0\xbbz\x13\xff=\xed\xb3\x0e\xd5|\x13-\xa16M\xfc\x05B\x01\xff\xfaR\x1d#h\xbe\x9c,\x0e-jF\x00\xbc\xf6=\xfe2~=\x9a:\xca\x17\x03\x03\x00\xfa\x0c7\xa1\x1c3j\x17\xbf\x953u\xb3\x98\xeb\xf5\x13N\n\x17`\xf7Fh\xba\xeb^RdqS\x9c\xd2\xd8c\xddO\xf2\xee\x99\xf5u8\x80&\x8d\xd0U\xe6p]f\x83\x19U'\x1f\xabC\x7f\xe9u\xe0\xd6,\xd5\xe0\x1dfH\xf7\xea\xe4i\xf5weLS\x01\xb2\x87\x18\x083\x1b`\xd0YQ\xb3_gE\xb1\x07x)\xad\x99\xa0 \x04\xd3\xa1\xd8\xcb\x97v\xb3\xcb\\\x8fT\x9f\xef\xc5\x7f\xb6\xdc\xcfTo\x84\xd3F[\xd9Ce\xc2G\xe5^t(\x0b\x96\xed\xa3\x13\xe7b\xa5\x93A\xed\xc8P\xca\x05\xd0+\tv\xa4\x14y\xadE\xeb\xe7{6\xcf6#\xa8\x9a\xea\x14jR\x06\xc0\xe9\xd2Q{\t&@\xdb\xd6@\x90~}o\x18m\xa0V\\o\x9d\t\xe2+B\xd9\xc8g\xaf\xa7ZX\x01\xb6\x85\xb4\xa2\x17\xf4Wi\xb5\xc13Y\xd6\xb3\xcf\xfea\xd3i\xbfK\x0509*\xaf\xa5\xd7\x98\x88\x19\x0c\xb1)\x06\x17N\xb1\xa3\x7f\xbaT\x03"
                                    [FAIL]

===============================================================================
[FAIL]
Traceback (most recent call last):
  File "/home/dmr/workspace/sygnal/tests/test_httpproxy_twisted.py", line 172, in test_https_request_via_proxy
    self.assertEqual(len(http_server.requests), 1)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/trial/_synctest.py", line 444, in assertEqual
    super().assertEqual(first, second, msg)
  File "/usr/lib64/python3.11/unittest/case.py", line 873, in assertEqual
    assertion_func(first, second, msg=msg)
  File "/usr/lib64/python3.11/unittest/case.py", line 866, in _baseAssertEqual
    raise self.failureException(msg)
twisted.trial.unittest.FailTest: 0 != 1

tests.test_httpproxy_twisted.SygnalTwistedProxyTests.test_https_request_via_proxy
-------------------------------------------------------------------------------
Ran 1 tests in 0.057s

FAILED (failures=1)
```

If I downgrade to the previous  twisted, this passes:

```
2023-11-21 15:55:47 ✔  $ python -m twisted.trial tests.test_httpproxy_twisted 
tests.test_httpproxy_twisted
  SygnalTwistedProxyTests
    test_https_request_via_proxy ... 2023-11-21 15:55:50,444 [541380] DEBUG sygnal.sygnal Started logging
2023-11-21 15:55:50,444 [541380] INFO  sygnal.sygnal Importing pushkin module: tests.test_gcm
2023-11-21 15:55:50,446 [541380] INFO  sygnal.sygnal Creating pushkin: TestGcmPushkin
2023-11-21 15:55:50,459 [541380] INFO  sygnal.sygnal Configured with app IDs: dict_keys(['com.example.gcm'])
2023-11-21 15:55:50,459 [541380] INFO  sygnal.sygnal Starting listening on 127.0.0.1 port 5000
2023-11-21 15:55:50,460 [541380] DEBUG sygnal.helper.proxy.proxyagent_twisted Requesting b'https://test.com/abc' via <HTTPConnectProxyEndpoint <HostnameEndpoint proxy.com:1080>>
2023-11-21 15:55:50,460 [541380] DEBUG sygnal.helper.proxy.connectproxyclient_twisted Connected to proxy, sending CONNECT
2023-11-21 15:55:50,461 [541380] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>-><twisted.web.http.HTTPChannel object at 0x7facb139b410>: b'CONNECT test.com:443 HTTP/1.0\r\n\r\n'
2023-11-21 15:55:50,461 [541380] INFO  tests.test_httpproxy_twisted Completed request <Request at 0x7facb11e6050 method=CONNECT uri=test.com:443 clientproto=HTTP/1.0>
Certificate request self-signature ok
subject=
2023-11-21 15:55:50,495 [541380] INFO  tests.twisted_test_helpers <twisted.web.http.HTTPChannel object at 0x7facb139b410>-><twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>: b'HTTP/1.0 200 OK\r\n\r\n'
2023-11-21 15:55:50,495 [541380] DEBUG sygnal.helper.proxy.connectproxyclient_twisted Got Status: b'200' b'OK' b'HTTP/1.0'
2023-11-21 15:55:50,495 [541380] DEBUG sygnal.helper.proxy.connectproxyclient_twisted End Headers
2023-11-21 15:55:50,495 [541380] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>-><twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>: b"\x16\x03\x01\x01E\x01\x00\x01A\x03\x03\x13\x1d\n\x10y\x87\x1c\x13n\xaa\xbbF3\xd1\x1eQ~\xbb\x99\xc1\x94eVU\x9d\x07\xc9\xa6\xe7\xe3\xc4\x9f ~\xbb\x93\x85qf\xbe\xedD\xe3\xe4X\x9a\xceV\x83k]\x1cc\xf8/}QS\x174\x9d\x1f\x0b9\x0f\x00V\x13\x02\x13\x03\x13\x01\xc0,\xc00\xc0+\xc0/\xcc\xa9\xcc\xa8\x00\x9f\x00\x9e\xcc\xaa\xc0\xaf\xc0\xad\xc0$\xc0(\xc0\n\xc0\x14\xc0\xa3\xc0\x9f\x00k\x009\xc0\xae\xc0\xac\xc0#\xc0'\xc0\t\xc0\x13\xc0\xa2\xc0\x9e\x00g\x003\x00\x9d\x00\x9c\xc0\xa1\xc0\x9d\xc0\xa0\xc0\x9c\x00=\x00<\x005\x00/\x00\xff\x01\x00\x00\xa2\x00\x00\x00\r\x00\x0b\x00\x00\x08test.com\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x00\x16\x00\x14\x00\x1d\x00\x17\x00\x1e\x00\x19\x00\x18\x01\x00\x01\x01\x01\x02\x01\x03\x01\x04\x00\x16\x00\x00\x00\x17\x00\x00\x00\r\x00*\x00(\x04\x03\x05\x03\x06\x03\x08\x07\x08\x08\x08\t\x08\n\x08\x0b\x08\x04\x08\x05\x08\x06\x04\x01\x05\x01\x06\x01\x03\x03\x03\x01\x03\x02\x04\x02\x05\x02\x06\x02\x00+\x00\x05\x04\x03\x04\x03\x03\x00-\x00\x02\x01\x01\x003\x00&\x00$\x00\x1d\x00 \xc1&\xe1\xe7\x93\x08f\xf1,\xc2\x1b\xc1\x9dO;\xfb\xe0|\xe3nco)\x00X\xa6.\xde\xcf`/y"
2023-11-21 15:55:50,496 [541380] INFO  tests.twisted_test_helpers <twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>-><twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>: b'\x16\x03\x03\x00z\x02\x00\x00v\x03\x03d\xea\x8f\xa64(\xda%\xeeLT\xdc\xe9\xf0\x99\xef\xd9\xf3\xec\xff\xc1\xfb\x96\xb7\xa8v\x9b\x88\x9d\xc1I> ~\xbb\x93\x85qf\xbe\xedD\xe3\xe4X\x9a\xceV\x83k]\x1cc\xf8/}QS\x174\x9d\x1f\x0b9\x0f\x13\x02\x00\x00.\x00+\x00\x02\x03\x04\x003\x00$\x00\x1d\x00 :%4\xeb\x86y\x7f\xfaj"ob\\\x82\x84%\x86P\xb61`\xf0|Qz$\xc61\x82\xc4i6\x14\x03\x03\x00\x01\x01\x17\x03\x03\x00\x17\xfaSj\x13\x11)\xd4*{\xa7\xf3\x1cxx\x82+x\xdb\x1c\xaf\x00\xba|\x17\x03\x03\x03&x\xdb\xea&\x06 \xc7\x19\x8b\x08A\xfe\xb2z\x1b\xa6\x1f\x97\xb6\xd0\xd7\xe1>i\xb5i>&\xd9\xb5O\t\x98\xe8\xdcd\x13te\xc3\xec\xfe\xea\xc0\xa5\x01F\xe0\xff\xe7~\xfe\x0b\x84\xbf2x\xa8\xa1\x98\xc3D\xc0\xefA_\x85\xd1\xee:\xde\xcd\xd1^\xc3K.#8\xc3BY~\x12\xb2\xcdm\xe1m\xcb\xbd\xbaM\xa8\xb1oC\xf9\x1c\xef\xb4$u\xad\xda\xf5\xa3\xf6\x00\xef\xf5\xcd\x81\xd7$\x1c&V\xdd7e.\x8f\xdczc\xee\xc2i\x91\xf6\xadr\x9cg\xf9%\xb1\xe7}A\xabf\x02.\xb9\x9c\x07x\x986}\x9a\x0b\x03\xd7t\xae\xb7\x9b:\x04( \xa0\xb6\x9e\xd4,z\xb1\xdd\xc8\xa5\xac_\xb2\xbf\x13D\xdf\xe0\xa64\xfd\x17\x9d\xbfU\\\xc79e\xe3r\xf5\x90h\'`_<31\x7f\xe0\xda\xb94\x04o\x9a\xe0\x81LF\x93P\xda\x89\x19\x91lAU\xde\xcc\xb6\xc4\'j\xdb\xf5R\xa62\xd9d#\xfbs=\xc5\xf0\xd3\xf0)\x1e\x90\xc0\x0c\x8bm\nf\x162\x8d\xb7oBN\x01\x84\xa4\x9ez#\x91\xcc\xba\x19d\xc0\x9f0\xed9\xb9>\xa3\xab2\x8f\x19\xda\xaa\xd0\x8a\x81~!\xc8\xbe\xdc\x85\x1fj2\xa0\xca\xf9\x9a\xa0\xce\xe0Ok\x92\x14\xc0C\xd8MX\xf8Ls\x15W\x18\xa4/\x12\xd4{\x1b.\x05x\xfb\xab\xaf\xba\x98|\x90\x16\x10E\xae\x08h\xa4\t\xcf\x97\x8e\xf2\x86\xed\xfe\xd5$\x9a\x1ci\x05\x07Qx\x83\x947g\xe6\xc7\x12\x00,\x85\xaa\x1c\x83\x89\x84\xfc#%v\xdf\xdb\xc7Fj1ZIp:\xf4/:\'\r\xb5\x83C<.\xbbE\x9d\xdf\xb6\x87[\xe1\x8f\xc8@\x040\xfe\xaeA\xfc\xca\xd8\x98s\xe2U\x94c\xa2\x0c\xb4\x85\x99q\xdeqk^\x84\xcaH\xb1\x90\x8f\xa6\xfc\x9a\xce\xa6\xd1b\'c\x95\x84\xcc\t\xa2\xcc\x0e\xe7yZ\x84\xdck\xe4\x16\x11\xe0z\x0c\xfd\x9e\rn\x00L\xcf"\xddQW\'f\x97A\xf1\xfa\x17\x04:\xa2=\x81\xaej\x8b\xf7c\x11\xfe\xdd\x12\x82\xd4L\x85U{t\x0f\x91+\xac\x02,O\xa7\x07\xa8c\x90_\xf7d\xf2\xccs\x81\xbb\x7f\x0fp\xe2\xe0\xfea\xd4\x9cvfm\xe5\x8a\xf8\x93\x08a"\x03\x83\x08C\xc5D\xceFSu\xe7\n\xad\xbcA\x96w\xea\xc0\xf6\x0c\xbb,\x9e\xea\x85\x86GN\x9f\xbe\xe4\xce\xe0\x96\x97\xdb\x85\xb8\xbd\xf0\xc91\xcfa\xad\x862t"\xd9\x97\xc0\x84\xae\x98t\xc2Q;\x92\x88\xddp\xaf\xdf\t&\xd8\x86)\xb3\xa4\x1cc\xfdj\x95\x82\xdd\xe6\x1c\x1a\xbc:\x92Qx\x97\xce&\x92\xff\xa6\xc2\x00\xf4E/\xa9J\xd1a2b\xb3l\xb8h|\x90\xbd5e\x7f\xa2Y\xa8\x0f\xc1r\xb0\xfe\x06\xbd\x06\xcdJ\xaa6R\xcdA\x00i\xc2,\x83~\x93\xb1\xd4dM\xbd\xec\xb3\xe5\x9a\x8d!\xe6\xd1\\I\xccX/~f\xe7\x07>\x07_\xd7-=\xd9}\xde\xae\xee\xb2\xb0\x0b:\x8e\xc8)j\xc0L])hs\xf6\xd2\x17u\xf0\'C\x04n\xa6u\xe4W\xd9\x10\t\x88\xf1H\xe0\xaa\x9b\xaa\xee\x01_\x984T\x075\x90\xe4\xf2\xe1\'\rw<u\xc2\r\x95F\xb3\x1f\x1b\x83\xf8\xe9\xcaBkq\x89\x03\xbe"\'A\x950\xd7\x13\xbee\xb4\xfc\xc3\x90\r\x05\x17\x03\x03\x01\x19\xbdV\xac\x1e\x9cJ\x12"D\xf5h\x14\xda\xd4\xa8\x86\xa7\xd1\xf4\xdd\xf0\xb2\t\x7f\xcd\xcd\xf2=\xb0\x16\xf5\xb5\xfd\xe3\xc0\x85\xba\x97\xa0\xaa\xa0\xe3[P\x03\xa4{\x94Z4\xe2\x87N\xe2\x9bNJ\xe5n\x1e\x13\xb6>~\xc8o\x08\xc1\xe0x\x8cu\x1f\xd2-\xf7`*\x1e\x89\xee\n\xdc\x85Z\xd9l>\xe1\xe9\xf0=L<G@\xf0\x0b\x84\x80ry\x8e\x93\x82\x00\x0c\xf4e+,\\\x9b#\xc0,^\r\xd0.\x19\xaf~\xeb7\xb7\xa0x\x0e$G\xc3\x8d=\x1b\xcf/i\xb5\xb5\x83\xfa\x0eh\x82R\x8fU\xc9\xe9{\xbf,\xb6\xcfF\xd3\xed\xd6\xdb\xb8\xe7\xecKt\xae\xda|n,\xfe\x8f\xd1\x942\xed/_\x91\xc2\xe5w\xfeJ\xffp\x0f$=\xc8\xe1s\xaal-\x9e\xaa\x98I.7\xf7/\xbeW%\x05\xde@\xcaE\xeb\x9c\xa4\xb3T7o\t\xdd\x15H\xdfOf\xea\xf2L\xe7p|\xed\x9d\xec\xe4~\xf7\xb2\xc7M\x142r|\xc6JyR\xfe2\xc5\xfb\x9a\xac0D\x11\x89\xcaf\x8d1I&\xd4\xdcV,y\x9djUh\x9bYN]\xb6\xa3\xec!\x17\x03\x03\x00E}\xa7Xw\xe6^\xb5$\xd6\xf3\x03\xf7\xb6\xb5?H\xb7\x1e\xd8\xbbS3\xee\xdb\x02\x9c\xc6\x11ci{\xc0\xf9j T\xfcD\xee\xf5\x03P\xbd\x0f\x80\x9b\x9aA\x89\xc35{Yp\xe0\x88\\Z\x99n\xc9o\xf6\x07}\xe8\xebz\xf3'
2023-11-21 15:55:50,497 [541380] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>-><twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>: b'\x14\x03\x03\x00\x01\x01\x17\x03\x03\x00E\xa1\x0e\xa5F\xd6\xb1\x0f\x0b\xfa\xca@\xf9vZ\x15L\x8b\xb1\xa5F\x1c)\xa9\xdc\xc7\x95\xb4\x82Nw\x1f\xda\xb6\xf4\xaf\xba\x99\x9b\xcc\x9ew\xa5z\x95\xbb\x0e\x85\xebD\xb2tB\xef\xbb_+\x03\x16\xd9\x1dA>\xe6eT04`\xa8\x17\x03\x03\x00I\xc9\xd5~\x1d\x7f\xca\xf1\xe7\x8c\xec\xf1 A\xcax\x91\xf9\xe92\x92]5\xb5\x9e\xbeG\xe0\xc8\x00\xe3\xe8l\x9e\x9c\xd2\xb1]\xe1\xc2\xa2\xa3\x98a\x11pxRr9N\x83"\xfa\xe6\xd9[\xa9{(f\x1a\xac\xf1!\x9f\xe7\x9e\xb5\xb0\xc2FH\x82'
2023-11-21 15:55:50,497 [541380] INFO  tests.twisted_test_helpers <twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>-><twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>: b'\x17\x03\x03\x00\xfa\xbb\x17vN\xf58\xc8\xf0"r\xdbE#B\xb0t%\xa7p\'5f\x84 \xf3s\xdc\x1dd\xbebu\x07\xbf\xc5\x1bU}\xfe\x0c\r\xe0\xde\xdc\x19\xb4\xcc1\xa1\xe54\xb5L\xc3\xa0U\xd4\xca\x0e\xc9\xf4\xeb\x1c,\x84\x945\x8e1\xd4\xd8\xa4\xd9\x01\x95\xa2\x052}-?\xc4\x98c^^T\xa4\x9b$0\xae\xe3\x94\xd1m\x98\xbb\xe7/\x93\xf6\x1b\x11bp\xcb3n%d\xa9\x9f\x87\x07?\x0e\xa1aq%la\x0b\x8f\'X\r#QV=\xa4e\x08\xb6\xb2\x9c\xaa\x15\x08\xac-\x9d\xe1\xbe\xa4o\xea$\x14\x99\xd3\x88\x12\x9b\x19\xe6\xfdA\x9e\xff \xe9\x82\xf8\xf1\xc7\xa5\x1e\xb5r\xca\x96\xca\xae\xd5\xd8\x9f\xcf\xfa+\xe6\x87{gx\x87@\xf7\x92A\x17\x8c\xf5\xe5\x83w\xbb\r\x00"w\xc5dw7\x94\x97\x1a\n\x97\xf4\xfe\x08\xfd,\xa9\xa2\xad\x7f\xc6\xb4\x8b\x1dM\x8f\xb3,?\xb3\xd6\x19\xbd_ln\x8a\xe0;\xb5\xa0e\xcc\xb5pU\xe1<\xbc\x17\x03\x03\x00\xfab\xbfK\x0c0\xd3\xef\x10\x08\'\xeb!\xdd\x9f\x15\x85\xd8\xc4\x14E\xfbyr({ba\x8a6\xd4\x05\xcd\x08\xac\xecp\x87\xfb\xae\x14~XC\xb0lH\x85\xd6\xfeu\xd3>\xcc\xb4O\xaaqt\xe2\xae\x15g\x90\x84\xa9i\x9agO\xe9F\x01\\\x851t\xa5\xf0m\xd5\xd9\x06\xc4\xa3\x97\x86K\xbc|Y\t\x05%\xc57\x92\x15\x90\x9f\xd4\x94>\x87\x7f\x19\nY\xa1i-\xc6\xbe\xb5=6~M\x9b\n\x1b\x1a\xaaR\x8c\x8cM\x9d\xaf\xa9\xc4c\xd7\xcfg\x0e\x08m_\x98\x01V\x9b\xf9F\x1f\x02\xdf9q@3\xb9c\xbf\xadj\xaaz!\xf7C\xc3=\xb9%\x83\xb9,\xb2\xfb\x84\xdf\xb0\'\xb4R\xa1\xc6\xe0\x8e4\xe0\xd8\x93\x17\xf4\x95O\xd0\xbf\x0f\xd6\xa2\xcf\xf00f\xb5\xb0,\x0c\x85\xa8\xd2\xe3\x05\x82H\xa0\xa8\x1a\xac\x97\xdc\xf8\x11\xc0\xc5\xd0E,\xa2W\xecU\xd3;\xa54\x076\xa8\xc7\x11\xf2\xf9\x93k\xbd\xce\xe8\x10\x1a\t\xe1Y\xc7\xa8\x95\xef'
2023-11-21 15:55:50,497 [541380] INFO  tests.twisted_test_helpers <twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>-><twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>: b'\x17\x03\x03\x00S\xa9Z\xf2\x9bJ\x1cjN~;`}/\x83\xc6x\xec\x8b\xee:\x0c=RTF\x06\xe0N\x10\xd0\xf7 \xeb\xda\xd0\x1b\xba\xd4\xa8\xf3"\x17b\x92O\xc8t\xce\xd1cu\xdb\xe6\x92\xec\xb8\xef\x86\xb1\xf7|\xe10\xfc\xff\xbd\xe4\xf2\xe0?)k\xb2\xccG\x1c\xfc5\xf6\x18D\x1d\xba\x17\x03\x03\x00\x1c~}\xfd\x81\x08BK\x84kn\xc3\xec%\x1f+\x1d!T u\xd4\x9bHL\xa6\xc8/\xa6'
2023-11-21 15:55:50,498 [541380] INFO  tests.test_httpproxy_twisted Completed request <Request at 0x7facb11e7110 method=GET uri=/abc clientproto=HTTP/1.1>
2023-11-21 15:55:50,498 [541380] INFO  tests.twisted_test_helpers <twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>-><twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>: b'\x17\x03\x03\x00\x16\x1dfu\xb5\r\xba\xf0\xc2D\x82_6\x8c\x1fwK\xad\x0fZ3_\x03\x17\x03\x03\x00\x13!$]T\x0e\xf3|\x8e\xb4\xc0\x1aO\xab\x82\xe7\xf7<\xff\\'
2023-11-21 15:55:50,498 [541380] INFO  tests.twisted_test_helpers FakeTransport: loseConnection(None)
2023-11-21 15:55:50,498 [541380] INFO  tests.twisted_test_helpers FakeTransport: Delaying disconnect until buffer is flushed
2023-11-21 15:55:50,498 [541380] ERROR tests.twisted_test_helpers Exception writing to protocol: 'NoneType' object has no attribute 'bio_read'
Traceback (most recent call last):
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 274, in _flushReceiveBIO
    bytes = self._tlsConnection.recv(2 ** 15)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/OpenSSL/SSL.py", line 2076, in recv
    self._raise_ssl_error(self._ssl, result)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/OpenSSL/SSL.py", line 1814, in _raise_ssl_error
    raise ZeroReturnError()
OpenSSL.SSL.ZeroReturnError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/dmr/workspace/sygnal/tests/twisted_test_helpers.py", line 155, in flush
    self.other.dataReceived(to_write)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/internet/endpoints.py", line 151, in dataReceived
    return self._wrappedProtocol.dataReceived(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/sygnal/helper/proxy/connectproxyclient_twisted.py", line 208, in dataReceived
    return self.wrapped_protocol.dataReceived(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 329, in dataReceived
    self._flushReceiveBIO()
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 285, in _flushReceiveBIO
    self._tlsShutdownFinished(None)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 375, in _tlsShutdownFinished
    self._flushSendBIO()
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 253, in _flushSendBIO
    bytes = self._tlsConnection.bio_read(2 ** 15)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'bio_read'
2023-11-21 15:55:50,500 [541380] INFO  tests.twisted_test_helpers <twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>-><twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>: b'\x17\x03\x03\x00\x16\x1dfu\xb5\r\xba\xf0\xc2D\x82_6\x8c\x1fwK\xad\x0fZ3_\x03\x17\x03\x03\x00\x13!$]T\x0e\xf3|\x8e\xb4\xc0\x1aO\xab\x82\xe7\xf7<\xff\\'
2023-11-21 15:55:50,500 [541380] ERROR tests.twisted_test_helpers Exception writing to protocol: 'NoneType' object has no attribute 'write'
Traceback (most recent call last):
  File "/home/dmr/workspace/sygnal/tests/twisted_test_helpers.py", line 155, in flush
    self.other.dataReceived(to_write)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/internet/endpoints.py", line 151, in dataReceived
    return self._wrappedProtocol.dataReceived(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/sygnal/helper/proxy/connectproxyclient_twisted.py", line 212, in dataReceived
    return self.http_setup_client.dataReceived(data)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/basic.py", line 555, in dataReceived
    why = self.rawDataReceived(data)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/web/http.py", line 733, in rawDataReceived
    self.handleResponsePart(data)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/web/http.py", line 702, in handleResponsePart
    self.__buffer.write(data)
    ^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'write'
2023-11-21 15:55:50,501 [541380] INFO  tests.twisted_test_helpers <twisted.internet.endpoints._WrappingProtocol object at 0x7facb11e6790>-><twisted.protocols.tls.TLSMemoryBIOProtocol object at 0x7facb11e5cd0>: b'\x17\x03\x03\x00\x13\x06kP\x0e\xa5Z9.\xf1T\xadplN\xaaf\xec0n'
2023-11-21 15:55:50,501 [541380] INFO  tests.twisted_test_helpers FakeTransport: loseConnection(None)
2023-11-21 15:55:50,501 [541380] INFO  tests.twisted_test_helpers FakeTransport: Delaying disconnect until buffer is flushed
2023-11-21 15:55:50,501 [541380] ERROR tests.twisted_test_helpers Exception writing to protocol: 'NoneType' object has no attribute 'bio_read'
Traceback (most recent call last):
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 274, in _flushReceiveBIO
    bytes = self._tlsConnection.recv(2 ** 15)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/OpenSSL/SSL.py", line 2076, in recv
    self._raise_ssl_error(self._ssl, result)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/OpenSSL/SSL.py", line 1814, in _raise_ssl_error
    raise ZeroReturnError()
OpenSSL.SSL.ZeroReturnError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/dmr/workspace/sygnal/tests/twisted_test_helpers.py", line 155, in flush
    self.other.dataReceived(to_write)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 329, in dataReceived
    self._flushReceiveBIO()
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 285, in _flushReceiveBIO
    self._tlsShutdownFinished(None)
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 375, in _tlsShutdownFinished
    self._flushSendBIO()
  File "/home/dmr/workspace/sygnal/.direnv/python-3.11.6/lib64/python3.11/site-packages/twisted/protocols/tls.py", line 253, in _flushSendBIO
    bytes = self._tlsConnection.bio_read(2 ** 15)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'bio_read'
                                      [OK]

-------------------------------------------------------------------------------
Ran 1 tests in 0.060s

PASSED (successes=1)

```

Discussion:

Patch:
diff --git a/changelog.d/380.bugfix b/changelog.d/380.bugfix
new file mode 100644
index 0000000..7ad24e7
--- /dev/null
+++ b/changelog.d/380.bugfix
@@ -0,0 +1,1 @@
+Don't delete android notification options specified in config.
diff --git a/changelog.d/381.misc b/changelog.d/381.misc
new file mode 100644
index 0000000..6110ec3
--- /dev/null
+++ b/changelog.d/381.misc
@@ -0,0 +1,1 @@
+Fix tests to allow Twisted versions >=23.10.
diff --git a/poetry.lock b/poetry.lock
index d42dd20..56e6a64 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -738,61 +738,61 @@ grpcio = "*"
 
 [[package]]
 name = "grpcio"
-version = "1.63.0"
+version = "1.64.0"
 description = "HTTP/2-based RPC framework"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "grpcio-1.63.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2e93aca840c29d4ab5db93f94ed0a0ca899e241f2e8aec6334ab3575dc46125c"},
-    {file = "grpcio-1.63.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:91b73d3f1340fefa1e1716c8c1ec9930c676d6b10a3513ab6c26004cb02d8b3f"},
-    {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:b3afbd9d6827fa6f475a4f91db55e441113f6d3eb9b7ebb8fb806e5bb6d6bd0d"},
-    {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f3f6883ce54a7a5f47db43289a0a4c776487912de1a0e2cc83fdaec9685cc9f"},
-    {file = "grpcio-1.63.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf8dae9cc0412cb86c8de5a8f3be395c5119a370f3ce2e69c8b7d46bb9872c8d"},
-    {file = "grpcio-1.63.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:08e1559fd3b3b4468486b26b0af64a3904a8dbc78d8d936af9c1cf9636eb3e8b"},
-    {file = "grpcio-1.63.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5c039ef01516039fa39da8a8a43a95b64e288f79f42a17e6c2904a02a319b357"},
-    {file = "grpcio-1.63.0-cp310-cp310-win32.whl", hash = "sha256:ad2ac8903b2eae071055a927ef74121ed52d69468e91d9bcbd028bd0e554be6d"},
-    {file = "grpcio-1.63.0-cp310-cp310-win_amd64.whl", hash = "sha256:b2e44f59316716532a993ca2966636df6fbe7be4ab6f099de6815570ebe4383a"},
-    {file = "grpcio-1.63.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:f28f8b2db7b86c77916829d64ab21ff49a9d8289ea1564a2b2a3a8ed9ffcccd3"},
-    {file = "grpcio-1.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:65bf975639a1f93bee63ca60d2e4951f1b543f498d581869922910a476ead2f5"},
-    {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b5194775fec7dc3dbd6a935102bb156cd2c35efe1685b0a46c67b927c74f0cfb"},
-    {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4cbb2100ee46d024c45920d16e888ee5d3cf47c66e316210bc236d5bebc42b3"},
-    {file = "grpcio-1.63.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff737cf29b5b801619f10e59b581869e32f400159e8b12d7a97e7e3bdeee6a2"},
-    {file = "grpcio-1.63.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd1e68776262dd44dedd7381b1a0ad09d9930ffb405f737d64f505eb7f77d6c7"},
-    {file = "grpcio-1.63.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f45f27f516548e23e4ec3fbab21b060416007dbe768a111fc4611464cc773f"},
-    {file = "grpcio-1.63.0-cp311-cp311-win32.whl", hash = "sha256:878b1d88d0137df60e6b09b74cdb73db123f9579232c8456f53e9abc4f62eb3c"},
-    {file = "grpcio-1.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:756fed02dacd24e8f488f295a913f250b56b98fb793f41d5b2de6c44fb762434"},
-    {file = "grpcio-1.63.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:93a46794cc96c3a674cdfb59ef9ce84d46185fe9421baf2268ccb556f8f81f57"},
-    {file = "grpcio-1.63.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a7b19dfc74d0be7032ca1eda0ed545e582ee46cd65c162f9e9fc6b26ef827dc6"},
-    {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8064d986d3a64ba21e498b9a376cbc5d6ab2e8ab0e288d39f266f0fca169b90d"},
-    {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:219bb1848cd2c90348c79ed0a6b0ea51866bc7e72fa6e205e459fedab5770172"},
-    {file = "grpcio-1.63.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2d60cd1d58817bc5985fae6168d8b5655c4981d448d0f5b6194bbcc038090d2"},
-    {file = "grpcio-1.63.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e350cb096e5c67832e9b6e018cf8a0d2a53b2a958f6251615173165269a91b0"},
-    {file = "grpcio-1.63.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:56cdf96ff82e3cc90dbe8bac260352993f23e8e256e063c327b6cf9c88daf7a9"},
-    {file = "grpcio-1.63.0-cp312-cp312-win32.whl", hash = "sha256:3a6d1f9ea965e750db7b4ee6f9fdef5fdf135abe8a249e75d84b0a3e0c668a1b"},
-    {file = "grpcio-1.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:d2497769895bb03efe3187fb1888fc20e98a5f18b3d14b606167dacda5789434"},
-    {file = "grpcio-1.63.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:fdf348ae69c6ff484402cfdb14e18c1b0054ac2420079d575c53a60b9b2853ae"},
-    {file = "grpcio-1.63.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a3abfe0b0f6798dedd2e9e92e881d9acd0fdb62ae27dcbbfa7654a57e24060c0"},
-    {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6ef0ad92873672a2a3767cb827b64741c363ebaa27e7f21659e4e31f4d750280"},
-    {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b416252ac5588d9dfb8a30a191451adbf534e9ce5f56bb02cd193f12d8845b7f"},
-    {file = "grpcio-1.63.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b77eaefc74d7eb861d3ffbdf91b50a1bb1639514ebe764c47773b833fa2d91"},
-    {file = "grpcio-1.63.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b005292369d9c1f80bf70c1db1c17c6c342da7576f1c689e8eee4fb0c256af85"},
-    {file = "grpcio-1.63.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cdcda1156dcc41e042d1e899ba1f5c2e9f3cd7625b3d6ebfa619806a4c1aadda"},
-    {file = "grpcio-1.63.0-cp38-cp38-win32.whl", hash = "sha256:01799e8649f9e94ba7db1aeb3452188048b0019dc37696b0f5ce212c87c560c3"},
-    {file = "grpcio-1.63.0-cp38-cp38-win_amd64.whl", hash = "sha256:6a1a3642d76f887aa4009d92f71eb37809abceb3b7b5a1eec9c554a246f20e3a"},
-    {file = "grpcio-1.63.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:75f701ff645858a2b16bc8c9fc68af215a8bb2d5a9b647448129de6e85d52bce"},
-    {file = "grpcio-1.63.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cacdef0348a08e475a721967f48206a2254a1b26ee7637638d9e081761a5ba86"},
-    {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:0697563d1d84d6985e40ec5ec596ff41b52abb3fd91ec240e8cb44a63b895094"},
-    {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6426e1fb92d006e47476d42b8f240c1d916a6d4423c5258ccc5b105e43438f61"},
-    {file = "grpcio-1.63.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48cee31bc5f5a31fb2f3b573764bd563aaa5472342860edcc7039525b53e46a"},
-    {file = "grpcio-1.63.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:50344663068041b34a992c19c600236e7abb42d6ec32567916b87b4c8b8833b3"},
-    {file = "grpcio-1.63.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:259e11932230d70ef24a21b9fb5bb947eb4703f57865a404054400ee92f42f5d"},
-    {file = "grpcio-1.63.0-cp39-cp39-win32.whl", hash = "sha256:a44624aad77bf8ca198c55af811fd28f2b3eaf0a50ec5b57b06c034416ef2d0a"},
-    {file = "grpcio-1.63.0-cp39-cp39-win_amd64.whl", hash = "sha256:166e5c460e5d7d4656ff9e63b13e1f6029b122104c1633d5f37eaea348d7356d"},
-    {file = "grpcio-1.63.0.tar.gz", hash = "sha256:f3023e14805c61bc439fb40ca545ac3d5740ce66120a678a3c6c2c55b70343d1"},
+    {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"},
+    {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"},
+    {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"},
+    {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"},
+    {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"},
+    {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"},
+    {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"},
+    {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"},
+    {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"},
+    {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"},
+    {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"},
+    {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"},
+    {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"},
+    {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"},
+    {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"},
+    {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"},
+    {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"},
+    {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"},
+    {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"},
+    {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"},
+    {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"},
+    {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"},
+    {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"},
+    {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"},
+    {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"},
+    {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"},
+    {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"},
+    {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"},
+    {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"},
+    {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"},
+    {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"},
+    {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"},
+    {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"},
+    {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"},
+    {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"},
+    {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"},
+    {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"},
+    {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"},
+    {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"},
+    {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"},
+    {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"},
+    {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"},
+    {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"},
+    {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"},
+    {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"},
+    {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"},
 ]
 
 [package.extras]
-protobuf = ["grpcio-tools (>=1.63.0)"]
+protobuf = ["grpcio-tools (>=1.64.0)"]
 
 [[package]]
 name = "h2"
@@ -1295,12 +1295,12 @@ twisted = ["twisted"]
 
 [[package]]
 name = "py-vapid"
-version = "1.9.0"
+version = "1.9.1"
 description = "Simple VAPID header generation library"
 optional = false
 python-versions = "*"
 files = [
-    {file = "py-vapid-1.9.0.tar.gz", hash = "sha256:0664ab7899742ef2b287397a4d461ef691ed0cc2f587205128d8cf617ffdb919"},
+    {file = "py_vapid-1.9.1.tar.gz", hash = "sha256:fe2b5461bf45c7baff1039df6981f03b87faa87cde0482addfa35b3fe636ac1b"},
 ]
 
 [package.dependencies]
@@ -1479,13 +1479,13 @@ files = [
 
 [[package]]
 name = "requests"
-version = "2.32.2"
+version = "2.32.3"
 description = "Python HTTP for Humans."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"},
-    {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"},
+    {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
+    {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
 ]
 
 [package.dependencies]
@@ -1540,13 +1540,13 @@ files = [
 
 [[package]]
 name = "sentry-sdk"
-version = "2.2.0"
+version = "2.3.1"
 description = "Python client for Sentry (https://sentry.io)"
 optional = false
 python-versions = ">=3.6"
 files = [
-    {file = "sentry_sdk-2.2.0-py2.py3-none-any.whl", hash = "sha256:674f58da37835ea7447fe0e34c57b4a4277fad558b0a7cb4a6c83bcb263086be"},
-    {file = "sentry_sdk-2.2.0.tar.gz", hash = "sha256:70eca103cf4c6302365a9d7cf522e7ed7720828910eb23d43ada8e50d1ecda9d"},
+    {file = "sentry_sdk-2.3.1-py2.py3-none-any.whl", hash = "sha256:c5aeb095ba226391d337dd42a6f9470d86c9fc236ecc71cfc7cd1942b45010c6"},
+    {file = "sentry_sdk-2.3.1.tar.gz", hash = "sha256:139a71a19f5e9eb5d3623942491ce03cf8ebc14ea2e39ba3e6fe79560d8a5b1f"},
 ]
 
 [package.dependencies]
@@ -1568,7 +1568,7 @@ django = ["django (>=1.8)"]
 falcon = ["falcon (>=1.4)"]
 fastapi = ["fastapi (>=0.79.0)"]
 flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"]
-grpcio = ["grpcio (>=1.21.1)"]
+grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"]
 httpx = ["httpx (>=0.16.0)"]
 huey = ["huey (>=2)"]
 huggingface-hub = ["huggingface-hub (>=0.22)"]
@@ -1614,19 +1614,18 @@ tests = ["coverage[toml] (>=5.0.2)", "pytest"]
 
 [[package]]
 name = "setuptools"
-version = "69.5.1"
+version = "70.0.0"
 description = "Easily download, build, install, upgrade, and uninstall Python packages"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
-    {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
+    {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"},
+    {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"},
 ]
 
 [package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
 
 [[package]]
 name = "six"
@@ -1752,13 +1751,13 @@ testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-po
 
 [[package]]
 name = "twisted"
-version = "23.8.0"
+version = "24.3.0"
 description = "An asynchronous networking framework written in Python"
 optional = false
-python-versions = ">=3.7.1"
+python-versions = ">=3.8.0"
 files = [
-    {file = "twisted-23.8.0-py3-none-any.whl", hash = "sha256:b8bdba145de120ffb36c20e6e071cce984e89fba798611ed0704216fb7f884cd"},
-    {file = "twisted-23.8.0.tar.gz", hash = "sha256:3c73360add17336a622c0d811c2a2ce29866b6e59b1125fd6509b17252098a24"},
+    {file = "twisted-24.3.0-py3-none-any.whl", hash = "sha256:039f2e6a49ab5108abd94de187fa92377abe5985c7a72d68d0ad266ba19eae63"},
+    {file = "twisted-24.3.0.tar.gz", hash = "sha256:6b38b6ece7296b5e122c9eb17da2eeab3d98a198f50ca9efd00fb03e5b4fd4ae"},
 ]
 
 [package.dependencies]
@@ -1768,19 +1767,18 @@ constantly = ">=15.1"
 hyperlink = ">=17.1.1"
 incremental = ">=22.10.0"
 twisted-iocpsupport = {version = ">=1.0.2,<2", markers = "platform_system == \"Windows\""}
-typing-extensions = ">=3.10.0"
+typing-extensions = ">=4.2.0"
 zope-interface = ">=5"
 
 [package.extras]
-all-non-platform = ["twisted[conch,contextvars,http2,serial,test,tls]", "twisted[conch,contextvars,http2,serial,test,tls]"]
+all-non-platform = ["twisted[conch,http2,serial,test,tls]", "twisted[conch,http2,serial,test,tls]"]
 conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"]
-contextvars = ["contextvars (>=2.4,<3)"]
 dev = ["coverage (>=6b1,<7)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "twisted[dev-release]", "twistedchecker (>=0.7,<1.0)"]
-dev-release = ["pydoctor (>=23.4.0,<23.5.0)", "pydoctor (>=23.4.0,<23.5.0)", "readthedocs-sphinx-ext (>=2.2,<3.0)", "readthedocs-sphinx-ext (>=2.2,<3.0)", "sphinx (>=5,<7)", "sphinx (>=5,<7)", "sphinx-rtd-theme (>=1.2,<2.0)", "sphinx-rtd-theme (>=1.2,<2.0)", "towncrier (>=22.12,<23.0)", "towncrier (>=22.12,<23.0)", "urllib3 (<2)", "urllib3 (<2)"]
+dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"]
 gtk-platform = ["pygobject", "pygobject", "twisted[all-non-platform]", "twisted[all-non-platform]"]
 http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"]
 macos-platform = ["pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "twisted[all-non-platform]", "twisted[all-non-platform]"]
-mypy = ["mypy (==0.981)", "mypy-extensions (==0.4.3)", "mypy-zope (==0.3.11)", "twisted[all-non-platform,dev]", "types-pyopenssl", "types-setuptools"]
+mypy = ["mypy (>=1.8,<2.0)", "mypy-zope (>=1.0.3,<1.1.0)", "twisted[all-non-platform,dev]", "types-pyopenssl", "types-setuptools"]
 osx-platform = ["twisted[macos-platform]", "twisted[macos-platform]"]
 serial = ["pyserial (>=3.0)", "pywin32 (!=226)"]
 test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"]
@@ -1868,13 +1866,13 @@ files = [
 
 [[package]]
 name = "types-requests"
-version = "2.31.0.20240406"
+version = "2.32.0.20240523"
 description = "Typing stubs for requests"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"},
-    {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"},
+    {file = "types-requests-2.32.0.20240523.tar.gz", hash = "sha256:26b8a6de32d9f561192b9942b41c0ab2d8010df5677ca8aa146289d11d505f57"},
+    {file = "types_requests-2.32.0.20240523-py3-none-any.whl", hash = "sha256:f19ed0e2daa74302069bbbbf9e82902854ffa780bc790742a810a9aaa52f65ec"},
 ]
 
 [package.dependencies]
@@ -1882,24 +1880,24 @@ urllib3 = ">=2"
 
 [[package]]
 name = "types-setuptools"
-version = "69.5.0.20240518"
+version = "70.0.0.20240524"
 description = "Typing stubs for setuptools"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "types-setuptools-69.5.0.20240518.tar.gz", hash = "sha256:2c6be895adc919fd0109414888c6ad5a972398d4eef59275416a1bcb0df6167f"},
-    {file = "types_setuptools-69.5.0.20240518-py3-none-any.whl", hash = "sha256:be23570baee0587f0c86ce970c15edc919718105c048f236dc6049327192a04d"},
+    {file = "types-setuptools-70.0.0.20240524.tar.gz", hash = "sha256:e31fee7b9d15ef53980526579ac6089b3ae51a005a281acf97178e90ac71aff6"},
+    {file = "types_setuptools-70.0.0.20240524-py3-none-any.whl", hash = "sha256:8f5379b9948682d72a9ab531fbe52932e84c4f38deda570255f9bae3edd766bc"},
 ]
 
 [[package]]
 name = "typing-extensions"
-version = "4.11.0"
+version = "4.12.0"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
-    {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
+    {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"},
+    {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"},
 ]
 
 [[package]]
@@ -2044,13 +2042,13 @@ multidict = ">=4.0"
 
 [[package]]
 name = "zipp"
-version = "3.18.2"
+version = "3.19.0"
 description = "Backport of pathlib-compatible object wrapper for zip files"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "zipp-3.18.2-py3-none-any.whl", hash = "sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e"},
-    {file = "zipp-3.18.2.tar.gz", hash = "sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059"},
+    {file = "zipp-3.19.0-py3-none-any.whl", hash = "sha256:96dc6ad62f1441bcaccef23b274ec471518daf4fbbc580341204936a5a3dddec"},
+    {file = "zipp-3.19.0.tar.gz", hash = "sha256:952df858fb3164426c976d9338d3961e8e8b3758e2e059e0f754b8c4262625ee"},
 ]
 
 [package.extras]
@@ -2077,39 +2075,47 @@ test = ["zope.testrunner"]
 
 [[package]]
 name = "zope-interface"
-version = "6.4"
+version = "6.4.post2"
 description = "Interfaces for Python"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "zope.interface-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72faa868fcfde49a29d287dce3c83180322467eecd725dd351098efe96e8d4bb"},
-    {file = "zope.interface-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:855b7233fa5d0d1f3be8c14fadf4718dee1c928e1d75f1584bea6ecec6dcc4af"},
-    {file = "zope.interface-6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36ee6e507a9fd4f1f0aab8e8dfc801d162e7211c27503cbfb47e1d558941a7fa"},
-    {file = "zope.interface-6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:604fa920478dfc0c76cdb7c203572400a8317ffcdac288245c408b42b3d9aee9"},
-    {file = "zope.interface-6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04bd4ee4766d285e83c6d8c042663a98efb934389e05ccd643fefb066c88a9d"},
-    {file = "zope.interface-6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4782e173c2fde4f649c2a9a68082445bc1f2c27f41907de06bf1ba82585847f2"},
-    {file = "zope.interface-6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:646cd83d24065d074f22f61fe101d20dbf4b729ca7831cc782ec986eb9156f93"},
-    {file = "zope.interface-6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f61ccbc26e08031d0e72b6a0cbf9b4030f035913cb2b39f940aa42eb8e0063"},
-    {file = "zope.interface-6.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:414e6dccdf4a5c96c0c98da68ba040dbf9ba7511b61b34e228f11b0ed90c439d"},
-    {file = "zope.interface-6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5092f2712e1fd07579fc3101b18e9c95857c853e836847598bf992c8e672434"},
-    {file = "zope.interface-6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21732994aa3ca43bbb6b36335c288023428a3c5b7322b637c7b0a03053937578"},
-    {file = "zope.interface-6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe636b49c333bfc5b0913590e36a2f151167c462fb36d9f4acc66029e45c974b"},
-    {file = "zope.interface-6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57f34b7997f8de7d2db08363eaccd05dad20f106e39efe95bed4fac84af2d022"},
-    {file = "zope.interface-6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6494dc0314e782ce4fb0e624b4ce2458f54d074382f50a920c7700c05cbcef28"},
-    {file = "zope.interface-6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cda82ab32f984985f09e4ec20a4f9665b26779a1b8e443b34a148de256f2052"},
-    {file = "zope.interface-6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f78e1eac48c4f4e0168a91cabcd8d1aedb972836df5c8769071fc6173294a0a3"},
-    {file = "zope.interface-6.4-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:8e246357f52952ae5fa950d19eda8572594c49e6cb1e5462508e6cec561a37de"},
-    {file = "zope.interface-6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93f28d84517dcd6c240979bd9b2f262a373832baef856fe663a24b9171d7f04d"},
-    {file = "zope.interface-6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cd56eb9a23767958c9a0654306b9a4a74def485f645b3a7378cc6ab661ef31c"},
-    {file = "zope.interface-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:502d2c9c4231d022b20225dba5c6c736236ed65e1d7e2f6f402b5aa6a7040ec9"},
-    {file = "zope.interface-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ee1e3ca6c98efe213a96dece89100a8aa52e210ac354861d8039d69bd1d6e5ff"},
-    {file = "zope.interface-6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e6b756663deade5270f67899753437b39d970f9eecd49e19fae3b880310cf0"},
-    {file = "zope.interface-6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f33af86ed460eb28dc9da1de1f3305795271a19c665161c1d973a737596b2081"},
-    {file = "zope.interface-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86e85eada0eb551950df05d72dc0e892320f14daa78bc434059e834d4b1f9300"},
-    {file = "zope.interface-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3945f4fda92c1b6fb0cb6eaaaf72599e5c2c2059654bdc42bc09c6e711c214c8"},
-    {file = "zope.interface-6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbbb290751f5c4ed81e54ae73fe8557c4a85973f5ab019edbb0f746244ecea6"},
-    {file = "zope.interface-6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4cc017206c1429a6d8fdd8a25c6efc15512065eec0a8d45c350df96a0911ed"},
-    {file = "zope_interface-6.4.tar.gz", hash = "sha256:b11f2b67ccc990a1522fa8cd3f5d185a068459f944ab2d0e7a1b15d31bcb4af4"},
+    {file = "zope.interface-6.4.post2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2eccd5bef45883802848f821d940367c1d0ad588de71e5cabe3813175444202c"},
+    {file = "zope.interface-6.4.post2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:762e616199f6319bb98e7f4f27d254c84c5fb1c25c908c2a9d0f92b92fb27530"},
+    {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ef8356f16b1a83609f7a992a6e33d792bb5eff2370712c9eaae0d02e1924341"},
+    {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e4fa5d34d7973e6b0efa46fe4405090f3b406f64b6290facbb19dcbf642ad6b"},
+    {file = "zope.interface-6.4.post2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d22fce0b0f5715cdac082e35a9e735a1752dc8585f005d045abb1a7c20e197f9"},
+    {file = "zope.interface-6.4.post2-cp310-cp310-win_amd64.whl", hash = "sha256:97e615eab34bd8477c3f34197a17ce08c648d38467489359cb9eb7394f1083f7"},
+    {file = "zope.interface-6.4.post2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:599f3b07bde2627e163ce484d5497a54a0a8437779362395c6b25e68c6590ede"},
+    {file = "zope.interface-6.4.post2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:136cacdde1a2c5e5bc3d0b2a1beed733f97e2dad8c2ad3c2e17116f6590a3827"},
+    {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47937cf2e7ed4e0e37f7851c76edeb8543ec9b0eae149b36ecd26176ff1ca874"},
+    {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0a6be264afb094975b5ef55c911379d6989caa87c4e558814ec4f5125cfa2e"},
+    {file = "zope.interface-6.4.post2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47654177e675bafdf4e4738ce58cdc5c6d6ee2157ac0a78a3fa460942b9d64a8"},
+    {file = "zope.interface-6.4.post2-cp311-cp311-win_amd64.whl", hash = "sha256:e2fb8e8158306567a3a9a41670c1ff99d0567d7fc96fa93b7abf8b519a46b250"},
+    {file = "zope.interface-6.4.post2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b912750b13d76af8aac45ddf4679535def304b2a48a07989ec736508d0bbfbde"},
+    {file = "zope.interface-6.4.post2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ac46298e0143d91e4644a27a769d1388d5d89e82ee0cf37bf2b0b001b9712a4"},
+    {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86a94af4a88110ed4bb8961f5ac72edf782958e665d5bfceaab6bf388420a78b"},
+    {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73f9752cf3596771c7726f7eea5b9e634ad47c6d863043589a1c3bb31325c7eb"},
+    {file = "zope.interface-6.4.post2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00b5c3e9744dcdc9e84c24ed6646d5cf0cf66551347b310b3ffd70f056535854"},
+    {file = "zope.interface-6.4.post2-cp312-cp312-win_amd64.whl", hash = "sha256:551db2fe892fcbefb38f6f81ffa62de11090c8119fd4e66a60f3adff70751ec7"},
+    {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96ac6b3169940a8cd57b4f2b8edcad8f5213b60efcd197d59fbe52f0accd66e"},
+    {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cebff2fe5dc82cb22122e4e1225e00a4a506b1a16fafa911142ee124febf2c9e"},
+    {file = "zope.interface-6.4.post2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33ee982237cffaf946db365c3a6ebaa37855d8e3ca5800f6f48890209c1cfefc"},
+    {file = "zope.interface-6.4.post2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:fbf649bc77510ef2521cf797700b96167bb77838c40780da7ea3edd8b78044d1"},
+    {file = "zope.interface-6.4.post2-cp37-cp37m-win_amd64.whl", hash = "sha256:4c0b208a5d6c81434bdfa0f06d9b667e5de15af84d8cae5723c3a33ba6611b82"},
+    {file = "zope.interface-6.4.post2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d3fe667935e9562407c2511570dca14604a654988a13d8725667e95161d92e9b"},
+    {file = "zope.interface-6.4.post2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a96e6d4074db29b152222c34d7eec2e2db2f92638d2b2b2c704f9e8db3ae0edc"},
+    {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:866a0f583be79f0def667a5d2c60b7b4cc68f0c0a470f227e1122691b443c934"},
+    {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fe919027f29b12f7a2562ba0daf3e045cb388f844e022552a5674fcdf5d21f1"},
+    {file = "zope.interface-6.4.post2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e0343a6e06d94f6b6ac52fbc75269b41dd3c57066541a6c76517f69fe67cb43"},
+    {file = "zope.interface-6.4.post2-cp38-cp38-win_amd64.whl", hash = "sha256:dabb70a6e3d9c22df50e08dc55b14ca2a99da95a2d941954255ac76fd6982bc5"},
+    {file = "zope.interface-6.4.post2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:706efc19f9679a1b425d6fa2b4bc770d976d0984335eaea0869bd32f627591d2"},
+    {file = "zope.interface-6.4.post2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d136e5b8821073e1a09dde3eb076ea9988e7010c54ffe4d39701adf0c303438"},
+    {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1730c93a38b5a18d24549bc81613223962a19d457cfda9bdc66e542f475a36f4"},
+    {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc2676312cc3468a25aac001ec727168994ea3b69b48914944a44c6a0b251e79"},
+    {file = "zope.interface-6.4.post2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a62fd6cd518693568e23e02f41816adedfca637f26716837681c90b36af3671"},
+    {file = "zope.interface-6.4.post2-cp39-cp39-win_amd64.whl", hash = "sha256:d3f7e001328bd6466b3414215f66dde3c7c13d8025a9c160a75d7b2687090d15"},
+    {file = "zope.interface-6.4.post2.tar.gz", hash = "sha256:1c207e6f6dfd5749a26f5a5fd966602d6b824ec00d2df84a7e9a924e8933654e"},
 ]
 
 [package.dependencies]
@@ -2143,4 +2149,4 @@ test = ["zope.i18nmessageid", "zope.testing", "zope.testrunner"]
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.8.0"
-content-hash = "8d19780a53139dc3e7d709512bd448dada105fa5ed3499ad1ecaaf61f11a694a"
+content-hash = "87931a1aa3338496e5aca523401aee9522fd19dca3d6c9eb4ad051401e2aefb8"
diff --git a/pyproject.toml b/pyproject.toml
index 2ef4830..29db48b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -83,8 +83,7 @@ pywebpush = ">=1.13.0"
 pyyaml = ">=5.1.1"
 sentry-sdk = ">=0.10.2"
 service_identity = ">=18.1.0"
-# Upper bound: tests fail on latest Twisted, see https://github.com/matrix-org/sygnal/issues/356
-Twisted = ">=19.7,<23.10"
+Twisted = ">=19.7"
 zope-interface = ">=5.0.0"
 
 [tool.poetry.group.dev.dependencies]
diff --git a/sygnal/gcmpushkin.py b/sygnal/gcmpushkin.py
index 010dd59..fa3360e 100644
--- a/sygnal/gcmpushkin.py
+++ b/sygnal/gcmpushkin.py
@@ -574,7 +574,10 @@ class GcmPushkin(ConcurrencyLimitedPushkin):
                 body["priority"] = "normal" if n.prio == "low" else "high"
             elif self.api_version is APIVersion.V1:
                 priority = {"priority": "normal" if n.prio == "low" else "high"}
-                body["android"] = priority
+                if "android" in body:
+                    body["android"].update(priority)
+                else:
+                    body["android"] = priority
 
             for retry_number in range(0, MAX_TRIES):
                 if self.api_version is APIVersion.Legacy:


Test patch:
diff --git a/tests/test_gcm.py b/tests/test_gcm.py
index 60c3d96..aa35f01 100644
--- a/tests/test_gcm.py
+++ b/tests/test_gcm.py
@@ -174,6 +174,13 @@ class GcmTestCase(testutils.TestCase):
             "project_id": "example_project",
             "service_account_file": self.service_account_file.name,
             "fcm_options": {
+                "android": {
+                    "notification": {
+                        "body": {
+                            "test body",
+                        },
+                    },
+                },
                 "apns": {
                     "payload": {
                         "aps": {
@@ -286,7 +293,14 @@ class GcmTestCase(testutils.TestCase):
                         "unread": "2",
                         "missed_calls": "1",
                     },
-                    "android": {"priority": "high"},
+                    "android": {
+                        "notification": {
+                            "body": {
+                                "test body",
+                            },
+                        },
+                        "priority": "high",
+                    },
                     "apns": {
                         "payload": {
                             "aps": {
diff --git a/tests/test_httpproxy_twisted.py b/tests/test_httpproxy_twisted.py
index 08418a2..2a1dc04 100644
--- a/tests/test_httpproxy_twisted.py
+++ b/tests/test_httpproxy_twisted.py
@@ -14,6 +14,8 @@
 # limitations under the License.
 import logging
 
+import twisted
+from incremental import Version
 from twisted.internet import interfaces  # noqa: F401
 from twisted.internet.protocol import Factory
 from twisted.protocols.tls import TLSMemoryBIOFactory
@@ -64,7 +66,7 @@ class SygnalTwistedProxyTests(TestCase):
             IProtocol: the server Protocol returned by server_factory
         """
         if ssl:
-            server_factory = _wrap_server_factory_for_tls(server_factory)
+            server_factory = _wrap_server_factory_for_tls(server_factory, self.reactor)
 
         server_protocol = server_factory.buildProtocol(None)
 
@@ -149,7 +151,9 @@ class SygnalTwistedProxyTests(TestCase):
         request.finish()
 
         # now we can replace the proxy channel with a new, SSL-wrapped HTTP channel
-        ssl_factory = _wrap_server_factory_for_tls(_get_test_protocol_factory())
+        ssl_factory = _wrap_server_factory_for_tls(
+            _get_test_protocol_factory(), self.reactor
+        )
         ssl_protocol = ssl_factory.buildProtocol(None)
         http_server = ssl_protocol.wrappedProtocol
 
@@ -188,7 +192,7 @@ class SygnalTwistedProxyTests(TestCase):
         self.assertEqual(body, b"result")
 
 
-def _wrap_server_factory_for_tls(factory, sanlist=None):
+def _wrap_server_factory_for_tls(factory, clock, sanlist=None):
     """Wrap an existing Protocol Factory with a test TLSMemoryBIOFactory
 
     The resultant factory will create a TLS server which presents a certificate
@@ -205,9 +209,15 @@ def _wrap_server_factory_for_tls(factory, sanlist=None):
         sanlist = [b"DNS:test.com"]
 
     connection_creator = TestServerTLSConnectionFactory(sanlist=sanlist)
-    return TLSMemoryBIOFactory(
-        connection_creator, isClient=False, wrappedFactory=factory
-    )
+    # Twisted > 23.8.0 has a different API that accepts a clock.
+    if twisted.version <= Version("Twisted", 23, 8, 0):  # type: ignore[attr-defined]
+        return TLSMemoryBIOFactory(
+            connection_creator, isClient=False, wrappedFactory=factory
+        )
+    else:
+        return TLSMemoryBIOFactory(
+            connection_creator, isClient=False, wrappedFactory=factory, clock=clock
+        )
 
 
 def _get_test_protocol_factory():
diff --git a/tests/testutils.py b/tests/testutils.py
index 29467be..cbeca64 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -18,6 +18,8 @@ from threading import Condition
 from typing import BinaryIO, Dict, List, Optional, Union
 
 import attr
+import twisted
+from incremental import Version
 from twisted.internet._resolver import SimpleResolverComplexifier
 from twisted.internet.defer import ensureDeferred, fail, succeed
 from twisted.internet.error import DNSLookupError
@@ -231,6 +233,16 @@ class ExtendedMemoryReactorClock(MemoryReactorClock):
 
         self.nameResolver = SimpleResolverComplexifier(FakeResolver())
 
+        # In order for the TLS protocol tests to work, modify _get_default_clock
+        # on newer Twisted versions to use the test reactor's clock.
+        #
+        # This is *super* dirty since it is never undone and relies on the next
+        # test to overwrite it.
+        if twisted.version > Version("Twisted", 23, 8, 0):  # type: ignore[attr-defined]
+            from twisted.protocols import tls
+
+            tls._get_default_clock = lambda: self
+
     def installNameResolver(self, resolver):
         # It is not expected that this gets called.
         raise RuntimeError(resolver)