UNPKG

n8n-nodes-feishu-lark

Version:

n8n custom nodes for n8n to interact with Feishu/Lark, including Lark Bot, Lark MCP, and Lark Trigger.

363 lines 14.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WSClient = void 0; const ws_1 = __importDefault(require("ws")); const protoBuf = __importStar(require("./proto-buf")); const ws_config_1 = require("./ws-config"); const data_cache_1 = require("./data-cache"); const enum_1 = require("./enum"); const pbbp2_1 = require("./proto-buf/pbbp2"); class WSClient { constructor(params) { this.wsConfig = new ws_config_1.WSConfig(); this.isConnecting = false; this.shouldReconnect = true; this.reconnectInfo = { lastConnectTime: 0, nextConnectTime: 0, }; const { appId, appSecret, logger, helpers, agent, domain, autoReconnect = true } = params; this.logger = logger; this.agent = agent; this.dataCache = new data_cache_1.DataCache({ logger: this.logger }); this.helpers = helpers; this.wsConfig.updateClient({ appId, appSecret, domain, }); this.wsConfig.updateWs({ autoReconnect, }); } async pullConnectConfig() { const { appId, appSecret } = this.wsConfig.getClient(); try { const response = await this.helpers.request({ method: 'POST', url: this.wsConfig.wsConfigUrl, body: { AppID: appId, AppSecret: appSecret, }, headers: { locale: 'zh', }, timeout: 15000, }); const { code, data: { URL: connectUrl, ClientConfig }, msg, } = JSON.parse(response); if (code !== enum_1.ErrorCode.ok) { this.logger.error(`[ws] code: ${code}, ${code === enum_1.ErrorCode.system_busy ? msg : 'system busy'}`); if (code === enum_1.ErrorCode.system_busy || code === enum_1.ErrorCode.internal_error) { return false; } } const parsedUrl = new URL(connectUrl); const device_id = parsedUrl.searchParams.get('device_id'); const service_id = parsedUrl.searchParams.get('service_id'); this.wsConfig.updateWs({ connectUrl, deviceId: device_id, serviceId: service_id, pingInterval: ClientConfig.PingInterval * 1000, reconnectCount: ClientConfig.ReconnectCount, reconnectInterval: ClientConfig.ReconnectInterval * 1000, reconnectNonce: ClientConfig.ReconnectNonce * 1000, }); this.logger.debug(`[ws] get connect config success, ws url: ${connectUrl}`); return true; } catch (e) { this.logger.error('[ws]', (e === null || e === void 0 ? void 0 : e.message) || 'system busy'); return false; } } connect() { const connectUrl = this.wsConfig.getWS('connectUrl'); let wsInstance; try { const { agent } = this; wsInstance = new ws_1.default(connectUrl, { agent }); } catch (e) { this.logger.error('[ws] new WebSocket error'); } if (!wsInstance) { return Promise.resolve(false); } return new Promise((resolve) => { wsInstance.on('open', () => { this.logger.debug('[ws] ws connect success'); this.wsConfig.setWSInstance(wsInstance); this.pingLoop(); resolve(true); }); wsInstance.on('error', () => { this.logger.error('[ws] ws connect failed'); resolve(false); }); }); } async reConnect(isStart = false) { if (this.isConnecting) { this.logger.debug('[ws] repeat connection'); return; } this.isConnecting = true; const tryConnect = () => { this.reconnectInfo.lastConnectTime = Date.now(); return this.pullConnectConfig() .then((isSuccess) => (isSuccess ? this.connect() : Promise.resolve(false))) .then((isSuccess) => { if (isSuccess) { this.communicate(); return Promise.resolve(true); } return Promise.resolve(false); }); }; if (this.pingInterval) { clearTimeout(this.pingInterval); } const wsInstance = this.wsConfig.getWSInstance(); if (isStart) { if (wsInstance) { wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.terminate(); } if (this.reconnectInterval) { clearTimeout(this.reconnectInterval); } let isSuccess = false; try { isSuccess = await tryConnect(); } finally { this.isConnecting = false; } if (!isSuccess) { this.logger.error('[ws] connect failed'); await this.reConnect(); } this.logger.info('[ws] ws client ready'); return; } const { autoReconnect, reconnectNonce, reconnectCount, reconnectInterval } = this.wsConfig.getWS(); if (!autoReconnect) { return; } this.logger.info('[ws] reconnect'); if (wsInstance) { wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.terminate(); } this.wsConfig.setWSInstance(null); const reconnectNonceTime = reconnectNonce ? reconnectNonce * Math.random() : 0; this.reconnectInterval = setTimeout(async () => { (async function loopReConnect(count) { count++; const isSuccess = await tryConnect(); if (isSuccess) { this.logger.debug('[ws] reconnect success'); this.isConnecting = false; return; } this.logger.info(`[ws] unable to connect to the server after trying ${count} times`); if (reconnectCount >= 0 && count >= reconnectCount) { this.isConnecting = false; return; } this.reconnectInterval = setTimeout(() => { loopReConnect.bind(this)(count); }, reconnectInterval); this.reconnectInfo.nextConnectTime = Date.now() + reconnectInterval; }).bind(this)(0); }, reconnectNonceTime); this.reconnectInfo.nextConnectTime = Date.now() + reconnectNonceTime; } pingLoop() { const { serviceId, pingInterval } = this.wsConfig.getWS(); const wsInstance = this.wsConfig.getWSInstance(); if ((wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.readyState) === ws_1.default.OPEN) { const frame = { headers: [ { key: enum_1.HeaderKey.type, value: enum_1.MessageType.ping, }, ], service: Number(serviceId), method: enum_1.FrameType.control, SeqID: 0, LogID: 0, }; this.sendMessage(frame); this.logger.info('[ws] ping success'); } this.pingInterval = setTimeout(this.pingLoop.bind(this), pingInterval); } communicate() { const wsInstance = this.wsConfig.getWSInstance(); wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.on('message', async (buffer) => { const data = protoBuf.decode(buffer); const { method } = data; if (method === enum_1.FrameType.control) { await this.handleControlData(data); } if (method === enum_1.FrameType.data) { await this.handleEventData(data); } }); wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.on('error', (e) => { this.logger.error('[ws] ws error'); }); wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.on('close', () => { if (this.shouldReconnect) { this.logger.info('[ws] client closed unexpectedly, try to reconnect'); this.reConnect(); } else { this.logger.info('[ws] client closed'); } }); } async handleControlData(data) { var _a; const type = (_a = data.headers.find((item) => item.key === enum_1.HeaderKey.type)) === null || _a === void 0 ? void 0 : _a.value; const payload = data.payload; if (type === enum_1.MessageType.ping) { return; } if (type === enum_1.MessageType.pong && payload) { this.logger.info('[ws] receive pong'); const dataString = new TextDecoder('utf-8').decode(payload); const { PingInterval, ReconnectCount, ReconnectInterval, ReconnectNonce } = JSON.parse(dataString); this.wsConfig.updateWs({ pingInterval: PingInterval * 1000, reconnectCount: ReconnectCount, reconnectInterval: ReconnectInterval * 1000, reconnectNonce: ReconnectNonce * 1000, }); this.logger.info('[ws] update wsConfig with pong data'); } } async handleEventData(data) { var _a; const headers = data.headers.reduce((acc, cur) => { acc[cur.key] = cur.value; return acc; }, {}); const { message_id, sum, seq, type, trace_id } = headers; const payload = data.payload; if (type !== enum_1.MessageType.event) { return; } const mergedData = this.dataCache.mergeData({ message_id, sum: Number(sum), seq: Number(seq), trace_id, data: payload, }); if (!mergedData) { return; } this.logger.debug(`[ws] receive message, message_type: ${type}; message_id: ${message_id}; trace_id: ${trace_id}; data: ${mergedData.data}`); const respPayload = { code: enum_1.HttpStatusCode.ok, }; const startTime = Date.now(); try { const result = await ((_a = this.eventDispatcher) === null || _a === void 0 ? void 0 : _a.invoke(mergedData)); if (result) { respPayload.data = Buffer.from(JSON.stringify(result)).toString('base64'); } } catch (error) { respPayload.code = enum_1.HttpStatusCode.internal_server_error; this.logger.error(`[ws] invoke event failed, message_type: ${type}; message_id: ${message_id}; trace_id: ${trace_id}; error: ${error}`); } const endTime = Date.now(); this.sendMessage({ ...data, headers: [...data.headers, { key: enum_1.HeaderKey.biz_rt, value: String(startTime - endTime) }], payload: new TextEncoder().encode(JSON.stringify(respPayload)), }); } sendMessage(data) { var _a; const wsInstance = this.wsConfig.getWSInstance(); if ((wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.readyState) === ws_1.default.OPEN) { const resp = pbbp2_1.pbbp2.Frame.encode(data).finish(); (_a = this.wsConfig.getWSInstance()) === null || _a === void 0 ? void 0 : _a.send(resp, (err) => { if (err) { this.logger.error('[ws] send data failed'); } }); } } getReconnectInfo() { return this.reconnectInfo; } async start(params) { const { eventDispatcher } = params; if (!eventDispatcher) { this.logger.error('[ws] client need to start with a eventDispatcher'); return; } this.eventDispatcher = eventDispatcher; this.reConnect(true); } async stop() { this.shouldReconnect = false; const wsInstance = this.wsConfig.getWSInstance(); if (wsInstance) { wsInstance.terminate(); } if (this.reconnectInterval) { clearTimeout(this.reconnectInterval); } if (this.pingInterval) { clearTimeout(this.pingInterval); } this.dataCache.clear(); this.eventDispatcher = undefined; this.isConnecting = false; } } exports.WSClient = WSClient; //# sourceMappingURL=index.js.map