parent
							
								
									c48cbd95f6
								
							
						
					
					
						commit
						80b5fda292
					
				
					 18 changed files with 193 additions and 24 deletions
				
			
		| 
						 | 
				
			
			@ -24,6 +24,9 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
			
		|||
		i: {
 | 
			
		||||
			type: Object,
 | 
			
		||||
			default: null
 | 
			
		||||
		},
 | 
			
		||||
		customEmojis: {
 | 
			
		||||
			required: false,
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -186,17 +189,18 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
			
		|||
 | 
			
		||||
				case 'emoji': {
 | 
			
		||||
					//#region カスタム絵文字
 | 
			
		||||
					const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis || [];
 | 
			
		||||
					const customEmoji = customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
 | 
			
		||||
					if (customEmoji) {
 | 
			
		||||
						return [createElement('img', {
 | 
			
		||||
							attrs: {
 | 
			
		||||
								src: customEmoji.url,
 | 
			
		||||
								alt: token.emoji,
 | 
			
		||||
								title: token.emoji,
 | 
			
		||||
								style: 'height: 2.5em; vertical-align: middle;'
 | 
			
		||||
							}
 | 
			
		||||
						})];
 | 
			
		||||
					if (this.customEmojis != null) {
 | 
			
		||||
						const customEmoji = this.customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
 | 
			
		||||
						if (customEmoji) {
 | 
			
		||||
							return [createElement('img', {
 | 
			
		||||
								attrs: {
 | 
			
		||||
									src: customEmoji.url,
 | 
			
		||||
									alt: token.emoji,
 | 
			
		||||
									title: token.emoji,
 | 
			
		||||
									style: 'height: 2.5em; vertical-align: middle;'
 | 
			
		||||
								}
 | 
			
		||||
							})];
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					//#endregion
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
					</div>
 | 
			
		||||
				</header>
 | 
			
		||||
				<div class="text">
 | 
			
		||||
					<misskey-flavored-markdown v-if="note.text" :text="note.text"/>
 | 
			
		||||
					<misskey-flavored-markdown v-if="note.text" :text="note.text" :customEmojis="p.emojis"/>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@
 | 
			
		|||
				<div class="text">
 | 
			
		||||
					<span v-if="p.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
			
		||||
					<span v-if="p.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
			
		||||
					<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
			
		||||
					<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :customEmojis="p.emojis" />
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
					<mk-media-list :media-list="p.files" :raw="true"/>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@
 | 
			
		|||
					<div class="text">
 | 
			
		||||
						<span v-if="appearNote.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
			
		||||
						<a class="reply" v-if="appearNote.reply">%fa:reply%</a>
 | 
			
		||||
						<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
 | 
			
		||||
						<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text" :customEmojis="appearNote.emojis"/>
 | 
			
		||||
						<a class="rp" v-if="appearNote.renote">RN:</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="files" v-if="appearNote.files.length > 0">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
		<span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
 | 
			
		||||
		<span v-if="note.deletedAt" style="opacity: 0.5">%i18n:@deleted%</span>
 | 
			
		||||
		<a class="reply" v-if="note.replyId">%fa:reply%</a>
 | 
			
		||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
 | 
			
		||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i" :customEmojis="note.emojis"/>
 | 
			
		||||
		<a class="rp" v-if="note.renoteId" :href="`/notes/${note.renoteId}`">RN: ...</a>
 | 
			
		||||
	</div>
 | 
			
		||||
	<details v-if="note.files.length > 0">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,7 @@
 | 
			
		|||
				<div class="text">
 | 
			
		||||
					<span v-if="p.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
			
		||||
					<span v-if="p.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
			
		||||
					<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i"/>
 | 
			
		||||
					<misskey-flavored-markdown v-if="p.text" :text="p.text" :i="$store.state.i" :customEmojis="p.emojis"/>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="files" v-if="p.files.length > 0">
 | 
			
		||||
					<mk-media-list :media-list="p.files" :raw="true"/>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,7 +30,7 @@
 | 
			
		|||
					<div class="text">
 | 
			
		||||
						<span v-if="appearNote.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
			
		||||
						<a class="reply" v-if="appearNote.reply">%fa:reply%</a>
 | 
			
		||||
						<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text"/>
 | 
			
		||||
						<misskey-flavored-markdown v-if="appearNote.text" :text="appearNote.text" :i="$store.state.i" :class="$style.text" :customEmojis="appearNote.emojis"/>
 | 
			
		||||
						<a class="rp" v-if="appearNote.renote != null">RN:</a>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div class="files" v-if="appearNote.files.length > 0">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
		<span v-if="note.isHidden" style="opacity: 0.5">(%i18n:@private%)</span>
 | 
			
		||||
		<span v-if="note.deletedAt" style="opacity: 0.5">(%i18n:@deleted%)</span>
 | 
			
		||||
		<a class="reply" v-if="note.replyId">%fa:reply%</a>
 | 
			
		||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i"/>
 | 
			
		||||
		<misskey-flavored-markdown v-if="note.text" :text="note.text" :i="$store.state.i" :customEmojis="note.emojis"/>
 | 
			
		||||
		<a class="rp" v-if="note.renoteId">RN: ...</a>
 | 
			
		||||
	</div>
 | 
			
		||||
	<details v-if="note.files.length > 0">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/models/emoji.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/models/emoji.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
import db from '../db/mongodb';
 | 
			
		||||
 | 
			
		||||
const Emoji = db.get<IEmoji>('emoji');
 | 
			
		||||
 | 
			
		||||
Emoji.createIndex(['name', 'host'], { unique: true });
 | 
			
		||||
 | 
			
		||||
export default Emoji;
 | 
			
		||||
 | 
			
		||||
export type IEmoji = {
 | 
			
		||||
	name: string;
 | 
			
		||||
	host: string;
 | 
			
		||||
	url: string;
 | 
			
		||||
	aliases?: string[];
 | 
			
		||||
	updatedAt?: Date;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const packEmojis = async (
 | 
			
		||||
	host: string,
 | 
			
		||||
	// MeiTODO: filter
 | 
			
		||||
) => {
 | 
			
		||||
	return await Emoji.find({ host });
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -12,6 +12,7 @@ import { packMany as packFileMany, IDriveFile } from './drive-file';
 | 
			
		|||
import Favorite from './favorite';
 | 
			
		||||
import Following from './following';
 | 
			
		||||
import config from '../config';
 | 
			
		||||
import { packEmojis } from './emoji';
 | 
			
		||||
 | 
			
		||||
const Note = db.get<INote>('notes');
 | 
			
		||||
Note.createIndex('uri', { sparse: true, unique: true });
 | 
			
		||||
| 
						 | 
				
			
			@ -228,6 +229,11 @@ export const pack = async (
 | 
			
		|||
 | 
			
		||||
	const id = _note._id;
 | 
			
		||||
 | 
			
		||||
	// _note._userを消す前か、_note.userを解決した後でないとホストがわからない
 | 
			
		||||
	if (_note._user) {
 | 
			
		||||
		_note.emojis = packEmojis(_note._user.host);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Rename _id to id
 | 
			
		||||
	_note.id = _note._id;
 | 
			
		||||
	delete _note._id;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								src/remote/activitypub/misc/get-emoji-names.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/remote/activitypub/misc/get-emoji-names.ts
									
										
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								src/remote/activitypub/models/icon.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/remote/activitypub/models/icon.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
export type IIcon = {
 | 
			
		||||
	type: string;
 | 
			
		||||
	mediaType?: string;
 | 
			
		||||
	url?: string;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -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: [],
 | 
			
		||||
			});
 | 
			
		||||
		})
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								src/remote/activitypub/models/tag.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/remote/activitypub/models/tag.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
import { IIcon } from "./icon";
 | 
			
		||||
 | 
			
		||||
/***
 | 
			
		||||
 * tag (ActivityPub)
 | 
			
		||||
 */
 | 
			
		||||
export type ITag = {
 | 
			
		||||
	id: string;
 | 
			
		||||
	type: string;
 | 
			
		||||
	name?: string;
 | 
			
		||||
	updated?: Date;
 | 
			
		||||
	icon?: IIcon;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										14
									
								
								src/remote/activitypub/renderer/emoji.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/remote/activitypub/renderer/emoji.ts
									
										
									
									
									
										Normal 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
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import * as os from 'os';
 | 
			
		|||
import config from '../../../config';
 | 
			
		||||
import Meta from '../../../models/meta';
 | 
			
		||||
import { ILocalUser } from '../../../models/user';
 | 
			
		||||
import Emoji from '../../../models/emoji';
 | 
			
		||||
 | 
			
		||||
const pkg = require('../../../../package.json');
 | 
			
		||||
const client = require('../../../../built/client/meta.json');
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,8 @@ export const meta = {
 | 
			
		|||
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
 | 
			
		||||
	const meta: any = (await Meta.findOne()) || {};
 | 
			
		||||
 | 
			
		||||
	const emojis = await Emoji.find({ host: null });
 | 
			
		||||
 | 
			
		||||
	res({
 | 
			
		||||
		maintainer: config.maintainer,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +53,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
 | 
			
		|||
		hidedTags: (me && me.isAdmin) ? meta.hidedTags : undefined,
 | 
			
		||||
		bannerUrl: meta.bannerUrl,
 | 
			
		||||
		maxNoteTextLength: config.maxNoteTextLength,
 | 
			
		||||
		emojis: meta.emojis,
 | 
			
		||||
		emojis: emojis,
 | 
			
		||||
 | 
			
		||||
		features: {
 | 
			
		||||
			registration: !meta.disableRegistration,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										31
									
								
								src/tools/add-emoji.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/tools/add-emoji.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
import * as debug from 'debug';
 | 
			
		||||
import Emoji from "../models/emoji";
 | 
			
		||||
 | 
			
		||||
debug.enable('*');
 | 
			
		||||
 | 
			
		||||
async function main(name: string, url: string, alias?: string): Promise<any> {
 | 
			
		||||
	const aliases = alias != null ? [ alias ] : [];
 | 
			
		||||
 | 
			
		||||
	await Emoji.insert({
 | 
			
		||||
		host: null,
 | 
			
		||||
		name,
 | 
			
		||||
		url,
 | 
			
		||||
		aliases,
 | 
			
		||||
		updatedAt: new Date()
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const args = process.argv.slice(2);
 | 
			
		||||
const name = args[0];
 | 
			
		||||
const url = args[1];
 | 
			
		||||
 | 
			
		||||
if (!name) throw 'require name';
 | 
			
		||||
if (!url) throw 'require url';
 | 
			
		||||
 | 
			
		||||
main(name, url).then(() => {
 | 
			
		||||
	console.log('success');
 | 
			
		||||
	process.exit(0);
 | 
			
		||||
}).catch(e => {
 | 
			
		||||
	console.warn(e);
 | 
			
		||||
	process.exit(1);
 | 
			
		||||
});
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue