diff --git a/src/remote/activitypub/models/identifier.ts b/src/remote/activitypub/models/identifier.ts new file mode 100644 index 0000000000..f6c3bb8c88 --- /dev/null +++ b/src/remote/activitypub/models/identifier.ts @@ -0,0 +1,5 @@ +export type IIdentifier = { + type: string; + name: string; + value: string; +}; diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index cbde5dc698..c7a76b4bd6 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -19,6 +19,7 @@ import getDriveFileUrl from '../../../misc/get-drive-file-url'; import { IEmoji } from '../../../models/emoji'; import { ITag } from './tag'; import Following from '../../../models/following'; +import { IIdentifier } from './identifier'; const log = debug('misskey:activitypub'); @@ -137,9 +138,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { - console.log(`cat not extract fields: ${e}`); - }); + const { fields, services } = analyzeAttachments(person.attachment); const isBot = object.type == 'Service'; @@ -171,7 +170,8 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise emoji.name); - const fields = await extractFields(person.attachment).catch(e => { - console.log(`cat not extract fields: ${e}`); - }); + const { fields, services } = analyzeAttachments(person.attachment); const updates = { lastFetchedAt: new Date(), @@ -350,6 +348,7 @@ export async function updatePerson(uri: string, resolver?: Resolver, hint?: obje url: person.url, endpoints: person.endpoints, fields, + ...services, isBot: object.type == 'Service', isCat: (person as any).isCat === true, isLocked: person.manuallyApprovesFollowers, @@ -413,16 +412,61 @@ export async function resolvePerson(uri: string, verifier?: string, resolver?: R return await createPerson(uri, resolver); } -export async function extractFields(attachments: ITag[]) { - if (!attachments) return []; +const isPropertyValue = (x: { + type: string, + name?: string, + value?: string + }) => + x && + x.type === 'PropertyValue' && + typeof x.name === 'string' && + typeof x.value === 'string'; - return attachments.filter(a => a.type === 'PropertyValue' && a.name && a.value) - .map(a => { - return { - name: a.name, - value: htmlToMFM(a.value) - }; - }); +const services: { + [x: string]: (id: string, username: string) => any + } = { + 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }), + 'misskey:authentication:github': (id, login) => ({ id, login }), + 'misskey:authentication:discord': (id, name) => $discord(id, name) +}; + +const $discord = (id: string, name: string) => { + if (typeof name !== 'string') + name = 'unknown#0000'; + const [username, discriminator] = name.split('#'); + return { id, username, discriminator }; +}; + +function addService(target: { [x: string]: any }, source: IIdentifier) { + const service = services[source.name]; + + if (typeof source.value !== 'string') + source.value = 'unknown'; + + const [id, username] = source.value.split('@'); + + if (service) + target[source.name.split(':')[2]] = service(id, username); +} + +export function analyzeAttachments(attachments: ITag[]) { + const fields: { + name: string, + value: string + }[] = []; + const services: { [x: string]: any } = {}; + + if (Array.isArray(attachments)) + for (const attachment of attachments.filter(isPropertyValue)) + if (isPropertyValue(attachment.identifier)) + addService(services, attachment.identifier); + else + fields.push({ + name: attachment.name, + value: htmlToMFM(attachment.value) + }); + + return { fields, services }; } export async function updateFeatured(userId: mongo.ObjectID) { diff --git a/src/remote/activitypub/models/tag.ts b/src/remote/activitypub/models/tag.ts index 347e001aec..b64d6bd4a7 100644 --- a/src/remote/activitypub/models/tag.ts +++ b/src/remote/activitypub/models/tag.ts @@ -1,4 +1,5 @@ import { IIcon } from './icon'; +import { IIdentifier } from './identifier'; /*** * tag (ActivityPub) @@ -10,4 +11,5 @@ export type ITag = { value?: string; updated?: Date; icon?: IIcon; + identifier?: IIdentifier; }; diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index aaf78444d4..096596b021 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -7,6 +7,7 @@ import parse from '../../../mfm/parse'; import DriveFile from '../../../models/drive-file'; import { getEmojis } from './note'; import renderEmoji from './emoji'; +import { IIdentifier } from '../models/identifier'; export default async (user: ILocalUser) => { const id = `${config.url}/users/${user._id}`; @@ -20,14 +21,20 @@ export default async (user: ILocalUser) => { type: string, name: string, value: string, - verified_at?: string + verified_at?: string, + identifier?: IIdentifier }[] = []; if (user.twitter) { attachment.push({ type: 'PropertyValue', name: 'Twitter', - value: `@${user.twitter.screenName}` + value: `@${user.twitter.screenName}`, + identifier: { + type: 'PropertyValue', + name: 'misskey:authentication:twitter', + value: `${user.twitter.userId}@${user.twitter.screenName}` + } }); } @@ -35,7 +42,12 @@ export default async (user: ILocalUser) => { attachment.push({ type: 'PropertyValue', name: 'GitHub', - value: `@${user.github.login}` + value: `@${user.github.login}`, + identifier: { + type: 'PropertyValue', + name: 'misskey:authentication:github', + value: `${user.github.id}@${user.github.login}` + } }); } @@ -43,7 +55,12 @@ export default async (user: ILocalUser) => { attachment.push({ type: 'PropertyValue', name: 'Discord', - value: `${user.discord.username}#${user.discord.discriminator}` + value: `${user.discord.username}#${user.discord.discriminator}`, + identifier: { + type: 'PropertyValue', + name: 'misskey:authentication:discord', + value: `${user.discord.id}@${user.discord.username}#${user.discord.discriminator}` + } }); }