mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-15 06:50:58 +00:00
feat: add itchat-uos WeChat adapter with QR code login
- Add itchat-uos adapter supporting personal WeChat via QR code login - Implement message/event converters for text, image, voice, sharing types - Bridge sync itchat callbacks to async LangBot pipeline via asyncio - Add QR login API endpoints with session management - Add frontend QR code login dialog integration - Fix plugin connector handler attribute check - Use fresh Core instance per login to avoid singleton state pollution
This commit is contained in:
@@ -16,7 +16,12 @@ import {
|
||||
} from 'lucide-react';
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
export type QrLoginPlatform = 'feishu' | 'weixin' | 'dingtalk' | 'wecombot';
|
||||
export type QrLoginPlatform =
|
||||
| 'feishu'
|
||||
| 'weixin'
|
||||
| 'dingtalk'
|
||||
| 'wecombot'
|
||||
| 'itchat';
|
||||
|
||||
interface PlatformConfig {
|
||||
titleKey: string;
|
||||
@@ -92,6 +97,20 @@ const PLATFORM_CONFIGS: Record<QrLoginPlatform, PlatformConfig> = {
|
||||
}),
|
||||
successNoteKey: 'wecombot.robotNameNote',
|
||||
},
|
||||
itchat: {
|
||||
titleKey: 'itchat.scanLogin',
|
||||
connectingKey: 'itchat.connecting',
|
||||
scanQRCodeKey: 'itchat.scanQRCode',
|
||||
waitingKey: 'itchat.waitingForScan',
|
||||
successKey: 'itchat.loginSuccess',
|
||||
failedKey: 'itchat.loginFailed',
|
||||
retryKey: 'itchat.retry',
|
||||
apiBase: '/api/v1/platform/adapters/itchat/login',
|
||||
extractSuccess: (data) => ({
|
||||
account_id: data.wxid || '',
|
||||
nickname: data.nickname || '',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
interface QrCodeLoginDialogProps {
|
||||
@@ -116,6 +135,7 @@ export default function QrCodeLoginDialog({
|
||||
|
||||
const [state, setState] = useState<DialogState>('connecting');
|
||||
const [qrDataUrl, setQrDataUrl] = useState('');
|
||||
const qrDataUrlRef = useRef('');
|
||||
const [expireIn, setExpireIn] = useState(0);
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const pollTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
@@ -176,6 +196,7 @@ export default function QrCodeLoginDialog({
|
||||
cleanedRef.current = false;
|
||||
setState('connecting');
|
||||
setQrDataUrl('');
|
||||
qrDataUrlRef.current = '';
|
||||
setExpireIn(0);
|
||||
setErrorMessage('');
|
||||
|
||||
@@ -204,12 +225,14 @@ export default function QrCodeLoginDialog({
|
||||
|
||||
if (qr_data_url) {
|
||||
setQrDataUrl(qr_data_url);
|
||||
qrDataUrlRef.current = qr_data_url;
|
||||
} else if (qr_url) {
|
||||
const dataUrl = await QRCode.toDataURL(qr_url, {
|
||||
width: 224,
|
||||
margin: 2,
|
||||
});
|
||||
setQrDataUrl(dataUrl);
|
||||
qrDataUrlRef.current = dataUrl;
|
||||
}
|
||||
setState('waiting');
|
||||
|
||||
@@ -289,6 +312,19 @@ export default function QrCodeLoginDialog({
|
||||
cleanup();
|
||||
setExpireIn(0);
|
||||
setState('expired');
|
||||
} else if (status === 'waiting') {
|
||||
// Update QR data URL if regenerated (e.g. itchat QR expiry)
|
||||
if (rest.qr_data_url && rest.qr_data_url !== qrDataUrlRef.current) {
|
||||
setQrDataUrl(rest.qr_data_url);
|
||||
qrDataUrlRef.current = rest.qr_data_url;
|
||||
}
|
||||
if (rest.expire_at) {
|
||||
const remaining = Math.max(
|
||||
0,
|
||||
Math.floor(rest.expire_at - Date.now() / 1000),
|
||||
);
|
||||
setExpireIn(remaining);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore poll errors
|
||||
|
||||
Reference in New Issue
Block a user