Remote custom emojis (#3074)

* Remote custom emojis

* んほおおおおお
This commit is contained in:
MeiMei
2018-11-02 08:59:40 +09:00
committed by syuilo
parent c48cbd95f6
commit 80b5fda292
18 changed files with 193 additions and 24 deletions

View File

@ -0,0 +1,6 @@
import parse from '../../../mfm/parse';
export default function(text: string) {
if (!text) return [];
return parse(text).filter(t => t.type === 'emoji').map(t => (t as any).emoji);
}

View File

@ -0,0 +1,5 @@
export type IIcon = {
type: string;
mediaType?: string;
url?: string;
};

View File

@ -10,6 +10,9 @@ import { resolvePerson, updatePerson } from './person';
import { resolveImage } from './image';
import { IRemoteUser, IUser } from '../../../models/user';
import htmlToMFM from '../../../mfm/html-to-mfm';
import Emoji from '../../../models/emoji';
import { ITag } from './tag';
import { toUnicode } from 'punycode';
const log = debug('misskey:activitypub');
@ -93,6 +96,10 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
// テキストのパース
const text = note._misskey_content ? note._misskey_content : htmlToMFM(note.content);
await extractEmojis(note.tag, actor.host).catch(e => {
console.log(`extractEmojis: ${e}`);
});
// ユーザーの情報が古かったらついでに更新しておく
if (actor.updatedAt == null || Date.now() - actor.updatedAt.getTime() > 1000 * 60 * 60 * 24) {
updatePerson(note.attributedTo);
@ -135,3 +142,35 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
return await createNote(uri, resolver);
}
async function extractEmojis(tags: ITag[], host_: string) {
const host = toUnicode(host_.toLowerCase());
if (!tags) return [];
const eomjiTags = tags.filter(tag => tag.type === 'Emoji' && tag.icon && tag.icon.url);
return await Promise.all(
eomjiTags.map(async tag => {
const name = tag.name.replace(/^:/, '').replace(/:$/, '');
const exists = await Emoji.findOne({
host,
name
});
if (exists) {
return exists;
}
log(`register emoji host=${host}, name=${name}`);
return await Emoji.insert({
host,
name,
url: tag.icon.url,
aliases: [],
});
})
);
}

View File

@ -0,0 +1,12 @@
import { IIcon } from "./icon";
/***
* tag (ActivityPub)
*/
export type ITag = {
id: string;
type: string;
name?: string;
updated?: Date;
icon?: IIcon;
};

View File

@ -0,0 +1,14 @@
import { IEmoji } from '../../../models/emoji';
import config from '../../../config';
export default (emoji: IEmoji) => ({
id: `${config.url}/emojis/${emoji.name}`,
type: 'Emoji',
name: `:${emoji.name}:`,
updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString,
icon: {
type: 'Image',
mediaType: 'image/png', //Mei-TODO
url: emoji.url
}
});

View File

@ -1,12 +1,16 @@
import renderDocument from './document';
import renderHashtag from './hashtag';
import renderMention from './mention';
import renderEmoji from './emoji';
import config from '../../../config';
import DriveFile, { IDriveFile } from '../../../models/drive-file';
import Note, { INote } from '../../../models/note';
import User from '../../../models/user';
import toHtml from '../misc/get-note-html';
import parseMfm from '../../../mfm/parse';
import getEmojiNames from '../misc/get-emoji-names';
import Emoji, { IEmoji } from '../../../models/emoji';
import { unique } from '../../../prelude/array';
export default async function renderNote(note: INote, dive = true): Promise<any> {
const promisedFiles: Promise<IDriveFile[]> = note.fileIds
@ -75,10 +79,6 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
const hashtagTags = (note.tags || []).map(tag => renderHashtag(tag));
const mentionTags = mentionedUsers.map(u => renderMention(u));
const tag = [
...hashtagTags,
...mentionTags,
];
const files = await promisedFiles;
@ -108,12 +108,24 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
}).join('');
}
const content = toHtml(Object.assign({}, note, { text }));
const emojiNames = unique(getEmojiNames(content));
const emojis = await getEmojis(emojiNames);
const apemojis = emojis.map(emoji => renderEmoji(emoji));
const tag = [
...hashtagTags,
...mentionTags,
...apemojis,
];
return {
id: `${config.url}/notes/${note._id}`,
type: 'Note',
attributedTo,
summary: note.cw,
content: toHtml(Object.assign({}, note, { text })),
content,
_misskey_content: text,
published: note.createdAt.toISOString(),
to,
@ -124,3 +136,18 @@ export default async function renderNote(note: INote, dive = true): Promise<any>
tag
};
}
async function getEmojis(names: string[]): Promise<IEmoji[]> {
if (names == null || names.length < 1) return [];
const emojis = await Promise.all(
names.map(async name => {
return await Emoji.findOne({
name,
host: null
});
})
);
return emojis.filter(emoji => emoji != null);
}