-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat: 特定ユーザーからのリアクションをブロックする機能の追加 #14992
base: develop
Are you sure you want to change the base?
Changes from 4 commits
3dd5af3
37627bb
51a2a7d
1b0ac28
9ef2dbb
202fcee
3ea69b6
24792e0
2665294
0301e86
da94dbe
34da11f
292809a
a96ae92
ac95b12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* SPDX-FileCopyrightText: syuilo and misskey-project | ||
* SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
|
||
export class AddBlockingReactionUser1731898598469 { | ||
name = 'AddBlockingReactionUser1731898598469' | ||
|
||
async up(queryRunner) { | ||
await queryRunner.query(`ALTER TABLE "blocking" ADD "isReactionBlock" boolean NOT NULL DEFAULT false`); | ||
await queryRunner.query(`COMMENT ON COLUMN "blocking"."isReactionBlock" IS 'Whether the blockee is a reaction block.'`); | ||
await queryRunner.query(`CREATE INDEX "IDX_7b0698c38d27a5554bed4858bd" ON "blocking" ("isReactionBlock") `); | ||
} | ||
|
||
async down(queryRunner) { | ||
await queryRunner.query(`DELETE FROM blocking WHERE "isReactionBlock" = 'true'`); // blockingテーブルのisReactionBlockカラムがtrueの行を削除する | ||
await queryRunner.query(`DROP INDEX "IDX_7b0698c38d27a5554bed4858bd"`); | ||
await queryRunner.query(`ALTER TABLE "blocking" DROP COLUMN "isReactionBlock"`); | ||
} | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -73,6 +73,7 @@ export class UserBlockingService implements OnModuleInit { | |
blockerId: blocker.id, | ||
blockee, | ||
blockeeId: blockee.id, | ||
isReactionBlock: false, | ||
} as MiBlocking; | ||
|
||
await this.blockingsRepository.insert(blocking); | ||
|
@@ -160,6 +161,7 @@ export class UserBlockingService implements OnModuleInit { | |
const blocking = await this.blockingsRepository.findOneBy({ | ||
blockerId: blocker.id, | ||
blockeeId: blockee.id, | ||
isReactionBlock: false, | ||
}); | ||
|
||
if (blocking == null) { | ||
|
@@ -169,28 +171,23 @@ export class UserBlockingService implements OnModuleInit { | |
|
||
// Since we already have the blocker and blockee, we do not need to fetch | ||
// them in the query above and can just manually insert them here. | ||
blocking.blocker = blocker; | ||
blocking.blockee = blockee; | ||
// But we don't need to do this because we are not using them in this function. | ||
// blocking.blocker = blocker; | ||
// blocking.blockee = blockee; | ||
|
||
await this.blockingsRepository.delete(blocking.id); | ||
|
||
this.cacheService.userBlockingCache.refresh(blocker.id); | ||
this.cacheService.userBlockedCache.refresh(blockee.id); | ||
this.cacheService.userReactionBlockedCache.refresh(blocker.id); | ||
this.cacheService.userReactionBlockedCache.refresh(blockee.id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. このServiceは通常のブロック向けかと思いますが、ここでリアクションブロック向けの機能を更新しているのは特殊な理由がありますか…? |
||
|
||
this.globalEventService.publishInternalEvent('blockingDeleted', { | ||
this.globalEventService.publishInternalEvent('blockingReactionDeleted', { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こちらも、配信するイベントが変わっています |
||
blockerId: blocker.id, | ||
blockeeId: blockee.id, | ||
}); | ||
|
||
// deliver if remote bloking | ||
if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { | ||
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker)); | ||
this.queueService.deliver(blocker, content, blockee.inbox, false); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ActivityPub向けにブロックイベントの配信をする処理なのですが、こちらも意図を確認させてください |
||
} | ||
|
||
@bindThis | ||
public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise<boolean> { | ||
return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); | ||
return (await this.cacheService.userReactionBlockingCache.fetch(blockerId)).has(blockeeId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* | ||
* SPDX-FileCopyrightText: syuilo and misskey-project | ||
* SPDX-License-Identifier: AGPL-3.0-only | ||
*/ | ||
|
||
import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; | ||
import { ModuleRef } from '@nestjs/core'; | ||
import { IdService } from '@/core/IdService.js'; | ||
import type { MiUser } from '@/models/User.js'; | ||
import type { MiBlocking } from '@/models/Blocking.js'; | ||
import { QueueService } from '@/core/QueueService.js'; | ||
import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||
import { DI } from '@/di-symbols.js'; | ||
import type { BlockingsRepository, UserListsRepository } from '@/models/_.js'; | ||
import Logger from '@/logger.js'; | ||
import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||
import { LoggerService } from '@/core/LoggerService.js'; | ||
import { UserWebhookService } from '@/core/UserWebhookService.js'; | ||
import { bindThis } from '@/decorators.js'; | ||
import { CacheService } from '@/core/CacheService.js'; | ||
import { UserFollowingService } from '@/core/UserFollowingService.js'; | ||
|
||
@Injectable() | ||
export class UserReactionBlockingService { | ||
private logger: Logger; | ||
private userFollowingService: UserFollowingService; | ||
|
||
constructor( | ||
private moduleRef: ModuleRef, | ||
|
||
@Inject(DI.blockingsRepository) | ||
private blockingsRepository: BlockingsRepository, | ||
|
||
@Inject(DI.userListsRepository) | ||
private userListsRepository: UserListsRepository, | ||
|
||
private cacheService: CacheService, | ||
private userEntityService: UserEntityService, | ||
private idService: IdService, | ||
private queueService: QueueService, | ||
private globalEventService: GlobalEventService, | ||
private webhookService: UserWebhookService, | ||
private apRendererService: ApRendererService, | ||
private loggerService: LoggerService, | ||
) { | ||
this.logger = this.loggerService.getLogger('user-block'); | ||
} | ||
|
||
@bindThis | ||
public async block(blocker: MiUser, blockee: MiUser, silent = false) { | ||
const blocking = { | ||
id: this.idService.gen(), | ||
blocker, | ||
blockerId: blocker.id, | ||
blockee, | ||
blockeeId: blockee.id, | ||
isReactionBlock: true, | ||
} as MiBlocking; | ||
kakkokari-gtyih marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
await this.blockingsRepository.insert(blocking); | ||
|
||
this.cacheService.userReactionBlockingCache.refresh(blocker.id); | ||
this.cacheService.userReactionBlockedCache.refresh(blockee.id); | ||
|
||
this.globalEventService.publishInternalEvent('blockingReactionCreated', { | ||
blockerId: blocker.id, | ||
blockeeId: blockee.id, | ||
}); | ||
} | ||
|
||
@bindThis | ||
public async unblock(blocker: MiUser, blockee: MiUser) { | ||
const blocking = await this.blockingsRepository.findOneBy({ | ||
blockerId: blocker.id, | ||
blockeeId: blockee.id, | ||
isReactionBlock: true, | ||
}); | ||
|
||
if (blocking == null) { | ||
this.logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); | ||
kakkokari-gtyih marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
} | ||
|
||
// Since we already have the blocker and blockee, we do not need to fetch | ||
// them in the query above and can just manually insert them here. | ||
blocking.blocker = blocker; | ||
blocking.blockee = blockee; | ||
|
||
await this.blockingsRepository.delete(blocking.id); | ||
|
||
this.cacheService.userBlockingCache.refresh(blocker.id); | ||
this.cacheService.userBlockedCache.refresh(blockee.id); | ||
|
||
this.globalEventService.publishInternalEvent('blockingDeleted', { | ||
blockerId: blocker.id, | ||
blockeeId: blockee.id, | ||
}); | ||
|
||
// deliver if remote bloking | ||
if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { | ||
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker)); | ||
this.queueService.deliver(blocker, content, blockee.inbox, false); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. もしかして通常のブロック側と逆になっています…? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. すみません、完全に全て逆になっていたようです。修正しました。 |
||
} | ||
|
||
@bindThis | ||
public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise<boolean> { | ||
return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
①
この変更は、
blocking.blockeeId
とblocking.blockerId
の組み合わせが2つ以上存在しうることを許容する前提のように見受けられますが、相違ございませんか?(通常のブロックで1レコード、リアクションブロックで1レコードずつ)もし上記の状態を期待するのであれば…やり方を変える必要があると考えます。
以下の画像の通り、
blocking.blockeeId
とblocking.blockerId
のユニークインデックスが既に存在しているので重複する組み合わせは登録できないようになっています。なので…blockingの1レコードでやりくりする必要があるかと思います。
②
isReactionBlock
よりもblockType
としてenumを保持するようにすると後から手を入れやすいかも…?