| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- // 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}`);
- }
|