N
n8n Store
Workflow Market
Track Upwork Jobs from Vollna RSS with Google Sheets Logging and Slack Alerts

Track Upwork Jobs from Vollna RSS with Google Sheets Logging and Slack Alerts

by jerrywright0 views

Description

Categories

🤖 AI & Machine Learning

Nodes Used

n8n-nodes-base.coden8n-nodes-base.sortn8n-nodes-base.gmailn8n-nodes-base.slackn8n-nodes-base.filtern8n-nodes-base.aggregaten8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNote
PriceKostenlos
Views0
Last Updated11/28/2025
workflow.json
{
  "meta": {
    "instanceId": "f92b2f5dd06caa798e438c28ffbfaefb7248b0e550ddfec39077609f45e21ab8",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "a13633fc-3429-4840-8a86-0f3f552fc4f5",
      "name": "Check for foreign characters",
      "type": "n8n-nodes-base.filter",
      "position": [
        240,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "da8372f6-230d-4bdd-9c59-a6ead041739e",
              "operator": {
                "type": "string",
                "operation": "regex"
              },
              "leftValue": "={{ $json.title }}",
              "rightValue": "=^[\\x00-\\x7F]+$"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b52a5f5f-fd41-4dab-8b5b-a2e1935ae0e0",
      "name": "Title, Budget, Link, Posted, Date, Job Description, Skills, Categories",
      "type": "n8n-nodes-base.code",
      "position": [
        816,
        -16
      ],
      "parameters": {
        "jsCode": "/**\n * Function node (not Function Item)\n * Processes all incoming items and returns one item per job.\n */\nconst inputs = $input.all();\n\nconst safeMultiDecode = (str, times = 3) => {\n  let prev = str, cur = str;\n  for (let i = 0; i < times; i++) {\n    try { cur = decodeURIComponent(prev); } catch { break; }\n    if (cur === prev) break;\n    prev = cur;\n  }\n  return cur;\n};\n\nconst tryExtractParam = (urlStr) => {\n  if (typeof urlStr !== \"string\" || !urlStr) return \"\";\n  try {\n    const u = new URL(urlStr);\n    const param = u.searchParams.get(\"url\");\n    if (param) return safeMultiDecode(param);\n    if (u.hostname.includes(\"upwork.com\")) return urlStr;\n    return \"\";\n  } catch {\n    const part = urlStr.split(\"url=\")[1];\n    return part ? safeMultiDecode(part) : \"\";\n  }\n};\n\nconst formatPosted = (pubDateStr) => {\n  if (typeof pubDateStr !== \"string\" || !pubDateStr.trim()) {\n    return { posted: \"\", date: \"\" };\n  }\n  const pubDate = new Date(pubDateStr);\n  if (isNaN(pubDate.getTime())) return { posted: \"\", date: \"\" };\n\n  const now = new Date();\n  const diffMs = now - pubDate;\n  const diffMinutes = Math.floor(diffMs / 60000);\n  const diffHours = Math.floor(diffMinutes / 60);\n  const diffDays = Math.floor(diffHours / 24);\n\n  let posted = \"\";\n  if (diffMinutes < 1) posted = \"Posted just now\";\n  else if (diffMinutes < 60) posted = `Posted ${diffMinutes} minute${diffMinutes !== 1 ? \"s\" : \"\"} ago`;\n  else if (diffHours < 24) posted = `Posted ${diffHours} hour${diffHours !== 1 ? \"s\" : \"\"} ago`;\n  else posted = `Posted ${diffDays} day${diffDays !== 1 ? \"s\" : \"\"} ago`;\n\n  const formattedDate = pubDate.toLocaleDateString(\"en-GB\", { day: \"numeric\", month: \"long\", year: \"numeric\" });\n  return { posted, date: formattedDate };\n};\n\nconst outputs = inputs.map(({ json }) => {\n  const out = {};\n\n  // 1) Title → { title, budget }\n  (() => {\n    const input = json?.title;\n    if (typeof input !== \"string\") {\n      out.title = \"\";\n      out.budget = \"\";\n      return;\n    }\n    const regex = /^(.*?)\\s*\\((.*?)\\)$/;\n    const match = input.match(regex);\n    out.title = match ? match[1].trim() : input;\n    out.budget = match ? match[2].trim() : \"\";\n  })();\n\n  // 2) Link → upwork_link\n  out.upwork_link = tryExtractParam(json?.link) || \"\";\n\n  // 3) pubDate → { posted, date }\n  Object.assign(out, formatPosted(json?.pubDate));\n\n  // 4) content → { job_description, skills, categories }\n  (() => {\n    const raw = json?.content;\n    if (typeof raw !== \"string\" || !raw) {\n      out.job_description = \"\";\n      out.skills = \"\";\n      out.categories = \"\";\n      return;\n    }\n    const text = raw\n      .replace(/&amp;/g, \"&\")\n      .replace(/&nbsp;/g, \" \")\n      .replace(/\\r\\n/g, \"\\n\")\n      .replace(/\\r/g, \"\\n\");\n\n    const skillsMatch = text.match(/^\\s*Skills:\\s*([^\\n\\r]+)/im);\n    const categoriesMatch = text.match(/^\\s*Categories:\\s*([^\\n\\r]+)/im);\n\n    out.skills = skillsMatch ? skillsMatch[1].trim() : \"\";\n    out.categories = categoriesMatch ? categoriesMatch[1].trim() : \"\";\n\n    out.job_description = text\n      .replace(/^\\s*Skills:.*$/gim, \"\")\n      .replace(/^\\s*Categories:.*$/gim, \"\");\n  })();\n\n  return { json: out };\n});\n\nreturn outputs;"
      },
      "typeVersion": 2
    },
    {
      "id": "407c809a-89a6-4727-b6af-f7596077bf66",
      "name": "Append row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1776,
        -32
      ],
      "parameters": {
        "columns": {
          "value": {
            "DATE": "={{ $json.date }}",
            "TITLE": "={{ $json.title }}",
            "BUDGET": "={{ $json.budget }}",
            "POSTED": "={{ $json.posted }}",
            "SKILLS": "={{ $json.skills }}",
            "CATEGORIES": "={{ $json.categories }}",
            "JOB DESCRIPTION": "={{ $json.job_description }}",
            "UPWORK JOB LINK": "={{ $json.upwork_link }}"
          },
          "schema": [
            {
              "id": "TITLE",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TITLE",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "BUDGET",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "BUDGET",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "POSTED",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "POSTED",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "JOB DESCRIPTION",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "JOB DESCRIPTION",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "DATE",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "DATE",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "SKILLS",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "SKILLS",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "CATEGORIES",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "CATEGORIES",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "UPWORK JOB LINK",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "UPWORK JOB LINK",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit?usp=drivesdk",
          "cachedResultName": "Upwork Jobs Automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "jOa9HPz3eugURQ5I",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "eff67652-6bd6-4ccc-8fb6-0b1a443b40cf",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -304,
        -16
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 3
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2cb9b9fc-0221-4039-8e32-6fb1d45c6d11",
      "name": "RSS Read",
      "type": "n8n-nodes-base.rssFeedRead",
      "position": [
        -48,
        -16
      ],
      "parameters": {
        "url": "https://www.vollna.com/rss/rftHMpSQCGeEfr2Zwjzb",
        "options": {
          "customFields": ""
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9e4f6014-dce7-4923-9cd3-b1655f1ac925",
      "name": "Sort",
      "type": "n8n-nodes-base.sort",
      "position": [
        512,
        -16
      ],
      "parameters": {
        "options": {},
        "sortFieldsUi": {
          "sortField": [
            {
              "order": "descending",
              "fieldName": "pubDate"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c6d4316a-f13f-464d-b3d7-7315f50b1a28",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1088,
        144
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.upwork_link }}",
              "lookupColumn": "UPWORK JOB LINK"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit?usp=drivesdk",
          "cachedResultName": "Upwork Jobs Automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "jOa9HPz3eugURQ5I",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "5925e47c-7174-4811-acbf-5aa7499922bc",
      "name": "Compare Datasets",
      "type": "n8n-nodes-base.compareDatasets",
      "position": [
        1360,
        -16
      ],
      "parameters": {
        "options": {},
        "mergeByFields": {
          "values": [
            {
              "field1": "upwork_link",
              "field2": "UPWORK JOB LINK"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "dd503da0-f241-42e2-a8dc-14794fc73bef",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        2112,
        -32
      ],
      "webhookId": "f6a937ae-0374-4164-a4f2-2706b006c013",
      "parameters": {
        "text": "=New Job Alert :fire: :fire:\n\n{{ $json.TITLE }} - {{ $json.BUDGET }}\n\n{{ $json.POSTED }}\n\n{{ $json['UPWORK JOB LINK'] }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09DV2HQ2M9",
          "cachedResultName": "n8n-jobs"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "id": "5KZwdKZ1j1iyAkZV",
          "name": "Slack account"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "7dc37ecc-6fe2-4fa3-8c54-0b1a76221a4d",
      "name": "Send a message1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2672,
        -32
      ],
      "webhookId": "41128593-6e44-40aa-bf1a-60f783744beb",
      "parameters": {
        "sendTo": "[email protected]",
        "message": "✅ A message was just sent to #n8n-jobs!",
        "options": {
          "appendAttribution": false
        },
        "subject": "Slack #n8n",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "tKmhWACh32U5uZX8",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "86e8692d-42e3-4598-9e59-efecbae62c67",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2368,
        -32
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "message"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "27b4377b-c6b7-410c-a896-9e74b5af9c05",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -528
      ],
      "parameters": {
        "width": 720,
        "height": 448,
        "content": "Trigger & Input\n\nLocation: Near the Schedule Trigger and RSS Read nodes\nNote Text:\nTrigger & Input\n\n    Runs every 3 minutes\n    Fetches new jobs from Vollna RSS feed\n\nData Cleaning & Filtering\n\nLocation: Next to the Check for foreign characters node\nNote Text:\nFilter Jobs\n\n    Only allow jobs with English (ASCII) titles\n    Prevents non-English/foreign character jobs\n"
      },
      "typeVersion": 1
    },
    {
      "id": "46cf316d-a0d9-493b-b933-f44925065ab2",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        -528
      ],
      "parameters": {
        "color": 5,
        "width": 800,
        "height": 448,
        "content": "Sorting\n\nLocation: Next to the Sort node\nNote Text:\nSort Jobs\n\n    Sorts jobs by most recent (descending by pubDate)\n\nData Extraction & Formatting\n\nLocation: Next to the Title, Budget, Link, Posted, Date, Job Description, Skills, Categories node\nNote Text:\nExtract & Format Data\n\n    Splits title and budget\n    Extracts Upwork link\n    Formats posted date\n    Extracts job description, skills, and categories\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b7c8fbe2-09b9-4a8f-9d57-372d4ac69e43",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        -528
      ],
      "parameters": {
        "color": 6,
        "width": 720,
        "height": 448,
        "content": "Duplicate Check\n\nLocation: Between Get row(s) in sheet and Compare Datasets nodes\nNote Text:\nCheck for Duplicates\n\n    Looks up job link in Google Sheet\n    Compares to avoid duplicate entries\n\nAppend to Sheet\n\nLocation: Next to Append row in sheet node\nNote Text:\nSave New Job\n\n    Appends new, unique jobs to Google Sheet\n"
      },
      "typeVersion": 1
    },
    {
      "id": "dca71453-7f0f-4e52-8862-a29e191372fd",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2144,
        -528
      ],
      "parameters": {
        "color": 4,
        "width": 704,
        "height": 448,
        "content": "Notification\n\nLocation: Next to Send a message node\nNote Text:\nSlack Notification\n\n    Sends new job alert to #n8n-jobs Slack channel\n\n(Optional) Email Confirmation\n\nLocation: Next to Aggregate and Send a message1 nodes\nNote Text:\nEmail Confirmation\n\n    Sends email to confirm Slack message was sent\n"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Sort": {
      "main": [
        [
          {
            "node": "Title, Budget, Link, Posted, Date, Job Description, Skills, Categories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Read": {
      "main": [
        [
          {
            "node": "Check for foreign characters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Send a message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        []
      ]
    },
    "Compare Datasets": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "RSS Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append row in sheet": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "Compare Datasets",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Check for foreign characters": {
      "main": [
        [
          {
            "node": "Sort",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Title, Budget, Link, Posted, Date, Job Description, Skills, Categories": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Compare Datasets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

相关工作流