feat: split long posts into replies (#10)
* post split text as reply --------- Co-authored-by: Maurice Renck <maurice@maurice-renck.de>
This commit is contained in:
parent
598fb3a7a9
commit
b093c7d5fc
1 changed files with 128 additions and 88 deletions
130
main.js
130
main.js
|
@ -8,67 +8,104 @@ const axios = require("axios");
|
||||||
const mastodonInstance = process.env.MASTODON_INSTANCE;
|
const mastodonInstance = process.env.MASTODON_INSTANCE;
|
||||||
const mastodonUser = process.env.MASTODON_USER;
|
const mastodonUser = process.env.MASTODON_USER;
|
||||||
|
|
||||||
// Bluesky agent
|
async function main() {
|
||||||
const agent = new BskyAgent({ service: process.env.BLUESKY_ENDPOINT });
|
// Bluesky agent
|
||||||
|
const agent = new BskyAgent({ service: process.env.BLUESKY_ENDPOINT });
|
||||||
|
const loginResponse = await agent.login({
|
||||||
|
identifier: process.env.BLUESKY_HANDLE,
|
||||||
|
password: process.env.BLUESKY_PASSWORD,
|
||||||
|
});
|
||||||
|
if (!loginResponse.success) console.error("🔒 login failed");
|
||||||
|
|
||||||
// File to store the last processed Mastodon post ID
|
// File to store the last processed Mastodon post ID
|
||||||
const lastProcessedPostIdFile = path.join(
|
const lastProcessedPostIdFile = path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
"data",
|
"data",
|
||||||
"lastProcessedPostId.txt"
|
"lastProcessedPostId.txt"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Variable to store the last processed Mastodon post ID
|
// Variable to store the last processed Mastodon post ID
|
||||||
let lastProcessedPostId = loadLastProcessedPostId();
|
let lastProcessedPostId = loadLastProcessedPostId();
|
||||||
|
|
||||||
// Function to load the last processed post ID from the file
|
// Function to load the last processed post ID from the file
|
||||||
function loadLastProcessedPostId() {
|
function loadLastProcessedPostId() {
|
||||||
try {
|
try {
|
||||||
return fs.readFileSync(lastProcessedPostIdFile, "utf8").trim();
|
return fs.readFileSync(lastProcessedPostIdFile, "utf8").trim();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading last processed post ID:", error);
|
console.error("Error loading last processed post ID:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to save the last processed post ID to the file
|
// Function to save the last processed post ID to the file
|
||||||
function saveLastProcessedPostId() {
|
function saveLastProcessedPostId() {
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(lastProcessedPostIdFile, `${lastProcessedPostId}`);
|
fs.writeFileSync(lastProcessedPostIdFile, `${lastProcessedPostId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving last processed post ID:", 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 truncate(text, timestampId) {
|
|
||||||
if (text.length > 300) {
|
|
||||||
console.warn(`✂ post '${timestampId}' was truncated`)
|
|
||||||
return text.substring(0, 299) + '…'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return text
|
async function createBlueskyMessage(text) {
|
||||||
}
|
const richText = new RichText({ text });
|
||||||
|
await richText.detectFacets(agent);
|
||||||
|
|
||||||
// Function to periodically fetch new Mastodon posts
|
return {
|
||||||
async function fetchNewPosts() {
|
text: richText.text,
|
||||||
|
facets: richText.facets
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function postToBluesky(textParts) {
|
||||||
|
const rootMessageResponse = await agent.post(await createBlueskyMessage(textParts[0]));
|
||||||
|
|
||||||
|
if (textParts.length === 1) return;
|
||||||
|
|
||||||
|
let replyMessageResponse = null
|
||||||
|
for (let index = 1; index < textParts.length; index++) {
|
||||||
|
replyMessageResponse = await agent.post({
|
||||||
|
...(await createBlueskyMessage(textParts[index])),
|
||||||
|
reply: {
|
||||||
|
root: rootMessageResponse,
|
||||||
|
parent: replyMessageResponse ?? rootMessageResponse,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeHtmlTags(input) {
|
||||||
|
return input.replace(/<[^>]*>/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitText(text, maxLength) {
|
||||||
|
// Split the text by spaces
|
||||||
|
const words = text.split(" ");
|
||||||
|
|
||||||
|
let result = [];
|
||||||
|
let currentChunk = "";
|
||||||
|
|
||||||
|
for (const word of words) {
|
||||||
|
// Add the current word to the current chunk
|
||||||
|
const potentialChunk = `${currentChunk} ${word}`.trim();
|
||||||
|
|
||||||
|
if (potentialChunk.length <= maxLength) {
|
||||||
|
// If the current chunk is still under max length, add the word
|
||||||
|
currentChunk = potentialChunk;
|
||||||
|
} else {
|
||||||
|
// Otherwise, add the current chunk to the result and start a new chunk
|
||||||
|
result.push(currentChunk);
|
||||||
|
currentChunk = word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last chunk to the result
|
||||||
|
result.push(currentChunk);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to periodically fetch new Mastodon posts
|
||||||
|
async function fetchNewPosts() {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${mastodonInstance}/users/${mastodonUser}/outbox?page=true`
|
`${mastodonInstance}/users/${mastodonUser}/outbox?page=true`
|
||||||
);
|
);
|
||||||
|
@ -90,8 +127,8 @@ async function fetchNewPosts() {
|
||||||
if (currentTimestampId > lastProcessedPostId && lastProcessedPostId != 0) {
|
if (currentTimestampId > lastProcessedPostId && lastProcessedPostId != 0) {
|
||||||
try {
|
try {
|
||||||
console.log('📧 posting to BlueSky', currentTimestampId)
|
console.log('📧 posting to BlueSky', currentTimestampId)
|
||||||
const text = truncate(removeHtmlTags(item.object.content), currentTimestampId);
|
const textParts = splitText(removeHtmlTags(item.object.content), 300);
|
||||||
postToBluesky(text);
|
postToBluesky(textParts);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('🔥 can\'t post to Bluesky', currentTimestampId, error)
|
console.error('🔥 can\'t post to Bluesky', currentTimestampId, error)
|
||||||
}
|
}
|
||||||
|
@ -102,8 +139,11 @@ async function fetchNewPosts() {
|
||||||
lastProcessedPostId = newTimestampId;
|
lastProcessedPostId = newTimestampId;
|
||||||
saveLastProcessedPostId();
|
saveLastProcessedPostId();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchNewPosts();
|
||||||
|
// Fetch new posts every 5 minutes (adjust as needed)
|
||||||
|
setInterval(fetchNewPosts, (process.env.INTERVAL_MINUTES ?? 5) * 60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchNewPosts();
|
main()
|
||||||
// Fetch new posts every 5 minutes (adjust as needed)
|
|
||||||
setInterval(fetchNewPosts, (process.env.INTERVAL_MINUTES ?? 5) * 60 * 1000);
|
|
Loading…
Reference in a new issue