/* * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ import { Inject, Injectable } from '@nestjs/common'; import { And, In, MoreThan, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { AutoRemovalConditionRepository, MiUserNotePining, NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import type { MiNote } from '@/models/Note.js'; import { bindThis } from '@/decorators.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { IdService } from '@/core/IdService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @Injectable() export class AutoNoteRemovalProcessorService { private logger: Logger; constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, @Inject(DI.usersRepository) private usersRepository: UsersRepository, @Inject(DI.userNotePiningsRepository) private userNotePiningsRepository: UserNotePiningsRepository, @Inject(DI.autoRemovalConditionRepository) private autoRemovalConditionRepository: AutoRemovalConditionRepository, private idService: IdService, private noteDeleteService: NoteDeleteService, private queueLoggerService: QueueLoggerService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('auto-note-removal'); } @bindThis public async process(): Promise { this.logger.info('Checking notes that to remove automatically...'); this.logger.info('Checking users that enabled note auto-removal'); const users = await this.usersRepository.find({ where: { autoRemoval: true } }); if (users.length < 1) { this.logger.info('Does not have any user that enabled autoRemoval'); return; } const now = Date.now(); for (const user of users) { const autoRemovalCondition = await this.autoRemovalConditionRepository.findOneByOrFail({ userId: user.id }); const pinings: MiUserNotePining[] = await this.userNotePiningsRepository.findBy({ userId: user.id }); const piningNoteIds: string[] = pinings.map(pining => pining.noteId); // pining.note always undefined (bug?) const specifiedNotes: MiNote[] = await this.notesRepository.findBy({ userId: user.id, visibility: Not(In(['public', 'home', 'followers'])), }); const specifiedNoteIds: string[] = specifiedNotes.map(note => note.id); const deleteAfter: number = autoRemovalCondition.deleteAfter * 86400000; // Delete notes let cursor: MiNote['id'] | null = null; let condition: string[] = []; if (autoRemovalCondition.noSpecifiedNotes === true) { condition = [...condition, ...specifiedNoteIds]; } if (autoRemovalCondition.noPiningNotes === true) { condition = [...condition, ...piningNoteIds]; } while (true) { const notes = await this.notesRepository.find({ where: { userId: user.id, ...(cursor ? { id: And(Not(In(condition)), MoreThan(cursor)), } : { id: Not(In(condition)), }), }, take: 100, order: { id: 1, }, }) as MiNote[]; if (notes.length === 0) { break; } cursor = notes.at(-1)?.id ?? null; for (const note of notes) { const createdAt: number = this.idService.parse(note.id).date.getTime(); const delta: number = now - createdAt; if (delta > deleteAfter) { await Promise.bind(this.noteDeleteService.delete(user, note, false, user)); } } } this.logger.succ('All of auto-removable notes deleted'); } this.logger.succ('All notes to auto-remove has beed removed.'); } }