mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-22 18:27:12 +00:00
feat(itchat): improve login session handling and error reporting
This commit is contained in:
@@ -691,7 +691,6 @@ class AdaptersRouterGroup(group.RouterGroup):
|
|||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
status_dir = os.path.join('data', 'itchat')
|
status_dir = os.path.join('data', 'itchat')
|
||||||
os.makedirs(status_dir, exist_ok=True)
|
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')
|
qr_path = os.path.join(status_dir, f'{session_id}-QR.png')
|
||||||
|
|
||||||
session = {
|
session = {
|
||||||
@@ -733,19 +732,22 @@ class AdaptersRouterGroup(group.RouterGroup):
|
|||||||
wxid = ''
|
wxid = ''
|
||||||
session['nickname'] = nick
|
session['nickname'] = nick
|
||||||
session['wxid'] = wxid
|
session['wxid'] = wxid
|
||||||
session['status'] = 'success'
|
|
||||||
session['logged_in'].set()
|
|
||||||
print(f'[itchat-login] Login success: {nick}', flush=True)
|
print(f'[itchat-login] Login success: {nick}', flush=True)
|
||||||
# Dump login status so the adapter can hot-reload it
|
# Dump login status so the adapter can hot-reload it
|
||||||
try:
|
try:
|
||||||
|
if not wxid:
|
||||||
|
raise ValueError('Unable to detect WeChat wxid after login')
|
||||||
account_status_path = ItchatAdapter.login_status_path_for_account(wxid)
|
account_status_path = ItchatAdapter.login_status_path_for_account(wxid)
|
||||||
_core.dump_login_status(account_status_path)
|
_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['login_status_path'] = account_status_path
|
||||||
|
session['status'] = 'success'
|
||||||
print(f'[itchat-login] Session saved to {account_status_path}', flush=True)
|
print(f'[itchat-login] Session saved to {account_status_path}', flush=True)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
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
|
# Stop the message loop - we only needed the session for QR login
|
||||||
_core.alive = False
|
_core.alive = False
|
||||||
|
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
def _safe_status_name(value: str) -> str:
|
def _safe_status_name(value: str) -> str:
|
||||||
cleaned = re.sub(r'[^A-Za-z0-9_.@-]+', '_', value.strip())
|
cleaned = re.sub(r'[^A-Za-z0-9_.@-]+', '_', value.strip())
|
||||||
cleaned = cleaned.strip('._')
|
cleaned = cleaned.strip('._')
|
||||||
return cleaned or 'itchat'
|
return cleaned
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def login_status_dir() -> str:
|
def login_status_dir() -> str:
|
||||||
@@ -340,8 +340,11 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def login_status_path_for_account(cls, account_id: str = '') -> str:
|
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'
|
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)
|
return os.path.join(cls.login_status_dir(), filename)
|
||||||
|
|
||||||
def _login_status_path(self) -> str:
|
def _login_status_path(self) -> str:
|
||||||
@@ -350,12 +353,10 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
return configured_path
|
return configured_path
|
||||||
|
|
||||||
account_id = self.config.get('account_id', '').strip()
|
account_id = self.config.get('account_id', '').strip()
|
||||||
if account_id:
|
if not account_id:
|
||||||
account_path = self.login_status_path_for_account(account_id)
|
raise ValueError('account_id is required. Please scan the QR code and save this bot first.')
|
||||||
if os.path.exists(account_path):
|
|
||||||
return account_path
|
|
||||||
|
|
||||||
return self.login_status_path_for_account()
|
return self.login_status_path_for_account(account_id)
|
||||||
|
|
||||||
def set_bot_uuid(self, bot_uuid: str):
|
def set_bot_uuid(self, bot_uuid: str):
|
||||||
self._bot_uuid = bot_uuid
|
self._bot_uuid = bot_uuid
|
||||||
@@ -683,8 +684,8 @@ class ItchatAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
async def run_async(self):
|
async def run_async(self):
|
||||||
"""Start the itchat adapter.
|
"""Start the itchat adapter.
|
||||||
|
|
||||||
If a cached session file (itchat.pkl) exists from a previous QR login,
|
If an account-specific cached session file exists from a previous QR login,
|
||||||
itchat will reuse it without requiring a new QR scan.
|
itchat will reuse data/itchat/<account_id>.pkl without requiring a new QR scan.
|
||||||
"""
|
"""
|
||||||
self._loop = asyncio.get_running_loop()
|
self._loop = asyncio.get_running_loop()
|
||||||
self._logged_in.clear()
|
self._logged_in.clear()
|
||||||
|
|||||||
@@ -36,12 +36,14 @@ spec:
|
|||||||
en_US: Bot Account ID
|
en_US: Bot Account ID
|
||||||
zh_Hans: 机器人账号标识
|
zh_Hans: 机器人账号标识
|
||||||
zh_Hant: 機器人帳號標識
|
zh_Hant: 機器人帳號標識
|
||||||
|
ja_JP: ボットアカウントID
|
||||||
description:
|
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.
|
en_US: Auto-filled after QR login with the WeChat wxid. Used for @-mention matching and to load data/itchat/<account_id>.pkl; do not change it to a nickname.
|
||||||
zh_Hans: 用于群聊 @ 检测匹配。扫码登录后自动填入(微信ID),可手动改为机器人昵称
|
zh_Hans: 扫码登录后自动填入微信 wxid。用于群聊 @ 匹配,并加载 data/itchat/<account_id>.pkl;不要改成昵称。
|
||||||
zh_Hant: 用於群聊 @ 檢測匹配。掃碼登入後自動填入(微信ID),可手動改為機器人暱稱
|
zh_Hant: 掃碼登入後自動填入微信 wxid。用於群聊 @ 匹配,並載入 data/itchat/<account_id>.pkl;不要改成暱稱。
|
||||||
|
ja_JP: QRログイン後にWeChatのwxidが自動入力されます。@メンション判定と data/itchat/<account_id>.pkl の読み込みに使うため、ニックネームへ変更しないでください。
|
||||||
type: string
|
type: string
|
||||||
required: false
|
required: true
|
||||||
default: ""
|
default: ""
|
||||||
- name: nickname
|
- name: nickname
|
||||||
label:
|
label:
|
||||||
|
|||||||
@@ -624,7 +624,10 @@ export default function DynamicFormComponent({
|
|||||||
onSuccess={(credentials) => {
|
onSuccess={(credentials) => {
|
||||||
for (const [key, value] of Object.entries(credentials)) {
|
for (const [key, value] of Object.entries(credentials)) {
|
||||||
if (value) {
|
if (value) {
|
||||||
form.setValue(key as keyof FormValues, value as never);
|
form.setValue(key as keyof FormValues, value as never, {
|
||||||
|
shouldDirty: true,
|
||||||
|
shouldValidate: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user