fix(backend): send Delete activity of a note to users who renoted or replied to it (#15554)
* fix(backend): send Delete activity of a note to users who renoted or replied to it * Update CHANGELOG.md
This commit is contained in:
parent
2b6638e160
commit
389ec6350b
@ -27,6 +27,7 @@
|
|||||||
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
|
- Fix: pgroongaでの検索時にはじめのキーワードのみが検索に使用される問題を修正
|
||||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886)
|
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/886)
|
||||||
- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように
|
- Fix: メールアドレスの形式が正しくなければ以降の処理を行わないように
|
||||||
|
- Fix: フォロワーではないユーザーにリノートもしくは返信された場合にノートのDeleteアクティビティが送られていない問題を修正
|
||||||
|
|
||||||
## 2025.2.0
|
## 2025.2.0
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Brackets, In } from 'typeorm';
|
import { Brackets, In, IsNull, Not } from 'typeorm';
|
||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { Injectable, Inject } from '@nestjs/common';
|
||||||
import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js';
|
import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js';
|
||||||
import type { MiNote, IMentionedRemoteUsers } from '@/models/Note.js';
|
import type { MiNote, IMentionedRemoteUsers } from '@/models/Note.js';
|
||||||
@ -189,13 +189,27 @@ export class NoteDeleteService {
|
|||||||
}) as MiRemoteUser[];
|
}) as MiRemoteUser[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async getRenotedOrRepliedRemoteUsers(note: MiNote) {
|
||||||
|
const query = this.notesRepository.createQueryBuilder('note')
|
||||||
|
.leftJoinAndSelect('note.user', 'user')
|
||||||
|
.where(new Brackets(qb => {
|
||||||
|
qb.orWhere('note.renoteId = :renoteId', { renoteId: note.id });
|
||||||
|
qb.orWhere('note.replyId = :replyId', { replyId: note.id });
|
||||||
|
}))
|
||||||
|
.andWhere({ userHost: Not(IsNull()) });
|
||||||
|
const notes = await query.getMany() as (MiNote & { user: MiRemoteUser })[];
|
||||||
|
const remoteUsers = notes.map(({ user }) => user);
|
||||||
|
return remoteUsers;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) {
|
private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) {
|
||||||
this.apDeliverManagerService.deliverToFollowers(user, content);
|
this.apDeliverManagerService.deliverToFollowers(user, content);
|
||||||
this.relayService.deliverToRelays(user, content);
|
this.relayService.deliverToRelays(user, content);
|
||||||
const remoteUsers = await this.getMentionedRemoteUsers(note);
|
this.apDeliverManagerService.deliverToUsers(user, content, [
|
||||||
for (const remoteUser of remoteUsers) {
|
...await this.getMentionedRemoteUsers(note),
|
||||||
this.apDeliverManagerService.deliverToUser(user, content, remoteUser);
|
...await this.getRenotedOrRepliedRemoteUsers(note),
|
||||||
}
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,6 +196,25 @@ export class ApDeliverManagerService {
|
|||||||
await manager.execute();
|
await manager.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver activity to users
|
||||||
|
* @param actor
|
||||||
|
* @param activity Activity
|
||||||
|
* @param targets Target users
|
||||||
|
*/
|
||||||
|
@bindThis
|
||||||
|
public async deliverToUsers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, targets: MiRemoteUser[]): Promise<void> {
|
||||||
|
const manager = new DeliverManager(
|
||||||
|
this.userEntityService,
|
||||||
|
this.followingsRepository,
|
||||||
|
this.queueService,
|
||||||
|
actor,
|
||||||
|
activity,
|
||||||
|
);
|
||||||
|
for (const to of targets) manager.addDirectRecipe(to);
|
||||||
|
await manager.execute();
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
|
public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
|
||||||
return new DeliverManager(
|
return new DeliverManager(
|
||||||
|
@ -139,7 +139,8 @@ describe('Note', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Deletion', () => {
|
describe('Deletion', () => {
|
||||||
describe('Check Delete consistency', () => {
|
describe('Check Delete is delivered', () => {
|
||||||
|
describe('To followers', () => {
|
||||||
let carol: LoginUser;
|
let carol: LoginUser;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -149,7 +150,7 @@ describe('Note', () => {
|
|||||||
await sleep();
|
await sleep();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Delete is derivered to followers', async () => {
|
test('Check', async () => {
|
||||||
const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote;
|
const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote;
|
||||||
const noteInA = await resolveRemoteNote('b.test', note.id, carol);
|
const noteInA = await resolveRemoteNote('b.test', note.id, carol);
|
||||||
await bob.client.request('notes/delete', { noteId: note.id });
|
await bob.client.request('notes/delete', { noteId: note.id });
|
||||||
@ -163,6 +164,75 @@ describe('Note', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await carol.client.request('following/delete', { userId: bobInA.id });
|
||||||
|
await sleep();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('To renoted and not followed user', () => {
|
||||||
|
test('Check', async () => {
|
||||||
|
const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote;
|
||||||
|
const noteInA = await resolveRemoteNote('b.test', note.id, alice);
|
||||||
|
await alice.client.request('notes/create', { renoteId: noteInA.id });
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
await bob.client.request('notes/delete', { noteId: note.id });
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
await rejects(
|
||||||
|
async () => await alice.client.request('notes/show', { noteId: noteInA.id }),
|
||||||
|
(err: any) => {
|
||||||
|
strictEqual(err.code, 'NO_SUCH_NOTE');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('To replied and not followed user', () => {
|
||||||
|
test('Check', async () => {
|
||||||
|
const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote;
|
||||||
|
const noteInA = await resolveRemoteNote('b.test', note.id, alice);
|
||||||
|
await alice.client.request('notes/create', { text: 'Hello Bob!', replyId: noteInA.id });
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
await bob.client.request('notes/delete', { noteId: note.id });
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
await rejects(
|
||||||
|
async () => await alice.client.request('notes/show', { noteId: noteInA.id }),
|
||||||
|
(err: any) => {
|
||||||
|
strictEqual(err.code, 'NO_SUCH_NOTE');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FIXME: not delivered
|
||||||
|
* @see https://github.com/misskey-dev/misskey/issues/15548
|
||||||
|
*/
|
||||||
|
describe('To only resolved and not followed user', () => {
|
||||||
|
test.failing('Check', async () => {
|
||||||
|
const note = (await bob.client.request('notes/create', { text: 'I\'m Bob.' })).createdNote;
|
||||||
|
const noteInA = await resolveRemoteNote('b.test', note.id, alice);
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
await bob.client.request('notes/delete', { noteId: note.id });
|
||||||
|
await sleep();
|
||||||
|
|
||||||
|
await rejects(
|
||||||
|
async () => await alice.client.request('notes/show', { noteId: noteInA.id }),
|
||||||
|
(err: any) => {
|
||||||
|
strictEqual(err.code, 'NO_SUCH_NOTE');
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Deletion of remote user\'s note for moderation', () => {
|
describe('Deletion of remote user\'s note for moderation', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user