2017-09-07 21:13:01 +02:00
|
|
|
import * as mongo from 'mongodb';
|
2018-06-18 02:54:53 +02:00
|
|
|
const deepcopy = require('deepcopy');
|
2018-02-02 00:06:01 +01:00
|
|
|
import rap from '@prezzemolo/rap';
|
2018-03-29 13:32:18 +02:00
|
|
|
import db from '../db/mongodb';
|
2018-09-10 11:02:46 +02:00
|
|
|
import { length } from 'stringz';
|
2018-02-02 00:06:01 +01:00
|
|
|
import { IUser, pack as packUser } from './user';
|
|
|
|
import { pack as packApp } from './app';
|
2018-04-14 23:34:55 +02:00
|
|
|
import PollVote, { deletePollVote } from './poll-vote';
|
2018-04-11 20:46:32 +02:00
|
|
|
import Reaction, { deleteNoteReaction } from './note-reaction';
|
2018-10-04 18:43:47 +02:00
|
|
|
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
2018-04-11 20:46:32 +02:00
|
|
|
import NoteWatching, { deleteNoteWatching } from './note-watching';
|
|
|
|
import NoteReaction from './note-reaction';
|
|
|
|
import Favorite, { deleteFavorite } from './favorite';
|
2018-04-14 23:34:55 +02:00
|
|
|
import Notification, { deleteNotification } from './notification';
|
2018-04-28 21:30:51 +02:00
|
|
|
import Following from './following';
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
const Note = db.get<INote>('notes');
|
|
|
|
Note.createIndex('uri', { sparse: true, unique: true });
|
2018-06-10 18:07:34 +02:00
|
|
|
Note.createIndex('userId');
|
2018-09-16 15:48:57 +02:00
|
|
|
Note.createIndex('mentions');
|
|
|
|
Note.createIndex('visibleUserIds');
|
2018-06-16 08:23:03 +02:00
|
|
|
Note.createIndex('tagsLower');
|
2018-09-25 14:09:38 +02:00
|
|
|
Note.createIndex('_files._id');
|
2018-09-05 12:32:46 +02:00
|
|
|
Note.createIndex('_files.contentType');
|
2018-06-08 21:14:26 +02:00
|
|
|
Note.createIndex({
|
|
|
|
createdAt: -1
|
|
|
|
});
|
2018-04-07 19:30:37 +02:00
|
|
|
export default Note;
|
2017-03-01 19:16:39 +01:00
|
|
|
|
|
|
|
export function isValidText(text: string): boolean {
|
2018-09-10 11:02:46 +02:00
|
|
|
return length(text.trim()) <= 1000 && text.trim() != '';
|
2017-03-01 19:16:39 +01:00
|
|
|
}
|
2017-09-07 21:13:01 +02:00
|
|
|
|
2018-03-30 04:24:07 +02:00
|
|
|
export function isValidCw(text: string): boolean {
|
2018-09-10 11:02:46 +02:00
|
|
|
return length(text.trim()) <= 100;
|
2018-03-30 04:24:07 +02:00
|
|
|
}
|
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
export type INote = {
|
2017-09-07 21:13:01 +02:00
|
|
|
_id: mongo.ObjectID;
|
2018-03-29 07:48:47 +02:00
|
|
|
createdAt: Date;
|
2018-04-07 00:19:30 +02:00
|
|
|
deletedAt: Date;
|
2018-09-05 12:32:46 +02:00
|
|
|
fileIds: mongo.ObjectID[];
|
2018-03-29 07:48:47 +02:00
|
|
|
replyId: mongo.ObjectID;
|
2018-04-07 19:30:37 +02:00
|
|
|
renoteId: mongo.ObjectID;
|
2018-06-18 02:54:53 +02:00
|
|
|
poll: {
|
|
|
|
choices: Array<{
|
|
|
|
id: number;
|
|
|
|
}>
|
|
|
|
};
|
2017-09-07 21:13:01 +02:00
|
|
|
text: string;
|
2018-04-01 11:07:29 +02:00
|
|
|
tags: string[];
|
2018-06-16 08:23:03 +02:00
|
|
|
tagsLower: string[];
|
2018-03-30 04:24:07 +02:00
|
|
|
cw: string;
|
2018-03-29 07:48:47 +02:00
|
|
|
userId: mongo.ObjectID;
|
|
|
|
appId: mongo.ObjectID;
|
|
|
|
viaMobile: boolean;
|
2018-04-07 19:30:37 +02:00
|
|
|
renoteCount: number;
|
2018-03-29 07:48:47 +02:00
|
|
|
repliesCount: number;
|
|
|
|
reactionCounts: any;
|
|
|
|
mentions: mongo.ObjectID[];
|
2018-06-12 22:11:55 +02:00
|
|
|
mentionedRemoteUsers: Array<{
|
|
|
|
uri: string;
|
|
|
|
username: string;
|
|
|
|
host: string;
|
|
|
|
}>;
|
2018-04-28 10:25:56 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* public ... 公開
|
|
|
|
* home ... ホームタイムライン(ユーザーページのタイムライン含む)のみに流す
|
|
|
|
* followers ... フォロワーのみ
|
2018-04-28 21:30:51 +02:00
|
|
|
* specified ... visibleUserIds で指定したユーザーのみ
|
2018-04-28 10:25:56 +02:00
|
|
|
* private ... 自分のみ
|
|
|
|
*/
|
2018-04-28 21:30:51 +02:00
|
|
|
visibility: 'public' | 'home' | 'followers' | 'specified' | 'private';
|
|
|
|
|
|
|
|
visibleUserIds: mongo.ObjectID[];
|
2018-04-28 10:25:56 +02:00
|
|
|
|
2018-03-05 00:44:37 +01:00
|
|
|
geo: {
|
2018-03-29 08:23:15 +02:00
|
|
|
coordinates: number[];
|
2018-03-05 00:44:37 +01:00
|
|
|
altitude: number;
|
|
|
|
accuracy: number;
|
|
|
|
altitudeAccuracy: number;
|
|
|
|
heading: number;
|
|
|
|
speed: number;
|
|
|
|
};
|
2018-04-03 16:45:13 +02:00
|
|
|
uri: string;
|
2018-04-05 21:04:50 +02:00
|
|
|
|
2018-04-19 05:43:25 +02:00
|
|
|
// 非正規化
|
2018-04-05 21:04:50 +02:00
|
|
|
_reply?: {
|
|
|
|
userId: mongo.ObjectID;
|
|
|
|
};
|
2018-04-07 19:30:37 +02:00
|
|
|
_renote?: {
|
2018-04-05 21:04:50 +02:00
|
|
|
userId: mongo.ObjectID;
|
|
|
|
};
|
|
|
|
_user: {
|
|
|
|
host: string;
|
2018-04-19 05:43:25 +02:00
|
|
|
inbox?: string;
|
2018-04-05 21:04:50 +02:00
|
|
|
};
|
2018-05-28 17:36:52 +02:00
|
|
|
_replyIds?: mongo.ObjectID[];
|
2018-09-05 12:32:46 +02:00
|
|
|
_files?: IDriveFile[];
|
2017-09-07 21:13:01 +02:00
|
|
|
};
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-04-11 20:46:32 +02:00
|
|
|
/**
|
|
|
|
* Noteを物理削除します
|
|
|
|
*/
|
|
|
|
export async function deleteNote(note: string | mongo.ObjectID | INote) {
|
2018-04-11 11:24:42 +02:00
|
|
|
let n: INote;
|
|
|
|
|
|
|
|
// Populate
|
|
|
|
if (mongo.ObjectID.prototype.isPrototypeOf(note)) {
|
|
|
|
n = await Note.findOne({
|
|
|
|
_id: note
|
|
|
|
});
|
|
|
|
} else if (typeof note === 'string') {
|
|
|
|
n = await Note.findOne({
|
|
|
|
_id: new mongo.ObjectID(note)
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
n = note as INote;
|
|
|
|
}
|
|
|
|
|
2018-04-15 04:57:33 +02:00
|
|
|
console.log(n == null ? `Note: delete skipped ${note}` : `Note: deleting ${n._id}`);
|
|
|
|
|
2018-04-11 11:24:42 +02:00
|
|
|
if (n == null) return;
|
|
|
|
|
2018-04-11 20:46:32 +02:00
|
|
|
// このNoteへの返信をすべて削除
|
|
|
|
await Promise.all((
|
|
|
|
await Note.find({ replyId: n._id })
|
|
|
|
).map(x => deleteNote(x)));
|
2018-04-11 11:24:42 +02:00
|
|
|
|
2018-04-11 20:46:32 +02:00
|
|
|
// このNoteのRenoteをすべて削除
|
|
|
|
await Promise.all((
|
|
|
|
await Note.find({ renoteId: n._id })
|
|
|
|
).map(x => deleteNote(x)));
|
2018-04-11 11:24:42 +02:00
|
|
|
|
2018-04-11 20:46:32 +02:00
|
|
|
// この投稿に対するNoteWatchingをすべて削除
|
|
|
|
await Promise.all((
|
|
|
|
await NoteWatching.find({ noteId: n._id })
|
|
|
|
).map(x => deleteNoteWatching(x)));
|
|
|
|
|
|
|
|
// この投稿に対するNoteReactionをすべて削除
|
|
|
|
await Promise.all((
|
|
|
|
await NoteReaction.find({ noteId: n._id })
|
|
|
|
).map(x => deleteNoteReaction(x)));
|
2018-04-11 11:24:42 +02:00
|
|
|
|
2018-04-12 00:19:28 +02:00
|
|
|
// この投稿に対するPollVoteをすべて削除
|
|
|
|
await Promise.all((
|
|
|
|
await PollVote.find({ noteId: n._id })
|
|
|
|
).map(x => deletePollVote(x)));
|
|
|
|
|
2018-04-11 11:24:42 +02:00
|
|
|
// この投稿に対するFavoriteをすべて削除
|
2018-04-11 20:46:32 +02:00
|
|
|
await Promise.all((
|
|
|
|
await Favorite.find({ noteId: n._id })
|
|
|
|
).map(x => deleteFavorite(x)));
|
|
|
|
|
2018-04-14 23:34:55 +02:00
|
|
|
// この投稿に対するNotificationをすべて削除
|
|
|
|
await Promise.all((
|
|
|
|
await Notification.find({ noteId: n._id })
|
|
|
|
).map(x => deleteNotification(x)));
|
|
|
|
|
2018-04-11 20:46:32 +02:00
|
|
|
// このNoteを削除
|
|
|
|
await Note.remove({
|
|
|
|
_id: n._id
|
|
|
|
});
|
2018-04-15 04:57:33 +02:00
|
|
|
|
|
|
|
console.log(`Note: deleted ${n._id}`);
|
2018-04-11 11:24:42 +02:00
|
|
|
}
|
|
|
|
|
2018-09-09 19:43:16 +02:00
|
|
|
export const hideNote = async (packedNote: any, meId: mongo.ObjectID) => {
|
|
|
|
let hide = false;
|
|
|
|
|
|
|
|
// visibility が private かつ投稿者のIDが自分のIDではなかったら非表示
|
|
|
|
if (packedNote.visibility == 'private' && (meId == null || !meId.equals(packedNote.userId))) {
|
|
|
|
hide = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// visibility が specified かつ自分が指定されていなかったら非表示
|
|
|
|
if (packedNote.visibility == 'specified') {
|
|
|
|
if (meId == null) {
|
|
|
|
hide = true;
|
|
|
|
} else if (meId.equals(packedNote.userId)) {
|
|
|
|
hide = false;
|
|
|
|
} else {
|
|
|
|
// 指定されているかどうか
|
2018-09-17 19:13:42 +02:00
|
|
|
const specified = packedNote.visibleUserIds.some((id: any) => meId.equals(id));
|
2018-09-09 19:43:16 +02:00
|
|
|
|
|
|
|
if (specified) {
|
|
|
|
hide = false;
|
|
|
|
} else {
|
|
|
|
hide = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示
|
|
|
|
if (packedNote.visibility == 'followers') {
|
|
|
|
if (meId == null) {
|
|
|
|
hide = true;
|
|
|
|
} else if (meId.equals(packedNote.userId)) {
|
|
|
|
hide = false;
|
|
|
|
} else {
|
|
|
|
// フォロワーかどうか
|
|
|
|
const following = await Following.findOne({
|
|
|
|
followeeId: packedNote.userId,
|
|
|
|
followerId: meId
|
|
|
|
});
|
|
|
|
|
|
|
|
if (following == null) {
|
|
|
|
hide = true;
|
|
|
|
} else {
|
|
|
|
hide = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hide) {
|
|
|
|
packedNote.fileIds = [];
|
|
|
|
packedNote.files = [];
|
|
|
|
packedNote.text = null;
|
|
|
|
packedNote.poll = null;
|
|
|
|
packedNote.cw = null;
|
|
|
|
packedNote.tags = [];
|
|
|
|
packedNote.geo = null;
|
|
|
|
packedNote.isHidden = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-10-03 17:39:11 +02:00
|
|
|
export const packMany = async (
|
|
|
|
notes: (string | mongo.ObjectID | INote)[],
|
|
|
|
me?: string | mongo.ObjectID | IUser,
|
|
|
|
options?: {
|
|
|
|
detail?: boolean;
|
|
|
|
skipHide?: boolean;
|
|
|
|
}
|
|
|
|
) => {
|
|
|
|
return (await Promise.all(notes.map(n => pack(n, me, options)))).filter(x => x != null);
|
|
|
|
};
|
|
|
|
|
2018-02-02 00:06:01 +01:00
|
|
|
/**
|
2018-04-07 19:30:37 +02:00
|
|
|
* Pack a note for API response
|
2018-02-02 00:06:01 +01:00
|
|
|
*
|
2018-04-07 19:30:37 +02:00
|
|
|
* @param note target
|
2018-02-02 00:06:01 +01:00
|
|
|
* @param me? serializee
|
|
|
|
* @param options? serialize options
|
|
|
|
* @return response
|
|
|
|
*/
|
|
|
|
export const pack = async (
|
2018-04-07 19:30:37 +02:00
|
|
|
note: string | mongo.ObjectID | INote,
|
2018-02-02 00:06:01 +01:00
|
|
|
me?: string | mongo.ObjectID | IUser,
|
|
|
|
options?: {
|
2018-09-09 19:43:16 +02:00
|
|
|
detail?: boolean;
|
|
|
|
skipHide?: boolean;
|
2018-02-02 00:06:01 +01:00
|
|
|
}
|
|
|
|
) => {
|
2018-04-29 00:01:47 +02:00
|
|
|
const opts = Object.assign({
|
2018-09-09 19:43:16 +02:00
|
|
|
detail: true,
|
|
|
|
skipHide: false
|
2018-04-29 00:01:47 +02:00
|
|
|
}, options);
|
2018-02-02 00:06:01 +01:00
|
|
|
|
|
|
|
// Me
|
|
|
|
const meId: mongo.ObjectID = me
|
|
|
|
? mongo.ObjectID.prototype.isPrototypeOf(me)
|
|
|
|
? me as mongo.ObjectID
|
|
|
|
: typeof me === 'string'
|
|
|
|
? new mongo.ObjectID(me)
|
|
|
|
: (me as IUser)._id
|
|
|
|
: null;
|
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
let _note: any;
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
// Populate the note if 'note' is ID
|
|
|
|
if (mongo.ObjectID.prototype.isPrototypeOf(note)) {
|
|
|
|
_note = await Note.findOne({
|
|
|
|
_id: note
|
2018-02-02 00:06:01 +01:00
|
|
|
});
|
2018-04-07 19:30:37 +02:00
|
|
|
} else if (typeof note === 'string') {
|
|
|
|
_note = await Note.findOne({
|
|
|
|
_id: new mongo.ObjectID(note)
|
2018-02-02 00:06:01 +01:00
|
|
|
});
|
|
|
|
} else {
|
2018-04-07 19:30:37 +02:00
|
|
|
_note = deepcopy(note);
|
2018-02-02 00:06:01 +01:00
|
|
|
}
|
|
|
|
|
2018-10-10 19:19:21 +02:00
|
|
|
// (データベースの欠損などで)投稿がデータベース上に見つからなかったとき
|
2018-10-03 17:39:11 +02:00
|
|
|
if (_note == null) {
|
2018-10-10 19:19:21 +02:00
|
|
|
console.warn(`[DAMAGED DB] (missing) pkg: note :: ${note}`);
|
2018-10-03 17:39:11 +02:00
|
|
|
return null;
|
|
|
|
}
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
const id = _note._id;
|
2018-02-02 00:06:01 +01:00
|
|
|
|
|
|
|
// Rename _id to id
|
2018-04-07 19:30:37 +02:00
|
|
|
_note.id = _note._id;
|
|
|
|
delete _note._id;
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-10-07 18:25:34 +02:00
|
|
|
delete _note.prev;
|
|
|
|
delete _note.next;
|
|
|
|
delete _note.tagsLower;
|
2018-04-29 00:01:47 +02:00
|
|
|
delete _note._user;
|
|
|
|
delete _note._reply;
|
2018-09-19 07:18:34 +02:00
|
|
|
delete _note._renote;
|
|
|
|
delete _note._files;
|
2018-04-07 19:30:37 +02:00
|
|
|
if (_note.geo) delete _note.geo.type;
|
2018-02-02 00:06:01 +01:00
|
|
|
|
|
|
|
// Populate user
|
2018-04-07 19:30:37 +02:00
|
|
|
_note.user = packUser(_note.userId, meId);
|
2018-02-02 00:06:01 +01:00
|
|
|
|
|
|
|
// Populate app
|
2018-04-07 19:30:37 +02:00
|
|
|
if (_note.appId) {
|
|
|
|
_note.app = packApp(_note.appId);
|
2018-02-02 00:06:01 +01:00
|
|
|
}
|
|
|
|
|
2018-09-05 12:32:46 +02:00
|
|
|
// Populate files
|
2018-10-04 18:43:47 +02:00
|
|
|
_note.files = packFileMany(_note.fileIds || []);
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-09-05 12:32:46 +02:00
|
|
|
// 後方互換性のため
|
|
|
|
_note.mediaIds = _note.fileIds;
|
|
|
|
_note.media = _note.files;
|
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
// When requested a detailed note data
|
2018-02-02 00:06:01 +01:00
|
|
|
if (opts.detail) {
|
2018-05-14 02:01:37 +02:00
|
|
|
//#region 重いので廃止
|
|
|
|
_note.prev = null;
|
|
|
|
_note.next = null;
|
|
|
|
//#endregion
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
if (_note.replyId) {
|
|
|
|
// Populate reply to note
|
|
|
|
_note.reply = pack(_note.replyId, meId, {
|
2018-02-02 00:06:01 +01:00
|
|
|
detail: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
if (_note.renoteId) {
|
|
|
|
// Populate renote
|
|
|
|
_note.renote = pack(_note.renoteId, meId, {
|
|
|
|
detail: _note.text == null
|
2018-02-02 00:06:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Poll
|
2018-09-09 19:43:16 +02:00
|
|
|
if (meId && _note.poll) {
|
2018-06-12 11:54:36 +02:00
|
|
|
_note.poll = (async poll => {
|
2018-04-14 23:34:55 +02:00
|
|
|
const vote = await PollVote
|
2018-02-02 00:06:01 +01:00
|
|
|
.findOne({
|
2018-03-29 07:48:47 +02:00
|
|
|
userId: meId,
|
2018-04-07 19:30:37 +02:00
|
|
|
noteId: id
|
2018-02-02 00:06:01 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
if (vote != null) {
|
|
|
|
const myChoice = poll.choices
|
2018-06-18 02:54:53 +02:00
|
|
|
.filter((c: any) => c.id == vote.choice)[0];
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-03-29 07:48:47 +02:00
|
|
|
myChoice.isVoted = true;
|
2018-02-02 00:06:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return poll;
|
2018-04-07 19:30:37 +02:00
|
|
|
})(_note.poll);
|
2018-02-02 00:06:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (meId) {
|
2018-10-12 17:54:30 +02:00
|
|
|
// Fetch my reaction
|
2018-04-07 19:30:37 +02:00
|
|
|
_note.myReaction = (async () => {
|
2018-02-02 00:06:01 +01:00
|
|
|
const reaction = await Reaction
|
|
|
|
.findOne({
|
2018-03-29 07:48:47 +02:00
|
|
|
userId: meId,
|
2018-04-07 19:30:37 +02:00
|
|
|
noteId: id,
|
2018-03-29 07:48:47 +02:00
|
|
|
deletedAt: { $exists: false }
|
2018-02-02 00:06:01 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
if (reaction) {
|
|
|
|
return reaction.reaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
})();
|
2018-10-12 17:54:30 +02:00
|
|
|
|
|
|
|
// isFavorited
|
|
|
|
_note.isFavorited = (async () => {
|
|
|
|
const favorite = await Favorite
|
|
|
|
.count({
|
|
|
|
userId: meId,
|
|
|
|
noteId: id
|
|
|
|
}, {
|
|
|
|
limit: 1
|
|
|
|
});
|
|
|
|
|
|
|
|
return favorite === 1;
|
|
|
|
})();
|
2018-02-02 00:06:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
// resolve promises in _note object
|
|
|
|
_note = await rap(_note);
|
2018-02-02 00:06:01 +01:00
|
|
|
|
2018-10-10 19:11:12 +02:00
|
|
|
//#region (データベースの欠損などで)参照しているデータがデータベース上に見つからなかったとき
|
2018-10-04 18:43:47 +02:00
|
|
|
if (_note.user == null) {
|
2018-10-10 19:19:21 +02:00
|
|
|
console.warn(`[DAMAGED DB] (missing) pkg: note -> user :: ${_note.id} (user ${_note.userId})`);
|
2018-10-04 18:43:47 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2018-10-11 10:00:22 +02:00
|
|
|
if (opts.detail) {
|
|
|
|
if (_note.replyId != null && _note.reply == null) {
|
|
|
|
console.warn(`[DAMAGED DB] (missing) pkg: note -> reply :: ${_note.id} (reply ${_note.replyId})`);
|
|
|
|
return null;
|
|
|
|
}
|
2018-10-10 19:11:12 +02:00
|
|
|
|
2018-10-11 10:00:22 +02:00
|
|
|
if (_note.renoteId != null && _note.renote == null) {
|
|
|
|
console.warn(`[DAMAGED DB] (missing) pkg: note -> renote :: ${_note.id} (renote ${_note.renoteId})`);
|
|
|
|
return null;
|
|
|
|
}
|
2018-10-10 19:11:12 +02:00
|
|
|
}
|
|
|
|
//#endregion
|
|
|
|
|
2018-05-21 04:08:08 +02:00
|
|
|
if (_note.user.isCat && _note.text) {
|
2018-08-11 22:19:32 +02:00
|
|
|
_note.text = _note.text.replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ');
|
2018-05-21 04:08:08 +02:00
|
|
|
}
|
|
|
|
|
2018-09-09 19:43:16 +02:00
|
|
|
if (!opts.skipHide) {
|
|
|
|
await hideNote(_note, meId);
|
2018-04-28 21:51:19 +02:00
|
|
|
}
|
|
|
|
|
2018-04-07 19:30:37 +02:00
|
|
|
return _note;
|
2018-02-02 00:06:01 +01:00
|
|
|
};
|