{ "kind": "open-flow-workflow", "version": 1, "name": "Send a Website Screenshot to Telegram", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_d5efd11dbc7fe89a": { "name": "Capture a website", "kind": "start", "inputDefs": [] }, "n_c42207c5225594cf": { "inputs": { "url": { "kind": "value", "value": "https://n8n.io" }, "format": { "kind": "value", "value": "png" }, "fullPage": { "kind": "value", "value": true }, "viewportWidth": { "kind": "value", "value": 1024 }, "deviceScaleFactor": { "kind": "value", "value": 1 } }, "name": "Capture the full page", "kind": "task", "task": { "inputs": [ { "description": "The complete HTTP or HTTPS webpage URL to capture.", "jsonSchema": { "type": "string", "format": "uri", "description": "The complete HTTP or HTTPS webpage URL to capture." }, "nullable": false, "handle": "url" }, { "description": "The image format for the hosted screenshot.", "jsonSchema": { "type": "string", "enum": [ "jpg", "jpeg", "png", "gif", "webp" ], "description": "The image format for the hosted screenshot." }, "nullable": true, "handle": "format", "value": null }, { "description": "The image quality to use when the format is jpg.", "jsonSchema": { "type": "integer", "description": "The image quality to use when the format is jpg." }, "nullable": true, "handle": "quality", "value": null }, { "description": "Whether to capture the full scrollable page.", "jsonSchema": { "type": "boolean", "description": "Whether to capture the full scrollable page." }, "nullable": true, "handle": "fullPage", "value": null }, { "description": "The browser viewport width in pixels.", "jsonSchema": { "type": "integer", "description": "The browser viewport width in pixels." }, "nullable": true, "handle": "viewportWidth", "value": null }, { "description": "The browser viewport height in pixels.", "jsonSchema": { "type": "integer", "description": "The browser viewport height in pixels." }, "nullable": true, "handle": "viewportHeight", "value": null }, { "description": "The browser device pixel ratio.", "jsonSchema": { "type": "number", "minimum": 0.5, "maximum": 3, "description": "The browser device pixel ratio." }, "nullable": true, "handle": "deviceScaleFactor", "value": null }, { "description": "The lowercase ISO 3166-1 alpha-2 country code for the capture exit IP.", "jsonSchema": { "type": "string", "enum": [ "ae", "al", "am", "ar", "at", "au", "az", "ba", "bd", "be", "bg", "bh", "bn", "bo", "br", "bw", "by", "ca", "cg", "ch", "cl", "cm", "cn", "co", "cr", "cy", "cz", "de", "dk", "do", "dz", "ec", "ee", "eg", "es", "fi", "fr", "gb", "ge", "gh", "gr", "gt", "hk", "hr", "hu", "id", "ie", "il", "im", "in", "iq", "is", "it", "jm", "jo", "jp", "ke", "kg", "kh", "kr", "kw", "kz", "la", "li", "lk", "lt", "lu", "lv", "ma", "md", "mk", "mm", "mo", "mw", "mx", "my", "mz", "ng", "nl", "no", "nz", "om", "pa", "pe", "ph", "pk", "pl", "pr", "pt", "py", "qa", "ro", "rs", "ru", "sa", "sc", "se", "sg", "si", "sk", "sl", "th", "tj", "tm", "tn", "tr", "tw", "tz", "ua", "us", "uy", "uz", "ve", "vn", "za", "zm", "zw" ], "description": "The lowercase ISO 3166-1 alpha-2 country code for the capture exit IP." }, "nullable": true, "handle": "ipCountryCode", "value": null }, { "description": "The seconds to wait after page load before capture.", "jsonSchema": { "type": "integer", "minimum": 0, "maximum": 30, "description": "The seconds to wait after page load before capture." }, "nullable": true, "handle": "delay", "value": null }, { "description": "The navigation timeout in seconds.", "jsonSchema": { "type": "integer", "minimum": 5, "maximum": 60, "description": "The navigation timeout in seconds." }, "nullable": true, "handle": "timeout", "value": null }, { "description": "The browser lifecycle event to wait for before capture.", "jsonSchema": { "type": "string", "enum": [ "load", "domcontentloaded", "networkidle0", "networkidle2" ], "description": "The browser lifecycle event to wait for before capture." }, "nullable": true, "handle": "waitUntil", "value": null }, { "description": "Whether to attempt to hide cookie-consent banners.", "jsonSchema": { "type": "boolean", "description": "Whether to attempt to hide cookie-consent banners." }, "nullable": true, "handle": "blockCookieBanners", "value": null }, { "description": "Whether to attempt to block advertisements.", "jsonSchema": { "type": "boolean", "description": "Whether to attempt to block advertisements." }, "nullable": true, "handle": "blockAds", "value": null }, { "description": "Whether to attempt to hide chat widgets.", "jsonSchema": { "type": "boolean", "description": "Whether to attempt to hide chat widgets." }, "nullable": true, "handle": "blockChats", "value": null }, { "description": "The CSS selectors of elements to hide before capture.", "jsonSchema": { "type": "array", "items": { "type": "string", "maxLength": 255, "description": "A CSS selector to hide." }, "description": "The CSS selectors of elements to hide before capture." }, "nullable": true, "handle": "hideSelectors", "value": null }, { "description": "The custom CSS to inject before capture.", "jsonSchema": { "type": "string", "maxLength": 10000, "description": "The custom CSS to inject before capture." }, "nullable": true, "handle": "styles", "value": null }, { "description": "The output filename without an extension.", "jsonSchema": { "type": "string", "maxLength": 255, "description": "The output filename without an extension." }, "nullable": true, "handle": "attachmentName", "value": null } ], "outputs": [ { "description": "The public URL of the generated screenshot.", "jsonSchema": { "type": "string", "format": "uri", "description": "The public URL of the generated screenshot." }, "nullable": false, "handle": "url" } ], "name": "Capture the full page", "executor": { "kind": "connector", "action": "screenshotbase.take_screenshot" } } }, "n_80646ef113e12eb6": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_c42207c5225594cf", "output": "url" } ] } }, "name": "Validate the screenshot URL", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string", "format": "uri", "description": "The public URL of the generated screenshot." }, "nullable": false, "handle": "url" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "description": "The photo URL or existing Telegram file_id to send." }, "nullable": false, "handle": "photo" } ], "name": "Validate the screenshot URL", "moduleId": "n_e57829021f62b496", "capabilities": [ { "kind": "http", "hosts": [ "cache.screenshotbase.com", "cdn.screenshotbase.com" ] } ] } }, "n_877fba925f6ac8eb": { "inputs": { "chatId": { "kind": "value", "value": "YOUR_TELEGRAM_CHAT_ID" }, "photo": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_80646ef113e12eb6", "output": "photo" } ] } }, "name": "Send the screenshot to Telegram", "kind": "task", "task": { "inputs": [ { "description": "The target Telegram chat ID or channel username.", "jsonSchema": { "anyOf": [ { "type": "integer", "description": "A union variant." }, { "type": "string", "pattern": "^-?\\d+$", "description": "A numeric Telegram chat identifier encoded as a string." }, { "type": "string", "pattern": "^@[A-Za-z0-9_]+$", "description": "A Telegram @username that identifies a public chat or channel." } ], "description": "The target Telegram chat ID or channel username." }, "nullable": false, "handle": "chatId" }, { "description": "The photo URL or existing Telegram file_id to send.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The photo URL or existing Telegram file_id to send." }, "nullable": false, "handle": "photo" }, { "description": "The caption for the photo.", "jsonSchema": { "type": "string", "maxLength": 1024, "description": "The caption for the photo." }, "nullable": true, "handle": "caption", "value": null }, { "description": "The parse mode used for message entities.", "jsonSchema": { "type": "string", "enum": [ "Markdown", "MarkdownV2", "HTML" ], "description": "The parse mode used for message entities." }, "nullable": true, "handle": "parseMode", "value": null }, { "description": "Whether to send the photo silently.", "jsonSchema": { "type": "boolean", "description": "Whether to send the photo silently." }, "nullable": true, "handle": "disableNotification", "value": null }, { "description": "Whether to protect the photo from forwarding and saving.", "jsonSchema": { "type": "boolean", "description": "Whether to protect the photo from forwarding and saving." }, "nullable": true, "handle": "protectContent", "value": null }, { "description": "The forum topic ID for the target message thread.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The forum topic ID for the target message thread." }, "nullable": true, "handle": "messageThreadId", "value": null }, { "description": "The message ID to reply to.", "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The message ID to reply to." }, "nullable": true, "handle": "replyToMessageId", "value": null } ], "outputs": [ { "description": "The unique message identifier inside the chat.", "jsonSchema": { "type": "integer", "description": "The unique message identifier inside the chat." }, "nullable": false, "handle": "messageId" }, { "description": "The Unix timestamp when the message was sent.", "jsonSchema": { "type": "integer", "description": "The Unix timestamp when the message was sent." }, "nullable": false, "handle": "date" }, { "description": "The chat the message belongs to.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The unique identifier of the chat." }, "type": { "type": "string", "description": "The chat type, such as private, group, supergroup, or channel." }, "title": { "type": "string", "description": "The chat title for groups, supergroups, and channels." }, "username": { "type": "string", "description": "The public username of the chat, when available." }, "firstName": { "type": "string", "description": "The first name for a private chat counterpart." }, "lastName": { "type": "string", "description": "The last name for a private chat counterpart." }, "isForum": { "type": "boolean", "description": "Whether the supergroup chat is a forum." } }, "required": [ "id", "type" ], "additionalProperties": false, "description": "The chat the message belongs to." }, "nullable": false, "handle": "chat" }, { "description": "The sender of the message.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The unique identifier of the user or bot." }, "isBot": { "type": "boolean", "description": "Whether this account is a bot." }, "firstName": { "type": "string", "description": "The first name of the user or bot." }, "username": { "type": "string", "description": "The username of the user or bot." }, "languageCode": { "type": "string", "description": "The IETF language tag of the user." }, "canJoinGroups": { "type": "boolean", "description": "Whether the bot can be invited to groups." }, "canReadAllGroupMessages": { "type": "boolean", "description": "Whether privacy mode is disabled for the bot." }, "supportsInlineQueries": { "type": "boolean", "description": "Whether the bot supports inline queries." }, "canConnectToBusiness": { "type": "boolean", "description": "Whether the bot can be connected to a Telegram Business account." }, "hasMainWebApp": { "type": "boolean", "description": "Whether the bot has a main Web App configured." } }, "required": [ "id", "isBot", "firstName" ], "additionalProperties": false, "description": "The sender of the message." }, "nullable": true, "handle": "from" }, { "description": "The chat on behalf of which the message was sent.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The unique identifier of the chat." }, "type": { "type": "string", "description": "The chat type, such as private, group, supergroup, or channel." }, "title": { "type": "string", "description": "The chat title for groups, supergroups, and channels." }, "username": { "type": "string", "description": "The public username of the chat, when available." }, "firstName": { "type": "string", "description": "The first name for a private chat counterpart." }, "lastName": { "type": "string", "description": "The last name for a private chat counterpart." }, "isForum": { "type": "boolean", "description": "Whether the supergroup chat is a forum." } }, "required": [ "id", "type" ], "additionalProperties": false, "description": "The chat on behalf of which the message was sent." }, "nullable": true, "handle": "senderChat" }, { "description": "The UTF-8 text of the message, for text messages.", "jsonSchema": { "type": "string", "description": "The UTF-8 text of the message, for text messages." }, "nullable": true, "handle": "text" }, { "description": "The caption for media messages.", "jsonSchema": { "type": "string", "description": "The caption for media messages." }, "nullable": true, "handle": "caption" }, { "description": "The available sizes for an attached photo.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "fileId": { "type": "string", "description": "The file identifier used to download or reuse the photo." }, "fileUniqueId": { "type": "string", "description": "The stable unique identifier of the photo." }, "width": { "type": "integer", "description": "The photo width in pixels." }, "height": { "type": "integer", "description": "The photo height in pixels." }, "fileSize": { "type": "integer", "description": "The photo file size in bytes." } }, "required": [ "fileId", "fileUniqueId", "width", "height" ], "additionalProperties": false, "description": "A Telegram photo size record." }, "description": "The available sizes for an attached photo." }, "nullable": true, "handle": "photo" }, { "description": "The attached document, when present.", "jsonSchema": { "type": "object", "properties": { "fileId": { "type": "string", "description": "The file identifier used to download or reuse the document." }, "fileUniqueId": { "type": "string", "description": "The stable unique identifier of the document." }, "fileName": { "type": "string", "description": "The original filename of the document." }, "mimeType": { "type": "string", "description": "The MIME type of the document." }, "fileSize": { "type": "integer", "description": "The document size in bytes." } }, "required": [ "fileId", "fileUniqueId" ], "additionalProperties": false, "description": "The attached document, when present." }, "nullable": true, "handle": "document" }, { "description": "The attached location, when present.", "jsonSchema": { "type": "object", "properties": { "latitude": { "type": "number", "description": "The latitude of the location." }, "longitude": { "type": "number", "description": "The longitude of the location." }, "horizontalAccuracy": { "type": "number", "description": "The radius of uncertainty for the location, in meters." }, "livePeriod": { "type": "integer", "description": "The live location update period in seconds." }, "heading": { "type": "integer", "description": "The direction in which the user is moving, in degrees." }, "proximityAlertRadius": { "type": "integer", "description": "The distance in meters for proximity alerts." } }, "required": [ "latitude", "longitude" ], "additionalProperties": false, "description": "The attached location, when present." }, "nullable": true, "handle": "location" }, { "description": "The attached poll, when present.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "string", "description": "The unique identifier of the poll." }, "question": { "type": "string", "description": "The poll question text." }, "options": { "type": "array", "items": { "type": "object", "properties": { "text": { "type": "string", "description": "The text of the poll option." }, "voterCount": { "type": "integer", "description": "The number of votes received by this option." } }, "required": [ "text", "voterCount" ], "additionalProperties": false, "description": "A Telegram poll option." }, "description": "The options available in the poll." }, "totalVoterCount": { "type": "integer", "description": "The total number of users that voted." }, "isClosed": { "type": "boolean", "description": "Whether the poll is closed." }, "isAnonymous": { "type": "boolean", "description": "Whether the poll is anonymous." }, "type": { "type": "string", "enum": [ "regular", "quiz" ], "description": "The type of the poll." }, "allowsMultipleAnswers": { "type": "boolean", "description": "Whether multiple answers are allowed." }, "closeDate": { "type": "integer", "description": "The Unix timestamp when the poll closes." }, "openPeriod": { "type": "integer", "description": "The amount of time in seconds the poll stays open." }, "explanation": { "type": "string", "description": "The explanation text shown for quiz polls." }, "correctOptionId": { "type": "integer", "description": "The index of the correct option for quiz polls." } }, "required": [ "id", "question", "options", "totalVoterCount", "isClosed", "isAnonymous", "type", "allowsMultipleAnswers" ], "additionalProperties": false, "description": "The attached poll, when present." }, "nullable": true, "handle": "poll" }, { "description": "The message entities parsed from the text.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Schema for anyObject." }, "description": "The message entities parsed from the text." }, "nullable": true, "handle": "entities" }, { "description": "The message entities parsed from the caption.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Schema for anyObject." }, "description": "The message entities parsed from the caption." }, "nullable": true, "handle": "captionEntities" }, { "description": "The Unix timestamp of the original forwarded message.", "jsonSchema": { "type": "integer", "description": "The Unix timestamp of the original forwarded message." }, "nullable": true, "handle": "forwardDate" }, { "description": "The original sender of a forwarded message.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The unique identifier of the user or bot." }, "isBot": { "type": "boolean", "description": "Whether this account is a bot." }, "firstName": { "type": "string", "description": "The first name of the user or bot." }, "username": { "type": "string", "description": "The username of the user or bot." }, "languageCode": { "type": "string", "description": "The IETF language tag of the user." }, "canJoinGroups": { "type": "boolean", "description": "Whether the bot can be invited to groups." }, "canReadAllGroupMessages": { "type": "boolean", "description": "Whether privacy mode is disabled for the bot." }, "supportsInlineQueries": { "type": "boolean", "description": "Whether the bot supports inline queries." }, "canConnectToBusiness": { "type": "boolean", "description": "Whether the bot can be connected to a Telegram Business account." }, "hasMainWebApp": { "type": "boolean", "description": "Whether the bot has a main Web App configured." } }, "required": [ "id", "isBot", "firstName" ], "additionalProperties": false, "description": "The original sender of a forwarded message." }, "nullable": true, "handle": "forwardFrom" }, { "description": "The original chat of a forwarded message sent on behalf of a chat.", "jsonSchema": { "type": "object", "properties": { "id": { "type": "integer", "description": "The unique identifier of the chat." }, "type": { "type": "string", "description": "The chat type, such as private, group, supergroup, or channel." }, "title": { "type": "string", "description": "The chat title for groups, supergroups, and channels." }, "username": { "type": "string", "description": "The public username of the chat, when available." }, "firstName": { "type": "string", "description": "The first name for a private chat counterpart." }, "lastName": { "type": "string", "description": "The last name for a private chat counterpart." }, "isForum": { "type": "boolean", "description": "Whether the supergroup chat is a forum." } }, "required": [ "id", "type" ], "additionalProperties": false, "description": "The original chat of a forwarded message sent on behalf of a chat." }, "nullable": true, "handle": "forwardFromChat" }, { "description": "The original message ID inside the source channel.", "jsonSchema": { "type": "integer", "description": "The original message ID inside the source channel." }, "nullable": true, "handle": "forwardFromMessageId" }, { "description": "The original author signature for a forwarded post.", "jsonSchema": { "type": "string", "description": "The original author signature for a forwarded post." }, "nullable": true, "handle": "forwardSignature" }, { "description": "The display name of a forwarded sender who disallows linking.", "jsonSchema": { "type": "string", "description": "The display name of a forwarded sender who disallows linking." }, "nullable": true, "handle": "forwardSenderName" }, { "description": "The link preview options attached to the message.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The link preview options attached to the message." }, "nullable": true, "handle": "linkPreviewOptions" } ], "name": "Send the screenshot to Telegram", "executor": { "kind": "connector", "action": "telegram.send_photo" } } } }, "edges": [ { "source": "n_d5efd11dbc7fe89a", "target": "n_c42207c5225594cf" }, { "source": "n_c42207c5225594cf", "target": "n_80646ef113e12eb6" }, { "source": "n_80646ef113e12eb6", "target": "n_877fba925f6ac8eb" } ] } }, "modules": { "n_e57829021f62b496": { "name": "Validate the screenshot URL", "imports": [], "source": " function validUrl(value) {\n if (typeof value !== 'string' || /[\\s\\x00-\\x1f\\x7f\\\\]/.test(value) || /%(?![0-9a-f]{2})/i.test(value)) return false;\n const match = /^https?:\\/\\/([^/?#]+)(?:[/?#].*)?$/i.exec(value);\n if (!match || match[1].includes('@')) return false;\n const authority = match[1];\n let host, port;\n if (authority.startsWith('[')) {\n const parts = /^\\[([0-9a-f:]+)\\](?::(\\d+))?$/i.exec(authority);\n if (!parts) return false;\n host = parts[1]; port = parts[2];\n const halves = host.split('::');\n if (halves.length > 2) return false;\n const groups = halves.flatMap(part => part ? part.split(':') : []);\n if (groups.some(group => !/^[0-9a-f]{1,4}$/i.test(group))) return false;\n if (halves.length === 1 ? groups.length !== 8 : groups.length >= 8) return false;\n } else {\n const parts = /^([^:]+)(?::(\\d+))?$/.exec(authority);\n if (!parts) return false;\n host = parts[1]; port = parts[2];\n if (host.length > 253) return false;\n const labels = host.replace(/\\.$/, '').split('.');\n if (!labels.every(label => label.length > 0 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(label))) return false;\n if (labels.every(label => /^\\d+$/.test(label)) && (labels.length !== 4 || labels.some(label => Number(label) > 255))) return false;\n }\n return port === undefined || (port.length <= 5 && Number(port) <= 65535);\n }\n\nfunction checkPng(bytes){if(!bytes||bytes.length<24)throw Error('Screenshot PNG header is incomplete');const sig=[137,80,78,71,13,10,26,10];if(sig.some((x,i)=>bytes[i]!==x)||bytes[8]!==0||bytes[9]!==0||bytes[10]!==0||bytes[11]!==13||String.fromCharCode(...bytes.slice(12,16))!=='IHDR')throw Error('Screenshot is not a PNG with an IHDR header');const u=i=>bytes[i]*16777216+bytes[i+1]*65536+bytes[i+2]*256+bytes[i+3],width=u(16),height=u(20);if(width!==1024||height<1||height>=20000)throw Error('Screenshot dimensions may indicate truncation or scaling');return true;}\n\nexport default async function photo({url},context){if(!validUrl(url))throw Error('Screenshot service returned no usable public image URL');let parsed;try{parsed=new URL(url)}catch{throw Error('Invalid screenshot URL')}if(parsed.protocol!=='https:'||parsed.port!==''||!['cache.screenshotbase.com','cdn.screenshotbase.com'].includes(parsed.hostname))throw Error('Screenshot host is not an allowed Screenshotbase CDN');let response;try{response=await context.fetch(url,{method:'GET',headers:{Range:'bytes=0-23'}})}catch{throw Error('Screenshot header request failed')}if(response.status!==200&&response.status!==206)throw Error('Screenshot header request failed');const bytes=new Uint8Array(await response.arrayBuffer());checkPng(bytes);return{photo:url};}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }