// Fetches the public Telegram channel feed (t.me/s/...) and saves the latest // posts for the home-page Telegram widget. Run manually: npm run fetch-news import * as cheerio from "cheerio"; import { mkdir, writeFile } from "node:fs/promises"; import path from "node:path"; const CHANNEL = "scientia_ru"; const UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"; const res = await fetch(`https://t.me/s/${CHANNEL}`, { headers: { "User-Agent": UA } }); if (!res.ok) { console.error(`Failed to fetch channel: HTTP ${res.status}`); process.exit(1); } const $ = cheerio.load(await res.text()); const posts = []; $(".tgme_widget_message").each((_, el) => { const $el = $(el); const textNode = $el.find(".tgme_widget_message_text").first(); // Keep basic formatting tags, drop telegram-internal spans/attrs textNode.find("a, b, i, u, s, strong, em, code, br").each((__, keep) => { const attrs = $(keep).attr() || {}; for (const name of Object.keys(attrs)) { if (name !== "href") $(keep).removeAttr(name); } }); const text = textNode.html()?.trim() || ""; const datetime = $el.find("time").first().attr("datetime") || ""; const photoStyle = $el.find(".tgme_widget_message_photo_wrap").first().attr("style") || ""; const photo = photoStyle.match(/url\('([^']+)'\)/)?.[1]?.split("?")[0] || ""; const video = $el.find(".tgme_widget_message_video_wrap, .tgme_widget_message_video_player").length > 0; const link = $el.find(".tgme_widget_message_date").first().attr("href") || `https://t.me/${CHANNEL}`; if (text || photo || video) posts.push({ text, datetime, photo, video, link }); }); // t.me/s serves the first page oldest-first — flip so the newest post comes first const latest = posts.slice(0, 12).reverse(); await mkdir(path.dirname("src/data/telegram-news.json"), { recursive: true }); await writeFile("src/data/telegram-news.json", JSON.stringify(latest, null, 2)); console.log(`saved ${latest.length} posts`); // Channel avatar for the widget header const avatar = await fetch(`https://t.me/i/userpic/320/${CHANNEL}.jpg`, { headers: { "User-Agent": UA } }); if (avatar.ok) { const buf = Buffer.from(await avatar.arrayBuffer()); if (buf.length > 500) { await writeFile("public/images/telegram-avatar.jpg", buf); console.log("avatar saved"); } else { console.log("avatar looks empty, skipped"); } } else { console.log(`avatar fetch failed: HTTP ${avatar.status}`); }