Skip to content

Commit

Permalink
feat: 노트 게시를 예약할 수 있음 (yojo-art/cherrypick#483, [Type4ny-Project/Type…
Browse files Browse the repository at this point in the history
…4ny@271c872c](Type4ny-Project@271c872))

(cherry picked from commit 4d8512388e8f56c6c687b6fca069e789cbc10ae5)

Co-Authored-By: mattyatea <mattyacocacora0@gmail.com>
Co-Authored-By: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-Authored-By: kozakura913 <98575220+kozakura913@users.noreply.github.com>
  • Loading branch information
4 people committed Nov 5, 2024
1 parent 6718a54 commit 619d404
Show file tree
Hide file tree
Showing 41 changed files with 1,461 additions and 8 deletions.
2 changes: 2 additions & 0 deletions locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,8 @@ _permissions:
"read:mutes": "View your list of muted users"
"write:mutes": "Edit your list of muted users"
"write:notes": "Compose or delete notes"
"read:notes-schedule": "View your list of scheduled notes"
"write:notes-schedule": "Compose or delete scheduled notes"
"read:notifications": "View your notifications"
"write:notifications": "Manage your notifications"
"read:reactions": "View your reactions"
Expand Down
8 changes: 8 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8150,6 +8150,14 @@ export interface Locale extends ILocale {
* ノートを作成・削除する
*/
"write:notes": string;
/**
* 予約投稿を見る
*/
"read:notes-schedule": string;
/**
* 予約投稿を作成・削除する
*/
"write:notes-schedule": string;
/**
* 通知を見る
*/
Expand Down
2 changes: 2 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2141,6 +2141,8 @@ _permissions:
"read:mutes": "ミュートを見る"
"write:mutes": "ミュートを操作する"
"write:notes": "ノートを作成・削除する"
"read:notes-schedule": "予約投稿を見る"
"write:notes-schedule": "予約投稿を作成・削除する"
"read:notifications": "通知を見る"
"write:notifications": "通知を操作する"
"read:reactions": "リアクションを見る"
Expand Down
2 changes: 2 additions & 0 deletions locales/ko-KR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2080,6 +2080,8 @@ _permissions:
"read:mutes": "뮤트 여부를 확인합니다"
"write:mutes": "뮤트를 하거나 해제합니다"
"write:notes": "노트를 작성하거나 삭제합니다"
"read:notes-schedule": "게시를 예약한 노트를 봅니다"
"write:notes-schedule": "노트 게시를 예약하거나 삭제합니다"
"read:notifications": "알림을 확인합니다"
"write:notifications": "알림을 모두 읽음 처리합니다"
"read:reactions": "리액션을 확인합니다"
Expand Down
17 changes: 17 additions & 0 deletions packages/backend/migration/1699437894737-scheduleNote.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class ScheduleNote1699437894737 {
name = 'ScheduleNote1699437894737'

async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "note_schedule" ("id" character varying(32) NOT NULL, "note" jsonb NOT NULL, "userId" character varying(260) NOT NULL, "scheduledAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_3a1ae2db41988f4994268218436" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_e798958c40009bf0cdef4f28b5" ON "note_schedule" ("userId") `);
}

async down(queryRunner) {
await queryRunner.query(`DROP TABLE "note_schedule"`);
}
}
12 changes: 12 additions & 0 deletions packages/backend/src/core/QueueModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
RelationshipJobData,
UserWebhookDeliverJobData,
SystemWebhookDeliverJobData,
ScheduleNotePostJobData,
} from '../queue/types.js';
import type { Provider } from '@nestjs/common';

Expand All @@ -28,6 +29,7 @@ export type RelationshipQueue = Bull.Queue<RelationshipJobData>;
export type ObjectStorageQueue = Bull.Queue;
export type UserWebhookDeliverQueue = Bull.Queue<UserWebhookDeliverJobData>;
export type SystemWebhookDeliverQueue = Bull.Queue<SystemWebhookDeliverJobData>;
export type ScheduleNotePostQueue = Bull.Queue<ScheduleNotePostJobData>;

const $system: Provider = {
provide: 'queue:system',
Expand Down Expand Up @@ -83,6 +85,12 @@ const $systemWebhookDeliver: Provider = {
inject: [DI.config],
};

const $scheduleNotePost: Provider = {
provide: 'queue:scheduleNotePost',
useFactory: (config: Config) => new Bull.Queue(QUEUE.SCHEDULE_NOTE_POST, baseQueueOptions(config, QUEUE.SCHEDULE_NOTE_POST)),
inject: [DI.config],
};

@Module({
imports: [
],
Expand All @@ -96,6 +104,7 @@ const $systemWebhookDeliver: Provider = {
$objectStorage,
$userWebhookDeliver,
$systemWebhookDeliver,
$scheduleNotePost,
],
exports: [
$system,
Expand All @@ -107,6 +116,7 @@ const $systemWebhookDeliver: Provider = {
$objectStorage,
$userWebhookDeliver,
$systemWebhookDeliver,
$scheduleNotePost,
],
})
export class QueueModule implements OnApplicationShutdown {
Expand All @@ -120,6 +130,7 @@ export class QueueModule implements OnApplicationShutdown {
@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
@Inject('queue:scheduleNotePost') public scheduleNotePostQueue: ScheduleNotePostQueue,
) {}

public async dispose(): Promise<void> {
Expand All @@ -136,6 +147,7 @@ export class QueueModule implements OnApplicationShutdown {
this.objectStorageQueue.close(),
this.userWebhookDeliverQueue.close(),
this.systemWebhookDeliverQueue.close(),
this.scheduleNotePostQueue.close(),
]);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/core/QueueService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type {
SystemQueue,
UserWebhookDeliverQueue,
SystemWebhookDeliverQueue,
ScheduleNotePostQueue,
} from './QueueModule.js';
import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq';
Expand All @@ -51,6 +52,7 @@ export class QueueService {
@Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue,
@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
@Inject('queue:scheduleNotePost') public ScheduleNotePostQueue: ScheduleNotePostQueue,
) {
this.systemQueue.add('tickCharts', {
}, {
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/core/RoleService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type RolePolicies = {
gtlAvailable: boolean;
ltlAvailable: boolean;
canPublicNote: boolean;
scheduleNoteMax: number;
mentionLimit: number;
canInvite: boolean;
inviteLimit: number;
Expand Down Expand Up @@ -69,6 +70,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
gtlAvailable: true,
ltlAvailable: true,
canPublicNote: true,
scheduleNoteMax: 5,
mentionLimit: 20,
canInvite: false,
inviteLimit: 0,
Expand Down Expand Up @@ -374,6 +376,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
scheduleNoteMax: calc('scheduleNoteMax', vs => Math.max(...vs)),
mentionLimit: calc('mentionLimit', vs => Math.max(...vs)),
canInvite: calc('canInvite', vs => vs.some(v => v === true)),
inviteLimit: calc('inviteLimit', vs => Math.max(...vs)),
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/di-symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,6 @@ export const DI = {
userMemosRepository: Symbol('userMemosRepository'),
bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'),
reversiGamesRepository: Symbol('reversiGamesRepository'),
noteScheduleRepository: Symbol('noteScheduleRepository'),
//#endregion
};
60 changes: 60 additions & 0 deletions packages/backend/src/models/NoteSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/

import { Entity, Index, Column, PrimaryColumn } from 'typeorm';
import { MiNote } from '@/models/Note.js';
import { id } from './util/id.js';
import { MiUser } from './User.js';
import { MiChannel } from './Channel.js';
import type { MiDriveFile } from './DriveFile.js';

type MinimumUser = {
id: MiUser['id'];
host: MiUser['host'];
username: MiUser['username'];
uri: MiUser['uri'];
};

export type MiScheduleNoteType={
/** Date.toISOString() */
createdAt: string;
visibility: 'public' | 'home' | 'followers' | 'specified';
visibleUsers: MinimumUser[];
channel?: MiChannel['id'];
poll: {
multiple: boolean;
choices: string[];
/** Date.toISOString() */
expiresAt: string | null
} | undefined;
renote?: MiNote['id'];
localOnly: boolean;
cw?: string | null;
reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null;
files: MiDriveFile['id'][];
text?: string | null;
reply?: MiNote['id'];
apMentions?: MinimumUser[] | null;
apHashtags?: string[] | null;
apEmojis?: string[] | null;
}

@Entity('note_schedule')
export class MiNoteSchedule {
@PrimaryColumn(id())
public id: string;

@Column('jsonb')
public note: MiScheduleNoteType;

@Index()
@Column('varchar', {
length: 260,
})
public userId: MiUser['id'];

@Column('timestamp with time zone')
public scheduledAt: Date;
}
9 changes: 9 additions & 0 deletions packages/backend/src/models/RepositoryModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
MiNote,
MiNoteFavorite,
MiNoteReaction,
MiNoteSchedule,
MiNoteThreadMuting,
MiNoteUnread,
MiPage,
Expand Down Expand Up @@ -495,6 +496,12 @@ const $reversiGamesRepository: Provider = {
inject: [DI.db],
};

const $noteScheduleRepository: Provider = {
provide: DI.noteScheduleRepository,
useFactory: (db: DataSource) => db.getRepository(MiNoteSchedule).extend(miRepository as MiRepository<MiNoteSchedule>),
inject: [DI.db],
};

@Module({
imports: [],
providers: [
Expand Down Expand Up @@ -567,6 +574,7 @@ const $reversiGamesRepository: Provider = {
$userMemosRepository,
$bubbleGameRecordsRepository,
$reversiGamesRepository,
$noteScheduleRepository,
],
exports: [
$usersRepository,
Expand Down Expand Up @@ -638,6 +646,7 @@ const $reversiGamesRepository: Provider = {
$userMemosRepository,
$bubbleGameRecordsRepository,
$reversiGamesRepository,
$noteScheduleRepository,
],
})
export class RepositoryModule {
Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/models/_.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import { MiFlashLike } from '@/models/FlashLike.js';
import { MiUserListFavorite } from '@/models/UserListFavorite.js';
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
import { MiReversiGame } from '@/models/ReversiGame.js';
import { MiNoteSchedule } from '@/models/NoteSchedule.js';
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';

export interface MiRepository<T extends ObjectLiteral> {
Expand Down Expand Up @@ -157,6 +158,7 @@ export {
MiNote,
MiNoteFavorite,
MiNoteReaction,
MiNoteSchedule,
MiNoteThreadMuting,
MiNoteUnread,
MiPage,
Expand Down Expand Up @@ -265,3 +267,4 @@ export type FlashLikesRepository = Repository<MiFlashLike> & MiRepository<MiFlas
export type UserMemoRepository = Repository<MiUserMemo> & MiRepository<MiUserMemo>;
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord> & MiRepository<MiBubbleGameRecord>;
export type ReversiGamesRepository = Repository<MiReversiGame> & MiRepository<MiReversiGame>;
export type NoteScheduleRepository = Repository<MiNoteSchedule> & MiRepository<MiNoteSchedule>;
4 changes: 4 additions & 0 deletions packages/backend/src/models/json-schema/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ export const packedRolePoliciesSchema = {
type: 'boolean',
optional: false, nullable: false,
},
scheduleNoteMax: {
type: 'integer',
optional: false, nullable: false,
},
},
} as const;

Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { MiFlashLike } from '@/models/FlashLike.js';
import { MiUserMemo } from '@/models/UserMemo.js';
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
import { MiReversiGame } from '@/models/ReversiGame.js';
import { MiNoteSchedule } from '@/models/NoteSchedule.js';

import { Config } from '@/config.js';
import MisskeyLogger from '@/logger.js';
Expand Down Expand Up @@ -155,6 +156,7 @@ export const entities = [
MiNote,
MiNoteFavorite,
MiNoteReaction,
MiNoteSchedule,
MiNoteThreadMuting,
MiNoteUnread,
MiPage,
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/queue/QueueProcessorModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { TickChartsProcessorService } from './processors/TickChartsProcessorServ
import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js';
import { ExportFavoritesProcessorService } from './processors/ExportFavoritesProcessorService.js';
import { RelationshipProcessorService } from './processors/RelationshipProcessorService.js';
import { ScheduleNotePostProcessorService } from './processors/ScheduleNotePostProcessorService.js';

@Module({
imports: [
Expand Down Expand Up @@ -84,6 +85,7 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor
CheckExpiredMutingsProcessorService,
CheckModeratorsActivityProcessorService,
QueueProcessorService,
ScheduleNotePostProcessorService,
],
exports: [
QueueProcessorService,
Expand Down
14 changes: 14 additions & 0 deletions packages/backend/src/queue/QueueProcessorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu
import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js';
import { CleanProcessorService } from './processors/CleanProcessorService.js';
import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js';
import { ScheduleNotePostProcessorService } from './processors/ScheduleNotePostProcessorService.js';
import { QueueLoggerService } from './QueueLoggerService.js';
import { QUEUE, baseQueueOptions } from './const.js';

Expand Down Expand Up @@ -84,6 +85,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
private relationshipQueueWorker: Bull.Worker;
private objectStorageQueueWorker: Bull.Worker;
private endedPollNotificationQueueWorker: Bull.Worker;
private schedulerNotePostQueueWorker: Bull.Worker;

constructor(
@Inject(DI.config)
Expand Down Expand Up @@ -123,6 +125,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
private bakeBufferedReactionsProcessorService: BakeBufferedReactionsProcessorService,
private checkModeratorsActivityProcessorService: CheckModeratorsActivityProcessorService,
private cleanProcessorService: CleanProcessorService,
private scheduleNotePostProcessorService: ScheduleNotePostProcessorService,
) {
this.logger = this.queueLoggerService.logger;

Expand Down Expand Up @@ -517,6 +520,15 @@ export class QueueProcessorService implements OnApplicationShutdown {
});
}
//#endregion

//#region schedule note post
{
this.schedulerNotePostQueueWorker = new Bull.Worker(QUEUE.SCHEDULE_NOTE_POST, (job) => this.scheduleNotePostProcessorService.process(job), {
...baseQueueOptions(this.config, QUEUE.SCHEDULE_NOTE_POST),
autorun: false,
});
}
//#endregion
}

@bindThis
Expand All @@ -531,6 +543,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
this.relationshipQueueWorker.run(),
this.objectStorageQueueWorker.run(),
this.endedPollNotificationQueueWorker.run(),
this.schedulerNotePostQueueWorker.run(),
]);
}

Expand All @@ -546,6 +559,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
this.relationshipQueueWorker.close(),
this.objectStorageQueueWorker.close(),
this.endedPollNotificationQueueWorker.close(),
this.schedulerNotePostQueueWorker.close(),
]);
}

Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/queue/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const QUEUE = {
OBJECT_STORAGE: 'objectStorage',
USER_WEBHOOK_DELIVER: 'userWebhookDeliver',
SYSTEM_WEBHOOK_DELIVER: 'systemWebhookDeliver',
SCHEDULE_NOTE_POST: 'scheduleNotePost',
};

export function baseQueueOptions(config: Config, queueName: typeof QUEUE[keyof typeof QUEUE]): Bull.QueueOptions {
Expand Down
Loading

0 comments on commit 619d404

Please sign in to comment.