mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-06-02 00:57:41 +08:00
feat: optimize weixin channel qr code generate
This commit is contained in:
@@ -160,6 +160,30 @@ class WeixinChannel(ChatChannel):
|
||||
print(f"\n 二维码链接: {qrcode_url}")
|
||||
print(" (安装 'qrcode' 包可在终端显示二维码)\n")
|
||||
|
||||
def _notify_cloud_qrcode(self, qrcode_url: str):
|
||||
"""Send QR code URL to cloud console when running in cloud mode."""
|
||||
if not self.cloud_mode:
|
||||
return
|
||||
try:
|
||||
from common import cloud_client
|
||||
client = getattr(cloud_client, "chat_client", None)
|
||||
if client and getattr(client, "client_id", None):
|
||||
client.send_channel_qrcode("weixin", qrcode_url)
|
||||
except Exception as e:
|
||||
logger.warning(f"[Weixin] Failed to notify cloud QR code: {e}")
|
||||
|
||||
def _notify_cloud_connected(self):
|
||||
"""Send connected status to cloud console when login succeeds."""
|
||||
if not self.cloud_mode:
|
||||
return
|
||||
try:
|
||||
from common import cloud_client
|
||||
client = getattr(cloud_client, "chat_client", None)
|
||||
if client and getattr(client, "client_id", None):
|
||||
client.send_channel_status("weixin", "connected")
|
||||
except Exception as e:
|
||||
logger.warning(f"[Weixin] Failed to notify cloud connected: {e}")
|
||||
|
||||
def _qr_login(self, base_url: str) -> dict:
|
||||
"""Perform interactive QR code login. Returns dict with token/base_url or empty dict."""
|
||||
api = WeixinApi(base_url=base_url)
|
||||
@@ -179,11 +203,12 @@ class WeixinChannel(ChatChannel):
|
||||
self._current_qr_url = qrcode_url
|
||||
logger.info(f"[Weixin] QR code URL: {qrcode_url}")
|
||||
self._print_qr(qrcode_url)
|
||||
self._notify_cloud_qrcode(qrcode_url)
|
||||
print(" 等待扫码...\n")
|
||||
|
||||
scanned_printed = False
|
||||
|
||||
while True:
|
||||
while not self._stop_event.is_set():
|
||||
try:
|
||||
status_resp = api.poll_qr_status(qrcode)
|
||||
except Exception as e:
|
||||
@@ -209,6 +234,7 @@ class WeixinChannel(ChatChannel):
|
||||
self._current_qr_url = qrcode_url
|
||||
logger.info(f"[Weixin] New QR code: {qrcode_url}")
|
||||
self._print_qr(qrcode_url)
|
||||
self._notify_cloud_qrcode(qrcode_url)
|
||||
except Exception as e:
|
||||
logger.error(f"[Weixin] QR refresh failed: {e}")
|
||||
return {}
|
||||
@@ -225,6 +251,7 @@ class WeixinChannel(ChatChannel):
|
||||
self._current_qr_url = ""
|
||||
print(f"\n ✅ 微信登录成功!bot_id={bot_id}")
|
||||
logger.info(f"[Weixin] Login confirmed: bot_id={bot_id}")
|
||||
self._notify_cloud_connected()
|
||||
|
||||
creds = {
|
||||
"token": bot_token,
|
||||
@@ -237,9 +264,10 @@ class WeixinChannel(ChatChannel):
|
||||
|
||||
return {"token": bot_token, "base_url": result_base_url}
|
||||
|
||||
time.sleep(1)
|
||||
self._stop_event.wait(1)
|
||||
|
||||
logger.warning("[Weixin] QR login timed out")
|
||||
logger.info("[Weixin] QR login cancelled by stop event")
|
||||
self._current_qr_url = ""
|
||||
return {}
|
||||
|
||||
# ── Long-poll loop ─────────────────────────────────────────────────
|
||||
|
||||
@@ -260,11 +260,27 @@ class CloudClient(LinkAIClient):
|
||||
self._remove_channel_type(local_config, channel_type)
|
||||
self._save_config_to_file(local_config)
|
||||
|
||||
if channel_type in ("weixin", "wx"):
|
||||
self._remove_weixin_credentials()
|
||||
|
||||
if self.channel_mgr:
|
||||
threading.Thread(
|
||||
target=self._do_remove_channel, args=(channel_type,), daemon=True
|
||||
).start()
|
||||
|
||||
@staticmethod
|
||||
def _remove_weixin_credentials():
|
||||
"""Remove the weixin token credentials file so next connect triggers QR login."""
|
||||
cred_path = os.path.expanduser(
|
||||
conf().get("weixin_credentials_path", "~/.weixin_cow_credentials.json")
|
||||
)
|
||||
try:
|
||||
if os.path.exists(cred_path):
|
||||
os.remove(cred_path)
|
||||
logger.info(f"[CloudClient] Removed weixin credentials: {cred_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[CloudClient] Failed to remove weixin credentials: {e}")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# channel credentials helpers
|
||||
# ------------------------------------------------------------------
|
||||
@@ -351,12 +367,31 @@ class CloudClient(LinkAIClient):
|
||||
except Exception as e:
|
||||
logger.error(f"[CloudClient] Failed to remove channel '{channel_type}': {e}")
|
||||
|
||||
def send_channel_qrcode(self, channel_type: str, qrcode_url: str):
|
||||
"""Report QR code URL for a channel that requires scan-to-login."""
|
||||
if self.client_id:
|
||||
from linkai.api.client.client import ClientMsgType
|
||||
msg = self._build_package(ClientMsgType.CHANNEL_STATUS)
|
||||
msg["data"]["channelType"] = channel_type
|
||||
msg["data"]["status"] = "qrcode"
|
||||
msg["data"]["qrcodeUrl"] = qrcode_url
|
||||
self._send_package(msg)
|
||||
logger.info(f"[CloudClient] Sent QR code status for '{channel_type}'")
|
||||
|
||||
def _report_channel_startup(self, channel_type: str):
|
||||
"""Wait for channel startup result and report to cloud."""
|
||||
ch = self.channel_mgr.get_channel(channel_type)
|
||||
if not ch:
|
||||
self.send_channel_status(channel_type, "error", "channel instance not found")
|
||||
return
|
||||
|
||||
if channel_type in ("weixin", "wx") and hasattr(ch, "login_status"):
|
||||
login_status = getattr(ch, "login_status", "")
|
||||
if login_status in ("waiting_scan", "scanned", "idle"):
|
||||
logger.info(f"[CloudClient] Channel '{channel_type}' is waiting for QR login, "
|
||||
"skip reporting connected")
|
||||
return
|
||||
|
||||
success, error = ch.wait_startup(timeout=3)
|
||||
if success:
|
||||
logger.info(f"[CloudClient] Channel '{channel_type}' connected, reporting status")
|
||||
|
||||
@@ -42,7 +42,7 @@ description: 将 CowAgent 接入个人微信
|
||||
首次启动时,终端会显示一个二维码(有效期约 2 分钟)。使用微信扫描二维码并在手机上确认后即可完成登录。
|
||||
|
||||
- 二维码过期后会自动刷新并重新显示
|
||||
- 安装 `qrcode` Python 包可在终端直接渲染二维码图案(`pip3 install qrcode`)
|
||||
- `requirements.txt` 中已默认包含 `qrcode` 依赖,安装后可在终端直接渲染二维码图案
|
||||
|
||||
### 凭证保存
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ Login credentials are automatically saved to `~/.weixin_cow_credentials.json`. T
|
||||
On first startup, a QR code is displayed in the terminal (valid for approximately 2 minutes). Scan it with WeChat and confirm on your phone.
|
||||
|
||||
- The QR code automatically refreshes when it expires
|
||||
- Install the `qrcode` Python package to render the QR code directly in the terminal: `pip3 install qrcode`
|
||||
- The `qrcode` dependency is already included in `requirements.txt`, enabling QR code rendering directly in the terminal
|
||||
|
||||
### Credential Persistence
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ description: CowAgent を個人の WeChat に接続する
|
||||
初回起動時に、ターミナルに QR コードが表示されます(有効期限は約 2 分)。WeChat でスキャンし、スマートフォンで確認してください。
|
||||
|
||||
- QR コードが期限切れになると自動的に更新・再表示されます
|
||||
- `qrcode` Python パッケージをインストールすると、ターミナルに直接 QR コードを表示できます:`pip3 install qrcode`
|
||||
- `qrcode` 依存関係は `requirements.txt` にデフォルトで含まれており、ターミナルに直接 QR コードを表示できます
|
||||
|
||||
### 認証情報の永続化
|
||||
|
||||
|
||||
Reference in New Issue
Block a user