diff --git a/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts b/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts index f3f51cb778..15224651a5 100644 --- a/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts +++ b/packages/nodes-base/nodes/Discord/v2/helpers/utils.ts @@ -392,11 +392,13 @@ export async function sendDiscordMessage( export function createSendAndWaitMessageBody(context: IExecuteFunctions) { const config = getSendAndWaitConfig(context); - - const instanceId = context.getInstanceId(); - const attributionText = 'This message was sent automatically with '; - const link = createUtmCampaignLink('n8n-nodes-base.discord', instanceId); - const description = `${config.message}\n\n_${attributionText}_[n8n](${link})`; + let description = config.message; + if (config.appendAttribution !== false) { + const instanceId = context.getInstanceId(); + const attributionText = 'This message was sent automatically with '; + const link = createUtmCampaignLink('n8n-nodes-base.discord', instanceId); + description = `${config.message}\n\n_${attributionText}_[n8n](${link})`; + } const body = { embeds: [ diff --git a/packages/nodes-base/nodes/EmailSend/test/v2/sendAndWait.operation.test.ts b/packages/nodes-base/nodes/EmailSend/test/v2/sendAndWait.operation.test.ts index f0dfea2042..46b6bd2bbd 100644 --- a/packages/nodes-base/nodes/EmailSend/test/v2/sendAndWait.operation.test.ts +++ b/packages/nodes-base/nodes/EmailSend/test/v2/sendAndWait.operation.test.ts @@ -46,7 +46,8 @@ describe('Test EmailSendV2, email => sendAndWait', () => { mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('my subject'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('http://localhost/waiting-webhook'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('nodeID'); - mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // approvalOptions + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // options mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('approval'); // configureWaitTillDate diff --git a/packages/nodes-base/nodes/EmailSend/v2/sendAndWait.operation.ts b/packages/nodes-base/nodes/EmailSend/v2/sendAndWait.operation.ts index 401e85731c..a8a7888a01 100644 --- a/packages/nodes-base/nodes/EmailSend/v2/sendAndWait.operation.ts +++ b/packages/nodes-base/nodes/EmailSend/v2/sendAndWait.operation.ts @@ -8,7 +8,10 @@ import type { import { fromEmailProperty, toEmailProperty } from './descriptions'; import { configureTransport } from './utils'; import { configureWaitTillDate } from '../../../utils/sendAndWait/configureWaitTillDate.util'; -import { createEmailBody } from '../../../utils/sendAndWait/email-templates'; +import { + createEmailBodyWithN8nAttribution, + createEmailBodyWithoutN8nAttribution, +} from '../../../utils/sendAndWait/email-templates'; import { createButton, getSendAndWaitConfig, @@ -30,9 +33,14 @@ export async function execute(this: IExecuteFunctions): Promise_`; - const buttons: string[] = config.options.map( (option) => `*<${`${config.url}?approved=${option.value}`}|${option.label}>*`, ); - const text = `${config.message}\n\n\n${buttons.join(' ')}\n\n${attribution}`; + let text = `${config.message}\n\n\n${buttons.join(' ')}`; + + if (config.appendAttribution !== false) { + const instanceId = context.getInstanceId(); + const attributionText = '_This_ _message_ _was_ _sent_ _automatically_ _with_'; + const link = createUtmCampaignLink('n8n-nodes-base.googleChat', instanceId); + const attribution = `${attributionText} _<${link}|n8n>_`; + text += `\n\n${attribution}`; + } const body = { text, diff --git a/packages/nodes-base/nodes/Google/Chat/test/node/sendAndWait.operation.test.ts b/packages/nodes-base/nodes/Google/Chat/test/node/sendAndWait.operation.test.ts index 0f18928c7c..a45fbb43f3 100644 --- a/packages/nodes-base/nodes/Google/Chat/test/node/sendAndWait.operation.test.ts +++ b/packages/nodes-base/nodes/Google/Chat/test/node/sendAndWait.operation.test.ts @@ -41,7 +41,8 @@ describe('Test GoogleChat, message => sendAndWait', () => { mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('my subject'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('http://localhost/waiting-webhook'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('nodeID'); - mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // approvalOptions + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // options mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('approval'); // configureWaitTillDate diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/sendAndWait.test.ts b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/sendAndWait.test.ts index 0375f1d40f..6d304590b1 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/sendAndWait.test.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/test/v2/node/message/sendAndWait.test.ts @@ -48,7 +48,8 @@ describe('Test MicrosoftOutlookV2, message => sendAndWait', () => { mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('my subject'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('http://localhost/waiting-webhook'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('nodeID'); - mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // approvalOptions + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // options mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('approval'); // configureWaitTillDate diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/sendAndWait.operation.ts b/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/sendAndWait.operation.ts index 53985249dc..9ac3e09330 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/sendAndWait.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/v2/actions/message/sendAndWait.operation.ts @@ -5,7 +5,10 @@ import type { INodeProperties, } from 'n8n-workflow'; -import { createEmailBody } from '../../../../../../utils/sendAndWait/email-templates'; +import { + createEmailBodyWithN8nAttribution, + createEmailBodyWithoutN8nAttribution, +} from '../../../../../../utils/sendAndWait/email-templates'; import { getSendAndWaitConfig, getSendAndWaitProperties, @@ -34,9 +37,13 @@ export async function execute(this: IExecuteFunctions, index: number, items: INo buttons.push(createButton(config.url, option.label, option.value, option.style)); } - const instanceId = this.getInstanceId(); - - const bodyContent = createEmailBody(config.message, buttons.join('\n'), instanceId); + let bodyContent: string; + if (config.appendAttribution !== false) { + const instanceId = this.getInstanceId(); + bodyContent = createEmailBodyWithN8nAttribution(config.message, buttons.join('\n'), instanceId); + } else { + bodyContent = createEmailBodyWithoutN8nAttribution(config.message, buttons.join('\n')); + } const fields: IDataObject = { subject: config.title, diff --git a/packages/nodes-base/nodes/Microsoft/Teams/v2/actions/chatMessage/sendAndWait.operation.ts b/packages/nodes-base/nodes/Microsoft/Teams/v2/actions/chatMessage/sendAndWait.operation.ts index e62309f9a8..fe2f82dba0 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/v2/actions/chatMessage/sendAndWait.operation.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/v2/actions/chatMessage/sendAndWait.operation.ts @@ -23,15 +23,18 @@ export async function execute(this: IExecuteFunctions, i: number, instanceId: st const chatId = this.getNodeParameter('chatId', i, '', { extractValue: true }) as string; const config = getSendAndWaitConfig(this); - const attributionText = 'This message was sent automatically with'; - const link = createUtmCampaignLink('n8n-nodes-base.microsoftTeams', instanceId); - const attribution = `${attributionText} n8n`; - const buttons = config.options.map( (option) => `${option.label}`, ); - const content = `${config.message}

${buttons.join(' ')}

${attribution}`; + let content = `${config.message}

${buttons.join(' ')}`; + + if (config.appendAttribution !== false) { + const attributionText = 'This message was sent automatically with'; + const link = createUtmCampaignLink('n8n-nodes-base.microsoftTeams', instanceId); + const attribution = `${attributionText} n8n`; + content += `

${attribution}`; + } const body = { body: { diff --git a/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts index 54cf540fd0..f17af1e88e 100644 --- a/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/V2/GenericFunctions.ts @@ -12,6 +12,7 @@ import { NodeOperationError } from 'n8n-workflow'; import type { SendAndWaitMessageBody } from './MessageInterface'; import { getSendAndWaitConfig } from '../../../utils/sendAndWait/utils'; +import { createUtmCampaignLink } from '../../../utils/utilities'; export async function slackApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, @@ -307,6 +308,19 @@ export function createSendAndWaitMessageBody(context: IExecuteFunctions) { ], }; + if (config.appendAttribution) { + const instanceId = context.getInstanceId(); + const attributionText = 'This message was sent automatically with '; + const link = createUtmCampaignLink('n8n-nodes-base.slack', instanceId); + body.blocks.push({ + type: 'section', + text: { + type: 'mrkdwn', + text: `${attributionText} _<${link}|n8n>_`, + }, + }); + } + if (context.getNode().typeVersion > 2.2 && body.blocks?.[1]?.type === 'section') { delete body.blocks[1].text.emoji; } diff --git a/packages/nodes-base/nodes/Telegram/GenericFunctions.ts b/packages/nodes-base/nodes/Telegram/GenericFunctions.ts index bbfeb6dbbd..d954e961e0 100644 --- a/packages/nodes-base/nodes/Telegram/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Telegram/GenericFunctions.ts @@ -260,14 +260,17 @@ export function createSendAndWaitMessageBody(context: IExecuteFunctions) { const config = getSendAndWaitConfig(context); let text = config.message; - const instanceId = context.getInstanceId(); - const attributionText = 'This message was sent automatically with '; - const link = createUtmCampaignLink('n8n-nodes-base.telegram', instanceId); - text = `${text}\n\n_${attributionText}_[n8n](${link})`; + if (config.appendAttribution !== false) { + const instanceId = context.getInstanceId(); + const attributionText = 'This message was sent automatically with '; + const link = createUtmCampaignLink('n8n-nodes-base.telegram', instanceId); + text = `${text}\n\n_${attributionText}_[n8n](${link})`; + } const body = { chat_id, text, + disable_web_page_preview: true, parse_mode: 'Markdown', reply_markup: { diff --git a/packages/nodes-base/nodes/Telegram/tests/node/sendAndWait.operation.test.ts b/packages/nodes-base/nodes/Telegram/tests/node/sendAndWait.operation.test.ts index 8acd58eed5..552bbb2415 100644 --- a/packages/nodes-base/nodes/Telegram/tests/node/sendAndWait.operation.test.ts +++ b/packages/nodes-base/nodes/Telegram/tests/node/sendAndWait.operation.test.ts @@ -44,7 +44,8 @@ describe('Test Telegram, message => sendAndWait', () => { mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('my subject'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('http://localhost/waiting-webhook'); mockExecuteFunctions.evaluateExpression.mockReturnValueOnce('nodeID'); - mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // approvalOptions + mockExecuteFunctions.getNodeParameter.mockReturnValueOnce({}); // options mockExecuteFunctions.getNodeParameter.mockReturnValueOnce('approval'); // configureWaitTillDate diff --git a/packages/nodes-base/nodes/WhatsApp/GenericFunctions.ts b/packages/nodes-base/nodes/WhatsApp/GenericFunctions.ts index db677d1bf8..fd14a13e74 100644 --- a/packages/nodes-base/nodes/WhatsApp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WhatsApp/GenericFunctions.ts @@ -15,6 +15,7 @@ import type { WhatsAppAppWebhookSubscription, } from './types'; import type { SendAndWaitConfig } from '../../utils/sendAndWait/utils'; +import { createUtmCampaignLink } from '../../utils/utilities'; export const WHATSAPP_BASE_URL = 'https://graph.facebook.com/v13.0/'; async function appAccessTokenRead( @@ -109,11 +110,19 @@ export const createMessage = ( sendAndWaitConfig: SendAndWaitConfig, phoneNumberId: string, recipientPhoneNumber: string, + instanceId: string, ): IHttpRequestOptions => { const buttons = sendAndWaitConfig.options.map((option) => { return `*${option.label}:*\n_${sendAndWaitConfig.url}?approved=${option.value}_\n\n`; }); + let n8nAttribution: string = ''; + if (sendAndWaitConfig.appendAttribution) { + const attributionText = 'This message was sent automatically with '; + const link = createUtmCampaignLink('n8n-nodes-base.whatsapp', instanceId); + n8nAttribution = `\n\n${attributionText}${link}`; + } + return { baseURL: WHATSAPP_BASE_URL, method: 'POST', @@ -121,7 +130,7 @@ export const createMessage = ( body: { messaging_product: 'whatsapp', text: { - body: `${sendAndWaitConfig.message}\n\n${buttons.join('')}`, + body: `${sendAndWaitConfig.message}\n\n${buttons.join('')}${n8nAttribution}`, }, type: 'text', to: recipientPhoneNumber, diff --git a/packages/nodes-base/nodes/WhatsApp/WhatsApp.node.ts b/packages/nodes-base/nodes/WhatsApp/WhatsApp.node.ts index ecfae62334..6c88c78fc6 100644 --- a/packages/nodes-base/nodes/WhatsApp/WhatsApp.node.ts +++ b/packages/nodes-base/nodes/WhatsApp/WhatsApp.node.ts @@ -83,11 +83,12 @@ export class WhatsApp implements INodeType { ); const config = getSendAndWaitConfig(this); + const instanceId = this.getInstanceId(); await this.helpers.httpRequestWithAuthentication.call( this, WHATSAPP_CREDENTIALS_TYPE, - createMessage(config, phoneNumberId, recipientPhoneNumber), + createMessage(config, phoneNumberId, recipientPhoneNumber, instanceId), ); const waitTill = configureWaitTillDate(this); diff --git a/packages/nodes-base/nodes/WhatsApp/tests/utils.test.ts b/packages/nodes-base/nodes/WhatsApp/tests/utils.test.ts index e881a18669..26359d1964 100644 --- a/packages/nodes-base/nodes/WhatsApp/tests/utils.test.ts +++ b/packages/nodes-base/nodes/WhatsApp/tests/utils.test.ts @@ -43,6 +43,7 @@ describe('createMessage', () => { mockSendAndWaitConfig, phoneID, recipientPhone, + '', ); expect(request).toEqual({ @@ -77,7 +78,12 @@ describe('createMessage', () => { ], }; - const request: IHttpRequestOptions = createMessage(singleOptionConfig, phoneID, recipientPhone); + const request: IHttpRequestOptions = createMessage( + singleOptionConfig, + phoneID, + recipientPhone, + '', + ); expect(request).toEqual({ baseURL: WHATSAPP_BASE_URL, diff --git a/packages/nodes-base/utils/sendAndWait/email-templates.ts b/packages/nodes-base/utils/sendAndWait/email-templates.ts index 1b0bc6153d..3860b98689 100644 --- a/packages/nodes-base/utils/sendAndWait/email-templates.ts +++ b/packages/nodes-base/utils/sendAndWait/email-templates.ts @@ -79,7 +79,11 @@ export const ACTION_RECORDED_PAGE = ` `; -export function createEmailBody(message: string, buttons: string, instanceId?: string) { +export function createEmailBodyWithN8nAttribution( + message: string, + buttons: string, + instanceId?: string, +) { const utm_campaign = instanceId ? `&utm_campaign=${instanceId}` : ''; const n8nWebsiteLink = `https://n8n.io/?utm_source=n8n-internal&utm_medium=send-and-wait${utm_campaign}`; return ` @@ -139,3 +143,52 @@ export function createEmailBody(message: string, buttons: string, instanceId?: s `; } + +export function createEmailBodyWithoutN8nAttribution(message: string, buttons: string) { + return ` + + + + + + + My form + + + + + + + +
+ + + + + + + +
+

${message}

+
+ ${buttons} +
+ + + + + + +
+ + +
+ + + + `; +} diff --git a/packages/nodes-base/utils/sendAndWait/utils.ts b/packages/nodes-base/utils/sendAndWait/utils.ts index f9083ea2f9..e61058be30 100644 --- a/packages/nodes-base/utils/sendAndWait/utils.ts +++ b/packages/nodes-base/utils/sendAndWait/utils.ts @@ -18,7 +18,8 @@ import { ACTION_RECORDED_PAGE, BUTTON_STYLE_PRIMARY, BUTTON_STYLE_SECONDARY, - createEmailBody, + createEmailBodyWithN8nAttribution, + createEmailBodyWithoutN8nAttribution, } from './email-templates'; import type { IEmail } from './interfaces'; import { formFieldsProperties } from '../../nodes/Form/Form.node'; @@ -30,6 +31,7 @@ export type SendAndWaitConfig = { message: string; url: string; options: Array<{ label: string; value: string; style: string }>; + appendAttribution?: boolean; }; type FormResponseTypeOptions = { @@ -57,6 +59,15 @@ const limitWaitTimeOption: INodeProperties = { ], }; +const appendAttributionOption: INodeProperties = { + displayName: 'Append n8n Attribution', + name: 'appendAttribution', + type: 'boolean', + default: true, + description: + 'Whether to include the phrase "This message was sent automatically with n8n" to the end of the message', +}; + // Operation Properties ---------------------------------------------------------- export function getSendAndWaitProperties( targetProperties: INodeProperties[], @@ -232,7 +243,7 @@ export function getSendAndWaitProperties( type: 'collection', placeholder: 'Add option', default: {}, - options: [limitWaitTimeOption], + options: [limitWaitTimeOption, appendAttributionOption], displayOptions: { show: { responseType: ['approval'], @@ -273,6 +284,7 @@ export function getSendAndWaitProperties( default: 'Submit', }, limitWaitTimeOption, + appendAttributionOption, ], displayOptions: { show: { @@ -456,11 +468,14 @@ export function getSendAndWaitConfig(context: IExecuteFunctions): SendAndWaitCon buttonDisapprovalStyle?: string; }; + const options = context.getNodeParameter('options', 0, {}); + const config: SendAndWaitConfig = { title: subject, message, url: `${resumeUrl}/${nodeId}`, options: [], + appendAttribution: options?.appendAttribution as boolean, }; const responseType = context.getNodeParameter('responseType', 0, 'approval') as string; @@ -525,14 +540,19 @@ export function createEmail(context: IExecuteFunctions) { for (const option of config.options) { buttons.push(createButton(config.url, option.label, option.value, option.style)); } - - const instanceId = context.getInstanceId(); + let emailBody: string; + if (config.appendAttribution !== false) { + const instanceId = context.getInstanceId(); + emailBody = createEmailBodyWithN8nAttribution(config.message, buttons.join('\n'), instanceId); + } else { + emailBody = createEmailBodyWithoutN8nAttribution(config.message, buttons.join('\n')); + } const email: IEmail = { to, subject: config.title, body: '', - htmlBody: createEmailBody(config.message, buttons.join('\n'), instanceId), + htmlBody: emailBody, }; return email;