Add RSS feed and GitHub Action workflow for automatic updates

This commit is contained in:
PawiX25 2025-03-23 18:52:09 +01:00
parent 242c3c4a55
commit 5612c9c601
5 changed files with 151 additions and 0 deletions

36
.github/workflows/generate-rss.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: Generate RSS Feed
on:
push:
paths:
- 'data.yml'
branches:
- main
workflow_dispatch:
jobs:
generate-rss:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm install js-yaml
- name: Generate RSS feed
run: node generate-rss.js
- name: Commit and push if changed
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add feed.xml
git diff --quiet && git diff --staged --quiet || git commit -m "Update RSS feed"
git push

View file

@ -12,6 +12,7 @@
- **Deadline Indicators:** Visual indicators for program deadlines, highlighting urgent and very urgent statuses to help prioritize participation.
- **Real-time Deadline Updates:** Deadlines are updated in real-time to reflect the current status, ensuring information is always up-to-date.
- **Responsive Design:** Optimized for various screen sizes and devices, providing a seamless experience on desktops, tablets, and mobile devices.
- **RSS Feed:** Subscribe to get notifications about active YSWS programs in your favorite RSS reader.
## Getting Started
@ -34,6 +35,14 @@
5. **Toggle Theme:**
- Click the 🌙/☀️ button to switch between dark and light modes.
## RSS Feed
You can subscribe to updates about active YSWS programs using our RSS feed:
- **Feed URL**: `https://ysws.hackclub.com/feed.xml`
This feed is automatically updated whenever new programs are added or existing programs' statuses change. You can use this feed with any RSS reader like Feedly, Inoreader, or apps like Glance to get notified about new opportunities.
## Project Structure
- **index.html:** The main HTML file containing the container for program cards and the modal structure.

86
generate-rss.js Normal file
View file

@ -0,0 +1,86 @@
const fs = require('fs');
const jsyaml = require('js-yaml');
const path = require('path');
function generateRSS(programs) {
const host = 'https://ysws.hackclub.com';
const now = new Date().toUTCString();
const activePrograms = Object.values(programs)
.flat()
.filter(program => program.status === 'active')
.sort((a, b) => {
if (!a.deadline) return 1;
if (!b.deadline) return -1;
return new Date(a.deadline) - new Date(b.deadline);
});
let rss = `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Hack Club YSWS Programs</title>
<link>${host}</link>
<description>Active "You Ship, We Ship" programs from Hack Club</description>
<language>en-us</language>
<lastBuildDate>${now}</lastBuildDate>
<atom:link href="${host}/feed.xml" rel="self" type="application/rss+xml" />
`;
activePrograms.forEach(program => {
const pubDate = program.pubDate ? new Date(program.pubDate).toUTCString() : now;
const deadline = program.deadline ?
`<p><strong>Deadline:</strong> ${new Date(program.deadline).toLocaleDateString('en-US', {
year: 'numeric', month: 'long', day: 'numeric'
})}</p>` : '';
const channelRef = program.slackChannel ? program.slackChannel.replace(/^#+/, '#') : '';
rss += `
<item>
<title>${escapeXML(program.name)}</title>
<link>${program.website || host}</link>
<guid isPermaLink="false">${program.name}-${Date.now()}</guid>
<pubDate>${pubDate}</pubDate>
<description><![CDATA[
<p>${escapeXML(program.description)}</p>
${deadline}
${program.detailedDescription ? `<p>${escapeXML(program.detailedDescription)}</p>` : ''}
${channelRef ? `<p>Join the discussion in <a href="${program.slack}">${channelRef}</a></p>` : ''}
]]></description>
</item>`;
});
rss += `
</channel>
</rss>`;
return rss;
}
function escapeXML(text) {
if (!text) return '';
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
}
async function main() {
try {
const dataFile = path.join(__dirname, 'data.yml');
const fileContent = fs.readFileSync(dataFile, 'utf8');
const data = jsyaml.load(fileContent);
const rssFeed = generateRSS(data);
fs.writeFileSync(path.join(__dirname, 'feed.xml'), rssFeed);
console.log('RSS feed generated successfully!');
} catch (error) {
console.error('Error generating RSS feed:', error);
process.exit(1);
}
}
main();

View file

@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="alternate" type="application/rss+xml" title="Hack Club YSWS Programs" href="/feed.xml" />
<script>
const favicons = [
"logos/(ch)airtable.png",
@ -135,6 +136,10 @@
</div>
<nav class="main-nav">
<a href="https://hackclub.com/slack" target="_blank" rel="noopenner noreferrer" class="nav-link">Join Slack</a>
<a href="/feed.xml" target="_blank" rel="noopenner noreferrer" class="nav-link rss-link">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 0 1 9 9"></path><path d="M4 4a16 16 0 0 1 16 16"></path><circle cx="5" cy="19" r="1"></circle></svg>
RSS Feed
</a>
<a href="https://github.com/hackclub/YSWS-Catalog" target="_blank" rel="noopenner noreferrer" class="nav-link">GitHub</a>
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode">🌙</button>
</nav>

View file

@ -1914,3 +1914,18 @@ body.modal-open {
html {
scroll-behavior: smooth;
}
.rss-link {
display: flex;
align-items: center;
gap: 0.5rem;
}
.rss-link svg {
color: var(--primary);
transition: transform 0.3s ease;
}
.rss-link:hover svg {
transform: scale(1.2);
}