Analyse quotidienne des matcheurs lanceurs vs frappeurs MLB (Google Sheets + Telegram)
Ceci est unMiscellaneous, Multimodal AIworkflow d'automatisation du domainecontenant 14 nœuds.Utilise principalement des nœuds comme Code, Telegram, HttpRequest, GoogleSheets, ScheduleTrigger. Analyse quotidienne des confrontations lanceurs vs frappeurs MLB avec Google Sheets et Telegram
- •Token Bot Telegram
- •Peut nécessiter les informations d'identification d'authentification de l'API cible
- •Informations d'identification Google Sheets API
Nœuds utilisés (14)
Catégorie
{
"meta": {
"instanceId": "4b9ebdcb82324c7ebd0d3194b3afce46ddeac81826241b95da802461b66d7743"
},
"nodes": [
{
"id": "9a8548ad-12f1-41a7-9226-c8d06806b7b2",
"name": "3. Extraire tous les ID de joueurs",
"type": "n8n-nodes-base.code",
"notes": "who will be playing today.",
"position": [
32,
-128
],
"parameters": {
"jsCode": "const allPlayerIds = new Set();\nconst allGames = items[0].json.dates[0]?.games || [];\n\nif (allGames.length === 0) {\n return [];\n}\n\n// Loop through all games to find every player\nfor (const game of allGames) {\n if (game.teams?.home?.probablePitcher?.id) {\n allPlayerIds.add(game.teams.home.probablePitcher.id);\n }\n if (game.teams?.away?.probablePitcher?.id) {\n allPlayerIds.add(game.teams.away.probablePitcher.id);\n }\n if (game.lineups && Array.isArray(game.lineups.homePlayers)) {\n for (const player of game.lineups.homePlayers) {\n if (player.id) allPlayerIds.add(player.id);\n }\n }\n if (game.lineups && Array.isArray(game.lineups.awayPlayers)) {\n for (const player of game.lineups.awayPlayers) {\n if (player.id) allPlayerIds.add(player.id);\n }\n }\n}\n\nif (allPlayerIds.size === 0) {\n return [];\n}\n\n// We pass the original game data THROUGH this node by adding it to the output\nconst output = items[0].json;\noutput.playerIdsString = Array.from(allPlayerIds).join(',');\nreturn [{ json: output }];"
},
"notesInFlow": true,
"typeVersion": 2
},
{
"id": "a0e5e8f0-e2fe-415e-92f4-cb4e29a6fd72",
"name": "4. Obtenir les statistiques des joueurs par lots",
"type": "n8n-nodes-base.httpRequest",
"position": [
256,
-128
],
"parameters": {
"url": "https://statsapi.mlb.com/api/v1/people",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "personIds",
"value": "={{$json.playerIdsString}}"
},
{
"name": "hydrate",
"value": "stats(group=[pitching,hitting,fielding],type=[season])"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "ff23a51f-3dc8-4b85-97b0-611e0bb6a304",
"name": "2. Obtenir les matchs quotidiens",
"type": "n8n-nodes-base.httpRequest",
"position": [
-176,
-128
],
"parameters": {
"url": "https://statsapi.mlb.com/api/v1/schedule",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "sportId",
"value": "1"
},
{
"name": "date",
"value": "={{ $now.toFormat('yyyy-MM-dd') }}"
},
{
"name": "hydrate",
"value": "probablePitcher,lineups"
}
]
}
},
"typeVersion": 4.1
},
{
"id": "32ccdb2c-0b9d-4af4-9590-c58b9902c31c",
"name": "5. Créer les lignes finales de duels",
"type": "n8n-nodes-base.code",
"notes": "game schedule data and merges it with the detailed player stats",
"position": [
496,
-128
],
"parameters": {
"jsCode": "const allMatchupRows = [];\n\n// Get player stats from the direct input of this node\nconst playerStatsData = items[0].json;\n\n// Get the original game data by looking back at the previous node\nconst scheduleNode = $('3. Extract All Player IDs').first();\nconst originalScheduleData = scheduleNode.json;\nconst games = originalScheduleData.dates[0]?.games || [];\n\nconst playersWithStats = playerStatsData.people || [];\n\nif (games.length === 0 || playersWithStats.length === 0) {\n return [];\n}\n\nconst statsMap = new Map(playersWithStats.map(p => [p.id, p]));\n\nfor (const game of games) {\n\n const gameStartTime = game.gameDate || 'N/A';\n\n // --- Home Pitcher vs Away Batters ---\n const homePitcherId = game.teams?.home?.probablePitcher?.id;\n const awayLineup = game.lineups?.awayPlayers;\n\n if (homePitcherId && Array.isArray(awayLineup)) {\n const pitcherData = statsMap.get(homePitcherId);\n if (pitcherData) {\n for (const batter of awayLineup) {\n const batterData = statsMap.get(batter.id);\n if (batterData) {\n const pitcherStats = pitcherData.stats?.find(s => s.group?.displayName === 'pitching')?.splits[0]?.stat || {};\n const batterStats = batterData.stats?.find(s => s.group?.displayName === 'hitting')?.splits[0]?.stat || {};\n allMatchupRows.push({\n gameStartTime: gameStartTime,\n gameDate: game.officialDate || 'N/A',\n pitcherName: pitcherData.fullName || 'N/A',\n pitcherTeam: game.teams?.home?.team?.name || 'N/A',\n pitcherThrows: pitcherData.pitchHand?.description || 'N/A',\n pitcherERA: pitcherStats.era,\n pitcherSO: pitcherStats.strikeOuts,\n opponent: game.teams?.away?.team?.name || 'N/A',\n batterName: batterData.fullName || 'N/A',\n batterPosition: batter.primaryPosition?.abbreviation || 'N/A',\n batterBats: batterData.batSide?.description || 'N/A',\n batterAVG: batterStats.avg,\n batterOPS: batterStats.ops,\n batterHR: batterStats.homeRuns,\n batterHits: batterStats.hits,\n batterRBI: batterStats.rbi,\n pitcherId: pitcherData.id, // ADDED: Pitcher ID\n batterId: batterData.id, // ADDED: Batter ID\n });\n }\n }\n }\n }\n\n // --- Away Pitcher vs Home Batters ---\n const awayPitcherId = game.teams?.away?.probablePitcher?.id;\n const homeLineup = game.lineups?.homePlayers;\n\n if (awayPitcherId && Array.isArray(homeLineup)) {\n const pitcherData = statsMap.get(awayPitcherId);\n if (pitcherData) {\n for (const batter of homeLineup) {\n const batterData = statsMap.get(batter.id);\n if (batterData) {\n const pitcherStats = pitcherData.stats?.find(s => s.group?.displayName === 'pitching')?.splits[0]?.stat || {};\n const batterStats = batterData.stats?.find(s => s.group?.displayName === 'hitting')?.splits[0]?.stat || {};\n allMatchupRows.push({\n gameStartTime: gameStartTime,\n gameDate: game.officialDate || 'N/A',\n pitcherName: pitcherData.fullName || 'N/A',\n pitcherTeam: game.teams?.away?.team?.name || 'N/A',\n pitcherThrows: pitcherData.pitchHand?.description || 'N/A',\n pitcherERA: pitcherStats.era,\n pitcherSO: pitcherStats.strikeOuts,\n opponent: game.teams?.home?.team?.name || 'N/A',\n batterName: batterData.fullName || 'N/A',\n batterPosition: batter.primaryPosition?.abbreviation || 'N/A',\n batterBats: batterData.batSide?.description || 'N/A',\n batterAVG: batterStats.avg,\n batterOPS: batterStats.ops,\n batterHR: batterStats.homeRuns,\n batterHits: batterStats.hits,\n batterRBI: batterStats.rbi,\n pitcherId: pitcherData.id, // ADDED: Pitcher ID\n batterId: batterData.id, // ADDED: Batter ID\n });\n }\n }\n }\n }\n}\n\nreturn allMatchupRows.map(row => ({ json: row }));"
},
"notesInFlow": true,
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "73bac962-fa64-45dc-86bd-671a17488992",
"name": "6. Filtrer pour les meilleurs duels",
"type": "n8n-nodes-base.code",
"notes": "Pandas to filter and sort before Sheets",
"position": [
736,
-128
],
"parameters": {
"jsCode": "// Convert to Eastern Time using proper timezone handling\nfunction convertToEasternTime(isoString) {\n\tconst date = new Date(isoString);\n\treturn date.toLocaleTimeString('en-US', {\n\t\thour: '2-digit',\n\t\tminute: '2-digit',\n\t\thour12: true,\n\t\ttimeZone: 'America/New_York'\n\t});\n}\n\nconst HEADER_ORDER = [\n\t\"gameDate\",\n\t\"gameStartTime\",\n\t\"pitcherId\",\n\t\"pitcherName\",\n\t\"pitcherTeam\",\n\t\"pitcherSO\",\n\t\"pitcherERA\",\n\t\"pitcherThrows\",\n\t\"batterBats\",\n\t\"opponent\",\n\t\"batterName\",\n\t\"batterAVG\",\n\t\"batterHR\",\n\t\"batterHits\",\n\t\"batterOPS\",\n\t\"batterRBI\",\n\t\"batterId\",\n\t\"batterPosition\"\n];\n\nconst allMatchups = items.map(item => item.json);\n\n// Filter valid rows\nconst valid = allMatchups.filter(m => {\n\tconst era = parseFloat(m.pitcherERA);\n\tconst ops = parseFloat(m.batterOPS);\n\treturn !isNaN(era) && era > 3.33 && !isNaN(ops) && m.gameStartTime;\n});\n\n// Parse and add helper fields\nvalid.forEach(m => {\n\tm.era = parseFloat(m.pitcherERA);\n\tm.ops = parseFloat(m.batterOPS);\n\tm.gameStartObj = new Date(m.gameStartTime);\n});\n\n// Step 1: Get top 9 unique pitchers with highest ERA\nconst seenPitchers = new Set();\nconst topPitchers = [];\n\nvalid\n\t.sort((a, b) => b.era - a.era)\n\t.forEach(m => {\n\t\tif (!seenPitchers.has(m.pitcherName)) {\n\t\t\tseenPitchers.add(m.pitcherName);\n\t\t\ttopPitchers.push(m.pitcherName);\n\t\t}\n\t});\n\nconst top9 = topPitchers.slice(0, 9);\n\n// Step 2: For each of these pitchers, get top 3 batters by OPS\nconst final = [];\n\ntop9.forEach(pitcherName => {\n\tconst matchups = valid.filter(m => m.pitcherName === pitcherName);\n\tconst seenBatters = new Set();\n\tconst topBatters = [];\n\n\tmatchups\n\t\t.sort((a, b) => b.ops - a.ops)\n\t\t.forEach(m => {\n\t\t\tif (!seenBatters.has(m.batterId)) {\n\t\t\t\tseenBatters.add(m.batterId);\n\t\t\t\ttopBatters.push(m);\n\t\t\t}\n\t\t});\n\n\tfinal.push(...topBatters.slice(0, 3));\n});\n\n// Final sort by game start time\nfinal.sort((a, b) => a.gameStartObj - b.gameStartObj);\n\n// Format time and construct output with locked header order\nconst output = final.map(m => {\n\tm.gameStartTime = convertToEasternTime(m.gameStartObj.toISOString());\n\tconst obj = {};\n\tHEADER_ORDER.forEach(key => obj[key] = m[key] ?? '');\n\treturn { json: obj };\n});\n\nreturn output;\n"
},
"notesInFlow": true,
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "7d9f19aa-a8a6-4f50-8462-0cad13de6e9c",
"name": "Note adhésive",
"type": "n8n-nodes-base.stickyNote",
"position": [
-480,
-368
],
"parameters": {
"color": 4,
"width": 2316,
"height": 500,
"content": "Hits"
},
"typeVersion": 1
},
{
"id": "38b3c276-1d1e-4ea5-8427-ddb9f6591bbe",
"name": "Ordre des colonnes",
"type": "n8n-nodes-base.code",
"position": [
944,
-128
],
"parameters": {
"jsCode": "const orderedKeys = [\n \"gameDate\",\n \"gameStartTime\",\n \"pitcherId\",\n \"pitcherName\",\n \"pitcherTeam\",\n \"pitcherSO\",\n \"pitcherERA\",\n \"pitcherThrows\",\n \"batterBats\",\n \"opponent\",\n \"batterName\",\n \"batterAVG\",\n \"batterHR\",\n \"batterHits\",\n \"batterOPS\",\n \"batterRBI\",\n \"batterId\",\n \"batterPosition\"\n];\n\n// fields that should explicitly be numbers\nconst numericKeys = [\n \"pitcherId\",\n \"pitcherSO\",\n \"pitcherERA\",\n \"batterAVG\",\n \"batterHR\",\n \"batterHits\",\n \"batterOPS\",\n \"batterRBI\",\n \"batterId\"\n];\n\n// 🔷 Helper: format to hh:mm AM/PM ET\nfunction toEasternTimeHHMM(value) {\n if (typeof value === 'string' && /^\\d{1,2}:\\d{2}/.test(value)) {\n return value.replace(/:00$/, ''); // clean up trailing :00 if it’s there\n }\n try {\n const utcDate = new Date(value);\n if (isNaN(utcDate.getTime())) return value;\n const options = {\n timeZone: 'America/New_York',\n hour: '2-digit',\n minute: '2-digit',\n hour12: true\n };\n return new Intl.DateTimeFormat('en-US', options).format(utcDate);\n } catch {\n return value;\n }\n}\n\n// Build new list\nconst enriched = items.map(item => {\n const json = { ...item.json };\n\n if (json.gameStartTime) {\n json.gameStartTime = toEasternTimeHHMM(json.gameStartTime);\n }\n\n const output = {};\n for (const key of orderedKeys) {\n let val = json[key] ?? \"\";\n if (numericKeys.includes(key) && val !== \"\") {\n val = Number(val);\n\n // Round batterAVG and batterOPS to .000\n if (key === \"batterAVG\" || key === \"batterOPS\") {\n val = Number(val.toFixed(3));\n }\n }\n output[key] = val;\n }\n\n return { json: output };\n});\n\n// Sort enriched list\nenriched.sort((a, b) => {\n const parseTime = (timeStr) => {\n const [time, meridian] = timeStr.split(' ');\n let [hours, minutes] = time.split(':').map(Number);\n if (meridian === 'PM' && hours !== 12) hours += 12;\n if (meridian === 'AM' && hours === 12) hours = 0;\n return hours * 60 + minutes; // total minutes since midnight\n };\n\n const aTime = parseTime(a.json.gameStartTime);\n const bTime = parseTime(b.json.gameStartTime);\n if (aTime < bTime) return -1;\n if (aTime > bTime) return 1;\n\n const aPitcher = Number(a.json.pitcherId) || 0;\n const bPitcher = Number(b.json.pitcherId) || 0;\n if (aPitcher < bPitcher) return -1;\n if (aPitcher > bPitcher) return 1;\n\n const aOpp = a.json.opponent || \"\";\n const bOpp = b.json.opponent || \"\";\n return aOpp.localeCompare(bOpp);\n});\n\n\nreturn enriched;\n"
},
"typeVersion": 2
},
{
"id": "09b74296-10fa-43a4-8fb4-65a017dd9f78",
"name": "9h Effacer",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-400,
-304
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
},
{
"triggerAtHour": 9,
"triggerAtMinute": 15
}
]
}
},
"typeVersion": 1.2
},
{
"id": "31005912-843f-4c2f-8614-c92c00404fb8",
"name": "11:02 - 8:02",
"type": "n8n-nodes-base.scheduleTrigger",
"notes": "02 11-20 * * *",
"position": [
-384,
-128
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "02 11-20 * * *"
}
]
}
},
"notesInFlow": true,
"typeVersion": 1
},
{
"id": "e7d4e4e6-09f1-4ce0-820c-a60cd795a858",
"name": "Notes : MLB Hits",
"type": "n8n-nodes-base.stickyNote",
"position": [
288,
-848
],
"parameters": {
"width": 752,
"height": 656,
"content": "MLB \"Hits\" Workflow — Overview\n• Pulls today's MLB schedule incl. probablePitcher + lineups (statsapi.mlb.com)\n• Batches season stats for all involved players\n• Builds pitcher vs. batter matchup rows\n• Filters: ERA > 3.33, take top 9 pitchers by ERA, then top 3 opposing batters by OPS each (27 rows)\n• Sorts by ET start time, writes to Google Sheets (Your Google SHeet → the tabName)\n• Sends Telegram message: Top 21 batters by OPS\n\nSetup\n1) Google Sheets OAuth2 → point to your Google Sheet → Tab name\n2) Telegram Bot cred + chatId\n3) Optional: tweak ERA/OPS thresholds or Top N in nodes 6 or 8.\n\nTriggers\n• \"11:02 – 8:02\" runs hourly at :02 (server time)\n• \"9am Clear\" clears the sheet at 09:00 & 09:15\n\nKey Nodes\n• 2. Get Daily Games — GET /schedule (date=today; hydrate=probablePitcher,lineups)\n• 3. Extract All Player IDs — collects personIds (pitchers + lineups)\n• 4. Get Batched Player Stats — GET /people?personIds=...&hydrate=stats(...)\n• 5. Create Final Matchup Rows — merges schedule + stats into pitcher↔batter rows\n• 6. Filter for Top Matchups — ERA/OPS filter; pick top 9×3; convert times to ET\n• Column Order — enforce column order, numeric typing, rounding (.000 for AVG/OPS)\n• 7. Update n8n-sheet — append/update by batterId\n• 8. 21 Hitters — composes Top 21 by OPS message\n• 9. Sends Telegram\n\nNotes\n• If lineups/schedule not ready yet, downstream may be empty (expected)\n• Keep node name \"3. Extract All Player IDs\" exact (used by $())\n• ET conversion is in code; cron uses server time"
},
"typeVersion": 1
},
{
"id": "6ef30ef8-0023-42bc-b322-2fffd650894b",
"name": "8. 21 Batteurs",
"type": "n8n-nodes-base.code",
"position": [
1408,
-128
],
"parameters": {
"jsCode": "// This code is for an n8n Code node.\n// It assumes the input from the preceding node (which now provides all 27 batters)\n// allows 'items' to contain all batter records.\n\nconsole.log(\"--- Code Node for Telegram Message Start ---\");\n\n// --- Input Processing (same as before to get all 27 batters) ---\nlet allBatterStats = [];\n\n// This block handles both scenarios:\n// 1. If \"Run Once for All Items\" is ON, 'items' contains all incoming records.\n// 2. If an \"Item Lists (Aggregate)\" node precedes this, 'items[0].json' will be the array.\nfor (const item of items) {\n // Scenario A: Item contains a single object (e.g., from a direct data source outputting individual items)\n if (item && typeof item.json === 'object' && item.json !== null && !Array.isArray(item.json)) {\n allBatterStats.push(item.json);\n }\n // Scenario B: Item.json is an array (e.g., from an Item Lists aggregate node, or if a source directly outputs a single array)\n else if (item && Array.isArray(item.json)) {\n allBatterStats.push(...item.json);\n }\n // Fallback: If the item itself (not its .json) is the object, or other unexpected structures\n else if (typeof item === 'object' && item !== null && item.batterName && item.batterHits) {\n allBatterStats.push(item);\n } else {\n console.warn(\"Skipping an input item with an unrecognized structure:\", JSON.stringify(item, null, 2));\n }\n}\n\nconsole.log(\"Total batter stats collected:\", allBatterStats.length);\n\n// Ensure batterHits is a number and filter out invalid entries.\nconst processedBatterStats = allBatterStats.map(batter => {\n const newBatter = { ...batter };\n newBatter.batterHits = typeof batter.batterHits === 'string'\n ? parseFloat(batter.batterHits)\n : batter.batterHits;\n if (isNaN(newBatter.batterHits)) {\n newBatter.batterHits = 0; // Default invalid numbers to 0\n }\n return newBatter;\n}).filter(batter =>\n typeof batter === 'object' &&\n batter !== null &&\n typeof batter.batterHits === 'number' &&\n typeof batter.batterName === 'string' &&\n batter.batterName.trim() !== ''\n);\n\nconsole.log(\"Number of valid and processed batter stats:\", processedBatterStats.length);\n\nif (processedBatterStats.length === 0) {\n console.warn(\"No valid batter stats found. Returning empty message.\");\n return [{ json: { text: \"No batter stats available to display.\" } }];\n}\n\n// Sort the stats by batterHits in descending order.\nprocessedBatterStats.sort((a, b) => b.batterOPS - a.batterOPS);\n\n// Get the top 21 batters.\nconst top21Batters = processedBatterStats.slice(0, 21);\n\nconsole.log(\"Number of top 21 batters selected:\", top21Batters.length);\n\n// --- Telegram Message Formatting ---\n\n// Start the message with a title. Using Markdown for bold.\nlet telegramMessage = \"⚾ **Top 21 Batters by OPS** ⚾\\n\\n\";\n\n// Add each batter to the message string.\ntop21Batters.forEach((batter, index) => {\n // Format each line using Markdown for name and hits.\n // Remember to escape any special Markdown characters in the data if necessary,\n // though typically names and numbers are safe.\n telegramMessage += `${index + 1}. **${batter.batterName}**: ${batter.batterOPS}\\n`;\n});\n\nconsole.log(\"Generated Telegram message length:\", telegramMessage.length);\n\n// --- Return as a single n8n item for the Telegram node ---\n// The Telegram node's 'Text' field will consume the 'text' property from this output.\nreturn [{\n json: {\n message: telegramMessage // This is the single string containing the entire message\n }\n}];"
},
"executeOnce": false,
"typeVersion": 2,
"alwaysOutputData": true
},
{
"id": "5d726f93-c7f4-42d6-b0a0-c0ff8abc159b",
"name": "Vider votre feuille",
"type": "n8n-nodes-base.googleSheets",
"position": [
-176,
-304
],
"parameters": {
"operation": "clear",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "",
"cachedResultUrl": "",
"cachedResultName": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "bL32JLOAfaCKsZaj",
"name": "Google Sheets account 2"
}
},
"typeVersion": 4.6
},
{
"id": "29d5c57d-4f31-4fcd-aea0-865341d60d9c",
"name": "7. Mettre à jour votre feuille",
"type": "n8n-nodes-base.googleSheets",
"onError": "continueErrorOutput",
"position": [
1168,
-128
],
"parameters": {
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": ""
},
"documentId": {
"__rl": true,
"mode": "list",
"value": ""
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "bL32JLOAfaCKsZaj",
"name": "Google Sheets account 2"
}
},
"notesInFlow": true,
"retryOnFail": true,
"typeVersion": 4,
"alwaysOutputData": true,
"waitBetweenTries": 5000
},
{
"id": "e9c4377c-93c9-4286-b567-e233e5e65014",
"name": "9. envoyerAuChatbotTelegram",
"type": "n8n-nodes-base.telegram",
"position": [
1600,
-128
],
"webhookId": "3f317a60-e451-4150-9b04-2f9e5427a22d",
"parameters": {
"text": "={{ $json.message }}",
"chatId": "createYourOwnOnTelegram@BOTFather",
"additionalFields": {
"appendAttribution": false
}
},
"credentials": {
"telegramApi": {
"id": "5HNLqEK3ZgiYkMmw",
"name": "Telegram account"
}
},
"executeOnce": true,
"typeVersion": 1.2
}
],
"pinData": {
"9am Clear": [
{
"Hour": "09",
"Year": "2025",
"Month": "August",
"Minute": "00",
"Second": "32",
"Timezone": "America/New_York (UTC-04:00)",
"timestamp": "2025-08-04T09:00:32.005-04:00",
"Day of week": "Monday",
"Day of month": "04",
"Readable date": "August 4th 2025, 9:00:32 am",
"Readable time": "9:00:32 am"
}
]
},
"connections": {
"09b74296-10fa-43a4-8fb4-65a017dd9f78": {
"main": [
[
{
"node": "5d726f93-c7f4-42d6-b0a0-c0ff8abc159b",
"type": "main",
"index": 0
}
]
]
},
"31005912-843f-4c2f-8614-c92c00404fb8": {
"main": [
[
{
"node": "ff23a51f-3dc8-4b85-97b0-611e0bb6a304",
"type": "main",
"index": 0
}
]
]
},
"38b3c276-1d1e-4ea5-8427-ddb9f6591bbe": {
"main": [
[
{
"node": "29d5c57d-4f31-4fcd-aea0-865341d60d9c",
"type": "main",
"index": 0
}
]
]
},
"6ef30ef8-0023-42bc-b322-2fffd650894b": {
"main": [
[
{
"node": "e9c4377c-93c9-4286-b567-e233e5e65014",
"type": "main",
"index": 0
}
]
]
},
"ff23a51f-3dc8-4b85-97b0-611e0bb6a304": {
"main": [
[
{
"node": "9a8548ad-12f1-41a7-9226-c8d06806b7b2",
"type": "main",
"index": 0
}
]
]
},
"29d5c57d-4f31-4fcd-aea0-865341d60d9c": {
"main": [
[
{
"node": "6ef30ef8-0023-42bc-b322-2fffd650894b",
"type": "main",
"index": 0
}
]
]
},
"9a8548ad-12f1-41a7-9226-c8d06806b7b2": {
"main": [
[
{
"node": "a0e5e8f0-e2fe-415e-92f4-cb4e29a6fd72",
"type": "main",
"index": 0
}
]
]
},
"a0e5e8f0-e2fe-415e-92f4-cb4e29a6fd72": {
"main": [
[
{
"node": "32ccdb2c-0b9d-4af4-9590-c58b9902c31c",
"type": "main",
"index": 0
}
]
]
},
"73bac962-fa64-45dc-86bd-671a17488992": {
"main": [
[
{
"node": "38b3c276-1d1e-4ea5-8427-ddb9f6591bbe",
"type": "main",
"index": 0
}
]
]
},
"32ccdb2c-0b9d-4af4-9590-c58b9902c31c": {
"main": [
[
{
"node": "73bac962-fa64-45dc-86bd-671a17488992",
"type": "main",
"index": 0
}
]
]
}
}
}Comment utiliser ce workflow ?
Copiez le code de configuration JSON ci-dessus, créez un nouveau workflow dans votre instance n8n et sélectionnez "Importer depuis le JSON", collez la configuration et modifiez les paramètres d'authentification selon vos besoins.
Dans quelles scénarios ce workflow est-il adapté ?
Intermédiaire - Divers, IA Multimodale
Est-ce payant ?
Ce workflow est entièrement gratuit et peut être utilisé directement. Veuillez noter que les services tiers utilisés dans le workflow (comme l'API OpenAI) peuvent nécessiter un paiement de votre part.
Workflows recommandés
TheStock
@thestockPartager ce workflow