49eb29b463
⚠️ Untested change ⚠️ This jumped out at me as a potential problem if there were a number of toots to be cross-posted in one go. Firing them all off at once means that they could potentially end up on bluesky out-of-order. Additionally, if any failed to cross-post, saving after each successful one would allow the script to pick up where it left off. I was thinking about changing the script to copy over all of my toots, not just future ones, but I realized that just writing a 1 to the lastProcessedPostId.txt would do that, and then this change would ensure it went in order and was recoverable. I'll try it out sometime soon - I was going to just file a bug report, but I figured it'd be easier to make the change than to describe it.
90 lines
2.6 KiB
JavaScript
90 lines
2.6 KiB
JavaScript
require("dotenv").config();
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const { RichText, BskyAgent } = require("@atproto/api");
|
|
const axios = require("axios");
|
|
|
|
// Mastodon credentials
|
|
const mastodonInstance = process.env.MASTODON_INSTANCE;
|
|
const mastodonUser = process.env.MASTODON_USER;
|
|
|
|
// Bluesky agent
|
|
const agent = new BskyAgent({ service: process.env.BLUESKY_ENDPOINT });
|
|
|
|
// File to store the last processed Mastodon post ID
|
|
const lastProcessedPostIdFile = path.join(__dirname, "lastProcessedPostId.txt");
|
|
|
|
// Variable to store the last processed Mastodon post ID
|
|
let lastProcessedPostId = loadLastProcessedPostId();
|
|
|
|
// Function to load the last processed post ID from the file
|
|
function loadLastProcessedPostId() {
|
|
try {
|
|
return fs.readFileSync(lastProcessedPostIdFile, "utf8").trim();
|
|
} catch (error) {
|
|
console.error("Error loading last processed post ID:", error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Function to save the last processed post ID to the file
|
|
function saveLastProcessedPostId() {
|
|
try {
|
|
fs.writeFileSync(lastProcessedPostIdFile, `${lastProcessedPostId}`);
|
|
} catch (error) {
|
|
console.error("Error saving last processed post ID:", error);
|
|
}
|
|
}
|
|
|
|
async function postToBluesky(text) {
|
|
await agent.login({
|
|
identifier: process.env.BLUESKY_HANDLE,
|
|
password: process.env.BLUESKY_PASSWORD,
|
|
});
|
|
|
|
const richText = new RichText({ text });
|
|
await richText.detectFacets(agent);
|
|
await agent.post({
|
|
text: richText.text,
|
|
facets: richText.facets,
|
|
});
|
|
}
|
|
|
|
function removeHtmlTags(input) {
|
|
return input.replace(/<[^>]*>/g, "");
|
|
}
|
|
|
|
// Function to periodically fetch new Mastodon posts
|
|
async function fetchNewPosts() {
|
|
const response = await axios.get(`${mastodonInstance}/users/${mastodonUser}/outbox?page=true`);
|
|
|
|
const reversed = response.data.orderedItems.filter(item => item.object.type === 'Note')
|
|
.filter(item => item.object.inReplyTo === null)
|
|
.reverse();
|
|
|
|
let newTimestampId = 0;
|
|
|
|
for(let item of reversed) {
|
|
const currentTimestampId = Date.parse(item.published);
|
|
|
|
if(currentTimestampId > newTimestampId) {
|
|
newTimestampId = currentTimestampId;
|
|
}
|
|
|
|
if(currentTimestampId > lastProcessedPostId && lastProcessedPostId != 0) {
|
|
const text = removeHtmlTags(item.object.content);
|
|
await postToBluesky(text);
|
|
lastProcessedPostId = newTimestampId;
|
|
await saveLastProcessedPostId();
|
|
}
|
|
})
|
|
|
|
if(newTimestampId > 0) {
|
|
lastProcessedPostId = newTimestampId;
|
|
saveLastProcessedPostId();
|
|
}
|
|
}
|
|
|
|
fetchNewPosts();
|
|
// Fetch new posts every 5 minutes (adjust as needed)
|
|
setInterval(fetchNewPosts, 2 * 60 * 1000);
|