Latest RSS Feed -> Rocket.Chat

Hey, hoping someone can point me in the right direction. I want to get all the latest items from an RSS feed and post these in a specific Rocket.Chat channel. I only want items I’ve not posted before (could be more than 1 new RSS post at a time).

RSS URL - https://steamcommunity.com/groups/freegamesfinders/rss/

I’ve tried testing this with an RSS feed that posts every 30 seconds (see below for URL) to test it and it keeps on posting the same things over and over again (with multiple RSS feeds in one block of text instead of separate posts or sometimes posts an empty message).

This is what I’ve got so far:

 {
  "name": "Test",
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        -720,
        -30
      ]
    },
    {
      "parameters": {
        "url": "=https://lorem-rss.herokuapp.com/feed?unit=second&interval=30"
      },
      "name": "RSS Feed Read",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [
        -440,
        200
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "channel": "internal-testing",
        "text": "={{$node[\"Return RSS JSON\"].json[\"titleText\"]}}",
        "options": {
          "alias": "Test RSS",
          "avatar": "https://imgur.com/U96w7xq.png"
        },
        "attachments": []
      },
      "name": "RocketChat",
      "type": "n8n-nodes-base.rocketchat",
      "typeVersion": 1,
      "position": [
        490,
        190
      ],
      "credentials": {
        "rocketchatApi": "Bot"
      }
    },
    {
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        }
      },
      "name": "Cron",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        -630,
        200
      ]
    },
    {
      "parameters": {
        "functionCode": "const staticData = this.getWorkflowStaticData('global');\n\nlatestRead = staticData.latestRead;\n\nfor (let item of items) {\n  item.json.latestRead = latestRead || '1970-01-01';\n}\n\nreturn items;"
      },
      "name": "Latest Read",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        -90,
        200
      ]
    },
    {
      "parameters": {
        "functionCode": "const staticData = this.getWorkflowStaticData('global');\n\nif (items.length > 0) {\n  staticData.latestRead = items[0].json.isoDate || staticData.latestRead;\n}\n\n\nreturn items;"
      },
      "name": "Write Latest Read",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        300,
        50
      ]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{new Date($node[\"Latest Read\"].data[\"latestRead\"]).getTime()}}",
              "value2": "={{new Date($node[\"RSS Feed Read\"].data[\"isoDate\"]).getTime()}}"
            }
          ]
        }
      },
      "name": "IF",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        90,
        200
      ]
    },
    {
      "parameters": {
        "functionCode": "let titleText = '';\nlet rssNum = 0;\n\nfor (let item of items) {\n  rssNum = rssNum + 1;\n  titleText += `${item.json.title}`;\n}\n\nreturn [{json: {titleText,rssNum}}];\n"
      },
      "name": "Return RSS JSON",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        300,
        200
      ]
    },
    {
      "parameters": {
        "options": {}
      },
      "name": "SplitInBatches",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 1,
      "position": [
        -260,
        200
      ]
    },
    {
      "parameters": {
        "functionCode": "let titleText = '';\nlet rssNum = 0;\n\nfor (let item of items) {\n  rssNum = rssNum + 1;\n  titleText += `${item.json.title}`;\n}\n\nreturn [{json: {titleText,rssNum}}];\n"
      },
      "name": "Test RSS Results When False - IGNORE",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        300,
        360
      ],
      "disabled": true
    }
  ],
  "connections": {
    "RSS Feed Read": {
      "main": [
        [
          {
            "node": "SplitInBatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cron": {
      "main": [
        [
          {
            "node": "RSS Feed Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Latest Read": {
      "main": [
        [
          {
            "node": "IF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF": {
      "main": [
        [
          {
            "node": "Return RSS JSON",
            "type": "main",
            "index": 0
          },
          {
            "node": "Write Latest Read",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Test RSS Results When False - IGNORE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Return RSS JSON": {
      "main": [
        [
          {
            "node": "RocketChat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SplitInBatches": {
      "main": [
        [
          {
            "node": "Latest Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Test RSS Results When False - IGNORE": {
      "main": [
        [
          {
            "node": "RocketChat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {},
  "id": "2"
}

Example Output:

image

Any pointers would be appreciated!

Hi @Nelinski, you can use static workflow data to check if certain items have already been sent so that it only sends new feed items, when available. You can find more information on how to do that here and an example of how it works here. The workflow would then probably be triggered by a Cron or Interval node.

Let me know if that helps or if you have any other questions. Have fun! :smiley:

2 Likes

It was a lot easier than I thought, I was just overcomplicating things thanks. Working example for others:

{
  "nodes": [
    {
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\nconst newRSSIds = items.map(item => item.json[\"Date\"]);\nconst oldRSSIds = staticData.oldRSSIds; \n\nif (!oldRSSIds) {\n  staticData.oldRSSIds = newRSSIds;\n  return items;\n}\n\n\nconst actualNewRSSIds = newRSSIds.filter((id) => !oldRSSIds.includes(id));\nconst actualNewRSS = items.filter((data) => actualNewRSSIds.includes(data.json['Date']));\nstaticData.oldRSSIds = [...actualNewRSSIds, ...oldRSSIds];\n\nreturn actualNewRSS;\n"
      },
      "name": "Only get new RSS",
      "type": "n8n-nodes-base.function",
      "position": [
        950,
        300
      ],
      "typeVersion": 1
    },
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        100,
        -70
      ]
    },
    {
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyX",
              "value": 1,
              "unit": "minutes"
            }
          ]
        }
      },
      "name": "Cron",
      "type": "n8n-nodes-base.cron",
      "position": [
        400,
        300
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "url": "=https://lorem-rss.herokuapp.com/feed?unit=second&interval=30"
      },
      "name": "RSS Feed Read",
      "type": "n8n-nodes-base.rssFeedRead",
      "typeVersion": 1,
      "position": [
        590,
        300
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "keepOnlySet": true,
        "values": {
          "string": [
            {
              "name": "Title",
              "value": "={{$node[\"RSS Feed Read\"].json[\"title\"]}}"
            },
            {
              "name": "Date",
              "value": "={{$node[\"RSS Feed Read\"].json[\"pubDate\"]}}"
            },
            {
              "name": "URL",
              "value": "={{$node[\"RSS Feed Read\"].json[\"link\"]}}"
            }
          ]
        },
        "options": {}
      },
      "name": "Filter RSS Data",
      "type": "n8n-nodes-base.set",
      "position": [
        780,
        300
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "functionCode": "const staticData = getWorkflowStaticData('global');\nconst newRSSIds = items.map(item => item.json[\"Date\"]);\nconst oldRSSIds = staticData.oldRSSIds; \n\nif (!oldRSSIds) {\n  staticData.oldRSSIds = newRSSIds;\n  return items;\n}\n\n\nconst actualNewRSSIds = newRSSIds.filter((id) => !oldRSSIds.includes(id));\nconst actualNewRSS = items.filter((data) => actualNewRSSIds.includes(data.json['Date']));\nstaticData.oldRSSIds = [...actualNewRSSIds, ...oldRSSIds];\n\nreturn actualNewRSS;\n"
      },
      "name": "Only get new RSS",
      "type": "n8n-nodes-base.function",
      "position": [
        950,
        300
      ],
      "typeVersion": 1
    },
    {
      "parameters": {
        "channel": "internal-testing",
        "text": "=Title: {{$node[\"Only get new RSS\"].json[\"Title\"]}}\nDate: {{$node[\"Only get new RSS\"].json[\"Date\"]}}\nURL: {{$node[\"Only get new RSS\"].json[\"URL\"]}}",
        "jsonParameters": true,
        "options": {
          "alias": "Free Game",
          "avatar": "https://imgur.com/U96w7xq.png"
        }
      },
      "name": "RocketChat",
      "type": "n8n-nodes-base.rocketchat",
      "position": [
        1120,
        300
      ],
      "typeVersion": 1,
      "credentials": {
        "rocketchatApi": "Bot"
      }
    }
  ],
  "connections": {
    "Only get new RSS": {
      "main": [
        [
          {
            "node": "RocketChat",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cron": {
      "main": [
        [
          {
            "node": "RSS Feed Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Feed Read": {
      "main": [
        [
          {
            "node": "Filter RSS Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter RSS Data": {
      "main": [
        [
          {
            "node": "Only get new RSS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
1 Like

That is very great to hear! But honestly is it currently much more complicated than it should be. We have it however on our radar and we hope to improve that situation in the future.

For now, have fun!

2 Likes

Hi Nelinski

Thanks for sharing your solution. Layman question on my part: if I have multiple workflows with different RSS feeds and I want only the latest item per RSS feed, I need to customize the Function node, right? That means the const variables have to have unique names per workflow, right (I assume the data is stored across workflows)? Or do I only have to adjust the global const variable? Sorry, I’m not yet so skilled in javascript

Hey,

Just to be clear you’ve got multiple workflows like this?

In my screenshot, I’ve got two workflows for 2 different RSS feeds. I haven’t changed anything between the two workflows apart from the RSS feed URL. Everything else stays the same including the const variables, function node etc.

I’m assuming this is correct, yet to receive an RSS through on both since setting it up.

Correct, that’s exactly how I have it currently.

All right, so I can leave the function node similar to your suggestion and simply adjust the RSS URL. Thanks a lot!

Wasn’t quite sure if the const are saved only for the specific workflow or apply across all workflows of n8n

1 Like

Yes should work totally fine. The static data gets either saved on a per workflow or on a per node basis, but never across workflows. You can find the documentation here:

3 Likes