{
  "id": "P6nG4HI9yO4OEIxs",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "GitHub Trending Repo Alerts",
  "tags": [],
  "nodes": [
    {
      "id": "00743f62-6979-405c-87ed-bcfe6296eb77",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2432,
        -288
      ],
      "parameters": {
        "width": 480,
        "height": 848,
        "content": "## GitHub Trending Repo Alerts\n\n### How it works\n\nThe workflow triggers on a defined schedule to initiate the data scraping process.\nIt sets basic configuration parameters like programming languages, star thresholds, and item limits.\nIt fetches HTML content from the GitHub trending pages and extracts relevant repository information.\nExtracted data is cleaned, filtered to exclude archived projects, and sorted by today's star count.\nThe highest-ranked repositories are aggregated into a single list to formulate a report.\nA summary alert is dispatched to Slack with the top trending repositories, provided there are results.\n\n### Setup steps\n\n- [ ] Configure the Schedule Trigger to run at your desired interval.\n- [ ] Review the 'Config' node to set your preferred languages, minStars, topN, and since values.\n- [ ] Add your Slack credentials in the 'Send Slack Alert' node.\n- [ ] Set your destination channel or user in the 'Send Slack Alert' node.\n\n### Customization\n\nYou can adjust the minimum stars and top N count in the Config node to refine your alerts."
      },
      "typeVersion": 1
    },
    {
      "id": "2b0f4c12-693a-43ec-a49e-ca7c5979ed46",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1872,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Initialize and set configuration\n\nTriggers the workflow on a set schedule, initializes thresholds and parameters, and loops over the targeted programming languages."
      },
      "typeVersion": 1
    },
    {
      "id": "3e53637b-abfc-4911-9bed-f9a7272f3392",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1200,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 304,
        "content": "## Fetch and extract repo data\n\nScrapes the GitHub trending page for each language and extracts the raw HTML blocks representing repositories."
      },
      "typeVersion": 1
    },
    {
      "id": "e3157cf5-b090-4229-9c8d-78e326542f00",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Clean and filter repositories\n\nComputes standard properties from the HTML snippets and filters out repositories that are archived or have too few stars."
      },
      "typeVersion": 1
    },
    {
      "id": "5a3018f2-9d5e-48ca-8226-0f6558765316",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        -160
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 304,
        "content": "## Sort and limit repositories\n\nNormalizes star numbers, sorts the lists from highest to lowest stars today, and limits to the configured top N results."
      },
      "typeVersion": 1
    },
    {
      "id": "82fc24c7-ae1d-4505-baf3-ef4de26e3737",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        -192
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 320,
        "content": "## Aggregate and evaluate results\n\nGroups all finalized repository items into a single bundle and checks if there are any results to report."
      },
      "typeVersion": 1
    },
    {
      "id": "a72983b3-02ba-4308-a114-c950d0f174e5",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        -288
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 512,
        "content": "## Send Slack alert or exit\n\nConstructs a summary message and alerts via Slack, or silently exits if no repositories met the criteria."
      },
      "typeVersion": 1
    },
    {
      "id": "9879790e-16a0-44dd-97d8-4d97f7423fe0",
      "name": "Daily Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1824,
        -32
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "39ba5c52-e08b-4046-b5d1-8cd971fa1373",
      "name": "Set Search Config",
      "type": "n8n-nodes-base.set",
      "position": [
        -1600,
        -32
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "lang-arr",
              "name": "languages",
              "type": "array",
              "value": "={{ [\"python\",\"javascript\",\"typescript\",\"go\",\"rust\"] }}"
            },
            {
              "id": "minstars",
              "name": "minStars",
              "type": "number",
              "value": 50
            },
            {
              "id": "topn",
              "name": "topN",
              "type": "number",
              "value": 3
            },
            {
              "id": "since",
              "name": "since",
              "type": "string",
              "value": "daily"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "b7172a51-0497-48d9-a4c4-6e02328d7687",
      "name": "Split Languages",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -1376,
        -32
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "languages"
      },
      "typeVersion": 1
    },
    {
      "id": "5eb62de2-4efd-4ae2-ac75-b01caf4d9cb5",
      "name": "Fetch Trending Github Page",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1152,
        -32
      ],
      "parameters": {
        "url": "=https://github.com/trending/{{ $json.languages }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        },
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "since",
              "value": "={{ $('Set Search Config').first().json.since }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (compatible; blankarray-trend-bot/1.0)"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "5b36af93-57b3-475e-8361-2f00d00600a8",
      "name": "Extract Repo Blocks from HTML",
      "type": "n8n-nodes-base.html",
      "position": [
        -928,
        -32
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "repoBlocks",
              "cssSelector": "article.Box-row",
              "returnArray": true,
              "returnValue": "html"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1408ac59-306a-4aac-b5a2-d6558f9c90b9",
      "name": "Split Repo Blocks",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -704,
        -32
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "repoBlocks"
      },
      "typeVersion": 1
    },
    {
      "id": "67fa4900-7ad2-448c-b297-aec147d9bcc2",
      "name": "Parse Repository Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -256,
        -32
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c1",
              "name": "repoName",
              "type": "string",
              "value": "={{ ($json.name || '').trim() }}"
            },
            {
              "id": "c2",
              "name": "repoFullName",
              "type": "string",
              "value": "={{ ($json.repoPath || '').trim().replace(/^\\//,'') }}"
            },
            {
              "id": "c3",
              "name": "repoUrl",
              "type": "string",
              "value": "={{ 'https://github.com' + ($json.repoPath || '').trim() }}"
            },
            {
              "id": "c4",
              "name": "description",
              "type": "string",
              "value": "={{ ($json.description || '').trim() || 'No description' }}"
            },
            {
              "id": "c5",
              "name": "searchLanguage",
              "type": "string",
              "value": "={{ ($json.repoLanguage || $('Split Languages').item.json.language || 'unknown').trim() }}"
            },
            {
              "id": "c6",
              "name": "starsToday",
              "type": "number",
              "value": "={{ parseInt((($json.starsTodayText || '0').match(/[\\d,]+/) || ['0'])[0].replace(/,/g,''), 10) || 0 }}"
            },
            {
              "id": "c7",
              "name": "totalStars",
              "type": "number",
              "value": "={{ parseInt((($json.totalStarsText || '0').match(/[\\d,]+/) || ['0'])[0].replace(/,/g,''), 10) || 0 }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "2b832345-1e0d-48c3-8506-fcf7a0ead1c8",
      "name": "Filter by Min Stars",
      "type": "n8n-nodes-base.filter",
      "position": [
        -32,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f1",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.starsToday }}",
              "rightValue": "={{ $('Set Search Config').first().json.minStars }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e519caa1-fde1-4738-b8b3-a7492a98649c",
      "name": "Filter Out Archived Repos",
      "type": "n8n-nodes-base.filter",
      "position": [
        192,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f2",
              "operator": {
                "type": "boolean",
                "operation": "notEquals"
              },
              "leftValue": "={{ $json.archived }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "368ed3dc-7759-4b9a-a3eb-4695ffd3e589",
      "name": "Sort by Stars Today",
      "type": "n8n-nodes-base.sort",
      "position": [
        640,
        -32
      ],
      "parameters": {
        "options": {},
        "sortFieldsUi": {
          "sortField": [
            {
              "order": "descending",
              "fieldName": "starsToday"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "10fc42a6-3af7-4250-a51a-6777b64712b5",
      "name": "Take Top N Results",
      "type": "n8n-nodes-base.limit",
      "position": [
        864,
        -32
      ],
      "parameters": {
        "maxItems": "={{ $('Set Search Config').first().json.topN }}"
      },
      "typeVersion": 1
    },
    {
      "id": "f31d4f37-0657-4cf2-bae8-dd3b99a1bb5b",
      "name": "Aggregate Repo List",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1088,
        -32
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "repos"
      },
      "typeVersion": 1
    },
    {
      "id": "f68385f1-7255-436c-b233-5ea53fe4a92a",
      "name": "If Repos Found",
      "type": "n8n-nodes-base.if",
      "position": [
        1312,
        -32
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f3",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.repos.length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "e94dbd86-ddea-4895-bb75-77385a10ce1a",
      "name": "Build Slack Notification Message",
      "type": "n8n-nodes-base.set",
      "position": [
        1536,
        -128
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "msg1",
              "name": "slackMessage",
              "type": "string",
              "value": "={{ '\ud83d\udd25 *Top ' + $json.repos.length + ' GitHub Trending Repos (' + $('Set Search Config').first().json.since + ')*\\n\\n' + $json.repos.map((r, i) => (i+1) + '. <' + r.repoUrl + '|' + r.repoFullName + '> _(' + (r.searchLanguage || 'N/A') + ')_\\n   \u2b50 ' + Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(r.starsToday) + ' stars today').join('\\n\\n') }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "4a1cec21-fcde-440f-b4f5-65bf63673738",
      "name": "Post to Slack API",
      "type": "n8n-nodes-base.slack",
      "position": [
        1760,
        -128
      ],
      "parameters": {
        "text": "={{ $json.slackMessage }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0B8VH1M5PX",
          "cachedResultName": "general"
        },
        "otherOptions": {
          "mrkdwn": true,
          "unfurl_links": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "161cb3a4-6e88-4f4e-8955-eaedd35dff20",
      "name": "No Repos Found",
      "type": "n8n-nodes-base.noOp",
      "position": [
        1536,
        64
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "5694094d-03a2-4e35-a8a1-56b09aede421",
      "name": "Extract Repository Details",
      "type": "n8n-nodes-base.html",
      "position": [
        -480,
        -32
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "dataPropertyName": "repoBlocks",
        "extractionValues": {
          "values": [
            {
              "key": "name",
              "cssSelector": "h2.h3.lh-condensed a"
            },
            {
              "key": "repoPath",
              "attribute": "href",
              "cssSelector": "h2.h3.lh-condensed a",
              "returnValue": "attribute"
            },
            {
              "key": "description",
              "cssSelector": "p.col-9"
            },
            {
              "key": "repoLanguage",
              "cssSelector": "span[itemprop=\"programmingLanguage\"]"
            },
            {
              "key": "starsTodayText",
              "cssSelector": "span.d-inline-block.float-sm-right"
            },
            {
              "key": "totalStarsText",
              "cssSelector": "a[href$=\"/stargazers\"]"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "e97519f2-3c56-4923-8274-d4c9bbad897c",
      "name": "Format Repository Data",
      "type": "n8n-nodes-base.set",
      "position": [
        416,
        -32
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "e36954fb-2d6c-4f5e-b8f7-f9b2c8802cab",
              "name": "starsToday",
              "type": "number",
              "value": "={{ $('Parse Repository Data').item.json.starsToday }}"
            },
            {
              "id": "9a38b1f2-11c5-4a2a-b9c1-7a7183bc6d01",
              "name": "repoName",
              "type": "string",
              "value": "={{ $('Parse Repository Data').item.json.repoName }}"
            },
            {
              "id": "f5b8c9d4-82a1-4e7c-b6f2-1b83d9a74c22",
              "name": "repoFullName",
              "type": "string",
              "value": "={{ $('Parse Repository Data').item.json.repoFullName }}"
            },
            {
              "id": "c7d9e0f5-93b2-4f8d-c7g3-2c94e0b85d33",
              "name": "repoUrl",
              "type": "string",
              "value": "={{ $('Parse Repository Data').item.json.repoUrl }}"
            },
            {
              "id": "d8e0f1a6-04c3-5g9e-d8h4-3d05f1c96e44",
              "name": "searchLanguage",
              "type": "string",
              "value": "={{ $('Parse Repository Data').item.json.searchLanguage }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "3521f9d2-39b9-4a4d-a9e4-2c5bcb4ed90c",
  "nodeGroups": [],
  "connections": {
    "Daily Trigger": {
      "main": [
        [
          {
            "node": "Set Search Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Repos Found": {
      "main": [
        [
          {
            "node": "Build Slack Notification Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No Repos Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Languages": {
      "main": [
        [
          {
            "node": "Fetch Trending Github Page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Search Config": {
      "main": [
        [
          {
            "node": "Split Languages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Repo Blocks": {
      "main": [
        [
          {
            "node": "Extract Repository Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Take Top N Results": {
      "main": [
        [
          {
            "node": "Aggregate Repo List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Repo List": {
      "main": [
        [
          {
            "node": "If Repos Found",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter by Min Stars": {
      "main": [
        [
          {
            "node": "Filter Out Archived Repos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sort by Stars Today": {
      "main": [
        [
          {
            "node": "Take Top N Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Repository Data": {
      "main": [
        [
          {
            "node": "Filter by Min Stars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Repository Data": {
      "main": [
        [
          {
            "node": "Sort by Stars Today",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Out Archived Repos": {
      "main": [
        [
          {
            "node": "Format Repository Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Repository Details": {
      "main": [
        [
          {
            "node": "Parse Repository Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Trending Github Page": {
      "main": [
        [
          {
            "node": "Extract Repo Blocks from HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Repo Blocks from HTML": {
      "main": [
        [
          {
            "node": "Split Repo Blocks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Slack Notification Message": {
      "main": [
        [
          {
            "node": "Post to Slack API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}