From 72887f54e0deb0824cc37fe8774654b6c84f7e74 Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Fri, 7 Jul 2023 20:15:24 +0200 Subject: [PATCH] [mastodon-client] populate user details for all notes --- .../src/server/api/mastodon/converters.ts | 6 +- packages/megalodon/src/misskey.ts | 79 +++++++++++++------ 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts index 37c6283a1..cbaf5287f 100644 --- a/packages/backend/src/server/api/mastodon/converters.ts +++ b/packages/backend/src/server/api/mastodon/converters.ts @@ -2,8 +2,10 @@ import { Entity } from "megalodon"; import { convertId, IdType } from "../index.js"; function simpleConvert(data: any) { - data.id = convertId(data.id, IdType.MastodonId); - return data; + // copy the object to bypass weird pass by reference bugs + const result = Object.assign({}, data); + result.id = convertId(data.id, IdType.MastodonId); + return result; } export function convertAccount(account: Entity.Account) { diff --git a/packages/megalodon/src/misskey.ts b/packages/megalodon/src/misskey.ts index 42268806f..4753eb480 100644 --- a/packages/megalodon/src/misskey.ts +++ b/packages/megalodon/src/misskey.ts @@ -333,7 +333,7 @@ export default class Misskey implements MegalodonInterface { if (res.data.pinnedNotes) { return { ...res, - data: await Promise.all(res.data.pinnedNotes.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.pinnedNotes.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))) } } return {...res, data: []} @@ -385,7 +385,7 @@ export default class Misskey implements MegalodonInterface { }) } return this.client.post>('/api/users/notes', params).then(async res => { - const statuses: Array = await Promise.all(res.data.map(note => this.noteWithMentions(note, this.baseUrlToHost(this.baseUrl), accountCache))) + const statuses: Array = await Promise.all(res.data.map(note => this.noteWithDetails(note, this.baseUrlToHost(this.baseUrl), accountCache))) return Object.assign(res, { data: statuses }) @@ -424,7 +424,7 @@ export default class Misskey implements MegalodonInterface { } return this.client.post>('/api/users/reactions', params).then(async res => { return Object.assign(res, { - data: await Promise.all(res.data.map(fav => this.noteWithMentions(fav.note, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.map(fav => this.noteWithDetails(fav.note, this.baseUrlToHost(this.baseUrl), accountCache))) }) }) } @@ -764,7 +764,7 @@ export default class Misskey implements MegalodonInterface { } return this.client.post>('/api/i/favorites', params).then(async res => { return Object.assign(res, { - data: await Promise.all(res.data.map(s => this.noteWithMentions(s.note, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.map(s => this.noteWithDetails(s.note, this.baseUrlToHost(this.baseUrl), accountCache))) }) }) } @@ -1222,7 +1222,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/create', params) .then(async res => ({ ...res, - data: await this.noteWithMentions(res.data.createdNote, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) + data: await this.noteWithDetails(res.data.createdNote, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) })) } @@ -1234,7 +1234,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({ ...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})); + .then(async res => ({ ...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})); } private getFreshAccountCache() :AccountCache { @@ -1244,12 +1244,22 @@ export default class Misskey implements MegalodonInterface { } } - public async noteWithMentions(n: MisskeyAPI.Entity.Note, host: string, cache: AccountCache): Promise { - const status = await this.converter.note(n, host); - return status.mentions.length === 0 ? this.addMentionsToStatus(status, cache) : status; + public async noteWithDetails(n: MisskeyAPI.Entity.Note, host: string, cache: AccountCache): Promise { + const status = await this.addUserDetailsToStatus(this.converter.note(n, host), cache); + return this.addMentionsToStatus(status, cache); } + public async addUserDetailsToStatus(status: Entity.Status, cache: AccountCache) : Promise { + if (status.account.followers_count === 0 && status.account.followers_count === 0 && status.account.statuses_count === 0) + status.account = await this.getAccountCached(status.account.id, status.account.acct, cache) ?? status.account; + + return status; + } + public async addMentionsToStatus(status: Entity.Status, cache: AccountCache) : Promise { + if (status.mentions.length > 0) + return status; + status.mentions = (await this.getMentions(status.plain_content!, cache)).filter(p => p != null); for (const m of status.mentions.filter((value, index, array) => array.indexOf(value) === index)) { status.content = status.content.replace(`@${m.acct}`, `@${m.acct}`); @@ -1307,6 +1317,23 @@ export default class Misskey implements MegalodonInterface { }) } + public async getAccountCached(id: string, acct: string, cache: AccountCache): Promise { + return await cache.locks.acquire(acct, async () => { + const cacheHit = cache.accounts.find(p => p.id === id); + const account = cacheHit ?? (await this.getAccount(id)).data; + + if (!account) { + return null; + } + + if (cacheHit == null) { + cache.accounts.push(account); + } + + return account; + }) + } + public async editStatus( _id: string, _options: { @@ -1375,11 +1402,11 @@ export default class Misskey implements MegalodonInterface { return this.client.post>('/api/notes/children', params).then(async res => { const accountCache = this.getFreshAccountCache(); const conversation = await this.client.post>('/api/notes/conversation', params); - const parents = await Promise.all(conversation.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))); + const parents = await Promise.all(conversation.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))); const context: Entity.Context = { ancestors: parents.reverse(), - descendants: this.dfs(await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache)))) + descendants: this.dfs(await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache)))) } return { ...res, @@ -1492,7 +1519,7 @@ export default class Misskey implements MegalodonInterface { }) .then(async res => ({ ...res, - data: await this.noteWithMentions(res.data.createdNote, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) + data: await this.noteWithDetails(res.data.createdNote, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) })) } @@ -1507,7 +1534,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } /** @@ -1521,7 +1548,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } /** @@ -1535,7 +1562,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } public async muteStatus(_id: string): Promise> { @@ -1563,7 +1590,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } /** @@ -1577,7 +1604,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } // ====================================== @@ -1663,7 +1690,7 @@ export default class Misskey implements MegalodonInterface { noteId: status_id }) .then(async res => { - const note = await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) + const note = await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache()) return {...res, data: note.poll} }) if (!res.data) { @@ -1768,7 +1795,7 @@ export default class Misskey implements MegalodonInterface { .post>('/api/notes/global-timeline', params) .then(async res => ({ ...res, - data: await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))) })) } @@ -1826,7 +1853,7 @@ export default class Misskey implements MegalodonInterface { .post>('/api/notes/local-timeline', params) .then(async res => ({ ...res, - data: await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))) })) } @@ -1890,7 +1917,7 @@ export default class Misskey implements MegalodonInterface { .post>('/api/notes/search-by-tag', params) .then(async res => ({ ...res, - data: await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))) })) } @@ -1945,7 +1972,7 @@ export default class Misskey implements MegalodonInterface { .post>('/api/notes/timeline', params) .then(async res => ({ ...res, - data: await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))) + data: await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))) })) } @@ -2001,7 +2028,7 @@ export default class Misskey implements MegalodonInterface { } return this.client .post>('/api/notes/user-list-timeline', params) - .then(async res => ({ ...res, data: await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))) })) + .then(async res => ({ ...res, data: await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))) })) } // ====================================== @@ -2444,7 +2471,7 @@ export default class Misskey implements MegalodonInterface { ...res, data: { accounts: [], - statuses: await Promise.all(res.data.map(n => this.noteWithMentions(n, this.baseUrlToHost(this.baseUrl), accountCache))), + statuses: await Promise.all(res.data.map(n => this.noteWithDetails(n, this.baseUrlToHost(this.baseUrl), accountCache))), hashtags: [] } })) @@ -2582,7 +2609,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } /** @@ -2596,7 +2623,7 @@ export default class Misskey implements MegalodonInterface { .post('/api/notes/show', { noteId: id }) - .then(async res => ({...res, data: await this.noteWithMentions(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) + .then(async res => ({...res, data: await this.noteWithDetails(res.data, this.baseUrlToHost(this.baseUrl), this.getFreshAccountCache())})) } public async getEmojiReactions(id: string): Promise>> {