mirror of
https://codeberg.org/yeentown/barkey.git
synced 2025-07-07 12:36:57 +00:00
re-type userFollowingsCache to match the others
This commit is contained in:
parent
0c84d73294
commit
853b548a43
17 changed files with 47 additions and 56 deletions
|
@ -130,7 +130,8 @@ export class AntennaService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.visibility === 'followers') {
|
if (note.visibility === 'followers') {
|
||||||
const isFollowing = Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(antenna.userId), note.userId);
|
const followings = await this.cacheService.userFollowingsCache.fetch(antenna.userId);
|
||||||
|
const isFollowing = followings.has(note.userId);
|
||||||
if (!isFollowing && antenna.userId !== note.userId) return false;
|
if (!isFollowing && antenna.userId !== note.userId) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { In, IsNull } from 'typeorm';
|
import { In, IsNull } from 'typeorm';
|
||||||
import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiFollowing, MiNote } from '@/models/_.js';
|
import type { BlockingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository, MiNote } from '@/models/_.js';
|
||||||
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
|
||||||
import { QuantumKVCache } from '@/misc/QuantumKVCache.js';
|
import { QuantumKVCache } from '@/misc/QuantumKVCache.js';
|
||||||
import type { MiLocalUser, MiUser } from '@/models/User.js';
|
import type { MiLocalUser, MiUser } from '@/models/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { GlobalEvents, InternalEventTypes } from '@/core/GlobalEventService.js';
|
import type { InternalEventTypes } from '@/core/GlobalEventService.js';
|
||||||
import { InternalEventService } from '@/core/InternalEventService.js';
|
import { InternalEventService } from '@/core/InternalEventService.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
public userBlockingCache: QuantumKVCache<Set<string>>;
|
public userBlockingCache: QuantumKVCache<Set<string>>;
|
||||||
public userBlockedCache: QuantumKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
|
public userBlockedCache: QuantumKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
|
||||||
public renoteMutingsCache: QuantumKVCache<Set<string>>;
|
public renoteMutingsCache: QuantumKVCache<Set<string>>;
|
||||||
public userFollowingsCache: QuantumKVCache<Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>;
|
public userFollowingsCache: QuantumKVCache<Map<string, { withReplies: boolean }>>;
|
||||||
protected userFollowStatsCache = new MemoryKVCache<FollowStats>(1000 * 60 * 10); // 10 minutes
|
protected userFollowStatsCache = new MemoryKVCache<FollowStats>(1000 * 60 * 10); // 10 minutes
|
||||||
protected translationsCache: RedisKVCache<CachedTranslationEntity>;
|
protected translationsCache: RedisKVCache<CachedTranslationEntity>;
|
||||||
|
|
||||||
|
@ -110,15 +110,9 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
fetcher: (key) => this.renoteMutingsRepository.find({ where: { muterId: key }, select: ['muteeId'] }).then(xs => new Set(xs.map(x => x.muteeId))),
|
fetcher: (key) => this.renoteMutingsRepository.find({ where: { muterId: key }, select: ['muteeId'] }).then(xs => new Set(xs.map(x => x.muteeId))),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.userFollowingsCache = new QuantumKVCache<Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>(this.internalEventService, 'userFollowings', {
|
this.userFollowingsCache = new QuantumKVCache<Map<string, { withReplies: boolean }>>(this.internalEventService, 'userFollowings', {
|
||||||
lifetime: 1000 * 60 * 30, // 30m
|
lifetime: 1000 * 60 * 30, // 30m
|
||||||
fetcher: (key) => this.followingsRepository.find({ where: { followerId: key }, select: ['followeeId', 'withReplies'] }).then(xs => {
|
fetcher: (key) => this.followingsRepository.find({ where: { followerId: key }, select: ['followeeId', 'withReplies'] }).then(xs => new Map(xs.map(f => [f.followeeId, { withReplies: f.withReplies }]))),
|
||||||
const obj: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
|
|
||||||
for (const x of xs) {
|
|
||||||
obj[x.followeeId] = { withReplies: x.withReplies };
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.translationsCache = new RedisKVCache<CachedTranslationEntity>(this.redisClient, 'translations', {
|
this.translationsCache = new RedisKVCache<CachedTranslationEntity>(this.redisClient, 'translations', {
|
||||||
|
@ -305,14 +299,14 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getUserFollowings(userIds: Iterable<string>): Promise<Map<string, Set<string>>> {
|
public async getUserFollowings(userIds: Iterable<string>): Promise<Map<string, Map<string, { withReplies: boolean }>>> {
|
||||||
const followings = new Map<string, Set<string>>();
|
const followings = new Map<string, Map<string, { withReplies: boolean }>>();
|
||||||
|
|
||||||
const toFetch: string[] = [];
|
const toFetch: string[] = [];
|
||||||
for (const userId of userIds) {
|
for (const userId of userIds) {
|
||||||
const fromCache = this.userFollowingsCache.get(userId);
|
const fromCache = this.userFollowingsCache.get(userId);
|
||||||
if (fromCache) {
|
if (fromCache) {
|
||||||
followings.set(userId, new Set(Object.keys(fromCache)));
|
followings.set(userId, fromCache);
|
||||||
} else {
|
} else {
|
||||||
toFetch.push(userId);
|
toFetch.push(userId);
|
||||||
}
|
}
|
||||||
|
@ -331,25 +325,25 @@ export class CacheService implements OnApplicationShutdown {
|
||||||
})
|
})
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
const toCache = new Map<string, Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>();
|
const toCache = new Map<string, Map<string, { withReplies: boolean }>>();
|
||||||
|
|
||||||
// Pivot to a map
|
// Pivot to a map
|
||||||
for (const { followerId, followeeId, withReplies } of fetchedFollowings) {
|
for (const { followerId, followeeId, withReplies } of fetchedFollowings) {
|
||||||
// Queue for cache
|
// Queue for cache
|
||||||
let cacheSet = toCache.get(followerId);
|
let cacheMap = toCache.get(followerId);
|
||||||
if (!cacheSet) {
|
if (!cacheMap) {
|
||||||
cacheSet = {};
|
cacheMap = new Map();
|
||||||
toCache.set(followerId, cacheSet);
|
toCache.set(followerId, cacheMap);
|
||||||
}
|
}
|
||||||
cacheSet[followeeId] = { withReplies };
|
cacheMap.set(followeeId, { withReplies });
|
||||||
|
|
||||||
// Queue for return
|
// Queue for return
|
||||||
let returnSet = followings.get(followerId);
|
let returnSet = followings.get(followerId);
|
||||||
if (!returnSet) {
|
if (!returnSet) {
|
||||||
returnSet = new Set();
|
returnSet = new Map();
|
||||||
followings.set(followerId, returnSet);
|
followings.set(followerId, returnSet);
|
||||||
}
|
}
|
||||||
returnSet.add(followeeId);
|
returnSet.set(followeeId, { withReplies });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update cache to speed up future calls
|
// Update cache to speed up future calls
|
||||||
|
|
|
@ -113,27 +113,27 @@ export class NotificationService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recieveConfig?.type === 'following') {
|
if (recieveConfig?.type === 'following') {
|
||||||
const isFollowing = await this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId));
|
const isFollowing = await this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId));
|
||||||
if (!isFollowing) {
|
if (!isFollowing) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (recieveConfig?.type === 'follower') {
|
} else if (recieveConfig?.type === 'follower') {
|
||||||
const isFollower = await this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId));
|
const isFollower = await this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId));
|
||||||
if (!isFollower) {
|
if (!isFollower) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (recieveConfig?.type === 'mutualFollow') {
|
} else if (recieveConfig?.type === 'mutualFollow') {
|
||||||
const [isFollowing, isFollower] = await Promise.all([
|
const [isFollowing, isFollower] = await Promise.all([
|
||||||
this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)),
|
this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId)),
|
||||||
this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)),
|
this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId)),
|
||||||
]);
|
]);
|
||||||
if (!(isFollowing && isFollower)) {
|
if (!(isFollowing && isFollower)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if (recieveConfig?.type === 'followingOrFollower') {
|
} else if (recieveConfig?.type === 'followingOrFollower') {
|
||||||
const [isFollowing, isFollower] = await Promise.all([
|
const [isFollowing, isFollower] = await Promise.all([
|
||||||
this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => Object.hasOwn(followings, notifierId)),
|
this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId)),
|
||||||
this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => Object.hasOwn(followings, notifieeId)),
|
this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId)),
|
||||||
]);
|
]);
|
||||||
if (!isFollowing && !isFollower) {
|
if (!isFollowing && !isFollower) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -133,7 +133,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null, hint?: {
|
public async hideNote(packedNote: Packed<'Note'>, meId: MiUser['id'] | null, hint?: {
|
||||||
myFollowing?: ReadonlySet<string>,
|
myFollowing?: ReadonlyMap<string, { withReplies: boolean }>,
|
||||||
myBlockers?: ReadonlySet<string>,
|
myBlockers?: ReadonlySet<string>,
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
if (meId === packedNote.userId) return;
|
if (meId === packedNote.userId) return;
|
||||||
|
@ -193,7 +193,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
} else {
|
} else {
|
||||||
const isFollowing = hint?.myFollowing
|
const isFollowing = hint?.myFollowing
|
||||||
? hint.myFollowing.has(packedNote.userId)
|
? hint.myFollowing.has(packedNote.userId)
|
||||||
: (await this.cacheService.userFollowingsCache.fetch(meId))[packedNote.userId] != null;
|
: (await this.cacheService.userFollowingsCache.fetch(meId)).has(packedNote.userId);
|
||||||
|
|
||||||
hide = !isFollowing;
|
hide = !isFollowing;
|
||||||
}
|
}
|
||||||
|
@ -358,14 +358,10 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
: this.cacheService.userBlockingCache.fetch(meId).then((ids) => ids.has(note.userId)),
|
: this.cacheService.userBlockingCache.fetch(meId).then((ids) => ids.has(note.userId)),
|
||||||
hint?.myFollowing
|
hint?.myFollowing
|
||||||
? hint.myFollowing.has(note.userId)
|
? hint.myFollowing.has(note.userId)
|
||||||
: this.followingsRepository.existsBy({
|
: this.cacheService.userFollowingsCache.fetch(meId).then(ids => ids.has(note.userId)),
|
||||||
followeeId: note.userId,
|
|
||||||
followerId: meId,
|
|
||||||
}),
|
|
||||||
hint?.me !== undefined
|
hint?.me !== undefined
|
||||||
? (hint.me?.host ?? null)
|
? (hint.me?.host ?? null)
|
||||||
: this.cacheService.userByIdCache.fetch(meId, () => this.usersRepository.findOneByOrFail({ id: meId }))
|
: this.cacheService.findUserById(meId).then(me => me.host),
|
||||||
.then(me => me.host),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (blocked) return false;
|
if (blocked) return false;
|
||||||
|
@ -420,7 +416,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
|
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
|
||||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
||||||
mentionHandles: Record<string, string | undefined>;
|
mentionHandles: Record<string, string | undefined>;
|
||||||
userFollowings: Map<string, Set<string>>;
|
userFollowings: Map<string, Map<string, { withReplies: boolean }>>;
|
||||||
userBlockers: Map<string, Set<string>>;
|
userBlockers: Map<string, Set<string>>;
|
||||||
polls: Map<string, MiPoll>;
|
polls: Map<string, MiPoll>;
|
||||||
pollVotes: Map<string, Map<string, MiPollVote[]>>;
|
pollVotes: Map<string, Map<string, MiPollVote[]>>;
|
||||||
|
|
|
@ -164,7 +164,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
excludeBots: !ps.withBots,
|
excludeBots: !ps.withBots,
|
||||||
noteFilter: note => {
|
noteFilter: note => {
|
||||||
if (note.reply && note.reply.visibility === 'followers') {
|
if (note.reply && note.reply.visibility === 'followers') {
|
||||||
if (!Object.hasOwn(followings, note.reply.userId) && note.reply.userId !== me.id) return false;
|
if (!followings.has(note.reply.userId) && note.reply.userId !== me.id) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -115,7 +115,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
excludePureRenotes: !ps.withRenotes,
|
excludePureRenotes: !ps.withRenotes,
|
||||||
noteFilter: note => {
|
noteFilter: note => {
|
||||||
if (note.reply && note.reply.visibility === 'followers') {
|
if (note.reply && note.reply.visibility === 'followers') {
|
||||||
if (!Object.hasOwn(followings, note.reply.userId) && note.reply.userId !== me.id) return false;
|
if (!followings.has(note.reply.userId) && note.reply.userId !== me.id) return false;
|
||||||
}
|
}
|
||||||
if (!ps.withBots && note.user?.isBot) return false;
|
if (!ps.withBots && note.user?.isBot) return false;
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (ps.withReplies) redisTimelines.push(`userTimelineWithReplies:${ps.userId}`);
|
if (ps.withReplies) redisTimelines.push(`userTimelineWithReplies:${ps.userId}`);
|
||||||
if (ps.withChannelNotes) redisTimelines.push(`userTimelineWithChannel:${ps.userId}`);
|
if (ps.withChannelNotes) redisTimelines.push(`userTimelineWithChannel:${ps.userId}`);
|
||||||
|
|
||||||
const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId);
|
const isFollowing = me && (await this.cacheService.userFollowingsCache.fetch(me.id)).has(ps.userId);
|
||||||
|
|
||||||
const timeline = await this.fanoutTimelineEndpointService.timeline({
|
const timeline = await this.fanoutTimelineEndpointService.timeline({
|
||||||
untilId,
|
untilId,
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default class Connection {
|
||||||
private channels = new Map<string, Channel>();
|
private channels = new Map<string, Channel>();
|
||||||
private subscribingNotes = new Map<string, number>();
|
private subscribingNotes = new Map<string, number>();
|
||||||
public userProfile: MiUserProfile | null = null;
|
public userProfile: MiUserProfile | null = null;
|
||||||
public following: Record<string, Pick<MiFollowing, 'withReplies'> | undefined> = {};
|
public following: Map<string, { withReplies: boolean }> = new Map();
|
||||||
public followingChannels: Set<string> = new Set();
|
public followingChannels: Set<string> = new Set();
|
||||||
public userIdsWhoMeMuting: Set<string> = new Set();
|
public userIdsWhoMeMuting: Set<string> = new Set();
|
||||||
public userIdsWhoBlockingMe: Set<string> = new Set();
|
public userIdsWhoBlockingMe: Set<string> = new Set();
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default abstract class Channel {
|
||||||
if (!this.user) return false;
|
if (!this.user) return false;
|
||||||
if (this.user.id === note.userId) return true;
|
if (this.user.id === note.userId) return true;
|
||||||
if (note.visibility === 'followers') {
|
if (note.visibility === 'followers') {
|
||||||
return this.following[note.userId] != null;
|
return this.following.has(note.userId);
|
||||||
}
|
}
|
||||||
if (!note.visibleUserIds) return false;
|
if (!note.visibleUserIds) return false;
|
||||||
return note.visibleUserIds.includes(this.user.id);
|
return note.visibleUserIds.includes(this.user.id);
|
||||||
|
@ -84,7 +84,7 @@ export default abstract class Channel {
|
||||||
if (note.user.requireSigninToViewContents && !this.user) return true;
|
if (note.user.requireSigninToViewContents && !this.user) return true;
|
||||||
|
|
||||||
// 流れてきたNoteがインスタンスミュートしたインスタンスが関わる
|
// 流れてきたNoteがインスタンスミュートしたインスタンスが関わる
|
||||||
if (isInstanceMuted(note, this.userMutedInstances) && !this.following[note.userId]) return true;
|
if (isInstanceMuted(note, this.userMutedInstances) && !this.following.has(note.userId)) return true;
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わる
|
// 流れてきたNoteがミュートしているユーザーが関わる
|
||||||
if (isUserRelated(note, this.userIdsWhoMeMuting)) return true;
|
if (isUserRelated(note, this.userIdsWhoMeMuting)) return true;
|
||||||
|
@ -101,7 +101,7 @@ export default abstract class Channel {
|
||||||
if (note.user.isSilenced || note.user.instance?.isSilenced) {
|
if (note.user.isSilenced || note.user.instance?.isSilenced) {
|
||||||
if (this.user == null) return true;
|
if (this.user == null) return true;
|
||||||
if (this.user.id === note.userId) return false;
|
if (this.user.id === note.userId) return false;
|
||||||
if (this.following[note.userId] == null) return true;
|
if (!this.following.has(note.userId)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO muted threads
|
// TODO muted threads
|
||||||
|
|
|
@ -62,7 +62,7 @@ class BubbleTimelineChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ class GlobalTimelineChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ class HomeTimelineChannel extends Channel {
|
||||||
if (!this.followingChannels.has(note.channelId)) return;
|
if (!this.followingChannels.has(note.channelId)) return;
|
||||||
} else {
|
} else {
|
||||||
// その投稿のユーザーをフォローしていなかったら弾く
|
// その投稿のユーザーをフォローしていなかったら弾く
|
||||||
if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
|
if (!isMe && !this.following.has(note.userId)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isNoteMutedOrBlocked(note)) return;
|
if (this.isNoteMutedOrBlocked(note)) return;
|
||||||
|
@ -57,7 +57,7 @@ class HomeTimelineChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ class HybridTimelineChannel extends Channel {
|
||||||
// フォローしているチャンネルの投稿 の場合だけ
|
// フォローしているチャンネルの投稿 の場合だけ
|
||||||
if (!(
|
if (!(
|
||||||
(note.channelId == null && isMe) ||
|
(note.channelId == null && isMe) ||
|
||||||
(note.channelId == null && Object.hasOwn(this.following, note.userId)) ||
|
(note.channelId == null && this.following.has(note.userId)) ||
|
||||||
(note.channelId == null && (note.user.host == null && note.visibility === 'public')) ||
|
(note.channelId == null && (note.user.host == null && note.visibility === 'public')) ||
|
||||||
(note.channelId != null && this.followingChannels.has(note.channelId))
|
(note.channelId != null && this.followingChannels.has(note.channelId))
|
||||||
)) return;
|
)) return;
|
||||||
|
@ -74,7 +74,7 @@ class HybridTimelineChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies && !this.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies && !this.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ class LocalTimelineChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ class RoleTimelineChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user?.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ class UserListChannel extends Channel {
|
||||||
const reply = note.reply;
|
const reply = note.reply;
|
||||||
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
// 自分のフォローしていないユーザーの visibility: followers な投稿への返信は弾く
|
||||||
if (!this.isNoteVisibleToMe(reply)) return;
|
if (!this.isNoteVisibleToMe(reply)) return;
|
||||||
if (!this.following[note.userId]?.withReplies) {
|
if (!this.following.get(note.userId)?.withReplies) {
|
||||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||||
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
if (reply.userId !== this.user!.id && !isMe && reply.userId !== note.userId) return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { FakeInternalEventService } from './FakeInternalEventService.js';
|
import { FakeInternalEventService } from './FakeInternalEventService.js';
|
||||||
import type { BlockingsRepository, FollowingsRepository, MiFollowing, MiUser, MiUserProfile, MutingsRepository, RenoteMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
import type { BlockingsRepository, FollowingsRepository, MiUser, MiUserProfile, MutingsRepository, RenoteMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
import type { MiLocalUser } from '@/models/User.js';
|
import type { MiLocalUser } from '@/models/User.js';
|
||||||
import { MemoryKVCache, MemorySingleCache, RedisKVCache, RedisSingleCache } from '@/misc/cache.js';
|
import { MemoryKVCache, MemorySingleCache, RedisKVCache, RedisSingleCache } from '@/misc/cache.js';
|
||||||
import { QuantumKVCache, QuantumKVOpts } from '@/misc/QuantumKVCache.js';
|
import { QuantumKVCache, QuantumKVOpts } from '@/misc/QuantumKVCache.js';
|
||||||
|
@ -106,7 +106,7 @@ export class NoOpCacheService extends CacheService {
|
||||||
onSet: this.renoteMutingsCache.onSet,
|
onSet: this.renoteMutingsCache.onSet,
|
||||||
onDelete: this.renoteMutingsCache.onDelete,
|
onDelete: this.renoteMutingsCache.onDelete,
|
||||||
});
|
});
|
||||||
this.userFollowingsCache = new NoOpQuantumKVCache<Record<string, Pick<MiFollowing, 'withReplies'> | undefined>>({
|
this.userFollowingsCache = new NoOpQuantumKVCache<Map<string, { withReplies: boolean }>>({
|
||||||
internalEventService: fakeInternalEventService,
|
internalEventService: fakeInternalEventService,
|
||||||
fetcher: this.userFollowingsCache.fetcher,
|
fetcher: this.userFollowingsCache.fetcher,
|
||||||
onSet: this.userFollowingsCache.onSet,
|
onSet: this.userFollowingsCache.onSet,
|
||||||
|
|
Loading…
Add table
Reference in a new issue