forked from datawhale/whale-town-end
feat(zulip): 添加全面的 Zulip 集成系统
* **新增 Zulip 模块**:包含完整的集成服务,涵盖客户端池(client pool)、会话管理及事件处理。 * **新增 WebSocket 网关**:用于处理 Zulip 的实时事件监听与双向通信。 * **新增安全服务**:支持 API 密钥加密存储及凭据的安全管理。 * **新增配置管理服务**:支持配置热加载(hot-reload),实现动态配置更新。 * **新增错误处理与监控服务**:提升系统的可靠性与可观测性。 * **新增消息过滤服务**:用于内容校验及速率限制(流控)。 * **新增流初始化与会话清理服务**:优化资源管理与回收。 * **完善测试覆盖**:包含单元测试及端到端(e2e)集成测试。 * **完善详细文档**:包括 API 参考手册、配置指南及集成概述。 * **新增地图配置系统**:实现游戏地点与 Zulip Stream(频道)及 Topic(话题)的逻辑映射。 * **新增环境变量配置**:涵盖 Zulip 服务器地址、身份验证及监控相关设置。 * **更新 App 模块**:注册并启用新的 Zulip 集成模块。 * **更新 Redis 接口**:以支持增强型的会话管理功能。 * **实现 WebSocket 协议支持**:确保与 Zulip 之间的实时双向通信。
This commit is contained in:
704
src/business/zulip/services/zulip-client.service.ts
Normal file
704
src/business/zulip/services/zulip-client.service.ts
Normal file
@@ -0,0 +1,704 @@
|
||||
/**
|
||||
* Zulip客户端核心服务
|
||||
*
|
||||
* 功能描述:
|
||||
* - 封装Zulip REST API调用
|
||||
* - 实现API Key验证和错误处理
|
||||
* - 提供消息发送、事件队列管理等核心功能
|
||||
*
|
||||
* 主要方法:
|
||||
* - initialize(): 初始化Zulip客户端并验证API Key
|
||||
* - sendMessage(): 发送消息到指定Stream/Topic
|
||||
* - registerQueue(): 注册事件队列
|
||||
* - deregisterQueue(): 注销事件队列
|
||||
* - getEvents(): 获取事件队列中的事件
|
||||
*
|
||||
* 使用场景:
|
||||
* - 用户登录时创建和验证Zulip客户端
|
||||
* - 消息发送和接收
|
||||
* - 事件队列管理
|
||||
*
|
||||
* @author 开发团队
|
||||
* @version 1.0.0
|
||||
* @since 2025-12-25
|
||||
*/
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ZulipAPI, Internal, Enums } from '../interfaces/zulip.interfaces';
|
||||
|
||||
/**
|
||||
* Zulip客户端配置接口
|
||||
*/
|
||||
export interface ZulipClientConfig {
|
||||
username: string;
|
||||
apiKey: string;
|
||||
realm: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zulip客户端实例接口
|
||||
*/
|
||||
export interface ZulipClientInstance {
|
||||
userId: string;
|
||||
config: ZulipClientConfig;
|
||||
client: any; // zulip-js客户端实例
|
||||
queueId?: string;
|
||||
lastEventId: number;
|
||||
createdAt: Date;
|
||||
lastActivity: Date;
|
||||
isValid: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息结果接口
|
||||
*/
|
||||
export interface SendMessageResult {
|
||||
success: boolean;
|
||||
messageId?: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件队列注册结果接口
|
||||
*/
|
||||
export interface RegisterQueueResult {
|
||||
success: boolean;
|
||||
queueId?: string;
|
||||
lastEventId?: number;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件结果接口
|
||||
*/
|
||||
export interface GetEventsResult {
|
||||
success: boolean;
|
||||
events?: ZulipAPI.Event[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ZulipClientService {
|
||||
private readonly logger = new Logger(ZulipClientService.name);
|
||||
|
||||
constructor() {
|
||||
this.logger.log('ZulipClientService初始化完成');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并初始化Zulip客户端
|
||||
*
|
||||
* 功能描述:
|
||||
* 使用提供的配置创建zulip-js客户端实例,并验证API Key的有效性
|
||||
*
|
||||
* 业务逻辑:
|
||||
* 1. 验证配置参数的完整性
|
||||
* 2. 创建zulip-js客户端实例
|
||||
* 3. 调用API验证凭证有效性
|
||||
* 4. 返回初始化后的客户端实例
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param config Zulip客户端配置
|
||||
* @returns Promise<ZulipClientInstance> 初始化后的客户端实例
|
||||
*
|
||||
* @throws Error 当配置无效或API Key验证失败时
|
||||
*/
|
||||
async createClient(userId: string, config: ZulipClientConfig): Promise<ZulipClientInstance> {
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger.log('开始创建Zulip客户端', {
|
||||
operation: 'createClient',
|
||||
userId,
|
||||
realm: config.realm,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
// 1. 验证配置参数
|
||||
this.validateConfig(config);
|
||||
|
||||
// 2. 动态导入zulip-js
|
||||
const zulipInit = await this.loadZulipModule();
|
||||
|
||||
// 3. 创建zulip-js客户端实例
|
||||
const client = await zulipInit({
|
||||
username: config.username,
|
||||
apiKey: config.apiKey,
|
||||
realm: config.realm,
|
||||
});
|
||||
|
||||
// 4. 验证API Key有效性 - 通过获取用户信息
|
||||
const profile = await client.users.me.getProfile();
|
||||
|
||||
if (profile.result !== 'success') {
|
||||
throw new Error(`API Key验证失败: ${profile.msg || '未知错误'}`);
|
||||
}
|
||||
|
||||
const clientInstance: ZulipClientInstance = {
|
||||
userId,
|
||||
config,
|
||||
client,
|
||||
lastEventId: -1,
|
||||
createdAt: new Date(),
|
||||
lastActivity: new Date(),
|
||||
isValid: true,
|
||||
};
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
this.logger.log('Zulip客户端创建成功', {
|
||||
operation: 'createClient',
|
||||
userId,
|
||||
realm: config.realm,
|
||||
userEmail: profile.email,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return clientInstance;
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
this.logger.error('创建Zulip客户端失败', {
|
||||
operation: 'createClient',
|
||||
userId,
|
||||
realm: config.realm,
|
||||
error: err.message,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
throw new Error(`创建Zulip客户端失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证API Key有效性
|
||||
*
|
||||
* 功能描述:
|
||||
* 通过调用Zulip API验证API Key是否有效
|
||||
*
|
||||
* @param clientInstance Zulip客户端实例
|
||||
* @returns Promise<boolean> API Key是否有效
|
||||
*/
|
||||
async validateApiKey(clientInstance: ZulipClientInstance): Promise<boolean> {
|
||||
this.logger.log('验证API Key有效性', {
|
||||
operation: 'validateApiKey',
|
||||
userId: clientInstance.userId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
const profile = await clientInstance.client.users.me.getProfile();
|
||||
const isValid = profile.result === 'success';
|
||||
|
||||
clientInstance.isValid = isValid;
|
||||
clientInstance.lastActivity = new Date();
|
||||
|
||||
this.logger.log('API Key验证完成', {
|
||||
operation: 'validateApiKey',
|
||||
userId: clientInstance.userId,
|
||||
isValid,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return isValid;
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
|
||||
this.logger.error('API Key验证失败', {
|
||||
operation: 'validateApiKey',
|
||||
userId: clientInstance.userId,
|
||||
error: err.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
clientInstance.isValid = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息到指定Stream/Topic
|
||||
*
|
||||
* 功能描述:
|
||||
* 使用Zulip客户端发送消息到指定的Stream和Topic
|
||||
*
|
||||
* 业务逻辑:
|
||||
* 1. 验证客户端实例有效性
|
||||
* 2. 构建消息请求参数
|
||||
* 3. 调用Zulip API发送消息
|
||||
* 4. 处理响应并返回结果
|
||||
*
|
||||
* @param clientInstance Zulip客户端实例
|
||||
* @param stream 目标Stream名称
|
||||
* @param topic 目标Topic名称
|
||||
* @param content 消息内容
|
||||
* @returns Promise<SendMessageResult> 发送结果
|
||||
*/
|
||||
async sendMessage(
|
||||
clientInstance: ZulipClientInstance,
|
||||
stream: string,
|
||||
topic: string,
|
||||
content: string,
|
||||
): Promise<SendMessageResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger.log('发送消息到Zulip', {
|
||||
operation: 'sendMessage',
|
||||
userId: clientInstance.userId,
|
||||
stream,
|
||||
topic,
|
||||
contentLength: content.length,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
// 1. 验证客户端有效性
|
||||
if (!clientInstance.isValid) {
|
||||
throw new Error('Zulip客户端无效');
|
||||
}
|
||||
|
||||
// 2. 构建消息参数
|
||||
const params = {
|
||||
type: 'stream',
|
||||
to: stream,
|
||||
subject: topic,
|
||||
content: content,
|
||||
};
|
||||
|
||||
// 3. 发送消息
|
||||
const response = await clientInstance.client.messages.send(params);
|
||||
|
||||
// 4. 更新最后活动时间
|
||||
clientInstance.lastActivity = new Date();
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
if (response.result === 'success') {
|
||||
this.logger.log('消息发送成功', {
|
||||
operation: 'sendMessage',
|
||||
userId: clientInstance.userId,
|
||||
stream,
|
||||
topic,
|
||||
messageId: response.id,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
messageId: response.id,
|
||||
};
|
||||
} else {
|
||||
this.logger.warn('消息发送失败', {
|
||||
operation: 'sendMessage',
|
||||
userId: clientInstance.userId,
|
||||
stream,
|
||||
topic,
|
||||
error: response.msg,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: response.msg || '消息发送失败',
|
||||
};
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
this.logger.error('发送消息异常', {
|
||||
operation: 'sendMessage',
|
||||
userId: clientInstance.userId,
|
||||
stream,
|
||||
topic,
|
||||
error: err.message,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: err.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册事件队列
|
||||
*
|
||||
* 功能描述:
|
||||
* 向Zulip服务器注册事件队列,用于接收消息通知
|
||||
*
|
||||
* 业务逻辑:
|
||||
* 1. 验证客户端实例有效性
|
||||
* 2. 构建队列注册参数
|
||||
* 3. 调用Zulip API注册队列
|
||||
* 4. 保存队列ID到客户端实例
|
||||
*
|
||||
* @param clientInstance Zulip客户端实例
|
||||
* @param eventTypes 要订阅的事件类型列表
|
||||
* @returns Promise<RegisterQueueResult> 注册结果
|
||||
*/
|
||||
async registerQueue(
|
||||
clientInstance: ZulipClientInstance,
|
||||
eventTypes: string[] = ['message'],
|
||||
): Promise<RegisterQueueResult> {
|
||||
const startTime = Date.now();
|
||||
|
||||
this.logger.log('注册Zulip事件队列', {
|
||||
operation: 'registerQueue',
|
||||
userId: clientInstance.userId,
|
||||
eventTypes,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
// 1. 验证客户端有效性
|
||||
if (!clientInstance.isValid) {
|
||||
throw new Error('Zulip客户端无效');
|
||||
}
|
||||
|
||||
// 2. 构建注册参数
|
||||
const params = {
|
||||
event_types: eventTypes,
|
||||
};
|
||||
|
||||
// 3. 注册队列
|
||||
const response = await clientInstance.client.queues.register(params);
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
if (response.result === 'success') {
|
||||
// 4. 保存队列信息
|
||||
clientInstance.queueId = response.queue_id;
|
||||
clientInstance.lastEventId = response.last_event_id;
|
||||
clientInstance.lastActivity = new Date();
|
||||
|
||||
this.logger.log('事件队列注册成功', {
|
||||
operation: 'registerQueue',
|
||||
userId: clientInstance.userId,
|
||||
queueId: response.queue_id,
|
||||
lastEventId: response.last_event_id,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
queueId: response.queue_id,
|
||||
lastEventId: response.last_event_id,
|
||||
};
|
||||
} else {
|
||||
this.logger.warn('事件队列注册失败', {
|
||||
operation: 'registerQueue',
|
||||
userId: clientInstance.userId,
|
||||
error: response.msg,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: response.msg || '事件队列注册失败',
|
||||
};
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
this.logger.error('注册事件队列异常', {
|
||||
operation: 'registerQueue',
|
||||
userId: clientInstance.userId,
|
||||
error: err.message,
|
||||
duration,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: err.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销事件队列
|
||||
*
|
||||
* 功能描述:
|
||||
* 注销已注册的Zulip事件队列
|
||||
*
|
||||
* @param clientInstance Zulip客户端实例
|
||||
* @returns Promise<boolean> 是否成功注销
|
||||
*/
|
||||
async deregisterQueue(clientInstance: ZulipClientInstance): Promise<boolean> {
|
||||
this.logger.log('注销Zulip事件队列', {
|
||||
operation: 'deregisterQueue',
|
||||
userId: clientInstance.userId,
|
||||
queueId: clientInstance.queueId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
if (!clientInstance.queueId) {
|
||||
this.logger.log('无事件队列需要注销', {
|
||||
operation: 'deregisterQueue',
|
||||
userId: clientInstance.userId,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
const response = await clientInstance.client.queues.deregister({
|
||||
queue_id: clientInstance.queueId,
|
||||
});
|
||||
|
||||
if (response.result === 'success') {
|
||||
clientInstance.queueId = undefined;
|
||||
clientInstance.lastEventId = -1;
|
||||
|
||||
this.logger.log('事件队列注销成功', {
|
||||
operation: 'deregisterQueue',
|
||||
userId: clientInstance.userId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return true;
|
||||
} else {
|
||||
this.logger.warn('事件队列注销失败', {
|
||||
operation: 'deregisterQueue',
|
||||
userId: clientInstance.userId,
|
||||
error: response.msg,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
|
||||
// 如果是JSON解析错误,说明队列可能已经过期或被删除,这是正常的
|
||||
if (err.message.includes('invalid json response') || err.message.includes('Unexpected token')) {
|
||||
this.logger.debug('事件队列可能已过期,跳过注销', {
|
||||
operation: 'deregisterQueue',
|
||||
userId: clientInstance.userId,
|
||||
queueId: clientInstance.queueId,
|
||||
});
|
||||
|
||||
// 清理本地状态
|
||||
clientInstance.queueId = undefined;
|
||||
clientInstance.lastEventId = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
this.logger.error('注销事件队列异常', {
|
||||
operation: 'deregisterQueue',
|
||||
userId: clientInstance.userId,
|
||||
error: err.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
// 即使注销失败,也清理本地状态
|
||||
clientInstance.queueId = undefined;
|
||||
clientInstance.lastEventId = -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件队列中的事件
|
||||
*
|
||||
* 功能描述:
|
||||
* 从Zulip事件队列中获取新事件
|
||||
*
|
||||
* @param clientInstance Zulip客户端实例
|
||||
* @param dontBlock 是否不阻塞等待新事件
|
||||
* @returns Promise<GetEventsResult> 获取结果
|
||||
*/
|
||||
async getEvents(
|
||||
clientInstance: ZulipClientInstance,
|
||||
dontBlock: boolean = false,
|
||||
): Promise<GetEventsResult> {
|
||||
this.logger.debug('获取Zulip事件', {
|
||||
operation: 'getEvents',
|
||||
userId: clientInstance.userId,
|
||||
queueId: clientInstance.queueId,
|
||||
lastEventId: clientInstance.lastEventId,
|
||||
dontBlock,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
if (!clientInstance.queueId) {
|
||||
throw new Error('事件队列未注册');
|
||||
}
|
||||
|
||||
const params = {
|
||||
queue_id: clientInstance.queueId,
|
||||
last_event_id: clientInstance.lastEventId,
|
||||
dont_block: dontBlock,
|
||||
};
|
||||
|
||||
const response = await clientInstance.client.events.retrieve(params);
|
||||
|
||||
if (response.result === 'success') {
|
||||
// 更新最后事件ID
|
||||
if (response.events && response.events.length > 0) {
|
||||
const lastEvent = response.events[response.events.length - 1];
|
||||
clientInstance.lastEventId = lastEvent.id;
|
||||
}
|
||||
|
||||
clientInstance.lastActivity = new Date();
|
||||
|
||||
this.logger.debug('获取事件成功', {
|
||||
operation: 'getEvents',
|
||||
userId: clientInstance.userId,
|
||||
eventCount: response.events?.length || 0,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
events: response.events || [],
|
||||
};
|
||||
} else {
|
||||
this.logger.warn('获取事件失败', {
|
||||
operation: 'getEvents',
|
||||
userId: clientInstance.userId,
|
||||
error: response.msg,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: response.msg || '获取事件失败',
|
||||
};
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
|
||||
this.logger.error('获取事件异常', {
|
||||
operation: 'getEvents',
|
||||
userId: clientInstance.userId,
|
||||
error: err.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: err.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁客户端实例
|
||||
*
|
||||
* 功能描述:
|
||||
* 清理客户端资源,注销事件队列
|
||||
*
|
||||
* @param clientInstance Zulip客户端实例
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
async destroyClient(clientInstance: ZulipClientInstance): Promise<void> {
|
||||
this.logger.log('销毁Zulip客户端', {
|
||||
operation: 'destroyClient',
|
||||
userId: clientInstance.userId,
|
||||
queueId: clientInstance.queueId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
try {
|
||||
// 注销事件队列
|
||||
if (clientInstance.queueId) {
|
||||
await this.deregisterQueue(clientInstance);
|
||||
}
|
||||
|
||||
// 标记客户端为无效
|
||||
clientInstance.isValid = false;
|
||||
clientInstance.client = null;
|
||||
|
||||
this.logger.log('Zulip客户端销毁完成', {
|
||||
operation: 'destroyClient',
|
||||
userId: clientInstance.userId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
|
||||
this.logger.error('销毁Zulip客户端异常', {
|
||||
operation: 'destroyClient',
|
||||
userId: clientInstance.userId,
|
||||
error: err.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
|
||||
// 即使出错也标记为无效
|
||||
clientInstance.isValid = false;
|
||||
clientInstance.client = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证配置参数
|
||||
*
|
||||
* @param config Zulip客户端配置
|
||||
* @throws Error 当配置无效时
|
||||
* @private
|
||||
*/
|
||||
private validateConfig(config: ZulipClientConfig): void {
|
||||
if (!config.username || typeof config.username !== 'string') {
|
||||
throw new Error('无效的username配置');
|
||||
}
|
||||
|
||||
if (!config.apiKey || typeof config.apiKey !== 'string') {
|
||||
throw new Error('无效的apiKey配置');
|
||||
}
|
||||
|
||||
if (!config.realm || typeof config.realm !== 'string') {
|
||||
throw new Error('无效的realm配置');
|
||||
}
|
||||
|
||||
// 验证realm是否为有效URL
|
||||
try {
|
||||
new URL(config.realm);
|
||||
} catch {
|
||||
throw new Error('realm必须是有效的URL');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态加载zulip-js模块
|
||||
*
|
||||
* @returns Promise<any> zulip-js初始化函数
|
||||
* @private
|
||||
*/
|
||||
private async loadZulipModule(): Promise<any> {
|
||||
try {
|
||||
// 使用动态导入加载zulip-js
|
||||
const zulipModule = await import('zulip-js');
|
||||
return zulipModule.default || zulipModule;
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
this.logger.error('加载zulip-js模块失败', {
|
||||
operation: 'loadZulipModule',
|
||||
error: err.message,
|
||||
timestamp: new Date().toISOString(),
|
||||
}, err.stack);
|
||||
throw new Error(`加载zulip-js模块失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user