AutomationFlowsAI & RAG › Automated Topic Content Generator with Gemini & Drive

Automated Topic Content Generator with Gemini & Drive

Original n8n title: Tgm — Topic Engine V3 (auto-recyclage + Fal.ai + Google Drive)

TGM — Topic Engine v3 (Auto-Recyclage + fal.ai + Google Drive). Uses httpRequest, googleGemini, googleDrive, telegram. Scheduled trigger; 15 nodes.

Cron / scheduled trigger★★★★☆ complexityAI-powered15 nodesHTTP RequestGoogle GeminiGoogle DriveTelegram
AI & RAG Trigger: Cron / scheduled Nodes: 15 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Google Drive → Googlegemini recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "TGM \u2014 Topic Engine v3 (Auto-Recyclage + fal.ai + Google Drive)",
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "nodes": [
    {
      "id": "eab6eae6-b921-4b63-ab47-88160668da11",
      "name": "Schedule \u2014 Lun/Mer/Ven 8h",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        912,
        -368
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1,3,5"
            }
          ]
        }
      }
    },
    {
      "id": "ee9dcfdf-6937-4e35-9537-3c3c688840eb",
      "name": "Code \u2014 D\u00e9termine angle du jour",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        -208
      ],
      "parameters": {
        "jsCode": "// Associe chaque jour de la semaine \u00e0 un angle \u00e9ditorial\n// Lundi=money, Mercredi=tools, Vendredi=pme\nconst DAY_TO_ANGLE = { 1: 'money', 3: 'tools', 5: 'pme' };\nconst DAY_LABELS = ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'];\nconst now = new Date();\nconst jour = now.getDay();\nconst angle = DAY_TO_ANGLE[jour];\nif (!angle) throw new Error('Pas de publication pr\u00e9vue aujourd\\'hui (jour ' + jour + ')');\nreturn [{ json: { angle, jour: DAY_LABELS[jour], date: now.toISOString().split('T')[0] } }];"
      }
    },
    {
      "id": "aec8765d-e009-4927-9bf8-6d1652eecc06",
      "name": "Supabase \u2014 SELECT sujets disponibles",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        912,
        -80
      ],
      "parameters": {
        "url": "=https://YOUR_SUPABASE_URL/rest/v1/tgm_carousel_history",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "statut",
              "value": "eq.disponible"
            },
            {
              "name": "angle",
              "value": "={{ $json.angle }}"
            },
            {
              "name": "select",
              "value": "id,sujet,angle,format,hook"
            },
            {
              "name": "order",
              "value": "created_at.asc"
            },
            {
              "name": "limit",
              "value": "20"
            }
          ]
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "67b49c72-e7ed-4fcc-a849-e9037533cd76",
      "name": "Code \u2014 Choisit sujet (avec signal recyclage)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        912,
        64
      ],
      "parameters": {
        "jsCode": "const sujets = $input.first().json;\nconst angleData = $('Code \u2014 D\u00e9termine angle du jour').item.json;\n\nconst FORMAT_PROMPTS = {\n  'Erreur + Solution': 'Slide 1 = probl\u00e8me choquant. Slides 2-6 alternent erreur courte et solution courte. Slide 7 = r\u00e8gle d\\'or. Slide 8 = CTA.',\n  'Avant / Apr\u00e8s': 'Slide 1 = situation AVANT. Slide 2 = APR\u00c8S. Slides 3-7 = \u00e9tapes. Slide 8 = CTA.',\n  'Chiffres qui choquent': 'Slide 1 = chiffre le plus impactant. Slides 2-6 = 1 chiffre + explication. Slide 7 = signification. Slide 8 = CTA.',\n  'Checklist': 'Slide 1 = promesse. Slides 2-7 = 1 item actionnable. Slide 8 = CTA.',\n  'Tutoriel': 'Slide 1 = r\u00e9sultat final. Slides 2-7 = \u00e9tapes num\u00e9rot\u00e9es. Slide 8 = CTA.',\n  'Concept simplifi\u00e9': 'Slide 1 = d\u00e9finition. Slide 2 = probl\u00e8me r\u00e9solu. Slides 3-5 = cas concrets. Slide 6 = quand NE PAS utiliser. Slide 7 = r\u00e9sum\u00e9. Slide 8 = CTA.'\n};\n\nif (Array.isArray(sujets) && sujets.length > 0) {\n  const sujet = sujets[Math.floor(Math.random() * sujets.length)];\n  return [{ json: {\n    sujet_id: sujet.id,\n    sujet: sujet.sujet,\n    hook: sujet.hook || '',\n    angle: sujet.angle,\n    format: sujet.format,\n    format_instructions: FORMAT_PROMPTS[sujet.format] || '',\n    jour: angleData.jour,\n    date: angleData.date,\n    nb_slides: 8,\n    nb_disponibles: sujets.length,\n    cycle_reset: false\n  }}];\n}\n\n// Tous les sujets \u00e9puis\u00e9s \u2192 signal recyclage automatique\nreturn [{ json: {\n  sujet_id: null, sujet: null,\n  angle: angleData.angle, jour: angleData.jour, date: angleData.date,\n  nb_slides: 8, nb_disponibles: 0, cycle_reset: true\n}}];"
      }
    },
    {
      "id": "195da959-fa3c-41ec-b5fd-8e93c3d5c296",
      "name": "IF \u2014 Recyclage n\u00e9cessaire ?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        912,
        192
      ],
      "parameters": {
        "conditions": {
          "conditions": [
            {
              "leftValue": "={{ $json.cycle_reset }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true"
              }
            }
          ]
        }
      }
    },
    {
      "id": "73a711c6-7f0d-41e7-a8c1-51b854b5ef6f",
      "name": "Supabase \u2014 RESET publi\u00e9s \u2192 disponible",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        736,
        336
      ],
      "parameters": {
        "method": "PATCH",
        "url": "=https://YOUR_SUPABASE_URL/rest/v1/tgm_carousel_history?statut=eq.publi\u00e9&angle=eq.{{ $('Code \u2014 Choisit sujet (avec signal recyclage)').item.json.angle }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Prefer",
              "value": "return=minimal"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\"statut\": \"disponible\", \"date_publication\": null}"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "afcb9d97-3c6f-4f50-9dfe-c879b9fef42a",
      "name": "Gemini \u2014 G\u00e9n\u00e9rer contenu slides",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "typeVersion": 1.1,
      "position": [
        1728,
        -448
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "models/gemini-2.5-flash",
          "mode": "list"
        },
        "messages": {
          "values": [
            {
              "content": "Tu es directeur \u00e9ditorial d'une agence B2B d'automatisation francophone. G\u00e9n\u00e8re un carrousel LinkedIn \u00e9ducatif en JSON strict."
            }
          ]
        },
        "options": {
          "systemMessage": "R\u00e9ponds UNIQUEMENT avec un objet JSON valide. Structure : { slides: [{numero, titre, corps, prompt_image}], post_linkedin }. 8 slides max. Texte slide = MAX 15 mots.",
          "maxTokens": 4096
        }
      },
      "credentials": {
        "googleGeminiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "be049150-cdb4-4634-8ba4-86e98a5d592f",
      "name": "Code \u2014 Parse r\u00e9ponse Gemini",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1792,
        -256
      ],
      "parameters": {
        "jsCode": "const raw = $input.first().json;\nlet text = '';\ntry { text = raw.candidates[0].content.parts[0].text; } catch(e) { throw new Error('R\u00e9ponse Gemini invalide'); }\ntext = text.replace(/```json/gi, '').replace(/```/g, '').trim();\nlet data;\ntry { data = JSON.parse(text); } catch(e) { throw new Error('JSON parse error: ' + text.substring(0, 200)); }\nif (!data.slides || !Array.isArray(data.slides) || data.slides.length === 0) throw new Error('Pas de slides dans la r\u00e9ponse');\nconst topicData = $('Merge \u2014 Sujet final').item.json;\nreturn [{ json: {\n  ...data,\n  sujet_original: topicData.sujet,\n  angle: topicData.angle,\n  format: topicData.format,\n  nb_slides_generes: data.slides.length,\n  post_linkedin: data.post_linkedin || '',\n  slides: data.slides.map((slide, i) => ({\n    numero: slide.numero || (i + 1),\n    titre: slide.titre || '',\n    corps: slide.corps || '',\n    prompt_image: slide.prompt_image || `Educational carousel slide. Beige #FAF7F2. Bold title: \"${slide.titre}\". Red accent #D72638. 1:1 square.`,\n    filename: `TGM_slide_${String(slide.numero || (i+1)).padStart(2, '0')}.png`\n  }))\n}}];"
      }
    },
    {
      "id": "a23b1ffd-3a05-44a4-ad27-a9da20c9a366",
      "name": "Split \u2014 1 item par slide",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1792,
        432
      ],
      "parameters": {
        "fieldToSplitOut": "=slides",
        "options": {
          "destinationFieldName": "slides"
        }
      }
    },
    {
      "id": "4b93b696-8bd1-492d-8ba7-2820c282928d",
      "name": "fal.ai \u2014 Ideogram 3.0 (Image g\u00e9n\u00e9ration)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1808,
        624
      ],
      "parameters": {
        "method": "POST",
        "url": "https://fal.run/fal-ai/ideogram/v3",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Authorization",
              "value": "=key {{ $env.FAL_AI_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ prompt: $json.slides.prompt_image, aspect_ratio: '1:1', rendering_speed: 'QUALITY', style_type: 'DESIGN' }) }}",
        "options": {
          "timeout": 90000
        }
      }
    },
    {
      "id": "20b4ea41-5933-4269-9c92-b992866e5818",
      "name": "Code \u2014 Extrait URL image",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1808,
        768
      ],
      "parameters": {
        "jsCode": "const falResponse = $input.first().json;\nconst slideData = $('Split \u2014 1 item par slide').item.json.slide;\nconst imageUrl = falResponse?.images?.[0]?.url || falResponse?.image?.url || null;\nif (!imageUrl) throw new Error(`Pas d'URL image pour slide ${slideData.numero}`);\nreturn [{ json: { image_url: imageUrl, slide_numero: slideData.numero, slide_titre: slideData.titre, filename: slideData.filename } }];"
      }
    },
    {
      "id": "3a65c0b4-a440-440b-8bd3-5f4919a72b6c",
      "name": "HTTP \u2014 Download image PNG",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1808,
        912
      ],
      "parameters": {
        "url": "={{ $json.image_url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      }
    },
    {
      "id": "a9e599e2-c365-4bd7-8842-71894559c483",
      "name": "Google Drive \u2014 Upload slide",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1808,
        1040
      ],
      "parameters": {
        "name": "={{ $('Code \u2014 Extrait URL image').item.json.filename }}",
        "folderId": {
          "__rl": true,
          "value": "YOUR_GOOGLE_DRIVE_FOLDER_ID",
          "mode": "id"
        },
        "options": {}
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "b37661cf-0c21-492c-afdc-05ff7d4a76b6",
      "name": "Aggregate \u2014 Collecte tous les slides",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        1808,
        1184
      ],
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "options": {}
      }
    },
    {
      "id": "f2a3aace-06a0-4fde-a1f3-5c136b9b5f75",
      "name": "Telegram \u2014 Notif finale",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1808,
        1328
      ],
      "parameters": {
        "chatId": "={{ $env.TELEGRAM_CHAT_ID }}",
        "text": "=\ud83c\udf89 <b>Carrousel TGM pr\u00eat !</b>\n\n\ud83d\udccc <b>{{ $('Merge \u2014 Sujet final').item.json.sujet }}</b>\n\n\ud83d\udcdd Post LinkedIn :\n<code>{{ $('Code \u2014 Parse r\u00e9ponse Gemini').item.json.post_linkedin }}</code>\n\n\u2705 Slides sur Google Drive \u2014 Assemble en PDF et publie sur LinkedIn",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule \u2014 Lun/Mer/Ven 8h": {
      "main": [
        [
          {
            "node": "Supabase \u2014 SELECT sujets disponibles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase \u2014 SELECT sujets disponibles": {
      "main": [
        [
          {
            "node": "Code \u2014 Choisit sujet (avec signal recyclage)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Choisit sujet (avec signal recyclage)": {
      "main": [
        [
          {
            "node": "IF \u2014 Recyclage n\u00e9cessaire ?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF \u2014 Recyclage n\u00e9cessaire ?": {
      "main": [
        [
          {
            "node": "Supabase \u2014 RESET publi\u00e9s \u2192 disponible",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Gemini \u2014 G\u00e9n\u00e9rer contenu slides",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase \u2014 RESET publi\u00e9s \u2192 disponible": {
      "main": [
        [
          {
            "node": "Gemini \u2014 G\u00e9n\u00e9rer contenu slides",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini \u2014 G\u00e9n\u00e9rer contenu slides": {
      "main": [
        [
          {
            "node": "Code \u2014 Parse r\u00e9ponse Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Parse r\u00e9ponse Gemini": {
      "main": [
        [
          {
            "node": "Split \u2014 1 item par slide",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split \u2014 1 item par slide": {
      "main": [
        [
          {
            "node": "fal.ai \u2014 Ideogram 3.0 (Image g\u00e9n\u00e9ration)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "fal.ai \u2014 Ideogram 3.0 (Image g\u00e9n\u00e9ration)": {
      "main": [
        [
          {
            "node": "Code \u2014 Extrait URL image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code \u2014 Extrait URL image": {
      "main": [
        [
          {
            "node": "HTTP \u2014 Download image PNG",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP \u2014 Download image PNG": {
      "main": [
        [
          {
            "node": "Google Drive \u2014 Upload slide",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Drive \u2014 Upload slide": {
      "main": [
        [
          {
            "node": "Aggregate \u2014 Collecte tous les slides",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate \u2014 Collecte tous les slides": {
      "main": [
        [
          {
            "node": "Telegram \u2014 Notif finale",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "tags": [
    "content",
    "carousel",
    "fal-ai",
    "gemini",
    "google-drive",
    "supabase",
    "auto-recyclage"
  ]
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

TGM — Topic Engine v3 (Auto-Recyclage + fal.ai + Google Drive). Uses httpRequest, googleGemini, googleDrive, telegram. Scheduled trigger; 15 nodes.

Source: https://github.com/guilloulearnlife/n8n-portfolio/blob/24614da8343195909d722fe7754bfcdb6d1820bf/workflows/tgm-topic-engine-v3.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Transform your job search with this comprehensive n8n workflow that automatically searches, analyzes, and applies to relevant positions across multiple job platforms. Perfect for developers, engineers

HTTP Request, Google Gemini, Notion +2
AI & RAG

AI Institutional Stock Valuation Engine with Risk Scoring & Scenario Targets

Google Sheets, XML, HTTP Request +3
AI & RAG

Overview This is a production-grade, fully automated stock analysis system built entirely in n8n. It combines institutional-level financial analysis, dual AI model consensus, and a self-improving back

Google Sheets, XML, HTTP Request +3
AI & RAG

A professional AI equity analysis automation built on n8n that transforms structured financial data and real-time news into disciplined, risk-adjusted price targets and actionable BUY/HOLD/SELL signal

Google Sheets, OpenAI, XML +3
AI & RAG

This workflow creates a daily “n8n News Radar” briefing: Pulls the latest n8n ecosystem updates from Blog, Community, GitHub Releases, and Reddit. Filters to the last 24 hours + keyword relevance. Use

Telegram, Notion, Google Gemini +2