Feat:アバターデコレーションを連合するように
Signed-off-by: mattyatea <mattyacocacora0@gmail.com>
This commit is contained in:
parent
4dd4a11cef
commit
bfd817ae10
|
@ -0,0 +1,13 @@
|
|||
export class Avatardecoration31698323592283 {
|
||||
name = 'Avatardecoration31698323592283'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "host" character varying(128)`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_3f8079d448095b8d867d318d12" ON "avatar_decoration" ("host") `);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_3f8079d448095b8d867d318d12"`);
|
||||
await queryRunner.query(`ALTER TABLE "avatar_decoration" DROP COLUMN "host"`);
|
||||
}
|
||||
}
|
|
@ -26,11 +26,14 @@ import type { MiUserKeypair } from '@/models/UserKeypair.js';
|
|||
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
|
||||
import { isNotNull } from '@/misc/is-not-null.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { MiAvatarDecoration } from '@/models/_.js';
|
||||
import { LdSignatureService } from './LdSignatureService.js';
|
||||
import { ApMfmService } from './ApMfmService.js';
|
||||
import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
|
||||
import type { IAccept, IActivity, IAdd, IAnnounce,
|
||||
IApAvatarDecoration, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js';
|
||||
|
||||
@Injectable()
|
||||
export class ApRendererService {
|
||||
|
@ -54,6 +57,7 @@ export class ApRendererService {
|
|||
private pollsRepository: PollsRepository,
|
||||
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private avatarDecorationService: AvatarDecorationService,
|
||||
private userEntityService: UserEntityService,
|
||||
private driveFileEntityService: DriveFileEntityService,
|
||||
private ldSignatureService: LdSignatureService,
|
||||
|
@ -184,7 +188,19 @@ export class ApRendererService {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public renderAvatarDecoration(avatarDecoration: MiAvatarDecoration): IApAvatarDecoration {
|
||||
return {
|
||||
id: avatarDecoration.url,
|
||||
type: 'AvatarDecoration',
|
||||
name: avatarDecoration.name,
|
||||
updated: avatarDecoration.updatedAt != null ? avatarDecoration.updatedAt.toISOString() : new Date().toISOString(),
|
||||
icon: {
|
||||
type: 'Image',
|
||||
url: avatarDecoration.url,
|
||||
},
|
||||
};
|
||||
}
|
||||
// to anonymise reporters, the reporting actor must be a system user
|
||||
@bindThis
|
||||
public renderFlag(user: MiLocalUser, object: IObject | string, content: string): IFlag {
|
||||
|
@ -472,11 +488,17 @@ export class ApRendererService {
|
|||
const emojis = await this.getEmojis(user.emojis);
|
||||
const apemojis = emojis.filter(emoji => !emoji.localOnly).map(emoji => this.renderEmoji(emoji));
|
||||
|
||||
const AvatarDecorations = await this.getAvatarDecorations(user.avatarDecorations);
|
||||
const apAvatarDecorations = AvatarDecorations.map(decoration => this.renderAvatarDecoration(decoration));
|
||||
|
||||
const hashtagTags = user.tags.map(tag => this.renderHashtag(tag));
|
||||
|
||||
AvatarDecorations.forEach((decoration, index) => user.avatarDecorations[index].id = decoration.name ); //デコレーションのidのところにnameを突っ込んでる(これ以外思いつかなかった)
|
||||
|
||||
const tag = [
|
||||
...apemojis,
|
||||
...hashtagTags,
|
||||
...apAvatarDecorations,
|
||||
];
|
||||
|
||||
const keypair = await this.userKeypairService.getUserKeypair(user.id);
|
||||
|
@ -503,6 +525,7 @@ export class ApRendererService {
|
|||
publicKey: this.renderKey(user, keypair, '#main-key'),
|
||||
isCat: user.isCat,
|
||||
attachment: attachment.length ? attachment : undefined,
|
||||
AvatarDecorations: user.avatarDecorations,
|
||||
};
|
||||
|
||||
if (user.movedToUri) {
|
||||
|
@ -720,4 +743,12 @@ export class ApRendererService {
|
|||
|
||||
return emojis;
|
||||
}
|
||||
@bindThis
|
||||
private async getAvatarDecorations(decorations: { id: string; angle: number; flipH: boolean }[]): Promise<MiAvatarDecoration[]> {
|
||||
if (decorations.length === 0) return [];
|
||||
const allAvatarDecorations = await this.avatarDecorationService.getAll();
|
||||
//const matchingDecorations = allAvatarDecorations.find(item1 => decorations.some(item2 => item1.id === item2.id )).map;
|
||||
const avatarDecorations = allAvatarDecorations.filter(item1 => decorations.some(item2 => item1.id === item2.id));
|
||||
return avatarDecorations ?? [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,13 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common';
|
|||
import promiseLimit from 'promise-limit';
|
||||
import { In } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { PollsRepository, EmojisRepository } from '@/models/_.js';
|
||||
import type { PollsRepository, EmojisRepository, AvatarDecorationsRepository } from '@/models/_.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import type { MiRemoteUser } from '@/models/User.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import { toArray, toSingle, unique } from '@/misc/prelude/array.js';
|
||||
import type { MiEmoji } from '@/models/Emoji.js';
|
||||
import type { MiAvatarDecoration } from '@/models/AvatarDecoration.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { AppLockService } from '@/core/AppLockService.js';
|
||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||
|
@ -24,7 +25,7 @@ import { StatusError } from '@/misc/status-error.js';
|
|||
import { UtilityService } from '@/core/UtilityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { checkHttps } from '@/misc/check-https.js';
|
||||
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js';
|
||||
import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType, isAvatarDecoration } from '../type.js';
|
||||
import { ApLoggerService } from '../ApLoggerService.js';
|
||||
import { ApMfmService } from '../ApMfmService.js';
|
||||
import { ApDbResolverService } from '../ApDbResolverService.js';
|
||||
|
@ -37,7 +38,6 @@ import { ApQuestionService } from './ApQuestionService.js';
|
|||
import { ApImageService } from './ApImageService.js';
|
||||
import type { Resolver } from '../ApResolverService.js';
|
||||
import type { IObject, IPost } from '../type.js';
|
||||
|
||||
@Injectable()
|
||||
export class ApNoteService {
|
||||
private logger: Logger;
|
||||
|
@ -52,6 +52,9 @@ export class ApNoteService {
|
|||
@Inject(DI.emojisRepository)
|
||||
private emojisRepository: EmojisRepository,
|
||||
|
||||
@Inject(DI.avatarDecorationsRepository)
|
||||
private avatarDecorationRepository: AvatarDecorationsRepository,
|
||||
|
||||
private idService: IdService,
|
||||
private apMfmService: ApMfmService,
|
||||
private apResolverService: ApResolverService,
|
||||
|
@ -397,4 +400,47 @@ export class ApNoteService {
|
|||
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}));
|
||||
}
|
||||
@bindThis
|
||||
public async extractAvatarDecorations(AvatarDecorations: IObject | IObject[], host: string): Promise<MiAvatarDecoration[]> {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
host = this.utilityService.toPuny(host);
|
||||
|
||||
const AvatarDecorationTags = toArray(AvatarDecorations).filter(isAvatarDecoration);
|
||||
const existingAvatars = await this.avatarDecorationRepository.findBy({
|
||||
host,
|
||||
name: In(AvatarDecorationTags.map((avatar) => avatar.name)),
|
||||
});
|
||||
return await Promise.all(AvatarDecorationTags.map(async tag => {
|
||||
const exists = existingAvatars.find(x => x.name === tag.name);
|
||||
if (exists) {
|
||||
if ((exists.updatedAt == null)
|
||||
|| (new Date(tag.updated) > exists.updatedAt)
|
||||
|| (tag.icon.url !== exists.url)
|
||||
) {
|
||||
await this.avatarDecorationRepository.update({
|
||||
host,
|
||||
name: tag.name,
|
||||
}, {
|
||||
url: tag.id,
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
const avatarDecoration = await this.avatarDecorationRepository.findOneBy({ host, name: tag.name });
|
||||
if (avatarDecoration == null) throw new Error('avatardecoration update failed');
|
||||
return avatarDecoration;
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
this.logger.info(`register avatarDecoration host=${host}, name=${tag.name}`);
|
||||
|
||||
return await this.avatarDecorationRepository.insert({
|
||||
id: this.idService.gen(),
|
||||
host,
|
||||
name: tag.name,
|
||||
url: tag.id,
|
||||
description: '',
|
||||
roleIdsThatCanBeUsedThisDecoration: [],
|
||||
updatedAt: new Date(),
|
||||
}).then(x => this.avatarDecorationRepository.findOneByOrFail(x.identifiers[0]));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,6 +291,15 @@ export class ApPersonService implements OnModuleInit {
|
|||
});
|
||||
//#endregion
|
||||
|
||||
//#region アバターデコレーション取得
|
||||
const avatardecorations = await this.apNoteService.extractAvatarDecorations(person.tag ?? [], host)
|
||||
.then(_decorations => _decorations.map(decorations => decorations.name))
|
||||
.catch(err => {
|
||||
this.logger.error('error occurred while fetching user avatar decorations', { stack: err });
|
||||
return [];
|
||||
});
|
||||
//#endregion
|
||||
|
||||
try {
|
||||
// Start transaction
|
||||
await this.db.transaction(async transactionalEntityManager => {
|
||||
|
@ -317,6 +326,7 @@ export class ApPersonService implements OnModuleInit {
|
|||
isBot,
|
||||
isCat: (person as any).isCat === true,
|
||||
emojis,
|
||||
avatarDecorations: avatardecorations,
|
||||
})) as MiRemoteUser;
|
||||
|
||||
await transactionalEntityManager.save(new MiUserProfile({
|
||||
|
@ -419,12 +429,26 @@ export class ApPersonService implements OnModuleInit {
|
|||
|
||||
this.logger.info(`Updating the Person: ${person.id}`);
|
||||
|
||||
const avatardecorations = await this.apNoteService.extractAvatarDecorations(person.tag ?? [], exist.host)
|
||||
.catch(err => {
|
||||
this.logger.error('error occurred while fetching user avatar decorations', { stack: err });
|
||||
return [];
|
||||
});
|
||||
|
||||
avatardecorations.forEach((value, index) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
avatardecorations[index].flipH = person.AvatarDecorations[index].flipH;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
avatardecorations[index].angle = person.AvatarDecorations[index].angle;
|
||||
});
|
||||
|
||||
// カスタム絵文字取得
|
||||
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => {
|
||||
this.logger.info(`extractEmojis: ${e}`);
|
||||
return [];
|
||||
});
|
||||
|
||||
const emojiNames = emojis.map(emoji => emoji.name);
|
||||
|
||||
const fields = this.analyzeAttachments(person.attachment ?? []);
|
||||
|
@ -454,9 +478,9 @@ export class ApPersonService implements OnModuleInit {
|
|||
movedToUri: person.movedTo ?? null,
|
||||
alsoKnownAs: person.alsoKnownAs ?? null,
|
||||
isExplorable: person.discoverable,
|
||||
avatarDecorations: avatardecorations,
|
||||
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
|
||||
} as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
|
||||
|
||||
const moving = ((): boolean => {
|
||||
// 移行先がない→ある
|
||||
if (
|
||||
|
|
|
@ -178,6 +178,10 @@ export interface IActor extends IObject {
|
|||
endpoints?: {
|
||||
sharedInbox?: string;
|
||||
};
|
||||
AvatarDecorations?:{
|
||||
angle: string;
|
||||
flipH: boolean;
|
||||
}
|
||||
'vcard:bday'?: string;
|
||||
'vcard:Address'?: string;
|
||||
}
|
||||
|
@ -228,10 +232,18 @@ export interface IApEmoji extends IObject {
|
|||
name: string;
|
||||
updated: string;
|
||||
}
|
||||
export interface IApAvatarDecoration extends IObject {
|
||||
type: 'AvatarDecoration';
|
||||
name: string;
|
||||
updated: string;
|
||||
}
|
||||
|
||||
export const isEmoji = (object: IObject): object is IApEmoji =>
|
||||
getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
|
||||
|
||||
export const isAvatarDecoration = (object: IObject): object is IApEmoji =>
|
||||
getApType(object) === 'AvatarDecoration' && !Array.isArray(object.icon) && object.icon.url != null;
|
||||
|
||||
export interface IKey extends IObject {
|
||||
type: 'Key';
|
||||
owner: string;
|
||||
|
|
|
@ -16,6 +16,10 @@ export class MiAvatarDecoration {
|
|||
})
|
||||
public updatedAt: Date | null;
|
||||
|
||||
@Column('varchar', {
|
||||
length: 1024, nullable: true,
|
||||
})
|
||||
public host: string;
|
||||
@Column('varchar', {
|
||||
length: 1024,
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue