1
0
forked from mirror/misskey

互換性のためのコードを追加 & #2623

This commit is contained in:
syuilo 2018-09-05 23:55:51 +09:00
parent 229e85b2c5
commit b5ff2abdb9
No known key found for this signature in database
GPG Key ID: BDC4C49D06AB9D69
8 changed files with 435 additions and 284 deletions

View File

@ -1,51 +1,65 @@
/**
* Module dependencies
*/
import $ from 'cafy'; import ID from '../../../misc/cafy-id'; import $ from 'cafy'; import ID from '../../../misc/cafy-id';
import Note, { pack } from '../../../models/note'; import Note, { pack } from '../../../models/note';
import getParams from '../get-params';
export const meta = {
desc: {
'ja-JP': '投稿を取得します。'
},
params: {
local: $.bool.optional.note({
desc: {
'ja-JP': 'ローカルの投稿に限定するか否か'
}
}),
reply: $.bool.optional.note({
desc: {
'ja-JP': '返信に限定するか否か'
}
}),
renote: $.bool.optional.note({
desc: {
'ja-JP': 'Renoteに限定するか否か'
}
}),
withFiles: $.bool.optional.note({
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か'
}
}),
media: $.bool.optional.note({
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
poll: $.bool.optional.note({
desc: {
'ja-JP': 'アンケートが添付された投稿に限定するか否か'
}
}),
limit: $.num.optional.range(1, 100).note({
default: 10
}),
sinceId: $.type(ID).optional.note({}),
untilId: $.type(ID).optional.note({}),
}
};
/**
* Get all notes
*/
export default (params: any) => new Promise(async (res, rej) => { export default (params: any) => new Promise(async (res, rej) => {
// Get 'local' parameter const [ps, psErr] = getParams(meta, params);
const [local, localErr] = $.bool.optional.get(params.local); if (psErr) throw psErr;
if (localErr) return rej('invalid local param');
// Get 'reply' parameter
const [reply, replyErr] = $.bool.optional.get(params.reply);
if (replyErr) return rej('invalid reply param');
// Get 'renote' parameter
const [renote, renoteErr] = $.bool.optional.get(params.renote);
if (renoteErr) return rej('invalid renote param');
// Get 'files' parameter
const [files, filesErr] = $.bool.optional.get(params.files);
if (filesErr) return rej('invalid files param');
// Get 'poll' parameter
const [poll, pollErr] = $.bool.optional.get(params.poll);
if (pollErr) return rej('invalid poll param');
// Get 'bot' parameter
//const [bot, botErr] = $.bool.optional.get(params.bot);
//if (botErr) return rej('invalid bot param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Check if both of sinceId and untilId is specified // Check if both of sinceId and untilId is specified
if (sinceId && untilId) { if (ps.sinceId && ps.untilId) {
return rej('cannot set sinceId and untilId'); return rej('cannot set sinceId and untilId');
} }
@ -56,35 +70,37 @@ export default (params: any) => new Promise(async (res, rej) => {
const query = { const query = {
visibility: 'public' visibility: 'public'
} as any; } as any;
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} }
if (local) { if (ps.local) {
query['_user.host'] = null; query['_user.host'] = null;
} }
if (reply != undefined) { if (ps.reply != undefined) {
query.replyId = reply ? { $exists: true, $ne: null } : null; query.replyId = ps.reply ? { $exists: true, $ne: null } : null;
} }
if (renote != undefined) { if (ps.renote != undefined) {
query.renoteId = renote ? { $exists: true, $ne: null } : null; query.renoteId = ps.renote ? { $exists: true, $ne: null } : null;
} }
if (files != undefined) { const withFiles = ps.withFiles != undefined ? ps.withFiles : ps.media;
query.fileIds = files ? { $exists: true, $ne: null } : [];
if (withFiles) {
query.fileIds = withFiles ? { $exists: true, $ne: null } : [];
} }
if (poll != undefined) { if (ps.poll != undefined) {
query.poll = poll ? { $exists: true, $ne: null } : null; query.poll = ps.poll ? { $exists: true, $ne: null } : null;
} }
// TODO // TODO
@ -95,7 +111,7 @@ export default (params: any) => new Promise(async (res, rej) => {
// Issue query // Issue query
const notes = await Note const notes = await Note
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });

View File

@ -3,40 +3,49 @@ import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { pack } from '../../../../models/note'; import { pack } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': 'グローバルタイムラインを取得します。'
},
params: {
withFiles: $.bool.optional.note({
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
limit: $.num.optional.range(1, 100).note({
default: 10
}),
sinceId: $.type(ID).optional.note({}),
untilId: $.type(ID).optional.note({}),
sinceDate: $.num.optional.note({}),
untilDate: $.num.optional.note({}),
}
};
/**
* Get timeline of global
*/
export default async (params: any, user: ILocalUser) => { export default async (params: any, user: ILocalUser) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) throw psErr;
if (limitErr) throw 'invalid limit param';
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) throw 'invalid sinceId param';
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) throw 'invalid untilId param';
// Get 'sinceDate' parameter
const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
if (sinceDateErr) throw 'invalid sinceDate param';
// Get 'untilDate' parameter
const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
if (untilDateErr) throw 'invalid untilDate param';
// Check if only one of sinceId, untilId, sinceDate, untilDate specified // Check if only one of sinceId, untilId, sinceDate, untilDate specified
if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) { if ([ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate].filter(x => x != null).length > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
} }
// Get 'withFiles' parameter
const [withFiles, withFilesErr] = $.bool.optional.get(params.withFiles);
if (withFilesErr) throw 'invalid withFiles param';
// ミュートしているユーザーを取得 // ミュートしているユーザーを取得
const mutedUserIds = user ? (await Mute.find({ const mutedUserIds = user ? (await Mute.find({
muterId: user._id muterId: user._id
@ -68,27 +77,29 @@ export default async (params: any, user: ILocalUser) => {
}; };
} }
const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
if (withFiles) { if (withFiles) {
query.fileIds = { $exists: true, $ne: [] }; query.fileIds = { $exists: true, $ne: [] };
} }
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} else if (sinceDate) { } else if (ps.sinceDate) {
sort._id = 1; sort._id = 1;
query.createdAt = { query.createdAt = {
$gt: new Date(sinceDate) $gt: new Date(ps.sinceDate)
}; };
} else if (untilDate) { } else if (ps.untilDate) {
query.createdAt = { query.createdAt = {
$lt: new Date(untilDate) $lt: new Date(ps.untilDate)
}; };
} }
//#endregion //#endregion
@ -96,7 +107,7 @@ export default async (params: any, user: ILocalUser) => {
// Issue query // Issue query
const timeline = await Note const timeline = await Note
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });

View File

@ -7,8 +7,6 @@ import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params'; import getParams from '../../get-params';
export const meta = { export const meta = {
name: 'notes/hybrid-timeline',
desc: { desc: {
'ja-JP': 'ハイブリッドタイムラインを取得します。' 'ja-JP': 'ハイブリッドタイムラインを取得します。'
}, },
@ -68,7 +66,13 @@ export const meta = {
withFiles: $.bool.optional.note({ withFiles: $.bool.optional.note({
desc: { desc: {
'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), }),
} }
@ -203,7 +207,7 @@ export default async (params: any, user: ILocalUser) => {
}); });
} }
if (ps.withFiles) { if (ps.withFiles || ps.mediaOnly) {
query.$and.push({ query.$and.push({
fileIds: { $exists: true, $ne: [] } fileIds: { $exists: true, $ne: [] }
}); });

View File

@ -3,40 +3,49 @@ import Note from '../../../../models/note';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { pack } from '../../../../models/note'; import { pack } from '../../../../models/note';
import { ILocalUser } from '../../../../models/user'; import { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': 'ローカルタイムラインを取得します。'
},
params: {
withFiles: $.bool.optional.note({
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
limit: $.num.optional.range(1, 100).note({
default: 10
}),
sinceId: $.type(ID).optional.note({}),
untilId: $.type(ID).optional.note({}),
sinceDate: $.num.optional.note({}),
untilDate: $.num.optional.note({}),
}
};
/**
* Get timeline of local
*/
export default async (params: any, user: ILocalUser) => { export default async (params: any, user: ILocalUser) => {
// Get 'limit' parameter const [ps, psErr] = getParams(meta, params);
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit); if (psErr) throw psErr;
if (limitErr) throw 'invalid limit param';
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) throw 'invalid sinceId param';
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) throw 'invalid untilId param';
// Get 'sinceDate' parameter
const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
if (sinceDateErr) throw 'invalid sinceDate param';
// Get 'untilDate' parameter
const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
if (untilDateErr) throw 'invalid untilDate param';
// Check if only one of sinceId, untilId, sinceDate, untilDate specified // Check if only one of sinceId, untilId, sinceDate, untilDate specified
if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) { if ([ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate].filter(x => x != null).length > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
} }
// Get 'withFiles' parameter
const [withFiles, withFilesErr] = $.bool.optional.get(params.withFiles);
if (withFilesErr) throw 'invalid withFiles param';
// ミュートしているユーザーを取得 // ミュートしているユーザーを取得
const mutedUserIds = user ? (await Mute.find({ const mutedUserIds = user ? (await Mute.find({
muterId: user._id muterId: user._id
@ -69,27 +78,29 @@ export default async (params: any, user: ILocalUser) => {
}; };
} }
const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
if (withFiles) { if (withFiles) {
query.fileIds = { $exists: true, $ne: [] }; query.fileIds = { $exists: true, $ne: [] };
} }
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} else if (sinceDate) { } else if (ps.sinceDate) {
sort._id = 1; sort._id = 1;
query.createdAt = { query.createdAt = {
$gt: new Date(sinceDate) $gt: new Date(ps.sinceDate)
}; };
} else if (untilDate) { } else if (ps.untilDate) {
query.createdAt = { query.createdAt = {
$lt: new Date(untilDate) $lt: new Date(ps.untilDate)
}; };
} }
//#endregion //#endregion
@ -97,7 +108,7 @@ export default async (params: any, user: ILocalUser) => {
// Issue query // Issue query
const timeline = await Note const timeline = await Note
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });

View File

@ -4,119 +4,152 @@ import User, { ILocalUser } from '../../../../models/user';
import Mute from '../../../../models/mute'; import Mute from '../../../../models/mute';
import { getFriendIds } from '../../common/get-friends'; import { getFriendIds } from '../../common/get-friends';
import { pack } from '../../../../models/note'; import { pack } from '../../../../models/note';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定されたタグが付けられた投稿を取得します。'
},
params: {
tag: $.str.note({
desc: {
'ja-JP': 'タグ'
}
}),
includeUserIds: $.arr($.type(ID)).optional.note({
default: []
}),
excludeUserIds: $.arr($.type(ID)).optional.note({
default: []
}),
includeUserUsernames: $.arr($.str).optional.note({
default: []
}),
excludeUserUsernames: $.arr($.str).optional.note({
default: []
}),
following: $.bool.optional.nullable.note({
default: null
}),
mute: $.str.optional.note({
default: 'mute_all'
}),
reply: $.bool.optional.nullable.note({
default: null,
desc: {
'ja-JP': '返信に限定するか否か'
}
}),
renote: $.bool.optional.nullable.note({
default: null,
desc: {
'ja-JP': 'Renoteに限定するか否か'
}
}),
withFiles: $.bool.optional.nullable.note({
default: null,
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か'
}
}),
media: $.bool.optional.nullable.note({
default: null,
desc: {
'ja-JP': 'ファイルが添付された投稿に限定するか否か (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
poll: $.bool.optional.nullable.note({
default: null,
desc: {
'ja-JP': 'アンケートが添付された投稿に限定するか否か'
}
}),
sinceDate: $.num.optional.note({
}),
untilDate: $.num.optional.note({
}),
offset: $.num.optional.min(0).note({
default: 0
}),
limit: $.num.optional.range(1, 30).note({
default: 10
}),
}
};
/**
* Search notes by tag
*/
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
// Get 'tag' parameter const [ps, psErr] = getParams(meta, params);
const [tag, tagError] = $.str.get(params.tag); if (psErr) throw psErr;
if (tagError) return rej('invalid tag param');
// Get 'includeUserIds' parameter if (ps.includeUserUsernames != null) {
const [includeUserIds = [], includeUserIdsErr] = $.arr($.type(ID)).optional.get(params.includeUserIds); const ids = (await Promise.all(ps.includeUserUsernames.map(async (username) => {
if (includeUserIdsErr) return rej('invalid includeUserIds param');
// Get 'excludeUserIds' parameter
const [excludeUserIds = [], excludeUserIdsErr] = $.arr($.type(ID)).optional.get(params.excludeUserIds);
if (excludeUserIdsErr) return rej('invalid excludeUserIds param');
// Get 'includeUserUsernames' parameter
const [includeUserUsernames = [], includeUserUsernamesErr] = $.arr($.str).optional.get(params.includeUserUsernames);
if (includeUserUsernamesErr) return rej('invalid includeUserUsernames param');
// Get 'excludeUserUsernames' parameter
const [excludeUserUsernames = [], excludeUserUsernamesErr] = $.arr($.str).optional.get(params.excludeUserUsernames);
if (excludeUserUsernamesErr) return rej('invalid excludeUserUsernames param');
// Get 'following' parameter
const [following = null, followingErr] = $.bool.optional.nullable.get(params.following);
if (followingErr) return rej('invalid following param');
// Get 'mute' parameter
const [mute = 'mute_all', muteErr] = $.str.optional.get(params.mute);
if (muteErr) return rej('invalid mute param');
// Get 'reply' parameter
const [reply = null, replyErr] = $.bool.optional.nullable.get(params.reply);
if (replyErr) return rej('invalid reply param');
// Get 'renote' parameter
const [renote = null, renoteErr] = $.bool.optional.nullable.get(params.renote);
if (renoteErr) return rej('invalid renote param');
// Get 'withFiles' parameter
const [withFiles = null, withFilesErr] = $.bool.optional.nullable.get(params.withFiles);
if (withFilesErr) return rej('invalid withFiles param');
// Get 'poll' parameter
const [poll = null, pollErr] = $.bool.optional.nullable.get(params.poll);
if (pollErr) return rej('invalid poll param');
// Get 'sinceDate' parameter
const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
if (sinceDateErr) throw 'invalid sinceDate param';
// Get 'untilDate' parameter
const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
if (untilDateErr) throw 'invalid untilDate param';
// Get 'offset' parameter
const [offset = 0, offsetErr] = $.num.optional.min(0).get(params.offset);
if (offsetErr) return rej('invalid offset param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 30).get(params.limit);
if (limitErr) return rej('invalid limit param');
if (includeUserUsernames != null) {
const ids = (await Promise.all(includeUserUsernames.map(async (username) => {
const _user = await User.findOne({ const _user = await User.findOne({
usernameLower: username.toLowerCase() usernameLower: username.toLowerCase()
}); });
return _user ? _user._id : null; return _user ? _user._id : null;
}))).filter(id => id != null); }))).filter(id => id != null);
ids.forEach(id => includeUserIds.push(id)); ids.forEach(id => ps.includeUserIds.push(id));
} }
if (excludeUserUsernames != null) { if (ps.excludeUserUsernames != null) {
const ids = (await Promise.all(excludeUserUsernames.map(async (username) => { const ids = (await Promise.all(ps.excludeUserUsernames.map(async (username) => {
const _user = await User.findOne({ const _user = await User.findOne({
usernameLower: username.toLowerCase() usernameLower: username.toLowerCase()
}); });
return _user ? _user._id : null; return _user ? _user._id : null;
}))).filter(id => id != null); }))).filter(id => id != null);
ids.forEach(id => excludeUserIds.push(id)); ids.forEach(id => ps.excludeUserIds.push(id));
} }
let q: any = { let q: any = {
$and: [{ $and: [{
tagsLower: tag.toLowerCase() tagsLower: ps.tag.toLowerCase()
}] }]
}; };
const push = (x: any) => q.$and.push(x); const push = (x: any) => q.$and.push(x);
if (includeUserIds && includeUserIds.length != 0) { if (ps.includeUserIds && ps.includeUserIds.length != 0) {
push({ push({
userId: { userId: {
$in: includeUserIds $in: ps.includeUserIds
} }
}); });
} else if (excludeUserIds && excludeUserIds.length != 0) { } else if (ps.excludeUserIds && ps.excludeUserIds.length != 0) {
push({ push({
userId: { userId: {
$nin: excludeUserIds $nin: ps.excludeUserIds
} }
}); });
} }
if (following != null && me != null) { if (ps.following != null && me != null) {
const ids = await getFriendIds(me._id, false); const ids = await getFriendIds(me._id, false);
push({ push({
userId: following ? { userId: ps.following ? {
$in: ids $in: ids
} : { } : {
$nin: ids.concat(me._id) $nin: ids.concat(me._id)
@ -131,7 +164,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
}); });
const mutedUserIds = mutes.map(m => m.muteeId); const mutedUserIds = mutes.map(m => m.muteeId);
switch (mute) { switch (ps.mute) {
case 'mute_all': case 'mute_all':
push({ push({
userId: { userId: {
@ -202,8 +235,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
} }
if (reply != null) { if (ps.reply != null) {
if (reply) { if (ps.reply) {
push({ push({
replyId: { replyId: {
$exists: true, $exists: true,
@ -223,8 +256,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
} }
if (renote != null) { if (ps.renote != null) {
if (renote) { if (ps.renote) {
push({ push({
renoteId: { renoteId: {
$exists: true, $exists: true,
@ -244,6 +277,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
} }
const withFiles = ps.withFiles != null ? ps.withFiles : ps.media;
if (withFiles != null) { if (withFiles != null) {
if (withFiles) { if (withFiles) {
push({ push({
@ -265,8 +300,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
} }
if (poll != null) { if (ps.poll != null) {
if (poll) { if (ps.poll) {
push({ push({
poll: { poll: {
$exists: true, $exists: true,
@ -286,18 +321,18 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
} }
} }
if (sinceDate) { if (ps.sinceDate) {
push({ push({
createdAt: { createdAt: {
$gt: new Date(sinceDate) $gt: new Date(ps.sinceDate)
} }
}); });
} }
if (untilDate) { if (ps.untilDate) {
push({ push({
createdAt: { createdAt: {
$lt: new Date(untilDate) $lt: new Date(ps.untilDate)
} }
}); });
} }
@ -312,8 +347,8 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
sort: { sort: {
_id: -1 _id: -1
}, },
limit: limit, limit: ps.limit,
skip: offset skip: ps.offset
}); });
// Serialize // Serialize

View File

@ -69,7 +69,13 @@ export const meta = {
withFiles: $.bool.optional.note({ withFiles: $.bool.optional.note({
desc: { desc: {
'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), }),
} }
@ -193,7 +199,9 @@ export default async (params: any, user: ILocalUser) => {
}); });
} }
if (ps.withFiles) { const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
if (withFiles) {
query.$and.push({ query.$and.push({
fileIds: { $exists: true, $ne: [] } fileIds: { $exists: true, $ne: [] }
}); });

View File

@ -75,7 +75,13 @@ export const meta = {
withFiles: $.bool.optional.note({ withFiles: $.bool.optional.note({
desc: { desc: {
'ja-JP': 'true にすると、メディアが添付された投稿だけ取得します' 'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
}
}),
mediaOnly: $.bool.optional.note({
desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
} }
}), }),
} }
@ -199,7 +205,9 @@ export default async (params: any, user: ILocalUser) => {
}); });
} }
if (ps.withFiles) { const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
if (withFiles) {
query.$and.push({ query.$and.push({
fileIds: { $exists: true, $ne: [] } fileIds: { $exists: true, $ne: [] }
}); });

View File

@ -2,63 +2,121 @@ import $ from 'cafy'; import ID from '../../../../misc/cafy-id';
import getHostLower from '../../common/get-host-lower'; import getHostLower from '../../common/get-host-lower';
import Note, { pack } from '../../../../models/note'; import Note, { pack } from '../../../../models/note';
import User, { ILocalUser } from '../../../../models/user'; import User, { ILocalUser } from '../../../../models/user';
import getParams from '../../get-params';
export const meta = {
desc: {
'ja-JP': '指定したユーザーのタイムラインを取得します。'
},
params: {
userId: $.type(ID).optional.note({
desc: {
'ja-JP': 'ユーザーID'
}
}),
username: $.str.optional.note({
desc: {
'ja-JP': 'ユーザー名'
}
}),
host: $.str.optional.note({
}),
includeReplies: $.bool.optional.note({
default: true,
desc: {
'ja-JP': 'リプライを含めるか否か'
}
}),
limit: $.num.optional.range(1, 100).note({
default: 10,
desc: {
'ja-JP': '最大数'
}
}),
sinceId: $.type(ID).optional.note({
desc: {
'ja-JP': '指定すると、この投稿を基点としてより新しい投稿を取得します'
}
}),
untilId: $.type(ID).optional.note({
desc: {
'ja-JP': '指定すると、この投稿を基点としてより古い投稿を取得します'
}
}),
sinceDate: $.num.optional.note({
desc: {
'ja-JP': '指定した時間を基点としてより新しい投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
untilDate: $.num.optional.note({
desc: {
'ja-JP': '指定した時間を基点としてより古い投稿を取得します。数値は、1970年1月1日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。'
}
}),
includeMyRenotes: $.bool.optional.note({
default: true,
desc: {
'ja-JP': '自分の行ったRenoteを含めるかどうか'
}
}),
includeRenotedMyNotes: $.bool.optional.note({
default: true,
desc: {
'ja-JP': 'Renoteされた自分の投稿を含めるかどうか'
}
}),
includeLocalRenotes: $.bool.optional.note({
default: true,
desc: {
'ja-JP': 'Renoteされたローカルの投稿を含めるかどうか'
}
}),
withFiles: $.bool.optional.note({
default: false,
desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します'
}
}),
mediaOnly: $.bool.optional.note({
default: false,
desc: {
'ja-JP': 'true にすると、ファイルが添付された投稿だけ取得します (このパラメータは廃止予定です。代わりに withFiles を使ってください。)'
}
}),
}
};
/**
* Get notes of a user
*/
export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => { export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => {
// Get 'userId' parameter const [ps, psErr] = getParams(meta, params);
const [userId, userIdErr] = $.type(ID).optional.get(params.userId); if (psErr) throw psErr;
if (userIdErr) return rej('invalid userId param');
// Get 'username' parameter if (ps.userId === undefined && ps.username === undefined) {
const [username, usernameErr] = $.str.optional.get(params.username);
if (usernameErr) return rej('invalid username param');
if (userId === undefined && username === undefined) {
return rej('userId or username is required'); return rej('userId or username is required');
} }
// Get 'host' parameter
const [host, hostErr] = $.str.optional.get(params.host);
if (hostErr) return rej('invalid host param');
// Get 'includeReplies' parameter
const [includeReplies = true, includeRepliesErr] = $.bool.optional.get(params.includeReplies);
if (includeRepliesErr) return rej('invalid includeReplies param');
// Get 'withFiles' parameter
const [withFiles = false, withFilesErr] = $.bool.optional.get(params.withFiles);
if (withFilesErr) return rej('invalid withFiles param');
// Get 'limit' parameter
const [limit = 10, limitErr] = $.num.optional.range(1, 100).get(params.limit);
if (limitErr) return rej('invalid limit param');
// Get 'sinceId' parameter
const [sinceId, sinceIdErr] = $.type(ID).optional.get(params.sinceId);
if (sinceIdErr) return rej('invalid sinceId param');
// Get 'untilId' parameter
const [untilId, untilIdErr] = $.type(ID).optional.get(params.untilId);
if (untilIdErr) return rej('invalid untilId param');
// Get 'sinceDate' parameter
const [sinceDate, sinceDateErr] = $.num.optional.get(params.sinceDate);
if (sinceDateErr) throw 'invalid sinceDate param';
// Get 'untilDate' parameter
const [untilDate, untilDateErr] = $.num.optional.get(params.untilDate);
if (untilDateErr) throw 'invalid untilDate param';
// Check if only one of sinceId, untilId, sinceDate, untilDate specified // Check if only one of sinceId, untilId, sinceDate, untilDate specified
if ([sinceId, untilId, sinceDate, untilDate].filter(x => x != null).length > 1) { if ([ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate].filter(x => x != null).length > 1) {
throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified'; throw 'only one of sinceId, untilId, sinceDate, untilDate can be specified';
} }
const q = userId !== undefined const q = ps.userId !== undefined
? { _id: userId } ? { _id: ps.userId }
: { usernameLower: username.toLowerCase(), host: getHostLower(host) } ; : { usernameLower: ps.username.toLowerCase(), host: getHostLower(ps.host) } ;
// Lookup user // Lookup user
const user = await User.findOne(q, { const user = await User.findOne(q, {
@ -80,30 +138,32 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
userId: user._id userId: user._id
} as any; } as any;
if (sinceId) { if (ps.sinceId) {
sort._id = 1; sort._id = 1;
query._id = { query._id = {
$gt: sinceId $gt: ps.sinceId
}; };
} else if (untilId) { } else if (ps.untilId) {
query._id = { query._id = {
$lt: untilId $lt: ps.untilId
}; };
} else if (sinceDate) { } else if (ps.sinceDate) {
sort._id = 1; sort._id = 1;
query.createdAt = { query.createdAt = {
$gt: new Date(sinceDate) $gt: new Date(ps.sinceDate)
}; };
} else if (untilDate) { } else if (ps.untilDate) {
query.createdAt = { query.createdAt = {
$lt: new Date(untilDate) $lt: new Date(ps.untilDate)
}; };
} }
if (!includeReplies) { if (!ps.includeReplies) {
query.replyId = null; query.replyId = null;
} }
const withFiles = ps.withFiles != null ? ps.withFiles : ps.mediaOnly;
if (withFiles) { if (withFiles) {
query.fileIds = { query.fileIds = {
$exists: true, $exists: true,
@ -115,12 +175,10 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) =>
// Issue query // Issue query
const notes = await Note const notes = await Note
.find(query, { .find(query, {
limit: limit, limit: ps.limit,
sort: sort sort: sort
}); });
// Serialize // Serialize
res(await Promise.all(notes.map(async (note) => res(await Promise.all(notes.map(note => pack(note, me))));
await pack(note, me)
)));
}); });