101 lines
3.0 KiB
JavaScript
101 lines
3.0 KiB
JavaScript
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 => `<a href="${encodeURI(l)}">${escapeHTML(l)}</a>`).join(" ")} <a href="${encodeURI(event_url)}">🔗</a>`
|
|
})
|
|
});
|
|
}
|
|
}
|
|
event.hasBeenProcessed();
|
|
} |