From 50338ec07b247857598862dbc25c51fb0e973be3 Mon Sep 17 00:00:00 2001 From: Artemy Date: Fri, 18 Aug 2023 20:50:26 +0300 Subject: [PATCH] feat: stackoverflow parser for questions page --- src/handlers/main.ts | 3 ++ src/handlers/stackoverflow/main.ts | 31 +++++++++++++++++++ src/handlers/stackoverflow/post-parser.ts | 9 ++++++ src/handlers/stackoverflow/questions-posts.ts | 26 ++++++++++++++++ static/get.css | 10 ++++-- templates/get.ejs | 1 + 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 src/handlers/stackoverflow/main.ts create mode 100644 src/handlers/stackoverflow/post-parser.ts create mode 100644 src/handlers/stackoverflow/questions-posts.ts diff --git a/src/handlers/main.ts b/src/handlers/main.ts index 6769283..0fbb172 100644 --- a/src/handlers/main.ts +++ b/src/handlers/main.ts @@ -15,6 +15,7 @@ import { LocalResourceError, NotHtmlMimetypeError, } from "../errors/main"; +import stackoverflow from "./stackoverflow/main"; export default async function handlePage( url: string, // remote URL @@ -60,11 +61,13 @@ type EngineFunction = (window: DOMWindow) => Promise; export const engines: Engines = { readability, google, + stackoverflow, }; export const engineList: string[] = Object.keys(engines); const fallback: Engines = { + "stackoverflow.com": engines.stackoverflow, "www.google.com": engines.google, "*": engines.readability, }; diff --git a/src/handlers/stackoverflow/main.ts b/src/handlers/stackoverflow/main.ts new file mode 100644 index 0000000..4ed4878 --- /dev/null +++ b/src/handlers/stackoverflow/main.ts @@ -0,0 +1,31 @@ +import { IHandlerOutput } from "../handler.interface"; +import { DOMWindow } from "jsdom"; +import { EngineParseError } from "../../errors/main"; +import qPostsHandler from "./questions-posts"; + +export default async function stackoverflow( + window: DOMWindow +): Promise { + const url = new URL(window.location.href); + + const path = url.pathname.split("/").filter((p) => p !== ""); + + let result: IHandlerOutput = { + content: "", + textContent: "", + title: "", + lang: "", + }; + + if (path[0] === "questions") { + if (path.length === 3) { + result = await qPostsHandler(window); + } else if (path.length === 1) { + result.content = "questions"; + } else { + throw new EngineParseError("Invalid URL [stackoverflow]"); + } + } + + return result; +} diff --git a/src/handlers/stackoverflow/post-parser.ts b/src/handlers/stackoverflow/post-parser.ts new file mode 100644 index 0000000..e5e6999 --- /dev/null +++ b/src/handlers/stackoverflow/post-parser.ts @@ -0,0 +1,9 @@ +export default function postParser(el: Element | null): string { + if (!el) { + return ""; + } + const body = el.querySelector(".js-post-body")?.innerHTML || ""; + const voteCount = el.querySelector(".js-vote-count")?.textContent || ""; + + return `

${voteCount} votes

${body}`; +} diff --git a/src/handlers/stackoverflow/questions-posts.ts b/src/handlers/stackoverflow/questions-posts.ts new file mode 100644 index 0000000..3d011f8 --- /dev/null +++ b/src/handlers/stackoverflow/questions-posts.ts @@ -0,0 +1,26 @@ +import { DOMWindow } from "jsdom"; +import { IHandlerOutput } from "../handler.interface"; +import postParser from "./post-parser"; + +export default async function qPostsHandler( + window: DOMWindow +): Promise { + const questionEl = window.document.getElementById("question"); + const question = postParser(questionEl); + + const title = + window.document.querySelector(".question-hyperlink")?.innerHTML || ""; + + const allAnswers = [...window.document.querySelectorAll(".answer")]; + + const answers = allAnswers.map((a) => postParser(a)); + + return { + content: `${question}
${answers.length} answers
${answers.join( + "
" + )}`, + textContent: "question", + title, + lang: "en", + }; +} diff --git a/static/get.css b/static/get.css index 0a03379..8966534 100644 --- a/static/get.css +++ b/static/get.css @@ -6,7 +6,8 @@ font-size: 0.9rem; } .title { - font-weight: 500; + font-weight: 1000; + margin: 1rem; } a { @@ -28,11 +29,14 @@ table { overflow-x: auto; } -img, picture, video { +img, +picture, +video { max-width: 100%; height: auto; } -frame, iframe { +frame, +iframe { max-width: 100%; } diff --git a/templates/get.ejs b/templates/get.ejs index 41490ef..20cbf27 100644 --- a/templates/get.ejs +++ b/templates/get.ejs @@ -18,6 +18,7 @@
<%= parsed.title %>
+
<%- parsed.content %>