{ "kind": "open-flow-workflow", "version": 1, "name": "Reply with a Random Cocktail Photo", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_ecd26f535429a108": { "name": "Receive a Telegram message", "kind": "integration", "config": { "updates": { "kind": "value", "value": [ "message" ] }, "chatIds": { "kind": "value", "value": [] }, "userIds": { "kind": "value", "value": [] }, "dropPendingUpdates": { "kind": "value", "value": false } }, "definition": { "configInputs": [ { "description": "Only accept updates from these numeric Telegram chat IDs. Empty means every chat.", "jsonSchema": { "type": "array", "items": { "type": "string", "pattern": "^-?[0-9]{1,20}$" }, "maxItems": 100, "uniqueItems": true }, "nullable": false, "handle": "chatIds", "value": [] }, { "description": "Discard updates queued before the webhook is created or resumed.", "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "dropPendingUpdates", "value": true }, { "description": "Telegram update types that trigger a Run. Use * for the Telegram default set.", "jsonSchema": { "type": "array", "items": { "enum": [ "*", "message", "edited_message", "channel_post", "edited_channel_post", "business_connection", "business_message", "edited_business_message", "deleted_business_messages", "guest_message", "message_reaction", "message_reaction_count", "inline_query", "chosen_inline_result", "callback_query", "shipping_query", "pre_checkout_query", "purchased_paid_media", "poll", "poll_answer", "my_chat_member", "chat_member", "chat_join_request", "chat_boost", "removed_chat_boost", "managed_bot", "subscription" ] }, "minItems": 1, "uniqueItems": true }, "nullable": false, "handle": "updates" }, { "description": "Only accept updates from these numeric Telegram user IDs. Empty means every user.", "jsonSchema": { "type": "array", "items": { "type": "string", "pattern": "^[0-9]{1,20}$" }, "maxItems": 100, "uniqueItems": true }, "nullable": false, "handle": "userIds", "value": [] } ], "definitionVersion": 2, "description": "Triggers when the connected Telegram bot receives a selected update.", "displayName": "Bot Update", "key": "telegram.on_update", "name": "on_update", "outputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "deliveryId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "event" } ], "provider": "telegram", "type": "integration", "endpoint": { "body": { "allowArray": false, "allowEmpty": false, "formats": [ "json" ] }, "methods": [ "POST" ], "successStatus": 200 } } }, "n_ae89002c94fb8c18": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ecd26f535429a108", "output": "body" } ] } }, "name": "Identify the reply", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "body" } ], "outputs": [ { "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" }, { "jsonSchema": { "type": "integer", "exclusiveMinimum": 0, "description": "The message ID to reply to." }, "nullable": true, "handle": "replyToMessageId" } ], "name": "Identify the reply", "moduleId": "n_8ad9e2552f28fb68", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_4685b4330a8c9e64": { "inputs": { "initialUrl": { "kind": "value", "value": "https://www.thecocktaildb.com/api/json/v1/1/random.php" }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_28d309c81e738410", "output": "state" } ] } }, "name": "Prepare the cocktail API URL", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "initialUrl" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" } ], "name": "Prepare the cocktail API URL", "moduleId": "n_a123cc1098a976f2", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_3c391217f22e3cf3": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4685b4330a8c9e64", "output": "url" } ] } }, "name": "Get a random cocktail", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" } ], "name": "Get a random cocktail", "executor": { "method": "GET", "query": { "mode": "fields", "names": [] }, "headers": { "mode": "fields", "names": [] }, "bodyFormat": "none", "authentication": { "type": "none" }, "responseFormat": "text", "statusPolicy": "all", "kind": "http-request" } } }, "n_28d309c81e738410": { "inputs": { "url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_4685b4330a8c9e64", "output": "url" } ] }, "statusCode": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3c391217f22e3cf3", "output": "statusCode" } ] }, "headers": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3c391217f22e3cf3", "output": "headers" } ] }, "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_3c391217f22e3cf3", "output": "body" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_28d309c81e738410", "output": "state" } ] } }, "name": "Select the cocktail photo", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "url" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "statusCode" }, { "jsonSchema": { "type": "object", "additionalProperties": { "type": "string" } }, "nullable": false, "handle": "headers" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "body" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "redirect" }, { "jsonSchema": { "type": "string", "minLength": 1, "description": "The photo URL or existing Telegram file_id to send." }, "nullable": false, "handle": "photo" }, { "jsonSchema": { "type": "string", "maxLength": 1024, "description": "The caption for the photo." }, "nullable": true, "handle": "caption" } ], "name": "Select the cocktail photo", "moduleId": "n_a04832be7ff81b91", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_57488ae430310395": { "inputs": {}, "name": "Follow the redirect?", "kind": "condition", "matchMode": "first", "cases": [ { "output": "follow", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_28d309c81e738410", "output": "redirect" } }, "operator": "isTrue" } ] } ] } ] }, "n_c03217036741ba3e": { "inputs": { "chatId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ae89002c94fb8c18", "output": "chatId" } ] }, "replyToMessageId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_ae89002c94fb8c18", "output": "replyToMessageId" } ] }, "photo": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_28d309c81e738410", "output": "photo" } ] }, "caption": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_28d309c81e738410", "output": "caption" } ] } }, "name": "Reply with the cocktail photo", "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": "Reply with the cocktail photo", "executor": { "kind": "connector", "action": "telegram.send_photo" } } } }, "edges": [ { "source": "n_ecd26f535429a108", "target": "n_ae89002c94fb8c18" }, { "source": "n_ae89002c94fb8c18", "target": "n_4685b4330a8c9e64" }, { "source": "n_4685b4330a8c9e64", "target": "n_3c391217f22e3cf3" }, { "source": "n_3c391217f22e3cf3", "target": "n_28d309c81e738410" }, { "source": "n_28d309c81e738410", "target": "n_57488ae430310395" }, { "source": "n_57488ae430310395", "target": "n_4685b4330a8c9e64", "sourceHandle": "follow" }, { "source": "n_57488ae430310395", "target": "n_c03217036741ba3e", "sourceHandle": "otherwise" } ] } }, "modules": { "n_8ad9e2552f28fb68": { "name": "Identify the reply", "imports": [], "source": "export default function identity({body}){const message=body?.message,id=message?.chat?.id,reply=message?.message_id;if(!Number.isSafeInteger(id)||id===0||!Number.isSafeInteger(reply)||reply<=0)throw Error('Telegram message has no valid chat or message ID');return{chatId:String(id),replyToMessageId:reply};}\n" }, "n_a123cc1098a976f2": { "name": "Prepare the cocktail API URL", "imports": [], "source": "export default function hop({initialUrl,previousState}) {const candidate=previousState?.nextUrl||initialUrl;let url;try{url=new URL(candidate)}catch{throw Error('Configure a valid HTTP or HTTPS target URL')}if(!['http:','https:'].includes(url.protocol)||url.username||url.password)throw Error('Configure an HTTP or HTTPS URL without credentials');return{url:url.toString()};}\n" }, "n_a04832be7ff81b91": { "name": "Select the cocktail photo", "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\nexport default function check({url,statusCode,headers,body,previousState}){const hops=previousState?.hops??0;if(!Number.isInteger(statusCode)||statusCode<100||statusCode>599||!Number.isInteger(hops)||hops<0)throw Error('Invalid response');if(statusCode>=300&&statusCode<400&&typeof headers?.location==='string'&&headers.location){if(hops>=21)throw Error('Too many redirects');let target;try{target=new URL(headers.location,url)}catch{throw Error('Invalid redirect')}if(!['http:','https:'].includes(target.protocol)||target.username||target.password)throw Error('Invalid redirect');return{state:{hops:hops+1,nextUrl:target.toString()},redirect:true,photo:'https://example.invalid/redirect',caption:''}}if(statusCode<200||statusCode>=300)throw Error('Cocktail API request failed');let data;try{data=JSON.parse(body)}catch{throw Error('Invalid cocktail JSON')}const drink=data?.drinks?.[0];if(typeof drink?.strDrink!=='string'||!drink.strDrink||typeof drink.strDrinkThumb!=='string'||!validUrl(drink.strDrinkThumb))throw Error('Missing cocktail name or photo');if(drink.strDrink.length>1024)throw Error('Cocktail caption exceeds Telegram limit');return{state:{hops,nextUrl:''},redirect:false,photo:drink.strDrinkThumb,caption:drink.strDrink};}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }