110 lines
3.6 KiB
JavaScript
110 lines
3.6 KiB
JavaScript
const masto = require("masto");
|
|
const rtj = require("rss-to-json");
|
|
const config = require("./config.json");
|
|
const cheerio = require("cheerio");
|
|
const axios = require("axios");
|
|
const fs = require("fs");
|
|
|
|
let prev;
|
|
|
|
(async function() {
|
|
if (config.prev) prev = config.prev;
|
|
config.spoiler = config.spoiler.split("{{username}}").join(config.account);
|
|
await runSync();
|
|
})();
|
|
|
|
async function runSync() {
|
|
const client = await masto.login(config.credentials)
|
|
let rss = await (await rtj.parse(`${config.instance}/${config.account}/rss?random=${Date.now() * 1}`)).items
|
|
let id = lastPost(rss).link.split("/status/")[1].split("#")[0];
|
|
if (prev !== id) {
|
|
let post = lastPost(rss);
|
|
console.log(`[mirror] Starting to mirror https://twitter.com/i/status/${id}`);
|
|
console.log(`[mirror] Extracting media...`);
|
|
let assets = await extractAssets(post.description);
|
|
console.log(`[mirror] Extracted media, ${assets.length} assets found`);
|
|
|
|
console.log(`[mirror] Attaching media...`);
|
|
let m = [];
|
|
for (let a in assets) {
|
|
let at = await client.mediaAttachments.create({
|
|
file: fs.createReadStream(assets[a].location)
|
|
});
|
|
m.push(at.id);
|
|
console.log(`[mirror] Attached asset ${(a + 1)}`);
|
|
}
|
|
console.log(`[mirror] Attached media.`)
|
|
if (m.length == 0) m = null;
|
|
|
|
|
|
if (post.title == "Image") post.title = "";
|
|
else if (config.linkOriginal) post.title = `${post.title}\n\n`;
|
|
|
|
let status;
|
|
status = post.title;
|
|
if (config.linkOriginal) status = `${status}[Original tweet: https://twitter.com/i/status/${id}]`;
|
|
|
|
let p = await client.statuses.create({
|
|
status: status,
|
|
visibility: config.visibility,
|
|
mediaIds: m,
|
|
spoilerText: config.spoiler
|
|
});
|
|
|
|
console.log(`[mirror] Successfully mirrored ${id} to ${p.url}`);
|
|
setTimeout(async function() {
|
|
config.prev = id;
|
|
prev = id;
|
|
fs.writeFileSync(`${__dirname}/config.json`, JSON.stringify(config, null, 2));
|
|
runSync();
|
|
}, config.updateInterval);
|
|
} else {
|
|
console.log(`[mirror] Already mirrored ${id}, trying again in ${(config.updateInterval / 1000).toFixed(0)} seconds.`);
|
|
setTimeout(async function() {
|
|
runSync();
|
|
}, config.updateInterval);
|
|
}
|
|
}
|
|
|
|
function detectType(text) {
|
|
if (typeof text == "object") text = text.title;
|
|
if (text.startsWith("R to ")) return "reply";
|
|
if (text.startsWith("RT by ")) return "repost";
|
|
if (text == "Image") return "image-only-post";
|
|
return "post";
|
|
}
|
|
|
|
function lastPost(array) {
|
|
for (let a in array) {
|
|
let t = detectType(array[a].title);
|
|
if (t == "image-only-post" || t == "post") return array[a];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function extractAssets(description) {
|
|
let $ = cheerio.load(description);
|
|
if ($("img").length == 0) return [];
|
|
let media = [];
|
|
for (let c in $("img")) {
|
|
if (typeof $("img")[c] !== "object") continue;
|
|
if ($("img")[c].type !== "tag") continue;
|
|
let ax = await axios($("img")[c].attribs?.src, {
|
|
headers: {
|
|
accept: "image/*, */*"
|
|
},
|
|
responseType: "stream"
|
|
});
|
|
let id = $("img")[c].attribs?.src?.split("media%2F")[1].split(".")[0];
|
|
let ex = $("img")[c].attribs?.src?.split(".")[$("img")[c].attribs?.src?.split(".").length - 1];
|
|
if (!fs.existsSync(`${__dirname}/temp`)) fs.mkdirSync(`${__dirname}/temp`);
|
|
if (fs.existsSync(`${__dirname}/temp/${id}.${ex}`)) fs.rmSync(`${__dirname}/temp/${id}.${ex}`);
|
|
await ax.data.pipe(fs.createWriteStream(`${__dirname}/temp/${id}.${ex}`));
|
|
media.push({
|
|
type: "image",
|
|
original: $("img")[c].attribs?.src,
|
|
location: `${__dirname}/temp/${id}.${ex}`
|
|
});
|
|
}
|
|
return media;
|
|
} |