UNPKG

n8n

Version:

n8n Workflow Automation Tool

184 lines • 8.66 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TelegramIntegration = void 0; const backend_common_1 = require("@n8n/backend-common"); const di_1 = require("@n8n/di"); const crypto_1 = require("crypto"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const conflict_error_1 = require("../../../../errors/response-errors/conflict.error"); const url_service_1 = require("../../../../services/url.service"); const agent_repository_1 = require("../../repositories/agent.repository"); const agent_chat_integration_1 = require("../agent-chat-integration"); const esm_loader_1 = require("../esm-loader"); let TelegramIntegration = class TelegramIntegration extends agent_chat_integration_1.AgentChatIntegration { constructor(logger, urlService, agentRepository, instanceSettings) { super(); this.logger = logger; this.urlService = urlService; this.agentRepository = agentRepository; this.instanceSettings = instanceSettings; this.type = 'telegram'; this.credentialTypes = ['telegramApi']; this.displayLabel = 'Telegram'; this.displayIcon = 'telegram'; this.builderGuidance = { capabilities: [ 'Receive Telegram messages as agent triggers.', 'Respond in Telegram conversations and send direct Telegram messages.', 'Render Telegram-compatible rich interaction cards with buttons.', ], useIntegrationWhen: [ 'The agent should be chatted with from Telegram or act as a Telegram bot.', 'The agent needs to reply to Telegram users in the same conversation context.', 'The agent should send Telegram messages as the connected Telegram bot.', ], useNodeToolWhen: [ 'Telegram is only a backend API step and the agent does not need to be connected as a Telegram chat surface.', 'The request is a one-off Telegram operation from another trigger without ongoing Telegram conversation context.', ], }; this.supportedComponents = ['section', 'button', 'divider', 'fields']; this.actions = ['respond', 'send_dm']; this.needsShortCallbackData = true; this.disableStreaming = true; this.formatThreadId = { fromSdk: (thread) => { const adapter = thread.adapter; const botUserId = adapter.botUserId; if (!botUserId) { throw new Error('Telegram bot user ID is not set'); } return `chat:${botUserId}-${thread.id}`; }, toSdk: (threadId) => { if (!threadId.includes('-')) { return threadId; } return threadId.split('-').slice(1).join('-'); }, }; } async createAdapter(ctx) { const botToken = this.extractBotToken(ctx.credential); const mode = this.getMode(); const secretToken = this.deriveSecretToken(ctx.agentId, ctx.credentialId); const { createTelegramAdapter } = await (0, esm_loader_1.loadTelegramAdapter)(); return createTelegramAdapter({ botToken, mode, secretToken }); } requiresLeader() { return this.getMode() === 'polling'; } async onBeforeConnect(ctx) { const others = await this.agentRepository.findByIntegrationCredential(this.type, ctx.credentialId, ctx.projectId, ctx.agentId); if (others.length > 0) { throw new conflict_error_1.ConflictError(`Telegram credential is already connected to agent "${others[0].name}"`); } } async onAfterConnect(ctx) { if (this.getMode() !== 'webhook') return; const webhookUrl = ctx.webhookUrlFor('telegram'); const secretToken = this.deriveSecretToken(ctx.agentId, ctx.credentialId); const resp = await fetch(this.botApiUrl(ctx.credential, 'setWebhook'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: webhookUrl, secret_token: secretToken }), }); if (!resp.ok) { throw new Error(`Failed to register Telegram webhook: ${await resp.text()}`); } this.logger.info(`[TelegramIntegration] Webhook registered: ${webhookUrl}`); } async onBeforeDisconnect(ctx) { if (this.getMode() !== 'webhook') return; const resp = await fetch(this.botApiUrl(ctx.credential, 'deleteWebhook'), { method: 'POST', }); if (!resp.ok) { throw new Error(`Failed to deregister Telegram webhook: ${await resp.text()}`); } this.logger.info(`[TelegramIntegration] Webhook deregistered for agent ${ctx.agentId}, credential ${ctx.credentialId}`); } isUserAllowed(author, integration) { if (!integration) return true; if (integration?.type !== 'telegram') { throw new n8n_workflow_1.UnexpectedError(`TelegramIntegration received settings with type "${integration?.type}"`); } if (!integration.settings) return true; if (integration.settings.accessMode === 'public') return true; return integration.settings.allowedUsers.some((allowed) => { const normalized = allowed.startsWith('@') ? allowed.slice(1) : allowed; return normalized === author.userId || normalized === author.userName; }); } normalizeComponents(components) { const normalized = []; for (const c of components) { switch (c.type) { case 'select': case 'radio_select': for (const opt of c.options ?? []) { normalized.push({ type: 'button', label: opt.label, value: opt.value }); } break; case 'image': if (c.url) { normalized.push({ type: 'section', text: `[${c.altText ?? 'Image'}](${c.url})` }); } break; default: normalized.push(c); } } return normalized; } getMode() { const baseUrl = this.urlService.getWebhookBaseUrl(); const isPublic = baseUrl.startsWith('https://') && !baseUrl.includes('localhost'); return isPublic ? 'webhook' : 'polling'; } deriveSecretToken(agentId, credentialId) { return (0, crypto_1.createHmac)('sha256', this.instanceSettings.encryptionKey) .update(`telegram:${agentId}:${credentialId}`) .digest('hex'); } extractBotToken(credential) { const token = credential.accessToken; if (typeof token === 'string' && token) { return token; } throw new Error('Could not extract a bot token from the Telegram credential. ' + 'Please ensure the credential has a valid access token from BotFather.'); } botApiUrl(credential, method) { const botToken = this.extractBotToken(credential); const raw = credential.baseUrl; const baseUrl = typeof raw === 'string' && raw.trim() ? raw.trim().replace(/\/+$/, '') : 'https://api.telegram.org'; return `${baseUrl}/bot${botToken}/${method}`; } }; exports.TelegramIntegration = TelegramIntegration; exports.TelegramIntegration = TelegramIntegration = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, url_service_1.UrlService, agent_repository_1.AgentRepository, n8n_core_1.InstanceSettings]) ], TelegramIntegration); //# sourceMappingURL=telegram-integration.js.map