feat(channel): support weichat channel (#620)

This commit is contained in:
Haze
2026-03-22 17:08:02 +08:00
committed by GitHub
Unverified
parent f12f4a74df
commit 1e7b40a486
32 changed files with 1610 additions and 156 deletions

View File

@@ -11,6 +11,7 @@ import {
} from '@/lib/channel-status';
import { useGatewayStore } from './gateway';
import { CHANNEL_NAMES, type Channel, type ChannelType } from '../types/channel';
import { toOpenClawChannelType, toUiChannelType } from '@/lib/channel-alias';
interface AddChannelParams {
type: ChannelType;
@@ -40,6 +41,17 @@ interface ChannelsState {
const reconnectTimers = new Map<string, NodeJS.Timeout>();
const reconnectAttempts = new Map<string, number>();
function splitChannelId(channelId: string): { channelType: string; accountId?: string } {
const separatorIndex = channelId.indexOf('-');
if (separatorIndex === -1) {
return { channelType: channelId };
}
return {
channelType: channelId.slice(0, separatorIndex),
accountId: channelId.slice(separatorIndex + 1),
};
}
export const useChannelsStore = create<ChannelsState>((set, get) => ({
channels: [],
loading: false,
@@ -75,6 +87,8 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
// Parse the complex channels.status response into simple Channel objects
const channelOrder = data.channelOrder || Object.keys(data.channels || {});
for (const channelId of channelOrder) {
const uiChannelId = toUiChannelType(channelId) as ChannelType;
const gatewayChannelId = toOpenClawChannelType(channelId);
const summary = (data.channels as Record<string, unknown> | undefined)?.[channelId] as Record<string, unknown> | undefined;
const configured =
typeof summary?.configured === 'boolean'
@@ -101,14 +115,17 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
: undefined;
channels.push({
id: `${channelId}-${primaryAccount?.accountId || 'default'}`,
type: channelId as ChannelType,
name: primaryAccount?.name || CHANNEL_NAMES[channelId as ChannelType] || channelId,
id: `${uiChannelId}-${primaryAccount?.accountId || 'default'}`,
type: uiChannelId,
name: primaryAccount?.name || CHANNEL_NAMES[uiChannelId] || uiChannelId,
status,
accountId: primaryAccount?.accountId,
error:
(typeof primaryAccount?.lastError === 'string' ? primaryAccount.lastError : undefined) ||
(typeof summaryError === 'string' ? summaryError : undefined),
metadata: {
gatewayChannelId,
},
});
}
@@ -162,7 +179,8 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
deleteChannel: async (channelId) => {
// Extract channel type from the channelId (format: "channelType-accountId")
const channelType = channelId.split('-')[0];
const { channelType } = splitChannelId(channelId);
const gatewayChannelType = toOpenClawChannelType(channelType);
try {
// Delete the channel configuration from openclaw.json
@@ -174,7 +192,7 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
}
try {
await useGatewayStore.getState().rpc('channels.delete', { channelId: channelType });
await useGatewayStore.getState().rpc('channels.delete', { channelId: gatewayChannelType });
} catch (error) {
// Continue with local deletion even if gateway fails
console.error('Failed to delete channel from gateway:', error);
@@ -191,7 +209,10 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
updateChannel(channelId, { status: 'connecting', error: undefined });
try {
await useGatewayStore.getState().rpc('channels.connect', { channelId });
const { channelType, accountId } = splitChannelId(channelId);
await useGatewayStore.getState().rpc('channels.connect', {
channelId: `${toOpenClawChannelType(channelType)}${accountId ? `-${accountId}` : ''}`,
});
updateChannel(channelId, { status: 'connected' });
} catch (error) {
updateChannel(channelId, { status: 'error', error: String(error) });
@@ -203,7 +224,10 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
clearAutoReconnect(channelId);
try {
await useGatewayStore.getState().rpc('channels.disconnect', { channelId });
const { channelType, accountId } = splitChannelId(channelId);
await useGatewayStore.getState().rpc('channels.disconnect', {
channelId: `${toOpenClawChannelType(channelType)}${accountId ? `-${accountId}` : ''}`,
});
} catch (error) {
console.error('Failed to disconnect channel:', error);
}
@@ -214,7 +238,7 @@ export const useChannelsStore = create<ChannelsState>((set, get) => ({
requestQrCode: async (channelType) => {
return await useGatewayStore.getState().rpc<{ qrCode: string; sessionId: string }>(
'channels.requestQr',
{ type: channelType },
{ type: toOpenClawChannelType(channelType) },
);
},