diff --git a/.env b/.env index 51f39e7..ec9c560 100644 --- a/.env +++ b/.env @@ -9,6 +9,7 @@ APIKEY=sk-85880595fc714d63bfd0b025e917bd26#千问apikey CALLBACK_BASE_URL=https://dissonant-destinee-nonsensibly.ngrok-free.dev # 固定隧道代理(socks5h):不填登录页代理时后端自动用此处,传给 7006 -TUNNEL_PROXY=218.78.109.253:16816 +# TUNNEL_PROXY=218.78.109.253:16816 +TUNNEL_PROXY=14.103.95.78:16816 TUNNEL_PROXY_USERNAME=fawbjjkk TUNNEL_PROXY_PASSWORD=5hz6avfb \ No newline at end of file diff --git a/.env.example b/.env.example index b430ee7..7e49b62 100644 --- a/.env.example +++ b/.env.example @@ -20,7 +20,7 @@ SLIDER_VERIFY_KEY=408449830 # 固定隧道代理(推荐):socks5h + 用户名密码,与 requests 示例一致 # 不填登录页代理时,后端自动使用此处配置 -TUNNEL_PROXY=218.78.109.253:16816 +TUNNEL_PROXY=14.103.95.78:16816 TUNNEL_PROXY_USERNAME=fawbjjkk TUNNEL_PROXY_PASSWORD=5hz6avfb @@ -36,6 +36,7 @@ TUNNEL_PROXY_PASSWORD=5hz6avfb # 消息实时回调(主入口):设置后向 7006 注册 SetCallback,新消息由 7006 POST 到本服务,不再走 WS # 需为 7006 能访问到的公网地址,例如 https://your-domain.com +# 项目启动时会自动为所有已知账号 key 重新注册回调,重启后无需再手动设置 # CALLBACK_BASE_URL=https://your-domain.com # 千问 API Key(用于个性化问候等),优先 QWEN_API_KEY,其次 APIKEY diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/LICENSE.txt b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/LICENSE.txt new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/METADATA b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/METADATA new file mode 100644 index 0000000..76eaeda --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/METADATA @@ -0,0 +1,101 @@ +Metadata-Version: 2.1 +Name: httpx-socks +Version: 0.9.2 +Summary: Proxy (HTTP, SOCKS) transports for httpx +Home-page: https://github.com/romis2012/httpx-socks +Author: Roman Snegirev +Author-email: snegiryev@gmail.com +License: Apache 2 +Keywords: httpx asyncio socks socks5 socks4 http proxy +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Requires-Dist: httpx<0.28.0,>=0.21.0 +Requires-Dist: httpcore<2.0,>=0.17.3 +Requires-Dist: python-socks>=2.0.0 +Provides-Extra: asyncio +Requires-Dist: async-timeout>=3.0.1; extra == "asyncio" +Provides-Extra: trio +Requires-Dist: trio>=0.16.0; extra == "trio" + +# httpx-socks + +[![CI](https://github.com/romis2012/httpx-socks/actions/workflows/ci.yml/badge.svg)](https://github.com/romis2012/httpx-socks/actions/workflows/ci.yml) +[![Coverage Status](https://codecov.io/gh/romis2012/httpx-socks/branch/master/graph/badge.svg)](https://codecov.io/gh/romis2012/httpx-socks) +[![PyPI version](https://badge.fury.io/py/httpx-socks.svg)](https://pypi.python.org/pypi/httpx-socks) + + +The `httpx-socks` package provides proxy transports for [httpx](https://github.com/encode/httpx) client. +SOCKS4(a), SOCKS5(h), HTTP (tunneling) proxy supported. +It uses [python-socks](https://github.com/romis2012/python-socks) for core proxy functionality. + + +## Requirements +- Python >= 3.6 +- httpx>=0.21.0 +- python-socks>=2.0.0 +- async-timeout>=3.0.1 (optional) +- trio>=0.16.0 (optional) + + +## Installation + +only sync proxy support: +``` +pip install httpx-socks +``` + +to include optional asyncio support (it requires async-timeout): +``` +pip install httpx-socks[asyncio] +``` + +to include optional trio support: +``` +pip install httpx-socks[trio] +``` + + +## Usage + +#### sync transport +```python +import httpx +from httpx_socks import SyncProxyTransport + +def fetch(url): + transport = SyncProxyTransport.from_url('socks5://user:password@127.0.0.1:1080') + with httpx.Client(transport=transport) as client: + res = client.get(url) + return res.text +``` + +#### async transport (asyncio, trio) +```python +import httpx +from httpx_socks import AsyncProxyTransport + +async def fetch(url): + transport = AsyncProxyTransport.from_url('socks5://user:password@127.0.0.1:1080') + async with httpx.AsyncClient(transport=transport) as client: + res = await client.get(url) + return res.text +``` + +#### secure proxy connections (aka "HTTPS proxies", experimental feature, both sync and async support) +```python +import ssl +import httpx +from httpx_socks import AsyncProxyTransport + +async def fetch(url): + proxy_ssl = ssl.SSLContext(ssl.PROTOCOL_TLS) + proxy_ssl.verify_mode = ssl.CERT_REQUIRED + proxy_ssl.load_verify_locations(...) + + transport = AsyncProxyTransport.from_url('http://user:password@127.0.0.1:8080', proxy_ssl=proxy_ssl) + async with httpx.AsyncClient(transport=transport) as client: + res = await client.get(url) + return res.text +``` diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/RECORD b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/RECORD new file mode 100644 index 0000000..dca8eac --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/RECORD @@ -0,0 +1,19 @@ +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/httpx_socks/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/httpx_socks/_async_transport.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/httpx_socks/_sync_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.cpython-39.pyc,, +httpx_socks-0.9.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +httpx_socks-0.9.2.dist-info/LICENSE.txt,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357 +httpx_socks-0.9.2.dist-info/METADATA,sha256=nYzxhiQ-G1XYBKP58owrMZWfJhGYCm6aKh7G4rn26zM,3007 +httpx_socks-0.9.2.dist-info/RECORD,, +httpx_socks-0.9.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +httpx_socks-0.9.2.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91 +httpx_socks-0.9.2.dist-info/top_level.txt,sha256=w-NjPGd_PneJsrpI5kELbCCdbD3YlL8mgwP3WkawJ0s,12 +httpx_socks/__init__.py,sha256=_iMI2My4TRAD66ZQgHrM3-CVQ8M-gGzLGVBibPF6hl0,448 +httpx_socks/_async_proxy.py,sha256=RqnPas10Ux-VRACZu2qZsXsCxF-a5ZoeSw1VLmaSPYc,8452 +httpx_socks/_async_transport.py,sha256=QEcg3Pb0hvMvcebKbolMdhrfdVuO1m-V_9-PrFOPF9A,3150 +httpx_socks/_sync_proxy.py,sha256=vMVrR2-qKxmRHhn8lKFXXz-dDolAZIi96PeBp4YQF3g,6544 +httpx_socks/_sync_stream.py,sha256=aaAH6bSPkAdHeDf2rvNDx_-lbu4kXFg1u2OMBNzQ3QQ,1421 +httpx_socks/_sync_transport.py,sha256=FiqT2gekBI3V1Jq_uoFRa7biksLDljcEPrNFPq0LX-c,3054 diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/REQUESTED b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/WHEEL new file mode 100644 index 0000000..9b78c44 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.3.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/top_level.txt new file mode 100644 index 0000000..002ac9b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks-0.9.2.dist-info/top_level.txt @@ -0,0 +1 @@ +httpx_socks diff --git a/.venv/lib/python3.9/site-packages/httpx_socks/__init__.py b/.venv/lib/python3.9/site-packages/httpx_socks/__init__.py new file mode 100644 index 0000000..1d33f3a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks/__init__.py @@ -0,0 +1,23 @@ +__title__ = 'httpx-socks' +__version__ = '0.9.2' + +from python_socks import ( + ProxyError, + ProxyTimeoutError, + ProxyConnectionError, + ProxyType +) + +from ._sync_transport import SyncProxyTransport +from ._async_transport import AsyncProxyTransport + +__all__ = ( + '__title__', + '__version__', + 'SyncProxyTransport', + 'AsyncProxyTransport', + 'ProxyError', + 'ProxyTimeoutError', + 'ProxyConnectionError', + 'ProxyType', +) diff --git a/.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py b/.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py new file mode 100644 index 0000000..0c6512f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py @@ -0,0 +1,258 @@ +import ssl + +import sniffio +from httpcore import ( + AsyncConnectionPool, + Origin, + AsyncConnectionInterface, + Request, + Response, + default_ssl_context, + AsyncHTTP11Connection, + ConnectionNotAvailable, +) +from httpcore import AsyncNetworkStream +from httpcore._synchronization import AsyncLock +from python_socks import ProxyType, parse_proxy_url + + +class AsyncProxy(AsyncConnectionPool): + def __init__( + self, + *, + proxy_type: ProxyType, + proxy_host: str, + proxy_port: int, + username=None, + password=None, + rdns=None, + proxy_ssl: ssl.SSLContext = None, + loop=None, + **kwargs, + ): + self._proxy_type = proxy_type + self._proxy_host = proxy_host + self._proxy_port = proxy_port + self._username = username + self._password = password + self._rdns = rdns + self._proxy_ssl = proxy_ssl + self._loop = loop + + super().__init__(**kwargs) + + def create_connection(self, origin: Origin) -> AsyncConnectionInterface: + return AsyncProxyConnection( + proxy_type=self._proxy_type, + proxy_host=self._proxy_host, + proxy_port=self._proxy_port, + username=self._username, + password=self._password, + rdns=self._rdns, + proxy_ssl=self._proxy_ssl, + loop=self._loop, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + ) + + @classmethod + def from_url(cls, url, **kwargs): + proxy_type, host, port, username, password = parse_proxy_url(url) + return cls( + proxy_type=proxy_type, + proxy_host=host, + proxy_port=port, + username=username, + password=password, + **kwargs, + ) + + +class AsyncProxyConnection(AsyncConnectionInterface): + def __init__( + self, + *, + proxy_type: ProxyType, + proxy_host: str, + proxy_port: int, + username=None, + password=None, + rdns=None, + proxy_ssl: ssl.SSLContext = None, + loop=None, + remote_origin: Origin, + ssl_context: ssl.SSLContext, + keepalive_expiry: float = None, + http1: bool = True, + http2: bool = False, + ) -> None: + + if ssl_context is None: # pragma: no cover + ssl_context = default_ssl_context() + + self._proxy_type = proxy_type + self._proxy_host = proxy_host + self._proxy_port = proxy_port + self._username = username + self._password = password + self._rdns = rdns + self._proxy_ssl = proxy_ssl + self._loop = loop + + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._connect_lock = AsyncLock() + self._connection = None + self._connect_failed: bool = False + + async def handle_async_request(self, request: Request) -> Response: + timeouts = request.extensions.get('timeout', {}) + timeout = timeouts.get('connect', None) + + try: + async with self._connect_lock: + if self._connection is None: + stream = await self._connect_via_proxy( + origin=self._remote_origin, + connect_timeout=timeout, + ) + + ssl_object = stream.get_extra_info("ssl_object") + http2_negotiated = ( + ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from httpcore import AsyncHTTP2Connection + + self._connection = AsyncHTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = AsyncHTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + elif not self._connection.is_available(): # pragma: no cover + raise ConnectionNotAvailable() + except BaseException as exc: + self._connect_failed = True + raise exc + + return await self._connection.handle_async_request(request) + + async def _connect_via_proxy(self, origin, connect_timeout) -> AsyncNetworkStream: + scheme, hostname, port = origin.scheme, origin.host, origin.port + + ssl_context = self._ssl_context if scheme == b'https' else None + host = hostname.decode('ascii') # ? + + return await self._open_stream( + host=host, + port=port, + connect_timeout=connect_timeout, + ssl_context=ssl_context, + ) + + async def _open_stream(self, host, port, connect_timeout, ssl_context): + backend = sniffio.current_async_library() + + if backend == 'asyncio': + return await self._open_aio_stream(host, port, connect_timeout, ssl_context) + + if backend == 'trio': + return await self._open_trio_stream(host, port, connect_timeout, ssl_context) + + # Curio support has been dropped in httpcore 0.14.0 + # if backend == 'curio': + # return await self._open_curio_stream(host, port, connect_timeout, ssl_context) + + raise RuntimeError(f'Unsupported concurrency backend {backend!r}') # pragma: no cover + + async def _open_aio_stream(self, host, port, connect_timeout, ssl_context): + from httpcore._backends.anyio import AnyIOStream + from python_socks.async_.anyio import Proxy + + proxy = Proxy.create( + proxy_type=self._proxy_type, + host=self._proxy_host, + port=self._proxy_port, + username=self._username, + password=self._password, + rdns=self._rdns, + proxy_ssl=self._proxy_ssl, + ) + + proxy_stream = await proxy.connect( + host, + port, + dest_ssl=ssl_context, + timeout=connect_timeout, + ) + + return AnyIOStream(proxy_stream.anyio_stream) + + async def _open_trio_stream(self, host, port, connect_timeout, ssl_context): + from httpcore._backends.trio import TrioStream + from python_socks.async_.trio.v2 import Proxy + + proxy = Proxy.create( + proxy_type=self._proxy_type, + host=self._proxy_host, + port=self._proxy_port, + username=self._username, + password=self._password, + rdns=self._rdns, + proxy_ssl=self._proxy_ssl, + ) + + proxy_stream = await proxy.connect( + host, + port, + dest_ssl=ssl_context, + timeout=connect_timeout, + ) + + return TrioStream(proxy_stream.trio_stream) + + async def aclose(self) -> None: + if self._connection is not None: + await self._connection.aclose() + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def is_available(self) -> bool: + if self._connection is None: # pragma: no cover + # return self._http2 and (self._remote_origin.scheme == b"https" or not self._http1) + return False + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: # pragma: no cover + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() diff --git a/.venv/lib/python3.9/site-packages/httpx_socks/_async_transport.py b/.venv/lib/python3.9/site-packages/httpx_socks/_async_transport.py new file mode 100644 index 0000000..b15405b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks/_async_transport.py @@ -0,0 +1,104 @@ +import ssl +import typing + +import httpcore +from httpx import AsyncBaseTransport, Request, Response, AsyncByteStream, Limits + +# noinspection PyProtectedMember +from httpx._config import DEFAULT_LIMITS, create_ssl_context +# noinspection PyProtectedMember +from httpx._transports.default import AsyncResponseStream, map_httpcore_exceptions +from python_socks import ProxyType, parse_proxy_url + +from ._async_proxy import AsyncProxy + + +class AsyncProxyTransport(AsyncBaseTransport): + def __init__( + self, + *, + proxy_type: ProxyType, + proxy_host: str, + proxy_port: int, + username=None, + password=None, + rdns=None, + proxy_ssl: ssl.SSLContext = None, + verify=True, + cert=None, + trust_env: bool = True, + limits: Limits = DEFAULT_LIMITS, + **kwargs, + ): + ssl_context = create_ssl_context( + verify=verify, + cert=cert, + trust_env=trust_env, + http2=kwargs.get('http2', False), + ) + + self._pool = AsyncProxy( + proxy_type=proxy_type, + proxy_host=proxy_host, + proxy_port=proxy_port, + username=username, + password=password, + rdns=rdns, + proxy_ssl=proxy_ssl, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + **kwargs, + ) + + async def handle_async_request(self, request: Request) -> Response: + assert isinstance(request.stream, AsyncByteStream) + + req = httpcore.Request( + method=request.method, + url=httpcore.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + + with map_httpcore_exceptions(): + resp = await self._pool.handle_async_request(req) + + assert isinstance(resp.stream, typing.AsyncIterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=AsyncResponseStream(resp.stream), + extensions=resp.extensions, + ) + + @classmethod + def from_url(cls, url, **kwargs): + proxy_type, host, port, username, password = parse_proxy_url(url) + return cls( + proxy_type=proxy_type, + proxy_host=host, + proxy_port=port, + username=username, + password=password, + **kwargs, + ) + + async def aclose(self) -> None: + await self._pool.aclose() # pragma: no cover + + async def __aenter__(self): + await self._pool.__aenter__() + return self + + async def __aexit__(self, exc_type=None, exc_value=None, traceback=None): + with map_httpcore_exceptions(): + await self._pool.__aexit__(exc_type, exc_value, traceback) diff --git a/.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py b/.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py new file mode 100644 index 0000000..73c6429 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py @@ -0,0 +1,206 @@ +import ssl + +from httpcore import ( + ConnectionPool, + Origin, + ConnectionInterface, + Request, + Response, + default_ssl_context, + HTTP11Connection, + ConnectionNotAvailable, +) +# from httpcore.backends.sync import SyncStream +from ._sync_stream import SyncStream +from httpcore._synchronization import Lock + +from python_socks import ProxyType, parse_proxy_url +from python_socks.sync.v2 import Proxy + + +class SyncProxy(ConnectionPool): + def __init__( + self, + *, + proxy_type: ProxyType, + proxy_host: str, + proxy_port: int, + username=None, + password=None, + rdns=None, + proxy_ssl: ssl.SSLContext = None, + **kwargs, + ): + self._proxy_type = proxy_type + self._proxy_host = proxy_host + self._proxy_port = proxy_port + self._username = username + self._password = password + self._rdns = rdns + self._proxy_ssl = proxy_ssl + + super().__init__(**kwargs) + + def create_connection(self, origin: Origin) -> ConnectionInterface: + return SyncProxyConnection( + proxy_type=self._proxy_type, + proxy_host=self._proxy_host, + proxy_port=self._proxy_port, + username=self._username, + password=self._password, + rdns=self._rdns, + proxy_ssl=self._proxy_ssl, + remote_origin=origin, + ssl_context=self._ssl_context, + keepalive_expiry=self._keepalive_expiry, + http1=self._http1, + http2=self._http2, + ) + + @classmethod + def from_url(cls, url, **kwargs): + proxy_type, host, port, username, password = parse_proxy_url(url) + return cls( + proxy_type=proxy_type, + proxy_host=host, + proxy_port=port, + username=username, + password=password, + **kwargs, + ) + + +class SyncProxyConnection(ConnectionInterface): + def __init__( + self, + *, + proxy_type: ProxyType, + proxy_host: str, + proxy_port: int, + username=None, + password=None, + rdns=None, + proxy_ssl: ssl.SSLContext = None, + remote_origin: Origin, + ssl_context: ssl.SSLContext, + keepalive_expiry: float = None, + http1: bool = True, + http2: bool = False, + ) -> None: + + if ssl_context is None: # pragma: no cover + ssl_context = default_ssl_context() + + self._proxy_type = proxy_type + self._proxy_host = proxy_host + self._proxy_port = proxy_port + self._username = username + self._password = password + self._rdns = rdns + self._proxy_ssl = proxy_ssl + + self._remote_origin = remote_origin + self._ssl_context = ssl_context + self._keepalive_expiry = keepalive_expiry + self._http1 = http1 + self._http2 = http2 + + self._connect_lock = Lock() + self._connection = None + self._connect_failed: bool = False + + def handle_request(self, request: Request) -> Response: + timeouts = request.extensions.get('timeout', {}) + timeout = timeouts.get('connect', None) + + try: + with self._connect_lock: + if self._connection is None: + stream = self._connect_via_proxy( + origin=self._remote_origin, + connect_timeout=timeout, + ) + + ssl_object = stream.get_extra_info('ssl_object') + http2_negotiated = ( + ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2" + ) + if http2_negotiated or (self._http2 and not self._http1): + from httpcore import HTTP2Connection + + self._connection = HTTP2Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + else: + self._connection = HTTP11Connection( + origin=self._remote_origin, + stream=stream, + keepalive_expiry=self._keepalive_expiry, + ) + elif not self._connection.is_available(): # pragma: no cover + raise ConnectionNotAvailable() + except BaseException as exc: + self._connect_failed = True + raise exc + + return self._connection.handle_request(request) + + def _connect_via_proxy(self, origin: Origin, connect_timeout: int): + scheme, hostname, port = origin.scheme, origin.host, origin.port + + ssl_context = self._ssl_context if scheme == b'https' else None + host = hostname.decode('ascii') + + proxy = Proxy.create( + proxy_type=self._proxy_type, + host=self._proxy_host, + port=self._proxy_port, + username=self._username, + password=self._password, + rdns=self._rdns, + proxy_ssl=self._proxy_ssl, + ) + + proxy_stream = proxy.connect( + host, + port, + dest_ssl=ssl_context, + timeout=connect_timeout, + ) + + return SyncStream(sock=proxy_stream.socket) + + def close(self) -> None: + if self._connection is not None: + self._connection.close() + + def can_handle_request(self, origin: Origin) -> bool: + return origin == self._remote_origin + + def is_available(self) -> bool: + if self._connection is None: # pragma: no cover + # return self._http2 and (self._remote_origin.scheme == b"https" or not self._http1) + return False + return self._connection.is_available() + + def has_expired(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.has_expired() + + def is_idle(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_idle() + + def is_closed(self) -> bool: + if self._connection is None: + return self._connect_failed + return self._connection.is_closed() + + def info(self) -> str: # pragma: no cover + if self._connection is None: + return "CONNECTION FAILED" if self._connect_failed else "CONNECTING" + return self._connection.info() diff --git a/.venv/lib/python3.9/site-packages/httpx_socks/_sync_stream.py b/.venv/lib/python3.9/site-packages/httpx_socks/_sync_stream.py new file mode 100644 index 0000000..606eaa2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks/_sync_stream.py @@ -0,0 +1,40 @@ +import ssl +import typing + +from httpcore._backends.sync import SyncStream as CoreSyncStream +from httpcore._utils import is_socket_readable +from python_socks.sync.v2._ssl_transport import SSLTransport + + +class SyncStream(CoreSyncStream): + def get_extra_info(self, info: str) -> typing.Any: + if info == "ssl_object": + if isinstance(self._sock, ssl.SSLSocket): + # noinspection PyProtectedMember + return self._sock._sslobj # type: ignore + if isinstance(self._sock, SSLTransport): + return self._sock.sslobj # type: ignore + return None + + if info == "client_addr": # pragma: nocover + if isinstance(self._sock, SSLTransport): + return self._sock.socket.getsockname() + else: + return self._sock.getsockname() + + if info == "server_addr": # pragma: nocover + if isinstance(self._sock, SSLTransport): + return self._sock.socket.getpeername() + else: + return self._sock.getpeername() + + if info == "socket": # pragma: nocover + return self._sock # ??? + + if info == "is_readable": + if isinstance(self._sock, SSLTransport): + return is_socket_readable(self._sock.socket) + else: + return is_socket_readable(self._sock) + + return None # pragma: nocover diff --git a/.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py b/.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py new file mode 100644 index 0000000..179f86b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py @@ -0,0 +1,105 @@ +import ssl +import typing + +import httpcore + +from httpx import BaseTransport, Request, Response, SyncByteStream, Limits + +# noinspection PyProtectedMember +from httpx._config import DEFAULT_LIMITS, create_ssl_context +# noinspection PyProtectedMember +from httpx._transports.default import ResponseStream, map_httpcore_exceptions + +from ._sync_proxy import SyncProxy +from python_socks import ProxyType, parse_proxy_url + + +class SyncProxyTransport(BaseTransport): + def __init__( + self, + *, + proxy_type: ProxyType, + proxy_host: str, + proxy_port: int, + username=None, + password=None, + rdns=None, + proxy_ssl: ssl.SSLContext = None, + verify=True, + cert=None, + trust_env: bool = True, + limits: Limits = DEFAULT_LIMITS, + **kwargs, + ): + ssl_context = create_ssl_context( + verify=verify, + cert=cert, + trust_env=trust_env, + http2=kwargs.get('http2', False), + ) + + self._pool = SyncProxy( + proxy_type=proxy_type, + proxy_host=proxy_host, + proxy_port=proxy_port, + username=username, + password=password, + rdns=rdns, + proxy_ssl=proxy_ssl, + ssl_context=ssl_context, + max_connections=limits.max_connections, + max_keepalive_connections=limits.max_keepalive_connections, + keepalive_expiry=limits.keepalive_expiry, + **kwargs, + ) + + def handle_request(self, request: Request) -> Response: + assert isinstance(request.stream, SyncByteStream) + + req = httpcore.Request( + method=request.method, + url=httpcore.URL( + scheme=request.url.raw_scheme, + host=request.url.raw_host, + port=request.url.port, + target=request.url.raw_path, + ), + headers=request.headers.raw, + content=request.stream, + extensions=request.extensions, + ) + + with map_httpcore_exceptions(): + resp = self._pool.handle_request(req) + + assert isinstance(resp.stream, typing.Iterable) + + return Response( + status_code=resp.status, + headers=resp.headers, + stream=ResponseStream(resp.stream), + extensions=resp.extensions, + ) + + @classmethod + def from_url(cls, url, **kwargs): + proxy_type, host, port, username, password = parse_proxy_url(url) + return cls( + proxy_type=proxy_type, + proxy_host=host, + proxy_port=port, + username=username, + password=password, + **kwargs, + ) + + def close(self) -> None: + self._pool.close() # pragma: no cover + + def __enter__(self): + self._pool.__enter__() + return self + + def __exit__(self, exc_type=None, exc_value=None, traceback=None): + with map_httpcore_exceptions(): + self._pool.__exit__(exc_type, exc_value, traceback) diff --git a/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/INSTALLER b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/METADATA b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/METADATA new file mode 100644 index 0000000..aaeb3ae --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/METADATA @@ -0,0 +1,295 @@ +Metadata-Version: 2.4 +Name: python-socks +Version: 2.8.1 +Summary: Proxy (SOCKS4, SOCKS5, HTTP CONNECT) client for Python +Author-email: Roman Snegirev +License: Apache-2.0 +Project-URL: homepage, https://github.com/romis2012/python-socks +Project-URL: repository, https://github.com/romis2012/python-socks +Keywords: socks,socks5,socks4,http,proxy,asyncio,trio,curio,anyio +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Operating System :: MacOS +Classifier: Operating System :: Microsoft +Classifier: Operating System :: POSIX :: Linux +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Intended Audience :: Developers +Classifier: Framework :: AsyncIO +Classifier: Framework :: Trio +Classifier: License :: OSI Approved :: Apache Software License +Requires-Python: >=3.8.0 +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Provides-Extra: asyncio +Requires-Dist: async-timeout>=4.0; python_version < "3.11" and extra == "asyncio" +Provides-Extra: trio +Requires-Dist: trio>=0.24; extra == "trio" +Provides-Extra: curio +Requires-Dist: curio>=1.4; extra == "curio" +Provides-Extra: anyio +Requires-Dist: anyio<5.0.0,>=3.3.4; extra == "anyio" +Dynamic: license-file + +## python-socks + +[![CI](https://github.com/romis2012/python-socks/actions/workflows/ci.yml/badge.svg)](https://github.com/romis2012/python-socks/actions/workflows/ci.yml) +[![Coverage Status](https://codecov.io/gh/romis2012/python-socks/branch/master/graph/badge.svg)](https://codecov.io/gh/romis2012/python-socks) +[![PyPI version](https://badge.fury.io/py/python-socks.svg)](https://pypi.python.org/pypi/python-socks) +[![versions](https://img.shields.io/pypi/pyversions/python-socks.svg)](https://github.com/romis2012/python-socks) + + +The `python-socks` package provides a core proxy client functionality for Python. +Supports `SOCKS4(a)`, `SOCKS5(h)`, `HTTP CONNECT` proxy and provides sync and async (asyncio, trio, curio, anyio) APIs. +You probably don't need to use `python-socks` directly. +It is used internally by +[aiohttp-socks](https://github.com/romis2012/aiohttp-socks) and [httpx-socks](https://github.com/romis2012/httpx-socks) packages. + +## Requirements +- Python >= 3.8 +- async-timeout >= 4.0 (optional) +- trio >= 0.24 (optional) +- curio >= 1.4 (optional) +- anyio >= 3.3.4 (optional) + +## Installation + +only sync proxy support: +``` +pip install python-socks +``` + +to include optional asyncio support: +``` +pip install python-socks[asyncio] +``` + +to include optional trio support: +``` +pip install python-socks[trio] +``` + +to include optional curio support: +``` +pip install python-socks[curio] +``` + +to include optional anyio support: +``` +pip install python-socks[anyio] +``` + +## Simple usage +We are making secure HTTP GET request via SOCKS5 proxy + +#### Sync +```python +import ssl +from python_socks.sync import Proxy + +proxy = Proxy.from_url('socks5://user:password@127.0.0.1:1080') + +# `connect` returns standard Python socket in blocking mode +sock = proxy.connect(dest_host='check-host.net', dest_port=443) + +sock = ssl.create_default_context().wrap_socket( + sock=sock, + server_hostname='check-host.net' +) + +request = ( + b'GET /ip HTTP/1.1\r\n' + b'Host: check-host.net\r\n' + b'Connection: close\r\n\r\n' +) +sock.sendall(request) +response = sock.recv(4096) +print(response) +``` + +#### Async (asyncio) +```python +import ssl +import asyncio +from python_socks.async_.asyncio import Proxy + +proxy = Proxy.from_url('socks5://user:password@127.0.0.1:1080') + +# `connect` returns standard Python socket in non-blocking mode +# so we can pass it to asyncio.open_connection(...) +sock = await proxy.connect(dest_host='check-host.net', dest_port=443) + +reader, writer = await asyncio.open_connection( + host=None, + port=None, + sock=sock, + ssl=ssl.create_default_context(), + server_hostname='check-host.net', +) + +request = ( + b'GET /ip HTTP/1.1\r\n' + b'Host: check-host.net\r\n' + b'Connection: close\r\n\r\n' +) + +writer.write(request) +response = await reader.read(-1) +print(response) +``` + +#### Async (trio) +```python +import ssl +import trio +from python_socks.async_.trio import Proxy + +proxy = Proxy.from_url('socks5://user:password@127.0.0.1:1080') + +# `connect` returns trio socket +# so we can pass it to trio.SocketStream +sock = await proxy.connect(dest_host='check-host.net', dest_port=443) + +stream = trio.SocketStream(sock) + +stream = trio.SSLStream( + stream, ssl.create_default_context(), + server_hostname='check-host.net' +) +await stream.do_handshake() + +request = ( + b'GET /ip HTTP/1.1\r\n' + b'Host: check-host.net\r\n' + b'Connection: close\r\n\r\n' +) + +await stream.send_all(request) +response = await stream.receive_some(4096) +print(response) +``` + +#### Async (curio) +```python +import curio.ssl as curiossl +from python_socks.async_.curio import Proxy + +proxy = Proxy.from_url('socks5://user:password@127.0.0.1:1080') +# `connect` returns curio.io.Socket +sock = await proxy.connect( + dest_host='check-host.net', + dest_port=443 +) + +request = ( + b'GET /ip HTTP/1.1\r\n' + b'Host: check-host.net\r\n' + b'Connection: close\r\n\r\n' +) + +ssl_context = curiossl.create_default_context() +sock = await ssl_context.wrap_socket( + sock, do_handshake_on_connect=False, server_hostname='check-host.net' +) + +await sock.do_handshake() + +stream = sock.as_stream() + +await stream.write(request) +response = await stream.read(1024) +print(response) +``` + +#### Async (anyio) +```python +import ssl +from python_socks.async_.anyio import Proxy + +proxy = Proxy.from_url('socks5://user:password@127.0.0.1:1080') + +# `connect` returns AnyioSocketStream +stream = await proxy.connect( + dest_host='check-host.net', + dest_port=443, + dest_ssl=ssl.create_default_context(), +) + +request = ( + b'GET /ip HTTP/1.1\r\n' + b'Host: check-host.net\r\n' + b'Connection: close\r\n\r\n' +) + +await stream.write_all(request) +response = await stream.read() +print(response) +``` + +## More complex example + +#### A urllib3 PoolManager that routes connections via the proxy + +```python +from urllib3 import PoolManager, HTTPConnectionPool, HTTPSConnectionPool +from urllib3.connection import HTTPConnection, HTTPSConnection +from python_socks.sync import Proxy + + +class ProxyHTTPConnection(HTTPConnection): + def __init__(self, *args, **kwargs): + socks_options = kwargs.pop('_socks_options') + self._proxy_url = socks_options['proxy_url'] + super().__init__(*args, **kwargs) + + def _new_conn(self): + proxy = Proxy.from_url(self._proxy_url) + return proxy.connect( + dest_host=self.host, + dest_port=self.port, + timeout=self.timeout + ) + + +class ProxyHTTPSConnection(ProxyHTTPConnection, HTTPSConnection): + pass + + +class ProxyHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = ProxyHTTPConnection + + +class ProxyHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = ProxyHTTPSConnection + + +class ProxyPoolManager(PoolManager): + def __init__(self, proxy_url, timeout=5, num_pools=10, headers=None, + **connection_pool_kw): + + connection_pool_kw['_socks_options'] = {'proxy_url': proxy_url} + connection_pool_kw['timeout'] = timeout + + super().__init__(num_pools, headers, **connection_pool_kw) + + self.pool_classes_by_scheme = { + 'http': ProxyHTTPConnectionPool, + 'https': ProxyHTTPSConnectionPool, + } + + +### and how to use it +manager = ProxyPoolManager('socks5://user:password@127.0.0.1:1080') +response = manager.request('GET', 'https://check-host.net/ip') +print(response.data) +``` diff --git a/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/RECORD b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/RECORD new file mode 100644 index 0000000..94d7c93 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/RECORD @@ -0,0 +1,149 @@ +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_abc.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/abc.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_async.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_sync.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_async.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_sync.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_async.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_sync.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_async.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_sync.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_errors.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_helpers.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_protocols/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_protocols/errors.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_protocols/http.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks4.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks5.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_types.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/_version.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/_proxy_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_resolver.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_resolver.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/curio/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_resolver.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_resolver.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/_resolver.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/_stream.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/v2/__init__.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_chain.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_connect.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_proxy.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_ssl_transport.cpython-39.pyc,, +../../../../../../../../Library/Caches/com.apple.python/Users/dannier/Desktop/living/AICLW/wechatAiclaw/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_stream.cpython-39.pyc,, +python_socks-2.8.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +python_socks-2.8.1.dist-info/METADATA,sha256=jDS83DnufjrnQhuWQBgKuXA7h0KMUNre3G83vwrOXC4,8151 +python_socks-2.8.1.dist-info/RECORD,, +python_socks-2.8.1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91 +python_socks-2.8.1.dist-info/licenses/LICENSE.txt,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357 +python_socks-2.8.1.dist-info/top_level.txt,sha256=7qyDAVwjTZ0sIYG1iatFbE-SLpWhONjaoMQ0Ib2DX0Q,13 +python_socks/__init__.py,sha256=gKywvwx_x4_0hWJEa2eif1dPzP9YeFXJdrNlHuvFmC8,367 +python_socks/_abc.py,sha256=_juTZE0iqoTChE53IHjBKEYFO-fxYZfPhdUc6EgFlwE,909 +python_socks/_connectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +python_socks/_connectors/abc.py,sha256=1fgjPiv64UAMiJDsJXYr-GofPGhuHTwTzaodQfrzf7Q,397 +python_socks/_connectors/factory_async.py,sha256=X63hJuhpr8iDdAxwrUlsbyXfHewZJtgKQRdSZs41RS4,1055 +python_socks/_connectors/factory_sync.py,sha256=QVaNbUXSEPZW9Cv43gRFF3kwVwU64moQR1BEtE4VOiE,1042 +python_socks/_connectors/http_async.py,sha256=vo8QhluTYEqnIoxL2XPcj9vSl8yhu9Tvm4KI-gMyJGM,951 +python_socks/_connectors/http_sync.py,sha256=Q6DRrUCljokQe2veoJJ6G8SZ7QEmq8x4_Q-faSADLEY,926 +python_socks/_connectors/socks4_async.py,sha256=QGoB1are0qU-uoPCUvanFBUvSCyFxgKN_XVMKSrDoYY,1179 +python_socks/_connectors/socks4_sync.py,sha256=yX4e3xMOHH1OzVPTYROeQDBf8gcZcXODFOxBHyRtVOc,1148 +python_socks/_connectors/socks5_async.py,sha256=qniozb0P8gtXF_vK3b7kjRmoGNMHmYAb9eIZg-heI8k,2829 +python_socks/_connectors/socks5_sync.py,sha256=G_FW1XsiaMDbzR7vcBMtRpD3R9LpO5V5zu9pltpo7xI,2602 +python_socks/_errors.py,sha256=M2KkgCrFy_ebEqw7JoxCWMjIIQ5Zu_B7YlXYhsLo-gk,329 +python_socks/_helpers.py,sha256=UacO07M9i8G8CfqzBYWnehgCTFbZTlwJWAsFoXR3mVk,2708 +python_socks/_protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +python_socks/_protocols/errors.py,sha256=4XqU4AqgrWaedmUVGMjd6TvQKXv3mV0H76s5lf8aazY,150 +python_socks/_protocols/http.py,sha256=QhLHqBgc6DfsvC8AIhylN1tjfk7QzxgwemrK1LuL3vk,4499 +python_socks/_protocols/socks4.py,sha256=j1Psqi3PDiKAF19g_Mh3sf_RwXu7o-8X7BqWOaoTbKE,3061 +python_socks/_protocols/socks5.py,sha256=1PEr1s4JxmpST9OYEeVgbidnV_d4Vb85JZHmWqK-r9c,9713 +python_socks/_types.py,sha256=dgQifNf2mIwdKoX7YmI91H_lUsW9-Y0ZkZGlIV6_NNw,90 +python_socks/_version.py,sha256=e7j6da3GYTfq-hRQ9-hzCSija3gACzgMjjpG6A56uGs,49 +python_socks/async_/__init__.py,sha256=TeBn5oirLy6aPoWcTc3OW9OxXh5Uv5LADwF_Y7f7HjY,64 +python_socks/async_/_proxy_chain.py,sha256=TiG1Uu45anONWi2ZFj11w4-LrNbja8SbG27bWCX9Et4,987 +python_socks/async_/anyio/__init__.py,sha256=pZnrND4Qd-pIGg7KI1ML41kZNkWHmkd3vrvwZZIJyk4,106 +python_socks/async_/anyio/_chain.py,sha256=-d4sR-JM8Agh73rB8nK7DBcB9oqDgtx2GOTAx-SMslU,1099 +python_socks/async_/anyio/_connect.py,sha256=dYx-X2rDDTe-mtLm6DTpV-gFB7Vt_OYS1Xs_4Ea9JZ4,306 +python_socks/async_/anyio/_proxy.py,sha256=nWHzIFPYPXatmuVITdDWN6uoQe_P2Xm6NomgfgYm5UA,4322 +python_socks/async_/anyio/_resolver.py,sha256=LPRLmEzgKPGCqAbEMmLSMVtFkNVKha_2u08MhrZZ-CY,590 +python_socks/async_/anyio/_stream.py,sha256=OcCl4YkccA0-5hZh90CP4YmsBDLPwHpS3B0wCLytnAY,1614 +python_socks/async_/anyio/v2/__init__.py,sha256=4xgyAJHDGpRANYVNfpUdn3Zo8Ib_gREWH8bH4APycK4,117 +python_socks/async_/anyio/v2/_chain.py,sha256=NZ_9dA1AQrG3pBOnYBszbtD-Kse85bQ_dT33VtmGx3o,797 +python_socks/async_/anyio/v2/_connect.py,sha256=_LF1f_cYJbfdSqkq-xEK4nF4l-bP5I1VWRFFyX1RH3Y,368 +python_socks/async_/anyio/v2/_proxy.py,sha256=7F-Y_xhVcqGnKjUDVnjYNkyz5VEFJjzZhkl0Xklyj9U,4085 +python_socks/async_/anyio/v2/_stream.py,sha256=40D3Pmb0rryk9gaQlmjpAMdiJi5H5O0xbBseblGG1FY,1616 +python_socks/async_/asyncio/__init__.py,sha256=0WAM07vy0YvI8IhKGmaUA0GaNLHaG3l-4mnJVbfoqHE,65 +python_socks/async_/asyncio/_connect.py,sha256=K4qqOwXI-1uVleSwf-mQ2ZPO5LwKeSr50Wr3jWImEng,1110 +python_socks/async_/asyncio/_proxy.py,sha256=HS1NfXZCYOZ2b4qUee01CwH4NF1OcXPzqYxEJXYJeT8,4120 +python_socks/async_/asyncio/_resolver.py,sha256=ROqOncuIub87J9_nMdvRTG_5zYOh8uafk265z0s2ORQ,681 +python_socks/async_/asyncio/_stream.py,sha256=RP7BIP9iFFy5nLRf9kHH7s1h_zPfo0yoEVKNpExqFcI,1021 +python_socks/async_/asyncio/v2/__init__.py,sha256=v9eJQeZ2yGJHeBQ0FYqNmIMCRrB547QqoAANHVBitxY,108 +python_socks/async_/asyncio/v2/_chain.py,sha256=KCtmKg0halaCIEqBY6JB-KGVNNiaa4APBb5l5z8kVPk,811 +python_socks/async_/asyncio/v2/_connect.py,sha256=3yPmPCNmMKlCCZZCnNvydER3ilnfE5gTevzBN7IoGEE,603 +python_socks/async_/asyncio/v2/_proxy.py,sha256=QPMx2nFeHCKObBkDqzONkgf2VLXdhuP-aMsBWAgdf-I,4714 +python_socks/async_/asyncio/v2/_stream.py,sha256=YdpJap_Ax-7tJnA-AJ7nayZmD5lpem2UIPbUepnzqxM,2753 +python_socks/async_/curio/__init__.py,sha256=1OyhLhvLJ23iDROgyxGqQACice66SrIYCMqmwjw_dqY,63 +python_socks/async_/curio/_connect.py,sha256=u1HRJ8n3OZbux-wETO9HuqZv9By2LGhhSsM78gnxr_0,328 +python_socks/async_/curio/_proxy.py,sha256=_Qgodw62VPvt-5j1T34l7P52R5Bk4Crq97QJ0f1Qf10,3644 +python_socks/async_/curio/_resolver.py,sha256=_jDZq_lzTl_QihxH2HNEY_7_ELo-qmzG6IO5PNNEOek,722 +python_socks/async_/curio/_stream.py,sha256=sIAbwkPuiFF4c8cEs7exVdGb7cfFgoggKTV4LYqDcm0,855 +python_socks/async_/trio/__init__.py,sha256=Y68xwGqkXGKXHcwM6nvev6n2ChbFpBUFW0RzBrsIrug,61 +python_socks/async_/trio/_connect.py,sha256=qVfK4jz3h1Tjwnr8sX0DNazASdg3DDXNHjMopaHGY0I,851 +python_socks/async_/trio/_proxy.py,sha256=yp3NJOTAaw2ReXlPt4fBRY51Xqc1C3tf16H3v0XvlCg,3786 +python_socks/async_/trio/_resolver.py,sha256=uqBoTkbYpwwESoph2SmXRUUHM_DsT7dALQDmKqp-sco,591 +python_socks/async_/trio/_stream.py,sha256=xunx_XUiwH1pBLQjWbkqq3P92SeXaRypPMPRYwHOsPg,1003 +python_socks/async_/trio/v2/__init__.py,sha256=cd8Sc6NLmrUZewkqeDvnxmQqGZBKNuwYEEb4NMW2qo0,116 +python_socks/async_/trio/v2/_chain.py,sha256=g9atl9HcWYrCHLCI8GtLR9GZC67sKiXEjcvGwZVGhDw,795 +python_socks/async_/trio/v2/_connect.py,sha256=Nk0ipvuFFnzH-NPfXkX57I74-sbT6ks--ZgbfN3r42o,360 +python_socks/async_/trio/v2/_proxy.py,sha256=9118wyv9SthZNVlvECfb3OfhrPMOFyyJSHhl7lG0TbI,4094 +python_socks/async_/trio/v2/_stream.py,sha256=wGB7sDjRO1ywVNjF_DFgdF2orjD3zrh8dPSmW9aBEV0,1475 +python_socks/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +python_socks/sync/__init__.py,sha256=Dslh61MLGktjBK50UVGYjAQ-iGl8tjoLR93_-cvddWM,106 +python_socks/sync/_chain.py,sha256=do_WtGenqKAG7B50nNZdltFqEQV8t-mSlGnMACNIwgE,973 +python_socks/sync/_connect.py,sha256=N1Y-uQXkOiJ_dxkwWHw1fBliIQlTL47ydql7IvaSqm8,344 +python_socks/sync/_proxy.py,sha256=Ny2QA8GI2aCUm0JL8IfY2j2IaXKzAZNp6bZeF7OmwYA,3287 +python_socks/sync/_resolver.py,sha256=2rf9-NdjRktYrab1Oyv9ZJLKByP95ysFmRE3TgjRCSA,547 +python_socks/sync/_stream.py,sha256=VeR-_ERvn5x_PjxFsPy88llEiNfTuejvXoZkAxlqOuY,811 +python_socks/sync/v2/__init__.py,sha256=eVZc6zh0pLhNMf_uSPZiH_1vNGEvedwzX5gObUSkRT8,116 +python_socks/sync/v2/_chain.py,sha256=IRVMioTVWwxylZ8maD5Gqxhhgw4VyrG5vo_hAOuEn8Q,579 +python_socks/sync/v2/_connect.py,sha256=QrZ1CZimYCUMUuaD8JdUblwlqlzIbqFrbsPwMzoxGkM,420 +python_socks/sync/v2/_proxy.py,sha256=7FuO6wML0DkEonoGAprhTlVd8fazOgNeNrJyxABmP1Q,3621 +python_socks/sync/v2/_ssl_transport.py,sha256=MURwzvKu-S2-MI7o8KaZTvvBFOiiKIu5R0ERVIMvERE,6088 +python_socks/sync/v2/_stream.py,sha256=Jq89x1xmIX3L_v-WyS1nRTItjTnYTcDcfDblsANxfJI,1544 diff --git a/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/WHEEL b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/WHEEL new file mode 100644 index 0000000..1ef5583 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (82.0.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/licenses/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/top_level.txt b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/top_level.txt new file mode 100644 index 0000000..75c3065 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks-2.8.1.dist-info/top_level.txt @@ -0,0 +1 @@ +python_socks diff --git a/.venv/lib/python3.9/site-packages/python_socks/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/__init__.py new file mode 100644 index 0000000..001a632 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/__init__.py @@ -0,0 +1,20 @@ +from ._version import __version__, __title__ + +from ._types import ProxyType +from ._helpers import parse_proxy_url + +from ._errors import ( + ProxyError, + ProxyTimeoutError, + ProxyConnectionError, +) + +__all__ = ( + '__title__', + '__version__', + 'ProxyError', + 'ProxyTimeoutError', + 'ProxyConnectionError', + 'ProxyType', + 'parse_proxy_url', +) diff --git a/.venv/lib/python3.9/site-packages/python_socks/_abc.py b/.venv/lib/python3.9/site-packages/python_socks/_abc.py new file mode 100644 index 0000000..85812ed --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_abc.py @@ -0,0 +1,40 @@ +from typing import Optional + + +class SyncResolver: + def resolve(self, host, port=0, family=0): + raise NotImplementedError() + + +class AsyncResolver: + async def resolve(self, host, port=0, family=0): + raise NotImplementedError() + + +class SyncSocketStream: + + def write_all(self, data: bytes): + raise NotImplementedError() + + def read(self, max_bytes: Optional[int] = None): + raise NotImplementedError() + + def read_exact(self, n: int): + raise NotImplementedError() + + def close(self): + raise NotImplementedError() + + +class AsyncSocketStream: + async def write_all(self, data: bytes): + raise NotImplementedError() + + async def read(self, max_bytes: Optional[int] = None): + raise NotImplementedError() + + async def read_exact(self, n: int): + raise NotImplementedError() + + async def close(self): + raise NotImplementedError() diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/abc.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/abc.py new file mode 100644 index 0000000..785d7ef --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/abc.py @@ -0,0 +1,21 @@ +from .._abc import SyncSocketStream, AsyncSocketStream + + +class SyncConnector: + def connect( + self, + stream: SyncSocketStream, + host: str, + port: int, + ): + raise NotImplementedError + + +class AsyncConnector: + async def connect( + self, + stream: AsyncSocketStream, + host: str, + port: int, + ): + raise NotImplementedError diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_async.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_async.py new file mode 100644 index 0000000..fc2a3da --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_async.py @@ -0,0 +1,40 @@ +from typing import Optional +from .._abc import AsyncResolver +from .._types import ProxyType + +from .abc import AsyncConnector +from .socks5_async import Socks5AsyncConnector +from .socks4_async import Socks4AsyncConnector +from .http_async import HttpAsyncConnector + + +def create_connector( + proxy_type: ProxyType, + username: Optional[str], + password: Optional[str], + rdns: Optional[bool], + resolver: AsyncResolver, +) -> AsyncConnector: + if proxy_type == ProxyType.SOCKS4: + return Socks4AsyncConnector( + user_id=username, + rdns=rdns, + resolver=resolver, + ) + + if proxy_type == ProxyType.SOCKS5: + return Socks5AsyncConnector( + username=username, + password=password, + rdns=rdns, + resolver=resolver, + ) + + if proxy_type == ProxyType.HTTP: + return HttpAsyncConnector( + username=username, + password=password, + resolver=resolver, + ) + + raise ValueError(f'Invalid proxy type: {proxy_type}') diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_sync.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_sync.py new file mode 100644 index 0000000..91657b9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/factory_sync.py @@ -0,0 +1,40 @@ +from typing import Optional +from .._abc import SyncResolver +from .._types import ProxyType + +from .abc import SyncConnector +from .socks5_sync import Socks5SyncConnector +from .socks4_sync import Socks4SyncConnector +from .http_sync import HttpSyncConnector + + +def create_connector( + proxy_type: ProxyType, + username: Optional[str], + password: Optional[str], + rdns: Optional[bool], + resolver: SyncResolver, +) -> SyncConnector: + if proxy_type == ProxyType.SOCKS4: + return Socks4SyncConnector( + user_id=username, + rdns=rdns, + resolver=resolver, + ) + + if proxy_type == ProxyType.SOCKS5: + return Socks5SyncConnector( + username=username, + password=password, + rdns=rdns, + resolver=resolver, + ) + + if proxy_type == ProxyType.HTTP: + return HttpSyncConnector( + username=username, + password=password, + resolver=resolver, + ) + + raise ValueError(f'Invalid proxy type: {proxy_type}') diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_async.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_async.py new file mode 100644 index 0000000..7f02b89 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_async.py @@ -0,0 +1,38 @@ +from typing import Optional +from .._abc import AsyncSocketStream, AsyncResolver +from .abc import AsyncConnector + +from .._protocols import http + + +class HttpAsyncConnector(AsyncConnector): + def __init__( + self, + username: Optional[str], + password: Optional[str], + resolver: AsyncResolver, + ): + self._username = username + self._password = password + self._resolver = resolver + + async def connect( + self, + stream: AsyncSocketStream, + host: str, + port: int, + ) -> http.ConnectReply: + conn = http.Connection() + + request = http.ConnectRequest( + host=host, + port=port, + username=self._username, + password=self._password, + ) + data = conn.send(request) + await stream.write_all(data) + + data = await stream.read() + reply: http.ConnectReply = conn.receive(data) + return reply diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_sync.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_sync.py new file mode 100644 index 0000000..1ff2dff --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/http_sync.py @@ -0,0 +1,38 @@ +from typing import Optional +from .._abc import SyncSocketStream, SyncResolver +from .abc import SyncConnector + +from .._protocols import http + + +class HttpSyncConnector(SyncConnector): + def __init__( + self, + username: Optional[str], + password: Optional[str], + resolver: SyncResolver, + ): + self._username = username + self._password = password + self._resolver = resolver + + def connect( + self, + stream: SyncSocketStream, + host: str, + port: int, + ) -> http.ConnectReply: + conn = http.Connection() + + request = http.ConnectRequest( + host=host, + port=port, + username=self._username, + password=self._password, + ) + data = conn.send(request) + stream.write_all(data) + + data = stream.read() + reply: http.ConnectReply = conn.receive(data) + return reply diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_async.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_async.py new file mode 100644 index 0000000..f9c3626 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_async.py @@ -0,0 +1,45 @@ +import socket +from typing import Optional + +from .._abc import AsyncSocketStream, AsyncResolver +from .abc import AsyncConnector + +from .._protocols import socks4 +from .._helpers import is_ip_address + + +class Socks4AsyncConnector(AsyncConnector): + def __init__( + self, + user_id: Optional[str], + rdns: Optional[bool], + resolver: AsyncResolver, + ): + if rdns is None: + rdns = False + + self._user_id = user_id + self._rdns = rdns + self._resolver = resolver + + async def connect( + self, + stream: AsyncSocketStream, + host: str, + port: int, + ) -> socks4.ConnectReply: + conn = socks4.Connection() + + if not is_ip_address(host) and not self._rdns: + _, host = await self._resolver.resolve( + host, + family=socket.AF_INET, + ) + + request = socks4.ConnectRequest(host=host, port=port, user_id=self._user_id) + data = conn.send(request) + await stream.write_all(data) + + data = await stream.read_exact(socks4.ConnectReply.SIZE) + reply: socks4.ConnectReply = conn.receive(data) + return reply diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_sync.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_sync.py new file mode 100644 index 0000000..b69e7f7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks4_sync.py @@ -0,0 +1,45 @@ +import socket +from typing import Optional + +from .._abc import SyncSocketStream, SyncResolver +from .abc import SyncConnector + +from .._protocols import socks4 +from .._helpers import is_ip_address + + +class Socks4SyncConnector(SyncConnector): + def __init__( + self, + user_id: Optional[str], + rdns: Optional[bool], + resolver: SyncResolver, + ): + if rdns is None: + rdns = False + + self._user_id = user_id + self._rdns = rdns + self._resolver = resolver + + def connect( + self, + stream: SyncSocketStream, + host: str, + port: int, + ) -> socks4.ConnectReply: + conn = socks4.Connection() + + if not is_ip_address(host) and not self._rdns: + _, host = self._resolver.resolve( + host, + family=socket.AF_INET, + ) + + request = socks4.ConnectRequest(host=host, port=port, user_id=self._user_id) + data = conn.send(request) + stream.write_all(data) + + data = stream.read_exact(socks4.ConnectReply.SIZE) + reply: socks4.ConnectReply = conn.receive(data) + return reply diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_async.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_async.py new file mode 100644 index 0000000..40e3e1e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_async.py @@ -0,0 +1,95 @@ +import socket +from typing import Optional + +from .._abc import AsyncSocketStream, AsyncResolver +from .abc import AsyncConnector + +from .._protocols import socks5 +from .._helpers import is_ip_address + + +class Socks5AsyncConnector(AsyncConnector): + def __init__( + self, + username: Optional[str], + password: Optional[str], + rdns: Optional[bool], + resolver: AsyncResolver, + ): + if rdns is None: + rdns = True + + self._username = username + self._password = password + self._rdns = rdns + self._resolver = resolver + + async def connect( + self, + stream: AsyncSocketStream, + host: str, + port: int, + ) -> socks5.ConnectReply: + conn = socks5.Connection() + + # Auth methods + request = socks5.AuthMethodsRequest( + username=self._username, + password=self._password, + ) + data = conn.send(request) + await stream.write_all(data) + + data = await stream.read_exact(socks5.AuthMethodReply.SIZE) + reply: socks5.AuthMethodReply = conn.receive(data) + + # Authenticate + if reply.method == socks5.AuthMethod.USERNAME_PASSWORD: + request = socks5.AuthRequest( + username=self._username, + password=self._password, + ) + data = conn.send(request) + await stream.write_all(data) + + data = await stream.read_exact(socks5.AuthReply.SIZE) + _: socks5.AuthReply = conn.receive(data) + + # Connect + if not is_ip_address(host) and not self._rdns: + _, host = await self._resolver.resolve( + host, + family=socket.AF_UNSPEC, + ) + + request = socks5.ConnectRequest(host=host, port=port) + data = conn.send(request) + await stream.write_all(data) + + data = await self._read_reply(stream) + reply: socks5.ConnectReply = conn.receive(data) + return reply + + # noinspection PyMethodMayBeStatic + async def _read_reply(self, stream: AsyncSocketStream) -> bytes: + data = await stream.read_exact(3) + if data[0] != socks5.SOCKS_VER: + return data + if data[1] != socks5.ReplyCode.SUCCEEDED: + return data + if data[2] != socks5.RSV: + return data + + data += await stream.read_exact(1) + addr_type = data[3] + + if addr_type == socks5.AddressType.IPV4: + data += await stream.read_exact(6) + elif addr_type == socks5.AddressType.IPV6: + data += await stream.read_exact(18) + elif addr_type == socks5.AddressType.DOMAIN: + data += await stream.read_exact(1) + host_len = data[-1] + data += await stream.read_exact(host_len + 2) + + return data diff --git a/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_sync.py b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_sync.py new file mode 100644 index 0000000..acd4789 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_connectors/socks5_sync.py @@ -0,0 +1,86 @@ +import socket +from typing import Optional + +from .._abc import SyncSocketStream, SyncResolver +from .abc import SyncConnector + +from .._protocols import socks5 +from .._helpers import is_ip_address + + +class Socks5SyncConnector(SyncConnector): + def __init__( + self, + username: Optional[str], + password: Optional[str], + rdns: Optional[bool], + resolver: SyncResolver, + ): + if rdns is None: + rdns = True + + self._username = username + self._password = password + self._rdns = rdns + self._resolver = resolver + + def connect( + self, + stream: SyncSocketStream, + host: str, + port: int, + ) -> socks5.ConnectReply: + conn = socks5.Connection() + + # Auth methods + request = socks5.AuthMethodsRequest(username=self._username, password=self._password) + data = conn.send(request) + stream.write_all(data) + + data = stream.read_exact(socks5.AuthMethodReply.SIZE) + reply: socks5.AuthMethodReply = conn.receive(data) + + # Authenticate + if reply.method == socks5.AuthMethod.USERNAME_PASSWORD: + request = socks5.AuthRequest(username=self._username, password=self._password) + data = conn.send(request) + stream.write_all(data) + + data = stream.read_exact(socks5.AuthReply.SIZE) + _: socks5.AuthReply = conn.receive(data) + + # Connect + if not is_ip_address(host) and not self._rdns: + _, host = self._resolver.resolve(host, family=socket.AF_UNSPEC) + + request = socks5.ConnectRequest(host=host, port=port) + data = conn.send(request) + stream.write_all(data) + + data = self._read_reply(stream) + reply: socks5.ConnectReply = conn.receive(data) + return reply + + # noinspection PyMethodMayBeStatic + def _read_reply(self, stream: SyncSocketStream) -> bytes: + data = stream.read_exact(3) + if data[0] != socks5.SOCKS_VER: + return data + if data[1] != socks5.ReplyCode.SUCCEEDED: + return data + if data[2] != socks5.RSV: + return data + + data += stream.read_exact(1) + addr_type = data[3] + + if addr_type == socks5.AddressType.IPV4: + data += stream.read_exact(6) + elif addr_type == socks5.AddressType.IPV6: + data += stream.read_exact(18) + elif addr_type == socks5.AddressType.DOMAIN: + data += stream.read_exact(1) + host_len = data[-1] + data += stream.read_exact(host_len + 2) + + return data diff --git a/.venv/lib/python3.9/site-packages/python_socks/_errors.py b/.venv/lib/python3.9/site-packages/python_socks/_errors.py new file mode 100644 index 0000000..67e271e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_errors.py @@ -0,0 +1,16 @@ +class ProxyException(Exception): + pass + + +class ProxyTimeoutError(ProxyException, TimeoutError): + pass + + +class ProxyConnectionError(ProxyException, OSError): + pass + + +class ProxyError(ProxyException): + def __init__(self, message, error_code=None): + super().__init__(message) + self.error_code = error_code diff --git a/.venv/lib/python3.9/site-packages/python_socks/_helpers.py b/.venv/lib/python3.9/site-packages/python_socks/_helpers.py new file mode 100644 index 0000000..365d4e1 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_helpers.py @@ -0,0 +1,81 @@ +import functools +import re +from typing import Optional, Tuple +from urllib.parse import urlparse, unquote + +from ._types import ProxyType + +# pylint:disable-next=invalid-name +_ipv4_pattern = ( + r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' + r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' +) + +# pylint:disable-next=invalid-name +_ipv6_pattern = ( + r'^(?:(?:(?:[A-F0-9]{1,4}:){6}|(?=(?:[A-F0-9]{0,4}:){0,6}' + r'(?:[0-9]{1,3}\.){3}[0-9]{1,3}$)(([0-9A-F]{1,4}:){0,5}|:)' + r'((:[0-9A-F]{1,4}){1,5}:|:)|::(?:[A-F0-9]{1,4}:){5})' + r'(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}' + r'(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])|(?:[A-F0-9]{1,4}:){7}' + r'[A-F0-9]{1,4}|(?=(?:[A-F0-9]{0,4}:){0,7}[A-F0-9]{0,4}$)' + r'(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}|:)|(?:[A-F0-9]{1,4}:){7}' + r':|:(:[A-F0-9]{1,4}){7})$' +) + +_ipv4_regex = re.compile(_ipv4_pattern) +_ipv6_regex = re.compile(_ipv6_pattern, flags=re.IGNORECASE) +_ipv4_regexb = re.compile(_ipv4_pattern.encode('ascii')) +_ipv6_regexb = re.compile(_ipv6_pattern.encode('ascii'), flags=re.IGNORECASE) + + +def _is_ip_address(regex, regexb, host): + # if host is None: + # return False + if isinstance(host, str): + return bool(regex.match(host)) + elif isinstance(host, (bytes, bytearray, memoryview)): + return bool(regexb.match(host)) + else: + raise TypeError( + '{} [{}] is not a str or bytes'.format(host, type(host)) # pragma: no cover + ) + + +is_ipv4_address = functools.partial(_is_ip_address, _ipv4_regex, _ipv4_regexb) +is_ipv6_address = functools.partial(_is_ip_address, _ipv6_regex, _ipv6_regexb) + + +def is_ip_address(host): + return is_ipv4_address(host) or is_ipv6_address(host) + + +def parse_proxy_url(url: str) -> Tuple[ProxyType, str, int, Optional[str], Optional[str]]: + parsed = urlparse(url) + + scheme = parsed.scheme + if scheme == 'socks5': + proxy_type = ProxyType.SOCKS5 + elif scheme == 'socks4': + proxy_type = ProxyType.SOCKS4 + elif scheme == 'http': + proxy_type = ProxyType.HTTP + else: + raise ValueError(f'Invalid scheme component: {scheme}') # pragma: no cover + + host = parsed.hostname + if not host: + raise ValueError('Empty host component') # pragma: no cover + + try: + port = parsed.port + assert port is not None + except (ValueError, TypeError, AssertionError) as e: # pragma: no cover + raise ValueError('Invalid port component') from e + + try: + username, password = (unquote(parsed.username), unquote(parsed.password)) + except (AttributeError, TypeError): + username, password = '', '' + + return proxy_type, host, port, username, password diff --git a/.venv/lib/python3.9/site-packages/python_socks/_protocols/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/_protocols/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/python_socks/_protocols/errors.py b/.venv/lib/python3.9/site-packages/python_socks/_protocols/errors.py new file mode 100644 index 0000000..2bf0875 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_protocols/errors.py @@ -0,0 +1,4 @@ +class ReplyError(Exception): + def __init__(self, message, error_code=None): + super().__init__(message) + self.error_code = error_code diff --git a/.venv/lib/python3.9/site-packages/python_socks/_protocols/http.py b/.venv/lib/python3.9/site-packages/python_socks/_protocols/http.py new file mode 100644 index 0000000..be762e9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_protocols/http.py @@ -0,0 +1,148 @@ +import sys +from dataclasses import dataclass +import base64 +import binascii +from collections import namedtuple +from typing import Optional + +from .._version import __title__, __version__ + +from .errors import ReplyError + +DEFAULT_USER_AGENT = 'Python/{0[0]}.{0[1]} {1}/{2}'.format( + sys.version_info, + __title__, + __version__, +) + +CRLF = '\r\n' + + +class BasicAuth(namedtuple('BasicAuth', ['login', 'password', 'encoding'])): + """Http basic authentication helper.""" + + def __new__(cls, login: str, password: str = '', encoding: str = 'latin1') -> 'BasicAuth': + if login is None: + raise ValueError('None is not allowed as login value') + + if password is None: + raise ValueError('None is not allowed as password value') + + if ':' in login: + raise ValueError('A ":" is not allowed in login (RFC 1945#section-11.1)') + + # noinspection PyTypeChecker,PyArgumentList + return super().__new__(cls, login, password, encoding) + + @classmethod + def decode(cls, auth_header: str, encoding: str = 'latin1') -> 'BasicAuth': + """Create a BasicAuth object from an Authorization HTTP header.""" + try: + auth_type, encoded_credentials = auth_header.split(' ', 1) + except ValueError: + raise ValueError('Could not parse authorization header.') + + if auth_type.lower() != 'basic': + raise ValueError('Unknown authorization method %s' % auth_type) + + try: + decoded = base64.b64decode(encoded_credentials.encode('ascii'), validate=True).decode( + encoding + ) + except binascii.Error: + raise ValueError('Invalid base64 encoding.') + + try: + # RFC 2617 HTTP Authentication + # https://www.ietf.org/rfc/rfc2617.txt + # the colon must be present, but the username and password may be + # otherwise blank. + username, password = decoded.split(':', 1) + except ValueError: + raise ValueError('Invalid credentials.') + + # noinspection PyTypeChecker + return cls(username, password, encoding=encoding) + + def encode(self) -> str: + """Encode credentials.""" + creds = ('%s:%s' % (self.login, self.password)).encode(self.encoding) + return 'Basic %s' % base64.b64encode(creds).decode(self.encoding) + + +class _Buffer: + def __init__(self, encoding: str = 'utf-8'): + self._encoding = encoding + self._buffer = bytearray() + + def append_line(self, line: str = ""): + if line: + self._buffer.extend(line.encode(self._encoding)) + + self._buffer.extend(CRLF.encode('ascii')) + + def dumps(self) -> bytes: + return bytes(self._buffer) + + +@dataclass +class ConnectRequest: + host: str + port: int + username: Optional[str] + password: Optional[str] + + def dumps(self) -> bytes: + buff = _Buffer() + buff.append_line(f'CONNECT {self.host}:{self.port} HTTP/1.1') + buff.append_line(f'Host: {self.host}:{self.port}') + buff.append_line(f'User-Agent: {DEFAULT_USER_AGENT}') + + if self.username and self.password: + auth = BasicAuth(self.username, self.password) + buff.append_line(f'Proxy-Authorization: {auth.encode()}') + + buff.append_line() + + return buff.dumps() + + +@dataclass +class ConnectReply: + status_code: int + message: str + + @classmethod + def loads(cls, data: bytes) -> 'ConnectReply': + if not data: + raise ReplyError('Invalid proxy response') # pragma: no cover + + line = data.split(CRLF.encode('ascii'), 1)[0] + line = line.decode('utf-8', 'surrogateescape') + + try: + version, code, *reason = line.split() + except ValueError: # pragma: no cover + raise ReplyError(f'Invalid status line: {line}') + + try: + status_code = int(code) + except ValueError: # pragma: no cover + raise ReplyError(f'Invalid status code: {code}') + + status_message = " ".join(reason) + + if status_code != 200: + msg = f'{status_code} {status_message}' + raise ReplyError(msg, error_code=status_code) + + return cls(status_code=status_code, message=status_message) + + +# noinspection PyMethodMayBeStatic +class Connection: + def send(self, request: ConnectRequest) -> bytes: + return request.dumps() + + def receive(self, data: bytes) -> ConnectReply: + return ConnectReply.loads(data) diff --git a/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks4.py b/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks4.py new file mode 100644 index 0000000..b8a8528 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks4.py @@ -0,0 +1,116 @@ +import enum +import ipaddress +import socket +from dataclasses import dataclass +from typing import Optional + +from .errors import ReplyError +from .._helpers import is_ipv4_address + +RSV = NULL = 0x00 +SOCKS_VER = 0x04 + + +class Command(enum.IntEnum): + CONNECT = 0x01 + BIND = 0x02 + + +class ReplyCode(enum.IntEnum): + REQUEST_GRANTED = 0x5A + REQUEST_REJECTED_OR_FAILED = 0x5B + CONNECTION_FAILED = 0x5C + AUTHENTICATION_FAILED = 0x5D + + +ReplyMessages = { + ReplyCode.REQUEST_GRANTED: 'Request granted', + ReplyCode.REQUEST_REJECTED_OR_FAILED: 'Request rejected or failed', + ReplyCode.CONNECTION_FAILED: ( + 'Request rejected because SOCKS server cannot connect to identd on the client' + ), + ReplyCode.AUTHENTICATION_FAILED: ( + 'Request rejected because the client program and identd report different user-ids' + ), +} + + +@dataclass +class ConnectRequest: + host: str # hostname or IPv4 address + port: int + user_id: Optional[str] + + def dumps(self): + port_bytes = self.port.to_bytes(2, 'big') + include_hostname = False + + if is_ipv4_address(self.host): + host_bytes = ipaddress.IPv4Address(self.host).packed + else: + include_hostname = True + host_bytes = bytes([NULL, NULL, NULL, 1]) + + data = bytearray([SOCKS_VER, Command.CONNECT]) + data += port_bytes + data += host_bytes + + if self.user_id: + data += self.user_id.encode('ascii') + + data.append(NULL) + + if include_hostname: + data += self.host.encode('idna') + data.append(NULL) + + return bytes(data) + + +@dataclass +class ConnectReply: + SIZE = 8 + + rsv: int + reply: ReplyCode + host: str # should be ignored when using Command.CONNECT + port: int # should be ignored when using Command.CONNECT + + @classmethod + def loads(cls, data: bytes) -> 'ConnectReply': + if len(data) != cls.SIZE: + raise ReplyError('Malformed connect reply') + + rsv = data[0] + if rsv != RSV: # pragma: no cover + raise ReplyError(f'Unexpected reply version: {data[0]:#02X}') + + try: + reply = ReplyCode(data[1]) + except ValueError: + raise ReplyError(f'Invalid reply code: {data[1]:#02X}') + + if reply != ReplyCode.REQUEST_GRANTED: # pragma: no cover + msg = ReplyMessages.get(reply, 'Unknown error') + raise ReplyError(msg, error_code=reply) + + try: + port = int.from_bytes(data[2:4], byteorder="big") + except ValueError: + raise ReplyError('Invalid port data') + + try: + host = socket.inet_ntop(socket.AF_INET, data[4:8]) + except ValueError: + raise ReplyError('Invalid port data') + + return cls(rsv=rsv, reply=reply, host=host, port=port) + + +# noinspection PyMethodMayBeStatic +class Connection: + def send(self, request: ConnectRequest) -> bytes: + return request.dumps() + + def receive(self, data: bytes) -> ConnectReply: + return ConnectReply.loads(data) diff --git a/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks5.py b/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks5.py new file mode 100644 index 0000000..cfc2d02 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_protocols/socks5.py @@ -0,0 +1,355 @@ +import enum +import ipaddress +import socket +from typing import Optional, Union +from dataclasses import dataclass, field + +from .errors import ReplyError +from .._helpers import is_ip_address + + +RSV = NULL = AUTH_GRANTED = 0x00 +SOCKS_VER = 0x05 + + +class AuthMethod(enum.IntEnum): + ANONYMOUS = 0x00 + GSSAPI = 0x01 + USERNAME_PASSWORD = 0x02 + NO_ACCEPTABLE = 0xFF + + +class AddressType(enum.IntEnum): + IPV4 = 0x01 + DOMAIN = 0x03 + IPV6 = 0x04 + + @classmethod + def from_ip_ver(cls, ver: int): + if ver == 4: + return cls.IPV4 + if ver == 6: + return cls.IPV6 + + raise ValueError('Invalid IP version') + + +class Command(enum.IntEnum): + CONNECT = 0x01 + BIND = 0x02 + UDP_ASSOCIATE = 0x03 + + +class ReplyCode(enum.IntEnum): + SUCCEEDED = 0x00 + GENERAL_FAILURE = 0x01 + CONNECTION_NOT_ALLOWED = 0x02 + NETWORK_UNREACHABLE = 0x03 + HOST_UNREACHABLE = 0x04 + CONNECTION_REFUSED = 0x05 + TTL_EXPIRED = 0x06 + COMMAND_NOT_SUPPORTED = 0x07 + ADDRESS_TYPE_NOT_SUPPORTED = 0x08 + + +ReplyMessages = { + ReplyCode.SUCCEEDED: 'Request granted', + ReplyCode.GENERAL_FAILURE: 'General SOCKS server failure', + ReplyCode.CONNECTION_NOT_ALLOWED: 'Connection not allowed by ruleset', + ReplyCode.NETWORK_UNREACHABLE: 'Network unreachable', + ReplyCode.HOST_UNREACHABLE: 'Host unreachable', + ReplyCode.CONNECTION_REFUSED: 'Connection refused by destination host', + ReplyCode.TTL_EXPIRED: 'TTL expired', + ReplyCode.COMMAND_NOT_SUPPORTED: 'Command not supported or protocol error', + ReplyCode.ADDRESS_TYPE_NOT_SUPPORTED: 'Address type not supported', +} + + +@dataclass +class AuthMethodsRequest: + username: Optional[str] + password: Optional[str] + methods: bytearray = field(init=False) + + def __post_init__(self): + methods = bytearray([AuthMethod.ANONYMOUS]) + + if self.username and self.password: + methods.append(AuthMethod.USERNAME_PASSWORD) + + self.methods = methods + + def dumps(self) -> bytes: + return bytes([SOCKS_VER, len(self.methods)]) + self.methods + + +@dataclass +class AuthMethodReply: + SIZE = 2 + + ver: int + method: AuthMethod + + def validate(self, request: AuthMethodsRequest): + if self.method not in request.methods: # pragma: no cover + raise ReplyError(f'Unexpected SOCKS authentication method: {self.method}') + + @classmethod + def loads(cls, data: bytes) -> 'AuthMethodReply': + if len(data) != cls.SIZE: + raise ReplyError('Malformed authentication method reply') + + ver = data[0] + if ver != SOCKS_VER: # pragma: no cover + raise ReplyError(f'Unexpected SOCKS version number: {ver}') + + try: + method = AuthMethod(data[1]) + except ValueError: + raise ReplyError(f'Invalid authentication method: {data[1]:#02X}') + + if method == AuthMethod.NO_ACCEPTABLE: # pragma: no cover + raise ReplyError('No acceptable authentication methods were offered') + + return cls(ver=ver, method=method) + + +@dataclass +class AuthRequest: + VER = 0x01 + + username: str + password: str + + def dumps(self) -> bytes: + data = bytearray() + data.append(self.VER) + data.append(len(self.username)) + data += self.username.encode('ascii') + data.append(len(self.password)) + data += self.password.encode('ascii') + return bytes(data) + + +@dataclass +class AuthReply: + SIZE = 2 + + ver: int + status: int + + @classmethod + def loads(cls, data: bytes) -> 'AuthReply': + if len(data) != cls.SIZE: + raise ReplyError('Malformed auth reply') + + ver = data[0] + if ver != AuthRequest.VER: # pragma: no cover + raise ReplyError('Invalid authentication response') + + status = data[1] + if status != AUTH_GRANTED: # pragma: no cover + raise ReplyError('Username and password authentication failure') + + return cls(ver=ver, status=status) + + +@dataclass +class ConnectRequest: + host: str # hostname or IPv4 or IPv6 address + port: int + + def dumps(self) -> bytes: + data = bytearray([SOCKS_VER, Command.CONNECT, RSV]) + data += self._build_addr_request() + return bytes(data) + + def _build_addr_request(self) -> bytes: + port = self.port.to_bytes(2, 'big') + + if is_ip_address(self.host): + ip = ipaddress.ip_address(self.host) + address_type = AddressType.from_ip_ver(ip.version) + return bytes([address_type]) + ip.packed + port + else: + address_type = AddressType.DOMAIN + host = self.host.encode('idna') + return bytes([address_type, len(host)]) + host + port + + +@dataclass +class ConnectReply: + ver: int + reply: ReplyCode + rsv: int + bound_host: str + bound_port: int + + def validate(self): + pass + + @classmethod + def loads(cls, data: bytes) -> 'ConnectReply': + if not data: + raise ReplyError('Empty connect reply') + + ver = data[0] + if ver != SOCKS_VER: # pragma: no cover + raise ReplyError(f'Unexpected SOCKS version number: {ver:#02X}') + + try: + reply = ReplyCode(data[1]) + except IndexError: + raise ReplyError('Malformed connect reply') + except ValueError: + raise ReplyError(f'Invalid reply code: {data[1]:#02X}') + + if reply != ReplyCode.SUCCEEDED: # pragma: no cover + msg = ReplyMessages.get(reply, 'Unknown error') # type: ignore + raise ReplyError(msg, error_code=reply) + + try: + rsv = data[2] + except IndexError: + raise ReplyError('Malformed connect reply') + + if rsv != RSV: # pragma: no cover + raise ReplyError(f'The reserved byte must be {RSV:#02X}') + + try: + addr_type = data[3] + bnd_host_data = data[4:-2] + bnd_port_data = data[-2:] + except IndexError: + raise ReplyError('Malformed connect reply') + + if addr_type == AddressType.IPV4: + bnd_host = socket.inet_ntop(socket.AF_INET, bnd_host_data) + elif addr_type == AddressType.IPV6: + bnd_host = socket.inet_ntop(socket.AF_INET6, bnd_host_data) + elif addr_type == AddressType.DOMAIN: # pragma: no cover + # host_len = bnd_host_data[0] + bnd_host = bnd_host_data[1:].decode() + else: # pragma: no cover + raise ReplyError(f'Invalid address type: {addr_type:#02X}') + + bnd_port = int.from_bytes(bnd_port_data, 'big') + + return cls( + ver=ver, + reply=reply, + rsv=rsv, + bound_host=bnd_host, + bound_port=bnd_port, + ) + + +class StateServerWaitingForAuthMethods: + pass + + +@dataclass +class StateClientSentAuthMethods: + data: AuthMethodsRequest + + +@dataclass +class StateServerWaitingForAuth: + data: AuthMethodReply + + +@dataclass +class StateClientAuthenticated: + data: Optional[AuthReply] = None + + +@dataclass +class StateClientSentAuthRequest: + data: AuthRequest + + +@dataclass +class StateClientSentConnectRequest: + data: ConnectRequest + + +@dataclass +class StateServerConnected: + data: ConnectReply + + +Request = Union[ + AuthMethodsRequest, + AuthRequest, + ConnectRequest, +] + +Reply = Union[ + AuthMethodReply, + AuthReply, + ConnectReply, +] + +ConnectionState = Union[ + StateServerWaitingForAuthMethods, + StateClientSentAuthMethods, + StateServerWaitingForAuth, + StateClientSentAuthRequest, + StateClientAuthenticated, + StateClientSentConnectRequest, + StateServerConnected, +] + + +class Connection: + _state: ConnectionState + + def __init__(self): + self._state = StateServerWaitingForAuthMethods() + + def send(self, request: Request) -> bytes: + if type(request) is AuthMethodsRequest: + if type(self._state) is not StateServerWaitingForAuthMethods: + raise RuntimeError('Server is not currently waiting for auth methods') + self._state = StateClientSentAuthMethods(request) + return request.dumps() + + if type(request) is AuthRequest: + if type(self._state) is not StateServerWaitingForAuth: + raise RuntimeError('Server is not currently waiting for authentication') + self._state = StateClientSentAuthRequest(request) + return request.dumps() + + if type(request) is ConnectRequest: + if type(self._state) is not StateClientAuthenticated: + raise RuntimeError('Client is not authenticated') + self._state = StateClientSentConnectRequest(request) + return request.dumps() + + raise RuntimeError(f'Invalid request type: {type(request)}') + + def receive(self, data: bytes) -> Reply: + if type(self._state) is StateClientSentAuthMethods: + reply = AuthMethodReply.loads(data) + reply.validate(self._state.data) + if reply.method == AuthMethod.USERNAME_PASSWORD: + self._state = StateServerWaitingForAuth(data=reply) + else: + self._state = StateClientAuthenticated() + return reply + + if type(self._state) is StateClientSentAuthRequest: + reply = AuthReply.loads(data) + self._state = StateClientAuthenticated(data=reply) + return reply + + if type(self._state) is StateClientSentConnectRequest: + reply = ConnectReply.loads(data) + self._state = StateServerConnected(data=reply) + return reply + + raise RuntimeError(f'Invalid connection state: {self._state}') + + @property + def state(self): + return self._state diff --git a/.venv/lib/python3.9/site-packages/python_socks/_types.py b/.venv/lib/python3.9/site-packages/python_socks/_types.py new file mode 100644 index 0000000..f8a8e0a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_types.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class ProxyType(Enum): + SOCKS4 = 1 + SOCKS5 = 2 + HTTP = 3 diff --git a/.venv/lib/python3.9/site-packages/python_socks/_version.py b/.venv/lib/python3.9/site-packages/python_socks/_version.py new file mode 100644 index 0000000..2146738 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/_version.py @@ -0,0 +1,2 @@ +__title__ = 'python-socks' +__version__ = '2.8.1' diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/__init__.py new file mode 100644 index 0000000..9dd7dcf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/__init__.py @@ -0,0 +1,3 @@ +from ._proxy_chain import ProxyChain + +__all__ = ('ProxyChain',) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/_proxy_chain.py b/.venv/lib/python3.9/site-packages/python_socks/async_/_proxy_chain.py new file mode 100644 index 0000000..9320677 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/_proxy_chain.py @@ -0,0 +1,34 @@ +from typing import Iterable +import warnings + + +class ProxyChain: + def __init__(self, proxies: Iterable): + warnings.warn( + 'This implementation of ProxyChain is deprecated and will be removed in the future', + DeprecationWarning, + stacklevel=2, + ) + self._proxies = proxies + + async def connect(self, dest_host, dest_port, timeout=None): + curr_socket = None + proxies = list(self._proxies) + + length = len(proxies) - 1 + for i in range(length): + curr_socket = await proxies[i].connect( + dest_host=proxies[i + 1].proxy_host, + dest_port=proxies[i + 1].proxy_port, + timeout=timeout, + _socket=curr_socket, + ) + + curr_socket = await proxies[length].connect( + dest_host=dest_host, + dest_port=dest_port, + timeout=timeout, + _socket=curr_socket, + ) + + return curr_socket diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/__init__.py new file mode 100644 index 0000000..048cb1b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/__init__.py @@ -0,0 +1,4 @@ +from ._proxy import AnyioProxy as Proxy +from ._chain import ProxyChain + +__all__ = ('Proxy', 'ProxyChain') diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_chain.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_chain.py new file mode 100644 index 0000000..cb1381c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_chain.py @@ -0,0 +1,42 @@ +from typing import Iterable +import warnings +from ._proxy import AnyioProxy + + +class ProxyChain: + def __init__(self, proxies: Iterable[AnyioProxy]): + warnings.warn( + 'This implementation of ProxyChain is deprecated and will be removed in the future', + DeprecationWarning, + stacklevel=2, + ) + self._proxies = proxies + + async def connect( + self, + dest_host, + dest_port, + dest_ssl=None, + timeout=None, + ): + _stream = None + proxies = list(self._proxies) + + length = len(proxies) - 1 + for i in range(length): + _stream = await proxies[i].connect( + dest_host=proxies[i + 1].proxy_host, + dest_port=proxies[i + 1].proxy_port, + timeout=timeout, + _stream=_stream, + ) + + _stream = await proxies[length].connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + timeout=timeout, + _stream=_stream, + ) + + return _stream diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_connect.py new file mode 100644 index 0000000..dcba2a9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_connect.py @@ -0,0 +1,16 @@ +from typing import Optional +import anyio +import anyio.abc + + +async def connect_tcp( + host: str, + port: int, + local_host: Optional[str] = None, +) -> anyio.abc.SocketStream: + + return await anyio.connect_tcp( + remote_host=host, + remote_port=port, + local_host=local_host, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_proxy.py new file mode 100644 index 0000000..df60a25 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_proxy.py @@ -0,0 +1,137 @@ +import ssl +from typing import Any, Optional +import warnings + +import anyio + +from ..._types import ProxyType +from ..._helpers import parse_proxy_url +from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from ._resolver import Resolver +from ._stream import AnyioSocketStream +from ._connect import connect_tcp + +from ..._protocols.errors import ReplyError +from ..._connectors.factory_async import create_connector + +DEFAULT_TIMEOUT = 60 + + +class AnyioProxy: + _stream: Optional[AnyioSocketStream] + + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + proxy_ssl: Optional[ssl.SSLContext] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._password = password + self._username = username + self._rdns = rdns + + self._proxy_ssl = proxy_ssl + self._resolver = Resolver() + + async def connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> AnyioSocketStream: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + _stream = kwargs.get('_stream') + if _stream is not None: + warnings.warn( + "The '_stream' argument is deprecated and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + local_host = kwargs.get('local_host') + try: + with anyio.fail_after(timeout): + if _stream is None: + try: + _stream = AnyioSocketStream( + await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + local_host=local_host, + ) + ) + except OSError as e: + msg = 'Could not connect to proxy {}:{} [{}]'.format( + self._proxy_host, + self._proxy_port, + e.strerror, + ) + raise ProxyConnectionError(e.errno, msg) from e + + stream = _stream + + try: + if self._proxy_ssl is not None: + stream = await stream.start_tls( + hostname=self._proxy_host, + ssl_context=self._proxy_ssl, + ) + + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + if dest_ssl is not None: + stream = await stream.start_tls( + hostname=dest_host, + ssl_context=dest_ssl, + ) + + return stream + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except BaseException: + await stream.close() + raise + + except TimeoutError as e: + raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e + + @property + def proxy_host(self): + return self._proxy_host + + @property + def proxy_port(self): + return self._proxy_port + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'AnyioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_resolver.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_resolver.py new file mode 100644 index 0000000..d35f65f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_resolver.py @@ -0,0 +1,22 @@ +import anyio +import socket + +from ... import _abc as abc + + +class Resolver(abc.AsyncResolver): + async def resolve(self, host, port=0, family=socket.AF_UNSPEC): + infos = await anyio.getaddrinfo( + host=host, + port=port, + family=family, + type=socket.SOCK_STREAM, + ) + + if not infos: # pragma: no cover + raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family)) + + infos = sorted(infos, key=lambda info: info[0]) + + family, _, _, _, address = infos[0] + return family, address[0] diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_stream.py new file mode 100644 index 0000000..67f6ae8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/_stream.py @@ -0,0 +1,59 @@ +import ssl +from typing import Union + +import anyio +import anyio.abc +from anyio.streams.tls import TLSStream + +from ..._errors import ProxyError +from ... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + +AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream] + + +class AnyioSocketStream(abc.AsyncSocketStream): + _stream: AnyioStreamType + + def __init__(self, stream: AnyioStreamType) -> None: + self._stream = stream + + async def write_all(self, data: bytes): + await self._stream.send(item=data) + + async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE): + try: + return await self._stream.receive(max_bytes=max_bytes) + except anyio.EndOfStream: # pragma: no cover + return b"" + + async def read_exact(self, n: int): + data = bytearray() + while len(data) < n: + packet = await self.read(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + async def start_tls( + self, + hostname: str, + ssl_context: ssl.SSLContext, + ) -> 'AnyioSocketStream': + ssl_stream = await TLSStream.wrap( + self._stream, + ssl_context=ssl_context, + hostname=hostname, + standard_compatible=False, + server_side=False, + ) + return AnyioSocketStream(ssl_stream) + + async def close(self): + await self._stream.aclose() + + @property + def anyio_stream(self) -> AnyioStreamType: # pragma: no cover + return self._stream diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/__init__.py new file mode 100644 index 0000000..4c268d4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/__init__.py @@ -0,0 +1,7 @@ +from ._proxy import AnyioProxy as Proxy +from ._chain import ProxyChain + +__all__ = ( + 'Proxy', + 'ProxyChain', +) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_chain.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_chain.py new file mode 100644 index 0000000..bc6dd31 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_chain.py @@ -0,0 +1,32 @@ +from typing import Sequence +import warnings +from ._proxy import AnyioProxy + + +class ProxyChain: + def __init__(self, proxies: Sequence[AnyioProxy]): + warnings.warn( + 'This implementation of ProxyChain is deprecated and will be removed in the future', + DeprecationWarning, + stacklevel=2, + ) + self._proxies = proxies + + async def connect( + self, + dest_host, + dest_port, + dest_ssl=None, + timeout=None, + ): + forward = None + for proxy in self._proxies: + proxy._forward = forward + forward = proxy + + return await forward.connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + timeout=timeout, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_connect.py new file mode 100644 index 0000000..8b79648 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_connect.py @@ -0,0 +1,17 @@ +from typing import Optional +import anyio +import anyio.abc +from ._stream import AnyioSocketStream + + +async def connect_tcp( + host: str, + port: int, + local_host: Optional[str] = None, +) -> AnyioSocketStream: + s = await anyio.connect_tcp( + remote_host=host, + remote_port=port, + local_host=local_host, + ) + return AnyioSocketStream(s) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_proxy.py new file mode 100644 index 0000000..b68213a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_proxy.py @@ -0,0 +1,135 @@ +import ssl +from typing import Any, Optional + +import anyio + +from ._connect import connect_tcp +from ._stream import AnyioSocketStream +from .._resolver import Resolver +from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from ...._types import ProxyType +from ...._helpers import parse_proxy_url + +from ...._protocols.errors import ReplyError +from ...._connectors.factory_async import create_connector + +DEFAULT_TIMEOUT = 60 + + +class AnyioProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + proxy_ssl: Optional[ssl.SSLContext] = None, + forward: Optional['AnyioProxy'] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._username = username + self._password = password + self._rdns = rdns + + self._proxy_ssl = proxy_ssl + self._forward = forward + + self._resolver = Resolver() + + async def connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> AnyioSocketStream: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + local_host = kwargs.get('local_host') + try: + with anyio.fail_after(timeout): + return await self._connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + local_host=local_host, + ) + except TimeoutError as e: + raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e + + async def _connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + local_host: Optional[str] = None, + ) -> AnyioSocketStream: + if self._forward is None: + try: + stream = await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + local_host=local_host, + ) + except OSError as e: + raise ProxyConnectionError( + e.errno, + "Couldn't connect to proxy" + f" {self._proxy_host}:{self._proxy_port} [{e.strerror}]", + ) from e + else: + stream = await self._forward.connect( + dest_host=self._proxy_host, + dest_port=self._proxy_port, + ) + + try: + if self._proxy_ssl is not None: + stream = await stream.start_tls( + hostname=self._proxy_host, + ssl_context=self._proxy_ssl, + ) + + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + if dest_ssl is not None: + stream = await stream.start_tls( + hostname=dest_host, + ssl_context=dest_ssl, + ) + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except BaseException: + with anyio.CancelScope(shield=True): + await stream.close() + raise + + return stream + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'AnyioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_stream.py new file mode 100644 index 0000000..ee9d0fc --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/anyio/v2/_stream.py @@ -0,0 +1,59 @@ +import ssl +from typing import Union + +import anyio +import anyio.abc +from anyio.streams.tls import TLSStream + +from ...._errors import ProxyError +from .... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + +AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream] + + +class AnyioSocketStream(abc.AsyncSocketStream): + _stream: AnyioStreamType + + def __init__(self, stream: AnyioStreamType) -> None: + self._stream = stream + + async def write_all(self, data: bytes): + await self._stream.send(item=data) + + async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE): + try: + return await self._stream.receive(max_bytes=max_bytes) + except anyio.EndOfStream: # pragma: no cover + return b"" + + async def read_exact(self, n: int): + data = bytearray() + while len(data) < n: + packet = await self.read(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + async def start_tls( + self, + hostname: str, + ssl_context: ssl.SSLContext, + ) -> 'AnyioSocketStream': + ssl_stream = await TLSStream.wrap( + self._stream, + ssl_context=ssl_context, + hostname=hostname, + standard_compatible=False, + server_side=False, + ) + return AnyioSocketStream(ssl_stream) + + async def close(self): + await self._stream.aclose() + + @property + def anyio_stream(self) -> AnyioStreamType: # pragma: no cover + return self._stream diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/__init__.py new file mode 100644 index 0000000..56a682a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/__init__.py @@ -0,0 +1,4 @@ +from ._proxy import AsyncioProxy as Proxy + + +__all__ = ('Proxy',) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_connect.py new file mode 100644 index 0000000..f5527e4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_connect.py @@ -0,0 +1,43 @@ +import socket +import asyncio +from typing import Optional, Tuple + +from ._resolver import Resolver +from ..._helpers import is_ipv4_address, is_ipv6_address + + +async def connect_tcp( + host: str, + port: int, + loop: asyncio.AbstractEventLoop, + local_addr: Optional[Tuple[str, int]] = None, +) -> socket.socket: + + family, host = await _resolve_host(host, loop) + + sock = socket.socket(family=family, type=socket.SOCK_STREAM) + sock.setblocking(False) + if local_addr is not None: # pragma: no cover + sock.bind(local_addr) + + if is_ipv6_address(host): + address = (host, port, 0, 0) # to fix OSError: [WinError 10022] + else: + address = (host, port) # type: ignore[assignment] + + try: + await loop.sock_connect(sock=sock, address=address) + except OSError: + sock.close() + raise + return sock + + +async def _resolve_host(host, loop): + if is_ipv4_address(host): + return socket.AF_INET, host + if is_ipv6_address(host): + return socket.AF_INET6, host + + resolver = Resolver(loop=loop) + return await resolver.resolve(host=host) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_proxy.py new file mode 100644 index 0000000..bce6d07 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_proxy.py @@ -0,0 +1,143 @@ +import asyncio +import socket +import sys +from typing import Any, Optional +import warnings + +from ..._types import ProxyType +from ..._helpers import parse_proxy_url +from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError +from ._stream import AsyncioSocketStream +from ._resolver import Resolver + +from ..._protocols.errors import ReplyError +from ..._connectors.factory_async import create_connector + +from ._connect import connect_tcp + +if sys.version_info >= (3, 11): + import asyncio as async_timeout # pylint:disable=reimported +else: + import async_timeout + +DEFAULT_TIMEOUT = 60 + + +class AsyncioProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ): + if loop is None: + loop = asyncio.get_event_loop() + + self._loop = loop + + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._password = password + self._username = username + self._rdns = rdns + + self._resolver = Resolver(loop=loop) + + async def connect( + self, + dest_host: str, + dest_port: int, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> socket.socket: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + _socket = kwargs.get('_socket') + if _socket is not None: + warnings.warn( + "The '_socket' argument is deprecated and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + local_addr = kwargs.get('local_addr') + try: + async with async_timeout.timeout(timeout): + return await self._connect( + dest_host=dest_host, + dest_port=dest_port, + _socket=_socket, + local_addr=local_addr, + ) + except asyncio.TimeoutError as e: + raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e + + async def _connect( + self, + dest_host, + dest_port, + _socket=None, + local_addr=None, + ) -> socket.socket: + if _socket is None: + try: + _socket = await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + loop=self._loop, + local_addr=local_addr, + ) + except OSError as e: + msg = 'Could not connect to proxy {}:{} [{}]'.format( + self._proxy_host, + self._proxy_port, + e.strerror, + ) + raise ProxyConnectionError(e.errno, msg) from e + + stream = AsyncioSocketStream(sock=_socket, loop=self._loop) + + try: + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + return _socket + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except (asyncio.CancelledError, Exception): # pragma: no cover + await stream.close() + raise + + @property + def proxy_host(self): + return self._proxy_host + + @property + def proxy_port(self): + return self._proxy_port + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'AsyncioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_resolver.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_resolver.py new file mode 100644 index 0000000..f5daaf9 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_resolver.py @@ -0,0 +1,25 @@ +import asyncio +import socket + +from ... import _abc as abc + + +class Resolver(abc.AsyncResolver): + def __init__(self, loop: asyncio.AbstractEventLoop): + self._loop = loop + + async def resolve(self, host, port=0, family=socket.AF_UNSPEC): + infos = await self._loop.getaddrinfo( + host=host, + port=port, + family=family, + type=socket.SOCK_STREAM, + ) + + if not infos: # pragma: no cover + raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family)) + + infos = sorted(infos, key=lambda info: info[0]) + + family, _, _, _, address = infos[0] + return family, address[0] diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_stream.py new file mode 100644 index 0000000..d476dd4 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/_stream.py @@ -0,0 +1,36 @@ +import asyncio +import socket + +from ..._errors import ProxyError + +from ... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + + +class AsyncioSocketStream(abc.AsyncSocketStream): + _loop: asyncio.AbstractEventLoop = None + _socket = None + + def __init__(self, sock: socket.socket, loop: asyncio.AbstractEventLoop): + self._loop = loop + self._socket = sock + + async def write_all(self, data): + await self._loop.sock_sendall(self._socket, data) + + async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return await self._loop.sock_recv(self._socket, max_bytes) + + async def read_exact(self, n): + data = bytearray() + while len(data) < n: + packet = await self._loop.sock_recv(self._socket, n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + async def close(self): + if self._socket is not None: + self._socket.close() diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/__init__.py new file mode 100644 index 0000000..c14b23e --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/__init__.py @@ -0,0 +1,4 @@ +from ._proxy import AsyncioProxy as Proxy +from ._chain import ProxyChain + +__all__ = ('Proxy', 'ProxyChain') diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_chain.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_chain.py new file mode 100644 index 0000000..3928bff --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_chain.py @@ -0,0 +1,32 @@ +from typing import Sequence +import warnings +from ._proxy import AsyncioProxy + + +class ProxyChain: + def __init__(self, proxies: Sequence[AsyncioProxy]): + warnings.warn( + 'This implementation of ProxyChain is deprecated and will be removed in the future', + DeprecationWarning, + stacklevel=2, + ) + self._proxies = proxies + + async def connect( + self, + dest_host: str, + dest_port: int, + dest_ssl=None, + timeout=None, + ): + forward = None + for proxy in self._proxies: + proxy._forward = forward + forward = proxy + + return await forward.connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + timeout=timeout, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_connect.py new file mode 100644 index 0000000..96b2d2f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_connect.py @@ -0,0 +1,26 @@ +import asyncio +from typing import Optional, Tuple +from ._stream import AsyncioSocketStream + + +async def connect_tcp( + host: str, + port: int, + loop: asyncio.AbstractEventLoop, + local_addr: Optional[Tuple[str, int]] = None, +) -> AsyncioSocketStream: + kwargs = {} + if local_addr is not None: + kwargs['local_addr'] = local_addr # pragma: no cover + + reader, writer = await asyncio.open_connection( + host=host, + port=port, + **kwargs, # type: ignore + ) + + return AsyncioSocketStream( + loop=loop, + reader=reader, + writer=writer, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_proxy.py new file mode 100644 index 0000000..04a12c8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_proxy.py @@ -0,0 +1,157 @@ +import asyncio +import ssl +from typing import Any, Optional, Tuple +import warnings +import sys + + +from ...._types import ProxyType +from ...._helpers import parse_proxy_url +from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from ...._protocols.errors import ReplyError +from ...._connectors.factory_async import create_connector + +from .._resolver import Resolver +from ._stream import AsyncioSocketStream +from ._connect import connect_tcp + +if sys.version_info >= (3, 11): + import asyncio as async_timeout # pylint:disable=reimported +else: + import async_timeout + + +DEFAULT_TIMEOUT = 60 + + +class AsyncioProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + proxy_ssl: Optional[ssl.SSLContext] = None, + forward: Optional['AsyncioProxy'] = None, + loop: Optional[asyncio.AbstractEventLoop] = None, + ): + if loop is not None: # pragma: no cover + warnings.warn( + 'The loop argument is deprecated and scheduled for removal in the future.', + DeprecationWarning, + stacklevel=2, + ) + + if loop is None: + loop = asyncio.get_event_loop() + + self._loop = loop + + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._username = username + self._password = password + self._rdns = rdns + + self._proxy_ssl = proxy_ssl + self._forward = forward + + self._resolver = Resolver(loop=loop) + + async def connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> AsyncioSocketStream: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + local_addr = kwargs.get('local_addr') + try: + async with async_timeout.timeout(timeout): + return await self._connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + local_addr=local_addr, + ) + except asyncio.TimeoutError as e: + raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e + + async def _connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + local_addr: Optional[Tuple[str, int]] = None, + ) -> AsyncioSocketStream: + if self._forward is None: + try: + stream = await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + loop=self._loop, + local_addr=local_addr, + ) + except OSError as e: + raise ProxyConnectionError( + e.errno, + "Couldn't connect to proxy" + f" {self._proxy_host}:{self._proxy_port} [{e.strerror}]", + ) from e + else: + stream = await self._forward.connect( + dest_host=self._proxy_host, + dest_port=self._proxy_port, + ) + + try: + if self._proxy_ssl is not None: + stream = await stream.start_tls( + hostname=self._proxy_host, + ssl_context=self._proxy_ssl, + ) + + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + if dest_ssl is not None: + stream = await stream.start_tls( + hostname=dest_host, + ssl_context=dest_ssl, + ) + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except (asyncio.CancelledError, Exception): + await stream.close() + raise + + return stream + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'AsyncioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_stream.py new file mode 100644 index 0000000..fcc0b7f --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/asyncio/v2/_stream.py @@ -0,0 +1,91 @@ +import asyncio +import ssl + +from .... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + + +class AsyncioSocketStream(abc.AsyncSocketStream): + _loop: asyncio.AbstractEventLoop + _reader: asyncio.StreamReader + _writer: asyncio.StreamWriter + + def __init__( + self, + loop: asyncio.AbstractEventLoop, + reader: asyncio.StreamReader, + writer: asyncio.StreamWriter, + ): + self._loop = loop + self._reader = reader + self._writer = writer + + async def write_all(self, data): + self._writer.write(data) + await self._writer.drain() + + async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return await self._reader.read(max_bytes) + + async def read_exact(self, n): + return await self._reader.readexactly(n) + + async def start_tls( + self, + hostname: str, + ssl_context: ssl.SSLContext, + ssl_handshake_timeout=None, + ) -> 'AsyncioSocketStream': + if hasattr(self._writer, 'start_tls'): # Python>=3.11 + await self._writer.start_tls( + ssl_context, + server_hostname=hostname, + ssl_handshake_timeout=ssl_handshake_timeout, + ) + return self + + reader = asyncio.StreamReader() + protocol = asyncio.StreamReaderProtocol(reader) + + transport: asyncio.Transport = await self._loop.start_tls( + self._writer.transport, # type: ignore + protocol, + ssl_context, + server_side=False, + server_hostname=hostname, + ssl_handshake_timeout=ssl_handshake_timeout, + ) + + # reader.set_transport(transport) + + # Initialize the protocol, so it is made aware of being tied to + # a TLS connection. + # See: https://github.com/encode/httpx/issues/859 + protocol.connection_made(transport) + + writer = asyncio.StreamWriter( + transport=transport, + protocol=protocol, + reader=reader, + loop=self._loop, + ) + + stream = AsyncioSocketStream(loop=self._loop, reader=reader, writer=writer) + # When we return a new SocketStream with new StreamReader/StreamWriter instances + # we need to keep references to the old StreamReader/StreamWriter so that they + # are not garbage collected and closed while we're still using them. + stream._inner = self # type: ignore # pylint:disable=W0212,W0201 + return stream + + async def close(self): + self._writer.close() + self._writer.transport.abort() # noqa + + @property + def reader(self): + return self._reader # pragma: no cover + + @property + def writer(self): + return self._writer # pragma: no cover diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/curio/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/__init__.py new file mode 100644 index 0000000..ca3d072 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/__init__.py @@ -0,0 +1,4 @@ +from ._proxy import CurioProxy as Proxy + + +__all__ = ('Proxy',) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_connect.py new file mode 100644 index 0000000..e9e7d2b --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_connect.py @@ -0,0 +1,17 @@ +from typing import Optional, Tuple + +import curio +import curio.io +import curio.socket + + +async def connect_tcp( + host: str, + port: int, + local_addr: Optional[Tuple[str, int]] = None, +) -> curio.io.Socket: + return await curio.open_connection( + host=host, + port=port, + source_addr=local_addr, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_proxy.py new file mode 100644 index 0000000..486f95c --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_proxy.py @@ -0,0 +1,132 @@ +from typing import Any, Optional +import warnings +import curio +import curio.io + +from ..._types import ProxyType +from ..._helpers import parse_proxy_url +from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from ._stream import CurioSocketStream +from ._resolver import Resolver +from ._connect import connect_tcp + +from ..._protocols.errors import ReplyError +from ..._connectors.factory_async import create_connector + + +DEFAULT_TIMEOUT = 60 + + +class CurioProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._password = password + self._username = username + self._rdns = rdns + + self._resolver = Resolver() + + async def connect( + self, + dest_host: str, + dest_port: int, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> curio.io.Socket: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + _socket = kwargs.get('_socket') + if _socket is not None: + warnings.warn( + "The '_socket' argument is deprecated and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + local_addr = kwargs.get('local_addr') + try: + return await curio.timeout_after( + timeout, + self._connect, + dest_host, + dest_port, + _socket, + local_addr, + ) + except curio.TaskTimeout as e: + raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e + + async def _connect( + self, + dest_host: str, + dest_port: int, + _socket=None, + local_addr=None, + ): + if _socket is None: + try: + _socket = await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + local_addr=local_addr, + ) + except OSError as e: + msg = 'Could not connect to proxy {}:{} [{}]'.format( + self._proxy_host, + self._proxy_port, + e.strerror, + ) + raise ProxyConnectionError(e.errno, msg) from e + + stream = CurioSocketStream(_socket) + + try: + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + return _socket + + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except BaseException: + await stream.close() + raise + + @property + def proxy_host(self): + return self._proxy_host + + @property + def proxy_port(self): + return self._proxy_port + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'CurioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_resolver.py b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_resolver.py new file mode 100644 index 0000000..b2ae5c8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_resolver.py @@ -0,0 +1,25 @@ +import socket +from curio.socket import getaddrinfo + +from ... import _abc as abc + + +class Resolver(abc.AsyncResolver): + async def resolve(self, host, port=0, family=socket.AF_UNSPEC): + try: + infos = await getaddrinfo( + host=host, + port=port, + family=family, + type=socket.SOCK_STREAM, + ) + except socket.gaierror: # pragma: no cover + infos = None + + if not infos: # pragma: no cover + raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family)) + + infos = sorted(infos, key=lambda info: info[0]) + + family, _, _, _, address = infos[0] + return family, address[0] diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_stream.py new file mode 100644 index 0000000..e6b52e2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/curio/_stream.py @@ -0,0 +1,32 @@ +import curio.io +import curio.socket + +from ... import _abc as abc +from ..._errors import ProxyError + +DEFAULT_RECEIVE_SIZE = 65536 + + +class CurioSocketStream(abc.AsyncSocketStream): + _socket: curio.io.Socket = None + + def __init__(self, sock: curio.io.Socket): + self._socket = sock + + async def write_all(self, data): + await self._socket.sendall(data) + + async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return await self._socket.recv(max_bytes) + + async def read_exact(self, n): + data = bytearray() + while len(data) < n: + packet = await self._socket.recv(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + async def close(self): + await self._socket.close() diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/__init__.py new file mode 100644 index 0000000..e609fc0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/__init__.py @@ -0,0 +1,3 @@ +from ._proxy import TrioProxy as Proxy + +__all__ = ('Proxy',) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_connect.py new file mode 100644 index 0000000..9419bbf --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_connect.py @@ -0,0 +1,36 @@ +from typing import Optional, Tuple + +import trio + +from ._resolver import Resolver +from ..._helpers import is_ipv4_address, is_ipv6_address + + +async def connect_tcp( + host: str, + port: int, + local_addr: Optional[Tuple[str, int]] = None, +) -> trio.socket.SocketType: + + family, host = await _resolve_host(host) + + sock = trio.socket.socket(family=family, type=trio.socket.SOCK_STREAM) + if local_addr is not None: # pragma: no cover + await sock.bind(local_addr) + + try: + await sock.connect((host, port)) + except OSError: + sock.close() + raise + return sock + + +async def _resolve_host(host): + if is_ipv4_address(host): + return trio.socket.AF_INET, host + if is_ipv6_address(host): + return trio.socket.AF_INET6, host + + resolver = Resolver() + return await resolver.resolve(host=host) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_proxy.py new file mode 100644 index 0000000..d1ae5b7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_proxy.py @@ -0,0 +1,131 @@ +from typing import Any, Optional +import warnings +import trio + +from ..._types import ProxyType +from ..._helpers import parse_proxy_url +from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from ._stream import TrioSocketStream +from ._resolver import Resolver +from ._connect import connect_tcp + +from ..._protocols.errors import ReplyError +from ..._connectors.factory_async import create_connector + + +DEFAULT_TIMEOUT = 60 + + +class TrioProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._password = password + self._username = username + self._rdns = rdns + + self._resolver = Resolver() + + async def connect( + self, + dest_host: str, + dest_port: int, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> trio.socket.SocketType: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + _socket = kwargs.get('_socket') + if _socket is not None: + warnings.warn( + "The '_socket' argument is deprecated and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + local_addr = kwargs.get('local_addr') + try: + with trio.fail_after(timeout): + return await self._connect( + dest_host=dest_host, + dest_port=dest_port, + _socket=_socket, + local_addr=local_addr, + ) + except trio.TooSlowError as e: + raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e + + async def _connect( + self, + dest_host: str, + dest_port: int, + _socket=None, + local_addr=None, + ) -> trio.socket.SocketType: + if _socket is None: + try: + _socket = await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + local_addr=local_addr, + ) + except OSError as e: + msg = 'Could not connect to proxy {}:{} [{}]'.format( + self._proxy_host, + self._proxy_port, + e.strerror, + ) + raise ProxyConnectionError(e.errno, msg) from e + + stream = TrioSocketStream(sock=_socket) + + try: + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + return _socket + + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except BaseException: # trio.Cancelled... + with trio.CancelScope(shield=True): + await stream.close() + raise + + @property + def proxy_host(self): + return self._proxy_host + + @property + def proxy_port(self): + return self._proxy_port + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'TrioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_resolver.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_resolver.py new file mode 100644 index 0000000..1d12292 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_resolver.py @@ -0,0 +1,21 @@ +import trio + +from ... import _abc as abc + + +class Resolver(abc.AsyncResolver): + async def resolve(self, host, port=0, family=trio.socket.AF_UNSPEC): + infos = await trio.socket.getaddrinfo( + host=host, + port=port, + family=family, + type=trio.socket.SOCK_STREAM, + ) + + if not infos: # pragma: no cover + raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family)) + + infos = sorted(infos, key=lambda info: info[0]) + + family, _, _, _, address = infos[0] + return family, address[0] diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_stream.py new file mode 100644 index 0000000..7936fa7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/_stream.py @@ -0,0 +1,35 @@ +import trio + +from ..._errors import ProxyError +from ... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + + +class TrioSocketStream(abc.AsyncSocketStream): + def __init__(self, sock): + self._socket = sock + + async def write_all(self, data): + total_sent = 0 + while total_sent < len(data): + remaining = data[total_sent:] + sent = await self._socket.send(remaining) + total_sent += sent + + async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return await self._socket.recv(max_bytes) + + async def read_exact(self, n): + data = bytearray() + while len(data) < n: + packet = await self._socket.recv(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + async def close(self): + if self._socket is not None: + self._socket.close() + await trio.lowlevel.checkpoint() diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/__init__.py new file mode 100644 index 0000000..564f680 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/__init__.py @@ -0,0 +1,7 @@ +from ._proxy import TrioProxy as Proxy +from ._chain import ProxyChain + +__all__ = ( + 'Proxy', + 'ProxyChain', +) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_chain.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_chain.py new file mode 100644 index 0000000..b56d6b2 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_chain.py @@ -0,0 +1,32 @@ +from typing import Sequence +import warnings +from ._proxy import TrioProxy + + +class ProxyChain: + def __init__(self, proxies: Sequence[TrioProxy]): + warnings.warn( + 'This implementation of ProxyChain is deprecated and will be removed in the future', + DeprecationWarning, + stacklevel=2, + ) + self._proxies = proxies + + async def connect( + self, + dest_host, + dest_port, + dest_ssl=None, + timeout=None, + ): + forward = None + for proxy in self._proxies: + proxy._forward = forward + forward = proxy + + return await forward.connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + timeout=timeout, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_connect.py new file mode 100644 index 0000000..25e75c6 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_connect.py @@ -0,0 +1,17 @@ +from typing import Optional + +import trio +from ._stream import TrioSocketStream + + +async def connect_tcp( + host: str, + port: int, + local_addr: Optional[str] = None, +) -> TrioSocketStream: + trio_stream = await trio.open_tcp_stream( + host=host, + port=port, + local_address=local_addr, + ) + return TrioSocketStream(trio_stream) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_proxy.py new file mode 100644 index 0000000..f23bd47 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_proxy.py @@ -0,0 +1,135 @@ +import ssl +from typing import Any, Optional + +import trio + +from ._connect import connect_tcp +from ._stream import TrioSocketStream +from .._resolver import Resolver + +from ...._types import ProxyType +from ...._helpers import parse_proxy_url +from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from ...._protocols.errors import ReplyError +from ...._connectors.factory_async import create_connector + +DEFAULT_TIMEOUT = 60 + + +class TrioProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + proxy_ssl: Optional[ssl.SSLContext] = None, + forward: Optional['TrioProxy'] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._username = username + self._password = password + self._rdns = rdns + + self._proxy_ssl = proxy_ssl + self._forward = forward + + self._resolver = Resolver() + + async def connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> TrioSocketStream: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + local_addr = kwargs.get('local_addr') + try: + with trio.fail_after(timeout): + return await self._connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + local_addr=local_addr, + ) + except trio.TooSlowError as e: + raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e + + async def _connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + local_addr: Optional[str] = None, + ) -> TrioSocketStream: + if self._forward is None: + try: + stream = await connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + local_addr=local_addr, + ) + except OSError as e: + raise ProxyConnectionError( + e.errno, + "Couldn't connect to proxy" + f" {self._proxy_host}:{self._proxy_port} [{e.strerror}]", + ) from e + else: + stream = await self._forward.connect( + dest_host=self._proxy_host, + dest_port=self._proxy_port, + ) + + try: + if self._proxy_ssl is not None: + stream = await stream.start_tls( + hostname=self._proxy_host, + ssl_context=self._proxy_ssl, + ) + + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + await connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + if dest_ssl is not None: + stream = await stream.start_tls( + hostname=dest_host, + ssl_context=dest_ssl, + ) + except ReplyError as e: + await stream.close() + raise ProxyError(e, error_code=e.error_code) + except BaseException: # trio.Cancelled... + with trio.CancelScope(shield=True): + await stream.close() + raise + + return stream + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'TrioProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_stream.py new file mode 100644 index 0000000..3a28bab --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/async_/trio/v2/_stream.py @@ -0,0 +1,55 @@ +import ssl +from typing import Union + +import trio + +from ...._errors import ProxyError +from .... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + +TrioStreamType = Union[trio.SocketStream, trio.SSLStream] + + +class TrioSocketStream(abc.AsyncSocketStream): + _stream: TrioStreamType + + def __init__(self, stream: TrioStreamType): + self._stream = stream + + async def write_all(self, data): + await self._stream.send_all(data) + + async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return await self._stream.receive_some(max_bytes) + + async def read_exact(self, n): + data = bytearray() + while len(data) < n: + packet = await self._stream.receive_some(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + async def start_tls( + self, + hostname: str, + ssl_context: ssl.SSLContext, + ) -> 'TrioSocketStream': + ssl_stream = trio.SSLStream( + self._stream, + ssl_context=ssl_context, + server_hostname=hostname, + https_compatible=True, + server_side=False, + ) + await ssl_stream.do_handshake() + return TrioSocketStream(ssl_stream) + + async def close(self): + await self._stream.aclose() + + @property + def trio_stream(self) -> TrioStreamType: # pragma: nocover + return self._stream diff --git a/.venv/lib/python3.9/site-packages/python_socks/py.typed b/.venv/lib/python3.9/site-packages/python_socks/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/sync/__init__.py new file mode 100644 index 0000000..ab46dcd --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/__init__.py @@ -0,0 +1,5 @@ +from ._proxy import SyncProxy as Proxy +from ._chain import ProxyChain + + +__all__ = ('Proxy', 'ProxyChain') diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/_chain.py b/.venv/lib/python3.9/site-packages/python_socks/sync/_chain.py new file mode 100644 index 0000000..8934e3a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/_chain.py @@ -0,0 +1,32 @@ +from typing import Iterable +import warnings +from ._proxy import SyncProxy + + +class ProxyChain: + def __init__(self, proxies: Iterable[SyncProxy]): + warnings.warn( + 'This implementation of ProxyChain is deprecated and will be removed in the future', + DeprecationWarning, + stacklevel=2, + ) + self._proxies = proxies + + def connect(self, dest_host, dest_port, timeout=None): + curr_socket = None + proxies = list(self._proxies) + + length = len(proxies) - 1 + for i in range(length): + curr_socket = proxies[i].connect( + dest_host=proxies[i + 1].proxy_host, + dest_port=proxies[i + 1].proxy_port, + timeout=timeout, + _socket=curr_socket, + ) + + curr_socket = proxies[length].connect( + dest_host=dest_host, dest_port=dest_port, timeout=timeout, _socket=curr_socket + ) + + return curr_socket diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/sync/_connect.py new file mode 100644 index 0000000..6c79a4a --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/_connect.py @@ -0,0 +1,16 @@ +import socket +from typing import Optional, Tuple + + +def connect_tcp( + host: str, + port: int, + timeout: Optional[float] = None, + local_addr: Optional[Tuple[str, int]] = None, +) -> socket.socket: + address = (host, port) + return socket.create_connection( + address, + timeout, + source_address=local_addr, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/sync/_proxy.py new file mode 100644 index 0000000..15402d8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/_proxy.py @@ -0,0 +1,116 @@ +import socket +from typing import Optional, Any +import warnings + +from .._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError + +from .._types import ProxyType +from .._helpers import parse_proxy_url +from .._protocols.errors import ReplyError +from .._connectors.factory_sync import create_connector + +from ._stream import SyncSocketStream +from ._resolver import SyncResolver +from ._connect import connect_tcp + + +DEFAULT_TIMEOUT = 60 + + +class SyncProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._password = password + self._username = username + self._rdns = rdns + + self._resolver = SyncResolver() + + def connect( + self, + dest_host: str, + dest_port: int, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> socket.socket: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + _socket = kwargs.get('_socket') + if _socket is not None: + warnings.warn( + "The '_socket' argument is deprecated and will be removed in the future", + DeprecationWarning, + stacklevel=2, + ) + + if _socket is None: + local_addr = kwargs.get('local_addr') + try: + _socket = connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + timeout=timeout, + local_addr=local_addr, + ) + except OSError as e: + msg = 'Could not connect to proxy {}:{} [{}]'.format( + self._proxy_host, + self._proxy_port, + e.strerror, + ) + raise ProxyConnectionError(e.errno, msg) from e + + stream = SyncSocketStream(_socket) + + try: + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + return _socket + except socket.timeout as e: + stream.close() + raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e + except ReplyError as e: + stream.close() + raise ProxyError(e, error_code=e.error_code) + except Exception: + stream.close() + raise + + @property + def proxy_host(self): + return self._proxy_host + + @property + def proxy_port(self): + return self._proxy_port + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'SyncProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/_resolver.py b/.venv/lib/python3.9/site-packages/python_socks/sync/_resolver.py new file mode 100644 index 0000000..3e7f6b0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/_resolver.py @@ -0,0 +1,16 @@ +import socket +from .. import _abc as abc + + +class SyncResolver(abc.SyncResolver): + # noinspection PyMethodMayBeStatic + def resolve(self, host, port=0, family=socket.AF_UNSPEC): + infos = socket.getaddrinfo(host=host, port=port, family=family, type=socket.SOCK_STREAM) + + if not infos: # pragma: no cover + raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family)) + + infos = sorted(infos, key=lambda info: info[0]) + + family, _, _, _, address = infos[0] + return family, address[0] diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/sync/_stream.py new file mode 100644 index 0000000..b52c247 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/_stream.py @@ -0,0 +1,32 @@ +import socket + +from .._errors import ProxyError +from .. import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + + +class SyncSocketStream(abc.SyncSocketStream): + _socket: socket.socket + + def __init__(self, sock: socket.socket): + self._socket = sock + + def write_all(self, data): + self._socket.sendall(data) + + def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return self._socket.recv(max_bytes) + + def read_exact(self, n): + data = bytearray() + while len(data) < n: + packet = self._socket.recv(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + def close(self): + if self._socket is not None: + self._socket.close() diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/v2/__init__.py b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/__init__.py new file mode 100644 index 0000000..f1a1da8 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/__init__.py @@ -0,0 +1,7 @@ +from ._proxy import SyncProxy as Proxy +from ._chain import ProxyChain + +__all__ = ( + 'Proxy', + 'ProxyChain', +) diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_chain.py b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_chain.py new file mode 100644 index 0000000..c6e9078 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_chain.py @@ -0,0 +1,26 @@ +from typing import Iterable +from ._proxy import SyncProxy + + +class ProxyChain: + def __init__(self, proxies: Iterable[SyncProxy]): + self._proxies = proxies + + def connect( + self, + dest_host, + dest_port, + dest_ssl=None, + timeout=None, + ): + forward = None + for proxy in self._proxies: + proxy._forward = forward + forward = proxy + + return forward.connect( + dest_host=dest_host, + dest_port=dest_port, + dest_ssl=dest_ssl, + timeout=timeout, + ) diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_connect.py b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_connect.py new file mode 100644 index 0000000..52ce030 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_connect.py @@ -0,0 +1,19 @@ +import socket +from typing import Optional, Tuple +from ._stream import SyncSocketStream + + +def connect_tcp( + host: str, + port: int, + timeout: Optional[float] = None, + local_addr: Optional[Tuple[str, int]] = None, +) -> SyncSocketStream: + address = (host, port) + sock = socket.create_connection( + address, + timeout, + source_address=local_addr, + ) + + return SyncSocketStream(sock) diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_proxy.py b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_proxy.py new file mode 100644 index 0000000..fdc77e7 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_proxy.py @@ -0,0 +1,121 @@ +import socket +import ssl +from typing import Any, Optional + +from ._connect import connect_tcp +from ._stream import SyncSocketStream +from .._resolver import SyncResolver +from ..._types import ProxyType +from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError +from ..._helpers import parse_proxy_url + +from ..._protocols.errors import ReplyError +from ..._connectors.factory_sync import create_connector + + +DEFAULT_TIMEOUT = 60 + + +class SyncProxy: + def __init__( + self, + proxy_type: ProxyType, + host: str, + port: int, + username: Optional[str] = None, + password: Optional[str] = None, + rdns: Optional[bool] = None, + proxy_ssl: Optional[ssl.SSLContext] = None, + forward: Optional['SyncProxy'] = None, + ): + self._proxy_type = proxy_type + self._proxy_host = host + self._proxy_port = port + self._username = username + self._password = password + self._rdns = rdns + self._proxy_ssl = proxy_ssl + self._forward = forward + + self._resolver = SyncResolver() + + def connect( + self, + dest_host: str, + dest_port: int, + dest_ssl: Optional[ssl.SSLContext] = None, + timeout: Optional[float] = None, + **kwargs: Any, + ) -> SyncSocketStream: + if timeout is None: + timeout = DEFAULT_TIMEOUT + + if self._forward is None: + local_addr = kwargs.get('local_addr') + try: + stream = connect_tcp( + host=self._proxy_host, + port=self._proxy_port, + timeout=timeout, + local_addr=local_addr, + ) + except OSError as e: + msg = 'Could not connect to proxy {}:{} [{}]'.format( + self._proxy_host, + self._proxy_port, + e.strerror, + ) + raise ProxyConnectionError(e.errno, msg) from e + else: + stream = self._forward.connect( + dest_host=self._proxy_host, + dest_port=self._proxy_port, + timeout=timeout, + ) + + try: + if self._proxy_ssl is not None: + stream = stream.start_tls( + hostname=self._proxy_host, + ssl_context=self._proxy_ssl, + ) + + connector = create_connector( + proxy_type=self._proxy_type, + username=self._username, + password=self._password, + rdns=self._rdns, + resolver=self._resolver, + ) + connector.connect( + stream=stream, + host=dest_host, + port=dest_port, + ) + + if dest_ssl is not None: + stream = stream.start_tls( + hostname=dest_host, + ssl_context=dest_ssl, + ) + + return stream + + except socket.timeout as e: + stream.close() + raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e + except ReplyError as e: + stream.close() + raise ProxyError(e, error_code=e.error_code) + except Exception: + stream.close() + raise + + @classmethod + def create(cls, *args, **kwargs): # for backward compatibility + return cls(*args, **kwargs) + + @classmethod + def from_url(cls, url: str, **kwargs) -> 'SyncProxy': + url_args = parse_proxy_url(url) + return cls(*url_args, **kwargs) diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_ssl_transport.py b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_ssl_transport.py new file mode 100644 index 0000000..d48c8f0 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_ssl_transport.py @@ -0,0 +1,200 @@ +""" +Copied from urllib3.util.ssltransport +""" +import io +import socket +import ssl + + +SSL_BLOCKSIZE = 16384 + + +class SSLTransport: + """ + The SSLTransport wraps an existing socket and establishes an SSL connection. + + Contrary to Python's implementation of SSLSocket, it allows you to chain + multiple TLS connections together. It's particularly useful if you need to + implement TLS within TLS. + + The class supports most of the socket API operations. + """ + + def __init__( + self, socket, ssl_context, server_hostname=None, suppress_ragged_eofs=True + ): + """ + Create an SSLTransport around socket using the provided ssl_context. + """ + self.incoming = ssl.MemoryBIO() + self.outgoing = ssl.MemoryBIO() + + self.suppress_ragged_eofs = suppress_ragged_eofs + self.socket = socket + + self.sslobj = ssl_context.wrap_bio( + self.incoming, self.outgoing, server_hostname=server_hostname + ) + + # Perform initial handshake. + self._ssl_io_loop(self.sslobj.do_handshake) + + def __enter__(self): + return self + + def __exit__(self, *_): + self.close() + + def fileno(self): + return self.socket.fileno() + + def read(self, len=1024, buffer=None): + return self._wrap_ssl_read(len, buffer) + + def recv(self, len=1024, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv") + return self._wrap_ssl_read(len) + + def recv_into(self, buffer, nbytes=None, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv_into") + if buffer and (nbytes is None): + nbytes = len(buffer) + elif nbytes is None: + nbytes = 1024 + return self.read(nbytes, buffer) + + def sendall(self, data, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to sendall") + count = 0 + with memoryview(data) as view, view.cast("B") as byte_view: + amount = len(byte_view) + while count < amount: + v = self.send(byte_view[count:]) + count += v + + def send(self, data, flags=0): + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to send") + response = self._ssl_io_loop(self.sslobj.write, data) + return response + + def makefile( + self, mode="r", buffering=None, encoding=None, errors=None, newline=None + ): + """ + Python's httpclient uses makefile and buffered io when reading HTTP + messages and we need to support it. + + This is unfortunately a copy and paste of socket.py makefile with small + changes to point to the socket directly. + """ + if not set(mode) <= {"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 = socket.SocketIO(self, rawmode) + self.socket._io_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 + + def unwrap(self): + self._ssl_io_loop(self.sslobj.unwrap) + + def close(self): + self.socket.close() + + def getpeercert(self, binary_form=False): + return self.sslobj.getpeercert(binary_form) + + def version(self): + return self.sslobj.version() + + def cipher(self): + return self.sslobj.cipher() + + def selected_alpn_protocol(self): + return self.sslobj.selected_alpn_protocol() + + def selected_npn_protocol(self): + return self.sslobj.selected_npn_protocol() + + def shared_ciphers(self): + return self.sslobj.shared_ciphers() + + def compression(self): + return self.sslobj.compression() + + def settimeout(self, value): + self.socket.settimeout(value) + + def gettimeout(self): + return self.socket.gettimeout() + + def _decref_socketios(self): + self.socket._decref_socketios() + + def _wrap_ssl_read(self, len, buffer=None): + try: + return self._ssl_io_loop(self.sslobj.read, len, buffer) + except ssl.SSLError as e: + if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: + return 0 # eof, return 0. + else: + raise + + def _ssl_io_loop(self, func, *args): + """Performs an I/O loop between incoming/outgoing and the socket.""" + should_loop = True + ret = None + + while should_loop: + errno = None + try: + ret = func(*args) + except ssl.SSLError as e: + if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): + # WANT_READ, and WANT_WRITE are expected, others are not. + raise e + errno = e.errno + + buf = self.outgoing.read() + self.socket.sendall(buf) + + if errno is None: + should_loop = False + elif errno == ssl.SSL_ERROR_WANT_READ: + buf = self.socket.recv(SSL_BLOCKSIZE) + if buf: + self.incoming.write(buf) + else: + self.incoming.write_eof() + return ret diff --git a/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_stream.py b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_stream.py new file mode 100644 index 0000000..512da63 --- /dev/null +++ b/.venv/lib/python3.9/site-packages/python_socks/sync/v2/_stream.py @@ -0,0 +1,56 @@ +import socket +import ssl +from typing import Union + +from ._ssl_transport import SSLTransport + +from ..._errors import ProxyError +from ... import _abc as abc + +DEFAULT_RECEIVE_SIZE = 65536 + +SocketType = Union[socket.socket, ssl.SSLSocket, SSLTransport] + + +class SyncSocketStream(abc.SyncSocketStream): + _socket: SocketType + + def __init__(self, sock: SocketType): + self._socket = sock + + def write_all(self, data): + self._socket.sendall(data) + + def read(self, max_bytes=DEFAULT_RECEIVE_SIZE): + return self._socket.recv(max_bytes) + + def read_exact(self, n): + data = bytearray() + while len(data) < n: + packet = self._socket.recv(n - len(data)) + if not packet: # pragma: no cover + raise ProxyError('Connection closed unexpectedly') + data += packet + return data + + def start_tls(self, hostname: str, ssl_context: ssl.SSLContext) -> 'SyncSocketStream': + if isinstance(self._socket, (ssl.SSLSocket, SSLTransport)): + ssl_socket = SSLTransport( + self._socket, + ssl_context=ssl_context, + server_hostname=hostname, + ) + else: # plain socket? + ssl_socket = ssl_context.wrap_socket( + self._socket, + server_hostname=hostname, + ) + + return SyncSocketStream(ssl_socket) + + def close(self): + self._socket.close() + + @property + def socket(self) -> SocketType: # pragma: nocover + return self._socket diff --git a/backend/data/logs/app.log b/backend/data/logs/app.log index f0524da..7a9756c 100644 --- a/backend/data/logs/app.log +++ b/backend/data/logs/app.log @@ -14597,3 +14597,541 @@ httpx.ConnectError: All connection attempts failed 2026-03-12 18:40:31,952 [INFO] wechat-backend - HTTP GET /api/ws-status -> 200 2026-03-12 18:41:31,922 [INFO] wechat-backend - HTTP GET /api/ws-status from 127.0.0.1 2026-03-12 18:41:31,928 [INFO] wechat-backend - HTTP GET /api/ws-status -> 200 +2026-03-15 16:47:21,416 [INFO] wechat-backend - proxy config: tunnel=True (TUNNEL_PROXY=14.103.95.78:16816), kdl=False (KDL_API=(empty)) +2026-03-15 16:52:03,861 [INFO] wechat-backend - proxy config: tunnel=True (TUNNEL_PROXY=14.103.95.78:16816), kdl=False (KDL_API=(empty)) +2026-03-15 16:53:19,865 [INFO] wechat-backend - proxy config: tunnel=True (TUNNEL_PROXY=14.103.95.78:16816), kdl=False (KDL_API=(empty)) +2026-03-15 16:53:20,099 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/message/SetCallback?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 16:53:20,100 [INFO] wechat-backend - SetCallback registered for key=***9BJZ, CallbackURL=https://dissonant-destinee-nonsensibly.ngrok-free.dev/api/callback/wechat-message +2026-03-15 16:53:20,100 [INFO] wechat-backend - 启动时已注册回调 key=***9BJZ +2026-03-15 16:53:20,100 [INFO] wechat-backend - 消息接收已切换为实时回调入口,不再启动 WS GetSyncMsg +2026-03-15 17:03:34,359 [INFO] wechat-backend - HTTP GET /api/check-proxy from 127.0.0.1 +2026-03-15 17:03:34,360 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:03:34,360 [INFO] wechat-backend - check-proxy: using tunnel -> socks5h://***@14.103.95.78:16816/ +2026-03-15 17:03:34,360 [INFO] wechat-backend - check-proxy: source=tunnel, proxy_preview=socks5h://fawbjjkk:***@14.103.95.78:16816/ +2026-03-15 17:03:34,363 [WARNING] wechat-backend - check-proxy: exception Unknown scheme for proxy URL URL('socks5h://fawbjjkk:[secure]@14.103.95.78:16816/') +2026-03-15 17:03:34,364 [INFO] wechat-backend - HTTP GET /api/check-proxy -> 200 +2026-03-15 17:05:11,886 [INFO] wechat-backend - HTTP POST /auth/wake from 127.0.0.1 +2026-03-15 17:05:11,889 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:05:11,889 [INFO] wechat-backend - WakeUpLogin: using proxy from tunnel (socks5h), len=47 +2026-03-15 17:05:11,889 [INFO] wechat-backend - WakeUpLogin: using proxy from body/env, len=47 +2026-03-15 17:05:11,889 [INFO] wechat-backend - WakeUpLogin 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/WakeUpLogin, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816/, Check=False, IpadOrmac=ipad +2026-03-15 17:05:14,682 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/WakeUpLogin?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:05:14,684 [INFO] wechat-backend - WakeUpLogin success: status=200 +2026-03-15 17:05:14,685 [INFO] wechat-backend - HTTP POST /auth/wake -> 200 +2026-03-15 17:06:45,653 [INFO] wechat-backend - proxy config: tunnel=True (TUNNEL_PROXY=14.103.95.78:16816), kdl=False (KDL_API=(empty)) +2026-03-15 17:06:45,837 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/message/SetCallback?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:06:45,855 [INFO] wechat-backend - SetCallback registered for key=***9BJZ, CallbackURL=https://dissonant-destinee-nonsensibly.ngrok-free.dev/api/callback/wechat-message +2026-03-15 17:06:45,855 [INFO] wechat-backend - 启动时已注册回调 key=***9BJZ +2026-03-15 17:06:45,855 [INFO] wechat-backend - 消息接收已切换为实时回调入口,不再启动 WS GetSyncMsg +2026-03-15 17:06:53,013 [INFO] wechat-backend - HTTP GET /api/check-proxy from 127.0.0.1 +2026-03-15 17:06:53,014 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:06:53,014 [INFO] wechat-backend - check-proxy: using tunnel -> socks5h://***@14.103.95.78:16816/ +2026-03-15 17:06:53,014 [INFO] wechat-backend - check-proxy: source=tunnel, proxy_preview=socks5h://fawbjjkk:***@14.103.95.78:16816/ +2026-03-15 17:06:53,038 [WARNING] wechat-backend - check-proxy: socks exception Invalid scheme component: socks5h +2026-03-15 17:06:53,051 [INFO] wechat-backend - HTTP GET /api/check-proxy -> 200 +2026-03-15 17:08:55,808 [INFO] wechat-backend - proxy config: tunnel=True (TUNNEL_PROXY=14.103.95.78:16816), kdl=False (KDL_API=(empty)) +2026-03-15 17:08:56,054 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/message/SetCallback?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:08:56,054 [INFO] wechat-backend - SetCallback registered for key=***9BJZ, CallbackURL=https://dissonant-destinee-nonsensibly.ngrok-free.dev/api/callback/wechat-message +2026-03-15 17:08:56,055 [INFO] wechat-backend - 启动时已注册回调 key=***9BJZ +2026-03-15 17:08:56,055 [INFO] wechat-backend - 消息接收已切换为实时回调入口,不再启动 WS GetSyncMsg +2026-03-15 17:08:59,213 [INFO] wechat-backend - HTTP GET /api/check-proxy from 127.0.0.1 +2026-03-15 17:08:59,215 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:08:59,215 [INFO] wechat-backend - check-proxy: using tunnel -> socks5h://***@14.103.95.78:16816/ +2026-03-15 17:08:59,216 [INFO] wechat-backend - check-proxy: source=tunnel, proxy_preview=socks5h://fawbjjkk:***@14.103.95.78:16816/ +2026-03-15 17:09:01,078 [INFO] httpx - HTTP Request: GET https://httpbin.org/ip "HTTP/1.1 200 OK" +2026-03-15 17:09:01,080 [INFO] wechat-backend - check-proxy: ok (socks), status=200 +2026-03-15 17:09:01,080 [INFO] wechat-backend - HTTP GET /api/check-proxy -> 200 +2026-03-15 17:09:05,988 [INFO] wechat-backend - HTTP POST /auth/wake from 127.0.0.1 +2026-03-15 17:09:05,993 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:09:05,993 [INFO] wechat-backend - WakeUpLogin: using proxy from tunnel (socks5h), len=47 +2026-03-15 17:09:05,994 [INFO] wechat-backend - WakeUpLogin: using proxy from body/env, len=47 +2026-03-15 17:09:05,994 [INFO] wechat-backend - WakeUpLogin 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/WakeUpLogin, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816/, Check=False, IpadOrmac=ipad +2026-03-15 17:09:09,847 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/WakeUpLogin?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:09,849 [INFO] wechat-backend - WakeUpLogin success: status=200 +2026-03-15 17:09:09,850 [INFO] wechat-backend - HTTP POST /auth/wake -> 200 +2026-03-15 17:09:28,145 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:09:28,145 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:09:29,259 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:29,260 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"w8tOKbiOZrtgfmVqW19Q","state":2,"wxid":"wxid_f2q8xscgg31322","wxnewpass":"extdevnewpwd_CiNBdzVlOGJrMkVjN2ZpY3RMSlQxQzZ5SUFAcXJ0aWNrZXRfMxJAV191bzR3NkpfY1FXU01EQ00zOFFLVlpFNG5jS09mQ0lTc0k2UlBRVnoyUDlFQW9rbjFJRHhNUG8yZzJpNzF6bxoYZ1NmVUdYY01XMk9pa1lCcEpOQTFqbTlD","head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":604200,"nick_name":"时代在召唤BOT","effective_time":236,"unknow":402670896,"device":"iphone","ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"登录成功","data62":"","ticket":""},"Text":"登录成功","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:09:29,261 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:09:29,334 [INFO] wechat-backend - HTTP GET /api/customers from 127.0.0.1 +2026-03-15 17:09:29,367 [INFO] wechat-backend - HTTP GET /api/customers -> 200 +2026-03-15 17:09:29,369 [INFO] wechat-backend - HTTP GET /api/greeting-tasks from 127.0.0.1 +2026-03-15 17:09:29,378 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:09:29,387 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:09:29,387 [INFO] wechat-backend - HTTP GET /api/greeting-tasks -> 200 +2026-03-15 17:09:29,396 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:09:29,396 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:09:29,404 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:09:29,407 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:09:29,408 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:09:29,411 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:09:29,418 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:09:29,420 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:09:29,423 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:09:29,423 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:09:31,057 [INFO] wechat-backend - HTTP GET /api/contact-list from 127.0.0.1 +2026-03-15 17:09:31,180 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/friend/GetContactList?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:31,182 [INFO] wechat-backend - GetContactList response structure (no items extracted): top_level_keys=['Code', 'Data', 'Text', 'Success', 'Data62', 'Ticket'], Data_keys=None +2026-03-15 17:09:31,183 [INFO] wechat-backend - GetContactList usernames total=0 +2026-03-15 17:09:31,184 [INFO] wechat-backend - api_contact_list key=***9BJZ -> 0 contacts +2026-03-15 17:09:31,184 [INFO] wechat-backend - HTTP GET /api/contact-list -> 200 +2026-03-15 17:09:32,235 [INFO] wechat-backend - HTTP GET /api/contact-list from 127.0.0.1 +2026-03-15 17:09:32,368 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/friend/GetContactList?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:32,370 [INFO] wechat-backend - GetContactList response structure (no items extracted): top_level_keys=['Code', 'Data', 'Text', 'Success', 'Data62', 'Ticket'], Data_keys=None +2026-03-15 17:09:32,370 [INFO] wechat-backend - GetContactList usernames total=0 +2026-03-15 17:09:32,370 [INFO] wechat-backend - api_contact_list key=***9BJZ -> 0 contacts +2026-03-15 17:09:32,371 [INFO] wechat-backend - HTTP GET /api/contact-list -> 200 +2026-03-15 17:09:32,946 [INFO] wechat-backend - HTTP GET /api/contact-list from 127.0.0.1 +2026-03-15 17:09:34,062 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/friend/GetContactList?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:34,063 [INFO] wechat-backend - GetContactList response structure (no items extracted): top_level_keys=['Code', 'Data', 'Text', 'Success', 'Data62', 'Ticket'], Data_keys=None +2026-03-15 17:09:34,064 [INFO] wechat-backend - GetContactList usernames total=0 +2026-03-15 17:09:34,064 [INFO] wechat-backend - api_contact_list key=***9BJZ -> 0 contacts +2026-03-15 17:09:34,064 [INFO] wechat-backend - HTTP GET /api/contact-list -> 200 +2026-03-15 17:09:41,645 [INFO] wechat-backend - HTTP POST /auth/qrcode from 127.0.0.1 +2026-03-15 17:09:41,661 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:09:41,661 [INFO] wechat-backend - GetLoginQrCodeNewDirect: using proxy from tunnel (socks5h), len=47 +2026-03-15 17:09:41,661 [INFO] wechat-backend - GetLoginQrCodeNewDirect: proxy=yes, force_mac=False, IpadOrmac=ipad +2026-03-15 17:09:41,665 [INFO] wechat-backend - GetLoginQrCodeNewDirect 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816/, Check=False, IpadOrmac=ipad +2026-03-15 17:09:44,439 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:44,440 [INFO] wechat-backend - Upstream GetLoginQrCodeNewDirect success: status=200, body_len=500 +2026-03-15 17:09:44,441 [INFO] wechat-backend - Stored Data62 (full) for key=HBpEnbtj9BJZ (len=344), valid=True, check=完整有效 +2026-03-15 17:09:44,441 [INFO] wechat-backend - Data62 full: 62706c6973743030d4010203040506090a582476657273696f6e58246f626a65637473592461726368697665725424746f7012000186a0a2070855246e756c6c5f102064346461356637326330626237633631326338336262626264613137323834345f100f4e534b657965644172636869766572d10b0c54726f6f74800108111a232d32373a406375787d0000000000000101000000000000000d0000000000000000000000000000007f +2026-03-15 17:09:44,442 [INFO] wechat-backend - HTTP POST /auth/qrcode -> 200 +2026-03-15 17:09:46,470 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:09:46,471 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:09:46,649 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:46,651 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":0,"push_login_url_expired_time":0,"effective_time":239,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:09:46,652 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:09:49,473 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:09:49,474 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:09:49,686 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:49,688 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":0,"push_login_url_expired_time":0,"effective_time":236,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:09:49,688 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:09:54,474 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:09:54,475 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:09:54,591 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:54,593 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:09:54,594 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:09:59,474 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:09:59,475 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:09:59,583 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:09:59,585 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":230,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:09:59,585 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:09:59,586 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:09:59,594 [INFO] wechat-backend - HTTP GET /auth/slider-assets/N_jYM_2V.js from 127.0.0.1 +2026-03-15 17:09:59,752 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7765/assets/N_jYM_2V.js "HTTP/1.1 200 OK" +2026-03-15 17:09:59,842 [INFO] wechat-backend - HTTP GET /auth/slider-assets/N_jYM_2V.js -> 200 +2026-03-15 17:10:05,059 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:05,060 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:05,155 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:05,157 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":225,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:05,157 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:05,158 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:09,473 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:09,474 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:09,669 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:09,671 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":220,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:09,672 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:09,672 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:15,016 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:15,017 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:15,165 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:15,166 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":215,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:15,167 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:15,168 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:20,059 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:20,060 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:20,192 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:20,194 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":210,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:20,195 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:20,195 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:25,059 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:25,060 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:25,157 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:25,158 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":205,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:25,159 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:25,159 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:30,060 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:30,061 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:30,167 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:30,168 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":200,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:30,168 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:30,169 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:35,084 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:35,085 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:35,197 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:35,200 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":195,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"请提交验证码后登录","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"请提交验证码后登录","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:35,200 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:35,201 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:48,032 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:48,033 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:48,145 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:48,147 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":182,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:48,148 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:48,149 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:50,717 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:50,718 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:50,822 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:50,825 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":179,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:50,826 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:50,826 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:10:55,059 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:10:55,060 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:10:55,159 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:10:55,161 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":175,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:10:55,162 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:10:55,162 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:00,059 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:00,060 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:00,291 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:00,294 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":170,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:11:00,294 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:11:00,295 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:05,059 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:05,060 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:05,239 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:05,240 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":165,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:11:05,240 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:11:05,241 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:10,058 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:10,059 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:10,181 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:10,183 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":160,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:11:10,184 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:11:10,184 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:15,057 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:15,058 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:15,171 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:15,173 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":155,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":"3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n"},"Text":"","Success":false,"Data62":"","Ticket":"","wechat_verify_url":"ticket=3_73d7229b40918b075bd8b8d54cb7010e\ufffd\u0001\u0002\b\u0000\ufffd\u0001\u0006\b\u0002\u0012\u0002\b\n","verify_instructions":"这个就是第二步返回的key, 请根据文档操作!!!"} +2026-03-15 17:11:15,173 [INFO] wechat-backend - Attached slider_url (slider-form) for key=HBpEnbtj9BJZ (ticket len=34, data62 len=344) +2026-03-15 17:11:15,174 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:20,058 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:20,058 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:20,164 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:20,166 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:11:20,167 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:38,355 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:38,356 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:38,468 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:38,471 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:11:38,472 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:39,473 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:39,474 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:39,564 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:39,566 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:11:39,567 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:44,473 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:44,474 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:44,610 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:44,612 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:11:44,613 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:49,475 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:49,475 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:49,578 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:49,580 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:11:49,581 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:11:55,060 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:11:55,061 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:11:55,192 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:11:55,194 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:11:55,195 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:02,031 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:02,032 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:02,325 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:02,326 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:02,326 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:05,600 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:05,600 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:05,937 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:05,939 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:05,939 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:19,031 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:19,032 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:19,161 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:19,163 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:19,164 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:21,535 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:21,536 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:21,658 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:21,660 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:21,661 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:24,470 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:24,471 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:24,580 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:24,582 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":152,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:24,583 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:29,327 [INFO] wechat-backend - HTTP POST /auth/qrcode from 127.0.0.1 +2026-03-15 17:12:29,329 [INFO] wechat-backend - GetLoginQrCodeNewDirect: proxy=yes, force_mac=False, IpadOrmac=ipad +2026-03-15 17:12:29,329 [INFO] wechat-backend - GetLoginQrCodeNewDirect 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816, Check=False, IpadOrmac=ipad +2026-03-15 17:12:29,473 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:29,475 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:29,587 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:29,589 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g9XegdOwKvygCtnQxZ6w","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":153,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"登录处理中,请稍候...","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:29,589 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:32,082 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:32,084 [INFO] wechat-backend - Upstream GetLoginQrCodeNewDirect success: status=200, body_len=500 +2026-03-15 17:12:32,085 [INFO] wechat-backend - Stored Data62 (full) for key=HBpEnbtj9BJZ (len=344), valid=True, check=完整有效 +2026-03-15 17:12:32,085 [INFO] wechat-backend - Data62 full: 62706c6973743030d4010203040506090a582476657273696f6e58246f626a65637473592461726368697665725424746f7012000186a0a2070855246e756c6c5f102064346461356637326330626237633631326338336262626264613137323834345f100f4e534b657965644172636869766572d10b0c54726f6f74800108111a232d32373a406375787d0000000000000101000000000000000d0000000000000000000000000000007f +2026-03-15 17:12:32,086 [INFO] wechat-backend - HTTP POST /auth/qrcode -> 200 +2026-03-15 17:12:34,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:34,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:34,218 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:34,220 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":0,"push_login_url_expired_time":0,"effective_time":239,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:34,220 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:37,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:37,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:37,220 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:37,222 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":238,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:37,223 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:39,780 [INFO] wechat-backend - HTTP POST /auth/wake from 127.0.0.1 +2026-03-15 17:12:39,781 [INFO] wechat-backend - WakeUpLogin: using proxy from body/env, len=46 +2026-03-15 17:12:39,782 [INFO] wechat-backend - WakeUpLogin 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/WakeUpLogin, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816, Check=False, IpadOrmac=ipad +2026-03-15 17:12:42,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:42,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:42,516 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/WakeUpLogin?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:42,518 [INFO] wechat-backend - WakeUpLogin success: status=200 +2026-03-15 17:12:42,519 [INFO] wechat-backend - HTTP POST /auth/wake -> 200 +2026-03-15 17:12:44,515 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:44,516 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":300,"Data":null,"Text":"不存在状态","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:44,517 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:47,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:47,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:47,209 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:47,210 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:47,211 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:52,108 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:52,109 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:52,261 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:52,263 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:52,264 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:12:57,108 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:12:57,109 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:12:57,225 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:12:57,227 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:12:57,228 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:02,103 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:02,104 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:02,275 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:02,277 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:02,278 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:07,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:07,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:07,200 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:07,202 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:07,202 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:12,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:12,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:12,222 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:12,224 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:12,225 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:17,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:17,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:17,214 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:17,216 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:17,217 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:22,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:22,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:22,226 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:22,228 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:22,229 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:27,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:27,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:27,262 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:27,264 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:27,265 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:32,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:32,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:32,231 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:32,233 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:32,234 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:37,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:37,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:37,264 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:37,266 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:37,266 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:42,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:42,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:42,202 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:42,204 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:42,205 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:47,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:47,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:47,221 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:47,223 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:47,224 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:52,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:52,109 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:52,247 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:52,249 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:52,250 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:13:57,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:13:57,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:13:57,286 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:13:57,288 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:13:57,289 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:02,108 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:02,109 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:02,213 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:02,215 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:02,216 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:07,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:07,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:07,210 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:07,211 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:07,212 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:12,107 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:12,108 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:12,234 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:12,236 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:12,237 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:17,105 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:17,107 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:17,238 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:17,240 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"二维码失效","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:17,241 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:19,105 [INFO] wechat-backend - HTTP POST /auth/wake from 127.0.0.1 +2026-03-15 17:14:19,106 [INFO] wechat-backend - WakeUpLogin: using proxy from body/env, len=46 +2026-03-15 17:14:19,106 [INFO] wechat-backend - WakeUpLogin 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/WakeUpLogin, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816, Check=False, IpadOrmac=ipad +2026-03-15 17:14:22,071 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/WakeUpLogin?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:22,073 [INFO] wechat-backend - WakeUpLogin success: status=200 +2026-03-15 17:14:22,074 [INFO] wechat-backend - HTTP POST /auth/wake -> 200 +2026-03-15 17:14:22,102 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:22,118 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:24,244 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:24,246 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":300,"Data":null,"Text":"不存在状态","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:24,247 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:25,961 [INFO] wechat-backend - HTTP POST /auth/qrcode from 127.0.0.1 +2026-03-15 17:14:25,963 [INFO] wechat-backend - GetLoginQrCodeNewDirect: proxy=yes, force_mac=True, IpadOrmac=mac +2026-03-15 17:14:25,963 [INFO] wechat-backend - GetLoginQrCodeNewDirect 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816, Check=False, IpadOrmac=mac +2026-03-15 17:14:27,106 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:27,115 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:27,252 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:27,254 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"I_wDcY_6k53oBvbOtpMf","state":4,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":301,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"二维码失效","data62":"","ticket":""},"Text":"登录处理中,请稍候...","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:27,254 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:28,912 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:28,914 [INFO] wechat-backend - Upstream GetLoginQrCodeNewDirect success: status=200, body_len=500 +2026-03-15 17:14:28,915 [INFO] wechat-backend - Stored Data62 (full) for key=HBpEnbtj9BJZ (len=344), valid=True, check=完整有效 +2026-03-15 17:14:28,915 [INFO] wechat-backend - Data62 full: 62706c6973743030d4010203040506090a582476657273696f6e58246f626a65637473592461726368697665725424746f7012000186a0a2070855246e756c6c5f102064346461356637326330626237633631326338336262626264613137323834345f100f4e534b657965644172636869766572d10b0c54726f6f74800108111a232d32373a406375787d0000000000000101000000000000000d0000000000000000000000000000007f +2026-03-15 17:14:28,916 [INFO] wechat-backend - HTTP POST /auth/qrcode -> 200 +2026-03-15 17:14:30,935 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:30,936 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:31,107 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:31,109 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"w_khE5QqfNBucmYPmKES","state":0,"push_login_url_expired_time":0,"effective_time":239,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:31,109 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:33,936 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:33,937 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:34,059 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:34,061 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"w_khE5QqfNBucmYPmKES","state":0,"push_login_url_expired_time":0,"effective_time":236,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:34,062 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:40,114 [INFO] wechat-backend - HTTP POST /auth/qrcode from 127.0.0.1 +2026-03-15 17:14:40,116 [INFO] wechat-backend - proxy resolve: auto -> tunnel (socks5h), TUNNEL_PROXY=14.103.95.78:16816 +2026-03-15 17:14:40,116 [INFO] wechat-backend - GetLoginQrCodeNewDirect: using proxy from tunnel (socks5h), len=47 +2026-03-15 17:14:40,116 [INFO] wechat-backend - GetLoginQrCodeNewDirect: proxy=yes, force_mac=True, IpadOrmac=mac +2026-03-15 17:14:40,116 [INFO] wechat-backend - GetLoginQrCodeNewDirect 请求参数: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect, Proxy=socks5h://fawbjjkk:5hz6avfb@14.103.95.78:16816/, Check=False, IpadOrmac=mac +2026-03-15 17:14:47,031 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/login/GetLoginQrCodeNewDirect?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:47,033 [INFO] wechat-backend - Upstream GetLoginQrCodeNewDirect success: status=200, body_len=500 +2026-03-15 17:14:47,033 [INFO] wechat-backend - Stored Data62 (full) for key=HBpEnbtj9BJZ (len=344), valid=True, check=完整有效 +2026-03-15 17:14:47,033 [INFO] wechat-backend - Data62 full: 62706c6973743030d4010203040506090a582476657273696f6e58246f626a65637473592461726368697665725424746f7012000186a0a2070855246e756c6c5f102064346461356637326330626237633631326338336262626264613137323834345f100f4e534b657965644172636869766572d10b0c54726f6f74800108111a232d32373a406375787d0000000000000101000000000000000d0000000000000000000000000000007f +2026-03-15 17:14:47,034 [INFO] wechat-backend - HTTP POST /auth/qrcode -> 200 +2026-03-15 17:14:49,062 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:49,064 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:49,173 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:49,175 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g7fFpCNEg0lDVYss4KDV","state":0,"push_login_url_expired_time":0,"effective_time":239,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:49,176 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:52,062 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:52,063 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:52,192 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:52,194 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g7fFpCNEg0lDVYss4KDV","state":0,"push_login_url_expired_time":0,"effective_time":236,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:52,195 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:14:57,062 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:14:57,063 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:14:57,178 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:14:57,180 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g7fFpCNEg0lDVYss4KDV","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":235,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:14:57,180 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:15:02,063 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:15:02,064 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:15:02,218 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:15:02,220 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"uuid":"g7fFpCNEg0lDVYss4KDV","state":1,"head_img_url":"http://wx.qlogo.cn/mmhead/ver_1/oynDcBAwCaDTE9YibF7bstkHrY3BY2sZsOicDkYlfbibSicsgWQepadjokaC9ZdVobj0JNaV7mpPJ8DJyo7ib0yrlMsHuXMYdic0PguibeTjdicdrAEzygRdW67FAHvzWISHbXUomibxttlicKxulycUvq8aVeQg/0","push_login_url_expired_time":0,"nick_name":"时代在召唤BOT","effective_time":231,"unknow":0,"ret":0,"othersInServerLogin":false,"tarGetServerIp":"","uuId":"","msg":"","data62":"","ticket":""},"Text":"","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:15:02,221 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:15:07,063 [INFO] wechat-backend - HTTP GET /auth/scan-status from 127.0.0.1 +2026-03-15 17:15:07,064 [INFO] wechat-backend - CheckLoginStatus: key=HBpEnbtj9BJZ, url=http://113.44.162.180:7006/login/CheckLoginStatus +2026-03-15 17:15:07,170 [INFO] httpx - HTTP Request: GET http://113.44.162.180:7006/login/CheckLoginStatus?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:15:07,172 [INFO] wechat-backend - Upstream CheckLoginStatus response: status=200, body={"Code":200,"Data":{"data62":"","loginState":"online","state":2,"ticket":""},"Text":"账号已登录","Success":false,"Data62":"","Ticket":""} +2026-03-15 17:15:07,173 [INFO] wechat-backend - HTTP GET /auth/scan-status -> 200 +2026-03-15 17:15:07,248 [INFO] wechat-backend - HTTP GET /api/customers from 127.0.0.1 +2026-03-15 17:15:07,258 [INFO] wechat-backend - HTTP GET /api/customers -> 200 +2026-03-15 17:15:07,259 [INFO] wechat-backend - HTTP GET /api/greeting-tasks from 127.0.0.1 +2026-03-15 17:15:07,283 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:07,285 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:07,286 [INFO] wechat-backend - HTTP GET /api/greeting-tasks -> 200 +2026-03-15 17:15:07,299 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:07,300 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:07,312 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:07,315 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:07,319 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:07,340 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:07,345 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:07,348 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:07,350 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:07,351 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:13,388 [INFO] wechat-backend - HTTP GET /api/customer-tags from 127.0.0.1 +2026-03-15 17:15:13,390 [INFO] wechat-backend - HTTP GET /api/customer-tags -> 200 +2026-03-15 17:15:14,863 [INFO] wechat-backend - HTTP GET /api/callback-status from 127.0.0.1 +2026-03-15 17:15:14,876 [INFO] wechat-backend - HTTP GET /api/ai-reply-config from 127.0.0.1 +2026-03-15 17:15:14,879 [INFO] wechat-backend - HTTP GET /api/ai-reply-config -> 200 +2026-03-15 17:15:14,987 [INFO] httpx - HTTP Request: POST http://113.44.162.180:7006/message/SetCallback?key=HBpEnbtj9BJZ "HTTP/1.1 200 OK" +2026-03-15 17:15:14,989 [INFO] wechat-backend - SetCallback registered for key=***9BJZ, CallbackURL=https://dissonant-destinee-nonsensibly.ngrok-free.dev/api/callback/wechat-message +2026-03-15 17:15:14,990 [INFO] wechat-backend - HTTP GET /api/callback-status -> 200 +2026-03-15 17:15:15,209 [INFO] wechat-backend - HTTP GET /api/ws-status from 127.0.0.1 +2026-03-15 17:15:15,213 [INFO] wechat-backend - HTTP GET /api/ws-status -> 200 +2026-03-15 17:15:18,846 [INFO] wechat-backend - HTTP GET /api/customers from 127.0.0.1 +2026-03-15 17:15:18,855 [INFO] wechat-backend - HTTP GET /api/greeting-tasks from 127.0.0.1 +2026-03-15 17:15:18,861 [INFO] wechat-backend - HTTP GET /api/customers -> 200 +2026-03-15 17:15:18,880 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:18,881 [INFO] wechat-backend - HTTP GET /api/greeting-tasks -> 200 +2026-03-15 17:15:18,887 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:18,891 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:18,894 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:18,902 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:18,908 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:18,908 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:18,911 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:18,915 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:18,917 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:18,920 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:18,920 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:19,019 [INFO] wechat-backend - HTTP GET /api/customers from 127.0.0.1 +2026-03-15 17:15:19,026 [INFO] wechat-backend - HTTP GET /api/greeting-tasks from 127.0.0.1 +2026-03-15 17:15:19,028 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:19,030 [INFO] wechat-backend - HTTP GET /api/customers -> 200 +2026-03-15 17:15:19,039 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:19,044 [INFO] wechat-backend - HTTP GET /api/greeting-tasks -> 200 +2026-03-15 17:15:19,046 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:19,048 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:19,064 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:19,072 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:19,072 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:19,075 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:19,083 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:19,085 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:19,088 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:19,088 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:19,168 [INFO] wechat-backend - HTTP GET /api/customers from 127.0.0.1 +2026-03-15 17:15:19,172 [INFO] wechat-backend - HTTP GET /api/greeting-tasks from 127.0.0.1 +2026-03-15 17:15:19,172 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:19,173 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:19,173 [INFO] wechat-backend - HTTP GET /api/customers -> 200 +2026-03-15 17:15:19,180 [INFO] wechat-backend - HTTP GET /api/greeting-tasks -> 200 +2026-03-15 17:15:19,181 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:19,182 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:19,187 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:19,190 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:19,192 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:19,192 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:19,195 [INFO] wechat-backend - HTTP GET /api/product-tags from 127.0.0.1 +2026-03-15 17:15:19,197 [INFO] wechat-backend - HTTP GET /api/push-groups from 127.0.0.1 +2026-03-15 17:15:19,200 [INFO] wechat-backend - HTTP GET /api/product-tags -> 200 +2026-03-15 17:15:19,200 [INFO] wechat-backend - HTTP GET /api/push-groups -> 200 +2026-03-15 17:15:28,058 [INFO] wechat-backend - HTTP GET /api/ws-status from 127.0.0.1 +2026-03-15 17:15:28,059 [INFO] wechat-backend - HTTP GET /api/ws-status -> 200 diff --git a/backend/data/logs/node-access.log b/backend/data/logs/node-access.log index 1026f96..a659e96 100644 --- a/backend/data/logs/node-access.log +++ b/backend/data/logs/node-access.log @@ -5479,3 +5479,140 @@ ::1 - - [12/Mar/2026:10:39:31 +0000] "GET /api/ws-status HTTP/1.1" 200 19 "http://localhost:3000/models.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" ::1 - - [12/Mar/2026:10:40:31 +0000] "GET /api/ws-status HTTP/1.1" 200 19 "http://localhost:3000/models.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" ::1 - - [12/Mar/2026:10:41:31 +0000] "GET /api/ws-status HTTP/1.1" 200 19 "http://localhost:3000/models.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" +[2026-03-15T08:47:21.858Z] Static frontend server listening on port 3000; access log: /Users/dannier/Desktop/living/AICLW/wechatAiclaw/backend/data/logs/node-access.log +[2026-03-15T08:52:04.263Z] Static frontend server listening on port 3000; access log: /Users/dannier/Desktop/living/AICLW/wechatAiclaw/backend/data/logs/node-access.log +[2026-03-15T08:53:20.303Z] Static frontend server listening on port 3000; access log: /Users/dannier/Desktop/living/AICLW/wechatAiclaw/backend/data/logs/node-access.log +::1 - - [15/Mar/2026:09:03:16 +0000] "GET / HTTP/1.1" 200 48426 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:03:16 +0000] "GET /health HTTP/1.1" 200 38 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:03:16 +0000] "GET /favicon.ico HTTP/1.1" 404 150 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:03:34 +0000] "GET /api/check-proxy HTTP/1.1" 200 185 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:05:14 +0000] "POST /auth/wake HTTP/1.1" 200 332 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +[2026-03-15T09:06:46.022Z] Static frontend server listening on port 3000; access log: /Users/dannier/Desktop/living/AICLW/wechatAiclaw/backend/data/logs/node-access.log +::1 - - [15/Mar/2026:09:06:53 +0000] "GET /api/check-proxy HTTP/1.1" 200 135 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +[2026-03-15T09:08:56.236Z] Static frontend server listening on port 3000; access log: /Users/dannier/Desktop/living/AICLW/wechatAiclaw/backend/data/logs/node-access.log +::1 - - [15/Mar/2026:09:09:01 +0000] "GET /api/check-proxy HTTP/1.1" 200 145 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:09 +0000] "POST /auth/wake HTTP/1.1" 200 231 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 803 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /manage.html?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/customers?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/greeting-tasks?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:29 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:31 +0000] "GET /api/contact-list?key=HBpEnbtj9BJZ&refresh=1 HTTP/1.1" 200 12 "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:32 +0000] "GET /api/contact-list?key=HBpEnbtj9BJZ&refresh=1 HTTP/1.1" 200 12 "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:34 +0000] "GET /api/contact-list?key=HBpEnbtj9BJZ&refresh=1 HTTP/1.1" 200 12 "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:44 +0000] "POST /auth/qrcode HTTP/1.1" 200 880 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:46 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:49 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:54 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 519 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:59 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:09:59 +0000] "GET /auth/slider-assets/N_jYM_2V.js HTTP/1.1" 200 64695 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:05 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:09 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:15 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:20 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:25 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:30 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:35 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1320 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:48 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:50 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:10:55 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:00 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:05 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:10 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:15 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 1266 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:20 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 519 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:38 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:39 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:44 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:49 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:11:55 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:02 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:05 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:19 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:21 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:24 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:29 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 549 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:32 +0000] "POST /auth/qrcode HTTP/1.1" 200 880 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:34 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:37 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 519 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:42 +0000] "POST /auth/wake HTTP/1.1" 200 159 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:44 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 89 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:47 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 551 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:52 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:12:57 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:02 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:07 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:12 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:17 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:22 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:27 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:32 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:37 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:42 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:47 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:52 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:13:57 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:02 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:07 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:12 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:17 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:22 +0000] "POST /auth/wake HTTP/1.1" 200 231 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:24 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 89 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:27 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 566 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:28 +0000] "POST /auth/qrcode HTTP/1.1" 200 880 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:31 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:34 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:37 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:37 +0000] "GET /health HTTP/1.1" 200 38 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:47 +0000] "POST /auth/qrcode HTTP/1.1" 200 880 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:49 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:52 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 274 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:14:57 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 519 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:02 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 519 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /auth/scan-status?key=HBpEnbtj9BJZ HTTP/1.1" 200 142 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /manage.html?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/customers?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/greeting-tasks?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:07 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:13 +0000] "GET /api/customer-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:14 +0000] "GET /api/ai-reply-config?key=HBpEnbtj9BJZ HTTP/1.1" 200 125 "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:14 +0000] "GET /api/callback-status?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:15 +0000] "GET /api/ws-status HTTP/1.1" 200 19 "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /manage.html?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/customers?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/greeting-tasks?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:18 +0000] "GET /manage.html?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/customers?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/greeting-tasks?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /manage.html?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/customers?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/greeting-tasks?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/product-tags?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:19 +0000] "GET /api/push-groups?key=HBpEnbtj9BJZ HTTP/1.1" 304 - "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" +::1 - - [15/Mar/2026:09:15:28 +0000] "GET /api/ws-status HTTP/1.1" 200 19 "http://localhost:3000/manage.html?key=HBpEnbtj9BJZ" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" diff --git a/backend/main.py b/backend/main.py index ce0a91f..718b551 100644 --- a/backend/main.py +++ b/backend/main.py @@ -335,9 +335,16 @@ async def _run_greeting_scheduler() -> None: async def lifespan(app: FastAPI): set_message_callback(_on_ws_message) _callback_key = (os.getenv("WECHAT_WS_KEY") or os.getenv("KEY") or os.getenv("WS_KEY") or "").strip() - if CALLBACK_BASE_URL and _callback_key: - ok = await _register_message_callback(_callback_key) - if ok: + if CALLBACK_BASE_URL: + keys_to_register = set(store.list_all_keys()) + if _callback_key: + keys_to_register.add(_callback_key) + for k in keys_to_register: + if k and k.strip(): + ok = await _register_message_callback(k.strip()) + if ok: + logger.info("启动时已注册回调 key=***%s", k[-4:] if len(k) >= 4 else "****") + if keys_to_register: logger.info("消息接收已切换为实时回调入口,不再启动 WS GetSyncMsg") else: asyncio.create_task(start_ws_sync()) @@ -448,6 +455,50 @@ async def api_check_proxy(proxy: Optional[str] = Query(None, description="可选 preview = _preview(proxy_url) logger.info("check-proxy: source=%s, proxy_preview=%s", source, preview) + is_socks = proxy_url.strip().lower().startswith("socks5") + if is_socks: + try: + from httpx_socks import AsyncProxyTransport + # httpx_socks/urllib 仅识别 socks5,将 socks5h 转为 socks5 + transport_url = proxy_url.strip() + if transport_url.lower().startswith("socks5h://"): + transport_url = "socks5://" + transport_url[10:] + transport = AsyncProxyTransport.from_url(transport_url) + async with httpx.AsyncClient(trust_env=False, timeout=15.0, transport=transport) as client: + resp = await client.get(PROXY_CHECK_URL) + if resp.status_code == 200: + logger.info("check-proxy: ok (socks), status=%s", resp.status_code) + return { + "ok": True, + "source": source, + "proxy_preview": preview, + "check_url": PROXY_CHECK_URL, + "status_code": resp.status_code, + } + logger.warning("check-proxy: fail (socks), status=%s", resp.status_code) + return { + "ok": False, + "source": source, + "proxy_preview": preview, + "error": f"请求测试页返回 {resp.status_code}", + "status_code": resp.status_code, + } + except ImportError: + logger.info("check-proxy: socks5 已配置,跳过连通性检测(需 pip install httpx-socks 方可检测)") + return { + "ok": True, + "source": source, + "proxy_preview": preview, + "note": "socks5 代理已配置;连通性检测需安装 pip install httpx-socks", + } + except Exception as e: + logger.warning("check-proxy: socks exception %s", e) + return { + "ok": False, + "source": source, + "proxy_preview": preview, + "error": str(e), + } try: async with httpx.AsyncClient(trust_env=False, timeout=15.0, proxy=proxy_url) as client: resp = await client.get(PROXY_CHECK_URL) diff --git a/backend/requirements.txt b/backend/requirements.txt index e28ac04..ae897ca 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -2,6 +2,7 @@ fastapi==0.115.0 uvicorn[standard]==0.30.0 python-dotenv>=1.0.0 httpx==0.27.0 +httpx-socks>=0.9.0 websockets>=12.0 openai>=1.0.0 PySocks>=1.7.0 diff --git a/backend/store.py b/backend/store.py index 11d85e6..39aa8da 100644 --- a/backend/store.py +++ b/backend/store.py @@ -361,6 +361,19 @@ def update_push_task_status(task_id: str, status: str) -> Optional[Dict]: # ---------- 同步消息 ---------- +def list_all_keys() -> List[str]: + """返回所有出现过的 key(sync_messages + ai_reply_config 去重),用于启动时批量注册回调等。""" + with _LOCK: + conn = _conn() + try: + cur = conn.execute( + "SELECT DISTINCT key FROM (SELECT key FROM sync_messages UNION SELECT key FROM ai_reply_config)" + ) + return [r[0] for r in cur.fetchall() if r[0]] + finally: + conn.close() + + def append_sync_messages(key: str, messages: List[Dict], max_per_key: int = 500) -> None: with _LOCK: conn = _conn() diff --git a/read.md b/read.md index e69de29..e9317a3 100644 --- a/read.md +++ b/read.md @@ -0,0 +1 @@ +请根据这swagger构建一个微信后台管理服务,主要是实现微信登录功能,并且放在docker环境下部署执行, diff --git a/run.sh b/run.sh deleted file mode 100644 index 7805567..0000000 --- a/run.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -# 统一启动:可选代理桥接 → ngrok 暴露回调 → 后端+前端 -set -e -cd "$(dirname "$0")" - -# 用法 -usage() { - echo "用法: ./run.sh [--proxy-bridge]" - echo " --proxy-bridge 先启动本地代理桥接(8899→127.0.0.1:7890),便于 7006 通过 ngrok 使用本机代理" - echo "无参数时仅: ngrok 暴露 8000 并写入 CALLBACK_BASE_URL → 启动 run-dev.sh" - echo " .env 中已有 CALLBACK_BASE_URL 时不会覆盖,重启无需重设;换 ngrok 地址后执行: ./run-ngrok.sh --update" - echo "" - echo "注意: ngrok 免费版仅 1 个隧道,已用于 8000(回调);代理需另开隧道或 cloudflared 暴露 8899 后填 .env。" -} - -USE_PROXY_BRIDGE=0 -for arg in "$@"; do - case "$arg" in - -h|--help) usage; exit 0 ;; - --proxy-bridge) USE_PROXY_BRIDGE=1 ;; - esac -done - -# 可选:启动本地代理桥接(8899 → 127.0.0.1:7890) -if [ "$USE_PROXY_BRIDGE" = "1" ]; then - if [ ! -f "scripts/local_proxy_bridge.py" ]; then - echo "未找到 scripts/local_proxy_bridge.py,跳过代理桥接" - else - PYTHON="python3" - if [ -d ".venv" ]; then - PYTHON=".venv/bin/python" - fi - if ! lsof -i :8899 >/dev/null 2>&1; then - echo "启动本地代理桥接 :8899 → 127.0.0.1:7890 ..." - nohup "$PYTHON" scripts/local_proxy_bridge.py >> /tmp/proxy-bridge.log 2>&1 & - echo $! > /tmp/proxy-bridge.pid - sleep 1 - echo " 已启动。若需 7006 走本机代理,需另用 ngrok 付费多隧道或 cloudflared 暴露 8899,将 URL 填入 .env 的 HTTP_PROXY。" - else - echo "端口 8899 已被占用,跳过代理桥接(可能已在运行)" - fi - fi -fi - -# 退出时清理本脚本启动的代理桥接 -cleanup_proxy_bridge() { - if [ -f /tmp/proxy-bridge.pid ]; then - PID=$(cat /tmp/proxy-bridge.pid 2>/dev/null) - if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then - kill "$PID" 2>/dev/null || true - echo "已停止代理桥接 (PID $PID)" - fi - rm -f /tmp/proxy-bridge.pid - fi -} -trap cleanup_proxy_bridge EXIT - -# 1) ngrok 暴露 8000,写入 .env 的 CALLBACK_BASE_URL -echo ">>> 配置 ngrok 回调地址..." -sh ./run-ngrok.sh - -# 2) 启动后端 + 前端(run-dev.sh) -echo ">>> 启动后端与前端..." -sh ./run-dev.sh