From 67cfa5311890a6e7cbe4422cbe518c17dfe008d5 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Fri, 6 Oct 2023 20:53:34 +0200
Subject: [PATCH] upd: federate listenbrainz, fix background federation

---
 .../backend/src/core/activitypub/ApRendererService.ts |  5 +++++
 .../src/core/activitypub/models/ApPersonService.ts    | 11 +++++++----
 packages/backend/src/core/activitypub/type.ts         |  2 ++
 packages/misskey-js/src/entities.ts                   |  1 +
 4 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index be922b9105..b47d785663 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -523,6 +523,10 @@ export class ApRendererService {
 			person['vcard:Address'] = profile.location;
 		}
 
+		if (profile.listenbrainz) {
+			person.listenbrainz = profile.listenbrainz;
+		}
+
 		return person;
 	}
 
@@ -797,6 +801,7 @@ export class ApRendererService {
 					// Sharkey
 					sharkey: "https://joinsharkey.org/ns#",
 					backgroundUrl: "sharkey:backgroundUrl",
+					listenbrainz: "sharkey:listenbrainz",
 					// vcard
 					vcard: 'http://www.w3.org/2006/vcard/ns#',
 				},
diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts
index 639d11add3..76b8073087 100644
--- a/packages/backend/src/core/activitypub/models/ApPersonService.ts
+++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts
@@ -225,8 +225,8 @@ export class ApPersonService implements OnModuleInit {
 		return null;
 	}
 
-	private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'backgroundId' | 'avatarUrl' | 'bannerUrl' | 'backgroundUrl' | 'avatarBlurhash' | 'bannerBlurhash' | 'backgroundBlurhash'>> {
-		const [avatar, banner, background] = await Promise.all([icon, image].map(img => {
+	private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any, bgimg: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'backgroundId' | 'avatarUrl' | 'bannerUrl' | 'backgroundUrl' | 'avatarBlurhash' | 'bannerBlurhash' | 'backgroundBlurhash'>> {
+		const [avatar, banner, background] = await Promise.all([icon, image, bgimg].map(img => {
 			if (img == null) return null;
 			if (user == null) throw new Error('failed to create user: user is null');
 			return this.apImageService.resolveImage(user, img).catch(() => null);
@@ -307,6 +307,7 @@ export class ApPersonService implements OnModuleInit {
 					id: this.idService.genId(),
 					avatarId: null,
 					bannerId: null,
+					backgroundId: null,
 					createdAt: new Date(),
 					lastFetchedAt: new Date(),
 					name: truncate(person.name, nameLength),
@@ -341,6 +342,7 @@ export class ApPersonService implements OnModuleInit {
 					birthday: bday?.[0] ?? null,
 					location: person['vcard:Address'] ?? null,
 					userHost: host,
+					listenbrainz: person.listenbrainz ?? null,
 				}));
 
 				if (person.publicKey) {
@@ -386,7 +388,7 @@ export class ApPersonService implements OnModuleInit {
 
 		//#region アバターとヘッダー画像をフェッチ
 		try {
-			const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image);
+			const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image, person.backgroundUrl);
 			await this.usersRepository.update(user.id, updates);
 			user = { ...user, ...updates };
 
@@ -469,7 +471,7 @@ export class ApPersonService implements OnModuleInit {
 			movedToUri: person.movedTo ?? null,
 			alsoKnownAs: person.alsoKnownAs ?? null,
 			isExplorable: person.discoverable,
-			...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
+			...(await this.resolveAvatarAndBanner(exist, person.icon, person.image, person.backgroundUrl).catch(() => ({}))),
 		} as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'speakAsCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
 
 		const moving = ((): boolean => {
@@ -508,6 +510,7 @@ export class ApPersonService implements OnModuleInit {
 			description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
 			birthday: bday?.[0] ?? null,
 			location: person['vcard:Address'] ?? null,
+			listenbrainz: person.listenbrainz ?? null,
 		});
 
 		this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: exist.id });
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index 5bef8befe5..9b19707ebb 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -183,6 +183,8 @@ export interface IActor extends IObject {
 	};
 	'vcard:bday'?: string;
 	'vcard:Address'?: string;
+	listenbrainz?: string;
+	backgroundUrl?: string;
 }
 
 export const isCollection = (object: IObject): object is ICollection =>
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 08d328c5b9..715cbc846a 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -38,6 +38,7 @@ export type UserDetailed = UserLite & {
 	backgroundUrl: string | null;
 	backgroundBlurhash: string | null;
 	birthday: string | null;
+	listenbrainz: string | null;
 	createdAt: DateString;
 	description: string | null;
 	ffVisibility: 'public' | 'followers' | 'private';