refactor: cache suspended users

This commit is contained in:
Namekuji 2023-09-20 01:57:02 -04:00
parent 19d7dc8b84
commit 8e0b0c7cb9
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
21 changed files with 234 additions and 225 deletions

View file

@ -404,7 +404,7 @@ export async function execPaginationQuery(
userIds?: string[]; userIds?: string[];
}, },
filter?: { filter?: {
note?: (_: ScyllaNote[]) => Promise<ScyllaNote[]>; note?: (_: ScyllaNote[]) => ScyllaNote[];
reaction?: (_: ScyllaNoteReaction[]) => Promise<ScyllaNoteReaction[]>; reaction?: (_: ScyllaNoteReaction[]) => Promise<ScyllaNoteReaction[]>;
notification?: (_: ScyllaNotification[]) => Promise<ScyllaNotification[]>; notification?: (_: ScyllaNotification[]) => Promise<ScyllaNotification[]>;
}, },
@ -492,7 +492,7 @@ export async function execPaginationQuery(
} else { } else {
const notes = result.rows.map(parseScyllaNote); const notes = result.rows.map(parseScyllaNote);
(found as ScyllaNote[]).push( (found as ScyllaNote[]).push(
...(filter?.note ? await filter.note(notes) : notes), ...(filter?.note ? filter.note(notes) : notes),
); );
untilDate = notes[notes.length - 1].createdAt; untilDate = notes[notes.length - 1].createdAt;
} }
@ -517,11 +517,11 @@ export async function execPaginationQuery(
return found as ScyllaNote[]; return found as ScyllaNote[];
} }
export async function filterVisibility( export function filterVisibility(
notes: ScyllaNote[], notes: ScyllaNote[],
user: { id: User["id"] } | null, user: { id: User["id"] } | null,
followingIds?: User["id"][], followingIds: User["id"][],
): Promise<ScyllaNote[]> { ): ScyllaNote[] {
let filtered = notes; let filtered = notes;
if (!user) { if (!user) {
@ -529,15 +529,6 @@ export async function filterVisibility(
["public", "home"].includes(note.visibility), ["public", "home"].includes(note.visibility),
); );
} else { } else {
let ids: User["id"][];
if (followingIds) {
ids = followingIds;
} else {
ids = await LocalFollowingsCache.init(user.id).then((cache) =>
cache.getAll(),
);
}
filtered = filtered.filter( filtered = filtered.filter(
(note) => (note) =>
["public", "home"].includes(note.visibility) || ["public", "home"].includes(note.visibility) ||
@ -545,18 +536,18 @@ export async function filterVisibility(
note.visibleUserIds.includes(user.id) || note.visibleUserIds.includes(user.id) ||
note.mentions.includes(user.id) || note.mentions.includes(user.id) ||
(note.visibility === "followers" && (note.visibility === "followers" &&
(ids.includes(note.userId) || note.replyUserId === user.id)), (followingIds.includes(note.userId) || note.replyUserId === user.id)),
); );
} }
return filtered; return filtered;
} }
export async function filterChannel( export function filterChannel(
notes: ScyllaNote[], notes: ScyllaNote[],
user: { id: User["id"] } | null, user: { id: User["id"] } | null,
followingIds?: Channel["id"][], followingIds: Channel["id"][],
): Promise<ScyllaNote[]> { ): ScyllaNote[] {
let filtered = notes; let filtered = notes;
if (!user) { if (!user) {
@ -564,16 +555,8 @@ export async function filterChannel(
} else { } else {
const channelNotes = filtered.filter((note) => !!note.channelId); const channelNotes = filtered.filter((note) => !!note.channelId);
if (channelNotes.length > 0) { if (channelNotes.length > 0) {
let followings: Channel["id"][];
if (followingIds) {
followings = followingIds;
} else {
followings = await ChannelFollowingsCache.init(user.id).then((cache) =>
cache.getAll(),
);
}
filtered = filtered.filter( filtered = filtered.filter(
(note) => !note.channelId || followings.includes(note.channelId), (note) => !note.channelId || followingIds.includes(note.channelId),
); );
} }
} }
@ -581,11 +564,11 @@ export async function filterChannel(
return filtered; return filtered;
} }
export async function filterReply( export function filterReply(
notes: ScyllaNote[], notes: ScyllaNote[],
withReplies: boolean, withReplies: boolean,
user: { id: User["id"] } | null, user: { id: User["id"] } | null,
): Promise<ScyllaNote[]> { ): ScyllaNote[] {
let filtered = notes; let filtered = notes;
if (!user) { if (!user) {
@ -605,113 +588,60 @@ export async function filterReply(
return filtered; return filtered;
} }
export async function filterMutedUser( export function filterMutedUser(
notes: ScyllaNote[], notes: ScyllaNote[],
user: { id: User["id"] }, mutedIds: User["id"][],
mutedIds?: User["id"][], mutedInstances: UserProfile["mutedInstances"],
mutedInstances?: UserProfile["mutedInstances"],
exclude?: User, exclude?: User,
): Promise<ScyllaNote[]> { ): ScyllaNote[] {
let ids: User["id"][]; let userIds: User["id"][] = mutedIds;
let instances: UserProfile["mutedInstances"];
if (mutedIds) {
ids = mutedIds;
} else {
ids = await UserMutingsCache.init(user.id).then((cache) => cache.getAll());
}
if (mutedInstances) {
instances = mutedInstances;
} else {
instances = await InstanceMutingsCache.init(user.id).then((cache) =>
cache.getAll(),
);
}
if (exclude) { if (exclude) {
ids = ids.filter((id) => id !== exclude.id); userIds = mutedIds.filter((id) => id !== exclude.id);
} }
return notes.filter( return notes.filter(
(note) => (note) =>
!ids.includes(note.userId) && !userIds.includes(note.userId) &&
!(note.replyUserId && ids.includes(note.replyUserId)) && !(note.replyUserId && userIds.includes(note.replyUserId)) &&
!(note.renoteUserId && ids.includes(note.renoteUserId)) && !(note.renoteUserId && userIds.includes(note.renoteUserId)) &&
!(note.userHost && instances.includes(note.userHost)) && !(note.userHost && mutedInstances.includes(note.userHost)) &&
!(note.replyUserHost && instances.includes(note.replyUserHost)) && !(note.replyUserHost && mutedInstances.includes(note.replyUserHost)) &&
!(note.renoteUserHost && instances.includes(note.renoteUserHost)), !(note.renoteUserHost && mutedInstances.includes(note.renoteUserHost)),
); );
} }
export async function filterMutedNote( export function filterMutedNote(
notes: ScyllaNote[], notes: ScyllaNote[],
user: { id: User["id"] }, user: { id: User["id"] },
mutedWords?: string[][], mutedWords: string[][],
): Promise<ScyllaNote[]> { ): ScyllaNote[] {
let words = mutedWords; if (mutedWords.length > 0) {
if (!words) {
words = await userWordMuteCache.fetchMaybe(user.id, () =>
UserProfiles.findOne({
select: ["mutedWords"],
where: { userId: user.id },
}).then((profile) => profile?.mutedWords),
);
}
if (words && words.length > 0) {
return notes.filter( return notes.filter(
(note) => !getWordHardMute(note, user, words as string[][]), (note) => !getWordHardMute(note, user, mutedWords),
); );
} }
return notes; return notes;
} }
export async function filterBlockUser( export function filterBlockUser(
notes: ScyllaNote[], notes: ScyllaNote[],
user: { id: User["id"] }, blockIds: User["id"][],
blockIds?: User["id"][], ): ScyllaNote[] {
): Promise<ScyllaNote[]> {
let ids: User["id"][];
if (blockIds) {
ids = blockIds;
} else {
const blocked = await UserBlockedCache.init(user.id).then((cache) =>
cache.getAll(),
);
const blocking = await UserBlockingCache.init(user.id).then((cache) =>
cache.getAll(),
);
ids = [...blocked, ...blocking];
}
return notes.filter( return notes.filter(
(note) => (note) =>
!ids.includes(note.userId) && !blockIds.includes(note.userId) &&
!(note.replyUserId && ids.includes(note.replyUserId)) && !(note.replyUserId && blockIds.includes(note.replyUserId)) &&
!(note.renoteUserId && ids.includes(note.renoteUserId)), !(note.renoteUserId && blockIds.includes(note.renoteUserId)),
); );
} }
export async function filterMutedRenotes( export function filterMutedRenotes(
notes: ScyllaNote[], notes: ScyllaNote[],
user: { id: User["id"] }, muteeIds: User["id"][],
muteeIds?: User["id"][], ): ScyllaNote[] {
): Promise<ScyllaNote[]> {
let ids: User["id"][];
if (muteeIds) {
ids = muteeIds;
} else {
ids = await RenoteMutingsCache.init(user.id).then((cache) =>
cache.getAll(),
);
}
return notes.filter( return notes.filter(
(note) => note.text || !note.renoteId || !ids.includes(note.userId), (note) => note.text || !note.renoteId || !muteeIds.includes(note.userId),
); );
} }

View file

@ -8,6 +8,7 @@ import {
Mutings, Mutings,
RenoteMutings, RenoteMutings,
UserProfiles, UserProfiles,
Users,
} from "@/models/index.js"; } from "@/models/index.js";
import { IsNull } from "typeorm"; import { IsNull } from "typeorm";
import config from "@/config/index.js"; import config from "@/config/index.js";
@ -479,3 +480,21 @@ export class RenoteMutingsCache extends SetCache {
return cache; return cache;
} }
} }
export class SuspendedUsersCache extends SetCache {
private constructor() {
const fetcher = () => Users.find({
select: ["id"],
where: { isSuspended: true }
}).then((users) => users.map(({ id }) => id));
super("suspendedUsers", "system", fetcher);
}
public static async init(): Promise<SuspendedUsersCache> {
const cache = new SuspendedUsersCache();
await cache.fetch();
return cache;
}
}

View file

@ -10,6 +10,7 @@ import {
scyllaClient, scyllaClient,
} from "@/db/scylla.js"; } from "@/db/scylla.js";
import { userByIdCache } from "@/services/user-cache.js"; import { userByIdCache } from "@/services/user-cache.js";
import { LocalFollowingsCache } from "@/misc/cache.js";
/** /**
* Get note for API processing, taking into account visibility. * Get note for API processing, taking into account visibility.
@ -28,7 +29,13 @@ export async function getNote(
); );
if (result.rowLength > 0) { if (result.rowLength > 0) {
const candidate = parseScyllaNote(result.first()); const candidate = parseScyllaNote(result.first());
const filtered = await filterVisibility([candidate], me, followingIds); let ids: string[] = [];
if (followingIds) {
ids = followingIds
} else if (me) {
ids = await LocalFollowingsCache.init(me.id).then((cache) => cache.getAll());
}
const filtered = filterVisibility([candidate], me, ids);
if (filtered.length > 0) { if (filtered.length > 0) {
note = filtered[0]; note = filtered[0];
} }

View file

@ -6,6 +6,7 @@ import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { doPostSuspend } from "@/services/suspend-user.js"; import { doPostSuspend } from "@/services/suspend-user.js";
import { publishUserEvent } from "@/services/stream.js"; import { publishUserEvent } from "@/services/stream.js";
import { scyllaClient } from "@/db/scylla.js"; import { scyllaClient } from "@/db/scylla.js";
import { SuspendedUsersCache } from "@/misc/cache.js";
export const meta = { export const meta = {
tags: ["admin"], tags: ["admin"],
@ -37,6 +38,7 @@ export default define(meta, paramDef, async (ps, me) => {
throw new Error("cannot suspend moderator"); throw new Error("cannot suspend moderator");
} }
await SuspendedUsersCache.init().then((cache) => cache.add(user.id));
await Users.update(user.id, { await Users.update(user.id, {
isSuspended: true, isSuspended: true,
}); });

View file

@ -1,3 +1,4 @@
import { SuspendedUsersCache } from "@/misc/cache.js";
import define from "../../define.js"; import define from "../../define.js";
import { Users } from "@/models/index.js"; import { Users } from "@/models/index.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js";
@ -25,6 +26,7 @@ export default define(meta, paramDef, async (ps, me) => {
throw new Error("user not found"); throw new Error("user not found");
} }
await SuspendedUsersCache.init().then((cache) => cache.delete(user.id));
await Users.update(user.id, { await Users.update(user.id, {
isSuspended: false, isSuspended: false,
}); });

View file

@ -23,6 +23,7 @@ import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -93,6 +94,7 @@ export default define(meta, paramDef, async (ps, user) => {
blockerIds, blockerIds,
blockingIds, blockingIds,
renoteMutedIds, renoteMutedIds,
suspendedUsers,
] = await Promise.all([ ] = await Promise.all([
LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()), LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()),
UserMutingsCache.init(user.id).then((cache) => cache.getAll()), UserMutingsCache.init(user.id).then((cache) => cache.getAll()),
@ -108,6 +110,7 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockedCache.init(user.id).then((cache) => cache.getAll()),
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()),
SuspendedUsersCache.init().then((cache) => cache.getAll()),
]); ]);
const userIds: string[] = []; const userIds: string[] = [];
@ -139,21 +142,21 @@ export default define(meta, paramDef, async (ps, user) => {
.map((xs) => xs.filter((x) => x !== "")) .map((xs) => xs.filter((x) => x !== ""))
.filter((xs) => xs.length > 0); .filter((xs) => xs.length > 0);
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = await filterVisibility(notes, user, followingUserIds); let filtered = filterVisibility(notes, user, followingUserIds);
filtered = await filterReply(filtered, antenna.withReplies, user); filtered = filterReply(filtered, antenna.withReplies, user);
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [ filtered = filterBlockUser(filtered, [
...blockerIds, ...blockerIds,
...blockingIds, ...blockingIds,
...suspendedUsers,
]); ]);
filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); filtered = filterMutedRenotes(filtered, renoteMutedIds);
if (antenna.withFile) { if (antenna.withFile) {
filtered = filtered.filter((n) => n.files.length > 0); filtered = filtered.filter((n) => n.files.length > 0);
} }

View file

@ -12,6 +12,7 @@ import {
scyllaClient, scyllaClient,
} from "@/db/scylla.js"; } from "@/db/scylla.js";
import { import {
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -87,15 +88,17 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUsers = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
if (!user) return notes; let filtered = filterBlockUser(notes, [
let filtered = await filterMutedUser(notes, user, mutedUserIds, []);
filtered = await filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [
...blockerIds, ...blockerIds,
...blockingIds, ...blockingIds,
...suspendedUsers
]); ]);
if (!user) return filtered;
filtered = filterMutedUser(notes, mutedUserIds, []);
filtered = filterMutedNote(filtered, user, mutedWords);
return filtered; return filtered;
}; };

View file

@ -18,6 +18,7 @@ import {
import { import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -91,6 +92,7 @@ export default define(meta, paramDef, async (ps, user) => {
} }
const noteIds = await ClipNotes.find({ const noteIds = await ClipNotes.find({
select: ["noteId"],
where: whereOpt, where: whereOpt,
order: { noteId: "DESC" }, order: { noteId: "DESC" },
take: Math.min(ps.limit * 2, config.scylla?.queryLimit ?? 100), take: Math.min(ps.limit * 2, config.scylla?.queryLimit ?? 100),
@ -108,6 +110,8 @@ export default define(meta, paramDef, async (ps, user) => {
blockingIds, blockingIds,
]: string[][] = []; ]: string[][] = [];
let mutedWords: string[][]; let mutedWords: string[][];
blockerIds = [];
blockingIds = [];
if (user) { if (user) {
[ [
followingUserIds, followingUserIds,
@ -132,22 +136,22 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes; let filtered = filterBlockUser(notes, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (user) { if (user) {
filtered = await filterVisibility(filtered, user, followingUserIds); filtered = filterVisibility(filtered, user, followingUserIds);
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [
...blockerIds,
...blockingIds,
]);
} }
return filtered; return filtered;
}; };

View file

@ -21,6 +21,7 @@ import {
import { import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -98,12 +99,14 @@ export default define(meta, paramDef, async (ps, user) => {
mutedInstances, mutedInstances,
blockerIds, blockerIds,
blockingIds, blockingIds,
suspendedUsers,
] = await Promise.all([ ] = await Promise.all([
LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()), LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()),
UserMutingsCache.init(user.id).then((cache) => cache.getAll()), UserMutingsCache.init(user.id).then((cache) => cache.getAll()),
InstanceMutingsCache.init(user.id).then((cache) => cache.getAll()), InstanceMutingsCache.init(user.id).then((cache) => cache.getAll()),
UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockedCache.init(user.id).then((cache) => cache.getAll()),
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
SuspendedUsersCache.init().then((cache) => cache.getAll()),
]); ]);
const validUserIds = [user.id, ...followingUserIds]; const validUserIds = [user.id, ...followingUserIds];
@ -130,7 +133,8 @@ export default define(meta, paramDef, async (ps, user) => {
n.notifierId && n.notifierId &&
(mutedUserIds.includes(n.notifierId) || (mutedUserIds.includes(n.notifierId) ||
blockingIds.includes(n.notifierId) || blockingIds.includes(n.notifierId) ||
blockerIds.includes(n.notifierId)) blockerIds.includes(n.notifierId) ||
suspendedUsers.includes(n.notifierId))
), ),
); );
if (ps.directOnly) { if (ps.directOnly) {

View file

@ -42,7 +42,7 @@ export const paramDef = {
export default define(meta, paramDef, async (ps) => { export default define(meta, paramDef, async (ps) => {
if (scyllaClient) { if (scyllaClient) {
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes.filter((note) => !note.localOnly); let filtered = notes.filter((note) => !note.localOnly);
if (ps.reply === undefined) { if (ps.reply === undefined) {
filtered = filtered.filter( filtered = filtered.filter(

View file

@ -19,6 +19,7 @@ import type { Note } from "@/models/entities/note.js";
import { import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -67,6 +68,8 @@ export default define(meta, paramDef, async (ps, user) => {
blockingIds, blockingIds,
]: string[][] = []; ]: string[][] = [];
let mutedWords: string[][] = []; let mutedWords: string[][] = [];
blockerIds = [];
blockingIds = [];
if (user) { if (user) {
[ [
followingUserIds, followingUserIds,
@ -91,6 +94,7 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const root = await getNote(ps.noteId, user, followingUserIds).catch( const root = await getNote(ps.noteId, user, followingUserIds).catch(
() => null, () => null,
@ -99,20 +103,20 @@ export default define(meta, paramDef, async (ps, user) => {
return await Notes.packMany([]); return await Notes.packMany([]);
} }
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = await filterVisibility(notes, user, followingUserIds); let filtered = filterVisibility(notes, user, followingUserIds);
filtered = filterBlockUser(filtered, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (user) { if (user) {
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [
...blockerIds,
...blockingIds,
]);
} }
return filtered; return filtered;
}; };
@ -123,7 +127,7 @@ export default define(meta, paramDef, async (ps, user) => {
[root.id], [root.id],
{ prepare: true }, { prepare: true },
); );
const foundNotes = await filter( const foundNotes = filter(
renoteResult.rows.map(parseScyllaNote).filter((note) => !!note.text), renoteResult.rows.map(parseScyllaNote).filter((note) => !!note.text),
); );
@ -140,7 +144,7 @@ export default define(meta, paramDef, async (ps, user) => {
[note.id], [note.id],
{ prepare: true }, { prepare: true },
); );
const replies = await filter(replyResult.rows.map(parseScyllaNote)); const replies = filter(replyResult.rows.map(parseScyllaNote));
if (replies.length > 0) { if (replies.length > 0) {
foundNotes.push(...replies); foundNotes.push(...replies);
queue.push(...replies); queue.push(...replies);

View file

@ -13,6 +13,7 @@ import {
} from "@/db/scylla.js"; } from "@/db/scylla.js";
import { import {
InstanceMutingsCache, InstanceMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -63,6 +64,9 @@ export default define(meta, paramDef, async (ps, user) => {
let [mutedUserIds, mutedInstances, blockerIds, blockingIds]: string[][] = let [mutedUserIds, mutedInstances, blockerIds, blockingIds]: string[][] =
[]; [];
let mutedWords: string[][] = []; let mutedWords: string[][] = [];
blockerIds = [];
blockingIds = [];
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const foundNotes: ScyllaNote[] = []; const foundNotes: ScyllaNote[] = [];
let searchedDays = 0; let searchedDays = 0;
@ -101,18 +105,19 @@ export default define(meta, paramDef, async (ps, user) => {
break; break;
} }
notes = filterBlockUser(notes, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (user) { if (user) {
notes = await filterMutedUser( notes = filterMutedUser(
notes, notes,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
notes = await filterMutedNote(notes, user, mutedWords); notes = filterMutedNote(notes, user, mutedWords);
notes = await filterBlockUser(notes, user, [
...blockerIds,
...blockingIds,
]);
} }
foundNotes.push(...notes); foundNotes.push(...notes);

View file

@ -22,6 +22,7 @@ import {
import { import {
InstanceMutingsCache, InstanceMutingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -103,6 +104,9 @@ export default define(meta, paramDef, async (ps, user) => {
renoteMutedIds, renoteMutedIds,
]: string[][] = []; ]: string[][] = [];
let mutedWords: string[][]; let mutedWords: string[][];
blockerIds = [];
blockingIds = [];
const suspendedUsers = await SuspendedUsersCache.init().then((cache) => cache.getAll());
if (user) { if (user) {
[ [
mutedUserIds, mutedUserIds,
@ -128,24 +132,24 @@ export default define(meta, paramDef, async (ps, user) => {
]); ]);
} }
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes.filter( let filtered = notes.filter(
(n) => n.visibility === "public" && !n.channelId, (n) => n.visibility === "public" && !n.channelId,
); );
filtered = await filterReply(filtered, ps.withReplies, user); filtered = filterReply(filtered, ps.withReplies, user);
filtered = filterBlockUser(filtered, [
...blockerIds,
...blockingIds,
...suspendedUsers,
]);
if (user) { if (user) {
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [ filtered = filterMutedRenotes(filtered, renoteMutedIds);
...blockerIds,
...blockingIds,
]);
filtered = await filterMutedRenotes(filtered, user, renoteMutedIds);
} }
if (ps.withFiles) { if (ps.withFiles) {
filtered = filtered.filter((n) => n.files.length > 0); filtered = filtered.filter((n) => n.files.length > 0);

View file

@ -29,6 +29,7 @@ import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -111,6 +112,7 @@ export default define(meta, paramDef, async (ps, user) => {
blockerIds, blockerIds,
blockingIds, blockingIds,
renoteMutedIds, renoteMutedIds,
suspendedUserIds,
] = await Promise.all([ ] = await Promise.all([
ChannelFollowingsCache.init(user.id).then((cache) => cache.getAll()), ChannelFollowingsCache.init(user.id).then((cache) => cache.getAll()),
LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()), LocalFollowingsCache.init(user.id).then((cache) => cache.getAll()),
@ -127,6 +129,7 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockedCache.init(user.id).then((cache) => cache.getAll()),
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()),
SuspendedUsersCache.init().then((cache) => cache.getAll()),
]); ]);
const homeUserIds = [user.id, ...followingUserIds]; const homeUserIds = [user.id, ...followingUserIds];
const optFilter = (n: ScyllaNote) => const optFilter = (n: ScyllaNote) =>
@ -137,22 +140,22 @@ export default define(meta, paramDef, async (ps, user) => {
const localFilter = (notes: ScyllaNote[]) => const localFilter = (notes: ScyllaNote[]) =>
notes.filter((n) => !homeUserIds.includes(n.userId)); notes.filter((n) => !homeUserIds.includes(n.userId));
const commonFilter = async (notes: ScyllaNote[]) => { const commonFilter = (notes: ScyllaNote[]) => {
let filtered = await filterChannel(notes, user, followingChannelIds); let filtered = filterChannel(notes, user, followingChannelIds);
filtered = await filterReply(filtered, ps.withReplies, user); filtered = filterReply(filtered, ps.withReplies, user);
filtered = await filterVisibility(filtered, user, followingUserIds); filtered = filterVisibility(filtered, user, followingUserIds);
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [ filtered = filterBlockUser(filtered, [
...blockerIds, ...blockerIds,
...blockingIds, ...blockingIds,
...suspendedUserIds,
]); ]);
filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); filtered = filterMutedRenotes(filtered, renoteMutedIds);
if (!ps.includeMyRenotes) { if (!ps.includeMyRenotes) {
filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n)); filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n));
} }

View file

@ -29,6 +29,7 @@ import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -120,6 +121,8 @@ export default define(meta, paramDef, async (ps, user) => {
renoteMutedIds, renoteMutedIds,
]: string[][] = []; ]: string[][] = [];
let mutedWords: string[][]; let mutedWords: string[][];
blockerIds = [];
blockingIds = [];
if (user) { if (user) {
[ [
followingChannelIds, followingChannelIds,
@ -148,24 +151,25 @@ export default define(meta, paramDef, async (ps, user) => {
RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = await filterChannel(notes, user, followingChannelIds); let filtered = filterChannel(notes, user, followingChannelIds);
filtered = await filterReply(filtered, ps.withReplies, user); filtered = filterReply(filtered, ps.withReplies, user);
filtered = await filterVisibility(filtered, user, followingUserIds); filtered = filterVisibility(filtered, user, followingUserIds);
filtered = filterBlockUser(filtered, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (user) { if (user) {
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [ filtered = filterMutedRenotes(filtered, renoteMutedIds);
...blockerIds,
...blockingIds,
]);
filtered = await filterMutedRenotes(filtered, user, renoteMutedIds);
} }
if (ps.withFiles) { if (ps.withFiles) {
filtered = filtered.filter((n) => n.files.length > 0); filtered = filtered.filter((n) => n.files.length > 0);

View file

@ -27,6 +27,7 @@ import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -116,6 +117,8 @@ export default define(meta, paramDef, async (ps, user) => {
renoteMutedIds, renoteMutedIds,
]: string[][] = []; ]: string[][] = [];
let mutedWords: string[][]; let mutedWords: string[][];
blockerIds = [];
blockingIds = [];
if (user) { if (user) {
[ [
followingUserIds, followingUserIds,
@ -142,8 +145,9 @@ export default define(meta, paramDef, async (ps, user) => {
RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes.filter( let filtered = notes.filter(
(n) => (n) =>
n.visibility === "public" && n.visibility === "public" &&
@ -151,21 +155,21 @@ export default define(meta, paramDef, async (ps, user) => {
m.recommendedInstances.includes(n.userHost) && m.recommendedInstances.includes(n.userHost) &&
!n.channelId, !n.channelId,
); );
filtered = await filterReply(filtered, ps.withReplies, user); filtered = filterReply(filtered, ps.withReplies, user);
filtered = await filterVisibility(filtered, user, followingUserIds); filtered = filterVisibility(filtered, user, followingUserIds);
filtered = filterBlockUser(filtered, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (user) { if (user) {
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [ filtered = filterMutedRenotes(filtered, renoteMutedIds);
...blockerIds,
...blockingIds,
]);
filtered = await filterMutedRenotes(filtered, user, renoteMutedIds);
} }
if (ps.withFiles) { if (ps.withFiles) {
filtered = filtered.filter((n) => n.files.length > 0); filtered = filtered.filter((n) => n.files.length > 0);

View file

@ -17,6 +17,7 @@ import {
import { import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -78,6 +79,8 @@ export default define(meta, paramDef, async (ps, user) => {
if (scyllaClient) { if (scyllaClient) {
let [mutedUserIds, mutedInstances, blockerIds, blockingIds]: string[][] = let [mutedUserIds, mutedInstances, blockerIds, blockingIds]: string[][] =
[]; [];
blockerIds = [];
blockingIds = [];
if (user) { if (user) {
[mutedUserIds, mutedInstances, blockerIds, blockingIds] = [mutedUserIds, mutedInstances, blockerIds, blockingIds] =
await Promise.all([ await Promise.all([
@ -87,24 +90,25 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes; let filtered = notes;
if (ps.userId) { if (ps.userId) {
filtered = filtered.filter((n) => n.userId === ps.userId); filtered = filtered.filter((n) => n.userId === ps.userId);
} }
filtered = await filterVisibility(filtered, user, followingUserIds); filtered = filterVisibility(filtered, user, followingUserIds);
filtered = filterBlockUser(filtered, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (user) { if (user) {
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterBlockUser(filtered, user, [
...blockerIds,
...blockingIds,
]);
} }
return filtered; return filtered;
}; };

View file

@ -28,6 +28,7 @@ import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -102,6 +103,7 @@ export default define(meta, paramDef, async (ps, user) => {
blockerIds, blockerIds,
blockingIds, blockingIds,
renoteMutedIds, renoteMutedIds,
suspendedUserIds,
] = await Promise.all([ ] = await Promise.all([
ChannelFollowingsCache.init(user.id).then((cache) => cache.getAll()), ChannelFollowingsCache.init(user.id).then((cache) => cache.getAll()),
followingsCache.getAll(), followingsCache.getAll(),
@ -118,28 +120,29 @@ export default define(meta, paramDef, async (ps, user) => {
UserBlockedCache.init(user.id).then((cache) => cache.getAll()), UserBlockedCache.init(user.id).then((cache) => cache.getAll()),
UserBlockingCache.init(user.id).then((cache) => cache.getAll()), UserBlockingCache.init(user.id).then((cache) => cache.getAll()),
RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()), RenoteMutingsCache.init(user.id).then((cache) => cache.getAll()),
SuspendedUsersCache.init().then((cache) => cache.getAll()),
]); ]);
const validUserIds = [user.id, ...followingUserIds]; const validUserIds = [user.id, ...followingUserIds];
const optFilter = (n: ScyllaNote) => const optFilter = (n: ScyllaNote) =>
!n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll; !n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll;
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes.filter((n) => validUserIds.includes(n.userId)); let filtered = notes.filter((n) => validUserIds.includes(n.userId));
filtered = await filterChannel(filtered, user, followingChannelIds); filtered = filterChannel(filtered, user, followingChannelIds);
filtered = await filterReply(filtered, ps.withReplies, user); filtered = filterReply(filtered, ps.withReplies, user);
filtered = await filterVisibility(filtered, user, followingUserIds); filtered = filterVisibility(filtered, user, followingUserIds);
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
user,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, user, mutedWords); filtered = filterMutedNote(filtered, user, mutedWords);
filtered = await filterBlockUser(filtered, user, [ filtered = filterBlockUser(filtered, [
...blockerIds, ...blockerIds,
...blockingIds, ...blockingIds,
...suspendedUserIds,
]); ]);
filtered = await filterMutedRenotes(filtered, user, renoteMutedIds); filtered = filterMutedRenotes(filtered, renoteMutedIds);
if (!ps.includeMyRenotes) { if (!ps.includeMyRenotes) {
filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n)); filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n));
} }

View file

@ -91,8 +91,8 @@ export default define(meta, paramDef, async (ps, user) => {
); );
const optFilter = (n: ScyllaNote) => const optFilter = (n: ScyllaNote) =>
!n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll; !n.renoteId || !!n.text || n.files.length > 0 || n.hasPoll;
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = await filterVisibility(notes, user, followingUserIds); let filtered = filterVisibility(notes, user, followingUserIds);
if (!ps.includeMyRenotes) { if (!ps.includeMyRenotes) {
filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n)); filtered = filtered.filter((n) => n.userId !== user.id || optFilter(n));
} }

View file

@ -21,6 +21,7 @@ import {
InstanceMutingsCache, InstanceMutingsCache,
LocalFollowingsCache, LocalFollowingsCache,
RenoteMutingsCache, RenoteMutingsCache,
SuspendedUsersCache,
UserBlockedCache, UserBlockedCache,
UserBlockingCache, UserBlockingCache,
UserMutingsCache, UserMutingsCache,
@ -94,6 +95,8 @@ export default define(meta, paramDef, async (ps, me) => {
blockingIds, blockingIds,
]: string[][] = []; ]: string[][] = [];
let mutedWords: string[][]; let mutedWords: string[][];
blockerIds = [];
blockingIds = [];
if (me) { if (me) {
[ [
followingUserIds, followingUserIds,
@ -118,6 +121,7 @@ export default define(meta, paramDef, async (ps, me) => {
UserBlockingCache.init(me.id).then((cache) => cache.getAll()), UserBlockingCache.init(me.id).then((cache) => cache.getAll()),
]); ]);
} }
const suspendedUserIds = await SuspendedUsersCache.init().then((cache) => cache.getAll());
if ( if (
me && me &&
@ -128,21 +132,21 @@ export default define(meta, paramDef, async (ps, me) => {
return Notes.packMany([]); return Notes.packMany([]);
} }
const filter = async (notes: ScyllaNote[]) => { const filter = (notes: ScyllaNote[]) => {
let filtered = notes.filter((n) => n.userId === ps.userId); let filtered = notes.filter((n) => n.userId === ps.userId);
filtered = await filterVisibility(filtered, me, followingUserIds); filtered = filterVisibility(filtered, me, followingUserIds);
filtered = filterBlockUser(filtered, [
...blockerIds,
...blockingIds,
...suspendedUserIds,
]);
if (me) { if (me) {
filtered = await filterMutedUser( filtered = filterMutedUser(
filtered, filtered,
me,
mutedUserIds, mutedUserIds,
mutedInstances, mutedInstances,
); );
filtered = await filterMutedNote(filtered, me, mutedWords); filtered = filterMutedNote(filtered, me, mutedWords);
filtered = await filterBlockUser(filtered, me, [
...blockerIds,
...blockingIds,
]);
} }
if (ps.withFiles) { if (ps.withFiles) {
filtered = filtered.filter((n) => n.files.length > 0); filtered = filtered.filter((n) => n.files.length > 0);

View file

@ -79,11 +79,11 @@ export default define(meta, paramDef, async (ps, me) => {
const notes = await client const notes = await client
.execute(prepared.note.select.byIds, [noteIds], { prepare: true }) .execute(prepared.note.select.byIds, [noteIds], { prepare: true })
.then((result) => result.rows.map(parseScyllaNote)); .then((result) => result.rows.map(parseScyllaNote));
const filteredNoteIds = await filterVisibility( const filteredNoteIds = filterVisibility(
notes, notes,
me, me,
followingUserIds, followingUserIds,
).then((notes) => notes.map(({ id }) => id)); ).map(({ id }) => id);
noteIds = noteIds.filter((id) => filteredNoteIds.includes(id)); noteIds = noteIds.filter((id) => filteredNoteIds.includes(id));
} }