diff --git a/src/app.js b/src/app.js index cda1182..d589803 100644 --- a/src/app.js +++ b/src/app.js @@ -1,4 +1,5 @@ import { memoryUsage } from "process"; +import { fork } from "child_process"; import { Telegraf } from "telegraf"; import dotenv from "dotenv"; import mongoose from "mongoose"; @@ -112,6 +113,8 @@ async function main() { } }); + fork(pathTo("./hackernews.js"), { detached: false }); + // For more information about what this is, please refer to: // https://nodejs.org/api/process.html#process_process_memoryusage terminal.log( diff --git a/src/hackernews.js b/src/hackernews.js new file mode 100644 index 0000000..5e33d48 --- /dev/null +++ b/src/hackernews.js @@ -0,0 +1,47 @@ +import { Telegraf } from "telegraf"; +import dotenv from "dotenv"; +import { pathTo } from "#utils/path.js"; +import { run } from "#services/hackernews.js"; +import { sentry } from "#utils/logger.js"; + +dotenv.config({ path: pathTo(import.meta.url, "../.env") }); + +const bot = new Telegraf(process.env.BOT_TOKEN); + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +for (;;) { + if (new Date().getUTCHours() === 0 || new Date().getUTCHours() === 11) { + let done = false; + + // eslint-disable-next-line no-await-in-loop + await run(bot) + .catch((error) => { + sentry.captureException(error); + }) + .finally(() => { + done = true; + }); + + while (!done) { + // wait + } + } + + // How long until the next time we have to send message? + const now = new Date(); + let nextTime; + + if (now.getUTCHours() > 11) { + // The next time is 00:00 AM + nextTime = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDay() + 1, 0, 0, 0, 0); + } else if (now.getHours() < 11) { + // The next time is 11:00 AM + nextTime = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDay(), 11, 0, 0, 0); + } + + // eslint-disable-next-line no-await-in-loop + await sleep(nextTime.getTime() - now.getTime()); +} diff --git a/src/services/hackernews/index.js b/src/services/hackernews/index.js new file mode 100644 index 0000000..7f812c6 --- /dev/null +++ b/src/services/hackernews/index.js @@ -0,0 +1,81 @@ +import got from "got"; +import { sanitize } from "#utils/sanitize.js"; + +async function getStories() { + const response = await got.get("https://hacker-news.firebaseio.com/v0/topstories.json", { + headers: { + Accept: "application/json" + }, + responseType: "json" + }); + + return response.body; +} + +async function getItem(id) { + const response = await got.get(`https://hacker-news.firebaseio.com/v0/item/${id}.json`, { + headers: { + Accept: "application/json" + }, + responseType: "json" + }); + + return response.body; +} + +/** + * + * @param {import('telegraf').Telegraf>} context + */ +export async function run(context) { + // Get all stories + const allStories = await getStories(); + + // Create an array for resulting stories + const resultingStories = []; + + const last24Hours = new Date(); + last24Hours.setHours(last24Hours.getHours() - 24); + + // Iterate each story + for (const story of allStories) { + // If the resultingStories' length is 10, we'll stop. + if (resultingStories.length === 10) break; + + // eslint-disable-next-line no-await-in-loop + const item = await getItem(story); + // We only want stories. Not job, comment, or poll + if (item?.type !== "story") continue; + // We only want stories that's high in rating + if (item?.score < 100) continue; + // It's useless if it's dead. + if (item?.dead) continue; + // We don't want old stories to come up. Limit this to last 24 hours. + if (item?.time && item.time < last24Hours.getTime()) continue; + + resultingStories.push(item); + } + + // Now we build the message + let message = "Today's Hacker News\n\n"; + for (let i = 1; i <= resultingStories.length; i++) { + const story = resultingStories[i-1]; + + message += `${i}. ${story.title}\n`; + if (story?.text) { + message += sanitize(story.text, true); + message += "\n"; + } + + if (i !== resultingStories.length) { + message += "\n"; + } + } + + // Finally, send the message + await context.telegram.sen1dMessage( + process.env.HOME_GROUP_ID, + message, + { parse_mode: "HTML", disable_web_page_preview: false } + ); +} \ No newline at end of file