From 0cce41895677b947c6948c80c81250d46da21922 Mon Sep 17 00:00:00 2001 From: fdc310 <2213070223@qq.com> Date: Thu, 2 Jul 2026 14:40:13 +0800 Subject: [PATCH] feat(itchat): improve login session handling and error reporting --- .../controller/groups/platform/adapters.py | 16 +++++++------- src/langbot/pkg/platform/sources/itchat.py | 21 ++++++++++--------- src/langbot/pkg/platform/sources/itchat.yaml | 10 +++++---- .../dynamic-form/DynamicFormComponent.tsx | 5 ++++- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/langbot/pkg/api/http/controller/groups/platform/adapters.py b/src/langbot/pkg/api/http/controller/groups/platform/adapters.py index 97eddfa56..7cdb50934 100644 --- a/src/langbot/pkg/api/http/controller/groups/platform/adapters.py +++ b/src/langbot/pkg/api/http/controller/groups/platform/adapters.py @@ -691,7 +691,6 @@ class AdaptersRouterGroup(group.RouterGroup): loop = asyncio.get_running_loop() status_dir = os.path.join('data', 'itchat') os.makedirs(status_dir, exist_ok=True) - default_status_path = os.path.join(status_dir, 'itchat.pkl') qr_path = os.path.join(status_dir, f'{session_id}-QR.png') session = { @@ -733,19 +732,22 @@ class AdaptersRouterGroup(group.RouterGroup): wxid = '' session['nickname'] = nick session['wxid'] = wxid - session['status'] = 'success' - session['logged_in'].set() print(f'[itchat-login] Login success: {nick}', flush=True) # Dump login status so the adapter can hot-reload it try: + if not wxid: + raise ValueError('Unable to detect WeChat wxid after login') account_status_path = ItchatAdapter.login_status_path_for_account(wxid) _core.dump_login_status(account_status_path) - if account_status_path != default_status_path: - _core.dump_login_status(default_status_path) session['login_status_path'] = account_status_path + session['status'] = 'success' print(f'[itchat-login] Session saved to {account_status_path}', flush=True) - except Exception: - pass + except Exception as e: + session['status'] = 'error' + session['error'] = str(e) + print(f'[itchat-login] Failed to save session: {e}', flush=True) + finally: + session['logged_in'].set() # Stop the message loop - we only needed the session for QR login _core.alive = False diff --git a/src/langbot/pkg/platform/sources/itchat.py b/src/langbot/pkg/platform/sources/itchat.py index 0a4aab95a..0edc7f011 100644 --- a/src/langbot/pkg/platform/sources/itchat.py +++ b/src/langbot/pkg/platform/sources/itchat.py @@ -331,7 +331,7 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): def _safe_status_name(value: str) -> str: cleaned = re.sub(r'[^A-Za-z0-9_.@-]+', '_', value.strip()) cleaned = cleaned.strip('._') - return cleaned or 'itchat' + return cleaned @staticmethod def login_status_dir() -> str: @@ -340,8 +340,11 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): return path @classmethod - def login_status_path_for_account(cls, account_id: str = '') -> str: - filename = f'{cls._safe_status_name(account_id)}.pkl' if account_id else 'itchat.pkl' + def login_status_path_for_account(cls, account_id: str) -> str: + safe_name = cls._safe_status_name(account_id) + if not safe_name: + raise ValueError('account_id is required for itchat login status') + filename = f'{safe_name}.pkl' return os.path.join(cls.login_status_dir(), filename) def _login_status_path(self) -> str: @@ -350,12 +353,10 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): return configured_path account_id = self.config.get('account_id', '').strip() - if account_id: - account_path = self.login_status_path_for_account(account_id) - if os.path.exists(account_path): - return account_path + if not account_id: + raise ValueError('account_id is required. Please scan the QR code and save this bot first.') - return self.login_status_path_for_account() + return self.login_status_path_for_account(account_id) def set_bot_uuid(self, bot_uuid: str): self._bot_uuid = bot_uuid @@ -683,8 +684,8 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): async def run_async(self): """Start the itchat adapter. - If a cached session file (itchat.pkl) exists from a previous QR login, - itchat will reuse it without requiring a new QR scan. + If an account-specific cached session file exists from a previous QR login, + itchat will reuse data/itchat/.pkl without requiring a new QR scan. """ self._loop = asyncio.get_running_loop() self._logged_in.clear() diff --git a/src/langbot/pkg/platform/sources/itchat.yaml b/src/langbot/pkg/platform/sources/itchat.yaml index fb3ed16cf..2d843c264 100644 --- a/src/langbot/pkg/platform/sources/itchat.yaml +++ b/src/langbot/pkg/platform/sources/itchat.yaml @@ -36,12 +36,14 @@ spec: en_US: Bot Account ID zh_Hans: 机器人账号标识 zh_Hant: 機器人帳號標識 + ja_JP: ボットアカウントID description: - en_US: Used for @-mention matching in groups. Auto-filled after QR login (wxid). You can change it to match your bot's display nickname if needed. - zh_Hans: 用于群聊 @ 检测匹配。扫码登录后自动填入(微信ID),可手动改为机器人昵称 - zh_Hant: 用於群聊 @ 檢測匹配。掃碼登入後自動填入(微信ID),可手動改為機器人暱稱 + en_US: Auto-filled after QR login with the WeChat wxid. Used for @-mention matching and to load data/itchat/.pkl; do not change it to a nickname. + zh_Hans: 扫码登录后自动填入微信 wxid。用于群聊 @ 匹配,并加载 data/itchat/.pkl;不要改成昵称。 + zh_Hant: 掃碼登入後自動填入微信 wxid。用於群聊 @ 匹配,並載入 data/itchat/.pkl;不要改成暱稱。 + ja_JP: QRログイン後にWeChatのwxidが自動入力されます。@メンション判定と data/itchat/.pkl の読み込みに使うため、ニックネームへ変更しないでください。 type: string - required: false + required: true default: "" - name: nickname label: diff --git a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx index da1860213..946dffd21 100644 --- a/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx +++ b/web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx @@ -624,7 +624,10 @@ export default function DynamicFormComponent({ onSuccess={(credentials) => { for (const [key, value] of Object.entries(credentials)) { if (value) { - form.setValue(key as keyof FormValues, value as never); + form.setValue(key as keyof FormValues, value as never, { + shouldDirty: true, + shouldValidate: true, + }); } } }}