upd: add notification for failures, add reasons for failure, apply suggestions

This commit is contained in:
Marie 2024-11-03 17:59:50 +01:00
parent 6d8fe60f11
commit 57ee1d8793
No known key found for this signature in database
GPG key ID: 7ADF6C9CD9A28555
17 changed files with 110 additions and 28 deletions

View file

@ -20,7 +20,7 @@ import type { OnModuleInit } from '@nestjs/common';
import type { UserEntityService } from './UserEntityService.js';
import type { NoteEntityService } from './NoteEntityService.js';
const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded', 'edited'] as (typeof groupedNotificationTypes[number])[]);
const NOTE_REQUIRED_NOTIFICATION_TYPES = new Set(['note', 'mention', 'reply', 'renote', 'renote:grouped', 'quote', 'reaction', 'reaction:grouped', 'pollEnded', 'edited', 'scheduledNoteFailed'] as (typeof groupedNotificationTypes[number])[]);
@Injectable()
export class NotificationEntityService implements OnModuleInit {
@ -169,6 +169,9 @@ export class NotificationEntityService implements OnModuleInit {
exportedEntity: notification.exportedEntity,
fileId: notification.fileId,
} : {}),
...(notification.type === 'scheduledNoteFailed' ? {
reason: notification.reason,
} : {}),
...(notification.type === 'app' ? {
body: notification.customBody,
header: notification.customHeader,

View file

@ -18,8 +18,6 @@ type MinimumUser = {
};
export type MiScheduleNoteType={
/** Date.toISOString() */
createdAt: string;
visibility: 'public' | 'home' | 'followers' | 'specified';
visibleUsers: MinimumUser[];
channel?: MiChannel['id'];

View file

@ -122,6 +122,11 @@ export type MiNotification = {
createdAt: string;
notifierId: MiUser['id'];
noteId: MiNote['id'];
} | {
type: 'scheduledNoteFailed';
id: string;
createdAt: string;
reason: string;
};
export type MiGroupedNotification = MiNotification | {

View file

@ -369,6 +369,20 @@ export const packedNotificationSchema = {
optional: false, nullable: false,
},
},
}, {
type: 'object',
properties: {
...baseSchema.properties,
type: {
type: 'string',
optional: false, nullable: false,
enum: ['scheduledNoteFailed'],
},
reason: {
type: 'string',
optional: false, nullable: false,
},
},
}, {
type: 'object',
properties: {

View file

@ -9,6 +9,7 @@ import { bindThis } from '@/decorators.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import type { ChannelsRepository, DriveFilesRepository, MiDriveFile, NoteScheduleRepository, NotesRepository, UsersRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { NotificationService } from '@/core/NotificationService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
import type { ScheduleNotePostJobData } from '../types.js';
@ -32,6 +33,7 @@ export class ScheduleNotePostProcessorService {
private noteCreateService: NoteCreateService,
private queueLoggerService: QueueLoggerService,
private notificationService: NotificationService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('schedule-note-post');
}
@ -50,8 +52,9 @@ export class ScheduleNotePostProcessorService {
const renote = note.reply ? await this.notesRepository.findOneBy({ id: note.renote }) : undefined;
const channel = note.channel ? await this.channelsRepository.findOneBy({ id: note.channel, isArchived: false }) : undefined;
let files: MiDriveFile[] = [];
const fileIds = note.files ?? null;
if (fileIds != null && fileIds.length > 0 && me) {
const fileIds = note.files;
if (fileIds.length > 0 && me) {
files = await this.driveFilesRepository.createQueryBuilder('file')
.where('file.userId = :userId AND file.id IN (:...fileIds)', {
userId: me.id,
@ -61,22 +64,52 @@ export class ScheduleNotePostProcessorService {
.setParameters({ fileIds })
.getMany();
}
if (
!data.userId ||
!me ||
(note.reply && !reply) ||
(note.renote && !renote) ||
(note.channel && !channel) ||
(note.files.length !== files.length)
) {
//キューに積んだときは有った物が消滅してたら予約投稿をキャンセルする
this.logger.warn('cancel schedule note');
if (!data.userId || !me) {
this.logger.warn('Schedule Note Failed Reason: User Not Found');
await this.noteScheduleRepository.remove(data);
return;
}
if (note.files.length !== files.length) {
this.logger.warn('Schedule Note Failed Reason: files are missing in the user\'s drive');
this.notificationService.createNotification(me.id, 'scheduledNoteFailed', {
reason: 'Some attached files on your scheduled note no longer exist',
});
await this.noteScheduleRepository.remove(data);
return;
}
if (note.reply && !reply) {
this.logger.warn('Schedule Note Failed Reason: parent note to reply does not exist');
this.notificationService.createNotification(me.id, 'scheduledNoteFailed', {
reason: 'Replied to note on your scheduled note no longer exists',
});
await this.noteScheduleRepository.remove(data);
return;
}
if (note.renote && !renote) {
this.logger.warn('Schedule Note Failed Reason: attached quote note no longer exists');
this.notificationService.createNotification(me.id, 'scheduledNoteFailed', {
reason: 'A quoted note from one of your scheduled notes no longer exists',
});
await this.noteScheduleRepository.remove(data);
return;
}
if (note.channel && !channel) {
this.logger.warn('Schedule Note Failed Reason: Channel does not exist');
this.notificationService.createNotification(me.id, 'scheduledNoteFailed', {
reason: 'An attached channel on your scheduled note no longer exists',
});
await this.noteScheduleRepository.remove(data);
return;
}
await this.noteCreateService.create(me, {
...note,
createdAt: new Date(note.createdAt), //typeORMのjsonbで何故かstringにされるから戻す
createdAt: new Date(),
files,
poll: note.poll ? {
choices: note.poll.choices,

View file

@ -292,7 +292,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
// Check blocking
if (reply.userId !== me.id) {
const blockExist = await this.blockingsRepository.exist({
const blockExist = await this.blockingsRepository.exists({
where: {
blockerId: reply.userId,
blockeeId: me.id,
@ -324,8 +324,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} else {
throw new ApiError(meta.errors.cannotCreateAlreadyExpiredSchedule);
}
const note:MiScheduleNoteType = {
createdAt: new Date(ps.scheduleNote.scheduledAt!).toISOString(),
const note: MiScheduleNoteType = {
files: files.map(f => f.id),
poll: ps.poll ? {
choices: ps.poll.choices,

View file

@ -35,6 +35,7 @@ export const notificationTypes = [
'roleAssigned',
'achievementEarned',
'exportCompleted',
'scheduledNoteFailed',
'app',
'test',
] as const;