UNPKG

rsshub

Version:
151 lines (134 loc) • 4.83 kB
import { Route } from '@/types'; import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; const ROW_COUNT = 100; const WEBSITE_URL = 'https://layoffs.fyi'; const ENTRY_URL = 'https://airtable.com/embed/shrqYt5kSqMzHV9R5/tbl8c8kanuNB6bPYr'; const AIRTABLE_HOST = 'https://airtable.com'; /** * getMapping can convert either an array or an object * of a specific structure into a Map, where the key is * the name and the value is the Id. It will only parse * entries with 'name' and 'id' in it * * Inputs: * obj = [{name: 'Apple', id: 1}, {name: 'Peach', id: 2}] * Returns: * [{1: 'Apple', 2: 'Peach'}, {Apple: 1, Peach: 2}] * * Inputs: * obj = {Apple: {name: 'Apple', id: 1}, Peach: {name: 'Peach', id: 2}} * Returns: * [{1: 'Apple', 2: 'Peach'}, {Apple: 1, Peach: 2}] */ const getMappings = function (obj) { const mapping = new Map(); const reverseMapping = new Map(); for (const key in obj) { if ('name' in obj[key] && 'id' in obj[key]) { reverseMapping.set(obj[key].name, obj[key].id); mapping.set(obj[key].id, obj[key].name); } } return [mapping, reverseMapping]; }; export const route: Route = { path: '/', radar: [ { source: ['layoffs.fyi/'], target: '', }, ], name: 'Unknown', maintainers: ['BrandNewLifeJackie26'], handler, url: 'layoffs.fyi/', }; async function handler() { const headers = { 'x-airtable-application-id': 'app1PaujS9zxVGUZ4', 'x-airtable-inter-service-client': 'webClient', 'x-requested-with': 'XMLHttpRequest', 'x-time-zone': 'America/Los_Angeles', }; // Get the latest data source url let dataSourceUrl = await cache.get(ENTRY_URL); let cacheInvalid = false; let response; if (dataSourceUrl) { // Try to fetch data, // it may fail due to outdated url try { response = await got({ method: 'get', url: dataSourceUrl, headers, }); if (response.statusCode >= 400) { cacheInvalid = true; } } catch { cacheInvalid = true; } } else { cacheInvalid = true; } if (cacheInvalid) { // Refetch the data source link from entry page const sourcePage = await got({ method: 'get', url: ENTRY_URL, }); const $ = load(sourcePage.data); dataSourceUrl = AIRTABLE_HOST + $('script') .text() .match(/urlWithParams: "(.*?)"/)[1] .replaceAll(String.raw`\u002F`, '/'); // Cache it again cache.set(ENTRY_URL, dataSourceUrl); // Refetch the data source response = await got({ method: 'get', url: dataSourceUrl, headers, }); } // Get data from data source const data = response.data.data; const table = data.table; // Columns are represented by special IDs const columnReverseMapping = getMappings(table.columns)[1]; const companyColumnId = columnReverseMapping.get('Company'); const dateAddedColumnId = columnReverseMapping.get('Date Added'); const numOfLaidOffColumnId = columnReverseMapping.get('# Laid Off'); const sourceColumnId = columnReverseMapping.get('Source'); const countryColumnId = columnReverseMapping.get('Country'); const countryMapping = getMappings(table.columns.find((col) => col.name === 'Country').typeOptions.choices)[0]; const rows = table.rows.slice(0, ROW_COUNT); return { title: 'Tech layoff data feed from layoffs.fyi', link: WEBSITE_URL, description: 'This feed gets tech layoff data from layoffs.fyi and display them in a user friendly way', item: rows.map((row) => { const rowContent = row.cellValuesByColumnId; const company = rowContent[companyColumnId]; const dateAdded = parseDate(rowContent[dateAddedColumnId]); const source = rowContent[sourceColumnId]; const numOfLaidOff = rowContent[numOfLaidOffColumnId] || 'some'; const country = countryMapping.get(rowContent[countryColumnId]); const title = `${company} Layoffs Happening!`; const description = `${company} lays off ${numOfLaidOff} employees in ${country}. For more details, please visit ${source}.`; return { title, description, // the article content pubDate: dateAdded, // Data publish date link: source, // Laid off source link }; }), }; }