diff --git a/src/remote/activitypub/kernel/like.ts b/src/remote/activitypub/kernel/like.ts index fa8983b38..b25f80aed 100644 --- a/src/remote/activitypub/kernel/like.ts +++ b/src/remote/activitypub/kernel/like.ts @@ -1,23 +1,16 @@ import { IRemoteUser } from '../../../models/entities/user'; -import { ILike } from '../type'; +import { ILike, getApId } from '../type'; import create from '../../../services/note/reaction/create'; -import { Notes } from '../../../models'; -import { apLogger } from '../logger'; +import { fetchNote } from '../models/note'; export default async (actor: IRemoteUser, activity: ILike) => { - const id = typeof activity.object == 'string' ? activity.object : activity.object.id; - if (id == null) throw new Error('missing id'); + const targetUri = getApId(activity.object); - // Transform: - // https://misskey.ex/notes/xxxx to - // xxxx - const noteId = id.split('/').pop(); + const note = await fetchNote(targetUri); + if (!note) return `skip: target note not found ${targetUri}`; - const note = await Notes.findOne(noteId); - if (note == null) { - apLogger.warn(`Like activity recivied, but no such note: ${id}`, { id }); - return; - } + if (actor.id === note.userId) return `skip: cannot react to my note`; await create(actor, note, activity._misskey_reaction || activity.content || activity.name); + return `ok`; }; diff --git a/src/remote/activitypub/kernel/undo/like.ts b/src/remote/activitypub/kernel/undo/like.ts index 2678828a9..bd6930c66 100644 --- a/src/remote/activitypub/kernel/undo/like.ts +++ b/src/remote/activitypub/kernel/undo/like.ts @@ -1,21 +1,17 @@ import { IRemoteUser } from '../../../../models/entities/user'; -import { ILike } from '../../type'; +import { ILike, getApId } from '../../type'; import deleteReaction from '../../../../services/note/reaction/delete'; -import { Notes } from '../../../../models'; +import { fetchNote } from '../../models/note'; /** * Process Undo.Like activity */ -export default async (actor: IRemoteUser, activity: ILike): Promise => { - const id = typeof activity.object == 'string' ? activity.object : activity.object.id; - if (id == null) throw new Error('missing id'); +export default async (actor: IRemoteUser, activity: ILike) => { + const targetUri = getApId(activity.object); - const noteId = id.split('/').pop(); - - const note = await Notes.findOne(noteId); - if (note == null) { - throw new Error('note not found'); - } + const note = await fetchNote(targetUri); + if (!note) return `skip: target note not found ${targetUri}`; await deleteReaction(actor, note); + return `ok`; }; diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts index a09fbd9c2..aee31813a 100644 --- a/src/services/note/reaction/create.ts +++ b/src/services/note/reaction/create.ts @@ -1,19 +1,18 @@ import { publishNoteStream } from '../../stream'; import watch from '../watch'; import renderLike from '../../../remote/activitypub/renderer/like'; -import { deliver } from '../../../queue'; +import DeliverManager from '../../../remote/activitypub/deliver-manager'; import { renderActivity } from '../../../remote/activitypub/renderer'; import { IdentifiableError } from '../../../misc/identifiable-error'; import { toDbReaction } from '../../../misc/reaction-lib'; -import { User } from '../../../models/entities/user'; +import { User, IRemoteUser } from '../../../models/entities/user'; import { Note } from '../../../models/entities/note'; import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles } from '../../../models'; import { Not } from 'typeorm'; import { perUserReactionsChart } from '../../chart'; import { genId } from '../../../misc/gen-id'; -import { NoteReaction } from '../../../models/entities/note-reaction'; import { createNotification } from '../../create-notification'; -import { isDuplicateKeyValueError } from '../../../misc/is-duplicate-key-value-error'; +import deleteReaction from './delete'; export default async (user: User, note: Note, reaction?: string) => { // Myself @@ -23,6 +22,21 @@ export default async (user: User, note: Note, reaction?: string) => { reaction = await toDbReaction(reaction); + const exist = await NoteReactions.findOne({ + noteId: note.id, + userId: user.id, + }); + + if (exist) { + if (exist.reaction !== reaction) { + // 別のリアクションがすでにされていたら置き換える + await deleteReaction(user, note); + } else { + // 同じリアクションがすでにされていたら何もしない + return; + } + } + // Create reaction await NoteReactions.save({ id: genId(), @@ -30,13 +44,6 @@ export default async (user: User, note: Note, reaction?: string) => { noteId: note.id, userId: user.id, reaction - } as NoteReaction).catch(e => { - // duplicate key error - if (isDuplicateKeyValueError(e)) { - throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298', 'already reacted'); - } - - throw e; }); // Increment reactions count @@ -86,12 +93,15 @@ export default async (user: User, note: Note, reaction?: string) => { } //#region 配信 - // リアクターがローカルユーザーかつリアクション対象がリモートユーザーの投稿なら配送 - if (Users.isLocalUser(user) && note.userHost !== null) { + if (Users.isLocalUser(user) && !note.localOnly) { const content = renderActivity(renderLike(user, note, reaction)); - Users.findOne(note.userId).then(u => { - deliver(user, content, u!.inbox); - }); + const dm = new DeliverManager(user, content); + if (note.userHost !== null) { + const reactee = await Users.findOne(note.userId) + dm.addDirectRecipe(reactee as IRemoteUser); + } + dm.addFollowersRecipe(); + dm.execute(); } //#endregion }; diff --git a/src/services/note/reaction/delete.ts b/src/services/note/reaction/delete.ts index 6e9611ca5..533c3b406 100644 --- a/src/services/note/reaction/delete.ts +++ b/src/services/note/reaction/delete.ts @@ -2,9 +2,9 @@ import { publishNoteStream } from '../../stream'; import renderLike from '../../../remote/activitypub/renderer/like'; import renderUndo from '../../../remote/activitypub/renderer/undo'; import { renderActivity } from '../../../remote/activitypub/renderer'; -import { deliver } from '../../../queue'; +import DeliverManager from '../../../remote/activitypub/deliver-manager'; import { IdentifiableError } from '../../../misc/identifiable-error'; -import { User } from '../../../models/entities/user'; +import { User, IRemoteUser } from '../../../models/entities/user'; import { Note } from '../../../models/entities/note'; import { NoteReactions, Users, Notes } from '../../../models'; @@ -39,12 +39,15 @@ export default async (user: User, note: Note) => { }); //#region 配信 - // リアクターがローカルユーザーかつリアクション対象がリモートユーザーの投稿なら配送 - if (Users.isLocalUser(user) && (note.userHost !== null)) { + if (Users.isLocalUser(user) && !note.localOnly) { const content = renderActivity(renderUndo(renderLike(user, note, exist.reaction), user)); - Users.findOne(note.userId).then(u => { - deliver(user, content, u!.inbox); - }); + const dm = new DeliverManager(user, content); + if (note.userHost !== null) { + const reactee = await Users.findOne(note.userId) + dm.addDirectRecipe(reactee as IRemoteUser); + } + dm.addFollowersRecipe(); + dm.execute(); } //#endregion };