import * as cheerio from 'cheerio'; import escapeHTML from 'escape-html'; import { ACCESS_TOKEN, SERVER_URL, SOURCE_ROOM_ID, TARGET_ROOM_ID } from "./conf.js"; import {readFileSync, writeFileSync} from "fs"; async function fetchMatrix(url, options = {}) { options.headers ||= {}; options.headers.Authorization ||= `Bearer ${ACCESS_TOKEN}`; url = SERVER_URL + url; while (true) { console.debug("fetch", url); try { var res = await fetch(url, options); if (res.ok) break; console.warn("status:", res.status); if (res.status == 429) { var {retry_after_ms} = await res.json(); } } catch (error) { console.error(error); } await new Promise(r => setTimeout(r, retry_after_ms || 60000)); } if (!res.ok) throw new Error(`HTTP ${res.status} ${await res.text()}`); return res; } async function getMatrixEvents(room_id, until_event_id) { var events = []; do { var url = `/_matrix/client/v3/rooms/${encodeURIComponent(room_id)}/messages?dir=b&limit=50`;//use 1000 for initial fill if (end) url += `&from=${end}`; var {chunk, end} = await fetchMatrix(url).then(res => res.json()); console.log(`got ${chunk.length}`); for (let event of chunk) { if (until_event_id && event.event_id == until_event_id) return events; events.push(event); } } while (end) return events; } async function getNewMatrixEvents(room_id) { var fn = `last_event_id_${room_id.replace(/[^\w]/g,'')}`; try { var last_event_id = readFileSync(fn, "utf8"); } catch (error) { console.log(error.message); } var events = await getMatrixEvents(room_id, last_event_id); for (let event of events) { event.hasBeenProcessed = function () { writeFileSync(fn, event.event_id); } } return events; } function getHrefs(html) { const $ = cheerio.load(html); const links = []; $('a[href]').each((_, el) => { links.push($(el).attr('href')); }); return links; } var events = await getNewMatrixEvents(SOURCE_ROOM_ID); for (let event of events.reverse()) { if (typeof event.content == "object") { let links; if (event.content.format == "org.matrix.custom.html" && typeof event.content.formatted_body == "string") { links = getHrefs(event.content.formatted_body); } else if (typeof event.content.body == "string") { links = event.content.body.match(/https?:\/\/[^\s<>()]+/g); } if (links) links = links.filter(l => !l.startsWith("https://matrix.to/#/")); if (links?.length) { let event_url = `https://matrix.to/#/${encodeURIComponent(event.room_id)}/${encodeURIComponent(event.event_id)}`; await fetchMatrix(`/_matrix/client/v3/rooms/${encodeURIComponent(TARGET_ROOM_ID)}/send/m.room.message/${Math.random()}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ msgtype: "m.text", body: `${links.join(" ")} ${event_url}`, format: "org.matrix.custom.html", formatted_body: `${links.map(l => `${escapeHTML(l)}`).join(" ")} 🔗` }) }); } } event.hasBeenProcessed(); }