Merge pull request 'backend: Fix various lints in services/note' (#206) from backend-services-note into main
Reviewed-on: FoundKeyGang/FoundKey#206
This commit is contained in:
commit
c36cca30cb
21 changed files with 113 additions and 117 deletions
|
@ -24,7 +24,7 @@ export type Source = {
|
||||||
db?: number;
|
db?: number;
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
};
|
};
|
||||||
elasticsearch: {
|
elasticsearch?: {
|
||||||
host: string;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
ssl?: boolean;
|
ssl?: boolean;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||||
import create from '@/services/note/reaction/create.js';
|
import { createReaction } from '@/services/note/reaction/create.js';
|
||||||
import { ILike, getApId } from '../type.js';
|
import { ILike, getApId } from '../type.js';
|
||||||
import { fetchNote, extractEmojis } from '../models/note.js';
|
import { fetchNote, extractEmojis } from '../models/note.js';
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export default async (actor: CacheableRemoteUser, activity: ILike) => {
|
||||||
|
|
||||||
await extractEmojis(activity.tag || [], actor.host).catch(() => null);
|
await extractEmojis(activity.tag || [], actor.host).catch(() => null);
|
||||||
|
|
||||||
return await create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => {
|
return await createReaction(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => {
|
||||||
if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') {
|
if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') {
|
||||||
return 'skip: already reacted';
|
return 'skip: already reacted';
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||||
import deleteReaction from '@/services/note/reaction/delete.js';
|
import { deleteReaction } from '@/services/note/reaction/delete.js';
|
||||||
import { ILike, getApId } from '@/remote/activitypub/type.js';
|
import { ILike, getApId } from '@/remote/activitypub/type.js';
|
||||||
import { fetchNote } from '@/remote/activitypub/models/note.js';
|
import { fetchNote } from '@/remote/activitypub/models/note.js';
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import config from '@/config/index.js';
|
||||||
import post from '@/services/note/create.js';
|
import post from '@/services/note/create.js';
|
||||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||||
import { unique, toArray, toSingle } from '@/prelude/array.js';
|
import { unique, toArray, toSingle } from '@/prelude/array.js';
|
||||||
import vote from '@/services/note/polls/vote.js';
|
import { vote } from '@/services/note/polls/vote.js';
|
||||||
import { DriveFile } from '@/models/entities/drive-file.js';
|
import { DriveFile } from '@/models/entities/drive-file.js';
|
||||||
import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
|
import { deliverQuestionUpdate } from '@/services/note/polls/update.js';
|
||||||
import { extractDbHost, toPuny } from '@/misc/convert-host.js';
|
import { extractDbHost, toPuny } from '@/misc/convert-host.js';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import readNote from '@/services/note/read.js';
|
import { readNote } from '@/services/note/read.js';
|
||||||
import { Antennas, Notes, AntennaNotes } from '@/models/index.js';
|
import { Antennas, Notes, AntennaNotes } from '@/models/index.js';
|
||||||
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
||||||
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
|
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Brackets } from 'typeorm';
|
import { Brackets } from 'typeorm';
|
||||||
import { notificationTypes } from 'foundkey-js';
|
import { notificationTypes } from 'foundkey-js';
|
||||||
import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/models/index.js';
|
import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/models/index.js';
|
||||||
import read from '@/services/note/read.js';
|
import { readNote } from '@/services/note/read.js';
|
||||||
import { readNotification } from '../../common/read-notification.js';
|
import { readNotification } from '../../common/read-notification.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
import { makePaginationQuery } from '../../common/make-pagination-query.js';
|
||||||
|
@ -137,7 +137,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!);
|
const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!);
|
||||||
|
|
||||||
if (notes.length > 0) {
|
if (notes.length > 0) {
|
||||||
read(user.id, notes);
|
readNote(user.id, notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Notifications.packMany(notifications, user.id);
|
return await Notifications.packMany(notifications, user.id);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Brackets } from 'typeorm';
|
import { Brackets } from 'typeorm';
|
||||||
import { noteVisibilities } from 'foundkey-js';
|
import { noteVisibilities } from 'foundkey-js';
|
||||||
import read from '@/services/note/read.js';
|
import { readNote } from '@/services/note/read.js';
|
||||||
import { Notes, Followings } from '@/models/index.js';
|
import { Notes, Followings } from '@/models/index.js';
|
||||||
import define from '../../define.js';
|
import define from '../../define.js';
|
||||||
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
|
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
|
||||||
|
@ -79,7 +79,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
|
|
||||||
const mentions = await query.take(ps.limit).getMany();
|
const mentions = await query.take(ps.limit).getMany();
|
||||||
|
|
||||||
read(user.id, mentions);
|
readNote(user.id, mentions);
|
||||||
|
|
||||||
return await Notes.packMany(mentions, user);
|
return await Notes.packMany(mentions, user);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import createReaction from '@/services/note/reaction/create.js';
|
import { createReaction } from '@/services/note/reaction/create.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { getNote } from '../../../common/getters.js';
|
import { getNote } from '../../../common/getters.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import deleteReaction from '@/services/note/reaction/delete.js';
|
import { deleteReaction } from '@/services/note/reaction/delete.js';
|
||||||
import { SECOND, HOUR } from '@/const.js';
|
import { SECOND, HOUR } from '@/const.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { getNote } from '../../../common/getters.js';
|
import { getNote } from '../../../common/getters.js';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { noteNotificationTypes } from 'foundkey-js';
|
import { noteNotificationTypes } from 'foundkey-js';
|
||||||
import { Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
import { Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import readNote from '@/services/note/read.js';
|
import { readNote } from '@/services/note/read.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { getNote } from '../../../common/getters.js';
|
import { getNote } from '../../../common/getters.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import watch from '@/services/note/watch.js';
|
import { watch } from '@/services/note/watch.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { getNote } from '../../../common/getters.js';
|
import { getNote } from '../../../common/getters.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import unwatch from '@/services/note/unwatch.js';
|
import { unwatch } from '@/services/note/unwatch.js';
|
||||||
import define from '../../../define.js';
|
import define from '../../../define.js';
|
||||||
import { getNote } from '../../../common/getters.js';
|
import { getNote } from '../../../common/getters.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as websocket from 'websocket';
|
import * as websocket from 'websocket';
|
||||||
import readNote from '@/services/note/read.js';
|
import { readNote } from '@/services/note/read.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { Channel as ChannelModel } from '@/models/entities/channel.js';
|
import { Channel as ChannelModel } from '@/models/entities/channel.js';
|
||||||
import { Followings, Mutings, RenoteMutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js';
|
import { Followings, Mutings, RenoteMutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js';
|
||||||
|
|
|
@ -35,6 +35,7 @@ import { webhookDeliver } from '@/queue/index.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { UserProfile } from '@/models/entities/user-profile.js';
|
import { UserProfile } from '@/models/entities/user-profile.js';
|
||||||
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
|
import { getActiveWebhooks } from '@/misc/webhook-cache.js';
|
||||||
|
import { IActivity } from '@/remote/activitypub/type.js';
|
||||||
import { updateHashtags } from '../update-hashtag.js';
|
import { updateHashtags } from '../update-hashtag.js';
|
||||||
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
|
import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js';
|
||||||
import { createNotification } from '../create-notification.js';
|
import { createNotification } from '../create-notification.js';
|
||||||
|
@ -59,14 +60,14 @@ class NotificationManager {
|
||||||
this.queue = [];
|
this.queue = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(notifiee: ILocalUser['id'], reason: NotificationType) {
|
public push(notifiee: ILocalUser['id'], reason: NotificationType): void {
|
||||||
// 自分自身へは通知しない
|
// No notification to yourself.
|
||||||
if (this.notifier.id === notifiee) return;
|
if (this.notifier.id === notifiee) return;
|
||||||
|
|
||||||
const exist = this.queue.find(x => x.target === notifiee);
|
const exist = this.queue.find(x => x.target === notifiee);
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
|
// If you have been "mentioned and replied to," make the notification as a reply, not as a mention.
|
||||||
if (reason !== 'mention') {
|
if (reason !== 'mention') {
|
||||||
exist.reason = reason;
|
exist.reason = reason;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ class NotificationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deliver() {
|
public async deliver(): Promise<void> {
|
||||||
for (const x of this.queue) {
|
for (const x of this.queue) {
|
||||||
// check if the sender or thread are muted
|
// check if the sender or thread are muted
|
||||||
const userMuted = await Mutings.findOneBy({
|
const userMuted = await Mutings.findOneBy({
|
||||||
|
@ -119,7 +120,7 @@ type Option = {
|
||||||
poll?: IPoll | null;
|
poll?: IPoll | null;
|
||||||
localOnly?: boolean | null;
|
localOnly?: boolean | null;
|
||||||
cw?: string | null;
|
cw?: string | null;
|
||||||
visibility?: string;
|
visibility?: 'home' | 'public' | 'followers' | 'specified';
|
||||||
visibleUsers?: MinimumUser[] | null;
|
visibleUsers?: MinimumUser[] | null;
|
||||||
channel?: Channel | null;
|
channel?: Channel | null;
|
||||||
apMentions?: MinimumUser[] | null;
|
apMentions?: MinimumUser[] | null;
|
||||||
|
@ -130,9 +131,9 @@ type Option = {
|
||||||
app?: App | null;
|
app?: App | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false) => new Promise<Note>(async (res, rej) => {
|
export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false): Promise<Note> => new Promise<Note>(async (res, rej) => {
|
||||||
// チャンネル外にリプライしたら対象のスコープに合わせる
|
// If you reply outside the channel, adjust to the scope of the target
|
||||||
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
|
// (I think this could be done client-side, but server-side for now)
|
||||||
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
|
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
|
||||||
if (data.reply.channelId) {
|
if (data.reply.channelId) {
|
||||||
data.channel = await Channels.findOneBy({ id: data.reply.channelId });
|
data.channel = await Channels.findOneBy({ id: data.reply.channelId });
|
||||||
|
@ -141,9 +142,9 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// チャンネル内にリプライしたら対象のスコープに合わせる
|
// When you reply to a channel, adjust the scope to that of the target.
|
||||||
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
|
// (I think this could be done client-side, but server-side for now)
|
||||||
if (data.reply && (data.channel == null) && data.reply.channelId) {
|
if (data.reply?.channelId && (data.channel == null)) {
|
||||||
data.channel = await Channels.findOneBy({ id: data.reply.channelId });
|
data.channel = await Channels.findOneBy({ id: data.reply.channelId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,32 +155,32 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
if (data.channel != null) data.visibleUsers = [];
|
if (data.channel != null) data.visibleUsers = [];
|
||||||
if (data.channel != null) data.localOnly = true;
|
if (data.channel != null) data.localOnly = true;
|
||||||
|
|
||||||
// サイレンス
|
// silence
|
||||||
if (user.isSilenced && data.visibility === 'public' && data.channel == null) {
|
if (user.isSilenced && data.visibility === 'public' && data.channel == null) {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renote対象が「ホームまたは全体」以外の公開範囲ならreject
|
// Reject if the target of the renote is not Home or Public.
|
||||||
if (data.renote && data.renote.visibility !== 'public' && data.renote.visibility !== 'home' && data.renote.userId !== user.id) {
|
if (data.renote && data.renote.visibility !== 'public' && data.renote.visibility !== 'home' && data.renote.userId !== user.id) {
|
||||||
return rej('Renote target is not public or home');
|
return rej('Renote target is not public or home');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renote対象がpublicではないならhomeにする
|
// If the target of the renote is not public, make it home.
|
||||||
if (data.renote && data.renote.visibility !== 'public' && data.visibility === 'public') {
|
if (data.renote && data.renote.visibility !== 'public' && data.visibility === 'public') {
|
||||||
data.visibility = 'home';
|
data.visibility = 'home';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renote対象がfollowersならfollowersにする
|
// If the target of Renote is followers, make it followers.
|
||||||
if (data.renote && data.renote.visibility === 'followers') {
|
if (data.renote && data.renote.visibility === 'followers') {
|
||||||
data.visibility = 'followers';
|
data.visibility = 'followers';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ローカルのみをRenoteしたらローカルのみにする
|
// Ff the original note is local-only, make the renote also local-only.
|
||||||
if (data.renote && data.renote.localOnly && data.channel == null) {
|
if (data.renote && data.renote.localOnly && data.channel == null) {
|
||||||
data.localOnly = true;
|
data.localOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ローカルのみにリプライしたらローカルのみにする
|
// If you reply to local only, make it local only.
|
||||||
if (data.reply && data.reply.localOnly && data.channel == null) {
|
if (data.reply && data.reply.localOnly && data.channel == null) {
|
||||||
data.localOnly = true;
|
data.localOnly = true;
|
||||||
}
|
}
|
||||||
|
@ -196,10 +197,10 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
// Parse MFM if needed
|
// Parse MFM if needed
|
||||||
if (!tags || !emojis || !mentionedUsers) {
|
if (!tags || !emojis || !mentionedUsers) {
|
||||||
const tokens = data.text ? mfm.parse(data.text)! : [];
|
const tokens = data.text ? mfm.parse(data.text) : [];
|
||||||
const cwTokens = data.cw ? mfm.parse(data.cw)! : [];
|
const cwTokens = data.cw ? mfm.parse(data.cw) : [];
|
||||||
const choiceTokens = data.poll && data.poll.choices
|
const choiceTokens = data.poll?.choices
|
||||||
? concat(data.poll.choices.map(choice => mfm.parse(choice)!))
|
? concat(data.poll.choices.map(choice => mfm.parse(choice)))
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens);
|
const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens);
|
||||||
|
@ -213,8 +214,8 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32);
|
tags = tags.filter(tag => Array.from(tag || '').length <= 128).splice(0, 32);
|
||||||
|
|
||||||
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) {
|
if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply?.userId)) {
|
||||||
mentionedUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId }));
|
mentionedUsers.push(await Users.findOneByOrFail({ id: data.reply.userId }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.visibility === 'specified') {
|
if (data.visibility === 'specified') {
|
||||||
|
@ -226,8 +227,8 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) {
|
if (data.reply && !data.visibleUsers.some(x => x.id === data.reply?.userId)) {
|
||||||
data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId }));
|
data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply.userId }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +236,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
|
|
||||||
res(note);
|
res(note);
|
||||||
|
|
||||||
// 統計を更新
|
// Update Statistics
|
||||||
notesChart.update(note, true);
|
notesChart.update(note, true);
|
||||||
perUserNotesChart.update(user, note, true);
|
perUserNotesChart.update(user, note, true);
|
||||||
|
|
||||||
|
@ -247,7 +248,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ハッシュタグ更新
|
// Hashtag Update
|
||||||
if (data.visibility === 'public' || data.visibility === 'home') {
|
if (data.visibility === 'public' || data.visibility === 'home') {
|
||||||
updateHashtags(user, tags);
|
updateHashtags(user, tags);
|
||||||
}
|
}
|
||||||
|
@ -301,7 +302,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
saveReply(data.reply, note);
|
saveReply(data.reply, note);
|
||||||
}
|
}
|
||||||
|
|
||||||
// この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき
|
// When there is no re-note of the specified note by the specified user except for this post
|
||||||
if (data.renote && (await countSameRenotes(user.id, data.renote.id, note.id) === 0)) {
|
if (data.renote && (await countSameRenotes(user.id, data.renote.id, note.id) === 0)) {
|
||||||
incRenoteCount(data.renote);
|
incRenoteCount(data.renote);
|
||||||
}
|
}
|
||||||
|
@ -319,12 +320,12 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
if (Users.isLocalUser(user)) activeUsersChart.write(user);
|
if (Users.isLocalUser(user)) activeUsersChart.write(user);
|
||||||
|
|
||||||
// 未読通知を作成
|
// Create unread notifications
|
||||||
if (data.visibility === 'specified') {
|
if (data.visibility === 'specified') {
|
||||||
if (data.visibleUsers == null) throw new Error('invalid param');
|
if (data.visibleUsers == null) throw new Error('invalid param');
|
||||||
|
|
||||||
for (const u of data.visibleUsers) {
|
for (const u of data.visibleUsers) {
|
||||||
// ローカルユーザーのみ
|
// Local users only
|
||||||
if (!Users.isLocalUser(u)) continue;
|
if (!Users.isLocalUser(u)) continue;
|
||||||
|
|
||||||
insertNoteUnread(u.id, note, {
|
insertNoteUnread(u.id, note, {
|
||||||
|
@ -334,7 +335,7 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const u of mentionedUsers) {
|
for (const u of mentionedUsers) {
|
||||||
// ローカルユーザーのみ
|
// Local users only
|
||||||
if (!Users.isLocalUser(u)) continue;
|
if (!Users.isLocalUser(u)) continue;
|
||||||
|
|
||||||
insertNoteUnread(u.id, note, {
|
insertNoteUnread(u.id, note, {
|
||||||
|
@ -423,24 +424,24 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
const noteActivity = await renderNoteOrRenoteActivity(data, note);
|
const noteActivity = await renderNoteOrRenoteActivity(data, note);
|
||||||
const dm = new DeliverManager(user, noteActivity);
|
const dm = new DeliverManager(user, noteActivity);
|
||||||
|
|
||||||
// メンションされたリモートユーザーに配送
|
// Delivered to remote users who have been mentioned
|
||||||
for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) {
|
for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) {
|
||||||
dm.addDirectRecipe(u as IRemoteUser);
|
dm.addDirectRecipe(u as IRemoteUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
|
// If the post is a reply and the poster is a local user and the poster of the post to which you are replying is a remote user, deliver
|
||||||
if (data.reply && data.reply.userHost !== null) {
|
if (data.reply && data.reply.userHost !== null) {
|
||||||
const u = await Users.findOneBy({ id: data.reply.userId });
|
const u = await Users.findOneBy({ id: data.reply.userId });
|
||||||
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
|
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送
|
// If the post is a Renote and the poster is a local user and the poster of the original Renote post is a remote user, deliver
|
||||||
if (data.renote && data.renote.userHost !== null) {
|
if (data.renote && data.renote.userHost !== null) {
|
||||||
const u = await Users.findOneBy({ id: data.renote.userId });
|
const u = await Users.findOneBy({ id: data.renote.userId });
|
||||||
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
|
if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// フォロワーに配送
|
// Deliver to followers
|
||||||
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
if (['public', 'home', 'followers'].includes(note.visibility)) {
|
||||||
dm.addFollowersRecipe();
|
dm.addFollowersRecipe();
|
||||||
}
|
}
|
||||||
|
@ -461,23 +462,23 @@ export default async (user: { id: User['id']; username: User['username']; host:
|
||||||
lastNotedAt: new Date(),
|
lastNotedAt: new Date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Notes.countBy({
|
const count = await Notes.countBy({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
channelId: data.channel.id,
|
channelId: data.channel.id,
|
||||||
}).then(count => {
|
|
||||||
// この処理が行われるのはノート作成後なので、ノートが一つしかなかったら最初の投稿だと判断できる
|
|
||||||
// TODO: とはいえノートを削除して何回も投稿すればその分だけインクリメントされる雑さもあるのでどうにかしたい
|
|
||||||
if (count === 1) {
|
|
||||||
Channels.increment({ id: data.channel!.id }, 'usersCount', 1);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// This process takes place after the note is created, so if there is only one note, you can determine that it is the first submission.
|
||||||
|
// TODO: but there's also the messiness of deleting a note and posting it multiple times, which is incremented by the number of times it's posted, so I'd like to do something about that.
|
||||||
|
if (count === 1) {
|
||||||
|
Channels.increment({ id: data.channel.id }, 'usersCount', 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register to search database
|
// Register to search database
|
||||||
index(note);
|
index(note);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function renderNoteOrRenoteActivity(data: Option, note: Note) {
|
async function renderNoteOrRenoteActivity(data: Option, note: Note): Promise<IActivity | null> {
|
||||||
if (data.localOnly) return null;
|
if (data.localOnly) return null;
|
||||||
|
|
||||||
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
|
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
|
||||||
|
@ -487,7 +488,7 @@ async function renderNoteOrRenoteActivity(data: Option, note: Note) {
|
||||||
return renderActivity(content);
|
return renderActivity(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
function incRenoteCount(renote: Note) {
|
function incRenoteCount(renote: Note): void {
|
||||||
Notes.createQueryBuilder().update()
|
Notes.createQueryBuilder().update()
|
||||||
.set({
|
.set({
|
||||||
renoteCount: () => '"renoteCount" + 1',
|
renoteCount: () => '"renoteCount" + 1',
|
||||||
|
@ -497,19 +498,17 @@ function incRenoteCount(renote: Note) {
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
|
async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]): Promise<Note> {
|
||||||
|
const createdAt = data.createdAt ?? new Date();
|
||||||
|
|
||||||
const insert = new Note({
|
const insert = new Note({
|
||||||
id: genId(data.createdAt!),
|
id: genId(createdAt),
|
||||||
createdAt: data.createdAt!,
|
createdAt,
|
||||||
fileIds: data.files ? data.files.map(file => file.id) : [],
|
fileIds: data.files?.map(file => file.id) ?? [],
|
||||||
replyId: data.reply ? data.reply.id : null,
|
replyId: data.reply?.id ?? null,
|
||||||
renoteId: data.renote ? data.renote.id : null,
|
renoteId: data.renote?.id ?? null,
|
||||||
channelId: data.channel ? data.channel.id : null,
|
channelId: data.channel?.id ?? null,
|
||||||
threadId: data.reply
|
threadId: data.reply?.threadId ?? data.reply?.id ?? null,
|
||||||
? data.reply.threadId
|
|
||||||
? data.reply.threadId
|
|
||||||
: data.reply.id
|
|
||||||
: null,
|
|
||||||
name: data.name,
|
name: data.name,
|
||||||
text: data.text,
|
text: data.text,
|
||||||
hasPoll: data.poll != null,
|
hasPoll: data.poll != null,
|
||||||
|
@ -517,15 +516,13 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O
|
||||||
tags: tags.map(tag => normalizeForSearch(tag)),
|
tags: tags.map(tag => normalizeForSearch(tag)),
|
||||||
emojis,
|
emojis,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
localOnly: data.localOnly!,
|
localOnly: data.localOnly ?? false,
|
||||||
visibility: data.visibility as any,
|
visibility: data.visibility,
|
||||||
visibleUserIds: data.visibility === 'specified'
|
visibleUserIds: data.visibility === 'specified'
|
||||||
? data.visibleUsers
|
? data.visibleUsers?.map(u => u.id) ?? []
|
||||||
? data.visibleUsers.map(u => u.id)
|
|
||||||
: []
|
|
||||||
: [],
|
: [],
|
||||||
|
|
||||||
attachedFileTypes: data.files ? data.files.map(file => file.type) : [],
|
attachedFileTypes: data.files?.map(file => file.type) ?? [],
|
||||||
|
|
||||||
// denormalized data below
|
// denormalized data below
|
||||||
replyUserId: data.reply?.userId,
|
replyUserId: data.reply?.userId,
|
||||||
|
@ -543,29 +540,26 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O
|
||||||
insert.mentions = mentionedUsers.map(u => u.id);
|
insert.mentions = mentionedUsers.map(u => u.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 投稿を作成
|
// Create a post
|
||||||
try {
|
try {
|
||||||
if (insert.hasPoll) {
|
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await db.transaction(async transactionalEntityManager => {
|
await db.transaction(async transactionalEntityManager => {
|
||||||
await transactionalEntityManager.insert(Note, insert);
|
await transactionalEntityManager.insert(Note, insert);
|
||||||
|
|
||||||
|
if (data.poll != null) {
|
||||||
const poll = new Poll({
|
const poll = new Poll({
|
||||||
noteId: insert.id,
|
noteId: insert.id,
|
||||||
choices: data.poll!.choices,
|
choices: data.poll.choices,
|
||||||
expiresAt: data.poll!.expiresAt,
|
expiresAt: data.poll.expiresAt,
|
||||||
multiple: data.poll!.multiple,
|
multiple: data.poll.multiple,
|
||||||
votes: new Array(data.poll!.choices.length).fill(0),
|
votes: new Array(data.poll.choices.length).fill(0),
|
||||||
noteVisibility: insert.visibility,
|
noteVisibility: insert.visibility,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
userHost: user.host,
|
userHost: user.host,
|
||||||
});
|
});
|
||||||
|
|
||||||
await transactionalEntityManager.insert(Poll, poll);
|
await transactionalEntityManager.insert(Poll, poll);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await Notes.insert(insert);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return insert;
|
return insert;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -582,10 +576,10 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function index(note: Note) {
|
function index(note: Note): void {
|
||||||
if (note.text == null || config.elasticsearch == null) return;
|
if (note.text == null || config.elasticsearch == null) return;
|
||||||
|
|
||||||
es!.index({
|
es.index({
|
||||||
index: config.elasticsearch.index || 'misskey_note',
|
index: config.elasticsearch.index || 'misskey_note',
|
||||||
id: note.id.toString(),
|
id: note.id.toString(),
|
||||||
body: {
|
body: {
|
||||||
|
@ -596,7 +590,7 @@ function index(note: Note) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType) {
|
async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType): Promise<void> {
|
||||||
const watchers = await NoteWatchings.findBy({
|
const watchers = await NoteWatchings.findBy({
|
||||||
noteId: renote.id,
|
noteId: renote.id,
|
||||||
userId: Not(user.id),
|
userId: Not(user.id),
|
||||||
|
@ -607,7 +601,7 @@ async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager) {
|
async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager): Promise<void> {
|
||||||
const watchers = await NoteWatchings.findBy({
|
const watchers = await NoteWatchings.findBy({
|
||||||
noteId: reply.id,
|
noteId: reply.id,
|
||||||
userId: Not(user.id),
|
userId: Not(user.id),
|
||||||
|
@ -618,7 +612,7 @@ async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) {
|
async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager): Promise<void> {
|
||||||
for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) {
|
for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) {
|
||||||
const threadMuted = await NoteThreadMutings.findOneBy({
|
const threadMuted = await NoteThreadMutings.findOneBy({
|
||||||
userId: u.id,
|
userId: u.id,
|
||||||
|
@ -653,11 +647,11 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveReply(reply: Note, note: Note) {
|
function saveReply(reply: Note, note: Note): void {
|
||||||
Notes.increment({ id: reply.id }, 'repliesCount', 1);
|
Notes.increment({ id: reply.id }, 'repliesCount', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function incNotesCountOfUser(user: { id: User['id']; }) {
|
function incNotesCountOfUser(user: { id: User['id']; }): void {
|
||||||
Users.createQueryBuilder().update()
|
Users.createQueryBuilder().update()
|
||||||
.set({
|
.set({
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
@ -668,7 +662,7 @@ function incNotesCountOfUser(user: { id: User['id']; }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> {
|
async function extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> {
|
||||||
if (tokens == null) return [];
|
if (tokens.length === 0) return [];
|
||||||
|
|
||||||
const mentions = extractMentions(tokens);
|
const mentions = extractMentions(tokens);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Brackets, In, IsNull, Not } from 'typeorm';
|
import { Brackets, FindOptionsWhere, In, IsNull, Not } from 'typeorm';
|
||||||
import { publishNoteStream } from '@/services/stream.js';
|
import { publishNoteStream } from '@/services/stream.js';
|
||||||
import renderDelete from '@/remote/activitypub/renderer/delete.js';
|
import renderDelete from '@/remote/activitypub/renderer/delete.js';
|
||||||
import renderAnnounce from '@/remote/activitypub/renderer/announce.js';
|
import renderAnnounce from '@/remote/activitypub/renderer/announce.js';
|
||||||
|
@ -41,10 +41,12 @@ export default async function(user: { id: User['id']; uri: User['uri']; host: Us
|
||||||
if (Users.isLocalUser(user) && !note.localOnly) {
|
if (Users.isLocalUser(user) && !note.localOnly) {
|
||||||
let renote: Note | null = null;
|
let renote: Note | null = null;
|
||||||
|
|
||||||
// if deletd note is renote
|
// if deleted note is renote
|
||||||
if (isPureRenote(note)) {
|
if (isPureRenote(note)) {
|
||||||
renote = await Notes.findOneBy({
|
renote = await Notes.findOneBy({
|
||||||
id: note.renoteId,
|
// isPureRenote checks if note.renoteId is null already, so renoteId should be non-null.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
id: note.renoteId!,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +108,7 @@ async function findCascadingNotes(note: Note): Promise<Note[]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMentionedRemoteUsers(note: Note): Promise<IRemoteUser[]> {
|
async function getMentionedRemoteUsers(note: Note): Promise<IRemoteUser[]> {
|
||||||
const where = [] as any[];
|
const where: FindOptionsWhere<User>[] = [];
|
||||||
|
|
||||||
// mention / reply / dm
|
// mention / reply / dm
|
||||||
if (note.mentions.length > 0) {
|
if (note.mentions.length > 0) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { PollVotes, NoteWatchings, Polls, Blockings, NoteThreadMutings } from '@
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { createNotification } from '@/services/create-notification.js';
|
import { createNotification } from '@/services/create-notification.js';
|
||||||
|
|
||||||
export default async function(user: CacheableUser, note: Note, choice: number) {
|
export async function vote(user: CacheableUser, note: Note, choice: number): Promise<void> {
|
||||||
const poll = await Polls.findOneBy({ noteId: note.id });
|
const poll = await Polls.findOneBy({ noteId: note.id });
|
||||||
|
|
||||||
if (poll == null) throw new Error('poll not found');
|
if (poll == null) throw new Error('poll not found');
|
||||||
|
|
|
@ -13,9 +13,9 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js
|
||||||
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
import { NoteReaction } from '@/models/entities/note-reaction.js';
|
||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||||
import { createNotification } from '@/services/create-notification.js';
|
import { createNotification } from '@/services/create-notification.js';
|
||||||
import deleteReaction from './delete.js';
|
import { deleteReaction } from './delete.js';
|
||||||
|
|
||||||
export default async (user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) => {
|
export async function createReaction(user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string): Promise<void> {
|
||||||
// Check blocking
|
// Check blocking
|
||||||
if (note.userId !== user.id) {
|
if (note.userId !== user.id) {
|
||||||
const block = await Blockings.findOneBy({
|
const block = await Blockings.findOneBy({
|
||||||
|
@ -148,4 +148,4 @@ export default async (user: { id: User['id']; host: User['host']; }, note: Note,
|
||||||
dm.execute();
|
dm.execute();
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
};
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Note } from '@/models/entities/note.js';
|
||||||
import { NoteReactions, Users, Notes } from '@/models/index.js';
|
import { NoteReactions, Users, Notes } from '@/models/index.js';
|
||||||
import { decodeReaction } from '@/misc/reaction-lib.js';
|
import { decodeReaction } from '@/misc/reaction-lib.js';
|
||||||
|
|
||||||
export default async (user: { id: User['id']; host: User['host']; }, note: Note) => {
|
export async function deleteReaction(user: { id: User['id']; host: User['host']; }, note: Note): Promise<void> {
|
||||||
// if already unreacted
|
// if already unreacted
|
||||||
const exist = await NoteReactions.findOneBy({
|
const exist = await NoteReactions.findOneBy({
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
|
@ -55,4 +55,4 @@ export default async (user: { id: User['id']; host: User['host']; }, note: Note)
|
||||||
dm.execute();
|
dm.execute();
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
};
|
}
|
||||||
|
|
|
@ -12,14 +12,14 @@ import { Packed } from '@/misc/schema.js';
|
||||||
/**
|
/**
|
||||||
* Mark notes as read
|
* Mark notes as read
|
||||||
*/
|
*/
|
||||||
export default async function(
|
export async function readNote(
|
||||||
userId: User['id'],
|
userId: User['id'],
|
||||||
notes: (Note | Packed<'Note'>)[],
|
notes: (Note | Packed<'Note'>)[],
|
||||||
info?: {
|
info?: {
|
||||||
following: Set<User['id']>;
|
following: Set<User['id']>;
|
||||||
followingChannels: Set<Channel['id']>;
|
followingChannels: Set<Channel['id']>;
|
||||||
},
|
},
|
||||||
) {
|
): Promise<void> {
|
||||||
const following = info?.following ? info.following : new Set<string>((await Followings.find({
|
const following = info?.following ? info.following : new Set<string>((await Followings.find({
|
||||||
where: {
|
where: {
|
||||||
followerId: userId,
|
followerId: userId,
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { User } from '@/models/entities/user.js';
|
||||||
import { NoteWatchings } from '@/models/index.js';
|
import { NoteWatchings } from '@/models/index.js';
|
||||||
import { Note } from '@/models/entities/note.js';
|
import { Note } from '@/models/entities/note.js';
|
||||||
|
|
||||||
export default async (me: User['id'], note: Note) => {
|
export async function unwatch(me: User['id'], note: Note): Promise<void> {
|
||||||
await NoteWatchings.delete({
|
await NoteWatchings.delete({
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: me,
|
userId: me,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { NoteWatchings } from '@/models/index.js';
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { NoteWatching } from '@/models/entities/note-watching.js';
|
import { NoteWatching } from '@/models/entities/note-watching.js';
|
||||||
|
|
||||||
export default async (me: User['id'], note: Note) => {
|
export async function watch(me: User['id'], note: Note): Promise<void> {
|
||||||
// 自分の投稿はwatchできない
|
// User can't watch their own posts.
|
||||||
if (me === note.userId) {
|
if (me === note.userId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,4 @@ export default async (me: User['id'], note: Note) => {
|
||||||
userId: me,
|
userId: me,
|
||||||
noteUserId: note.userId,
|
noteUserId: note.userId,
|
||||||
} as NoteWatching);
|
} as NoteWatching);
|
||||||
};
|
}
|
||||||
|
|
Loading…
Reference in a new issue