@rmdes/indiekit-syndicator-bluesky 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +4 -1
- package/lib/bluesky.js +15 -4
- package/lib/utils.js +116 -9
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -8,7 +8,8 @@ const defaults = {
|
|
|
8
8
|
profileUrl: "https://bsky.app/profile",
|
|
9
9
|
serviceUrl: "https://bsky.social",
|
|
10
10
|
includePermalink: false,
|
|
11
|
-
syndicateExternalLikes: true, //
|
|
11
|
+
syndicateExternalLikes: true, // Enable syndication of external likes
|
|
12
|
+
syndicateExternalReposts: true, // Enable syndication of external reposts
|
|
12
13
|
checked: false,
|
|
13
14
|
};
|
|
14
15
|
|
|
@@ -23,6 +24,7 @@ export default class BlueskySyndicator {
|
|
|
23
24
|
* @param {string} [options.password] - Password
|
|
24
25
|
* @param {boolean} [options.includePermalink] - Include permalink in status
|
|
25
26
|
* @param {boolean} [options.syndicateExternalLikes] - Syndicate likes of external URLs as posts
|
|
27
|
+
* @param {boolean} [options.syndicateExternalReposts] - Syndicate reposts of external URLs as posts
|
|
26
28
|
* @param {boolean} [options.checked] - Check syndicator in UI
|
|
27
29
|
*/
|
|
28
30
|
constructor(options = {}) {
|
|
@@ -92,6 +94,7 @@ export default class BlueskySyndicator {
|
|
|
92
94
|
serviceUrl: this.#serviceUrl,
|
|
93
95
|
includePermalink: this.options.includePermalink,
|
|
94
96
|
syndicateExternalLikes: this.options.syndicateExternalLikes,
|
|
97
|
+
syndicateExternalReposts: this.options.syndicateExternalReposts,
|
|
95
98
|
});
|
|
96
99
|
|
|
97
100
|
return await bluesky.post(properties, publication.me);
|
package/lib/bluesky.js
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
getPostImage,
|
|
7
7
|
getPostText,
|
|
8
8
|
getLikePostText,
|
|
9
|
+
getRepostPostText,
|
|
10
|
+
getBookmarkPostText,
|
|
9
11
|
buildPostText,
|
|
10
12
|
getPostParts,
|
|
11
13
|
uriToPostUrl,
|
|
@@ -23,6 +25,7 @@ export class Bluesky {
|
|
|
23
25
|
* @param {string} options.serviceUrl - Service URL
|
|
24
26
|
* @param {boolean} [options.includePermalink] - Include permalink in status
|
|
25
27
|
* @param {boolean} [options.syndicateExternalLikes] - Syndicate likes of external URLs
|
|
28
|
+
* @param {boolean} [options.syndicateExternalReposts] - Syndicate reposts of external URLs
|
|
26
29
|
*/
|
|
27
30
|
constructor(options) {
|
|
28
31
|
this.identifier = options.identifier;
|
|
@@ -31,6 +34,7 @@ export class Bluesky {
|
|
|
31
34
|
this.serviceUrl = options.serviceUrl;
|
|
32
35
|
this.includePermalink = options.includePermalink || false;
|
|
33
36
|
this.syndicateExternalLikes = options.syndicateExternalLikes !== false; // Default true
|
|
37
|
+
this.syndicateExternalReposts = options.syndicateExternalReposts !== false; // Default true
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
/**
|
|
@@ -331,7 +335,15 @@ export class Bluesky {
|
|
|
331
335
|
if (isSameOrigin(repostUrl, this.profileUrl)) {
|
|
332
336
|
return this.postRepost(repostUrl);
|
|
333
337
|
}
|
|
334
|
-
|
|
338
|
+
|
|
339
|
+
// Syndicate reposts of external URLs as posts with link card
|
|
340
|
+
if (this.syndicateExternalReposts) {
|
|
341
|
+
const text = getRepostPostText(properties, repostUrl);
|
|
342
|
+
const richText = await createRichText(client, text);
|
|
343
|
+
const externalEmbed = await this.createExternalEmbed(repostUrl);
|
|
344
|
+
return this.postPost(richText, { images, externalEmbed });
|
|
345
|
+
}
|
|
346
|
+
|
|
335
347
|
return;
|
|
336
348
|
}
|
|
337
349
|
|
|
@@ -356,12 +368,11 @@ export class Bluesky {
|
|
|
356
368
|
return;
|
|
357
369
|
}
|
|
358
370
|
|
|
359
|
-
// Handle bookmarks -
|
|
371
|
+
// Handle bookmarks - OG card shows bookmarked URL, text has commentary + permalink
|
|
360
372
|
const bookmarkOfUrl = properties["bookmark-of"];
|
|
361
373
|
if (bookmarkOfUrl) {
|
|
362
|
-
const text =
|
|
374
|
+
const text = getBookmarkPostText(properties, bookmarkOfUrl);
|
|
363
375
|
const richText = await createRichText(client, text);
|
|
364
|
-
// Create external embed for the bookmarked URL
|
|
365
376
|
const externalEmbed = await this.createExternalEmbed(bookmarkOfUrl);
|
|
366
377
|
return this.postPost(richText, { images, externalEmbed });
|
|
367
378
|
}
|
package/lib/utils.js
CHANGED
|
@@ -293,7 +293,9 @@ export function buildPostText(properties, options = {}) {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
/**
|
|
296
|
-
* Get post text for a like of an external URL
|
|
296
|
+
* Get post text for a like of an external URL (Bluesky version).
|
|
297
|
+
* The external URL is shown as an OG card embed, so the text contains
|
|
298
|
+
* commentary + blog permalink (for webmentions), not the liked URL.
|
|
297
299
|
* @param {object} properties - JF2 properties
|
|
298
300
|
* @param {string} likedUrl - The URL being liked
|
|
299
301
|
* @returns {string} Post text
|
|
@@ -308,20 +310,125 @@ export const getLikePostText = (properties, likedUrl) => {
|
|
|
308
310
|
text = properties.content.text;
|
|
309
311
|
}
|
|
310
312
|
|
|
311
|
-
//
|
|
312
|
-
if (text) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
// Remove the liked URL from text if already present (OG card shows it)
|
|
314
|
+
if (text && likedUrl) {
|
|
315
|
+
text = text.replace(likedUrl, "").trim();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Append blog permalink for webmentions
|
|
319
|
+
const permalink = properties.url;
|
|
320
|
+
if (permalink) {
|
|
321
|
+
if (text) {
|
|
322
|
+
text = `${text}\n\n${permalink}`;
|
|
323
|
+
} else {
|
|
324
|
+
text = `❤️ ${permalink}`;
|
|
316
325
|
}
|
|
317
|
-
} else {
|
|
318
|
-
//
|
|
326
|
+
} else if (!text) {
|
|
327
|
+
// Fallback: no content, no permalink — use liked URL
|
|
319
328
|
text = `❤️ ${likedUrl}`;
|
|
320
329
|
}
|
|
321
330
|
|
|
322
331
|
// Truncate if needed (Bluesky limit is 300 chars)
|
|
323
332
|
if (text.length > 300) {
|
|
324
|
-
const suffix = `\n\n❤️ ${likedUrl}`;
|
|
333
|
+
const suffix = permalink ? `\n\n${permalink}` : `\n\n❤️ ${likedUrl}`;
|
|
334
|
+
const maxLen = 300 - suffix.length - 3;
|
|
335
|
+
const contentPart = text.replace(suffix, "").slice(0, maxLen).trim();
|
|
336
|
+
text = contentPart + "..." + suffix;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return text;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get post text for a repost of an external URL (Bluesky version).
|
|
344
|
+
* The external URL is shown as an OG card embed, so the text contains
|
|
345
|
+
* commentary + blog permalink (for webmentions), not the reposted URL.
|
|
346
|
+
* @param {object} properties - JF2 properties
|
|
347
|
+
* @param {string} repostUrl - The URL being reposted
|
|
348
|
+
* @returns {string} Post text
|
|
349
|
+
*/
|
|
350
|
+
export const getRepostPostText = (properties, repostUrl) => {
|
|
351
|
+
let text = "";
|
|
352
|
+
|
|
353
|
+
// Get the content/comment
|
|
354
|
+
if (properties.content?.html) {
|
|
355
|
+
text = htmlToStatusText(properties.content.html);
|
|
356
|
+
} else if (properties.content?.text) {
|
|
357
|
+
text = properties.content.text;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Remove the reposted URL from text if already present (OG card shows it)
|
|
361
|
+
if (text && repostUrl) {
|
|
362
|
+
text = text.replace(repostUrl, "").trim();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Append blog permalink for webmentions
|
|
366
|
+
const permalink = properties.url;
|
|
367
|
+
if (permalink) {
|
|
368
|
+
if (text) {
|
|
369
|
+
text = `${text}\n\n${permalink}`;
|
|
370
|
+
} else {
|
|
371
|
+
text = `🔁 ${permalink}`;
|
|
372
|
+
}
|
|
373
|
+
} else if (!text) {
|
|
374
|
+
// Fallback: no content, no permalink — use repost URL
|
|
375
|
+
text = `🔁 ${repostUrl}`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Truncate if needed (Bluesky limit is 300 chars)
|
|
379
|
+
if (text.length > 300) {
|
|
380
|
+
const suffix = permalink ? `\n\n${permalink}` : `\n\n🔁 ${repostUrl}`;
|
|
381
|
+
const maxLen = 300 - suffix.length - 3;
|
|
382
|
+
const contentPart = text.replace(suffix, "").slice(0, maxLen).trim();
|
|
383
|
+
text = contentPart + "..." + suffix;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return text;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Get post text for a bookmark of an external URL (Bluesky version).
|
|
391
|
+
* The external URL is shown as an OG card embed, so the text contains
|
|
392
|
+
* commentary + blog permalink (for webmentions), not the bookmarked URL.
|
|
393
|
+
* @param {object} properties - JF2 properties
|
|
394
|
+
* @param {string} bookmarkUrl - The URL being bookmarked
|
|
395
|
+
* @returns {string} Post text
|
|
396
|
+
*/
|
|
397
|
+
export const getBookmarkPostText = (properties, bookmarkUrl) => {
|
|
398
|
+
let text = "";
|
|
399
|
+
|
|
400
|
+
// Get the content/comment
|
|
401
|
+
if (properties.name && properties.name !== "") {
|
|
402
|
+
text = properties.name;
|
|
403
|
+
} else if (properties.content?.html) {
|
|
404
|
+
text = htmlToStatusText(properties.content.html);
|
|
405
|
+
} else if (properties.content?.text) {
|
|
406
|
+
text = properties.content.text;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Remove the bookmarked URL from text if already present (OG card shows it)
|
|
410
|
+
if (text && bookmarkUrl) {
|
|
411
|
+
text = text.replace(bookmarkUrl, "").trim();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Append blog permalink for webmentions
|
|
415
|
+
const permalink = properties.url;
|
|
416
|
+
if (permalink) {
|
|
417
|
+
if (text) {
|
|
418
|
+
if (!text.includes(permalink)) {
|
|
419
|
+
text = `${text}\n\n${permalink}`;
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
text = `🔖 ${permalink}`;
|
|
423
|
+
}
|
|
424
|
+
} else if (!text) {
|
|
425
|
+
// Fallback: no content, no permalink — use bookmarked URL
|
|
426
|
+
text = `🔖 ${bookmarkUrl}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Truncate if needed (Bluesky limit is 300 chars)
|
|
430
|
+
if (text.length > 300) {
|
|
431
|
+
const suffix = permalink ? `\n\n${permalink}` : `\n\n🔖 ${bookmarkUrl}`;
|
|
325
432
|
const maxLen = 300 - suffix.length - 3;
|
|
326
433
|
const contentPart = text.replace(suffix, "").slice(0, maxLen).trim();
|
|
327
434
|
text = contentPart + "..." + suffix;
|