diff --git a/create-circle.js b/create-circle.js index c043a87..50ee20c 100644 --- a/create-circle.js +++ b/create-circle.js @@ -42,6 +42,7 @@ async function apiRequest(url, options = null) * replies: number, * renotes: number, * favorites: number, + * extra_reacts: boolean, * instance: string, * author?: FediUser, * }} Note @@ -109,6 +110,14 @@ class ApiClient { return client; } + let features = apiResponse?.metadata?.features; + if (Array.isArray(features) && features.includes("pleroma_api")) { + const has_emoji_reacts = features.includes("pleroma_emoji_reactions"); + const client = new PleromaApiClient(instance, has_emoji_reacts); + instanceTypeCache.set(instance, client); + return client; + } + const client = new MastodonApiClient(instance); instanceTypeCache.set(instance, client); return client; @@ -144,10 +153,11 @@ class ApiClient { /** * @param {Note} note + * @param {boolean} extra_reacts * * return {Promise} */ - async getFavs(note) { throw new Error("Not implemented"); } + async getFavs(note, extra_reacts) { throw new Error("Not implemented"); } /** * @return string @@ -193,6 +203,8 @@ class MastodonApiClient extends ApiClient { replies: note["replies_count"] || 0, renotes: note["reblogs_count"] || 0, favorites: note["favourites_count"], + // Actually a Pleroma/Akkoma thing + extra_reacts: note?.["pleroma"]?.["emoji_reactions"]?.length > 0, instance: this._instance, author: user })); @@ -231,6 +243,8 @@ class MastodonApiClient extends ApiClient { replies: note["replies_count"] || 0, renotes: note["reblogs_count"] || 0, favorites: note["favourites_count"], + // Actually a Pleroma/Akkoma thing + extra_reacts: note?.["pleroma"]?.["emoji_reactions"]?.length > 0, instance: handle.instance, author: { id: note["account"]["id"], @@ -243,7 +257,7 @@ class MastodonApiClient extends ApiClient { }); } - async getFavs(note) { + async getFavs(note, extra_reacts) { const url = `https://${this._instance}/api/v1/statuses/${note.id}/favourited_by`; const response = await apiRequest(url); @@ -265,6 +279,61 @@ class MastodonApiClient extends ApiClient { } } +class PleromaApiClient extends MastodonApiClient { + /** + * @param {string} instance + * @param {boolean} emoji_reacts + */ + constructor(instance, emoji_reacts) { + super(instance); + this._emoji_reacts = emoji_reacts; + } + + async getFavs(note, extra_reacts) { + // Pleroma/Akkoma supports both favs and emoji reacts + // with several emoji reacts per users being possible. + // Coalesce them and count every user only once + let favs = await super.getFavs(note); + + if (!this._emoji_reacts || !extra_reacts) + return favs; + + /** + * @type {Map} + */ + let users = new Map(); + if (favs !== null) { + favs.forEach(u => { + users.set(u.id, u); + }); + } + + const url = `https://${this._instance}/api/v1/pleroma/statuses/${note.id}/reactions`; + const response = await apiRequest(url) ?? []; + + for (const reaction of response) { + reaction["accounts"] + .map(account => ({ + id: account["id"], + avatar: account["avatar"], + bot: account["bot"], + name: account["display_name"], + handle: parseHandle(account["acct"], note.instance) + })) + .forEach(u => { + if(!users.has(u.id)) + users.set(u.id, u); + }) + } + + return Array.from(users.values()); + } + + getClientName() { + return "pleroma"; + } +} + class MisskeyApiClient extends ApiClient { /** * @param {string} instance @@ -316,6 +385,7 @@ class MisskeyApiClient extends ApiClient { replies: note["repliesCount"], renotes: note["renoteCount"], favorites: Object.values(note["reactions"]).reduce((a, b) => a + b, 0), + extra_reacts: false, instance: this._instance, author: user })); @@ -366,6 +436,7 @@ class MisskeyApiClient extends ApiClient { replies: reply["repliesCount"], renotes: reply["renoteCount"], favorites: Object.values(reply["reactions"]).reduce((a, b) => a + b, 0), + extra_reacts: false, instance: handle.instance, author: { id: reply["user"]["id"], @@ -378,7 +449,7 @@ class MisskeyApiClient extends ApiClient { }); } - async getFavs(note) { + async getFavs(note, extra_reacts) { const url = `https://${this._instance}/api/notes/reactions`; const response = await apiRequest(url, { method: "POST", @@ -455,6 +526,9 @@ async function circleMain() { case "mastodon": client = new MastodonApiClient(selfUser.instance); break; + case "pleroma": + client = new PleromaApiClient(selfUser.instance, true); + break; case "misskey": client = new MisskeyApiClient(selfUser.instance); break; @@ -524,8 +598,8 @@ async function processNotes(client, connectionList, notes) { * @param {Note} note */ async function evaluateNote(client, connectionList, note) { - if (note.favorites > 0) { - await client.getFavs(note).then(users => { + if (note.favorites > 0 || note.extra_reacts) { + await client.getFavs(note, note.extra_reacts).then(users => { if (!users) return; diff --git a/index.html b/index.html index 272b75c..bba4a8a 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,7 @@ +