リモート投稿にリモートでされたリアクションが表示されるように (#5817)

* 第3インスタンスへのLikeも受け入れるように

* リアクション済みだったらエラーにせずに置き換えるように

* Likeを第3インスタンスにdeliverするように

* fix

* fix

* 同じリアクションがすでにされていたら何もしない

* リモートから自身の投稿へリアクションした場合にエラーにならないように
This commit is contained in:
MeiMei 2020-02-04 08:26:00 +09:00 committed by GitHub
parent f640da911b
commit b7a4f286b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 48 deletions

View file

@ -1,23 +1,16 @@
import { IRemoteUser } from '../../../models/entities/user'; import { IRemoteUser } from '../../../models/entities/user';
import { ILike } from '../type'; import { ILike, getApId } from '../type';
import create from '../../../services/note/reaction/create'; import create from '../../../services/note/reaction/create';
import { Notes } from '../../../models'; import { fetchNote } from '../models/note';
import { apLogger } from '../logger';
export default async (actor: IRemoteUser, activity: ILike) => { export default async (actor: IRemoteUser, activity: ILike) => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id; const targetUri = getApId(activity.object);
if (id == null) throw new Error('missing id');
// Transform: const note = await fetchNote(targetUri);
// https://misskey.ex/notes/xxxx to if (!note) return `skip: target note not found ${targetUri}`;
// xxxx
const noteId = id.split('/').pop();
const note = await Notes.findOne(noteId); if (actor.id === note.userId) return `skip: cannot react to my note`;
if (note == null) {
apLogger.warn(`Like activity recivied, but no such note: ${id}`, { id });
return;
}
await create(actor, note, activity._misskey_reaction || activity.content || activity.name); await create(actor, note, activity._misskey_reaction || activity.content || activity.name);
return `ok`;
}; };

View file

@ -1,21 +1,17 @@
import { IRemoteUser } from '../../../../models/entities/user'; import { IRemoteUser } from '../../../../models/entities/user';
import { ILike } from '../../type'; import { ILike, getApId } from '../../type';
import deleteReaction from '../../../../services/note/reaction/delete'; import deleteReaction from '../../../../services/note/reaction/delete';
import { Notes } from '../../../../models'; import { fetchNote } from '../../models/note';
/** /**
* Process Undo.Like activity * Process Undo.Like activity
*/ */
export default async (actor: IRemoteUser, activity: ILike): Promise<void> => { export default async (actor: IRemoteUser, activity: ILike) => {
const id = typeof activity.object == 'string' ? activity.object : activity.object.id; const targetUri = getApId(activity.object);
if (id == null) throw new Error('missing id');
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) {
throw new Error('note not found');
}
await deleteReaction(actor, note); await deleteReaction(actor, note);
return `ok`;
}; };

View file

@ -1,19 +1,18 @@
import { publishNoteStream } from '../../stream'; import { publishNoteStream } from '../../stream';
import watch from '../watch'; import watch from '../watch';
import renderLike from '../../../remote/activitypub/renderer/like'; 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 { renderActivity } from '../../../remote/activitypub/renderer';
import { IdentifiableError } from '../../../misc/identifiable-error'; import { IdentifiableError } from '../../../misc/identifiable-error';
import { toDbReaction } from '../../../misc/reaction-lib'; 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 { Note } from '../../../models/entities/note';
import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles } from '../../../models'; import { NoteReactions, Users, NoteWatchings, Notes, UserProfiles } from '../../../models';
import { Not } from 'typeorm'; import { Not } from 'typeorm';
import { perUserReactionsChart } from '../../chart'; import { perUserReactionsChart } from '../../chart';
import { genId } from '../../../misc/gen-id'; import { genId } from '../../../misc/gen-id';
import { NoteReaction } from '../../../models/entities/note-reaction';
import { createNotification } from '../../create-notification'; 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) => { export default async (user: User, note: Note, reaction?: string) => {
// Myself // Myself
@ -23,6 +22,21 @@ export default async (user: User, note: Note, reaction?: string) => {
reaction = await toDbReaction(reaction); 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 // Create reaction
await NoteReactions.save({ await NoteReactions.save({
id: genId(), id: genId(),
@ -30,13 +44,6 @@ export default async (user: User, note: Note, reaction?: string) => {
noteId: note.id, noteId: note.id,
userId: user.id, userId: user.id,
reaction 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 // Increment reactions count
@ -86,12 +93,15 @@ export default async (user: User, note: Note, reaction?: string) => {
} }
//#region 配信 //#region 配信
// リアクターがローカルユーザーかつリアクション対象がリモートユーザーの投稿なら配送 if (Users.isLocalUser(user) && !note.localOnly) {
if (Users.isLocalUser(user) && note.userHost !== null) {
const content = renderActivity(renderLike(user, note, reaction)); const content = renderActivity(renderLike(user, note, reaction));
Users.findOne(note.userId).then(u => { const dm = new DeliverManager(user, content);
deliver(user, content, u!.inbox); if (note.userHost !== null) {
}); const reactee = await Users.findOne(note.userId)
dm.addDirectRecipe(reactee as IRemoteUser);
}
dm.addFollowersRecipe();
dm.execute();
} }
//#endregion //#endregion
}; };

View file

@ -2,9 +2,9 @@ import { publishNoteStream } from '../../stream';
import renderLike from '../../../remote/activitypub/renderer/like'; import renderLike from '../../../remote/activitypub/renderer/like';
import renderUndo from '../../../remote/activitypub/renderer/undo'; import renderUndo from '../../../remote/activitypub/renderer/undo';
import { renderActivity } from '../../../remote/activitypub/renderer'; import { renderActivity } from '../../../remote/activitypub/renderer';
import { deliver } from '../../../queue'; import DeliverManager from '../../../remote/activitypub/deliver-manager';
import { IdentifiableError } from '../../../misc/identifiable-error'; 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 { Note } from '../../../models/entities/note';
import { NoteReactions, Users, Notes } from '../../../models'; import { NoteReactions, Users, Notes } from '../../../models';
@ -39,12 +39,15 @@ export default async (user: User, note: Note) => {
}); });
//#region 配信 //#region 配信
// リアクターがローカルユーザーかつリアクション対象がリモートユーザーの投稿なら配送 if (Users.isLocalUser(user) && !note.localOnly) {
if (Users.isLocalUser(user) && (note.userHost !== null)) {
const content = renderActivity(renderUndo(renderLike(user, note, exist.reaction), user)); const content = renderActivity(renderUndo(renderLike(user, note, exist.reaction), user));
Users.findOne(note.userId).then(u => { const dm = new DeliverManager(user, content);
deliver(user, content, u!.inbox); if (note.userHost !== null) {
}); const reactee = await Users.findOne(note.userId)
dm.addDirectRecipe(reactee as IRemoteUser);
}
dm.addFollowersRecipe();
dm.execute();
} }
//#endregion //#endregion
}; };