1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-06-10 01:24:08 +02:00

Add mutingType field to Muting model and update related code

Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-11-07 12:04:42 +00:00
parent 1810d6e837
commit 685847e1b6
15 changed files with 72 additions and 23 deletions

View File

@@ -5,7 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis';
import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing } from '@/models/_.js';
import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing, MiMuting } from '@/models/_.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import type { MiLocalUser, MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
@@ -21,7 +21,7 @@ export class CacheService implements OnApplicationShutdown {
public localUserByIdCache: MemoryKVCache<MiLocalUser>;
public uriPersonCache: MemoryKVCache<MiUser | null>;
public userProfileCache: RedisKVCache<MiUserProfile>;
public userMutingsCache: RedisKVCache<Set<string>>;
public userMutingsCache: RedisKVCache<Map<string, Pick<MiMuting, 'mutingType'>>>;
public userBlockingCache: RedisKVCache<Set<string>>;
public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
public renoteMutingsCache: RedisKVCache<Set<string>>;
@@ -69,12 +69,12 @@ export class CacheService implements OnApplicationShutdown {
fromRedisConverter: (value) => JSON.parse(value), // TODO: date型の考慮
});
this.userMutingsCache = new RedisKVCache<Set<string>>(this.redisClient, 'userMutings', {
this.userMutingsCache = new RedisKVCache<Map<string, Pick<MiMuting, 'mutingType'>>>(this.redisClient, 'userMutings', {
lifetime: 1000 * 60 * 30, // 30m
memoryCacheLifetime: 1000 * 60, // 1m
fetcher: (key) => this.mutingsRepository.find({ where: { muterId: key }, select: ['muteeId'] }).then(xs => new Set(xs.map(x => x.muteeId))),
toRedisConverter: (value) => JSON.stringify(Array.from(value)),
fromRedisConverter: (value) => new Set(JSON.parse(value)),
fetcher: (key) => this.mutingsRepository.find({ where: { muterId: key }, select: ['muteeId', 'mutingType'] }).then(xs => new Map(xs.map(x => [x.muteeId, { mutingType: x.mutingType }]))),
toRedisConverter: (value) => JSON.stringify(Array.from(value.entries())),
fromRedisConverter: (value) => new Map(JSON.parse(value)),
});
this.userBlockingCache = new RedisKVCache<Set<string>>(this.redisClient, 'userBlocking', {

View File

@@ -111,7 +111,7 @@ export class FanoutTimelineEndpointService {
if (ps.me) {
const me = ps.me;
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingMap,
userIdsWhoMeMutingRenotes,
userIdsWhoBlockingMe,
userMutedInstances,
@@ -124,6 +124,8 @@ export class FanoutTimelineEndpointService {
this.channelMutingService.mutingChannelsCache.fetch(me.id),
]);
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
const parentFilter = filter;
filter = (note) => {
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;

View File

@@ -108,7 +108,8 @@ export class NotificationService implements OnApplicationShutdown {
}
const mutings = await this.cacheService.userMutingsCache.fetch(notifieeId);
if (mutings.has(notifierId)) {
const muting = mutings.get(notifierId);
if (muting && muting.mutingType === 'all') {
return null;
}

View File

@@ -286,14 +286,16 @@ export class SearchService {
}
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingMap,
userIdsWhoBlockingMe,
] = me
? await Promise.all([
this.cacheService.userMutingsCache.fetch(me.id),
this.cacheService.userBlockedCache.fetch(me.id),
])
: [new Set<string>(), new Set<string>()];
: [new Map<string, { mutingType: 'all' | 'timelineOnly' }>(), new Set<string>()];
const userIdsWhoMeMuting = new Set(userIdsWhoMeMutingMap.keys());
const query = this.notesRepository.createQueryBuilder('note')
.innerJoinAndSelect('note.user', 'user')

View File

@@ -24,12 +24,13 @@ export class UserMutingService {
}
@bindThis
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null, mutingType: 'all' | 'timelineOnly' = 'all'): Promise<void> {
await this.mutingsRepository.insert({
id: this.idService.gen(),
expiresAt: expiresAt ?? null,
muterId: user.id,
muteeId: target.id,
mutingType: mutingType,
});
this.cacheService.userMutingsCache.refresh(user.id);

View File

@@ -40,6 +40,7 @@ export class MutingEntityService {
id: muting.id,
createdAt: this.idService.parse(muting.id).date.toISOString(),
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
mutingType: muting.mutingType,
muteeId: muting.muteeId,
mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
schema: 'UserDetailedNotMe',

View File

@@ -289,12 +289,13 @@ export class NotificationEntityService implements OnModuleInit {
*/
#validateNotifier <T extends MiNotification | MiGroupedNotification> (
notification: T,
userIdsWhoMeMuting: Set<MiUser['id']>,
userIdsWhoMeMutingMap: Map<MiUser['id'], { mutingType: 'all' | 'timelineOnly' }>,
userMutedInstances: Set<string>,
notifiers: MiUser[],
): boolean {
if (!('notifierId' in notification)) return true;
if (userIdsWhoMeMuting.has(notification.notifierId)) return false;
const muting = userIdsWhoMeMutingMap.get(notification.notifierId);
if (muting && muting.mutingType === 'all') return false;
const notifier = notifiers.find(x => x.id === notification.notifierId) ?? null;
@@ -324,7 +325,7 @@ export class NotificationEntityService implements OnModuleInit {
meId: MiUser['id'],
): Promise<T[]> {
const [
userIdsWhoMeMuting,
userIdsWhoMeMutingMap,
userMutedInstances,
] = await Promise.all([
this.cacheService.userMutingsCache.fetch(meId),
@@ -337,7 +338,7 @@ export class NotificationEntityService implements OnModuleInit {
}) : [];
const filteredNotifications = ((await Promise.all(notifications.map(async (notification) => {
const isValid = this.#validateNotifier(notification, userIdsWhoMeMuting, userMutedInstances, notifiers);
const isValid = this.#validateNotifier(notification, userIdsWhoMeMutingMap, userMutedInstances, notifiers);
return isValid ? notification : null;
}))) as [T | null] ).filter(x => x != null);