fix: 修复代理问题
This commit is contained in:
3
.env
3
.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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
[](https://github.com/romis2012/httpx-socks/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/romis2012/httpx-socks)
|
||||
[](https://pypi.python.org/pypi/httpx-socks)
|
||||
<!--
|
||||
[](https://pepy.tech/project/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
|
||||
```
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: setuptools (75.3.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
httpx_socks
|
||||
23
.venv/lib/python3.9/site-packages/httpx_socks/__init__.py
Normal file
23
.venv/lib/python3.9/site-packages/httpx_socks/__init__.py
Normal file
@@ -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',
|
||||
)
|
||||
258
.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py
Normal file
258
.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py
Normal file
@@ -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()
|
||||
@@ -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)
|
||||
206
.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py
Normal file
206
.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py
Normal file
@@ -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()
|
||||
@@ -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
|
||||
105
.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py
Normal file
105
.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py
Normal file
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -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 <snegiryev@gmail.com>
|
||||
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
|
||||
|
||||
[](https://github.com/romis2012/python-socks/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/romis2012/python-socks)
|
||||
[](https://pypi.python.org/pypi/python-socks)
|
||||
[](https://github.com/romis2012/python-socks)
|
||||
<!--
|
||||
[](https://pepy.tech/project/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)
|
||||
```
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: setuptools (82.0.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
python_socks
|
||||
20
.venv/lib/python3.9/site-packages/python_socks/__init__.py
Normal file
20
.venv/lib/python3.9/site-packages/python_socks/__init__.py
Normal file
@@ -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',
|
||||
)
|
||||
40
.venv/lib/python3.9/site-packages/python_socks/_abc.py
Normal file
40
.venv/lib/python3.9/site-packages/python_socks/_abc.py
Normal file
@@ -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()
|
||||
@@ -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
|
||||
@@ -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}')
|
||||
@@ -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}')
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
16
.venv/lib/python3.9/site-packages/python_socks/_errors.py
Normal file
16
.venv/lib/python3.9/site-packages/python_socks/_errors.py
Normal file
@@ -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
|
||||
81
.venv/lib/python3.9/site-packages/python_socks/_helpers.py
Normal file
81
.venv/lib/python3.9/site-packages/python_socks/_helpers.py
Normal file
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
class ReplyError(Exception):
|
||||
def __init__(self, message, error_code=None):
|
||||
super().__init__(message)
|
||||
self.error_code = error_code
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
7
.venv/lib/python3.9/site-packages/python_socks/_types.py
Normal file
7
.venv/lib/python3.9/site-packages/python_socks/_types.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ProxyType(Enum):
|
||||
SOCKS4 = 1
|
||||
SOCKS5 = 2
|
||||
HTTP = 3
|
||||
@@ -0,0 +1,2 @@
|
||||
__title__ = 'python-socks'
|
||||
__version__ = '2.8.1'
|
||||
@@ -0,0 +1,3 @@
|
||||
from ._proxy_chain import ProxyChain
|
||||
|
||||
__all__ = ('ProxyChain',)
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
from ._proxy import AnyioProxy as Proxy
|
||||
from ._chain import ProxyChain
|
||||
|
||||
__all__ = ('Proxy', 'ProxyChain')
|
||||
@@ -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
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
from ._proxy import AnyioProxy as Proxy
|
||||
from ._chain import ProxyChain
|
||||
|
||||
__all__ = (
|
||||
'Proxy',
|
||||
'ProxyChain',
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
from ._proxy import AsyncioProxy as Proxy
|
||||
|
||||
|
||||
__all__ = ('Proxy',)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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()
|
||||
@@ -0,0 +1,4 @@
|
||||
from ._proxy import AsyncioProxy as Proxy
|
||||
from ._chain import ProxyChain
|
||||
|
||||
__all__ = ('Proxy', 'ProxyChain')
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
from ._proxy import CurioProxy as Proxy
|
||||
|
||||
|
||||
__all__ = ('Proxy',)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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()
|
||||
@@ -0,0 +1,3 @@
|
||||
from ._proxy import TrioProxy as Proxy
|
||||
|
||||
__all__ = ('Proxy',)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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()
|
||||
@@ -0,0 +1,7 @@
|
||||
from ._proxy import TrioProxy as Proxy
|
||||
from ._chain import ProxyChain
|
||||
|
||||
__all__ = (
|
||||
'Proxy',
|
||||
'ProxyChain',
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
from ._proxy import SyncProxy as Proxy
|
||||
from ._chain import ProxyChain
|
||||
|
||||
|
||||
__all__ = ('Proxy', 'ProxyChain')
|
||||
@@ -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
|
||||
@@ -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,
|
||||
)
|
||||
116
.venv/lib/python3.9/site-packages/python_socks/sync/_proxy.py
Normal file
116
.venv/lib/python3.9/site-packages/python_socks/sync/_proxy.py
Normal file
@@ -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)
|
||||
@@ -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]
|
||||
@@ -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()
|
||||
@@ -0,0 +1,7 @@
|
||||
from ._proxy import SyncProxy as Proxy
|
||||
from ._chain import ProxyChain
|
||||
|
||||
__all__ = (
|
||||
'Proxy',
|
||||
'ProxyChain',
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
121
.venv/lib/python3.9/site-packages/python_socks/sync/v2/_proxy.py
Normal file
121
.venv/lib/python3.9/site-packages/python_socks/sync/v2/_proxy.py
Normal file
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
64
run.sh
64
run.sh
@@ -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
|
||||
Reference in New Issue
Block a user