visit
In my case, the easiest way to notifications is to use Telegram Bots. I can quickly set up and start using it immediately.
To make the dashboard accessible on any device, at any time and from anywhere, it should be hosted on services like AWS or IONOS.
Here's a simple example of Puppeteer in action, which performs the following steps:
import puppeteer from 'puppeteer';
(async (searchValue) => {
// Launch the browser and open a new blank page
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Search
await page.goto('//google.com');
await page.locator('textarea').fill(searchValue);
await page.$eval('form', form => form.submit());
// Go to the first link
await page.waitForNavigation();
await page.click(`div[data-async-context^="query:"] a`);
// Take a screenshot
await page.waitForNavigation();
await page.screenshot({path: './screenshot.png'});
await browser.close();
})("HackerNoon");
The following code collects data from HackerNoon Top Stories and HackerNoon Jobs every hour, generates simple HTML content from this data, and then serves this HTML content when we receive an HTTP request. It's quite straightforward.
import http from 'http';
import * as scraper from './scraper.js';
(async () => {
let scrapedHtml = 'Try again later...';
http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'})
res.end(scrapedHtml);
}).listen(8080);
scrapedHtml = await scrapeAll();
setInterval(async () => scrapedHtml = await scrapeAll(), 60*60*1000);
})();
async function scrapeAll() {
const browser = await scraper.launchBrowser();
const [stories, jobs] = await Promise.all([
scraper.getTopStories(browser),
scraper.getJobs('Software Engineer', browser)
]);
await browser.close();
return `
<h2>Top Stories</h2>
<ul>${stories.map(e => linkToHtml(e.title, e.url)).join('')}</ul>
<h2>Jobs</h2>
<ul>${jobs.map(e => linkToHtml(e.title, e.url)).join('')}</ul>
`;
}
const linkToHtml = (title, url) => {
return `<li>
<a target="_blank" href="${url}">
${title}
</a>
</li>`;
}
import puppeteer, {Browser} from 'puppeteer';
/**
*
* @returns {Browser}
*/
export async function launchBrowser() {
return await puppeteer.launch();
}
/**
*
* @param {Browser} browser
* @returns {[{title: String, url: String}]}
*/
export async function getTopStories(browser) {
const page = await browser.newPage();
await page.goto('//gzht888.com/tagged/hackernoon-top-story');
// Wait for articles
await page.waitForSelector('main .story-card');
// Get articles
const res = [];
const articles = await page.$$('main .story-card h2 a');
for (const article of articles) {
res.push(
await article.evaluate(el => ({
"title": el.textContent,
"url": el.href,
}))
);
}
return res;
}
/**
*
* @param {String} keyword
* @param {Browser} browser
* @returns {[{title: String, url: String}]}
*/
export async function getJobs(keyword, browser) {
const page = await browser.newPage();
await page.goto('//jobs.gzht888.com');
// Search
await page.locator('#search-jobkeyword input').fill(keyword);
await page.click('button[type=submit]');
// Wait for result
await page.waitForSelector('.job-list-item');
// Get jobs
const res = [];
const items = await page.$$('.job-list-item');
for (const item of items) {
res.push(
await item.evaluate(el => ({
"title": [
el.querySelector('.job-name'),
...el.querySelectorAll('.desktop-view span')
].map(e => e.textContent).join(', '),
"url": el.href,
}))
);
}
return res;
}