import { IExecuteFunctions, IHookFunctions, ILoadOptionsFunctions, } from 'n8n-core'; import { OptionsWithUri } from 'request'; import { IDataObject } from 'n8n-workflow'; // Interface in n8n export interface IMarkupKeyboard { rows?: IMarkupKeyboardRow[]; } export interface IMarkupKeyboardRow { row?: IMarkupKeyboardRow; } export interface IMarkupKeyboardRow { buttons?: IMarkupKeyboardButton[]; } export interface IMarkupKeyboardButton { text: string; additionalFields?: IDataObject; } // Interface in Telegram export interface ITelegramInlineReply { inline_keyboard?: ITelegramKeyboardButton[][]; } export interface ITelegramKeyboardButton { [key: string]: string | number | boolean; } export interface ITelegramReplyKeyboard extends IMarkupReplyKeyboardOptions { keyboard: ITelegramKeyboardButton[][]; } // Shared interfaces export interface IMarkupForceReply { force_reply?: boolean; selective?: boolean; } export interface IMarkupReplyKeyboardOptions { one_time_keyboard?: boolean; resize_keyboard?: boolean; selective?: boolean; } export interface IMarkupReplyKeyboardRemove { force_reply?: boolean; selective?: boolean; } /** * Add the additional fields to the body * * @param {IExecuteFunctions} this * @param {IDataObject} body The body object to add fields to * @param {number} index The index of the item * @returns */ export function addAdditionalFields(this: IExecuteFunctions, body: IDataObject, index: number) { // Add the additional fields const additionalFields = this.getNodeParameter('additionalFields', index) as IDataObject; Object.assign(body, additionalFields); // Add the reply markup const replyMarkupOption = this.getNodeParameter('replyMarkup', index) as string; if (replyMarkupOption === 'none') { return; } body.reply_markup = {} as IMarkupForceReply | IMarkupReplyKeyboardRemove | ITelegramInlineReply | ITelegramReplyKeyboard; if (['inlineKeyboard', 'replyKeyboard'].includes(replyMarkupOption)) { let setParameterName = 'inline_keyboard'; if (replyMarkupOption === 'replyKeyboard') { setParameterName = 'keyboard'; } const keyboardData = this.getNodeParameter(replyMarkupOption, index) as IMarkupKeyboard; // @ts-ignore (body.reply_markup as ITelegramInlineReply | ITelegramReplyKeyboard)[setParameterName] = [] as ITelegramKeyboardButton[][]; let sendButtonData: ITelegramKeyboardButton; if (keyboardData.rows !== undefined) { for (const row of keyboardData.rows) { const sendRows: ITelegramKeyboardButton[] = []; if (row.row === undefined || row.row.buttons === undefined) { continue; } for (const button of row.row.buttons) { sendButtonData = {}; sendButtonData.text = button.text; if (button.additionalFields) { Object.assign(sendButtonData, button.additionalFields); } sendRows.push(sendButtonData); } // @ts-ignore ((body.reply_markup as ITelegramInlineReply | ITelegramReplyKeyboard)[setParameterName] as ITelegramKeyboardButton[][]).push(sendRows); } } } else if (replyMarkupOption === 'forceReply') { const forceReply = this.getNodeParameter('forceReply', index) as IMarkupForceReply; body.reply_markup = forceReply; } else if (replyMarkupOption === 'replyKeyboardRemove') { const forceReply = this.getNodeParameter('replyKeyboardRemove', index) as IMarkupReplyKeyboardRemove; body.reply_markup = forceReply; } if (replyMarkupOption === 'replyKeyboard') { const replyKeyboardOptions = this.getNodeParameter('replyKeyboardOptions', index) as IMarkupReplyKeyboardOptions; Object.assign(body.reply_markup, replyKeyboardOptions); } } /** * Make an API request to Telegram * * @param {IHookFunctions} this * @param {string} method * @param {string} url * @param {object} body * @returns {Promise} */ export async function apiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: object, query?: IDataObject): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('telegramApi'); if (credentials === undefined) { throw new Error('No credentials got returned!'); } query = query || {}; const options: OptionsWithUri = { headers: { }, method, body, qs: query, uri: `https://api.telegram.org/bot${credentials.accessToken}/${endpoint}`, json: true, }; try { return await this.helpers.request!(options); } catch (error) { if (error.statusCode === 401) { // Return a clear error throw new Error('The Telegram credentials are not valid!'); } if (error.response && error.response.body && error.response.body.error_code) { // Try to return the error prettier const errorBody = error.response.body; throw new Error(`Telegram error response [${errorBody.error_code}]: ${errorBody.description}`); } // Expected error data did not get returned so throw the actual error throw error; } }