n8n
Version:
n8n Workflow Automation Tool
184 lines • 8.66 kB
JavaScript
;
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