Automatisation Google Sheets avec n8n : publication de blogs automatisée
Ce workflow n8n est conçu pour automatiser la publication de blogs en utilisant Google Sheets comme base de données. L'objectif principal est de simplifier le processus de création et de publication de contenu, permettant ainsi aux équipes marketing de se concentrer sur la stratégie plutôt que sur les tâches répétitives. Grâce à ce système, les utilisateurs peuvent programmer des publications, récupérer des données depuis Google Sheets, et interagir avec des modèles de langage pour générer du contenu pertinent. Le workflow débute par un déclencheur de type 'ScheduleTrigger', qui permet de définir une fréquence de publication. Ensuite, il utilise des noeuds Google Sheets pour récupérer les données nécessaires et les préparer pour la publication. Un noeud 'AgentLLM' est intégré pour générer du contenu à partir des informations fournies. Les conditions sont vérifiées à chaque étape grâce à des noeuds 'If', permettant de s'assurer que seules les publications prêtes sont envoyées. Une fois le contenu validé, il est publié via un noeud 'CreatePost', qui envoie une requête HTTP pour effectuer la publication sur la plateforme choisie. Les bénéfices de cette automatisation n8n sont multiples : réduction du temps de publication, minimisation des erreurs humaines et amélioration de la cohérence du contenu publié. En intégrant ce workflow, les entreprises peuvent optimiser leur stratégie de contenu tout en garantissant une publication régulière et efficace. Tags clés : automatisation, Google Sheets, publication de contenu.
Vue d'ensemble du workflow n8n
Schéma des nœuds et connexions de ce workflow n8n, généré à partir du JSON n8n.
Détail des nœuds du workflow n8n
Inscris-toi pour voir l'intégralité du workflow
Inscription gratuite
S'inscrire gratuitementBesoin d'aide ?{
"id": "b0KRVIuuUxE5afHo",
"meta": {
"instanceId": "98bf0d6aef1dd8b7a752798121440fb171bf7686b95727fd617f43452393daa3",
"templateCredsSetupCompleted": true
},
"name": "Blog Automation TEMPLATE",
"tags": [
{
"id": "uumvgGHY5e6zEL7V",
"name": "Published Template",
"createdAt": "2025-02-10T11:18:10.923Z",
"updatedAt": "2025-02-10T11:18:10.923Z"
}
],
"nodes": [
{
"id": "20e00146-6bda-4a8a-9544-bf7e5fd4e12e",
"name": "Settings",
"type": "n8n-nodes-base.set",
"position": [
-420,
-160
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "528b371f-0fba-4be1-9801-0502652da23e",
"name": "urlSpreadsheet",
"type": "string",
"value": "https://docs.google.com/spreadsheets/d/1Kg1-U6mJF4bahH1jCw8kT48MiKz1UMC5n-9q77BHM3Q/edit?gid=0#gid=0"
},
{
"id": "1be018c7-51fe-4ea2-967d-ce47a2e8795c",
"name": "urlWordpress",
"type": "string",
"value": "SUBDOMAIN.wordpress.com"
},
{
"id": "95377f4f-184b-46a7-94c7-b2313c314cb2",
"name": "wordpressUsername",
"type": "string",
"value": "YourUserName"
},
{
"id": "fdc99dc6-d9b0-4d2f-b770-1d8b6b360cad",
"name": "wordpressApplicationPassword",
"type": "string",
"value": "y0ur app1 p4ss w0rd"
},
{
"id": "517cb9ff-24fc-41d6-8bcc-253078f56356",
"name": "sheetSchedule",
"type": "string",
"value": "=Schedule"
},
{
"id": "584e11da-546b-4472-8674-33ca7e8f4f30",
"name": "sheetConfig",
"type": "string",
"value": "Config"
},
{
"id": "ba38cb1e-fd97-4aed-9147-1946c318ddab",
"name": "actionPublish",
"type": "string",
"value": "publish"
},
{
"id": "678394b5-20af-4718-9249-4ff6a3c77018",
"name": "actionUpdate",
"type": "string",
"value": ""
},
{
"id": "f375b2fa-8772-4313-9d6b-a104edd918b3",
"name": "sheetLog",
"type": "string",
"value": "Log"
},
{
"id": "3d7f9677-c753-4126-b33a-d78ef701771f",
"name": "",
"type": "string",
"value": ""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "35731842-9215-43df-9009-9b130d663237",
"name": "ScheduleTrigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-620,
-280
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "4c284d44-ac46-4cdf-9dcb-727b464269a0",
"name": "ManualTrigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-620,
-100
],
"parameters": {},
"typeVersion": 1
},
{
"id": "b63e7345-67d0-4761-8c1a-49275f34e88d",
"name": "Schedule",
"type": "n8n-nodes-base.googleSheets",
"position": [
-220,
-80
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.sheetSchedule }}"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Settings').item.json.urlSpreadsheet }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "XeXufn5uZvHp3lcX",
"name": "Google Sheets account 2"
}
},
"notesInFlow": true,
"typeVersion": 4.5
},
{
"id": "5fed06a3-3188-4aed-8040-04e245b74e20",
"name": "Config",
"type": "n8n-nodes-base.code",
"position": [
40,
-220
],
"parameters": {
"jsCode": "let a = $(\"fetchConfig\").all();\nlet params = {};\na.forEach(p => params[p.json.Key] = p.json.Value);\n\nreturn params;\n"
},
"typeVersion": 2
},
{
"id": "685490c8-6b45-40c2-b4db-e97a81c4be8e",
"name": "fetchConfig",
"type": "n8n-nodes-base.googleSheets",
"position": [
-220,
-220
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.sheetConfig }}"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Settings').item.json.urlSpreadsheet }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "XeXufn5uZvHp3lcX",
"name": "Google Sheets account 2"
}
},
"notesInFlow": true,
"typeVersion": 4.5
},
{
"id": "52a39db8-f9cc-44bb-9c3e-a9abf5821a04",
"name": "AgentLLM",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-400,
440
],
"parameters": {
"model": "={{ $json.model }}",
"options": {}
},
"credentials": {
"openAiApi": {
"id": "66JEQJ5kJel1P9t3",
"name": "OpenRouter"
}
},
"typeVersion": 1.1
},
{
"id": "6a311ac4-032b-42da-b06e-c916209d2843",
"name": "IfScheduledNow",
"type": "n8n-nodes-base.if",
"position": [
-620,
780
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "bb707069-b372-4bbd-8ba5-b7f6b492ab9d",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ DateTime.now().ts }}",
"rightValue": "={{ DateTime.fromFormat($json.row.Scheduled, \"yyyy-MM-dd HH:mm:ss\").ts }}"
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "845e419b-15ad-4548-86c5-44bda0433b71",
"name": "PreparedData",
"type": "n8n-nodes-base.code",
"position": [
40,
-80
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "function replacePlaceholders(text, row, config) {\n function checkProp(prop, lookup) {\n // console.log('checkProp:' + prop);\n if (!lookup.hasOwnProperty(prop)) return false;\n let value = lookup[prop];\n if (typeof(value) == 'string') {\n value = value.trim();\n if (value == '') return false;\n }\n // console.log('checkProp found:', value)\n return value;\n }\n function replaceMatch(fullMatch, prop) { \n prop = prop.trim();\n // Return the corresponding value\n return checkProp(prop, row)\n || checkProp(prop, config)\n || checkProp(prop + checkProp('Context', row), config)\n || `[could not find \"${ prop }]\"`;\n }\n\n if (typeof(text) != 'string') return '';\n\n // Regex to capture {{ ... }}\n const pattern = /\\{\\{\\s*([^}]+)\\s*\\}\\}/g\n const result = text.replace(pattern, replaceMatch);\n return result.trim();\n}\n\nconst row = $json;\nconst settings = $(\"Settings\").first().json;\nconst config = $(\"Config\").first().json;\nconst prompt_key = 'prompt_' + row.Action;\nconst prompt = replacePlaceholders(config[prompt_key], row, config);\nconst model_key = prompt_key + '_model';\nconst model = replacePlaceholders(config[model_key], row, config);\nconst outputFormat = config[prompt_key + '_outputFormat'];\nconst takeAction = row.Action != row.Status;\nconst action = row.Action\n\n// console.log('prompt', prompt);\n\n// console.log(prompt);\nreturn { takeAction, action, model_key, model, prompt_key, prompt, outputFormat, row, config, settings }"
},
"typeVersion": 2
},
{
"id": "db294805-df67-4266-919f-94fb0f32c593",
"name": "RecombinedDataRow",
"type": "n8n-nodes-base.code",
"position": [
40,
280
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "/**\n * Attempts to parse the \"text\" property in a JSON object\n * that may contain malformed or incorrectly escaped JSON.\n *\n * @param {Object} raw - A string to parse.\n * @returns {Object|null} The parsed JSON object if successful, or null if all attempts fail.\n */\nfunction parseTextAsJson(raw) {\n // 1) First, try a direct parse.\n try {\n return JSON.parse(raw);\n } catch (e) {\n // Continue to next strategy\n }\n\n // Common \"fix-up\" strategies:\n // Strategy A: Attempt to remove over-escaped quotes like `\\\\\"` -> `\"`\n try {\n const fixedA = raw.replace(/\\\\\"/g, '\"');\n return JSON.parse(fixedA);\n } catch (e) {\n // Continue\n }\n\n // Strategy B: Remove escaped newlines, tabs, carriage returns if they’re suspected\n try {\n const fixedB = raw\n .replace(/\\\\n/g, ' ')\n .replace(/\\\\r/g, ' ')\n .replace(/\\\\t/g, ' ');\n return JSON.parse(fixedB);\n } catch (e) {\n // Continue\n }\n\n // Strategy C: Replace single quotes with double quotes (useful if the JSON was incorrectly quoted).\n // NOTE: This is a very rough fix. If your data legitimately includes single quotes you may need\n // a more nuanced approach.\n try {\n const fixedC = raw.replace(/'/g, '\"');\n return JSON.parse(fixedC);\n } catch (e) {\n // Continue\n }\n\n // Strategy D: Combine strategies or chain them if needed:\n // For example, single-quote fix plus removing new lines, etc.\n try {\n let fixedD = raw.replace(/\\\\\"/g, '\"');\n fixedD = fixedD.replace(/\\\\n|\\\\r|\\\\t/g, ' ');\n fixedD = fixedD.replace(/'/g, '\"');\n return JSON.parse(fixedD);\n } catch (e) {\n // If all attempts fail, log or handle the error as needed\n console.error('Could not parse \"text\" property as JSON.', e);\n return { 'Fulltext': raw };\n }\n}\n\nfunction isolateCurlySubstring(str) {\n // This pattern greedily matches everything from the first '{' to the last '}'.\n const match = str.match(/\\{[\\s\\S]*\\}/);\n \n // If a match is found, return it; otherwise return the entire string.\n return match ? match[0] : str;\n}\n\nfunction fixJsonSyntax(str) {\n str = str.replace('\\\"', '\"');\n str = str\n .split(/(\"[^\"]*\"|'[^']*')/)\n .map((part, i) => i % 2 ? part : part.replace(/\\n/g, \" \"))\n .join(\"\");\n return str;\n}\n\nfunction normalizeLLMOutput(param, iteration = 3) {\n // If it's not an object or it's null or an array, just return it as is.\n // (In some workflows, you might decide to throw an error or handle differently.)\n if (!iteration || typeof param !== 'object' || param === null || Array.isArray(param)) {\n return param;\n }\n\n // Get the object's own property keys\n const keys = Object.keys(param);\n\n // If there's more than one property, we assume it's already the complex object we want.\n if (keys.length > 1) {\n // console.log('keys > 1 → return param', param);\n return param;\n }\n\n // If there are no properties, just return it (though this is likely an empty object).\n if (keys.length === 0) {\n return param;\n }\n\n // If there's exactly one property, it might be a JSON-string that we need to parse.\n const singleKey = keys[0];\n const value = param[singleKey];\n // If that single property is a string, fix it and try to parse it as JSON.\n if (typeof value === 'string') {\n try {\n return parseTextAsJson(isolateCurlySubstring(value));\n } catch (e) {\n console.log('value is string → parse failed with error:', e.toString(), '→ return param:', param, 'value:', value);\n // Parsing failed; perhaps it's just a plain string or invalid JSON, so return as is.\n return param;\n }\n }\n\n // Otherwise, repeat this process itratively.\n return normalizeLLMOutput(value, iteration-1);\n}\n\nconst preparedData = $(\"PreparedData\").itemMatching($itemIndex).json;\nconst row = preparedData.row;\nlet gen = normalizeLLMOutput($json);\nlet fulltext = gen.hasOwnProperty('Fulltext') ? gen.Fulltext : gen;\n\n// Append any fulltext field returned to the field\n// in our data row corresponding to the current action. \ngen[row.Action] = fulltext;\n\n// Concatenate any generated fields with those already exisiting\n// in our data row (using seperator if necessary),\n// so we don't loose any pre-entered data.\nconst combined = {};\nObject.keys(gen).forEach(key => {\n const a = String(row[key] ?? \"\");\n const b = String(gen[key]);\n combined[key] = (a && b) ? (a + \"\\n---\\n\" + b) : (a || b);\n});\n\n// Add the row number and set the new status to the action just performed.\ncombined.row_number = row.row_number;\ncombined.Status = row.Action;\ncombined.model = preparedData.model;\n\nreturn combined;"
},
"typeVersion": 2
},
{
"id": "e0c993c1-678f-4236-8976-735cccb49fee",
"name": "SaveBackToSheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
480,
280
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "ID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Topic",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Topic",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Scheduled",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Scheduled",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Action",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Action",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Context",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Context",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Idea",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Idea",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Content",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Content",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Length",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Length",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Media",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Media",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LinksInternal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "LinksInternal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LinksExternal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "LinksExternal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Title",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sections",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Sections",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "MainPoints",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "MainPoints",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "GuidingPrinciple",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "GuidingPrinciple",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Metaphor",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Metaphor",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Draft",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Draft",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Final",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Final",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "internal notes",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "internal notes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"handlingExtraData": "ignoreIt"
},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.sheetSchedule }}"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Settings').item.json.urlSpreadsheet }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "XeXufn5uZvHp3lcX",
"name": "Google Sheets account 2"
}
},
"typeVersion": 4.5
},
{
"id": "e0b982d9-d24e-4fd0-bc03-8642cd4c988b",
"name": "IfActionPublish",
"type": "n8n-nodes-base.if",
"position": [
500,
-80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c3735d0d-da54-44e7-afe6-fdfacb6117f2",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.row.Action }}",
"rightValue": "={{ $('Settings').item.json.actionPublish }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "1d5c2731-61a1-434c-bdf1-294217e4ac1c",
"name": "IfTakeAction",
"type": "n8n-nodes-base.if",
"position": [
260,
-80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "85536861-b213-4567-9c9a-f844a28b5405",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.takeAction }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "aae766a4-d29e-4357-a344-74ee36a382e1",
"name": "IfPromptExists",
"type": "n8n-nodes-base.if",
"position": [
-600,
280
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "73333657-16ed-4b0d-a81f-34add6c22a1b",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.prompt }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "5b4c4bdf-8997-4c19-8e95-8c84b725404c",
"name": "Basic LLM Chain",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"position": [
-360,
280
],
"parameters": {
"text": "={{ $json.prompt }}",
"promptType": "define"
},
"typeVersion": 1.5
},
{
"id": "8dc422a3-6b86-4f57-8c4c-df6422f72f57",
"name": "CreatePost",
"type": "n8n-nodes-base.httpRequest",
"position": [
-220,
780
],
"parameters": {
"url": "=https://{{ $('Settings').item.json.urlWordpress }}/xmlrpc.php",
"body": "={{ $json.xmlRequestBody }}",
"method": "POST",
"options": {},
"sendBody": true,
"contentType": "raw",
"sendHeaders": true,
"rawContentType": "text/xml",
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "text/xml"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "6ad42453-d56b-4bae-aaf3-eb689df998cc",
"name": "SetToPublish",
"type": "n8n-nodes-base.googleSheets",
"position": [
700,
780
],
"parameters": {
"columns": {
"value": {
"Status": "={{ $('Settings').item.json.actionPublish }}",
"row_number": "={{ $('PreparedData').item.json.row.row_number }}"
},
"schema": [
{
"id": "ID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Topic",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Topic",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Scheduled",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Scheduled",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Action",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Action",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Context",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Context",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Ideas",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Ideas",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Content",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Content",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Length",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Length",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Media",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Media",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LinksInternal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "LinksInternal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "LinksExternal",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "LinksExternal",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Sections",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Sections",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "MainPoints",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "MainPoints",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "GuidingPrinciple",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "GuidingPrinciple",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Metaphor",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Metaphor",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Title",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Title",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "draft",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "draft",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "words",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "words",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "final",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "final",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "words",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "words",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "TeaserTitle",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "TeaserTitle",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "TeaserText",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "TeaserText",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "internal notes",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "internal notes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"row_number"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.sheetSchedule }}"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Settings').item.json.urlSpreadsheet }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "XeXufn5uZvHp3lcX",
"name": "Google Sheets account 2"
}
},
"typeVersion": 4.5
},
{
"id": "a1af0f00-de59-48d4-93d2-9cc20e7f1c1c",
"name": "PrepareXmlPost",
"type": "n8n-nodes-base.code",
"position": [
-380,
780
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const username = $('Settings').item.json.wordpressUsername;\nconst password = $('Settings').item.json.wordpressApplicationPassword;\nconst blogId = 0;\nconst published = 1; // 0 = draft, 1 = published\nconst title = $json.row.Title;\nconst text = $json.row.final;\n\n// Helper function to escape XML special characters\nfunction escapeXml(unsafe) {\n return unsafe.replace(/[<>&'\"]/g, (c) => {\n switch (c) {\n case '<': return '<';\n case '>': return '>';\n case '&': return '&';\n case '\\'': return ''';\n case '\"': return '"';\n default: return c;\n }\n });\n}\n\n// Your actual post text, which may contain characters needing escaping\nconst titleEscaped = escapeXml(title);\nconst textEscaped = escapeXml(text);\n\n// Build the XML payload\nconst xmlData = `<?xml version=\"1.0\"?>\n<methodCall>\n <methodName>wp.newPost</methodName>\n <params>\n <param>\n <value><string>${blogId}</string></value>\n </param>\n <param>\n <value><string>${username}</string></value>\n </param>\n <param>\n <value><string>${password}</string></value>\n </param>\n <param>\n <value>\n <struct>\n <member>\n <name>post_title</name>\n <value><string>${titleEscaped}</string></value>\n </member>\n <member>\n <name>post_content</name>\n <value><string>${textEscaped}</string></value>\n </member>\n </struct>\n </value>\n </param>\n <param>\n <value><boolean>${published}</boolean></value>\n </param>\n </params>\n</methodCall>`;\n\n\n// Add a new field called 'myNewField' to the JSON of the item\n$input.item.json.xmlRequestBody = xmlData;\n\nreturn $input.item;"
},
"typeVersion": 2
},
{
"id": "00e6d2ab-6dc4-42ba-8a92-04a35d104908",
"name": "HandleXMLRPCResponse",
"type": "n8n-nodes-base.code",
"position": [
40,
780
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Get the XML response from the incoming JSON\nconst xmlResponse = $json.data;\n\n// Helper function to extract a value by matching a regex pattern\nfunction extractValue(pattern, xml) {\n const match = xml.match(pattern);\n return match ? match[1] : null;\n}\n\n// Check if the XML contains a fault\nif (xmlResponse.indexOf(\"<fault>\") !== -1) {\n // Extract the faultCode and faultString using regex\n // This regex matches the value inside <int> or <string> for faultCode\n const faultCode = extractValue(/<name>faultCode<\\/name>\\s*<value><(?:int|string)>(.*?)<\\/(?:int|string)>/s, xmlResponse);\n // This regex extracts the faultString from within <string>\n const faultString = extractValue(/<name>faultString<\\/name>\\s*<value><string>(.*?)<\\/string>/s, xmlResponse);\n return { 'errorCode': faultCode, 'error': faultString };\n} else {\n // Otherwise, assume a successful response.\n // The post ID is contained inside a <string> tag within <params>\n const postId = extractValue(/<params>[\\s\\S]*?<string>(.*?)<\\/string>/, xmlResponse);\n return { postId };\n}"
},
"typeVersion": 2
},
{
"id": "23212e92-4ad1-4a8c-8e0a-04d8d2a4511d",
"name": "PostingSuccessful",
"type": "n8n-nodes-base.if",
"position": [
480,
780
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "815d85a1-8f91-4338-977f-503f02c53ea2",
"operator": {
"type": "string",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('HandleXMLRPCResponse').item.json.postId }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "45c786f0-d795-4ed4-b6d2-f005b43e797f",
"name": "LogStatus",
"type": "n8n-nodes-base.googleSheets",
"position": [
260,
280
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $now }}",
"Type": "=info",
"Message": "=Status {{ $json.Status }} for row {{ $('PreparedData').item.json.row.row_number }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"display": true,
"required": false,
"displayName": "Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Type",
"type": "string",
"display": true,
"required": false,
"displayName": "Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message",
"type": "string",
"display": true,
"required": false,
"displayName": "Message",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.sheetLog }}"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Settings').item.json.urlSpreadsheet }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "XeXufn5uZvHp3lcX",
"name": "Google Sheets account 2"
}
},
"typeVersion": 4.5
},
{
"id": "f58306f5-a5e9-4e44-9c5d-3810e18e6605",
"name": "LogPublished",
"type": "n8n-nodes-base.googleSheets",
"position": [
260,
780
],
"parameters": {
"columns": {
"value": {
"Date": "={{ $now }}",
"Type": "={{ $json.errorCode ? 'error' : 'info' }}",
"Message": "=Publishing row {{ $('PreparedData').item.json.row.row_number }}: {{ $json.postId }}{{ $json.errorCode }}{{ $json.error }}"
},
"schema": [
{
"id": "Date",
"type": "string",
"display": true,
"required": false,
"displayName": "Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Type",
"type": "string",
"display": true,
"required": false,
"displayName": "Type",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message",
"type": "string",
"display": true,
"required": false,
"displayName": "Message",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "name",
"value": "={{ $('Settings').item.json.sheetLog }}"
},
"documentId": {
"__rl": true,
"mode": "url",
"value": "={{ $('Settings').item.json.urlSpreadsheet }}"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "XeXufn5uZvHp3lcX",
"name": "Google Sheets account 2"
}
},
"typeVersion": 4.5
},
{
"id": "c227b790-e1ee-4370-9f24-a734443d1e97",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-460,
-300
],
"parameters": {
"width": 180,
"height": 360,
"content": "## Settings"
},
"typeVersion": 1
},
{
"id": "904da209-68fd-4139-885f-bd3f25034aeb",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-440,
180
],
"parameters": {
"color": 3,
"width": 380,
"height": 380,
"content": "## Author Blog-Post\nUsing OpenRouter to make model fully configurable for each authoring stage"
},
"typeVersion": 1
},
{
"id": "29f35bf0-6dd3-4c3c-b688-73eb46781c87",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-40,
-300
],
"parameters": {
"color": 5,
"height": 360,
"content": "## Post-process Data\n{{ Placehoder }} replacement"
},
"typeVersion": 1
},
{
"id": "296c3257-836d-488c-b048-72261180e286",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
220,
180
],
"parameters": {
"color": 4,
"width": 180,
"height": 380,
"content": "## Log to Sheet"
},
"typeVersion": 1
},
{
"id": "42a06803-087f-4dc4-9dd5-1f0281942a30",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
420,
180
],
"parameters": {
"color": 6,
"width": 420,
"height": 380,
"content": "## Save Result To Sheet"
},
"typeVersion": 1
},
{
"id": "7a6393e9-ae81-4b9b-856b-7be18f783cf4",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-440,
620
],
"parameters": {
"color": 3,
"width": 380,
"height": 380,
"content": "## Publish Blog-Post\nUse a generic XMLHttpRequest with subsequent response handling, since the Wordpress node did not work at all."
},
"typeVersion": 1
},
{
"id": "2d154bd4-c3bc-4137-90ce-7885bac77c71",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-40,
180
],
"parameters": {
"color": 5,
"height": 380,
"content": "## Post-process Data\nNormalize and re-merge output data structure. "
},
"typeVersion": 1
},
{
"id": "83834b00-a647-403f-b88a-4c38d9750eb0",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-40,
620
],
"parameters": {
"color": 5,
"height": 380,
"content": "## Post-process Data\nExtract post id or error message from response."
},
"typeVersion": 1
},
{
"id": "e7494d0b-b796-437e-b977-a5350b1a8dc5",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
220,
620
],
"parameters": {
"color": 4,
"width": 180,
"height": 380,
"content": "## Log to Sheet"
},
"typeVersion": 1
},
{
"id": "1d036f6a-c6e4-428d-b0ce-1e710eb7d90c",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
420,
620
],
"parameters": {
"color": 6,
"width": 420,
"height": 380,
"content": "## Save Status To Sheet"
},
"typeVersion": 1
},
{
"id": "105e0743-b4e8-47d7-a4bf-3939df43a43c",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-640,
160
],
"parameters": {
"color": 7,
"width": 1500,
"height": 420,
"content": "## Authoring\n## Stage"
},
"typeVersion": 1
},
{
"id": "80fefb90-35b2-4f0b-b4d5-1cca8519361d",
"name": "Sticky Note11",
"type": "n8n-nodes-base.stickyNote",
"position": [
-640,
600
],
"parameters": {
"color": 7,
"width": 1500,
"height": 420,
"content": "## Publishing\n## Stage"
},
"typeVersion": 1
},
{
"id": "99b0a7b7-6513-47b0-af16-ee66d37dd821",
"name": "Sticky Note12",
"type": "n8n-nodes-base.stickyNote",
"position": [
-260,
-300
],
"parameters": {
"width": 200,
"height": 360,
"content": "## Config & Data"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "7005e556-a7ae-484c-af71-57c75abd3e17",
"connections": {
"Config": {
"main": [
[]
]
},
"AgentLLM": {
"ai_languageModel": [
[
{
"node": "Basic LLM Chain",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Schedule": {
"main": [
[
{
"node": "PreparedData",
"type": "main",
"index": 0
}
]
]
},
"Settings": {
"main": [
[
{
"node": "fetchConfig",
"type": "main",
"index": 0
},
{
"node": "Schedule",
"type": "main",
"index": 0
}
]
]
},
"LogStatus": {
"main": [
[
{
"node": "SaveBackToSheet",
"type": "main",
"index": 0
}
]
]
},
"CreatePost": {
"main": [
[
{
"node": "HandleXMLRPCResponse",
"type": "main",
"index": 0
}
]
]
},
"fetchConfig": {
"main": [
[
{
"node": "Config",
"type": "main",
"index": 0
}
]
]
},
"IfTakeAction": {
"main": [
[
{
"node": "IfActionPublish",
"type": "main",
"index": 0
}
]
]
},
"LogPublished": {
"main": [
[
{
"node": "PostingSuccessful",
"type": "main",
"index": 0
}
]
]
},
"PreparedData": {
"main": [
[
{
"node": "IfTakeAction",
"type": "main",
"index": 0
}
]
]
},
"SetToPublish": {
"main": [
[]
]
},
"ManualTrigger": {
"main": [
[
{
"node": "Settings",
"type": "main",
"index": 0
}
]
]
},
"IfPromptExists": {
"main": [
[
{
"node": "Basic LLM Chain",
"type": "main",
"index": 0
}
]
]
},
"IfScheduledNow": {
"main": [
[
{
"node": "PrepareXmlPost",
"type": "main",
"index": 0
}
]
]
},
"PrepareXmlPost": {
"main": [
[
{
"node": "CreatePost",
"type": "main",
"index": 0
}
]
]
},
"Basic LLM Chain": {
"main": [
[
{
"node": "RecombinedDataRow",
"type": "main",
"index": 0
}
]
]
},
"IfActionPublish": {
"main": [
[
{
"node": "IfScheduledNow",
"type": "main",
"index": 0
}
],
[
{
"node": "IfPromptExists",
"type": "main",
"index": 0
}
]
]
},
"SaveBackToSheet": {
"main": [
[]
]
},
"ScheduleTrigger": {
"main": [
[
{
"node": "Settings",
"type": "main",
"index": 0
}
]
]
},
"PostingSuccessful": {
"main": [
[
{
"node": "SetToPublish",
"type": "main",
"index": 0
}
]
]
},
"RecombinedDataRow": {
"main": [
[
{
"node": "LogStatus",
"type": "main",
"index": 0
}
]
]
},
"HandleXMLRPCResponse": {
"main": [
[
{
"node": "LogPublished",
"type": "main",
"index": 0
}
]
]
}
}
}Pour qui est ce workflow ?
Ce workflow s'adresse aux équipes marketing et aux rédacteurs de contenu au sein des PME et des grandes entreprises. Il est idéal pour ceux qui cherchent à automatiser leur processus de publication tout en ayant des compétences techniques intermédiaires. Les utilisateurs de Google Sheets bénéficieront particulièrement de cette automatisation.
Problème résolu
Ce workflow résout le problème de la gestion manuelle des publications de blogs, qui peut être chronophage et sujet à des erreurs. En automatisant ce processus, les utilisateurs peuvent éviter les oublis de publication et garantir une diffusion régulière de contenu. Cela permet également de libérer du temps pour se concentrer sur des tâches à plus forte valeur ajoutée, comme la création de stratégies de contenu innovantes.
Étapes du workflow
Étape 1 : Le workflow est déclenché par un 'ScheduleTrigger' qui définit la fréquence de publication. Étape 2 : Les données sont récupérées depuis Google Sheets via le noeud 'Schedule'. Étape 3 : Un modèle de langage est utilisé pour générer le contenu à l'aide du noeud 'AgentLLM'. Étape 4 : Des conditions sont vérifiées avec des noeuds 'If' pour s'assurer que le contenu est prêt à être publié. Étape 5 : Le contenu validé est envoyé à la plateforme via le noeud 'CreatePost'. Étape 6 : Les statuts de publication sont enregistrés dans Google Sheets pour un suivi efficace.
Guide de personnalisation du workflow n8n
Pour personnaliser ce workflow, commencez par ajuster le déclencheur 'ScheduleTrigger' pour définir la fréquence de publication souhaitée. Modifiez les paramètres des noeuds Google Sheets pour pointer vers votre document spécifique. Vous pouvez également adapter le modèle de langage dans le noeud 'AgentLLM' en fonction de votre ton et style de contenu. Enfin, assurez-vous que le noeud 'CreatePost' est configuré avec l'URL et les paramètres appropriés pour votre plateforme de publication. Pensez à tester le workflow pour vous assurer que tout fonctionne comme prévu.